sdl

FORK: Simple Directmedia Layer
git clone https://git.neptards.moe/neptards/sdl.git
Log | Files | Refs

SDL_uikitopenglview.m (13042B)


      1 /*
      2   Simple DirectMedia Layer
      3   Copyright (C) 1997-2020 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 && (SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2)
     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 || aBits > 0) {
    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: */