SDL_uikitopenglview.m
Go to the documentation of this file.
/*
Simple DirectMedia Layer
Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
This software is provided 'as-is', without any express or implied
warranty. In no event will the authors be held liable for any damages
arising from the use of this software.
Permission is granted to anyone to use this software for any purpose,
including commercial applications, and to alter it and redistribute it
freely, subject to the following restrictions:
1. The origin of this software must not be misrepresented; you must not
claim that you wrote the original software. If you use this software
in a product, an acknowledgment in the product documentation would be
appreciated but is not required.
2. Altered source versions must be plainly marked as such, and must not be
misrepresented as being the original software.
3. This notice may not be removed or altered from any source distribution.
*/
#include "../../SDL_internal.h"
#if SDL_VIDEO_DRIVER_UIKIT
#include <OpenGLES/EAGLDrawable.h>
#include <OpenGLES/ES2/glext.h>
#import "SDL_uikitopenglview.h"
#include "SDL_uikitwindow.h"
@implementation SDL_uikitopenglview {
/* The renderbuffer and framebuffer used to render to this layer. */
GLuint viewRenderbuffer, viewFramebuffer;
/* The depth buffer that is attached to viewFramebuffer, if it exists. */
GLuint depthRenderbuffer;
GLenum colorBufferFormat;
/* format of depthRenderbuffer */
GLenum depthBufferFormat;
/* The framebuffer and renderbuffer used for rendering with MSAA. */
GLuint msaaFramebuffer, msaaRenderbuffer;
/* The number of MSAA samples. */
int samples;
BOOL retainedBacking;
}
@synthesize context;
@synthesize backingWidth;
@synthesize backingHeight;
+ (Class)layerClass
{
return [CAEAGLLayer class];
}
- (instancetype)initWithFrame:(CGRect)frame
scale:(CGFloat)scale
retainBacking:(BOOL)retained
rBits:(int)rBits
gBits:(int)gBits
bBits:(int)bBits
aBits:(int)aBits
depthBits:(int)depthBits
stencilBits:(int)stencilBits
sRGB:(BOOL)sRGB
multisamples:(int)multisamples
context:(EAGLContext *)glcontext
{
if ((self = [super initWithFrame:frame])) {
const BOOL useStencilBuffer = (stencilBits != 0);
const BOOL useDepthBuffer = (depthBits != 0);
NSString *colorFormat = nil;
context = glcontext;
samples = multisamples;
retainedBacking = retained;
if (!context || ![EAGLContext setCurrentContext:context]) {
SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
return nil;
}
if (samples > 0) {
GLint maxsamples = 0;
glGetIntegerv(GL_MAX_SAMPLES, &maxsamples);
/* Clamp the samples to the max supported count. */
samples = MIN(samples, maxsamples);
}
if (sRGB) {
/* sRGB EAGL drawable support was added in iOS 7. */
if (UIKit_IsSystemVersionAtLeast(7.0)) {
colorFormat = kEAGLColorFormatSRGBA8;
colorBufferFormat = GL_SRGB8_ALPHA8;
} else {
SDL_SetError("sRGB drawables are not supported.");
return nil;
}
} else if (rBits >= 8 || gBits >= 8 || bBits >= 8) {
/* if user specifically requests rbg888 or some color format higher than 16bpp */
colorFormat = kEAGLColorFormatRGBA8;
colorBufferFormat = GL_RGBA8;
} else {
/* default case (potentially faster) */
colorFormat = kEAGLColorFormatRGB565;
colorBufferFormat = GL_RGB565;
}
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking:@(retained),
kEAGLDrawablePropertyColorFormat:colorFormat
};
/* Set the appropriate scale (for retina display support) */
self.contentScaleFactor = scale;
/* Create the color Renderbuffer Object */
glGenRenderbuffers(1, &viewRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
SDL_SetError("Failed to create OpenGL ES drawable");
return nil;
}
/* Create the Framebuffer Object */
glGenFramebuffers(1, &viewFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
/* attach the color renderbuffer to the FBO */
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
SDL_SetError("Failed creating OpenGL ES framebuffer");
return nil;
}
/* When MSAA is used we'll use a separate framebuffer for rendering to,
* since we'll need to do an explicit MSAA resolve before presenting. */
if (samples > 0) {
glGenFramebuffers(1, &msaaFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
glGenRenderbuffers(1, &msaaRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
}
if (useDepthBuffer || useStencilBuffer) {
if (useStencilBuffer) {
/* Apparently you need to pack stencil and depth into one buffer. */
depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
} else if (useDepthBuffer) {
/* iOS only uses 32-bit float (exposed as fixed point 24-bit)
* depth buffers. */
depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
}
glGenRenderbuffers(1, &depthRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
if (samples > 0) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
} else {
glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
}
if (useDepthBuffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
}
if (useStencilBuffer) {
glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
}
}
if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
SDL_SetError("Failed creating OpenGL ES framebuffer");
return nil;
}
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[self setDebugLabels];
}
return self;
}
- (GLuint)drawableRenderbuffer
{
return viewRenderbuffer;
}
- (GLuint)drawableFramebuffer
{
/* When MSAA is used, the MSAA draw framebuffer is used for drawing. */
if (msaaFramebuffer) {
return msaaFramebuffer;
} else {
return viewFramebuffer;
}
}
- (GLuint)msaaResolveFramebuffer
{
/* When MSAA is used, the MSAA draw framebuffer is used for drawing and the
* view framebuffer is used as a MSAA resolve framebuffer. */
if (msaaFramebuffer) {
return viewFramebuffer;
} else {
return 0;
}
}
- (void)updateFrame
{
GLint prevRenderbuffer = 0;
glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
if (msaaRenderbuffer != 0) {
glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
}
if (depthRenderbuffer != 0) {
glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
if (samples > 0) {
glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
} else {
glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
}
}
glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
}
- (void)setDebugLabels
{
if (viewFramebuffer != 0) {
glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
}
if (viewRenderbuffer != 0) {
glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
}
if (depthRenderbuffer != 0) {
if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
} else {
glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
}
}
if (msaaFramebuffer != 0) {
glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO");
}
if (msaaRenderbuffer != 0) {
glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer");
}
}
- (void)swapBuffers
{
if (msaaFramebuffer) {
const GLenum attachments[] = {GL_COLOR_ATTACHMENT0};
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer);
/* OpenGL ES 3+ provides explicit MSAA resolves via glBlitFramebuffer.
* In OpenGL ES 1 and 2, MSAA resolves must be done via an extension. */
if (context.API >= kEAGLRenderingAPIOpenGLES3) {
int w = backingWidth;
int h = backingHeight;
glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
if (!retainedBacking) {
/* Discard the contents of the MSAA drawable color buffer. */
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
}
} else {
glResolveMultisampleFramebufferAPPLE();
if (!retainedBacking) {
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
}
}
/* We assume the "drawable framebuffer" (MSAA draw framebuffer) was
* previously bound... */
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
}
/* viewRenderbuffer should always be bound here. Code that binds something
* else is responsible for rebinding viewRenderbuffer, to reduce duplicate
* state changes. */
[context presentRenderbuffer:GL_RENDERBUFFER];
}
- (void)layoutSubviews
{
[super layoutSubviews];
int width = (int) (self.bounds.size.width * self.contentScaleFactor);
int height = (int) (self.bounds.size.height * self.contentScaleFactor);
/* Update the color and depth buffer storage if the layer size has changed. */
if (width != backingWidth || height != backingHeight) {
EAGLContext *prevContext = [EAGLContext currentContext];
if (prevContext != context) {
[EAGLContext setCurrentContext:context];
}
[self updateFrame];
if (prevContext != context) {
[EAGLContext setCurrentContext:prevContext];
}
}
}
- (void)destroyFramebuffer
{
if (viewFramebuffer != 0) {
glDeleteFramebuffers(1, &viewFramebuffer);
viewFramebuffer = 0;
}
if (viewRenderbuffer != 0) {
glDeleteRenderbuffers(1, &viewRenderbuffer);
viewRenderbuffer = 0;
}
if (depthRenderbuffer != 0) {
glDeleteRenderbuffers(1, &depthRenderbuffer);
depthRenderbuffer = 0;
}
if (msaaFramebuffer != 0) {
glDeleteFramebuffers(1, &msaaFramebuffer);
msaaFramebuffer = 0;
}
if (msaaRenderbuffer != 0) {
glDeleteRenderbuffers(1, &msaaRenderbuffer);
msaaRenderbuffer = 0;
}
}
- (void)dealloc
{
if (context && context == [EAGLContext currentContext]) {
[self destroyFramebuffer];
[EAGLContext setCurrentContext:nil];
}
}
@end
#endif /* SDL_VIDEO_DRIVER_UIKIT */
/* vi: set ts=4 sw=4 expandtab: */
scale
GLenum GLenum GLenum GLenum GLenum scale
Definition: SDL_opengl_glext.h:9375
UIKit_IsSystemVersionAtLeast
SDL_bool UIKit_IsSystemVersionAtLeast(double version)
glRenderbufferStorage
GL_APICALL void GL_APIENTRY glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
GL_DEPTH24_STENCIL8_OES
#define GL_DEPTH24_STENCIL8_OES
Definition: SDL_opengles2_gl2ext.h:320
glCheckFramebufferStatus
GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus(GLenum target)
SDL_uikitopenglview::drawableRenderbuffer
GLuint drawableRenderbuffer
Definition: SDL_uikitopenglview.h:50
GL_RENDERBUFFER
#define GL_RENDERBUFFER
Definition: SDL_opengl_glext.h:1052
GL_COLOR_ATTACHMENT0
#define GL_COLOR_ATTACHMENT0
Definition: SDL_opengl_glext.h:1033
GL_MAX_SAMPLES
#define GL_MAX_SAMPLES
Definition: SDL_opengl_glext.h:1067
GL_DEPTH_ATTACHMENT
#define GL_DEPTH_ATTACHMENT
Definition: SDL_opengl_glext.h:1049
glBindRenderbuffer
GL_APICALL void GL_APIENTRY glBindRenderbuffer(GLenum target, GLuint renderbuffer)
glBindFramebuffer
GL_APICALL void GL_APIENTRY glBindFramebuffer(GLenum target, GLuint framebuffer)
h
GLfloat GLfloat GLfloat GLfloat h
Definition: SDL_opengl_glext.h:1946
GL_RGBA8
#define GL_RGBA8
Definition: SDL_opengl.h:788
GL_SRGB8_ALPHA8
#define GL_SRGB8_ALPHA8
Definition: SDL_opengl_glext.h:859
GLint
int GLint
Definition: SDL_opengl.h:182
SDL_uikitopenglview::backingHeight
int backingHeight
Definition: SDL_uikitopenglview.h:48
glFramebufferRenderbuffer
GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
SDL_uikitwindow.h
layer
GLenum GLuint GLint GLint layer
Definition: SDL_opengl_glext.h:1186
SDL_uikitopenglview::drawableFramebuffer
GLuint drawableFramebuffer
Definition: SDL_uikitopenglview.h:51
SDL_uikitopenglview::msaaResolveFramebuffer
GLuint msaaResolveFramebuffer
Definition: SDL_uikitopenglview.h:52
glGetRenderbufferParameteriv
GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params)
GL_FRAMEBUFFER_COMPLETE
#define GL_FRAMEBUFFER_COMPLETE
Definition: SDL_opengl_glext.h:1026
colorFormat
GLenum GLenum colorFormat
Definition: SDL_opengl_glext.h:9178
GL_FRAMEBUFFER
#define GL_FRAMEBUFFER
Definition: SDL_opengl_glext.h:1051
width
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
GL_RENDERBUFFER_HEIGHT
#define GL_RENDERBUFFER_HEIGHT
Definition: SDL_opengl_glext.h:1054
SDL_uikitopenglview
Definition: SDL_uikitopenglview.h:29
GL_STENCIL_ATTACHMENT
#define GL_STENCIL_ATTACHMENT
Definition: SDL_opengl_glext.h:1050
glDeleteRenderbuffers
GL_APICALL void GL_APIENTRY glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers)
frame
int frame
Definition: teststreaming.c:60
SDL_uikitopenglview::context
EAGLContext * context
Definition: SDL_uikitopenglview.h:44
GL_DRAW_FRAMEBUFFER
#define GL_DRAW_FRAMEBUFFER
Definition: SDL_opengl_glext.h:1018
w
GLubyte GLubyte GLubyte GLubyte w
Definition: SDL_opengl_glext.h:731
-[SDL_uikitopenglview swapBuffers]
void swapBuffers()
GL_RENDERBUFFER_WIDTH
#define GL_RENDERBUFFER_WIDTH
Definition: SDL_opengl_glext.h:1053
GL_RGB565
#define GL_RGB565
Definition: SDL_opengl_glext.h:1847
GL_COLOR_BUFFER_BIT
#define GL_COLOR_BUFFER_BIT
Definition: SDL_opengl.h:742
SDL_uikitopenglview.h
SDL_uikitopenglview::backingWidth
int backingWidth
Definition: SDL_uikitopenglview.h:47
GL_READ_FRAMEBUFFER
#define GL_READ_FRAMEBUFFER
Definition: SDL_opengl_glext.h:1017
-[SDL_uikitopenglview updateFrame]
void updateFrame()
GL_RENDERBUFFER_BINDING
#define GL_RENDERBUFFER_BINDING
Definition: SDL_opengl_glext.h:1016
GLenum
unsigned int GLenum
Definition: SDL_opengl.h:176
glGetIntegerv
GLAPI void GLAPIENTRY glGetIntegerv(GLenum pname, GLint *params)
glGenRenderbuffers
GL_APICALL void GL_APIENTRY glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)
GLuint
unsigned int GLuint
Definition: SDL_opengl.h:185
SDL_SetError
#define SDL_SetError
Definition: SDL_dynapi_overrides.h:30
height
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
void
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
Definition: SDL_dynapi_procs.h:89
glGenFramebuffers
GL_APICALL void GL_APIENTRY glGenFramebuffers(GLsizei n, GLuint *framebuffers)
GL_NEAREST
#define GL_NEAREST
Definition: SDL_opengl.h:704
GL_DEPTH_COMPONENT24_OES
#define GL_DEPTH_COMPONENT24_OES
Definition: SDL_opengles2_gl2ext.h:58
glDeleteFramebuffers
GL_APICALL void GL_APIENTRY glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
attachments
GLsizei const GLenum * attachments
Definition: SDL_opengl_glext.h:2458
samples
GLsizei samples
Definition: SDL_opengl_glext.h:1185
MIN
#define MIN(a, b)
Definition: SDL_rotate.h:23