src/video/uikit/SDL_uikitopenglview.m
author Alex Szpakowski <slime73@gmail.com>
Wed, 23 Jul 2014 03:05:31 -0300
branchiOS-improvements
changeset 9501 574db299498f
parent 9500 cbf5c5ecf5ac
child 9502 933ed557b7c1
permissions -rw-r--r--
More cleanup of the iOS Objective-C code.
slouken@2765
     1
/*
slouken@5535
     2
  Simple DirectMedia Layer
slouken@8149
     3
  Copyright (C) 1997-2014 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 <QuartzCore/QuartzCore.h>
slouken@6686
    26
#include <OpenGLES/EAGLDrawable.h>
slouken@6686
    27
#include "SDL_uikitopenglview.h"
slouken@6686
    28
#include "SDL_uikitmessagebox.h"
slouken@2765
    29
slouken@2765
    30
slime73@9501
    31
@implementation SDL_uikitopenglview {
slime73@9501
    32
slime73@9501
    33
    /* OpenGL names for the renderbuffer and framebuffers used to render to this view */
slime73@9501
    34
    GLuint viewRenderbuffer, viewFramebuffer;
slime73@9501
    35
slime73@9501
    36
    /* OpenGL name for the depth buffer that is attached to viewFramebuffer, if it exists (0 if it does not exist) */
slime73@9501
    37
    GLuint depthRenderbuffer;
slime73@9501
    38
slime73@9501
    39
    /* format of depthRenderbuffer */
slime73@9501
    40
    GLenum depthBufferFormat;
slime73@9501
    41
slime73@9501
    42
    id displayLink;
slime73@9501
    43
    int animationInterval;
slime73@9501
    44
    void (*animationCallback)(void*);
slime73@9501
    45
    void *animationCallbackParam;
slime73@9501
    46
slime73@9501
    47
}
slouken@2765
    48
slouken@2765
    49
@synthesize context;
slouken@2765
    50
slime73@9501
    51
@synthesize backingWidth;
slime73@9501
    52
@synthesize backingHeight;
slime73@9488
    53
kees@6003
    54
+ (Class)layerClass
kees@6003
    55
{
slouken@5129
    56
    return [CAEAGLLayer class];
slouken@2765
    57
}
slouken@2765
    58
kees@6003
    59
- (id)initWithFrame:(CGRect)frame
slime73@9498
    60
              scale:(CGFloat)scale
kees@6003
    61
      retainBacking:(BOOL)retained
slime73@9498
    62
              rBits:(int)rBits
slime73@9498
    63
              gBits:(int)gBits
slime73@9498
    64
              bBits:(int)bBits
slime73@9498
    65
              aBits:(int)aBits
slime73@9498
    66
          depthBits:(int)depthBits
slime73@9498
    67
        stencilBits:(int)stencilBits
slime73@9498
    68
               sRGB:(BOOL)sRGB
slime73@9498
    69
       majorVersion:(int)majorVersion
slime73@9498
    70
         shareGroup:(EAGLSharegroup*)shareGroup
