ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.50
Committed: 2005-04-02T09:50:17Z (19 years, 3 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.49: +55 -10 lines
Log Message:
Rewrite update_display_dga_vosf() for much improved performance and cope
with palette changes. Also slightly improve the generic code when we need
to further update the screen (e.g. SDL without hardware surface)

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