src/video/uikit/SDL_uikitopenglview.m
author Alex Szpakowski <slime73@gmail.com>
Thu, 20 Nov 2014 18:45:04 -0400
branchiOS-improvements
changeset 9524 9de159f9cefd
parent 9520 6a655264dd47
child 9525 64e3f446d6d7
permissions -rw-r--r--
Minor objective-c code style improvements
     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 
   169     return self;
   170 }
   171 
   172 - (GLuint)drawableRenderbuffer
   173 {
   174     return viewRenderbuffer;
   175 }
   176 
   177 - (GLuint)drawableFramebuffer
   178 {
   179     return viewFramebuffer;
   180 }
   181 
   182 - (void)updateFrame
   183 {
   184     glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
   185     glBindRenderbuffer(GL_RENDERBUFFER, 0);
   186     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, 0);
   187     glDeleteRenderbuffers(1, &viewRenderbuffer);
   188 
   189     glGenRenderbuffers(1, &viewRenderbuffer);
   190     glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   191     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
   192     glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
   193 
   194     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
   195     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
   196 
   197     if (depthRenderbuffer != 0) {
   198         glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
   199         glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
   200     }
   201 
   202     glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   203 }
   204 
   205 - (void)setAnimationCallback:(int)interval
   206                     callback:(void (*)(void*))callback
   207                callbackParam:(void*)callbackParam
   208 {
   209     [self stopAnimation];
   210 
   211     animationInterval = interval;
   212     animationCallback = callback;
   213     animationCallbackParam = callbackParam;
   214 
   215     if (animationCallback) {
   216         [self startAnimation];
   217     }
   218 }
   219 
   220 - (void)startAnimation
   221 {
   222     displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
   223     [displayLink setFrameInterval:animationInterval];
   224     [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
   225 }
   226 
   227 - (void)stopAnimation
   228 {
   229     [displayLink invalidate];
   230     displayLink = nil;
   231 }
   232 
   233 - (void)doLoop:(CADisplayLink*)sender
   234 {
   235     /* Don't run the game loop while a messagebox is up */
   236     if (!UIKit_ShowingMessageBox()) {
   237         animationCallback(animationCallbackParam);
   238     }
   239 }
   240 
   241 - (void)setCurrentContext
   242 {
   243     [EAGLContext setCurrentContext:context];
   244 }
   245 
   246 - (void)swapBuffers
   247 {
   248     /* viewRenderbuffer should always be bound here. Code that binds something
   249        else is responsible for rebinding viewRenderbuffer, to reduce duplicate
   250        state changes. */
   251     [context presentRenderbuffer:GL_RENDERBUFFER];
   252 }
   253 
   254 - (void)layoutSubviews
   255 {
   256     [super layoutSubviews];
   257 
   258     [EAGLContext setCurrentContext:context];
   259     [self updateFrame];
   260 }
   261 
   262 - (void)destroyFramebuffer
   263 {
   264     if (viewFramebuffer != 0) {
   265         glDeleteFramebuffers(1, &viewFramebuffer);
   266         viewFramebuffer = 0;
   267     }
   268 
   269     if (viewRenderbuffer != 0) {
   270         glDeleteRenderbuffers(1, &viewRenderbuffer);
   271         viewRenderbuffer = 0;
   272     }
   273 
   274     if (depthRenderbuffer != 0) {
   275         glDeleteRenderbuffers(1, &depthRenderbuffer);
   276         depthRenderbuffer = 0;
   277     }
   278 }
   279 
   280 
   281 - (void)dealloc
   282 {
   283     if ([EAGLContext currentContext] == context) {
   284         [self destroyFramebuffer];
   285         [EAGLContext setCurrentContext:nil];
   286     }
   287 }
   288 
   289 @end
   290 
   291 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   292 
   293 /* vi: set ts=4 sw=4 expandtab: */