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

File Contents

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