summaryrefslogtreecommitdiff
path: root/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m
diff options
context:
space:
mode:
Diffstat (limited to 'contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m')
-rw-r--r--contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m377
1 files changed, 377 insertions, 0 deletions
diff --git a/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m
new file mode 100644
index 0000000..71d167f
--- /dev/null
+++ b/contrib/SDL-3.2.8/src/video/uikit/SDL_uikitopenglview.m
@@ -0,0 +1,377 @@
1/*
2 Simple DirectMedia Layer
3 Copyright (C) 1997-2025 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 defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_OPENGL_ES) || defined(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{
32 // The renderbuffer and framebuffer used to render to this layer.
33 GLuint viewRenderbuffer, viewFramebuffer;
34
35 // The depth buffer that is attached to viewFramebuffer, if it exists.
36 GLuint depthRenderbuffer;
37
38 GLenum colorBufferFormat;
39
40 // format of depthRenderbuffer
41 GLenum depthBufferFormat;
42
43 // The framebuffer and renderbuffer used for rendering with MSAA.
44 GLuint msaaFramebuffer, msaaRenderbuffer;
45
46 // The number of MSAA samples.
47 int samples;
48
49 BOOL retainedBacking;
50}
51
52@synthesize context;
53@synthesize backingWidth;
54@synthesize backingHeight;
55
56+ (Class)layerClass
57{
58 return [CAEAGLLayer class];
59}
60
61- (instancetype)initWithFrame:(CGRect)frame
62 scale:(CGFloat)scale
63 retainBacking:(BOOL)retained
64 rBits:(int)rBits
65 gBits:(int)gBits
66 bBits:(int)bBits
67 aBits:(int)aBits
68 depthBits:(int)depthBits
69 stencilBits:(int)stencilBits
70 sRGB:(BOOL)sRGB
71 multisamples:(int)multisamples
72 context:(EAGLContext *)glcontext
73{
74 if ((self = [super initWithFrame:frame])) {
75 const BOOL useStencilBuffer = (stencilBits != 0);
76 const BOOL useDepthBuffer = (depthBits != 0);
77 NSString *colorFormat = nil;
78
79 context = glcontext;
80 samples = multisamples;
81 retainedBacking = retained;
82
83 if (!context || ![EAGLContext setCurrentContext:context]) {
84 SDL_SetError("Could not create OpenGL ES drawable (could not make context current)");
85 return nil;
86 }
87
88 if (samples > 0) {
89 GLint maxsamples = 0;
90 glGetIntegerv(GL_MAX_SAMPLES, &maxsamples);
91
92 // Clamp the samples to the max supported count.
93 samples = SDL_min(samples, maxsamples);
94 }
95
96 if (sRGB) {
97 colorFormat = kEAGLColorFormatSRGBA8;
98 colorBufferFormat = GL_SRGB8_ALPHA8;
99 } else if (rBits >= 8 || gBits >= 8 || bBits >= 8 || aBits > 0) {
100 // if user specifically requests rbg888 or some color format higher than 16bpp
101 colorFormat = kEAGLColorFormatRGBA8;
102 colorBufferFormat = GL_RGBA8;
103 } else {
104 // default case (potentially faster)
105 colorFormat = kEAGLColorFormatRGB565;
106 colorBufferFormat = GL_RGB565;
107 }
108
109 CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
110
111 eaglLayer.opaque = YES;
112 eaglLayer.drawableProperties = @{
113 kEAGLDrawablePropertyRetainedBacking : @(retained),
114 kEAGLDrawablePropertyColorFormat : colorFormat
115 };
116
117 // Set the appropriate scale (for retina display support)
118 self.contentScaleFactor = scale;
119
120 // Create the color Renderbuffer Object
121 glGenRenderbuffers(1, &viewRenderbuffer);
122 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
123
124 if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
125 SDL_SetError("Failed to create OpenGL ES drawable");
126 return nil;
127 }
128
129 // Create the Framebuffer Object
130 glGenFramebuffers(1, &viewFramebuffer);
131 glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
132
133 // attach the color renderbuffer to the FBO
134 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer);
135
136 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
137 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
138
139 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
140 SDL_SetError("Failed creating OpenGL ES framebuffer");
141 return nil;
142 }
143
144 /* When MSAA is used we'll use a separate framebuffer for rendering to,
145 * since we'll need to do an explicit MSAA resolve before presenting. */
146 if (samples > 0) {
147 glGenFramebuffers(1, &msaaFramebuffer);
148 glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer);
149
150 glGenRenderbuffers(1, &msaaRenderbuffer);
151 glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
152
153 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
154
155 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer);
156 }
157
158 if (useDepthBuffer || useStencilBuffer) {
159 if (useStencilBuffer) {
160 // Apparently you need to pack stencil and depth into one buffer.
161 depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
162 } else if (useDepthBuffer) {
163 /* iOS only uses 32-bit float (exposed as fixed point 24-bit)
164 * depth buffers. */
165 depthBufferFormat = GL_DEPTH_COMPONENT24_OES;
166 }
167
168 glGenRenderbuffers(1, &depthRenderbuffer);
169 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
170
171 if (samples > 0) {
172 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
173 } else {
174 glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
175 }
176
177 if (useDepthBuffer) {
178 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
179 }
180 if (useStencilBuffer) {
181 glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer);
182 }
183 }
184
185 if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) {
186 SDL_SetError("Failed creating OpenGL ES framebuffer");
187 return nil;
188 }
189
190 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
191
192 [self setDebugLabels];
193 }
194
195 return self;
196}
197
198- (GLuint)drawableRenderbuffer
199{
200 return viewRenderbuffer;
201}
202
203- (GLuint)drawableFramebuffer
204{
205 // When MSAA is used, the MSAA draw framebuffer is used for drawing.
206 if (msaaFramebuffer) {
207 return msaaFramebuffer;
208 } else {
209 return viewFramebuffer;
210 }
211}
212
213- (GLuint)msaaResolveFramebuffer
214{
215 /* When MSAA is used, the MSAA draw framebuffer is used for drawing and the
216 * view framebuffer is used as a MSAA resolve framebuffer. */
217 if (msaaFramebuffer) {
218 return viewFramebuffer;
219 } else {
220 return 0;
221 }
222}
223
224- (void)updateFrame
225{
226 GLint prevRenderbuffer = 0;
227 glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer);
228
229 glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
230 [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer];
231
232 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth);
233 glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight);
234
235 if (msaaRenderbuffer != 0) {
236 glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer);
237 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight);
238 }
239
240 if (depthRenderbuffer != 0) {
241 glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer);
242
243 if (samples > 0) {
244 glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight);
245 } else {
246 glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight);
247 }
248 }
249
250 glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer);
251}
252
253- (void)setDebugLabels
254{
255 if (viewFramebuffer != 0) {
256 glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO");
257 }
258
259 if (viewRenderbuffer != 0) {
260 glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer");
261 }
262
263 if (depthRenderbuffer != 0) {
264 if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) {
265 glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer");
266 } else {
267 glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer");
268 }
269 }
270
271 if (msaaFramebuffer != 0) {
272 glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO");
273 }
274
275 if (msaaRenderbuffer != 0) {
276 glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer");
277 }
278}
279
280- (void)swapBuffers
281{
282 if (msaaFramebuffer) {
283 const GLenum attachments[] = { GL_COLOR_ATTACHMENT0 };
284
285 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer);
286
287 /* OpenGL ES 3+ provides explicit MSAA resolves via glBlitFramebuffer.
288 * In OpenGL ES 1 and 2, MSAA resolves must be done via an extension. */
289 if (context.API >= kEAGLRenderingAPIOpenGLES3) {
290 int w = backingWidth;
291 int h = backingHeight;
292 glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST);
293
294 if (!retainedBacking) {
295 // Discard the contents of the MSAA drawable color buffer.
296 glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
297 }
298 } else {
299 glResolveMultisampleFramebufferAPPLE();
300
301 if (!retainedBacking) {
302 glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
303 }
304 }
305
306 /* We assume the "drawable framebuffer" (MSAA draw framebuffer) was
307 * previously bound... */
308 glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
309 }
310
311 /* viewRenderbuffer should always be bound here. Code that binds something
312 * else is responsible for rebinding viewRenderbuffer, to reduce duplicate
313 * state changes. */
314 [context presentRenderbuffer:GL_RENDERBUFFER];
315}
316
317- (void)layoutSubviews
318{
319 [super layoutSubviews];
320
321 int width = (int)(self.bounds.size.width * self.contentScaleFactor);
322 int height = (int)(self.bounds.size.height * self.contentScaleFactor);
323
324 // Update the color and depth buffer storage if the layer size has changed.
325 if (width != backingWidth || height != backingHeight) {
326 EAGLContext *prevContext = [EAGLContext currentContext];
327 if (prevContext != context) {
328 [EAGLContext setCurrentContext:context];
329 }
330
331 [self updateFrame];
332
333 if (prevContext != context) {
334 [EAGLContext setCurrentContext:prevContext];
335 }
336 }
337}
338
339- (void)destroyFramebuffer
340{
341 if (viewFramebuffer != 0) {
342 glDeleteFramebuffers(1, &viewFramebuffer);
343 viewFramebuffer = 0;
344 }
345
346 if (viewRenderbuffer != 0) {
347 glDeleteRenderbuffers(1, &viewRenderbuffer);
348 viewRenderbuffer = 0;
349 }
350
351 if (depthRenderbuffer != 0) {
352 glDeleteRenderbuffers(1, &depthRenderbuffer);
353 depthRenderbuffer = 0;
354 }
355
356 if (msaaFramebuffer != 0) {
357 glDeleteFramebuffers(1, &msaaFramebuffer);
358 msaaFramebuffer = 0;
359 }
360
361 if (msaaRenderbuffer != 0) {
362 glDeleteRenderbuffers(1, &msaaRenderbuffer);
363 msaaRenderbuffer = 0;
364 }
365}
366
367- (void)dealloc
368{
369 if (context && context == [EAGLContext currentContext]) {
370 [self destroyFramebuffer];
371 [EAGLContext setCurrentContext:nil];
372 }
373}
374
375@end
376
377#endif // SDL_VIDEO_DRIVER_UIKIT