ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.62
Committed: 2008-01-01T09:40:33Z (16 years, 10 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.61: +1 -1 lines
Log Message:
Happy New Year!

File Contents

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