Darrell added support for emulated SDL_DOUBLEBUF on MacOSX
--HG-- extra : convert_revision : svn%3Ac70aab31-4412-0410-b14c-859654838e24/trunk%40589
This commit is contained in:
parent
cfe943448d
commit
b489d8c975
2 changed files with 225 additions and 10 deletions
|
@ -53,7 +53,9 @@
|
|||
#include <Carbon/Carbon.h>
|
||||
#include <QuickTime/QuickTime.h>
|
||||
#include <IOKit/IOKitLib.h> /* For powersave handling */
|
||||
#include <pthread.h>
|
||||
|
||||
#include "SDL_thread.h"
|
||||
#include "SDL_video.h"
|
||||
#include "SDL_error.h"
|
||||
#include "SDL_timer.h"
|
||||
|
@ -137,6 +139,11 @@ typedef struct SDL_PrivateVideoData {
|
|||
Uint8 grab_state; /* used to manage grab behavior */
|
||||
NSPoint cursor_loc; /* saved cursor coords, for activate/deactivate when grabbed */
|
||||
BOOL cursor_visible; /* tells if cursor was hidden or not */
|
||||
Uint8* sw_buffers[2]; /* pointers to the two software buffers for double-buffer emulation */
|
||||
SDL_Thread *thread; /* thread for async updates to the screen */
|
||||
SDL_sem *sem1, *sem2; /* synchronization for async screen updates */
|
||||
Uint8 *current_buffer; /* the buffer being copied to the screen */
|
||||
BOOL quit_thread; /* used to quit the async blitting thread */
|
||||
|
||||
ImageDescriptionHandle yuv_idh;
|
||||
MatrixRecordPtr yuv_matrix;
|
||||
|
@ -176,6 +183,12 @@ typedef struct SDL_PrivateVideoData {
|
|||
#define grab_state (this->hidden->grab_state)
|
||||
#define cursor_loc (this->hidden->cursor_loc)
|
||||
#define cursor_visible (this->hidden->cursor_visible)
|
||||
#define sw_buffers (this->hidden->sw_buffers)
|
||||
#define thread (this->hidden->thread)
|
||||
#define sem1 (this->hidden->sem1)
|
||||
#define sem2 (this->hidden->sem2)
|
||||
#define current_buffer (this->hidden->current_buffer)
|
||||
#define quit_thread (this->hidden->quit_thread)
|
||||
|
||||
#define yuv_idh (this->hidden->yuv_idh)
|
||||
#define yuv_matrix (this->hidden->yuv_matrix)
|
||||
|
@ -262,6 +275,8 @@ extern CGSError CGSDisplayCanHWFill (CGDirectDisplayID id);
|
|||
|
||||
extern CGSError CGSGetMouseEnabledFlags (CGSConnectionID cid, CGSWindowID wid, int *flags);
|
||||
|
||||
int CGSDisplayHWSync (CGDirectDisplayID id);
|
||||
|
||||
/* Bootstrap functions */
|
||||
static int QZ_Available ();
|
||||
static SDL_VideoDevice* QZ_CreateDevice (int device_index);
|
||||
|
@ -280,6 +295,13 @@ static SDL_Surface* QZ_SetVideoMode (_THIS, SDL_Surface *current,
|
|||
static int QZ_ToggleFullScreen (_THIS, int on);
|
||||
static int QZ_SetColors (_THIS, int first_color,
|
||||
int num_colors, SDL_Color *colors);
|
||||
|
||||
static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface);
|
||||
static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface);
|
||||
static int QZ_ThreadFlip (_THIS);
|
||||
static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface);
|
||||
static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects);
|
||||
|
||||
static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects);
|
||||
static int QZ_LockWindow (_THIS, SDL_Surface *surface);
|
||||
static void QZ_UnlockWindow (_THIS, SDL_Surface *surface);
|
||||
|
|
|
@ -32,6 +32,7 @@ VideoBootStrap QZ_bootstrap = {
|
|||
"Quartz", "Mac OS X CoreGraphics", QZ_Available, QZ_CreateDevice
|
||||
};
|
||||
|
||||
|
||||
/* Bootstrap functions */
|
||||
static int QZ_Available () {
|
||||
return 1;
|
||||
|
@ -360,6 +361,16 @@ static void QZ_UnsetVideoMode (_THIS) {
|
|||
|
||||
gamma_error = QZ_FadeGammaOut (this, &gamma_table);
|
||||
|
||||
/* Release double buffer stuff */
|
||||
if ( mode_flags & (SDL_HWSURFACE|SDL_DOUBLEBUF)) {
|
||||
quit_thread = YES;
|
||||
SDL_SemPost (sem1);
|
||||
SDL_WaitThread (thread, NULL);
|
||||
SDL_DestroySemaphore (sem1);
|
||||
SDL_DestroySemaphore (sem2);
|
||||
free (sw_buffers[0]);
|
||||
}
|
||||
|
||||
/*
|
||||
Release the OpenGL context
|
||||
Do this first to avoid trash on the display before fade
|
||||
|
@ -372,7 +383,7 @@ static void QZ_UnsetVideoMode (_THIS) {
|
|||
|
||||
/* Restore original screen resolution/bpp */
|
||||
CGDisplaySwitchToMode (display_id, save_mode);
|
||||
CGDisplayRelease (display_id);
|
||||
CGReleaseAllDisplays ();
|
||||
ShowMenuBar ();
|
||||
|
||||
/*
|
||||
|
@ -408,6 +419,7 @@ static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int widt
|
|||
int gamma_error;
|
||||
SDL_QuartzGammaTable gamma_table;
|
||||
NSRect screen_rect;
|
||||
CGError error;
|
||||
|
||||
/* Destroy any previous mode */
|
||||
if (video_set == SDL_TRUE)
|
||||
|
@ -427,7 +439,12 @@ static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int widt
|
|||
gamma_error = QZ_FadeGammaOut (this, &gamma_table);
|
||||
|
||||
/* Put up the blanking window (a window above all other windows) */
|
||||
if ( CGDisplayNoErr != CGDisplayCapture (display_id) ) {
|
||||
if (getenv ("SDL_SINGLEDISPLAY"))
|
||||
error = CGDisplayCapture (display_id);
|
||||
else
|
||||
error = CGCaptureAllDisplays ();
|
||||
|
||||
if ( CGDisplayNoErr != error ) {
|
||||
SDL_SetError ("Failed capturing display");
|
||||
goto ERR_NO_CAPTURE;
|
||||
}
|
||||
|
@ -451,11 +468,41 @@ static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int widt
|
|||
this->UpdateRects = QZ_DirectUpdate;
|
||||
this->LockHWSurface = QZ_LockHWSurface;
|
||||
this->UnlockHWSurface = QZ_UnlockHWSurface;
|
||||
|
||||
/* Setup some mode-dependant info */
|
||||
if ( CGSDisplayCanHWFill (display_id) ) {
|
||||
this->info.blit_fill = 1;
|
||||
this->FillHWRect = QZ_FillHWRect;
|
||||
|
||||
/* Setup double-buffer emulation */
|
||||
if ( flags & SDL_DOUBLEBUF ) {
|
||||
|
||||
/*
|
||||
Setup a software backing store for reasonable results when
|
||||
double buffering is requested (since a single-buffered hardware
|
||||
surface looks hideous).
|
||||
|
||||
The actual screen blit occurs in a separate thread to allow
|
||||
other blitting while waiting on the VBL (and hence results in higher framerates).
|
||||
*/
|
||||
this->LockHWSurface = NULL;
|
||||
this->UnlockHWSurface = NULL;
|
||||
this->UpdateRects = NULL;
|
||||
|
||||
current->flags |= (SDL_HWSURFACE|SDL_DOUBLEBUF);
|
||||
this->UpdateRects = QZ_DoubleBufferUpdate;
|
||||
this->LockHWSurface = QZ_LockDoubleBuffer;
|
||||
this->UnlockHWSurface = QZ_UnlockDoubleBuffer;
|
||||
this->FlipHWSurface = QZ_FlipDoubleBuffer;
|
||||
|
||||
current->pixels = malloc (current->pitch * current->h * 2);
|
||||
if (current->pixels == NULL) {
|
||||
SDL_OutOfMemory ();
|
||||
goto ERR_DOUBLEBUF;
|
||||
}
|
||||
|
||||
sw_buffers[0] = current->pixels;
|
||||
sw_buffers[1] = (Uint8*)current->pixels + current->pitch * current->h;
|
||||
|
||||
quit_thread = NO;
|
||||
sem1 = SDL_CreateSemaphore (0);
|
||||
sem2 = SDL_CreateSemaphore (1);
|
||||
thread = SDL_CreateThread ((int (*)(void *))QZ_ThreadFlip, this);
|
||||
}
|
||||
|
||||
if ( CGDisplayCanSetPalette (display_id) )
|
||||
|
@ -511,10 +558,11 @@ static SDL_Surface* QZ_SetVideoFullScreen (_THIS, SDL_Surface *current, int widt
|
|||
return current;
|
||||
|
||||
/* Since the blanking window covers *all* windows (even force quit) correct recovery is crucial */
|
||||
ERR_NO_GL: CGDisplaySwitchToMode (display_id, save_mode);
|
||||
ERR_NO_SWITCH: CGDisplayRelease (display_id);
|
||||
ERR_NO_GL:
|
||||
ERR_DOUBLEBUF: CGDisplaySwitchToMode (display_id, save_mode);
|
||||
ERR_NO_SWITCH: CGReleaseAllDisplays ();
|
||||
ERR_NO_CAPTURE: if (!gamma_error) { QZ_FadeGammaIn (this, &gamma_table); }
|
||||
ERR_NO_MATCH: return NULL;
|
||||
ERR_NO_MATCH: return NULL;
|
||||
}
|
||||
|
||||
static SDL_Surface* QZ_SetVideoWindowed (_THIS, SDL_Surface *current, int width,
|
||||
|
@ -723,6 +771,151 @@ static int QZ_SetColors (_THIS, int first_color, int num_colors,
|
|||
return 1;
|
||||
}
|
||||
|
||||
static int QZ_LockDoubleBuffer (_THIS, SDL_Surface *surface) {
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
static void QZ_UnlockDoubleBuffer (_THIS, SDL_Surface *surface) {
|
||||
|
||||
}
|
||||
|
||||
/* The VBL delay is based on code by Ian R Ollmann's RezLib <iano@cco.caltech.edu> */
|
||||
static AbsoluteTime QZ_SecondsToAbsolute ( double seconds ) {
|
||||
|
||||
union
|
||||
{
|
||||
UInt64 i;
|
||||
Nanoseconds ns;
|
||||
} temp;
|
||||
|
||||
temp.i = seconds * 1000000000.0;
|
||||
|
||||
return NanosecondsToAbsolute ( temp.ns );
|
||||
}
|
||||
|
||||
static int QZ_ThreadFlip (_THIS) {
|
||||
|
||||
Uint8 *src, *dst;
|
||||
int skip, len, h;
|
||||
|
||||
/*
|
||||
Give this thread the highest scheduling priority possible,
|
||||
in the hopes that it will immediately run after the VBL delay
|
||||
*/
|
||||
{
|
||||
pthread_t current_thread;
|
||||
int policy;
|
||||
struct sched_param param;
|
||||
|
||||
current_thread = pthread_self ();
|
||||
pthread_getschedparam (current_thread, &policy, ¶m);
|
||||
policy = SCHED_RR;
|
||||
param.sched_priority = sched_get_priority_max (policy);
|
||||
pthread_setschedparam (current_thread, policy, ¶m);
|
||||
}
|
||||
|
||||
while (1) {
|
||||
|
||||
SDL_SemWait (sem1);
|
||||
if (quit_thread)
|
||||
return 0;
|
||||
|
||||
dst = CGDisplayBaseAddress (display_id);
|
||||
src = current_buffer;
|
||||
len = SDL_VideoSurface->w * SDL_VideoSurface->format->BytesPerPixel;
|
||||
h = SDL_VideoSurface->h;
|
||||
skip = SDL_VideoSurface->pitch;
|
||||
|
||||
/* Wait for the VBL to occur (estimated since we don't have a hardware interrupt) */
|
||||
{
|
||||
|
||||
/* The VBL delay is based on Ian Ollmann's RezLib <iano@cco.caltech.edu> */
|
||||
double refreshRate;
|
||||
double linesPerSecond;
|
||||
double target;
|
||||
double position;
|
||||
double adjustment;
|
||||
AbsoluteTime nextTime;
|
||||
CFNumberRef refreshRateCFNumber;
|
||||
|
||||
refreshRateCFNumber = CFDictionaryGetValue (mode, kCGDisplayRefreshRate);
|
||||
if ( NULL == refreshRateCFNumber ) {
|
||||
SDL_SetError ("Mode has no refresh rate");
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
if ( 0 == CFNumberGetValue (refreshRateCFNumber, kCFNumberDoubleType, &refreshRate) ) {
|
||||
SDL_SetError ("Error getting refresh rate");
|
||||
goto ERROR;
|
||||
}
|
||||
|
||||
if ( 0 == refreshRate ) {
|
||||
|
||||
SDL_SetError ("Display has no refresh rate, using 60hz");
|
||||
|
||||
/* ok, for LCD's we'll emulate a 60hz refresh, which may or may not look right */
|
||||
refreshRate = 60.0;
|
||||
}
|
||||
|
||||
linesPerSecond = refreshRate * h;
|
||||
target = h;
|
||||
|
||||
/* Figure out the first delay so we start off about right */
|
||||
position = CGDisplayBeamPosition (display_id);
|
||||
if (position > target)
|
||||
position = 0;
|
||||
|
||||
adjustment = (target - position) / linesPerSecond;
|
||||
|
||||
nextTime = AddAbsoluteToAbsolute (UpTime (), QZ_SecondsToAbsolute (adjustment));
|
||||
|
||||
MPDelayUntil (&nextTime);
|
||||
}
|
||||
|
||||
|
||||
/* On error, skip VBL delay */
|
||||
ERROR:
|
||||
|
||||
while ( h-- ) {
|
||||
|
||||
memcpy (dst, src, len);
|
||||
src += skip;
|
||||
dst += skip;
|
||||
}
|
||||
|
||||
/* signal flip completion */
|
||||
SDL_SemPost (sem2);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int QZ_FlipDoubleBuffer (_THIS, SDL_Surface *surface) {
|
||||
|
||||
/* wait for previous flip to complete */
|
||||
SDL_SemWait (sem2);
|
||||
|
||||
current_buffer = surface->pixels;
|
||||
|
||||
if (surface->pixels == sw_buffers[0])
|
||||
surface->pixels = sw_buffers[1];
|
||||
else
|
||||
surface->pixels = sw_buffers[0];
|
||||
|
||||
/* signal worker thread to do the flip */
|
||||
SDL_SemPost (sem1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
static void QZ_DoubleBufferUpdate (_THIS, int num_rects, SDL_Rect *rects) {
|
||||
|
||||
/* perform a flip if someone calls updaterects on a doublebuferred surface */
|
||||
this->FlipHWSurface (this, SDL_VideoSurface);
|
||||
}
|
||||
|
||||
static void QZ_DirectUpdate (_THIS, int num_rects, SDL_Rect *rects) {
|
||||
#pragma unused(this,num_rects,rects)
|
||||
}
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue