ViewVC Help
View File | Revision Log | Show Annotations | Revision Graph | Root Listing
root/cebix/BasiliskII/src/Unix/video_vosf.h
Revision: 1.57
Committed: 2006-05-13T16:48:47Z (18 years, 6 months ago) by gbeauche
Content type: text/plain
Branch: MAIN
Changes since 1.56: +81 -3 lines
Log Message:
Use NQD dirty boxes, VOSF backend
+ make video_vosf_profitable() NQD accel aware
+ call vm_protect() only if PFLAG_CLEAR(page)

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