src/video/uikit/SDL_uikitopenglview.m
author Sam Lantinga
Fri, 27 Jan 2017 06:05:50 -0800
changeset 10856 486aa38c6a88
parent 10737 3406a0f8b041
child 11811 5d94cb6b24d3
permissions -rw-r--r--
Added Thrustmaster Wheel FFB entry to the list of wheels
     1 /*
     2   Simple DirectMedia Layer
     3   Copyright (C) 1997-2017 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 <OpenGLES/EAGLDrawable.h>
    26 #include <OpenGLES/ES2/glext.h>
    27 #import "SDL_uikitopenglview.h"
    28 #include "SDL_uikitwindow.h"
    29 
    30 @implementation SDL_uikitopenglview {
    31     /* The renderbuffer and framebuffer used to render to this layer. */
    32     GLuint viewRenderbuffer, viewFramebuffer;
    33 
    34     /* The depth buffer that is attached to viewFramebuffer, if it exists. */
    35     GLuint depthRenderbuffer;
    36 
    37     GLenum colorBufferFormat;
    38 
    39     /* format of depthRenderbuffer */
    40     GLenum depthBufferFormat;
    41 
    42     /* The framebuffer and renderbuffer used for rendering with MSAA. */
    43     GLuint msaaFramebuffer, msaaRenderbuffer;
    44 
    45     /* The number of MSAA samples. */
    46     int samples;
    47 
    48     BOOL retainedBacking;
    49 }
    50 
    51 @synthesize context;
    52 @synthesize backingWidth;
    53 @synthesize backingHeight;
    54 
    55 + (Class)layerClass
    56 {
    57     return [CAEAGLLayer class];
    58 }
    59 
    60 - (instancetype)initWithFrame:(CGRect)frame
    61                         scale:(CGFloat)scale
    62                 retainBacking:(BOOL)retained
    63                         rBits:(int)rBits
    64                         gBits:(int)gBits
    65                         bBits:(int)bBits
    66                         aBits:(int)aBits
    67                     depthBits:(int)depthBits
    68                   stencilBits:(int)stencilBits
    69                          sRGB:(BOOL)sRGB
    70                  multisamples:(int)multisamples
    71                       context:(EAGLContext *)glcontext
    72 {
    73     if ((self = [super initWithFrame:frame])) {
    74         const BOOL useStencilBuffer = (stencilBits != 0);
    75         const BOOL useDepthBuffer = (depthBits != 0);
    76         NSString *colorFormat = nil;
    77 
    78         context = glcontext;
    79         samples = multisamples;
    80         retainedBacking = retained;
    81 
    82         if (!context || ![EAGLContext setCurrentContext:context]) {
    83             SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
    84             return nil;
    85         }
    86 
    87         if (samples > 0) {
    88             GLint maxsamples = 0;
    89             glGetIntegerv(GL_MAX_SAMPLES, &maxsamples);
    90 
    91             /* Clamp the samples to the max supported count. */
    92             samples = MIN(samples, maxsamples);
    93         }
    94 
    95         if (sRGB) {
    96             /* sRGB EAGL drawable support was added in iOS 7. */
    97             if (UIKit_IsSystemVersionAtLeast(7.0)) {
    98                 colorFormat = kEAGLColorFormatSRGBA8;
    99                 colorBufferFormat = GL_SRGB8_ALPHA8;
   100             } else {
   101                 SDL_SetError("sRGB drawables are not supported.");
   102                 return nil;
   103             }
   104         } else if (rBits >= 8 || gBits >= 8 || bBits >= 8) {
   105             /* if user specifically requests rbg888 or some color format higher than 16bpp */
   106             colorFormat = kEAGLColorFormatRGBA8;
   107             colorBufferFormat = GL_RGBA8;
   108         } else {
   109             /* default case (potentially faster) */
   110             colorFormat = kEAGLColorFormatRGB565;
   111             colorBufferFormat = GL_RGB565;
   112         }
   113 
   114         CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
   115 
   116         eaglLayer.opaque = YES;
   117         eaglLayer.drawableProperties = @{
   118             kEAGLDrawablePropertyRetainedBacking:@(retained),
   119             kEAGLDrawablePropertyColorFormat:colorFormat
   120         };
   121 
   122         /* Set the appropriate scale (for retina display support) */
   123         self.contentScaleFactor = scale;
   124 
   125         /* Create the color Renderbuffer Object */
   126         glGenRenderbuffers(1, &viewRenderbuffer);
   127         glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   128 
   129         if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
   130             SDL_SetError("Failed to create OpenGL ES drawable");
   131             return nil;
   132         }
   133 
   134         /* Create the Framebuffer Object */
   135         glGenFramebuffers(1, &viewFramebuffer);
   136         glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
   137 
   138         /* attach the color renderbuffer to the FBO */
   139         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
   140 
   141         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
   142         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
   143 
   144         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
   145             SDL_SetError("Failed creating OpenGL ES framebuffer");
   146             return nil;
   147         }
   148 
   149         /* When MSAA is used we'll use a separate framebuffer for rendering to,
   150          * since we'll need to do an explicit MSAA resolve before presenting. */
   151         if (samples > 0) {
   152             glGenFramebuffers(1, &msaaFramebuffer);
   153             glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
   154 
   155             glGenRenderbuffers(1, &msaaRenderbuffer);
   156             glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
   157 
   158             glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
   159 
   160             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
   161         }
   162 
   163         if (useDepthBuffer || useStencilBuffer) {
   164             if (useStencilBuffer) {
   165                 /* Apparently you need to pack stencil and depth into one buffer. */
   166                 depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
   167             } else if (useDepthBuffer) {
   168                 /* iOS only uses 32-bit float (exposed as fixed point 24-bit)
   169                  * depth buffers. */
   170                 depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
   171             }
   172 
   173             glGenRenderbuffers(1, &depthRenderbuffer);
   174             glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
   175 
   176             if (samples > 0) {
   177                 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
   178             } else {
   179                 glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
   180             }
   181 
   182             if (useDepthBuffer) {
   183                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
   184             }
   185             if (useStencilBuffer) {
   186                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
   187             }
   188         }
   189 
   190         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
   191             SDL_SetError("Failed creating OpenGL ES framebuffer");
   192             return nil;
   193         }
   194 
   195         glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   196 
   197         [self setDebugLabels];
   198     }
   199 
   200     return self;
   201 }
   202 
   203 - (GLuint)drawableRenderbuffer
   204 {
   205     return viewRenderbuffer;
   206 }
   207 
   208 - (GLuint)drawableFramebuffer
   209 {
   210     /* When MSAA is used, the MSAA draw framebuffer is used for drawing. */
   211     if (msaaFramebuffer) {
   212         return msaaFramebuffer;
   213     } else {
   214         return viewFramebuffer;
   215     }
   216 }
   217 
   218 - (GLuint)msaaResolveFramebuffer
   219 {
   220     /* When MSAA is used, the MSAA draw framebuffer is used for drawing and the
   221      * view framebuffer is used as a MSAA resolve framebuffer. */
   222     if (msaaFramebuffer) {
   223         return viewFramebuffer;
   224     } else {
   225         return 0;
   226     }
   227 }
   228 
   229 - (void)updateFrame
   230 {
   231     GLint prevRenderbuffer = 0;
   232     glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
   233 
   234     glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
   235     [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
   236 
   237     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
   238     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
   239 
   240     if (msaaRenderbuffer != 0) {
   241         glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
   242         glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
   243     }
   244 
   245     if (depthRenderbuffer != 0) {
   246         glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
   247 
   248         if (samples > 0) {
   249             glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
   250         } else {
   251             glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
   252         }
   253     }
   254 
   255     glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
   256 }
   257 
   258 - (void)setDebugLabels
   259 {
   260     if (viewFramebuffer != 0) {
   261         glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
   262     }
   263 
   264     if (viewRenderbuffer != 0) {
   265         glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
   266     }
   267 
   268     if (depthRenderbuffer != 0) {
   269         if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
   270             glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
   271         } else {
   272             glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
   273         }
   274     }
   275 
   276     if (msaaFramebuffer != 0) {
   277         glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO");
   278     }
   279 
   280     if (msaaRenderbuffer != 0) {
   281         glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer");
   282     }
   283 }
   284 
   285 - (void)swapBuffers
   286 {
   287     if (msaaFramebuffer) {
   288         const GLenum attachments[] = {GL_COLOR_ATTACHMENT0};
   289 
   290         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer);
   291 
   292         /* OpenGL ES 3+ provides explicit MSAA resolves via glBlitFramebuffer.
   293          * In OpenGL ES 1 and 2, MSAA resolves must be done via an extension. */
   294         if (context.API >= kEAGLRenderingAPIOpenGLES3) {
   295             int w = backingWidth;
   296             int h = backingHeight;
   297             glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
   298 
   299             if (!retainedBacking) {
   300                 /* Discard the contents of the MSAA drawable color buffer. */
   301                 glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
   302             }
   303         } else {
   304             glResolveMultisampleFramebufferAPPLE();
   305 
   306             if (!retainedBacking) {
   307                 glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
   308             }
   309         }
   310 
   311         /* We assume the "drawable framebuffer" (MSAA draw framebuffer) was
   312          * previously bound... */
   313         glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
   314     }
   315 
   316     /* viewRenderbuffer should always be bound here. Code that binds something
   317      * else is responsible for rebinding viewRenderbuffer, to reduce duplicate
   318      * state changes. */
   319     [context presentRenderbuffer:GL_RENDERBUFFER];
   320 }
   321 
   322 - (void)layoutSubviews
   323 {
   324     [super layoutSubviews];
   325 
   326     int width  = (int) (self.bounds.size.width * self.contentScaleFactor);
   327     int height = (int) (self.bounds.size.height * self.contentScaleFactor);
   328 
   329     /* Update the color and depth buffer storage if the layer size has changed. */
   330     if (width != backingWidth || height != backingHeight) {
   331         EAGLContext *prevContext = [EAGLContext currentContext];
   332         if (prevContext != context) {
   333             [EAGLContext setCurrentContext:context];
   334         }
   335 
   336         [self updateFrame];
   337 
   338         if (prevContext != context) {
   339             [EAGLContext setCurrentContext:prevContext];
   340         }
   341     }
   342 }
   343 
   344 - (void)destroyFramebuffer
   345 {
   346     if (viewFramebuffer != 0) {
   347         glDeleteFramebuffers(1, &viewFramebuffer);
   348         viewFramebuffer = 0;
   349     }
   350 
   351     if (viewRenderbuffer != 0) {
   352         glDeleteRenderbuffers(1, &viewRenderbuffer);
   353         viewRenderbuffer = 0;
   354     }
   355 
   356     if (depthRenderbuffer != 0) {
   357         glDeleteRenderbuffers(1, &depthRenderbuffer);
   358         depthRenderbuffer = 0;
   359     }
   360 
   361     if (msaaFramebuffer != 0) {
   362         glDeleteFramebuffers(1, &msaaFramebuffer);
   363         msaaFramebuffer = 0;
   364     }
   365 
   366     if (msaaRenderbuffer != 0) {
   367         glDeleteRenderbuffers(1, &msaaRenderbuffer);
   368         msaaRenderbuffer = 0;
   369     }
   370 }
   371 
   372 - (void)dealloc
   373 {
   374     if (context && context == [EAGLContext currentContext]) {
   375         [self destroyFramebuffer];
   376         [EAGLContext setCurrentContext:nil];
   377     }
   378 }
   379 
   380 @end
   381 
   382 #endif /* SDL_VIDEO_DRIVER_UIKIT */
   383 
   384 /* vi: set ts=4 sw=4 expandtab: */