slouken@2765
    71
{
icculus@6022
    72
    if ((self = [super initWithFrame:frame])) {
icculus@6022
    73
        const BOOL useStencilBuffer = (stencilBits != 0);
icculus@6022
    74
        const BOOL useDepthBuffer = (depthBits != 0);
icculus@6022
    75
        NSString *colorFormat = nil;
icculus@6022
    76
slime73@9498
    77
        self.autoresizingMask = UIViewAutoresizingFlexibleWidth | UIViewAutoresizingFlexibleHeight;
slime73@9498
    78
        self.autoresizesSubviews = YES;
slime73@9498
    79
slouken@8233
    80
        /* The EAGLRenderingAPI enum values currently map 1:1 to major GLES
slouken@8233
    81
           versions, and this allows us to handle future OpenGL ES versions.
slouken@8233
    82
         */
slouken@8233
    83
        EAGLRenderingAPI api = majorVersion;
slouken@8233
    84
slime73@9498
    85
        context = [[EAGLContext alloc] initWithAPI:api sharegroup:shareGroup];
slime73@9498
    86
        if (!context || ![EAGLContext setCurrentContext:context]) {
slime73@9498
    87
            [self release];
slime73@9498
    88
            SDL_SetError("OpenGL ES %d not supported", majorVersion);
slime73@9498
    89
            return nil;
slime73@9498
    90
        }
slime73@9498
    91
slime73@9498
    92
        BOOL hasiOS7 = [[UIDevice currentDevice].systemVersion compare:@"7.0" options:NSNumericSearch] != NSOrderedAscending;
slime73@9498
    93
        if (sRGB && hasiOS7) {
slime73@9500
    94
             /* sRGB EAGL drawable support was added in iOS 7 */
slime73@9498
    95
            colorFormat = kEAGLColorFormatSRGBA8;
slime73@9500
    96
        } else if (rBits >= 8 && gBits >= 8 && bBits >= 8) {
icculus@6022
    97
            /* if user specifically requests rbg888 or some color format higher than 16bpp */
icculus@6022
    98
            colorFormat = kEAGLColorFormatRGBA8;
icculus@6022
    99
        } else {
icculus@6022
   100
            /* default case (faster) */
icculus@6022
   101
            colorFormat = kEAGLColorFormatRGB565;
icculus@6021
   102
        }
kees@6001
   103
icculus@6022
   104
        /* Get the layer */
slouken@5129
   105
        CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
kees@6001
   106
slouken@5129
   107
        eaglLayer.opaque = YES;
slime73@9499
   108
        eaglLayer.drawableProperties = @{
slime73@9499
   109
            kEAGLDrawablePropertyRetainedBacking: @(retained),
slime73@9499
   110
            kEAGLDrawablePropertyColorFormat: colorFormat
slime73@9499
   111
        };
kees@6001
   112
slime73@9498
   113
        /* Set the appropriate scale (for retina display support) */
slime73@9498
   114
        self.contentScaleFactor = scale;
slime73@9498
   115
slime73@9498
   116
        /* Create the color Renderbuffer Object */
slime73@9498
   117
        glGenRenderbuffersOES(1, &viewRenderbuffer);
slime73@9498
   118
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
slime73@9498
   119
slime73@9498
   120
        if (![context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:eaglLayer]) {
slouken@5129
   121
            [self release];
slime73@9498
   122
            SDL_SetError("Failed creating OpenGL ES drawable");
slouken@5129
   123
            return nil;
slouken@5129
   124
        }
kees@6001
   125
slime73@9498
   126
        /* Create the Framebuffer Object */
slime73@9498
   127
        glGenFramebuffersOES(1, &viewFramebuffer);
slime73@9498
   128
        glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
slouken@6085
   129
slime73@9498
   130
        /* attach the color renderbuffer to the FBO */
slouken@5129
   131
        glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
kees@6001
   132
slouken@5129
   133
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
slouken@5129
   134
        glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
icculus@5527
   135
icculus@6021
   136
        if ((useDepthBuffer) || (useStencilBuffer)) {
icculus@6021
   137
            if (useStencilBuffer) {
icculus@6022
   138
                /* Apparently you need to pack stencil and depth into one buffer. */
icculus@6021
   139
                depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
icculus@6022
   140
            } else if (useDepthBuffer) {
icculus@6022
   141
                /* iOS only has 24-bit depth buffers, even with GL_DEPTH_COMPONENT16_OES */
icculus@6022
   142
                depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
icculus@6021
   143
            }
icculus@6022
   144
slouken@5129
   145
            glGenRenderbuffersOES(1, &depthRenderbuffer);
slouken@5129
   146
            glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
slouken@5129
   147
            glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthBufferFormat, backingWidth, backingHeight);
icculus@6021
   148
            if (useDepthBuffer) {
icculus@6021
   149
                glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_DEPTH_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
icculus@6021
   150
            }
icculus@6021
   151
            if (useStencilBuffer) {
icculus@6021
   152
                glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_STENCIL_ATTACHMENT_OES, GL_RENDERBUFFER_OES, depthRenderbuffer);
icculus@6021
   153
            }
slouken@5129
   154
        }
kees@6001
   155
kees@6003
   156
        if (glCheckFramebufferStatusOES(GL_FRAMEBUFFER_OES) != GL_FRAMEBUFFER_COMPLETE_OES) {
slime73@9498
   157
            [self release];
slime73@9498
   158
            SDL_SetError("Failed creating OpenGL ES framebuffer");
slime73@9498
   159
            return nil;
slouken@5129
   160
        }
icculus@7784
   161
icculus@7784
   162
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
slime73@9498
   163
    }
slouken@5130
   164
slouken@5129
   165
    return self;
slouken@2765
   166
}
slouken@2765
   167
kees@6003
   168
- (void)updateFrame
kees@6003
   169
{
icculus@5527
   170
    glBindFramebufferOES(GL_FRAMEBUFFER_OES, viewFramebuffer);
icculus@5527
   171
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, 0);
icculus@5527
   172
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, 0);
icculus@5527
   173
    glDeleteRenderbuffersOES(1, &viewRenderbuffer);
icculus@5527
   174
icculus@5527
   175
    glGenRenderbuffersOES(1, &viewRenderbuffer);
icculus@5527
   176
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
icculus@5527
   177
    [context renderbufferStorage:GL_RENDERBUFFER_OES fromDrawable:(CAEAGLLayer*)self.layer];
kees@6001
   178
    glFramebufferRenderbufferOES(GL_FRAMEBUFFER_OES, GL_COLOR_ATTACHMENT0_OES, GL_RENDERBUFFER_OES, viewRenderbuffer);
icculus@5527
   179
icculus@5527
   180
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_WIDTH_OES, &backingWidth);
icculus@5527
   181
    glGetRenderbufferParameterivOES(GL_RENDERBUFFER_OES, GL_RENDERBUFFER_HEIGHT_OES, &backingHeight);
icculus@5527
   182
