2 Simple DirectMedia Layer
3 Copyright (C) 1997-2020 Sam Lantinga <slouken@libsdl.org>
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.
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:
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.
21 #include "../../SDL_internal.h"
23 #if SDL_VIDEO_DRIVER_UIKIT && (SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2)
25 #include <OpenGLES/EAGLDrawable.h>
26 #include <OpenGLES/ES2/glext.h>
27 #import "SDL_uikitopenglview.h"
28 #include "SDL_uikitwindow.h"
30 @implementation SDL_uikitopenglview {
31 /* The renderbuffer and framebuffer used to render to this layer. */
32 GLuint viewRenderbuffer, viewFramebuffer;
34 /* The depth buffer that is attached to viewFramebuffer, if it exists. */
35 GLuint depthRenderbuffer;
37 GLenum colorBufferFormat;
39 /* format of depthRenderbuffer */
40 GLenum depthBufferFormat;
42 /* The framebuffer and renderbuffer used for rendering with MSAA. */
43 GLuint msaaFramebuffer, msaaRenderbuffer;
45 /* The number of MSAA samples. */
52 @synthesize backingWidth;
53 @synthesize backingHeight;
57 return [CAEAGLLayer class];
60 - (instancetype)initWithFrame:(CGRect)frame
62 retainBacking:(BOOL)retained
67 depthBits:(int)depthBits
68 stencilBits:(int)stencilBits
70 multisamples:(int)multisamples
71 context:(EAGLContext *)glcontext
73 if ((self = [super initWithFrame:frame])) {
74 const BOOL useStencilBuffer = (stencilBits != 0);
75 const BOOL useDepthBuffer = (depthBits != 0);
76 NSString *colorFormat = nil;
79 samples = multisamples;
80 retainedBacking = retained;
82 if (!context || ![EAGLContext setCurrentContext:context]) {
83 SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
89 glGetIntegerv(GL_MAX_SAMPLES, &maxsamples);
91 /* Clamp the samples to the max supported count. */
92 samples = MIN(samples, maxsamples);
96 /* sRGB EAGL drawable support was added in iOS 7. */
97 if (UIKit_IsSystemVersionAtLeast(7.0)) {
98 colorFormat = kEAGLColorFormatSRGBA8;
99 colorBufferFormat = GL_SRGB8_ALPHA8;
101 SDL_SetError("sRGB drawables are not supported.");
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;
109 /* default case (potentially faster) */
110 colorFormat = kEAGLColorFormatRGB565;
111 colorBufferFormat = GL_RGB565;
114 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
116 eaglLayer.opaque = YES;
117 eaglLayer.drawableProperties = @{
118 kEAGLDrawablePropertyRetainedBacking:@(retained),
119 kEAGLDrawablePropertyColorFormat:colorFormat
122 /* Set the appropriate scale (for retina display support) */
123 self.contentScaleFactor = scale;
125 /* Create the color Renderbuffer Object */
126 glGenRenderbuffers(1, &viewRenderbuffer);
127 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
129 if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
130 SDL_SetError("Failed to create OpenGL ES drawable");
134 /* Create the Framebuffer Object */
135 glGenFramebuffers(1, &viewFramebuffer);
136 glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
138 /* attach the color renderbuffer to the FBO */
139 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
141 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
142 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
144 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
145 SDL_SetError("Failed creating OpenGL ES framebuffer");
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. */
152 glGenFramebuffers(1, &msaaFramebuffer);
153 glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
155 glGenRenderbuffers(1, &msaaRenderbuffer);
156 glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
158 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
160 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
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)
170 depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
173 glGenRenderbuffers(1, &depthRenderbuffer);
174 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
177 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
179 glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
182 if (useDepthBuffer) {
183 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
185 if (useStencilBuffer) {
186 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
190 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
191 SDL_SetError("Failed creating OpenGL ES framebuffer");
195 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
197 [self setDebugLabels];
203 - (GLuint)drawableRenderbuffer
205 return viewRenderbuffer;
208 - (GLuint)drawableFramebuffer
210 /* When MSAA is used, the MSAA draw framebuffer is used for drawing. */
211 if (msaaFramebuffer) {
212 return msaaFramebuffer;
214 return viewFramebuffer;
218 - (GLuint)msaaResolveFramebuffer
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;
231 GLint prevRenderbuffer = 0;
232 glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
234 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
235 [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
237 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
238 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
240 if (msaaRenderbuffer != 0) {
241 glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
242 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
245 if (depthRenderbuffer != 0) {
246 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
249 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
251 glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
255 glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
258 - (void)setDebugLabels
260 if (viewFramebuffer != 0) {
261 glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
264 if (viewRenderbuffer != 0) {
265 glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
268 if (depthRenderbuffer != 0) {
269 if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
270 glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
272 glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
276 if (msaaFramebuffer != 0) {
277 glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO");
280 if (msaaRenderbuffer != 0) {
281 glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer");
287 if (msaaFramebuffer) {
288 const GLenum attachments[] = {GL_COLOR_ATTACHMENT0};
290 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer);
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);
299 if (!retainedBacking) {
300 /* Discard the contents of the MSAA drawable color buffer. */
301 glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
304 glResolveMultisampleFramebufferAPPLE();
306 if (!retainedBacking) {
307 glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
311 /* We assume the "drawable framebuffer" (MSAA draw framebuffer) was
312 * previously bound... */
313 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
316 /* viewRenderbuffer should always be bound here. Code that binds something
317 * else is responsible for rebinding viewRenderbuffer, to reduce duplicate
319 [context presentRenderbuffer:GL_RENDERBUFFER];
322 - (void)layoutSubviews
324 [super layoutSubviews];
326 int width = (int) (self.bounds.size.width * self.contentScaleFactor);
327 int height = (int) (self.bounds.size.height * self.contentScaleFactor);
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];
338 if (prevContext != context) {
339 [EAGLContext setCurrentContext:prevContext];
344 - (void)destroyFramebuffer
346 if (viewFramebuffer != 0) {
347 glDeleteFramebuffers(1, &viewFramebuffer);
351 if (viewRenderbuffer != 0) {
352 glDeleteRenderbuffers(1, &viewRenderbuffer);
353 viewRenderbuffer = 0;
356 if (depthRenderbuffer != 0) {
357 glDeleteRenderbuffers(1, &depthRenderbuffer);
358 depthRenderbuffer = 0;
361 if (msaaFramebuffer != 0) {
362 glDeleteFramebuffers(1, &msaaFramebuffer);
366 if (msaaRenderbuffer != 0) {
367 glDeleteRenderbuffers(1, &msaaRenderbuffer);
368 msaaRenderbuffer = 0;
374 if (context && context == [EAGLContext currentContext]) {
375 [self destroyFramebuffer];
376 [EAGLContext setCurrentContext:nil];
382 #endif /* SDL_VIDEO_DRIVER_UIKIT */
384 /* vi: set ts=4 sw=4 expandtab: */