src/video/uikit/SDL_uikitopenglview.m
author Alex Szpakowski <slime73@gmail.com>
Fri, 21 Nov 2014 10:03:02 -0400
branchiOS-improvements
changeset 9526 b1e51123fbea
parent 9525 64e3f446d6d7
child 9529 4bf9830d8153
permissions -rw-r--r--
The iOS OpenGL ES context's color Renderbuffer Object is no longer completely destroyed and recreated in layoutSubviews. Its storage is now only reallocated when the actual size of the layer changes. The framebuffer object and renderbuffer object binding states are no longer clobbered by the storage reallocation code in layoutSubviews.
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2014 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 #include "../../SDL_internal.h"
    22 
    23 #if SDL_VIDEO_DRIVER_UIKIT
    24 
    25 #include <QuartzCore/QuartzCore.h>
    26 #include <OpenGLES/EAGLDrawable.h>
    27 #include <OpenGLES/ES2/gl.h>
    28 #include <OpenGLES/ES2/glext.h>
    29 #include "SDL_uikitopenglview.h"
    30 #include "SDL_uikitmessagebox.h"
    31 #include "SDL_uikitvideo.h"
    32 
    33 
    34 @implementation SDL_uikitopenglview {
    35 
    36     /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
    37     GLuint viewRenderbuffer, viewFramebuffer;
    38 
    39     /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
    40     GLuint depthRenderbuffer;
    41 
    42     /* format of depthRenderbuffer */
    43     GLenum depthBufferFormat;
    44 
    45     id displayLink;
    46     int animationInterval;
    47     void (*animationCallback)(void*);
    48     void *animationCallbackParam;
    49 
    50 }
    51 
    52 @synthesize context;
    53 
    54 @synthesize backingWidth;
    55 @synthesize backingHeight;
    56 
    57 + (Class)layerClass
    58 {
    59     return [CAEAGLLayer class];
    60 }
    61 
    62 - (id)initWithFrame:(CGRect)frame
    63               scale:(CGFloat)scale
    64       retainBacking:(BOOL)retained
    65               rBits:(int)rBits
    66               gBits:(int)gBits
    67               bBits:(int)bBits
    68               aBits:(int)aBits
    69           depthBits:(int)depthBits
    70         stencilBits:(int)stencilBits
    71                sRGB:(BOOL)sRGB
    72        majorVersion:(int)majorVersion
    73          shareGroup:(EAGLSharegroup*)shareGroup
    74 {
    75     if ((self = [super initWithFrame:frame])) {
    76         const BOOL useStencilBuffer = (stencilBits != 0);
    77         const BOOL useDepthBuffer = (depthBits != 0);
    78         NSString *colorFormat = nil;
    79 
    80         self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
    81         self.autoresizesSubviews = YES;
    82 
    83         /* The EAGLRenderingAPI enum values currently map 1:1 to major GLES
    84            versions, and this allows us to handle future OpenGL ES versions.
    85          */
    86         EAGLRenderingAPI api = majorVersion;
    87 
    88         context = [[EAGLContext alloc] initWithAPI:api sharegroup:shareGroup];
    89         if (!context || ![EAGLContext setCurrentContext:context]) {
    90             SDL_SetError("OpenGL ES %d not supported", majorVersion);
    91             return nil;
    92         }
    93 
    94         if (sRGB) {
    95             /* sRGB EAGL drawable support was added in iOS 7. */
    96             if (UIKit_IsSystemVersionAtLeast(7.0)) {
    97                 colorFormat = kEAGLColorFormatSRGBA8;
    98             } else {
    99                 SDL_SetError("sRGB drawables are not supported.");
   100                 return nil;
   101             }
   102         } else if (rBits >= 8 || gBits >= 8 || bBits >= 8) {
   103             /* if user specifically requests rbg888 or some color format higher than 16bpp */
   104             colorFormat = kEAGLColorFormatRGBA8;
   105         } else {
   106             /* default case (faster) */
   107             colorFormat = kEAGLColorFormatRGB565;
   108         }
   109 
   110         /* Get the layer */
   111         CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
   112 
   113         eaglLayer.opaque = YES;
   114         eaglLayer.drawableProperties = @{
   115             kEAGLDrawablePropertyRetainedBacking:@(retained),
   116             kEAGLDrawablePropertyColorFormat:colorFormat
   117         };
   118 
   119         /* Set the appropriate scale (for retina display support) */
   120         self.contentScaleFactor = scale;
   121 
   122         /* Create the color Renderbuffer Object */
   123         glGenRenderbuffers(1, &viewRenderbuffer);
   124         glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   125 
   126         if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
   127             SDL_SetError("Failed to create OpenGL ES drawable");
   128             return nil;
   129         }
   130 
   131         /* Create the Framebuffer Object */
   132         glGenFramebuffers(1, &viewFramebuffer);
   133         glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
   134 
   135         /* attach the color renderbuffer to the FBO */
   136         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
   137 
   138         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
   139         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
   140 
   141         if ((useDepthBuffer) || (useStencilBuffer)) {
   142             if (useStencilBuffer) {
   143                 /* Apparently you need to pack stencil and depth into one buffer. */
   144                 depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
   145             } else if (useDepthBuffer) {
   146                 /* iOS only has 24-bit depth buffers, even with GL_DEPTH_COMPONENT16 */
   147                 depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
   148             }
   149 
   150             glGenRenderbuffers(1, &depthRenderbuffer);
   151             glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
   152             glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
   153             if (useDepthBuffer) {
   154                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
   155             }
   156             if (useStencilBuffer) {
   157                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
   158             }
   159         }
   160 
   161         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
   162             SDL_SetError("Failed creating OpenGL ES framebuffer");
   163             return nil;
   164         }
   165 
   166         glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   167 
   168         [self setDebugLabels];
   169     }
   170 
   171     return self;
   172 }
   173 
   174 - (GLuint)drawableRenderbuffer
   175 {
   176     return viewRenderbuffer;
   177 }
   178 
   179 - (GLuint)drawableFramebuffer
   180 {
   181     return viewFramebuffer;
   182 }
   183 
   184 - (void)updateFrame
   185 {
   186     GLint prevRenderbuffer = 0;
   187     glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
   188 
   189     glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   190     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
   191 
   192     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
   193     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
   194 
   195     if (depthRenderbuffer != 0) {
   196         glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
   197         glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
   198     }
   199 
   200     glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
   201 }
   202 
   203 - (void)setDebugLabels
   204 {
   205     if (viewFramebuffer != 0) {
   206         glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
   207     }
   208 
   209     if (viewRenderbuffer != 0) {
   210         glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
   211     }
   212 
   213     if (depthRenderbuffer != 0) {
   214         if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
   215             glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
   216         } else {
   217             glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
   218         }
   219     }
   220 }
   221 
   222 - (void)setAnimationCallback:(int)interval
   223                     callback:(void (*)(void*))callback
   224                callbackParam:(void*)callbackParam
   225 {
   226     [self stopAnimation];
   227 
   228     animationInterval = interval;
   229     animationCallback = callback;
   230     animationCallbackParam = callbackParam;
   231 
   232     if (animationCallback) {
   233         [self startAnimation];
   234     }
   235 }
   236 
   237 - (void)startAnimation
   238 {
   239     displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
   240     [displayLink setFrameInterval:animationInterval];
   241     [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   242 }
   243 
   244 - (void)stopAnimation
   245 {
   246     [displayLink invalidate];
   247     displayLink = nil;
   248 }
   249 
   250 - (void)doLoop:(CADisplayLink*)sender
   251 {
   252     /* Don't run the game loop while a messagebox is up */
   253     if (!UIKit_ShowingMessageBox()) {
   254         animationCallback(animationCallbackParam);
   255     }
   256 }
   257 
   258 - (void)setCurrentContext
   259 {
   260     [EAGLContext setCurrentContext:context];
   261 }
   262 
   263 - (void)swapBuffers
   264 {
   265     /* viewRenderbuffer should always be bound here. Code that binds something
   266        else is responsible for rebinding viewRenderbuffer, to reduce duplicate
   267        state changes. */
   268     [context presentRenderbuffer:GL_RENDERBUFFER];
   269 }
   270 
   271 - (void)layoutSubviews
   272 {
   273     [super layoutSubviews];
   274 
   275     CGSize layersize = self.layer.bounds.size;
   276     int width = (int) (layersize.width * self.layer.contentsScale);
   277     int height = (int) (layersize.height * self.layer.contentsScale);
   278 
   279     /* Update the color and depth buffer storage if the layer size has changed. */
   280     if (width != backingWidth || height != backingHeight) {
   281         [EAGLContext setCurrentContext:context];
   282         [self updateFrame];
   283     }
   284 }
   285 
   286 - (void)destroyFramebuffer
   287 {
   288     if (viewFramebuffer != 0) {
   289         glDeleteFramebuffers(1, &viewFramebuffer);
   290         viewFramebuffer = 0;
   291     }
   292 
   293     if (viewRenderbuffer != 0) {
   294         glDeleteRenderbuffers(1, &viewRenderbuffer);
   295         viewRenderbuffer = 0;
   296     }
   297 
   298     if (depthRenderbuffer != 0) {
   299         glDeleteRenderbuffers(1, &depthRenderbuffer);
   300         depthRenderbuffer = 0;
   301     }
   302 }
   303 
   304 
   305 - (void)dealloc
   306 {
   307     if ([EAGLContext currentContext] == context) {
   308         [self destroyFramebuffer];
   309         [EAGLContext setCurrentContext:nil];
   310     }
   311 }
   312 
   313 @end
   314 
   315 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   316 
   317 /* vi: set ts=4 sw=4 expandtab: */