ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.54
Committed: 2005-06-25T11:40:29Z (19 years, 4 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.53: +8 -8 lines
Log Message:
Use fast spinlocks only for small enough atomic operations. Otherwise, you
run into some performance problems in e.g. video graphics experience because
of busywaits in the current spin_lock() implementation.

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