ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.56
Committed: 2006-01-21T16:00:17Z (18 years, 9 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.55: +8 -0 lines
Log Message:
streamline for standalone VOSF performance testing

File Contents

# User Rev Content
1 gbeauche 1.1 /*
2     * video_vosf.h - Video/graphics emulation, video on SEGV signals support
3     *
4 gbeauche 1.48 * Basilisk II (C) 1997-2005 Christian Bauer
5 gbeauche 1.1 *
6     * This program is free software; you can redistribute it and/or modify
7     * it under the terms of the GNU General Public License as published by
8     * the Free Software Foundation; either version 2 of the License, or
9     * (at your option) any later version.
10     *
11     * This program is distributed in the hope that it will be useful,
12     * but WITHOUT ANY WARRANTY; without even the implied warranty of
13     * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14     * GNU General Public License for more details.
15     *
16     * You should have received a copy of the GNU General Public License
17     * along with this program; if not, write to the Free Software
18     * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
19     */
20    
21     #ifndef VIDEO_VOSF_H
22     #define VIDEO_VOSF_H
23    
24 gbeauche 1.34 // Note: this file must be #include'd only in video_x.cpp
25 gbeauche 1.1 #ifdef ENABLE_VOSF
26    
27 cebix 1.19 #include "sigsegv.h"
28     #include "vm_alloc.h"
29 gbeauche 1.46 #ifdef _WIN32
30     #include "util_windows.h"
31     #endif
32 cebix 1.19
33 gbeauche 1.39 // Glue for SDL and X11 support
34 gbeauche 1.56 #ifdef TEST_VOSF_PERFORMANCE
35     #define MONITOR_INIT /* nothing */
36     #else
37 gbeauche 1.38 #ifdef USE_SDL_VIDEO
38     #define MONITOR_INIT SDL_monitor_desc &monitor
39 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT driver_window *drv
40 gbeauche 1.52 #define VIDEO_DRV_DGA_INIT driver_fullscreen *drv
41 gbeauche 1.55 #define VIDEO_DRV_LOCK_PIXELS SDL_VIDEO_LOCK_SURFACE(drv->s)
42     #define VIDEO_DRV_UNLOCK_PIXELS SDL_VIDEO_UNLOCK_SURFACE(drv->s)
43 gbeauche 1.51 #define VIDEO_DRV_DEPTH drv->s->format->BitsPerPixel
44     #define VIDEO_DRV_WIDTH drv->s->w
45     #define VIDEO_DRV_HEIGHT drv->s->h
46     #define VIDEO_DRV_ROW_BYTES drv->s->pitch
47 gbeauche 1.38 #else
48 gbeauche 1.39 #ifdef SHEEPSHAVER
49 gbeauche 1.38 #define MONITOR_INIT /* nothing */
50 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT /* nothing */
51     #define VIDEO_DRV_DGA_INIT /* nothing */
52 gbeauche 1.33 #define VIDEO_DRV_WINDOW the_win
53     #define VIDEO_DRV_GC the_gc
54     #define VIDEO_DRV_IMAGE img
55     #define VIDEO_DRV_HAVE_SHM have_shm
56     #else
57 gbeauche 1.38 #define MONITOR_INIT X11_monitor_desc &monitor
58 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT driver_window *drv
59     #define VIDEO_DRV_DGA_INIT driver_dga *drv
60 gbeauche 1.33 #define VIDEO_DRV_WINDOW drv->w
61     #define VIDEO_DRV_GC drv->gc
62     #define VIDEO_DRV_IMAGE drv->img
63     #define VIDEO_DRV_HAVE_SHM drv->have_shm
64 gbeauche 1.38 #endif
65     #define VIDEO_DRV_LOCK_PIXELS /* nothing */
66     #define VIDEO_DRV_UNLOCK_PIXELS /* nothing */
67 gbeauche 1.51 #define VIDEO_DRV_DEPTH VIDEO_DRV_IMAGE->depth
68     #define VIDEO_DRV_WIDTH VIDEO_DRV_IMAGE->width
69     #define VIDEO_DRV_HEIGHT VIDEO_DRV_IMAGE->height
70 gbeauche 1.38 #define VIDEO_DRV_ROW_BYTES VIDEO_DRV_IMAGE->bytes_per_line
71 gbeauche 1.33 #endif
72 gbeauche 1.56 #endif
73 gbeauche 1.33
74 cebix 1.19 // Variables for Video on SEGV support
75 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
76 cebix 1.19
77     struct ScreenPageInfo {
78     int top, bottom; // Mapping between this virtual page and Mac scanlines
79     };
80    
81     struct ScreenInfo {
82     uintptr memStart; // Start address aligned to page boundary
83     uint32 memLength; // Length of the memory addressed by the screen pages
84    
85 gbeauche 1.27 uintptr pageSize; // Size of a page
86 cebix 1.19 int pageBits; // Shift count to get the page number
87     uint32 pageCount; // Number of pages allocated to the screen
88    
89     bool dirty; // Flag: set if the frame buffer was touched
90 gbeauche 1.50 bool very_dirty; // Flag: set if the frame buffer was completely modified (e.g. colormap changes)
91 cebix 1.19 char * dirtyPages; // Table of flags set if page was altered
92     ScreenPageInfo * pageInfo; // Table of mappings page -> Mac scanlines
93     };
94    
95     static ScreenInfo mainBuffer;
96    
97     #define PFLAG_SET_VALUE 0x00
98     #define PFLAG_CLEAR_VALUE 0x01
99     #define PFLAG_SET_VALUE_4 0x00000000
100     #define PFLAG_CLEAR_VALUE_4 0x01010101
101     #define PFLAG_SET(page) mainBuffer.dirtyPages[page] = PFLAG_SET_VALUE
102     #define PFLAG_CLEAR(page) mainBuffer.dirtyPages[page] = PFLAG_CLEAR_VALUE
103     #define PFLAG_ISSET(page) (mainBuffer.dirtyPages[page] == PFLAG_SET_VALUE)
104     #define PFLAG_ISCLEAR(page) (mainBuffer.dirtyPages[page] != PFLAG_SET_VALUE)
105    
106     #ifdef UNALIGNED_PROFITABLE
107     # define PFLAG_ISSET_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_SET_VALUE_4)
108     # define PFLAG_ISCLEAR_4(page) (*((uint32 *)(mainBuffer.dirtyPages + (page))) == PFLAG_CLEAR_VALUE_4)
109     #else
110     # define PFLAG_ISSET_4(page) \
111     PFLAG_ISSET(page ) && PFLAG_ISSET(page+1) \
112     && PFLAG_ISSET(page+2) && PFLAG_ISSET(page+3)
113     # define PFLAG_ISCLEAR_4(page) \
114     PFLAG_ISCLEAR(page ) && PFLAG_ISCLEAR(page+1) \
115     && PFLAG_ISCLEAR(page+2) && PFLAG_ISCLEAR(page+3)
116     #endif
117    
118     // Set the selected page range [ first_page, last_page [ into the SET state
119     #define PFLAG_SET_RANGE(first_page, last_page) \
120     memset(mainBuffer.dirtyPages + (first_page), PFLAG_SET_VALUE, \
121     (last_page) - (first_page))
122    
123     // Set the selected page range [ first_page, last_page [ into the CLEAR state
124     #define PFLAG_CLEAR_RANGE(first_page, last_page) \
125     memset(mainBuffer.dirtyPages + (first_page), PFLAG_CLEAR_VALUE, \
126     (last_page) - (first_page))
127    
128     #define PFLAG_SET_ALL do { \
129     PFLAG_SET_RANGE(0, mainBuffer.pageCount); \
130     mainBuffer.dirty = true; \
131     } while (0)
132    
133     #define PFLAG_CLEAR_ALL do { \
134     PFLAG_CLEAR_RANGE(0, mainBuffer.pageCount); \
135     mainBuffer.dirty = false; \
136 gbeauche 1.50 mainBuffer.very_dirty = false; \
137     } while (0)
138    
139     #define PFLAG_SET_VERY_DIRTY do { \
140     mainBuffer.very_dirty = true; \
141 cebix 1.19 } while (0)
142    
143     // Set the following macro definition to 1 if your system
144     // provides a really fast strchr() implementation
145     //#define HAVE_FAST_STRCHR 0
146    
147     static inline int find_next_page_set(int page)
148     {
149     #if HAVE_FAST_STRCHR
150     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_SET_VALUE);
151     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
152     #else
153     while (PFLAG_ISCLEAR_4(page))
154     page += 4;
155     while (PFLAG_ISCLEAR(page))
156     page++;
157     return page;
158     #endif
159     }
160    
161     static inline int find_next_page_clear(int page)
162     {
163     #if HAVE_FAST_STRCHR
164     char *match = strchr(mainBuffer.dirtyPages + page, PFLAG_CLEAR_VALUE);
165     return match ? match - mainBuffer.dirtyPages : mainBuffer.pageCount;
166     #else
167     while (PFLAG_ISSET_4(page))
168     page += 4;
169     while (PFLAG_ISSET(page))
170     page++;
171     return page;
172     #endif
173     }
174    
175 gbeauche 1.54 #if defined(HAVE_PTHREADS)
176     static pthread_mutex_t vosf_lock = PTHREAD_MUTEX_INITIALIZER; // Mutex to protect frame buffer (dirtyPages in fact)
177     #define LOCK_VOSF pthread_mutex_lock(&vosf_lock);
178     #define UNLOCK_VOSF pthread_mutex_unlock(&vosf_lock);
179 gbeauche 1.46 #elif defined(_WIN32)
180     static mutex_t vosf_lock; // Mutex to protect frame buffer (dirtyPages in fact)
181     #define LOCK_VOSF vosf_lock.lock();
182     #define UNLOCK_VOSF vosf_lock.unlock();
183 gbeauche 1.54 #elif defined(HAVE_SPINLOCKS)
184     static spinlock_t vosf_lock = SPIN_LOCK_UNLOCKED; // Mutex to protect frame buffer (dirtyPages in fact)
185     #define LOCK_VOSF spin_lock(&vosf_lock)
186     #define UNLOCK_VOSF spin_unlock(&vosf_lock)
187 cebix 1.19 #else
188     #define LOCK_VOSF
189     #define UNLOCK_VOSF
190     #endif
191    
192     static int log_base_2(uint32 x)
193     {
194     uint32 mask = 0x80000000;
195     int l = 31;
196     while (l >= 0 && (x & mask) == 0) {
197     mask >>= 1;
198     l--;
199     }
200     return l;
201     }
202    
203 gbeauche 1.20 // Extend size to page boundary
204     static uint32 page_extend(uint32 size)
205     {
206 gbeauche 1.47 const uint32 page_size = vm_get_page_size();
207 gbeauche 1.20 const uint32 page_mask = page_size - 1;
208     return (size + page_mask) & ~page_mask;
209     }
210    
211 cebix 1.19
212     /*
213 gbeauche 1.40 * Check if VOSF acceleration is profitable on this platform
214     */
215    
216 gbeauche 1.41 const int VOSF_PROFITABLE_TRIES = 3; // Make 3 attempts for full screen update
217     const int VOSF_PROFITABLE_THRESHOLD = 16667; // 60 Hz
218 gbeauche 1.40
219     static bool video_vosf_profitable(void)
220     {
221 gbeauche 1.41 int64 durations[VOSF_PROFITABLE_TRIES];
222     int mean_duration = 0;
223 gbeauche 1.40
224 gbeauche 1.41 for (int i = 0; i < VOSF_PROFITABLE_TRIES; i++) {
225     uint64 start = GetTicks_usec();
226     for (int p = 0; p < mainBuffer.pageCount; p++) {
227     uint8 *addr = (uint8 *)(mainBuffer.memStart + (p * mainBuffer.pageSize));
228     addr[0] = 0; // Trigger Screen_fault_handler()
229     }
230     int64 duration = GetTicks_usec() - start;
231     mean_duration += duration;
232     durations[i] = duration;
233    
234     PFLAG_CLEAR_ALL;
235     mainBuffer.dirty = false;
236     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
237     return false;
238 gbeauche 1.40 }
239    
240 gbeauche 1.41 mean_duration /= VOSF_PROFITABLE_TRIES;
241     D(bug("Triggered %d screen faults in %ld usec on average\n", mainBuffer.pageCount, mean_duration));
242     return (mean_duration < (VOSF_PROFITABLE_THRESHOLD * (frame_skip ? frame_skip : 1)));
243 gbeauche 1.40 }
244    
245    
246     /*
247 gbeauche 1.27 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
248 cebix 1.19 */
249    
250 gbeauche 1.38 static bool video_vosf_init(MONITOR_INIT)
251 cebix 1.19 {
252 gbeauche 1.42 VIDEO_MODE_INIT_MONITOR;
253 cebix 1.31
254 gbeauche 1.47 const uintptr page_size = vm_get_page_size();
255 gbeauche 1.27 const uintptr page_mask = page_size - 1;
256    
257     // Round up frame buffer base to page boundary
258     mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
259    
260     // The frame buffer size shall already be aligned to page boundary (use page_extend)
261     mainBuffer.memLength = the_buffer_size;
262    
263     mainBuffer.pageSize = page_size;
264     mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
265     mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
266    
267     // The "2" more bytes requested are a safety net to insure the
268     // loops in the update routines will terminate.
269     // See "How can we deal with array overrun conditions ?" hereunder for further details.
270 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
271     if (mainBuffer.dirtyPages == NULL)
272 gbeauche 1.27 return false;
273 cebix 1.19
274 gbeauche 1.27 PFLAG_CLEAR_ALL;
275     PFLAG_CLEAR(mainBuffer.pageCount);
276     PFLAG_SET(mainBuffer.pageCount+1);
277    
278     // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
279 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
280     if (mainBuffer.pageInfo == NULL)
281 gbeauche 1.27 return false;
282    
283     uint32 a = 0;
284 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
285 gbeauche 1.33 unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
286     if (y1 >= VIDEO_MODE_Y)
287     y1 = VIDEO_MODE_Y - 1;
288    
289     unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
290     if (y2 >= VIDEO_MODE_Y)
291     y2 = VIDEO_MODE_Y - 1;
292 gbeauche 1.27
293     mainBuffer.pageInfo[i].top = y1;
294     mainBuffer.pageInfo[i].bottom = y2;
295    
296     a += mainBuffer.pageSize;
297     if (a > mainBuffer.memLength)
298     a = mainBuffer.memLength;
299     }
300    
301     // We can now write-protect the frame buffer
302     if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
303     return false;
304    
305     // The frame buffer is sane, i.e. there is no write to it yet
306     mainBuffer.dirty = false;
307     return true;
308     }
309 cebix 1.19
310    
311 gbeauche 1.27 /*
312     * Deinitialize VOSF system
313     */
314 cebix 1.19
315 gbeauche 1.27 static void video_vosf_exit(void)
316     {
317 gbeauche 1.29 if (mainBuffer.pageInfo) {
318     free(mainBuffer.pageInfo);
319     mainBuffer.pageInfo = NULL;
320 gbeauche 1.27 }
321 gbeauche 1.29 if (mainBuffer.dirtyPages) {
322     free(mainBuffer.dirtyPages);
323     mainBuffer.dirtyPages = NULL;
324 cebix 1.19 }
325     }
326    
327    
328 gbeauche 1.1 /*
329 gbeauche 1.20 * Screen fault handler
330 gbeauche 1.1 */
331    
332 gbeauche 1.33 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
333 gbeauche 1.1 {
334 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
335    
336 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
337 gbeauche 1.20 * now so that the data could actually be written to. It will be made
338 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
339     */
340 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
341     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
342 gbeauche 1.11 LOCK_VOSF;
343     PFLAG_SET(page);
344 gbeauche 1.27 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
345 gbeauche 1.13 mainBuffer.dirty = true;
346 gbeauche 1.11 UNLOCK_VOSF;
347 gbeauche 1.16 return true;
348 gbeauche 1.1 }
349    
350 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
351 gbeauche 1.16 return false;
352 gbeauche 1.1 }
353    
354 gbeauche 1.20
355 gbeauche 1.1 /*
356     * Update display for Windowed mode and VOSF
357     */
358    
359 gbeauche 1.12 /* How can we deal with array overrun conditions ?
360    
361     The state of the framebuffer pages that have been touched are maintained
362     in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
363    
364     Terminology
365    
366     "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
367     "CLEAR Page Guard" refers to the page following the Last Page but is always
368     in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
369     Page Guard but is always in the SET state.
370    
371     Rough process
372    
373 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
374 gbeauche 1.12 screen. This job consists in finding the first_page that was touched.
375     i.e. find the next page that is SET. Then, finding how many pages were
376     touched starting from first_page. i.e. find the next page that is CLEAR.
377    
378 gbeauche 1.13 There are two cases to check:
379 gbeauche 1.12
380     - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
381     but it is beyond the valid pageCount value. Therefore, we exit from the
382     update routine.
383    
384     - Last Page is SET: first_page equals (pageCount - 1) and
385     find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
386     page to the screen. On the next iteration, page equals pageCount and
387     find_next_page_set() will reach the SET Page Guard. We still safely exit
388     from the update routine because the SET Page Guard position is greater
389     than pageCount.
390     */
391    
392 gbeauche 1.56 #ifndef TEST_VOSF_PERFORMANCE
393 gbeauche 1.52 static void update_display_window_vosf(VIDEO_DRV_WIN_INIT)
394 gbeauche 1.1 {
395 gbeauche 1.33 VIDEO_MODE_INIT;
396 cebix 1.31
397 gbeauche 1.1 int page = 0;
398     for (;;) {
399 cebix 1.28 const unsigned first_page = find_next_page_set(page);
400 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
401 gbeauche 1.1 break;
402 gbeauche 1.11
403     page = find_next_page_clear(first_page);
404     PFLAG_CLEAR_RANGE(first_page, page);
405 cebix 1.7
406 gbeauche 1.1 // Make the dirty pages read-only again
407     const int32 offset = first_page << mainBuffer.pageBits;
408     const uint32 length = (page - first_page) << mainBuffer.pageBits;
409 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
410 gbeauche 1.1
411     // There is at least one line to update
412     const int y1 = mainBuffer.pageInfo[first_page].top;
413     const int y2 = mainBuffer.pageInfo[page - 1].bottom;
414     const int height = y2 - y1 + 1;
415 gbeauche 1.38
416 gbeauche 1.49 // Update the_host_buffer
417 gbeauche 1.38 VIDEO_DRV_LOCK_PIXELS;
418 gbeauche 1.49 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
419     const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
420     int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
421     for (j = y1; j <= y2; j++) {
422     Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
423     i1 += src_bytes_per_row;
424     i2 += dst_bytes_per_row;
425 gbeauche 1.1 }
426 gbeauche 1.38 VIDEO_DRV_UNLOCK_PIXELS;
427    
428     #ifdef USE_SDL_VIDEO
429     SDL_UpdateRect(drv->s, 0, y1, VIDEO_MODE_X, height);
430     #else
431 gbeauche 1.33 if (VIDEO_DRV_HAVE_SHM)
432     XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
433 gbeauche 1.1 else
434 gbeauche 1.33 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
435 gbeauche 1.38 #endif
436 gbeauche 1.1 }
437 gbeauche 1.13 mainBuffer.dirty = false;
438 gbeauche 1.1 }
439 gbeauche 1.56 #endif
440 gbeauche 1.1
441    
442     /*
443     * Update display for DGA mode and VOSF
444 gbeauche 1.20 * (only in Real or Direct Addressing mode)
445 gbeauche 1.1 */
446    
447 gbeauche 1.56 #ifndef TEST_VOSF_PERFORMANCE
448 gbeauche 1.1 #if REAL_ADDRESSING || DIRECT_ADDRESSING
449 gbeauche 1.52 static void update_display_dga_vosf(VIDEO_DRV_DGA_INIT)
450 gbeauche 1.1 {
451 gbeauche 1.33 VIDEO_MODE_INIT;
452 cebix 1.31
453 gbeauche 1.51 // Compute number of bytes per row, take care to virtual screens
454     const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
455     const int dst_bytes_per_row = TrivialBytesPerRow(VIDEO_MODE_X, DepthModeForPixelDepth(VIDEO_DRV_DEPTH));
456     const int scr_bytes_per_row = VIDEO_DRV_ROW_BYTES;
457     assert(dst_bytes_per_row <= scr_bytes_per_row);
458     const int scr_bytes_left = scr_bytes_per_row - dst_bytes_per_row;
459    
460     // Full screen update requested?
461 gbeauche 1.50 if (mainBuffer.very_dirty) {
462     PFLAG_CLEAR_ALL;
463     vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ);
464 gbeauche 1.51 memcpy(the_buffer_copy, the_buffer, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
465 gbeauche 1.50 VIDEO_DRV_LOCK_PIXELS;
466 gbeauche 1.51 int i1 = 0, i2 = 0;
467     for (int j = 0; j < VIDEO_MODE_Y; j++) {
468     Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
469     i1 += src_bytes_per_row;
470     i2 += scr_bytes_per_row;
471     }
472     #ifdef USE_SDL_VIDEO
473     SDL_UpdateRect(drv->s, 0, 0, VIDEO_MODE_X, VIDEO_MODE_Y);
474     #endif
475 gbeauche 1.50 VIDEO_DRV_UNLOCK_PIXELS;
476     return;
477     }
478    
479 gbeauche 1.51 // Setup partial blitter (use 64-pixel wide chunks)
480     const int n_pixels = 64;
481     const int n_chunks = VIDEO_MODE_X / n_pixels;
482 gbeauche 1.53 const int n_pixels_left = VIDEO_MODE_X - (n_chunks * n_pixels);
483 gbeauche 1.51 const int src_chunk_size = src_bytes_per_row / n_chunks;
484     const int dst_chunk_size = dst_bytes_per_row / n_chunks;
485     const int src_chunk_size_left = src_bytes_per_row - (n_chunks * src_chunk_size);
486     const int dst_chunk_size_left = dst_bytes_per_row - (n_chunks * dst_chunk_size);
487    
488 gbeauche 1.52 int page = 0, last_scanline = -1;
489 gbeauche 1.1 for (;;) {
490 cebix 1.28 const unsigned first_page = find_next_page_set(page);
491 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
492 gbeauche 1.1 break;
493 gbeauche 1.11
494     page = find_next_page_clear(first_page);
495     PFLAG_CLEAR_RANGE(first_page, page);
496    
497 gbeauche 1.1 // Make the dirty pages read-only again
498     const int32 offset = first_page << mainBuffer.pageBits;
499     const uint32 length = (page - first_page) << mainBuffer.pageBits;
500 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
501 gbeauche 1.49
502 gbeauche 1.52 // Optimized for scanlines, don't process overlapping lines again
503     int y1 = mainBuffer.pageInfo[first_page].top;
504     int y2 = mainBuffer.pageInfo[page - 1].bottom;
505     if (y1 <= last_scanline && ++y1 >= VIDEO_MODE_Y)
506     continue;
507     if (y2 <= last_scanline && ++y2 >= VIDEO_MODE_Y)
508     continue;
509     last_scanline = y2;
510    
511     // Update the_host_buffer and copy of the_buffer, one line at a time
512 gbeauche 1.50 int i1 = y1 * src_bytes_per_row;
513 gbeauche 1.51 int i2 = y1 * scr_bytes_per_row;
514 gbeauche 1.53 #ifdef USE_SDL_VIDEO
515     int bbi = 0;
516     SDL_Rect bb[3] = {
517     { VIDEO_MODE_X, y1, 0, 0 },
518     { VIDEO_MODE_X, -1, 0, 0 },
519     { VIDEO_MODE_X, -1, 0, 0 }
520     };
521     #endif
522 gbeauche 1.50 VIDEO_DRV_LOCK_PIXELS;
523     for (int j = y1; j <= y2; j++) {
524     for (int i = 0; i < n_chunks; i++) {
525     if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size) != 0) {
526     memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size);
527     Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size);
528 gbeauche 1.51 #ifdef USE_SDL_VIDEO
529 gbeauche 1.53 const int x = i * n_pixels;
530     if (x < bb[bbi].x) {
531     if (bb[bbi].w)
532     bb[bbi].w += bb[bbi].x - x;
533     else
534     bb[bbi].w = n_pixels;
535     bb[bbi].x = x;
536     }
537     else if (x >= bb[bbi].x + bb[bbi].w)
538     bb[bbi].w = x + n_pixels - bb[bbi].x;
539 gbeauche 1.51 #endif
540 gbeauche 1.50 }
541     i1 += src_chunk_size;
542     i2 += dst_chunk_size;
543     }
544     if (src_chunk_size_left && dst_chunk_size_left) {
545     if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left) != 0) {
546     memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left);
547     Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size_left);
548     }
549     i1 += src_chunk_size_left;
550     i2 += dst_chunk_size_left;
551 gbeauche 1.53 #ifdef USE_SDL_VIDEO
552     const int x = n_chunks * n_pixels;
553     if (x < bb[bbi].x) {
554     if (bb[bbi].w)
555     bb[bbi].w += bb[bbi].x - x;
556     else
557     bb[bbi].w = n_pixels_left;
558     bb[bbi].x = x;
559     }
560     else if (x >= bb[bbi].x + bb[bbi].w)
561     bb[bbi].w = x + n_pixels_left - bb[bbi].x;
562     #endif
563 gbeauche 1.50 }
564 gbeauche 1.51 i2 += scr_bytes_left;
565 gbeauche 1.53 #ifdef USE_SDL_VIDEO
566     bb[bbi].h++;
567     if (bb[bbi].w && (j == y1 || j == y2 - 1 || j == y2)) {
568     bbi++;
569     assert(bbi <= 3);
570     if (j != y2)
571     bb[bbi].y = j + 1;
572     }
573     #endif
574 gbeauche 1.50 }
575 gbeauche 1.53 #ifdef USE_SDL_VIDEO
576     SDL_UpdateRects(drv->s, bbi, bb);
577     #endif
578 gbeauche 1.50 VIDEO_DRV_UNLOCK_PIXELS;
579 gbeauche 1.1 }
580 gbeauche 1.13 mainBuffer.dirty = false;
581 gbeauche 1.1 }
582     #endif
583 gbeauche 1.56 #endif
584 gbeauche 1.1
585     #endif /* ENABLE_VOSF */
586    
587     #endif /* VIDEO_VOSF_H */