ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.63
Committed: 2008-01-20T11:51:08Z (16 years, 9 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.62: +14 -5 lines
Log Message:
Arrangements for the SIGSEGV perf tester.

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