Attempt to handle the OGL contexts more gracefully.

This commit is contained in:
Steve Checkoway
2006-01-18 08:44:52 +00:00
parent e60c194a69
commit 5676e10e33
2 changed files with 137 additions and 192 deletions
@@ -8,21 +8,13 @@
typedef void *id; typedef void *id;
#endif #endif
#define CONCURRENT_RENDERING 0 // This seems to crash the computer...
typedef const struct __CFDictionary *CFDictionaryRef; typedef const struct __CFDictionary *CFDictionaryRef;
class LowLevelWindow_Cocoa : public LowLevelWindow class LowLevelWindow_Cocoa : public LowLevelWindow
{ {
VideoModeParams m_CurrentParams; VideoModeParams m_CurrentParams;
id m_Window; id m_Window;
id m_WindowContext; id m_Context;
id m_FullScreenContext;
#if CONCURRENT_RENDERING
id m_ConcurrentWindowContext;
id m_ConcurrentFullScreenContext;
#endif
bool m_bSharingContexts;
CFDictionaryRef m_CurrentDisplayMode; CFDictionaryRef m_CurrentDisplayMode;
public: public:
@@ -34,12 +26,6 @@ public:
void SwapBuffers(); void SwapBuffers();
void Update(); void Update();
#if CONCURRENT_RENDERING
bool SupportsThreadedRendering();
void BeginConcurrentRendering();
void EndConcurrentRendering();
#endif
VideoModeParams GetActualVideoModeParams() const { return m_CurrentParams; } VideoModeParams GetActualVideoModeParams() const { return m_CurrentParams; }
private: private:
void ShutDownFullScreen(); void ShutDownFullScreen();
@@ -9,6 +9,7 @@
#import <Cocoa/Cocoa.h> #import <Cocoa/Cocoa.h>
#import <OpenGl/OpenGl.h> #import <OpenGl/OpenGl.h>
#import <OpenGL/gl.h>
#import <mach-o/dyld.h> #import <mach-o/dyld.h>
static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask | static const unsigned int g_iStyleMask = NSTitledWindowMask | NSClosableWindowMask |
@@ -72,13 +73,8 @@ public:
@end @end
LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : m_WindowContext(nil), m_FullScreenContext(nil), LowLevelWindow_Cocoa::LowLevelWindow_Cocoa() : m_Context(nil), m_CurrentDisplayMode(NULL)
m_bSharingContexts(false), m_CurrentDisplayMode(NULL)
{ {
#if CONCURRENT_RENDERING
m_ConcurrentWindowContext = nil;
m_ConcurrentFullScreenContext = nil;
#endif
POOL; POOL;
NSRect rect = { {0, 0}, {0, 0} }; NSRect rect = { {0, 0}, {0, 0} };
SMMainThread *mt = [[SMMainThread alloc] init]; SMMainThread *mt = [[SMMainThread alloc] init];
@@ -113,12 +109,11 @@ LowLevelWindow_Cocoa::~LowLevelWindow_Cocoa()
ADD_ACTION1( mt, m_Window, orderOut:, nil ); ADD_ACTION1( mt, m_Window, orderOut:, nil );
ADD_ACTION0( mt, m_Window, release ); ADD_ACTION0( mt, m_Window, release );
[m_WindowContext clearDrawable]; [m_Context clearDrawable];
[m_Context release];
[mt performOnMainThread]; [mt performOnMainThread];
[delegate release]; [delegate release];
[mt release]; [mt release];
[m_WindowContext release];
[m_FullScreenContext release];
} }
void *LowLevelWindow_Cocoa::GetProcAddress( CString s ) void *LowLevelWindow_Cocoa::GetProcAddress( CString s )
@@ -135,12 +130,60 @@ void *LowLevelWindow_Cocoa::GetProcAddress( CString s )
return symbol ? NSAddressOfSymbol( symbol ) : NULL; return symbol ? NSAddressOfSymbol( symbol ) : NULL;
} }
static void SetOGLParameters( NSOpenGLContext *context ) static NSOpenGLContext *CreateOGLContext( int iColorDepth, bool bWindowed, NSOpenGLContext *share, bool &bShared )
{ {
long val = 64; NSOpenGLPixelFormat *pixelFormat;
[context setValues:&val forParameter:NSOpenGLContextParameter(280)]; if( bWindowed )
[context setValues:&val forParameter:NSOpenGLContextParameter(284)]; {
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFANoRecovery, // so we can share with the full screen context
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(16),
NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(iColorDepth == 16 ? 16 : 24),
NSOpenGLPixelFormatAttribute(0)
};
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
}
else
{
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFAFullScreen,
// Choose which screen to use: the main one.
NSOpenGLPFAScreenMask,
NSOpenGLPixelFormatAttribute( CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay) ),
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(16),
NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(iColorDepth == 16 ? 16 : 24),
NSOpenGLPixelFormatAttribute(0)
};
pixelFormat = [[NSOpenGLPixelFormat alloc] initWithAttributes:attrs];
}
if( !pixelFormat )
return nil;
NSOpenGLContext *context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:share];
bShared = share && context;
if( !context )
context = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
[pixelFormat release];
if( !context )
return nil;
//long val = 64;
//[context setValues:&val forParameter:NSOpenGLContextParameter(280)];
//[context setValues:&val forParameter:NSOpenGLContextParameter(284)];
return context;
} }
CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut ) CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newDeviceOut )
@@ -162,83 +205,46 @@ CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newD
newDeviceOut = false; newDeviceOut = false;
NSRect contentRect = { { 0, 0 }, { p.width, p.height } }; NSRect contentRect = { { 0, 0 }, { p.width, p.height } };
SMMainThread *mt = [[[SMMainThread alloc] init] autorelease]; const bool bRebuildContext = p.bpp != m_CurrentParams.bpp ||
!m_Context || p.windowed != m_CurrentParams.windowed;
// Change the window and the title
ADD_ACTIONn( mt, m_Window, setContentSize:, 1, &contentRect.size );
ADD_ACTION1( mt, m_Window, setTitle:, [NSString stringWithUTF8String:p.sWindowTitle.c_str()] );
m_CurrentParams.width = p.width;
m_CurrentParams.height = p.height;
ASSERT( p.bpp == 16 || p.bpp == 32 ); ASSERT( p.bpp == 16 || p.bpp == 32 );
if( p.bpp != m_CurrentParams.bpp || !m_WindowContext )
{
NSOpenGLPixelFormat *pixelFormat;
NSOpenGLPixelFormatAttribute attrs[] = {
NSOpenGLPFANoRecovery, // so we can share with the full screen context
NSOpenGLPFADoubleBuffer,
NSOpenGLPFAAccelerated,
NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(16),
NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(p.bpp == 16 ? 16 : 24),
NSOpenGLPixelFormatAttribute(0)
};
pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
if( pixelFormat == nil )
{
[mt performOnMainThread];
return "Failed to set the windowed pixel format.";
}
id nextContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
if( nextContext == nil )
{
[mt performOnMainThread];
return "Failed to create windowed OGL context.";
}
SetOGLParameters( nextContext );
newDeviceOut = true;
[m_WindowContext clearDrawable];
[m_WindowContext release];
m_WindowContext = nextContext;
#if CONCURRENT_RENDERING
[m_ConcurrentWindowContext release];
m_ConcurrentWindowContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
shareContext:m_WindowContext];
if( m_ConcurrentWindowContext )
SetOGLParameters( m_ConcurrentWindowContext );
#endif
// We need to recreate the full screen context as well.
[m_FullScreenContext clearDrawable];
[m_FullScreenContext release];
m_FullScreenContext = nil;
m_CurrentParams.bpp = p.bpp;
m_bSharingContexts = false;
}
if( p.windowed ) if( p.windowed )
{ {
ShutDownFullScreen(); if( bRebuildContext )
{
bool bShared;
NSOpenGLContext *newContext = CreateOGLContext( p.bpp, true, m_Context, bShared );
if( !newContext )
return "Failed to create OGL context.";
ShutDownFullScreen();
[m_Context release];
m_Context = newContext;
newDeviceOut = !bShared;
m_CurrentParams.bpp = p.bpp;
}
SMMainThread *mt = [[SMMainThread alloc] init];
// Change the window and the title
ADD_ACTIONn( mt, m_Window, setContentSize:, 1, &contentRect.size );
ADD_ACTION1( mt, m_Window, setTitle:, [NSString stringWithUTF8String:p.sWindowTitle.c_str()] );
ADD_ACTION0( mt, m_Window, center ); ADD_ACTION0( mt, m_Window, center );
ADD_ACTION1( mt, m_Window, makeKeyAndOrderFront:, nil ); ADD_ACTION1( mt, m_Window, makeKeyAndOrderFront:, nil );
[mt performOnMainThread]; [mt performOnMainThread];
[m_WindowContext setView:[m_Window contentView]]; [mt release];
[m_WindowContext update]; [m_Context setView:[m_Window contentView]];
[m_WindowContext makeCurrentContext]; [m_Context update];
[m_Context makeCurrentContext];
m_CurrentParams.windowed = true; m_CurrentParams.windowed = true;
SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) ); SetActualParamsFromMode( CGDisplayCurrentMode(kCGDirectMainDisplay) );
m_CurrentParams.vsync = p.vsync; // hack m_CurrentParams.vsync = p.vsync; // hack
newDeviceOut = newDeviceOut || !m_bSharingContexts;
return CString(); return CString();
} }
[mt performOnMainThread];
int result = ChangeDisplayMode( p ); int result = ChangeDisplayMode( p );
if( result ) if( result )
@@ -246,60 +252,33 @@ CString LowLevelWindow_Cocoa::TryVideoMode( const VideoModeParams& p, bool& newD
return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.", return ssprintf( "Failed to switch to full screen:%d x %d @ %d. Error %d.",
p.width, p.height, p.rate, result ); p.width, p.height, p.rate, result );
} }
if( m_FullScreenContext == nil )
if( bRebuildContext )
{ {
NSOpenGLPixelFormatAttribute attrs[] = { bool bShared;
NSOpenGLPFAFullScreen, NSOpenGLContext *newContext = CreateOGLContext( p.bpp, false, m_Context, bShared );
// Choose which screen to use: the main one.
NSOpenGLPFAScreenMask,
NSOpenGLPixelFormatAttribute( CGDisplayIDToOpenGLDisplayMask(kCGDirectMainDisplay) ),
NSOpenGLPFADoubleBuffer, if( !newContext )
NSOpenGLPFAAccelerated,
NSOpenGLPFADepthSize, NSOpenGLPixelFormatAttribute(16),
NSOpenGLPFAColorSize, NSOpenGLPixelFormatAttribute(p.bpp == 16 ? 16 : 24),
NSOpenGLPixelFormatAttribute(0)
};
NSOpenGLPixelFormat *pixelFormat;
// Autorelease to simplify logic.
pixelFormat = [[[NSOpenGLPixelFormat alloc] initWithAttributes:attrs] autorelease];
if( pixelFormat == nil )
{ {
ShutDownFullScreen(); // If this fails, we need to leave full screen. ShutDownFullScreen();
return "Failed to set full screen pixel format."; return "Failed to create full screen OGL context.";
} }
[m_Context clearDrawable];
m_FullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:m_WindowContext]; [m_Context release];
if( !(m_bSharingContexts = m_FullScreenContext != nil) ) m_Context = newContext;
{ newDeviceOut = !bShared;
LOG->Warn( "Failed to share openGL contexts." ); m_CurrentParams.bpp = p.bpp;
m_FullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat shareContext:nil];
if( !m_FullScreenContext )
{
ShutDownFullScreen(); // Same here.
return "Failed to create full screen openGL context.";
}
}
SetOGLParameters( m_FullScreenContext );
#if CONCURRENT_RENDERING
[m_ConcurrentFullScreenContext release];
m_ConcurrentFullScreenContext = [[NSOpenGLContext alloc] initWithFormat:pixelFormat
shareContext:m_WindowContext];
if( m_ConcurrentFullScreenContext )
SetOGLParameters( m_ConcurrentFullScreenContext );
#endif
} }
[m_Context setFullScreen];
[m_Context update];
[m_Context makeCurrentContext];
long swap = p.vsync ? 1 : 0; long swap = p.vsync ? 1 : 0;
[m_FullScreenContext setValues:&swap forParameter:NSOpenGLCPSwapInterval]; [m_Context setValues:&swap forParameter:NSOpenGLCPSwapInterval];
[m_FullScreenContext setFullScreen];
[m_FullScreenContext update];
[m_FullScreenContext makeCurrentContext];
m_CurrentParams.vsync = p.vsync; m_CurrentParams.vsync = p.vsync;
newDeviceOut = newDeviceOut || !m_bSharingContexts;
return CString(); return CString();
} }
@@ -308,11 +287,17 @@ void LowLevelWindow_Cocoa::ShutDownFullScreen()
if( m_CurrentParams.windowed ) if( m_CurrentParams.windowed )
return; return;
ASSERT( m_CurrentDisplayMode ); ASSERT( m_CurrentDisplayMode );
[m_FullScreenContext clearDrawable];
#if CONCURRENT_RENDERING // Clear the front and back framebuffers before switching out of FullScreen mode.
[m_ConcurrentFullScreenContext clearDrawable]; // (This is not strictly necessary, but avoids an untidy flash of garbage.)
#endif glClearColor( 0.0f, 0.0f, 0.0f, 0.0f );
glClear( GL_COLOR_BUFFER_BIT );
[m_Context flushBuffer];
glClear( GL_COLOR_BUFFER_BIT );
[m_Context flushBuffer];
[NSOpenGLContext clearCurrentContext];
[m_Context clearDrawable];
CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, m_CurrentDisplayMode ); CGDisplayErr err = CGDisplaySwitchToMode( kCGDirectMainDisplay, m_CurrentDisplayMode );
@@ -326,41 +311,13 @@ void LowLevelWindow_Cocoa::ShutDownFullScreen()
m_CurrentParams.windowed = true; m_CurrentParams.windowed = true;
} }
#if CONCURRENT_RENDERING
bool LowLevelWindow_Cocoa::SupportsThreadedRendering()
{
return m_CurrentParams.windowed ? m_ConcurrentWindowContext : m_ConcurrentFullScreenContext;
}
void LowLevelWindow_Cocoa::BeginConcurrentRendering()
{
if( m_CurrentParams.windowed )
{
[m_ConcurrentWindowContext setView:[m_Window contentView]];
[m_ConcurrentWindowContext update];
[m_ConcurrentWindowContext makeCurrentContext];
}
else
{
[m_ConcurrentFullScreenContext setFullScreen];
[m_ConcurrentFullScreenContext update];
[m_ConcurrentFullScreenContext makeCurrentContext];
}
}
void LowLevelWindow_Cocoa::EndConcurrentRendering()
{
[NSOpenGLContext clearCurrentContext];
}
#endif
int LowLevelWindow_Cocoa::ChangeDisplayMode( const VideoModeParams& p ) int LowLevelWindow_Cocoa::ChangeDisplayMode( const VideoModeParams& p )
{ {
CFDictionaryRef mode = NULL; CFDictionaryRef mode = NULL;
CFDictionaryRef newMode; CFDictionaryRef newMode;
CGDisplayErr err; CGDisplayErr err;
if( m_CurrentParams.windowed ) if( !m_CurrentDisplayMode )
{ {
if( (err = CGCaptureAllDisplays()) != kCGErrorSuccess ) if( (err = CGCaptureAllDisplays()) != kCGErrorSuccess )
return err; return err;
@@ -380,38 +337,40 @@ int LowLevelWindow_Cocoa::ChangeDisplayMode( const VideoModeParams& p )
if( err != kCGErrorSuccess ) if( err != kCGErrorSuccess )
return err; // We don't own mode, don't release it. return err; // We don't own mode, don't release it.
SetActualParamsFromMode( newMode );
if( m_CurrentParams.windowed ) if( !m_CurrentDisplayMode )
{
m_CurrentDisplayMode = mode; m_CurrentDisplayMode = mode;
m_CurrentParams.windowed = false; m_CurrentParams.windowed = false;
} SetActualParamsFromMode( newMode );
return 0; return 0;
} }
void LowLevelWindow_Cocoa::SetActualParamsFromMode( CFDictionaryRef mode ) void LowLevelWindow_Cocoa::SetActualParamsFromMode( CFDictionaryRef mode )
{ {
SInt32 width, height, rate, bpp; SInt32 rate;
if( CFNumberGetValue( (CFNumberRef)CFDictionaryGetValue(mode, CFSTR("RefreshRate")), kCFNumberSInt32Type, &rate) )
m_CurrentParams.rate = rate;
#define X(prop,var) CFNumberGetValue( CFNumberRef(CFDictionaryGetValue(mode, CFSTR(prop))), kCFNumberSInt32Type, &var )
X( "Width", width );
X( "Height", height );
X( "RefreshRate", rate );
X( "BitsPerPixel", bpp );
#undef X
if( !m_CurrentParams.windowed ) if( !m_CurrentParams.windowed )
{ {
long swap; long swap;
m_CurrentParams.width = width; m_CurrentParams.width = CGDisplayPixelsWide( kCGDirectMainDisplay );
m_CurrentParams.height = height; m_CurrentParams.height = CGDisplayPixelsHigh( kCGDirectMainDisplay );
CGLGetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &swap ); CGLGetParameter( CGLGetCurrentContext(), kCGLCPSwapInterval, &swap );
m_CurrentParams.vsync = swap != 0; m_CurrentParams.vsync = swap != 0;
} }
m_CurrentParams.bpp = bpp; else
m_CurrentParams.rate = rate; {
NSSize size = [[m_Window contentView] frame].size;
m_CurrentParams.width = int(size.width);
m_CurrentParams.height = int(size.height);
}
m_CurrentParams.bpp = CGDisplayBitsPerPixel( kCGDirectMainDisplay );
} }
void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const
@@ -440,7 +399,7 @@ void LowLevelWindow_Cocoa::GetDisplayResolutions( DisplayResolutions &dr ) const
void LowLevelWindow_Cocoa::SwapBuffers() void LowLevelWindow_Cocoa::SwapBuffers()
{ {
[(m_CurrentParams.windowed ? m_WindowContext : m_FullScreenContext) flushBuffer]; [m_Context flushBuffer];
} }
void LowLevelWindow_Cocoa::Update() void LowLevelWindow_Cocoa::Update()
@@ -454,6 +413,6 @@ void LowLevelWindow_Cocoa::Update()
m_CurrentParams.width = g_iWidth; m_CurrentParams.width = g_iWidth;
m_CurrentParams.height = g_iHeight; m_CurrentParams.height = g_iHeight;
lock.Unlock(); // Unlock before calling ResolutionChanged(). lock.Unlock(); // Unlock before calling ResolutionChanged().
[m_WindowContext update]; [m_Context update];
DISPLAY->ResolutionChanged(); DISPLAY->ResolutionChanged();
} }