ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.46
Committed: 2004-12-11T10:20:32Z (19 years, 6 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.45: +9 -4 lines
Log Message:
remove obsolete headers inclusion, implement vosf lock with native windows
mutexes, use new vm_page_size() accessor

File Contents

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