#include "SDL_internal.h"
#if defined(SDL_VIDEO_DRIVER_UIKIT) && (defined(SDL_VIDEO_OPENGL_ES) || defined(SDL_VIDEO_OPENGL_ES2))
#include <OpenGLES/EAGLDrawable.h>
#include <OpenGLES/ES2/glext.h>
#import "SDL_uikitopenglview.h"
#include "SDL_uikitwindow.h"
@implementation SDL_uikitopenglview
{
GLuint viewRenderbuffer, viewFramebuffer;
GLuint depthRenderbuffer;
GLenum colorBufferFormat;
GLenum depthBufferFormat;
GLuint msaaFramebuffer, msaaRenderbuffer;
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:(int)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);
samples = SDL_min(samples, maxsamples);
}
if (sRGB > 0) {
colorFormat = kEAGLColorFormatSRGBA8;
colorBufferFormat = GL_SRGB8_ALPHA8;
} else if (rBits >= 8 || gBits >= 8 || bBits >= 8 || aBits > 0) {
colorFormat = kEAGLColorFormatRGBA8;
colorBufferFormat = GL_RGBA8;
} else {
colorFormat = kEAGLColorFormatRGB565;
colorBufferFormat = GL_RGB565;
}
CAEAGLLayer *eaglLayer = (CAEAGLLayer *)self.layer;
eaglLayer.opaque = YES;
eaglLayer.drawableProperties = @{
kEAGLDrawablePropertyRetainedBacking : @(retained),
kEAGLDrawablePropertyColorFormat : colorFormat
};
self.contentScaleFactor = scale;
glGenRenderbuffers(1, &viewRenderbuffer);
glBindRenderbuffer(GL_RENDERBUFFER, viewRenderbuffer);
if (![context renderbufferStorage:GL_RENDERBUFFER fromDrawable:eaglLayer]) {
SDL_SetError("Failed to create OpenGL ES drawable");
return nil;
}
glGenFramebuffers(1, &viewFramebuffer);
glBindFramebuffer(GL_FRAMEBUFFER, viewFramebuffer);
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;
}
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) {
depthBufferFormat = GL_DEPTH24_STENCIL8_OES;
} else if (useDepthBuffer) {
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
{
if (msaaFramebuffer) {
return msaaFramebuffer;
} else {
return viewFramebuffer;
}
}
- (GLuint)msaaResolveFramebuffer
{
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);
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) {
glInvalidateFramebuffer(GL_READ_FRAMEBUFFER, 1, attachments);
}
} else {
glResolveMultisampleFramebufferAPPLE();
if (!retainedBacking) {
glDiscardFramebufferEXT(GL_READ_FRAMEBUFFER, 1, attachments);
}
}
glBindFramebuffer(GL_DRAW_FRAMEBUFFER, msaaFramebuffer);
}
[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);
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