ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.60
Committed: 2007-06-16T07:24:39Z (17 years, 3 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.59: +7 -9 lines
Log Message:
Improve heuristic for VOSF profitability: we should not spend more than
the half of a video interrupt quantum for updating the screen. Also improve
the wording of the result.

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.56 #ifdef TEST_VOSF_PERFORMANCE
35     #define MONITOR_INIT /* nothing */
36     #else
37 gbeauche 1.38 #ifdef USE_SDL_VIDEO
38     #define MONITOR_INIT SDL_monitor_desc &monitor
39 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT driver_window *drv
40 gbeauche 1.52 #define VIDEO_DRV_DGA_INIT driver_fullscreen *drv
41 gbeauche 1.55 #define VIDEO_DRV_LOCK_PIXELS SDL_VIDEO_LOCK_SURFACE(drv->s)
42     #define VIDEO_DRV_UNLOCK_PIXELS SDL_VIDEO_UNLOCK_SURFACE(drv->s)
43 gbeauche 1.51 #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 gbeauche 1.38 #else
48 gbeauche 1.39 #ifdef SHEEPSHAVER
49 gbeauche 1.38 #define MONITOR_INIT /* nothing */
50 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT /* nothing */
51     #define VIDEO_DRV_DGA_INIT /* nothing */
52 gbeauche 1.33 #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 gbeauche 1.38 #define MONITOR_INIT X11_monitor_desc &monitor
58 gbeauche 1.51 #define VIDEO_DRV_WIN_INIT driver_window *drv
59     #define VIDEO_DRV_DGA_INIT driver_dga *drv
60 gbeauche 1.33 #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 gbeauche 1.38 #endif
65     #define VIDEO_DRV_LOCK_PIXELS /* nothing */
66     #define VIDEO_DRV_UNLOCK_PIXELS /* nothing */
67 gbeauche 1.51 #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 gbeauche 1.38 #define VIDEO_DRV_ROW_BYTES VIDEO_DRV_IMAGE->bytes_per_line
71 gbeauche 1.33 #endif
72 gbeauche 1.56 #endif
73 gbeauche 1.33
74 gbeauche 1.57 // Prototypes
75     static void vosf_do_set_dirty_area(uintptr first, uintptr last);
76 gbeauche 1.59 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 gbeauche 1.57
78 cebix 1.19 // Variables for Video on SEGV support
79 gbeauche 1.20 static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
80 cebix 1.19
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 gbeauche 1.27 uintptr pageSize; // Size of a page
90 cebix 1.19 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 gbeauche 1.50 bool very_dirty; // Flag: set if the frame buffer was completely modified (e.g. colormap changes)
95 cebix 1.19 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 gbeauche 1.50 mainBuffer.very_dirty = false; \
141     } while (0)
142    
143     #define PFLAG_SET_VERY_DIRTY do { \
144     mainBuffer.very_dirty = true; \
145 cebix 1.19 } 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 gbeauche 1.54 #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 gbeauche 1.46 #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 gbeauche 1.54 #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 cebix 1.19 #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 gbeauche 1.20 // Extend size to page boundary
208     static uint32 page_extend(uint32 size)
209     {
210 gbeauche 1.47 const uint32 page_size = vm_get_page_size();
211 gbeauche 1.20 const uint32 page_mask = page_size - 1;
212     return (size + page_mask) & ~page_mask;
213     }
214    
215 cebix 1.19
216     /*
217 gbeauche 1.40 * Check if VOSF acceleration is profitable on this platform
218     */
219    
220 gbeauche 1.41 const int VOSF_PROFITABLE_TRIES = 3; // Make 3 attempts for full screen update
221 gbeauche 1.60 const int VOSF_PROFITABLE_THRESHOLD = 16667/2; // 60 Hz (half of the quantum)
222 gbeauche 1.40
223     static bool video_vosf_profitable(void)
224     {
225 gbeauche 1.60 uint32 duration = 0;
226     const uint32 n_page_faults = mainBuffer.pageCount * VOSF_PROFITABLE_TRIES;
227 gbeauche 1.40
228 gbeauche 1.57 #ifdef SHEEPSHAVER
229     const bool accel = PrefsFindBool("gfxaccel");
230     #else
231     const bool accel = false;
232     #endif
233    
234 gbeauche 1.41 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 gbeauche 1.57 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 gbeauche 1.41 }
243 gbeauche 1.60 uint64 elapsed = GetTicks_usec() - start;
244     duration += elapsed;
245 gbeauche 1.41
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 gbeauche 1.40 }
251    
252 gbeauche 1.60 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 gbeauche 1.40 }
255    
256    
257     /*
258 gbeauche 1.27 * Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
259 cebix 1.19 */
260    
261 gbeauche 1.38 static bool video_vosf_init(MONITOR_INIT)
262 cebix 1.19 {
263 gbeauche 1.42 VIDEO_MODE_INIT_MONITOR;
264 cebix 1.31
265 gbeauche 1.47 const uintptr page_size = vm_get_page_size();
266 gbeauche 1.27 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 gbeauche 1.29 mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
282     if (mainBuffer.dirtyPages == NULL)
283 gbeauche 1.27 return false;
284 cebix 1.19
285 gbeauche 1.27 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 gbeauche 1.29 mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
291     if (mainBuffer.pageInfo == NULL)
292 gbeauche 1.27 return false;
293    
294     uint32 a = 0;
295 cebix 1.28 for (unsigned i = 0; i < mainBuffer.pageCount; i++) {
296 gbeauche 1.33 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 gbeauche 1.27
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 cebix 1.19
321    
322 gbeauche 1.27 /*
323     * Deinitialize VOSF system
324     */
325 cebix 1.19
326 gbeauche 1.27 static void video_vosf_exit(void)
327     {
328 gbeauche 1.29 if (mainBuffer.pageInfo) {
329     free(mainBuffer.pageInfo);
330     mainBuffer.pageInfo = NULL;
331 gbeauche 1.27 }
332 gbeauche 1.29 if (mainBuffer.dirtyPages) {
333     free(mainBuffer.dirtyPages);
334     mainBuffer.dirtyPages = NULL;
335 cebix 1.19 }
336     }
337    
338    
339 gbeauche 1.1 /*
340 gbeauche 1.57 * 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 gbeauche 1.58 uint8 *addr = (uint8 *)(first & -mainBuffer.pageSize);
348 gbeauche 1.57 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 gbeauche 1.59 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 gbeauche 1.57 {
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 gbeauche 1.59 if (x + w > screen_width)
370     w -= (x + w) - screen_width;
371     if (y + h > screen_height)
372     h -= (y + h) - screen_height;
373 gbeauche 1.57 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 gbeauche 1.20 * Screen fault handler
408 gbeauche 1.1 */
409    
410 gbeauche 1.33 bool Screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
411 gbeauche 1.1 {
412 gbeauche 1.16 const uintptr addr = (uintptr)fault_address;
413    
414 gbeauche 1.11 /* Someone attempted to write to the frame buffer. Make it writeable
415 gbeauche 1.20 * now so that the data could actually be written to. It will be made
416 gbeauche 1.11 * read-only back in one of the screen update_*() functions.
417     */
418 gbeauche 1.27 if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
419     const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
420 gbeauche 1.11 LOCK_VOSF;
421 gbeauche 1.57 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 gbeauche 1.13 mainBuffer.dirty = true;
426 gbeauche 1.11 UNLOCK_VOSF;
427 gbeauche 1.16 return true;
428 gbeauche 1.1 }
429    
430 gbeauche 1.11 /* Otherwise, we don't know how to handle the fault, let it crash */
431 gbeauche 1.16 return false;
432 gbeauche 1.1 }
433    
434 gbeauche 1.20
435 gbeauche 1.1 /*
436     * Update display for Windowed mode and VOSF
437     */
438    
439 gbeauche 1.12 /* 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 gbeauche 1.13 The update routines must determine which pages have to be blitted to the
454 gbeauche 1.12 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 gbeauche 1.13 There are two cases to check:
459 gbeauche 1.12
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 gbeauche 1.56 #ifndef TEST_VOSF_PERFORMANCE
473 gbeauche 1.52 static void update_display_window_vosf(VIDEO_DRV_WIN_INIT)
474 gbeauche 1.1 {
475 gbeauche 1.33 VIDEO_MODE_INIT;
476 cebix 1.31
477 gbeauche 1.1 int page = 0;
478     for (;;) {
479 cebix 1.28 const unsigned first_page = find_next_page_set(page);
480 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
481 gbeauche 1.1 break;
482 gbeauche 1.11
483     page = find_next_page_clear(first_page);
484     PFLAG_CLEAR_RANGE(first_page, page);
485 cebix 1.7
486 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
490 gbeauche 1.1
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 gbeauche 1.38
496 gbeauche 1.49 // Update the_host_buffer
497 gbeauche 1.38 VIDEO_DRV_LOCK_PIXELS;
498 gbeauche 1.49 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 gbeauche 1.1 }
506 gbeauche 1.38 VIDEO_DRV_UNLOCK_PIXELS;
507    
508     #ifdef USE_SDL_VIDEO
509     SDL_UpdateRect(drv->s, 0, y1, VIDEO_MODE_X, height);
510     #else
511 gbeauche 1.33 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 gbeauche 1.1 else
514 gbeauche 1.33 XPutImage(x_display, VIDEO_DRV_WINDOW, VIDEO_DRV_GC, VIDEO_DRV_IMAGE, 0, y1, 0, y1, VIDEO_MODE_X, height);
515 gbeauche 1.38 #endif
516 gbeauche 1.1 }
517 gbeauche 1.13 mainBuffer.dirty = false;
518 gbeauche 1.1 }
519 gbeauche 1.56 #endif
520 gbeauche 1.1
521    
522     /*
523     * Update display for DGA mode and VOSF
524 gbeauche 1.20 * (only in Real or Direct Addressing mode)
525 gbeauche 1.1 */
526    
527 gbeauche 1.56 #ifndef TEST_VOSF_PERFORMANCE
528 gbeauche 1.1 #if REAL_ADDRESSING || DIRECT_ADDRESSING
529 gbeauche 1.52 static void update_display_dga_vosf(VIDEO_DRV_DGA_INIT)
530 gbeauche 1.1 {
531 gbeauche 1.33 VIDEO_MODE_INIT;
532 cebix 1.31
533 gbeauche 1.51 // 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 gbeauche 1.50 if (mainBuffer.very_dirty) {
542     PFLAG_CLEAR_ALL;
543     vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ);
544 gbeauche 1.51 memcpy(the_buffer_copy, the_buffer, VIDEO_MODE_ROW_BYTES * VIDEO_MODE_Y);
545 gbeauche 1.50 VIDEO_DRV_LOCK_PIXELS;
546 gbeauche 1.51 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 gbeauche 1.50 VIDEO_DRV_UNLOCK_PIXELS;
556     return;
557     }
558    
559 gbeauche 1.51 // 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 gbeauche 1.53 const int n_pixels_left = VIDEO_MODE_X - (n_chunks * n_pixels);
563 gbeauche 1.51 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 gbeauche 1.52 int page = 0, last_scanline = -1;
569 gbeauche 1.1 for (;;) {
570 cebix 1.28 const unsigned first_page = find_next_page_set(page);
571 gbeauche 1.11 if (first_page >= mainBuffer.pageCount)
572 gbeauche 1.1 break;
573 gbeauche 1.11
574     page = find_next_page_clear(first_page);
575     PFLAG_CLEAR_RANGE(first_page, page);
576    
577 gbeauche 1.1 // 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 gbeauche 1.17 vm_protect((char *)mainBuffer.memStart + offset, length, VM_PAGE_READ);
581 gbeauche 1.49
582 gbeauche 1.52 // 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 gbeauche 1.50 int i1 = y1 * src_bytes_per_row;
593 gbeauche 1.51 int i2 = y1 * scr_bytes_per_row;
594 gbeauche 1.53 #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 gbeauche 1.50 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 gbeauche 1.51 #ifdef USE_SDL_VIDEO
609 gbeauche 1.53 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 gbeauche 1.51 #endif
620 gbeauche 1.50 }
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 gbeauche 1.53 #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 gbeauche 1.50 }
644 gbeauche 1.51 i2 += scr_bytes_left;
645 gbeauche 1.53 #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 gbeauche 1.50 }
655 gbeauche 1.53 #ifdef USE_SDL_VIDEO
656     SDL_UpdateRects(drv->s, bbi, bb);
657     #endif
658 gbeauche 1.50 VIDEO_DRV_UNLOCK_PIXELS;
659 gbeauche 1.1 }
660 gbeauche 1.13 mainBuffer.dirty = false;
661 gbeauche 1.1 }
662     #endif
663 gbeauche 1.56 #endif
664 gbeauche 1.1
665     #endif /* ENABLE_VOSF */
666    
667     #endif /* VIDEO_VOSF_H */