diff options
Diffstat (limited to 'Source/external/SDL2/src/video/uikit/SDL_uikitopenglview.m')
-rw-r--r-- | Source/external/SDL2/src/video/uikit/SDL_uikitopenglview.m | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/Source/external/SDL2/src/video/uikit/SDL_uikitopenglview.m b/Source/external/SDL2/src/video/uikit/SDL_uikitopenglview.m new file mode 100644 index 0000000..9024376 --- /dev/null +++ b/Source/external/SDL2/src/video/uikit/SDL_uikitopenglview.m @@ -0,0 +1,384 @@ +/* + Simple DirectMedia Layer + Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org> + + This software is provided 'as-is', without any express or implied + warranty. In no event will the authors be held liable for any damages + arising from the use of this software. + + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it + freely, subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not + claim that you wrote the original software. If you use this software + in a product, an acknowledgment in the product documentation would be + appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be + misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. +*/ +#include "../../SDL_internal.h" + +#if SDL_VIDEO_DRIVER_UIKIT + +#include <OpenGLES/EAGLDrawable.h> +#include <OpenGLES/ES2/glext.h> +#import "SDL_uikitopenglview.h" +#include "SDL_uikitwindow.h" + +@implementation SDL_uikitopenglview { + /* The renderbuffer and framebuffer used to render to this layer. */ + GLuint viewRenderbuffer, viewFramebuffer; + + /* The depth buffer that is attached to viewFramebuffer, if it exists. */ + GLuint depthRenderbuffer; + + GLenum colorBufferFormat; + + /* format of depthRenderbuffer */ + GLenum depthBufferFormat; + + /* The framebuffer and renderbuffer used for rendering with MSAA. */ + GLuint msaaFramebuffer, msaaRenderbuffer; + + /* The number of MSAA samples. */ + int samples; + + BOOL retainedBacking; +} + +@synthesize context; +@synthesize backingWidth; +@synthesize backingHeight; + ++ (Class)layerClass +{ + return [CAEAGLLayer class]; +} + +- (instancetype)initWithFrame:(CGRect)frame + scale:(CGFloat)scale + retainBacking:(BOOL)retained + rBits:(int)rBits + gBits:(int)gBits + bBits:(int)bBits + aBits:(int)aBits + depthBits:(int)depthBits + stencilBits:(int)stencilBits + sRGB:(BOOL)sRGB + multisamples:(int)multisamples + context:(EAGLContext *)glcontext +{ + if ((self = [super initWithFrame:frame])) { + const BOOL useStencilBuffer = (stencilBits != 0); + const BOOL useDepthBuffer = (depthBits != 0); + NSString *colorFormat = nil; + + context = glcontext; + samples = multisamples; + retainedBacking = retained; + + if (!context || ![EAGLContext setCurrentContext:context]) { + SDL_SetError("Could not create OpenGL ES drawable (could not make context current)"); + return nil; + } + + if (samples > 0) { + GLint maxsamples = 0; + glGetIntegerv(GL_MAX_SAMPLES, &maxsamples); + + /* Clamp the samples to the max supported count. */ + samples = MIN(samples, maxsamples); + } + + if (sRGB) { + /* sRGB EAGL drawable support was added in iOS 7. */ + if (UIKit_IsSystemVersionAtLeast(7.0)) { + colorFormat = kEAGLColorFormatSRGBA8; + colorBufferFormat = GL_SRGB8_ALPHA8; + } else { + SDL_SetError("sRGB drawables are not supported."); + return nil; + } + } else if (rBits >= 8 || gBits >= 8 || bBits >= 8 || aBits > 0) { + /* if user specifically requests rbg888 or some color format higher than 16bpp */ + colorFormat = kEAGLColorFormatRGBA8; + colorBufferFormat = GL_RGBA8; + } else { + /* default case (potentially faster) */ + colorFormat = kEAGLColorFormatRGB565; + colorBufferFormat = GL_RGB565; + } + + CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer; + + eaglLayer.opaque = YES; + eaglLayer.drawableProperties = @{ + kEAGLDrawablePropertyRetainedBacking:@(retained), + kEAGLDrawablePropertyColorFormat:colorFormat + }; + + /* Set the appropriate scale (for retina display support) */ + self.contentScaleFactor = scale; + + /* Create the color Renderbuffer Object */ + glGenRenderbuffers(1, &viewRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); + + if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) { + SDL_SetError("Failed to create OpenGL ES drawable"); + return nil; + } + + /* Create the Framebuffer Object */ + glGenFramebuffers(1, &viewFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer); + + /* attach the color renderbuffer to the FBO */ + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, viewRenderbuffer); + + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SDL_SetError("Failed creating OpenGL ES framebuffer"); + return nil; + } + + /* When MSAA is used we'll use a separate framebuffer for rendering to, + * since we'll need to do an explicit MSAA resolve before presenting. */ + if (samples > 0) { + glGenFramebuffers(1, &msaaFramebuffer); + glBindFramebuffer(GL_FRAMEBUFFER, msaaFramebuffer); + + glGenRenderbuffers(1, &msaaRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer); + + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight); + + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, msaaRenderbuffer); + } + + if (useDepthBuffer || useStencilBuffer) { + if (useStencilBuffer) { + /* Apparently you need to pack stencil and depth into one buffer. */ + depthBufferFormat = GL_DEPTH24_STENCIL8_OES; + } else if (useDepthBuffer) { + /* iOS only uses 32-bit float (exposed as fixed point 24-bit) + * depth buffers. */ + depthBufferFormat = GL_DEPTH_COMPONENT24_OES; + } + + glGenRenderbuffers(1, &depthRenderbuffer); + glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); + + if (samples > 0) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight); + } else { + glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight); + } + + if (useDepthBuffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); + } + if (useStencilBuffer) { + glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, GL_RENDERBUFFER, depthRenderbuffer); + } + } + + if (glCheckFramebufferStatus(GL_FRAMEBUFFER) != GL_FRAMEBUFFER_COMPLETE) { + SDL_SetError("Failed creating OpenGL ES framebuffer"); + return nil; + } + + glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); + + [self setDebugLabels]; + } + + return self; +} + +- (GLuint)drawableRenderbuffer +{ + return viewRenderbuffer; +} + +- (GLuint)drawableFramebuffer +{ + /* When MSAA is used, the MSAA draw framebuffer is used for drawing. */ + if (msaaFramebuffer) { + return msaaFramebuffer; + } else { + return viewFramebuffer; + } +} + +- (GLuint)msaaResolveFramebuffer +{ + /* When MSAA is used, the MSAA draw framebuffer is used for drawing and the + * view framebuffer is used as a MSAA resolve framebuffer. */ + if (msaaFramebuffer) { + return viewFramebuffer; + } else { + return 0; + } +} + +- (void)updateFrame +{ + GLint prevRenderbuffer = 0; + glGetIntegerv(GL_RENDERBUFFER_BINDING, &prevRenderbuffer); + + glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer); + [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer *)self.layer]; + + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, &backingWidth); + glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, &backingHeight); + + if (msaaRenderbuffer != 0) { + glBindRenderbuffer(GL_RENDERBUFFER, msaaRenderbuffer); + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, colorBufferFormat, backingWidth, backingHeight); + } + + if (depthRenderbuffer != 0) { + glBindRenderbuffer(GL_RENDERBUFFER, depthRenderbuffer); + + if (samples > 0) { + glRenderbufferStorageMultisample(GL_RENDERBUFFER, samples, depthBufferFormat, backingWidth, backingHeight); + } else { + glRenderbufferStorage(GL_RENDERBUFFER, depthBufferFormat, backingWidth, backingHeight); + } + } + + glBindRenderbuffer(GL_RENDERBUFFER, prevRenderbuffer); +} + +- (void)setDebugLabels +{ + if (viewFramebuffer != 0) { + glLabelObjectEXT(GL_FRAMEBUFFER, viewFramebuffer, 0, "context FBO"); + } + + if (viewRenderbuffer != 0) { + glLabelObjectEXT(GL_RENDERBUFFER, viewRenderbuffer, 0, "context color buffer"); + } + + if (depthRenderbuffer != 0) { + if (depthBufferFormat == GL_DEPTH24_STENCIL8_OES) { + glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth-stencil buffer"); + } else { + glLabelObjectEXT(GL_RENDERBUFFER, depthRenderbuffer, 0, "context depth buffer"); + } + } + + if (msaaFramebuffer != 0) { + glLabelObjectEXT(GL_FRAMEBUFFER, msaaFramebuffer, 0, "context MSAA FBO"); + } + + if (msaaRenderbuffer != 0) { + glLabelObjectEXT(GL_RENDERBUFFER, msaaRenderbuffer, 0, "context MSAA renderbuffer"); + } +} + +- (void)swapBuffers +{ + if (msaaFramebuffer) { + const GLenum attachments[] = {GL_COLOR_ATTACHMENT0}; + + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, viewFramebuffer); + + /* OpenGL ES 3+ provides explicit MSAA resolves via glBlitFramebuffer. + * In OpenGL ES 1 and 2, MSAA resolves must be done via an extension. */ + if (context.API >= kEAGLRenderingAPIOpenGLES3) { + int w = backingWidth; + int h = backingHeight; + glBlitFramebuffer(0, 0, w, h, 0, 0, w, h, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + if (!retainedBacking) { + /* Discard the contents of the MSAA drawable color buffer. */ + glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments); + } + } else { + glResolveMultisampleFramebufferAPPLE(); + + if (!retainedBacking) { + glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments); + } + } + + /* We assume the "drawable framebuffer" (MSAA draw framebuffer) was + * previously bound... */ + glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer); + } + + /* viewRenderbuffer should always be bound here. Code that binds something + * else is responsible for rebinding viewRenderbuffer, to reduce duplicate + * state changes. */ + [context presentRenderbuffer:GL_RENDERBUFFER]; +} + +- (void)layoutSubviews +{ + [super layoutSubviews]; + + int width = (int) (self.bounds.size.width * self.contentScaleFactor); + int height = (int) (self.bounds.size.height * self.contentScaleFactor); + + /* Update the color and depth buffer storage if the layer size has changed. */ + if (width != backingWidth || height != backingHeight) { + EAGLContext *prevContext = [EAGLContext currentContext]; + if (prevContext != context) { + [EAGLContext setCurrentContext:context]; + } + + [self updateFrame]; + + if (prevContext != context) { + [EAGLContext setCurrentContext:prevContext]; + } + } +} + +- (void)destroyFramebuffer +{ + if (viewFramebuffer != 0) { + glDeleteFramebuffers(1, &viewFramebuffer); + viewFramebuffer = 0; + } + + if (viewRenderbuffer != 0) { + glDeleteRenderbuffers(1, &viewRenderbuffer); + viewRenderbuffer = 0; + } + + if (depthRenderbuffer != 0) { + glDeleteRenderbuffers(1, &depthRenderbuffer); + depthRenderbuffer = 0; + } + + if (msaaFramebuffer != 0) { + glDeleteFramebuffers(1, &msaaFramebuffer); + msaaFramebuffer = 0; + } + + if (msaaRenderbuffer != 0) { + glDeleteRenderbuffers(1, &msaaRenderbuffer); + msaaRenderbuffer = 0; + } +} + +- (void)dealloc +{ + if (context && context == [EAGLContext currentContext]) { + [self destroyFramebuffer]; + [EAGLContext setCurrentContext:nil]; + } +} + +@end + +#endif /* SDL_VIDEO_DRIVER_UIKIT */ + +/* vi: set ts=4 sw=4 expandtab: */ |