Imported Upstream version 2.0.14
[platform/upstream/SDL.git] / src / video / uikit / SDL_uikitopenglview.m
1 /*
2   Simple DirectMedia Layer
3   Copyright (C) 1997-2020 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 && (SDL_VIDEO_OPENGL_ES || SDL_VIDEO_OPENGL_ES2)
24
25 #include <OpenGLES/EAGLDrawable.h>
26 #include <OpenGLES/ES2/glext.h>
27 #import "SDL_uikitopenglview.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. */
97             if (UIKit_IsSystemVersionAtLeast(7.0)) {
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 */
139         glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
140
141         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
142         glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
143
144         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
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
160             glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
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) {
183                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
184             }
185             if (useStencilBuffer) {
186                 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
187             }
188         }
189
190         if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
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
237     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
238     glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
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) {
288         const GLenum attachments[] = {GL_COLOR_ATTACHMENT0};
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: */