Atlas - SDL_uikitopenglview.m
Home / ext / SDL / src / video / uikit Lines: 1 | Size: 12730 bytes [Download] [Show on GitHub] [Search similar files] [Raw] [Raw (proxy)][FILE BEGIN]1/* 2 Simple DirectMedia Layer 3 Copyright (C) 1997-2025 Sam Lantinga <[email protected]> 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:(int)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 > 0) { 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 378[FILE END](C) 2025 0x4248 (C) 2025 4248 Media and 4248 Systems, All part of 0x4248 See LICENCE files for more information. Not all files are by 0x4248 always check Licencing.