icculus@5527
   183
    if (depthRenderbuffer != 0) {
icculus@5527
   184
        glBindRenderbufferOES(GL_RENDERBUFFER_OES, depthRenderbuffer);
icculus@5527
   185
        glRenderbufferStorageOES(GL_RENDERBUFFER_OES, depthBufferFormat, backingWidth, backingHeight);
icculus@5527
   186
    }
icculus@7784
   187
icculus@7784
   188
    glBindRenderbufferOES(GL_RENDERBUFFER_OES, viewRenderbuffer);
icculus@5527
   189
}
icculus@5527
   190
slouken@6342
   191
- (void)setAnimationCallback:(int)interval
slouken@6342
   192
    callback:(void (*)(void*))callback
slouken@6342
   193
    callbackParam:(void*)callbackParam
slouken@6342
   194
{
slouken@6342
   195
    [self stopAnimation];
slouken@6342
   196
slouken@6342
   197
    animationInterval = interval;
slouken@6342
   198
    animationCallback = callback;
slouken@6342
   199
    animationCallbackParam = callbackParam;
slouken@6342
   200
slime73@9492
   201
    if (animationCallback) {
slouken@6342
   202
        [self startAnimation];
slime73@9492
   203
    }
slouken@6342
   204
}
slouken@6342
   205
slouken@6342
   206
- (void)startAnimation
slouken@6342
   207
{
slouken@8891
   208
    displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(doLoop:)];
slouken@6342
   209
    [displayLink setFrameInterval:animationInterval];
slouken@6342
   210
    [displayLink addToRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
slouken@6342
   211
}
slouken@6342
   212
slouken@6342
   213
- (void)stopAnimation
slouken@6342
   214
{
slouken@6342
   215
    [displayLink invalidate];
slouken@6342
   216
    displayLink = nil;
slouken@6342
   217
}
slouken@6342
   218
slouken@8891
   219
- (void)doLoop:(CADisplayLink*)sender
slouken@6342
   220
{
slouken@7191
   221
    /* Don't run the game loop while a messagebox is up */
slouken@6686
   222
    if (!UIKit_ShowingMessageBox()) {
slouken@6686
   223
        animationCallback(animationCallbackParam);
slouken@6686
   224
    }
slouken@6342
   225
}
slouken@6342
   226
kees@6003
   227
- (void)setCurrentContext
kees@6003
   228
{
slouken@5129
   229
    [EAGLContext setCurrentContext:context];
slouken@2765
   230
}
slouken@2765
   231
slouken@2765
   232
kees@6003
   233
- (void)swapBuffers
kees@6003
   234
{
icculus@7784
   235
    /* viewRenderbuffer should always be bound here. Code that binds something
icculus@7784
   236
        else is responsible for rebinding viewRenderbuffer, to reduce
icculus@7784
   237
        duplicate state changes. */
slouken@5129
   238
    [context presentRenderbuffer:GL_RENDERBUFFER_OES];
slouken@2765
   239
}
slouken@2765
   240
slouken@2765
   241
kees@6003
   242
- (void)layoutSubviews
kees@6003
   243
{
slime73@9496
   244
    [super layoutSubviews];
slime73@9496
   245
slouken@5129
   246
    [EAGLContext setCurrentContext:context];
slouken@6435
   247
    [self updateFrame];
slouken@2765
   248
}
slouken@2765
   249
kees@6003
   250
- (void)destroyFramebuffer
kees@6003
   251
{
slime73@9498
   252
    if (viewFramebuffer != 0) {
slime73@9498
   253
        glDeleteFramebuffersOES(1, &viewFramebuffer);
slime73@9498
   254
        viewFramebuffer = 0;
slime73@9498
   255
    }
kees@6001
   256
slime73@9498
   257
    if (viewRenderbuffer != 0) {
slime73@9498
   258
        glDeleteRenderbuffersOES(1, &viewRenderbuffer);
slime73@9498
   259
        viewRenderbuffer = 0;
slime73@9498
   260
    }
slime73@9498
   261
slime73@9498
   262
    if (depthRenderbuffer != 0) {
slouken@5129
   263
        glDeleteRenderbuffersOES(1, &depthRenderbuffer);
slouken@5129
   264
        depthRenderbuffer = 0;
slouken@5129
   265
    }
slouken@2765
   266
}
slouken@2765
   267
slouken@2765
   268
kees@6003
   269
- (void)dealloc
kees@6003
   270
{
slouken@5129
   271
    [self destroyFramebuffer];
slouken@5129
   272
    if ([EAGLContext currentContext] == context) {
slouken@5129
   273
        [EAGLContext setCurrentContext:nil];
slouken@5129
   274
    }
kees@6001
   275
    [context release];
slouken@5129
   276
    [super dealloc];
slouken@2765
   277
}
slouken@2765
   278
slouken@2859
   279
@end
slouken@5132
   280
slouken@6044
   281
#endif /* SDL_VIDEO_DRIVER_UIKIT */
slouken@6044
   282
slouken@5132
   283
/* vi: set ts=4 sw=4 expandtab: */