SDL  2.0
SDL_uikitopenglview.m
Go to the documentation of this file.
1 /*
2  Simple DirectMedia Layer
3  Copyright (C) 1997-2018 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
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. */
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) {
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 */
140 
143 
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 
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 {
180  }
181 
182  if (useDepthBuffer) {
184  }
185  if (useStencilBuffer) {
187  }
188  }
189 
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 
204 {
205  return viewRenderbuffer;
206 }
207 
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 
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 
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 {
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) {
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: */
GLenum GLenum GLenum GLenum GLenum scale
SDL_bool UIKit_IsSystemVersionAtLeast(double version)
GL_APICALL void GL_APIENTRY glRenderbufferStorage(GLenum target, GLenum internalformat, GLsizei width, GLsizei height)
#define GL_DEPTH24_STENCIL8_OES
GL_APICALL GLenum GL_APIENTRY glCheckFramebufferStatus(GLenum target)
#define GL_RENDERBUFFER
#define GL_COLOR_ATTACHMENT0
#define GL_MAX_SAMPLES
#define GL_DEPTH_ATTACHMENT
GL_APICALL void GL_APIENTRY glBindRenderbuffer(GLenum target, GLuint renderbuffer)
GL_APICALL void GL_APIENTRY glBindFramebuffer(GLenum target, GLuint framebuffer)
GLfloat GLfloat GLfloat GLfloat h
#define GL_RGBA8
Definition: SDL_opengl.h:788
#define GL_SRGB8_ALPHA8
int GLint
Definition: SDL_opengl.h:182
GL_APICALL void GL_APIENTRY glFramebufferRenderbuffer(GLenum target, GLenum attachment, GLenum renderbuffertarget, GLuint renderbuffer)
GLenum GLuint GLint GLint layer
GL_APICALL void GL_APIENTRY glGetRenderbufferParameteriv(GLenum target, GLenum pname, GLint *params)
#define GL_FRAMEBUFFER_COMPLETE
GLenum GLenum colorFormat
#define GL_FRAMEBUFFER
GLint GLint GLsizei width
Definition: SDL_opengl.h:1572
#define GL_RENDERBUFFER_HEIGHT
#define GL_STENCIL_ATTACHMENT
GL_APICALL void GL_APIENTRY glDeleteRenderbuffers(GLsizei n, const GLuint *renderbuffers)
int frame
Definition: teststreaming.c:60
#define GL_DRAW_FRAMEBUFFER
GLubyte GLubyte GLubyte GLubyte w
#define GL_RENDERBUFFER_WIDTH
#define GL_RGB565
#define GL_COLOR_BUFFER_BIT
Definition: SDL_opengl.h:742
#define GL_READ_FRAMEBUFFER
#define GL_RENDERBUFFER_BINDING
unsigned int GLenum
Definition: SDL_opengl.h:176
GLAPI void GLAPIENTRY glGetIntegerv(GLenum pname, GLint *params)
GL_APICALL void GL_APIENTRY glGenRenderbuffers(GLsizei n, GLuint *renderbuffers)
unsigned int GLuint
Definition: SDL_opengl.h:185
#define SDL_SetError
GLint GLint GLsizei GLsizei height
Definition: SDL_opengl.h:1572
SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char int SDL_PRINTF_FORMAT_STRING const char const char SDL_SCANF_FORMAT_STRING const char return SDL_ThreadFunction const char void return Uint32 return Uint32 void
GL_APICALL void GL_APIENTRY glGenFramebuffers(GLsizei n, GLuint *framebuffers)
#define GL_NEAREST
Definition: SDL_opengl.h:704
#define GL_DEPTH_COMPONENT24_OES
GL_APICALL void GL_APIENTRY glDeleteFramebuffers(GLsizei n, const GLuint *framebuffers)
GLsizei const GLenum * attachments
GLsizei samples
#define MIN(a, b)
Definition: SDL_rotate.h:23