ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.64
Committed: 2008-01-20T11:52:13Z (16 years, 4 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
CVS Tags: HEAD
Changes since 1.63: +1 -1 lines
Log Message:
Err, restore a reasonnable value for the emulators. ;-)

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 #ifndef VOSF_PROFITABLE_TRIES
221 #define VOSF_PROFITABLE_TRIES VOSF_PROFITABLE_TRIES_DFL
222 #endif
223 const int VOSF_PROFITABLE_TRIES_DFL = 3; // Make 3 attempts for full screen update
224 const int VOSF_PROFITABLE_THRESHOLD = 16667/2; // 60 Hz (half of the quantum)
225
226 static bool video_vosf_profitable(uint32 *duration_p = NULL, uint32 *n_page_faults_p = NULL)
227 {
228 uint32 duration = 0;
229 uint32 n_tries = VOSF_PROFITABLE_TRIES;
230 const uint32 n_page_faults = mainBuffer.pageCount * n_tries;
231
232 #ifdef SHEEPSHAVER
233 const bool accel = PrefsFindBool("gfxaccel");
234 #else
235 const bool accel = false;
236 #endif
237
238 for (int i = 0; i < n_tries; i++) {
239 uint64 start = GetTicks_usec();
240 for (int p = 0; p < mainBuffer.pageCount; p++) {
241 uint8 *addr = (uint8 *)(mainBuffer.memStart + (p * mainBuffer.pageSize));
242 if (accel)
243 vosf_do_set_dirty_area((uintptr)addr, (uintptr)addr + mainBuffer.pageSize - 1);
244 else
245 addr[0] = 0; // Trigger Screen_fault_handler()
246 }
247 uint64 elapsed = GetTicks_usec() - start;
248 duration += elapsed;
249
250 PFLAG_CLEAR_ALL;
251 mainBuffer.dirty = false;
252 if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
253 return false;
254 }
255
256 if (duration_p)
257 *duration_p = duration;
258 if (n_page_faults_p)
259 *n_page_faults_p = n_page_faults;
260
261 D(bug("Triggered %d page faults in %ld usec (%.1f usec per fault)\n", n_page_faults, duration, double(duration) / double(n_page_faults)));
262 return ((duration / n_tries) < (VOSF_PROFITABLE_THRESHOLD * (frame_skip ? frame_skip : 1)));
263 }
264
265
266 /*
267 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
268 */
269
270 static bool video_vosf_init(MONITOR_INIT)
271 {
272 VIDEO_MODE_INIT_MONITOR;
273
274 const uintptr page_size = vm_get_page_size();
275 const uintptr page_mask = page_size - 1;
276
277 // Round up frame buffer base to page boundary
278 mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
279
280 // The frame buffer size shall already be aligned to page boundary (use page_extend)
281 mainBuffer.memLength = the_buffer_size;
282
283 mainBuffer.pageSize = page_size;
284 mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
285 mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
286
287 // The "2" more bytes requested are a safety net to insure the
288 // loops in the update routines will terminate.
289 // See "How can we deal with array overrun conditions ?" hereunder for further details.
290 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
291 if (mainBuffer.dirtyPages == NULL)
292 return false;
293
294 PFLAG_CLEAR_ALL;
295 PFLAG_CLEAR(mainBuffer.pageCount);
296 PFLAG_SET(mainBuffer.pageCount+1);
297
298 // Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
299 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
300 if (mainBuffer.pageInfo == NULL)
301 return false;
302
303 uint32 a = 0;
304 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
305 unsigned y1 = a / VIDEO_MODE_ROW_BYTES;
306 if (y1 >= VIDEO_MODE_Y)
307 y1 = VIDEO_MODE_Y - 1;
308
309 unsigned y2 = (a + mainBuffer.pageSize) / VIDEO_MODE_ROW_BYTES;
310 if (y2 >= VIDEO_MODE_Y)
311 y2 = VIDEO_MODE_Y - 1;
312
313 mainBuffer.pageInfo[i].top = y1;
314 mainBuffer.pageInfo[i].bottom = y2;
315
316 a += mainBuffer.pageSize;
317 if (a > mainBuffer.memLength)
318 a = mainBuffer.memLength;
319 }
320
321 // We can now write-protect the frame buffer
322 if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
323 return false;
324
325 // The frame buffer is sane, i.e. there is no write to it yet
326 mainBuffer.dirty = false;
327 return true;
328 }
329
330
331 /*
332 * Deinitialize VOSF system
333 */
334
335 static void video_vosf_exit(void)
336 {
337 if (mainBuffer.pageInfo) {
338 free(mainBuffer.pageInfo);
339 mainBuffer.pageInfo = NULL;
340 }
341 if (mainBuffer.dirtyPages) {
342 free(mainBuffer.dirtyPages);
343 mainBuffer.dirtyPages = NULL;
344 }
345 }
346
347
348 /*
349 * Update VOSF state with specified dirty area
350 */
351
352 static void vosf_do_set_dirty_area(uintptr first, uintptr last)
353 {
354 const int first_page = (first - mainBuffer.memStart) >> mainBuffer.pageBits;
355 const int last_page = (last - mainBuffer.memStart) >> mainBuffer.pageBits;
356 uint8 *addr = (uint8 *)(first & -mainBuffer.pageSize);
357 for (int i = first_page; i <= last_page; i++) {
358 if (PFLAG_ISCLEAR(i)) {
359 PFLAG_SET(i);
360 vm_protect(addr, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
361 }
362 addr += mainBuffer.pageSize;
363 }
364 }
365
366 static void vosf_set_dirty_area(int x, int y, int w, int h, int screen_width, int screen_height, int bytes_per_row)
367 {
368 if (x < 0) {
369 w -= -x;
370 x = 0;
371 }
372 if (y < 0) {
373 h -= -y;
374 y = 0;
375 }
376 if (w <= 0 || h <= 0)
377 return;
378 if (x + w > screen_width)
379 w -= (x + w) - screen_width;
380 if (y + h > screen_height)
381 h -= (y + h) - screen_height;
382 LOCK_VOSF;
383 if (bytes_per_row >= screen_width) {
384 const int bytes_per_pixel = bytes_per_row / screen_width;
385 if (bytes_per_row <= mainBuffer.pageSize) {
386 const uintptr a0 = mainBuffer.memStart + y * bytes_per_row + x * bytes_per_pixel;
387 const uintptr a1 = mainBuffer.memStart + (y + h - 1) * bytes_per_row + (x + w - 1) * bytes_per_pixel;
388 vosf_do_set_dirty_area(a0, a1);
389 } else {
390 for (int j = y; j < y + h; j++) {
391 const uintptr a0 = mainBuffer.memStart + j * bytes_per_row + x * bytes_per_pixel;
392 const uintptr a1 = a0 + (w - 1) * bytes_per_pixel;
393 vosf_do_set_dirty_area(a0, a1);
394 }
395 }
396 } else {
397 const int pixels_per_byte = screen_width / bytes_per_row;
398 if (bytes_per_row <= mainBuffer.pageSize) {
399 const uintptr a0 = mainBuffer.memStart + y * bytes_per_row + x / pixels_per_byte;
400 const uintptr a1 = mainBuffer.memStart + (y + h - 1) * bytes_per_row + (x + w - 1) / pixels_per_byte;
401 vosf_do_set_dirty_area(a0, a1);
402 } else {
403 for (int j = y; j < y + h; j++) {
404 const uintptr a0 = mainBuffer.memStart + j * bytes_per_row + x / pixels_per_byte;
405 const uintptr a1 = mainBuffer.memStart + j * bytes_per_row + (x + w - 1) / pixels_per_byte;
406 vosf_do_set_dirty_area(a0, a1);
407 }
408 }
409 }
410 mainBuffer.dirty = true;
411 UNLOCK_VOSF;
412 }
413
414
415 /*
416 * Screen fault handler
417 */
418
419 bool Screen_fault_handler(sigsegv_info_t *sip)
420 {
421 const uintptr addr = (uintptr)sigsegv_get_fault_address(sip);
422
423 /* Someone attempted to write to the frame buffer. Make it writeable
424 * now so that the data could actually be written to. It will be made
425 * read-only back in one of the screen update_*() functions.
426 */
427 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
428 const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
429 LOCK_VOSF;
430 if (PFLAG_ISCLEAR(page)) {
431 PFLAG_SET(page);
432 vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
433 }
434 mainBuffer.dirty = true;
435 UNLOCK_VOSF;
436 return true;
437 }
438
439 /* Otherwise, we don't know how to handle the fault, let it crash */
440 return false;
441 }
442
443
444 /*
445 * Update display for Windowed mode and VOSF
446 */
447
448 /* How can we deal with array overrun conditions ?
449
450 The state of the framebuffer pages that have been touched are maintained
451 in the dirtyPages[] table. That table is (pageCount + 2) bytes long.
452
453 Terminology
454
455 "Last Page" denotes the pageCount-nth page, i.e. dirtyPages[pageCount - 1].
456 "CLEAR Page Guard" refers to the page following the Last Page but is always
457 in the CLEAR state. "SET Page Guard" refers to the page following the CLEAR
458 Page Guard but is always in the SET state.
459
460 Rough process
461
462 The update routines must determine which pages have to be blitted to the
463 screen. This job consists in finding the first_page that was touched.
464 i.e. find the next page that is SET. Then, finding how many pages were
465 touched starting from first_page. i.e. find the next page that is CLEAR.
466
467 There are two cases to check:
468
469 - Last Page is CLEAR: find_next_page_set() will reach the SET Page Guard
470 but it is beyond the valid pageCount value. Therefore, we exit from the
471 update routine.
472
473 - Last Page is SET: first_page equals (pageCount - 1) and
474 find_next_page_clear() will reach the CLEAR Page Guard. We blit the last
475 page to the screen. On the next iteration, page equals pageCount and
476 find_next_page_set() will reach the SET Page Guard. We still safely exit
477 from the update routine because the SET Page Guard position is greater
478 than pageCount.
479 */
480
481 #ifndef TEST_VOSF_PERFORMANCE
482 static void update_display_window_vosf(VIDEO_DRV_WIN_INIT)
483 {
484 VIDEO_MODE_INIT;
485
486 int page = 0;
487 for (;;) {
488 const unsigned first_page = find_next_page_set(page);
489 if (first_page >= mainBuffer.pageCount)
490 break;
491
492 page = find_next_page_clear(first_page);
493 PFLAG_CLEAR_RANGE(first_page, page);
494
495 // Make the dirty pages read-only again
496 const int32 offset = first_page << mainBuffer.pageBits;
497 const uint32 length = (page - first_page) << mainBuffer.pageBits;
498 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
499
500 // There is at least one line to update
501 const int y1 = mainBuffer.pageInfo[first_page].top;
502 const int y2 = mainBuffer.pageInfo[page - 1].bottom;
503 const int height = y2 - y1 + 1;
504
505 // Update the_host_buffer
506 VIDEO_DRV_LOCK_PIXELS;
507 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
508 const int dst_bytes_per_row = VIDEO_DRV_ROW_BYTES;
509 int i1 = y1 * src_bytes_per_row, i2 = y1 * dst_bytes_per_row, j;
510 for (j = y1; j <= y2; j++) {
511 Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
512 i1 += src_bytes_per_row;
513 i2 += dst_bytes_per_row;
514 }
515 VIDEO_DRV_UNLOCK_PIXELS;
516
517 #ifdef USE_SDL_VIDEO
518 SDL_UpdateRect(drv->s, 0, y1, VIDEO_MODE_X, height);
519 #else
520 if (VIDEO_DRV_HAVE_SHM)
521 XShmPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height, 0);
522 else
523 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
524 #endif
525 }
526 mainBuffer.dirty = false;
527 }
528 #endif
529
530
531 /*
532 * Update display for DGA mode and VOSF
533 * (only in Real or Direct Addressing mode)
534 */
535
536 #ifndef TEST_VOSF_PERFORMANCE
537 #if REAL_ADDRESSING || DIRECT_ADDRESSING
538 static void update_display_dga_vosf(VIDEO_DRV_DGA_INIT)
539 {
540 VIDEO_MODE_INIT;
541
542 // Compute number of bytes per row, take care to virtual screens
543 const int src_bytes_per_row = VIDEO_MODE_ROW_BYTES;
544 const int dst_bytes_per_row = TrivialBytesPerRow(VIDEO_MODE_X, DepthModeForPixelDepth(VIDEO_DRV_DEPTH));
545 const int scr_bytes_per_row = VIDEO_DRV_ROW_BYTES;
546 assert(dst_bytes_per_row <= scr_bytes_per_row);
547 const int scr_bytes_left = scr_bytes_per_row - dst_bytes_per_row;
548
549 // Full screen update requested?
550 if (mainBuffer.very_dirty) {
551 PFLAG_CLEAR_ALL;
552 vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ);
553 memcpy(the_buffer_copy, the_buffer, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
554 VIDEO_DRV_LOCK_PIXELS;
555 int i1 = 0, i2 = 0;
556 for (int j = 0; j < VIDEO_MODE_Y; j++) {
557 Screen_blit(the_host_buffer + i2, the_buffer + i1, src_bytes_per_row);
558 i1 += src_bytes_per_row;
559 i2 += scr_bytes_per_row;
560 }
561 #ifdef USE_SDL_VIDEO
562 SDL_UpdateRect(drv->s, 0, 0, VIDEO_MODE_X, VIDEO_MODE_Y);
563 #endif
564 VIDEO_DRV_UNLOCK_PIXELS;
565 return;
566 }
567
568 // Setup partial blitter (use 64-pixel wide chunks)
569 const int n_pixels = 64;
570 const int n_chunks = VIDEO_MODE_X / n_pixels;
571 const int n_pixels_left = VIDEO_MODE_X - (n_chunks * n_pixels);
572 const int src_chunk_size = src_bytes_per_row / n_chunks;
573 const int dst_chunk_size = dst_bytes_per_row / n_chunks;
574 const int src_chunk_size_left = src_bytes_per_row - (n_chunks * src_chunk_size);
575 const int dst_chunk_size_left = dst_bytes_per_row - (n_chunks * dst_chunk_size);
576
577 int page = 0, last_scanline = -1;
578 for (;;) {
579 const unsigned first_page = find_next_page_set(page);
580 if (first_page >= mainBuffer.pageCount)
581 break;
582
583 page = find_next_page_clear(first_page);
584 PFLAG_CLEAR_RANGE(first_page, page);
585
586 // Make the dirty pages read-only again
587 const int32 offset = first_page << mainBuffer.pageBits;
588 const uint32 length = (page - first_page) << mainBuffer.pageBits;
589 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
590
591 // Optimized for scanlines, don't process overlapping lines again
592 int y1 = mainBuffer.pageInfo[first_page].top;
593 int y2 = mainBuffer.pageInfo[page - 1].bottom;
594 if (y1 <= last_scanline && ++y1 >= VIDEO_MODE_Y)
595 continue;
596 if (y2 <= last_scanline && ++y2 >= VIDEO_MODE_Y)
597 continue;
598 last_scanline = y2;
599
600 // Update the_host_buffer and copy of the_buffer, one line at a time
601 int i1 = y1 * src_bytes_per_row;
602 int i2 = y1 * scr_bytes_per_row;
603 #ifdef USE_SDL_VIDEO
604 int bbi = 0;
605 SDL_Rect bb[3] = {
606 { VIDEO_MODE_X, y1, 0, 0 },
607 { VIDEO_MODE_X, -1, 0, 0 },
608 { VIDEO_MODE_X, -1, 0, 0 }
609 };
610 #endif
611 VIDEO_DRV_LOCK_PIXELS;
612 for (int j = y1; j <= y2; j++) {
613 for (int i = 0; i < n_chunks; i++) {
614 if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size) != 0) {
615 memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size);
616 Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size);
617 #ifdef USE_SDL_VIDEO
618 const int x = i * n_pixels;
619 if (x < bb[bbi].x) {
620 if (bb[bbi].w)
621 bb[bbi].w += bb[bbi].x - x;
622 else
623 bb[bbi].w = n_pixels;
624 bb[bbi].x = x;
625 }
626 else if (x >= bb[bbi].x + bb[bbi].w)
627 bb[bbi].w = x + n_pixels - bb[bbi].x;
628 #endif
629 }
630 i1 += src_chunk_size;
631 i2 += dst_chunk_size;
632 }
633 if (src_chunk_size_left && dst_chunk_size_left) {
634 if (memcmp(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left) != 0) {
635 memcpy(the_buffer_copy + i1, the_buffer + i1, src_chunk_size_left);
636 Screen_blit(the_host_buffer + i2, the_buffer + i1, src_chunk_size_left);
637 }
638 i1 += src_chunk_size_left;
639 i2 += dst_chunk_size_left;
640 #ifdef USE_SDL_VIDEO
641 const int x = n_chunks * n_pixels;
642 if (x < bb[bbi].x) {
643 if (bb[bbi].w)
644 bb[bbi].w += bb[bbi].x - x;
645 else
646 bb[bbi].w = n_pixels_left;
647 bb[bbi].x = x;
648 }
649 else if (x >= bb[bbi].x + bb[bbi].w)
650 bb[bbi].w = x + n_pixels_left - bb[bbi].x;
651 #endif
652 }
653 i2 += scr_bytes_left;
654 #ifdef USE_SDL_VIDEO
655 bb[bbi].h++;
656 if (bb[bbi].w && (j == y1 || j == y2 - 1 || j == y2)) {
657 bbi++;
658 assert(bbi <= 3);
659 if (j != y2)
660 bb[bbi].y = j + 1;
661 }
662 #endif
663 }
664 #ifdef USE_SDL_VIDEO
665 SDL_UpdateRects(drv->s, bbi, bb);
666 #endif
667 VIDEO_DRV_UNLOCK_PIXELS;
668 }
669 mainBuffer.dirty = false;
670 }
671 #endif
672 #endif
673
674 #endif /* ENABLE_VOSF */
675
676 #endif /* VIDEO_VOSF_H */