- added video_vosf_init()/video_vosf_exit() for initialization and destruction

of the internal structures used for the VOSF system
- use vm_acquire()/vm_release() for VOSF buffers and hope the_buffer is
  allocated above RAM address space (temporary workaround for 64-bit
  addressing systems)
- don't free() screen buffers in driver_base dtor
- don't free() memory mapped buffers in driver_base dtor
This commit is contained in:
gbeauche 2001-07-07 09:14:47 +00:00
parent 054e33c8fc
commit 8d733ed691
2 changed files with 150 additions and 93 deletions

View File

@ -35,19 +35,16 @@
// Variables for Video on SEGV support
static uint8 *the_host_buffer; // Host frame buffer in VOSF mode
static uint32 the_buffer_size; // Size of allocated the_buffer
struct ScreenPageInfo {
int top, bottom; // Mapping between this virtual page and Mac scanlines
};
struct ScreenInfo {
uintptr memBase; // Real start address
uintptr memStart; // Start address aligned to page boundary
uintptr memEnd; // Address of one-past-the-end of the screen
uint32 memLength; // Length of the memory addressed by the screen pages
uint32 pageSize; // Size of a page
uintptr pageSize; // Size of a page
int pageBits; // Shift count to get the page number
uint32 pageCount; // Number of pages allocated to the screen
@ -161,104 +158,118 @@ static uint32 page_extend(uint32 size)
/*
* Initialize mainBuffer structure
* Initialize the VOSF system (mainBuffer structure, SIGSEGV handler)
*/
static bool video_init_buffer(void)
static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction);
static bool video_vosf_init(void)
{
if (use_vosf) {
const uint32 page_size = getpagesize();
const uint32 page_mask = page_size - 1;
const uintptr page_size = getpagesize();
const uintptr page_mask = page_size - 1;
mainBuffer.memBase = (uintptr) the_buffer;
// Round up frame buffer base to page boundary
mainBuffer.memStart = (uintptr)((((unsigned long) the_buffer) + page_mask) & ~page_mask);
mainBuffer.memLength = the_buffer_size;
mainBuffer.memEnd = mainBuffer.memStart + mainBuffer.memLength;
// Round up frame buffer base to page boundary
mainBuffer.memStart = (((uintptr) the_buffer) + page_mask) & ~page_mask;
mainBuffer.pageSize = page_size;
mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
// The frame buffer size shall already be aligned to page boundary (use page_extend)
mainBuffer.memLength = the_buffer_size;
if (mainBuffer.dirtyPages) {
free(mainBuffer.dirtyPages);
mainBuffer.dirtyPages = NULL;
}
mainBuffer.pageSize = page_size;
mainBuffer.pageBits = log_base_2(mainBuffer.pageSize);
mainBuffer.pageCount = (mainBuffer.memLength + page_mask)/mainBuffer.pageSize;
mainBuffer.dirtyPages = (char *) malloc(mainBuffer.pageCount + 2);
// The "2" more bytes requested are a safety net to insure the
// loops in the update routines will terminate.
// See "How can we deal with array overrun conditions ?" hereunder for further details.
mainBuffer.dirtyPages = (char *) vm_acquire(mainBuffer.pageCount + 2);
if (mainBuffer.dirtyPages == VM_MAP_FAILED)
return false;
if (mainBuffer.pageInfo) {
free(mainBuffer.pageInfo);
mainBuffer.pageInfo = NULL;
}
PFLAG_CLEAR_ALL;
PFLAG_CLEAR(mainBuffer.pageCount);
PFLAG_SET(mainBuffer.pageCount+1);
mainBuffer.pageInfo = (ScreenPageInfo *) malloc(mainBuffer.pageCount * sizeof(ScreenPageInfo));
// Allocate and fill in pageInfo with start and end (inclusive) row in number of bytes
mainBuffer.pageInfo = (ScreenPageInfo *) vm_acquire(mainBuffer.pageCount * sizeof(ScreenPageInfo));
if (mainBuffer.pageInfo == VM_MAP_FAILED)
return false;
if ((mainBuffer.dirtyPages == NULL) || (mainBuffer.pageInfo == NULL))
return false;
uint32 a = 0;
for (int i = 0; i < mainBuffer.pageCount; i++) {
int y1 = a / VideoMonitor.mode.bytes_per_row;
if (y1 >= VideoMonitor.mode.y)
y1 = VideoMonitor.mode.y - 1;
mainBuffer.dirty = false;
int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row;
if (y2 >= VideoMonitor.mode.y)
y2 = VideoMonitor.mode.y - 1;
PFLAG_CLEAR_ALL;
// Safety net to insure the loops in the update routines will terminate
// See "How can we deal with array overrun conditions ?" hereunder for further details
PFLAG_CLEAR(mainBuffer.pageCount);
PFLAG_SET(mainBuffer.pageCount+1);
mainBuffer.pageInfo[i].top = y1;
mainBuffer.pageInfo[i].bottom = y2;
uint32 a = 0;
for (int i = 0; i < mainBuffer.pageCount; i++) {
int y1 = a / VideoMonitor.mode.bytes_per_row;
if (y1 >= VideoMonitor.mode.y)
y1 = VideoMonitor.mode.y - 1;
int y2 = (a + mainBuffer.pageSize) / VideoMonitor.mode.bytes_per_row;
if (y2 >= VideoMonitor.mode.y)
y2 = VideoMonitor.mode.y - 1;
mainBuffer.pageInfo[i].top = y1;
mainBuffer.pageInfo[i].bottom = y2;
a += mainBuffer.pageSize;
if (a > mainBuffer.memLength)
a = mainBuffer.memLength;
}
// We can now write-protect the frame buffer
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
return false;
a += mainBuffer.pageSize;
if (a > mainBuffer.memLength)
a = mainBuffer.memLength;
}
// We can now write-protect the frame buffer
if (vm_protect((char *)mainBuffer.memStart, mainBuffer.memLength, VM_PAGE_READ) != 0)
return false;
// Initialize the handler for SIGSEGV
if (!sigsegv_install_handler(screen_fault_handler))
return false;
// The frame buffer is sane, i.e. there is no write to it yet
mainBuffer.dirty = false;
return true;
}
/*
* Deinitialize VOSF system
*/
static void video_vosf_exit(void)
{
if (mainBuffer.pageInfo != VM_MAP_FAILED) {
vm_release(mainBuffer.pageInfo, mainBuffer.pageCount * sizeof(ScreenPageInfo));
mainBuffer.pageInfo = (ScreenPageInfo *) VM_MAP_FAILED;
}
if (mainBuffer.dirtyPages != VM_MAP_FAILED) {
vm_release(mainBuffer.dirtyPages, mainBuffer.pageCount + 2);
mainBuffer.dirtyPages = (char *) VM_MAP_FAILED;
}
}
/*
* Screen fault handler
*/
static bool screen_fault_handler(sigsegv_address_t fault_address, sigsegv_address_t fault_instruction)
{
// D(bug("screen_fault_handler: ADDR=0x%08X from IP=0x%08X\n", fault_address, fault_instruction));
// D(bug("screen_fault_handler: ADDR=%p from IP=%p\n", fault_address, fault_instruction));
const uintptr addr = (uintptr)fault_address;
/* Someone attempted to write to the frame buffer. Make it writeable
* now so that the data could actually be written to. It will be made
* read-only back in one of the screen update_*() functions.
*/
if ((addr >= mainBuffer.memStart) && (addr < mainBuffer.memEnd)) {
const int page = (addr - mainBuffer.memStart) >> mainBuffer.pageBits;
caddr_t page_ad = (caddr_t)(addr & -mainBuffer.pageSize);
if (((uintptr)addr - mainBuffer.memStart) < mainBuffer.memLength) {
const int page = ((uintptr)addr - mainBuffer.memStart) >> mainBuffer.pageBits;
LOCK_VOSF;
PFLAG_SET(page);
vm_protect((char *)page_ad, mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
vm_protect((char *)(addr & -mainBuffer.pageSize), mainBuffer.pageSize, VM_PAGE_READ | VM_PAGE_WRITE);
mainBuffer.dirty = true;
UNLOCK_VOSF;
return true;
}
/* Otherwise, we don't know how to handle the fault, let it crash */
fprintf(stderr, "do_handle_screen_fault: unhandled address 0x%08X", addr);
fprintf(stderr, "do_handle_screen_fault: unhandled address %p", fault_address);
if (fault_instruction != SIGSEGV_INVALID_PC)
fprintf(stderr, " [IP=0x%08X]", fault_instruction);
fprintf(stderr, " [IP=%p]", fault_instruction);
fprintf(stderr, "\n");
#if EMULATED_68K
uaecptr nextpc;

View File

@ -93,6 +93,7 @@ static int display_type = DISPLAY_WINDOW; // See enum above
static bool local_X11; // Flag: X server running on local machine?
static uint8 *the_buffer = NULL; // Mac frame buffer (where MacOS draws into)
static uint8 *the_buffer_copy = NULL; // Copy of Mac frame buffer (for refreshed modes)
static uint32 the_buffer_size; // Size of allocated the_buffer
static bool redraw_thread_active = false; // Flag: Redraw thread installed
#ifdef HAVE_PTHREADS
@ -509,12 +510,12 @@ driver_base::~driver_base()
free(the_host_buffer);
the_host_buffer = NULL;
}
if (the_buffer != (uint8 *)VM_MAP_FAILED) {
vm_release(the_buffer, the_buffer_size);
if (the_buffer) {
free(the_buffer);
the_buffer = NULL;
}
if (the_buffer_copy != (uint8 *)VM_MAP_FAILED) {
vm_release(the_buffer_copy, the_buffer_size);
if (the_buffer_copy) {
free(the_buffer_copy);
the_buffer_copy = NULL;
}
}
@ -629,6 +630,7 @@ driver_window::driver_window(const video_mode &mode)
}
#ifdef ENABLE_VOSF
use_vosf = true;
// Allocate memory for frame buffer (SIZE is extended to page-boundary)
the_host_buffer = the_buffer_copy;
the_buffer_size = page_extend((aligned_height + 2) * img->bytes_per_line);
@ -682,6 +684,19 @@ driver_window::~driver_window()
the_buffer_copy = NULL; // don't free() in driver_base dtor
#endif
}
#ifdef ENABLE_VOSF
if (use_vosf) {
// don't free() memory mapped buffers in driver_base dtor
if (the_buffer != VM_MAP_FAILED) {
vm_release(the_buffer, the_buffer_size);
the_buffer = NULL;
}
if (the_buffer_copy != VM_MAP_FAILED) {
vm_release(the_buffer_copy, the_buffer_size);
the_buffer_copy = NULL;
}
}
#endif
if (img)
XDestroyImage(img);
if (have_shm) {
@ -1001,8 +1016,9 @@ driver_fbdev::driver_fbdev(const video_mode &mode)
int bytes_per_row = TrivialBytesPerRow(mode.x, mode.depth);
// Map frame buffer
if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
if ((the_buffer = (uint8 *) mmap(NULL, height * bytes_per_row, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
the_buffer_size = height * bytes_per_row;
if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fbdev_fd, fb_offset)) == MAP_FAILED) {
if ((the_buffer = (uint8 *) mmap(NULL, the_buffer_size, PROT_READ | PROT_WRITE, MAP_SHARED, fbdev_fd, fb_offset)) == MAP_FAILED) {
char str[256];
sprintf(str, GetString(STR_FBDEV_MMAP_ERR), strerror(errno));
ErrorAlert(str);
@ -1041,6 +1057,30 @@ driver_fbdev::driver_fbdev(const video_mode &mode)
// Close display
driver_fbdev::~driver_fbdev()
{
if (!use_vosf) {
if (the_buffer != MAP_FAILED) {
// don't free() the screen buffer in driver_base dtor
munmap(the_buffer, the_buffer_size);
the_buffer = NULL;
}
}
#ifdef ENABLE_VOSF
else {
if (the_host_buffer != MAP_FAILED) {
// don't free() the screen buffer in driver_base dtor
munmap(the_host_buffer, the_buffer_size);
the_host_buffer = NULL;
}
if (the_buffer_copy != VM_MAP_FAILED) {
vm_release(the_buffer_copy, the_buffer_size);
the_buffer_copy = NULL;
}
if (the_buffer != VM_MAP_FAILED) {
vm_release(the_buffer, the_buffer_size);
the_buffer = NULL;
}
}
#endif
}
#endif
@ -1126,7 +1166,7 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode)
// Init blitting routines
int bytes_per_row = TrivialBytesPerRow((v_width + 7) & ~7, mode.depth);
#ifdef VIDEO_VOSF
#if VIDEO_VOSF
#if REAL_ADDRESSING || DIRECT_ADDRESSING
// Screen_blitter_init() returns TRUE if VOSF is mandatory
// i.e. the framebuffer update function is not Blit_Copy_Raw
@ -1157,6 +1197,25 @@ driver_xf86dga::driver_xf86dga(const video_mode &mode)
driver_xf86dga::~driver_xf86dga()
{
XF86DGADirectVideo(x_display, screen, 0);
if (!use_vosf) {
// don't free() the screen buffer in driver_base dtor
the_buffer = NULL;
}
#ifdef ENABLE_VOSF
else {
// don't free() the screen buffer in driver_base dtor
the_host_buffer = NULL;
if (the_buffer_copy != VM_MAP_FAILED) {
vm_release(the_buffer_copy, the_buffer_size);
the_buffer_copy = NULL;
}
if (the_buffer != VM_MAP_FAILED) {
vm_release(the_buffer, the_buffer_size);
the_buffer = NULL;
}
}
#endif
#ifdef ENABLE_XF86_VIDMODE
if (has_vidmode)
XF86VidModeSwitchToMode(x_display, screen, x_video_modes[0]);
@ -1343,17 +1402,11 @@ static bool video_open(const video_mode &mode)
#ifdef ENABLE_VOSF
if (use_vosf) {
// Initialize the mainBuffer structure
if (!video_init_buffer()) {
// Initialize the VOSF system
if (!video_vosf_init()) {
ErrorAlert(STR_VOSF_INIT_ERR);
return false;
}
// Initialize the handler for SIGSEGV
if (!sigsegv_install_handler(screen_fault_handler)) {
ErrorAlert("Could not initialize Video on SEGV signals");
return false;
}
}
#endif
@ -1569,16 +1622,9 @@ static void video_close(void)
XSync(x_display, false);
#ifdef ENABLE_VOSF
// Deinitialize VOSF
if (use_vosf) {
if (mainBuffer.pageInfo) {
free(mainBuffer.pageInfo);
mainBuffer.pageInfo = NULL;
}
if (mainBuffer.dirtyPages) {
free(mainBuffer.dirtyPages);
mainBuffer.dirtyPages = NULL;
}
// Deinitialize VOSF
video_vosf_exit();
}
#endif