ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.51
Committed: 2005-05-12T11:09:31Z (19 years, 6 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.50: +46 -87 lines
Log Message:
Fix DGA when visible screen is smaller than virtual display, aka fix
fullscreen mode when VidMode extension is enabled. Also fix SDL fullscreen
to really update the screen as this is necessary by default on Linux since
simple windowed is used (and not DGA for fullscreen).

Always prefer the 64 pixel chunks update code.

Rearrange B2 video_x.cpp to match video_vosf.h updates

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