iOS: Fixed some cases where SDL_DestroyWindow or SDL_GL_DeleteContext can cause crashes.

This commit is contained in:
Alex Szpakowski 2015-06-09 21:08:24 -03:00
parent cd1d7c94e9
commit a86df3b7cf
5 changed files with 57 additions and 42 deletions

View File

@ -34,6 +34,24 @@
#include "SDL_loadso.h" #include "SDL_loadso.h"
#include <dlfcn.h> #include <dlfcn.h>
@interface SDLEAGLContext : EAGLContext
/* The OpenGL ES context owns a view / drawable. */
@property (nonatomic, strong) SDL_uikitopenglview *sdlView;
@end
@implementation SDLEAGLContext
- (void)dealloc
{
/* When the context is deallocated, its view should be removed from any
* SDL window that it's attached to. */
[self.sdlView setSDLWindow:NULL];
}
@end
static int UIKit_GL_Initialize(_THIS); static int UIKit_GL_Initialize(_THIS);
void * void *
@ -117,12 +135,17 @@ SDL_GLContext
UIKit_GL_CreateContext(_THIS, SDL_Window * window) UIKit_GL_CreateContext(_THIS, SDL_Window * window)
{ {
@autoreleasepool { @autoreleasepool {
SDLEAGLContext *context = nil;
SDL_uikitopenglview *view; SDL_uikitopenglview *view;
SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata; SDL_WindowData *data = (__bridge SDL_WindowData *) window->driverdata;
CGRect frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen); CGRect frame = UIKit_ComputeViewFrame(window, data.uiwindow.screen);
EAGLSharegroup *sharegroup = nil; EAGLSharegroup *sharegroup = nil;
CGFloat scale = 1.0; CGFloat scale = 1.0;
/* The EAGLRenderingAPI enum values currently map 1:1 to major GLES
* versions. */
EAGLRenderingAPI api = _this->gl_config.major_version;
if (_this->gl_config.share_with_current_context) { if (_this->gl_config.share_with_current_context) {
EAGLContext *context = (__bridge EAGLContext *) SDL_GL_GetCurrentContext(); EAGLContext *context = (__bridge EAGLContext *) SDL_GL_GetCurrentContext();
sharegroup = context.sharegroup; sharegroup = context.sharegroup;
@ -142,6 +165,12 @@ UIKit_GL_CreateContext(_THIS, SDL_Window * window)
} }
} }
context = [[SDLEAGLContext alloc] initWithAPI:api sharegroup:sharegroup];
if (!context) {
SDL_SetError("OpenGL ES %d context could not be created", _this->gl_config.major_version);
return NULL;
}
/* construct our view, passing in SDL's OpenGL configuration data */ /* construct our view, passing in SDL's OpenGL configuration data */
view = [[SDL_uikitopenglview alloc] initWithFrame:frame view = [[SDL_uikitopenglview alloc] initWithFrame:frame
scale:scale scale:scale
@ -153,13 +182,15 @@ UIKit_GL_CreateContext(_THIS, SDL_Window * window)
depthBits:_this->gl_config.depth_size depthBits:_this->gl_config.depth_size
stencilBits:_this->gl_config.stencil_size stencilBits:_this->gl_config.stencil_size
sRGB:_this->gl_config.framebuffer_srgb_capable sRGB:_this->gl_config.framebuffer_srgb_capable
majorVersion:_this->gl_config.major_version context:context];
shareGroup:sharegroup];
if (!view) { if (!view) {
return NULL; return NULL;
} }
SDLEAGLContext *context = view.context; /* The context owns the view / drawable. */
context.sdlView = view;
if (UIKit_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext) context) < 0) { if (UIKit_GL_MakeCurrent(_this, window, (__bridge SDL_GLContext) context) < 0) {
UIKit_GL_DeleteContext(_this, (SDL_GLContext) CFBridgingRetain(context)); UIKit_GL_DeleteContext(_this, (SDL_GLContext) CFBridgingRetain(context));
return NULL; return NULL;
@ -175,11 +206,10 @@ void
UIKit_GL_DeleteContext(_THIS, SDL_GLContext context) UIKit_GL_DeleteContext(_THIS, SDL_GLContext context)
{ {
@autoreleasepool { @autoreleasepool {
/* Transfer ownership the +1'd context to ARC. */ /* The context was retained in SDL_GL_CreateContext, so we release it
SDLEAGLContext *eaglcontext = (SDLEAGLContext *) CFBridgingRelease(context); * here. The context's view will be detached from its window when the
* context is deallocated. */
/* Detach the context's view from its window. */ CFRelease(context);
[eaglcontext.sdlView setSDLWindow:NULL];
} }
} }

View File

@ -26,14 +26,6 @@
#import "SDL_uikitview.h" #import "SDL_uikitview.h"
#include "SDL_uikitvideo.h" #include "SDL_uikitvideo.h"
@class SDL_uikitopenglview;
@interface SDLEAGLContext : EAGLContext
@property (nonatomic, weak) SDL_uikitopenglview *sdlView;
@end
@interface SDL_uikitopenglview : SDL_uikitview @interface SDL_uikitopenglview : SDL_uikitview
- (instancetype)initWithFrame:(CGRect)frame - (instancetype)initWithFrame:(CGRect)frame
@ -46,10 +38,9 @@
depthBits:(int)depthBits depthBits:(int)depthBits
stencilBits:(int)stencilBits stencilBits:(int)stencilBits
sRGB:(BOOL)sRGB sRGB:(BOOL)sRGB
majorVersion:(int)majorVersion context:(EAGLContext *)glcontext;
shareGroup:(EAGLSharegroup*)shareGroup;
@property (nonatomic, readonly, strong) SDLEAGLContext *context; @property (nonatomic, readonly, weak) EAGLContext *context;
/* The width and height of the drawable in pixels (as opposed to points.) */ /* The width and height of the drawable in pixels (as opposed to points.) */
@property (nonatomic, readonly) int backingWidth; @property (nonatomic, readonly) int backingWidth;
@ -59,7 +50,6 @@
@property (nonatomic, readonly) GLuint drawableFramebuffer; @property (nonatomic, readonly) GLuint drawableFramebuffer;
- (void)swapBuffers; - (void)swapBuffers;
- (void)setCurrentContext;
- (void)updateFrame; - (void)updateFrame;

View File

@ -27,10 +27,6 @@
#import "SDL_uikitopenglview.h" #import "SDL_uikitopenglview.h"
#include "SDL_uikitwindow.h" #include "SDL_uikitwindow.h"
@implementation SDLEAGLContext
@end
@implementation SDL_uikitopenglview { @implementation SDL_uikitopenglview {
/* The renderbuffer and framebuffer used to render to this layer. */ /* The renderbuffer and framebuffer used to render to this layer. */
GLuint viewRenderbuffer, viewFramebuffer; GLuint viewRenderbuffer, viewFramebuffer;
@ -61,26 +57,20 @@
depthBits:(int)depthBits depthBits:(int)depthBits
stencilBits:(int)stencilBits stencilBits:(int)stencilBits
sRGB:(BOOL)sRGB sRGB:(BOOL)sRGB
majorVersion:(int)majorVersion context:(EAGLContext *)glcontext
shareGroup:(EAGLSharegroup*)shareGroup
{ {
if ((self = [super initWithFrame:frame])) { if ((self = [super initWithFrame:frame])) {
const BOOL useStencilBuffer = (stencilBits != 0); const BOOL useStencilBuffer = (stencilBits != 0);
const BOOL useDepthBuffer = (depthBits != 0); const BOOL useDepthBuffer = (depthBits != 0);
NSString *colorFormat = nil; NSString *colorFormat = nil;
/* The EAGLRenderingAPI enum values currently map 1:1 to major GLES context = glcontext;
* versions, and this allows us to handle future OpenGL ES versions. */
EAGLRenderingAPI api = majorVersion;
context = [[SDLEAGLContext alloc] initWithAPI:api sharegroup:shareGroup];
if (!context || ![EAGLContext setCurrentContext:context]) { if (!context || ![EAGLContext setCurrentContext:context]) {
SDL_SetError("OpenGL ES %d not supported", majorVersion); SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
return nil; return nil;
} }
context.sdlView = self;
if (sRGB) { if (sRGB) {
/* sRGB EAGL drawable support was added in iOS 7. */ /* sRGB EAGL drawable support was added in iOS 7. */
if (UIKit_IsSystemVersionAtLeast(7.0)) { if (UIKit_IsSystemVersionAtLeast(7.0)) {
@ -209,11 +199,6 @@
} }
} }
- (void)setCurrentContext
{
[EAGLContext setCurrentContext:context];
}
- (void)swapBuffers - (void)swapBuffers
{ {
/* viewRenderbuffer should always be bound here. Code that binds something /* viewRenderbuffer should always be bound here. Code that binds something
@ -264,7 +249,7 @@
- (void)dealloc - (void)dealloc
{ {
if ([EAGLContext currentContext] == context) { if (context && context == [EAGLContext currentContext]) {
[self destroyFramebuffer]; [self destroyFramebuffer];
[EAGLContext setCurrentContext:nil]; [EAGLContext setCurrentContext:nil];
} }

View File

@ -62,6 +62,7 @@
return; return;
} }
/* Remove ourself from the old window. */
if (sdlwindow) { if (sdlwindow) {
SDL_uikitview *view = nil; SDL_uikitview *view = nil;
data = (__bridge SDL_WindowData *) sdlwindow->driverdata; data = (__bridge SDL_WindowData *) sdlwindow->driverdata;
@ -71,9 +72,7 @@
[self removeFromSuperview]; [self removeFromSuperview];
/* Restore the next-oldest view in the old window. */ /* Restore the next-oldest view in the old window. */
if (data.views.count > 0) { view = data.views.lastObject;
view = data.views[data.views.count - 1];
}
data.viewcontroller.view = view; data.viewcontroller.view = view;
@ -83,6 +82,7 @@
[data.uiwindow layoutIfNeeded]; [data.uiwindow layoutIfNeeded];
} }
/* Add ourself to the new window. */
if (window) { if (window) {
data = (__bridge SDL_WindowData *) window->driverdata; data = (__bridge SDL_WindowData *) window->driverdata;

View File

@ -305,7 +305,17 @@ UIKit_DestroyWindow(_THIS, SDL_Window * window)
@autoreleasepool { @autoreleasepool {
if (window->driverdata != NULL) { if (window->driverdata != NULL) {
SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata); SDL_WindowData *data = (SDL_WindowData *) CFBridgingRelease(window->driverdata);
NSArray *views = nil;
[data.viewcontroller stopAnimation]; [data.viewcontroller stopAnimation];
/* Detach all views from this window. We use a copy of the array
* because setSDLWindow will remove the object from the original
* array, which would be undesirable if we were iterating over it. */
views = [data.views copy];
for (SDL_uikitview *view in views) {
[view setSDLWindow:NULL];
}
} }
} }
window->driverdata = NULL; window->driverdata = NULL;