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