SDL 2.0
SDL_uikitopenglview.m
Go to the documentation of this file.
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2019 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>
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. */
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 */
140
143
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
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) {
184 }
185 if (useStencilBuffer) {
187 }
188 }
189
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
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) {
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: */
#define SDL_SetError
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
#define GL_RGBA8
Definition: SDL_opengl.h:788
unsigned int GLenum
Definition: SDL_opengl.h:176
unsigned int GLuint
Definition: SDL_opengl.h:185
#define GL_NEAREST
Definition: SDL_opengl.h:704
#define GL_COLOR_BUFFER_BIT
Definition: SDL_opengl.h:742
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
int GLint
Definition: SDL_opengl.h:182
GLAPI void GLAPIENTRY glGetIntegerv(GLenum pname, GLint *params)
GLsizei samples
GLenum GLenum GLenum GLenum GLenum scale
#define GL_RENDERBUFFER_WIDTH
#define GL_COLOR_ATTACHMENT0
#define GL_RGB565
#define GL_RENDERBUFFER_HEIGHT
#define GL_SRGB8_ALPHA8
#define GL_FRAMEBUFFER_COMPLETE
#define GL_DRAW_FRAMEBUFFER
#define GL_MAX_SAMPLES
#define GL_RENDERBUFFER
#define GL_FRAMEBUFFER
#define GL_RENDERBUFFER_BINDING
GLsizei const GLenum * attachments
#define GL_READ_FRAMEBUFFER
#define GL_DEPTH_ATTACHMENT
GLenum GLuint GLint GLint layer
GLenum GLenum colorFormat
#define GL_STENCIL_ATTACHMENT
GLfloat GLfloat GLfloat GLfloat h
GLubyte GLubyte GLubyte GLubyte w
GL_APICALL void GL_APIENTRY glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
GL_APICALL void GL_APIENTRY glBindRenderbuffer(GLenum target, GLuint renderbuffer)
GL_APICALL void GL_APIENTRY glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers)
GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus(GLenum target)
GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params)
GL_APICALL void GL_APIENTRY glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
GL_APICALL void GL_APIENTRY glGenFramebuffers(GLsizei n, GLuint *framebuffers)
GL_APICALL void GL_APIENTRY glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)
GL_APICALL void GL_APIENTRY glBindFramebuffer(GLenum target, GLuint framebuffer)
#define GL_DEPTH24_STENCIL8_OES
#define GL_DEPTH_COMPONENT24_OES
#define MIN(a, b)
Definition: SDL_rotate.h:26
SDL_bool UIKit_IsSystemVersionAtLeast(double version)
int frame
Definition: teststreaming.c:60
static screen_context_t context
Definition: video.c:25