| // Copyright 2014 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "base/mac/scoped_nsautorelease_pool.h" |
| #include "ui/gl/gl_surface_ios.h" |
| #include "ui/gl/gl_context.h" |
| #include "ui/gl/gl_enums.h" |
| #include "base/logging.h" |
| |
| #import <OpenGLES/ES2/gl.h> |
| #import <OpenGLES/ES2/glext.h> |
| #import <QuartzCore/CAEAGLLayer.h> |
| |
| namespace gfx { |
| |
| #define WIDGET_AS_LAYER (reinterpret_cast<CAEAGLLayer*>(widget_)) |
| #define CAST_CONTEXT(c) (reinterpret_cast<EAGLContext*>((c))) |
| |
| GLSurfaceIOS::GLSurfaceIOS(gfx::AcceleratedWidget widget, |
| const gfx::SurfaceConfiguration requested_configuration) |
| : GLSurface(requested_configuration), |
| widget_(widget), |
| framebuffer_(GL_NONE), |
| colorbuffer_(GL_NONE), |
| depthbuffer_(GL_NONE), |
| stencilbuffer_(GL_NONE), |
| depth_stencil_packed_buffer_(GL_NONE), |
| last_configured_size_(), |
| framebuffer_setup_complete_(false) { |
| } |
| |
| #ifndef NDEBUG |
| static void GLSurfaceIOS_AssertFramebufferCompleteness(void) { |
| GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER); |
| DLOG_IF(FATAL, status != GL_FRAMEBUFFER_COMPLETE) |
| << "Framebuffer incomplete on GLSurfaceIOS::MakeCurrent: " |
| << GLEnums::GetStringEnum(status); |
| } |
| #else |
| #define GLSurfaceIOS_AssertFramebufferCompleteness(...) |
| #endif |
| |
| bool GLSurfaceIOS::OnMakeCurrent(GLContext* context) { |
| Size new_size = GetSize(); |
| |
| if (new_size == last_configured_size_) { |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); |
| GLSurfaceIOS_AssertFramebufferCompleteness(); |
| return true; |
| } |
| |
| base::mac::ScopedNSAutoreleasePool pool; |
| |
| SetupFramebufferIfNecessary(); |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| auto context_handle = context->GetHandle(); |
| DCHECK(context_handle); |
| |
| BOOL res = [CAST_CONTEXT(context_handle) renderbufferStorage:GL_RENDERBUFFER |
| fromDrawable:WIDGET_AS_LAYER]; |
| |
| if (!res) { |
| return false; |
| } |
| |
| GLint width = 0; |
| GLint height = 0; |
| bool rebind_color_buffer = false; |
| if (depthbuffer_ != GL_NONE |
| || stencilbuffer_ != GL_NONE |
| || depth_stencil_packed_buffer_ != GL_NONE) { |
| // Fetch the dimensions of the color buffer whose backing was just updated |
| // so that backing of the attachments can be updated |
| |
| glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_WIDTH, |
| &width); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| glGetRenderbufferParameteriv(GL_RENDERBUFFER, GL_RENDERBUFFER_HEIGHT, |
| &height); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| rebind_color_buffer = true; |
| } |
| |
| if (depth_stencil_packed_buffer_ != GL_NONE) { |
| glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_packed_buffer_); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8_OES, |
| width, height); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| if (depthbuffer_ != GL_NONE) { |
| glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_DEPTH_COMPONENT16, width, height); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| if (stencilbuffer_ != GL_NONE) { |
| glBindRenderbuffer(GL_RENDERBUFFER, stencilbuffer_); |
| glRenderbufferStorage(GL_RENDERBUFFER, GL_STENCIL_INDEX8, width, height); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| if (rebind_color_buffer) { |
| glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| last_configured_size_ = new_size; |
| GLSurfaceIOS_AssertFramebufferCompleteness(); |
| return true; |
| } |
| |
| void GLSurfaceIOS::SetupFramebufferIfNecessary() { |
| if (framebuffer_setup_complete_) { |
| return; |
| } |
| |
| DCHECK(framebuffer_ == GL_NONE); |
| DCHECK(colorbuffer_ == GL_NONE); |
| |
| DCHECK(widget_ != kNullAcceleratedWidget); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| // Generate the framebuffer |
| |
| glGenFramebuffers(1, &framebuffer_); |
| DCHECK(framebuffer_ != GL_NONE); |
| |
| glBindFramebuffer(GL_FRAMEBUFFER, framebuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| // Setup color attachment |
| |
| glGenRenderbuffers(1, &colorbuffer_); |
| DCHECK(colorbuffer_ != GL_NONE); |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, |
| GL_RENDERBUFFER, colorbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| auto config = get_surface_configuration(); |
| |
| // On iOS, if both depth and stencil attachments are requested, we are |
| // required to create a single renderbuffer that acts as both. |
| |
| auto requires_packed = (config.depth_bits != 0) && (config.stencil_bits != 0); |
| |
| if (requires_packed) { |
| glGenRenderbuffers(1, &depth_stencil_packed_buffer_); |
| glBindRenderbuffer(GL_RENDERBUFFER, depth_stencil_packed_buffer_); |
| DCHECK(depth_stencil_packed_buffer_ != GL_NONE); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, depth_stencil_packed_buffer_); |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, depth_stencil_packed_buffer_); |
| DCHECK(depth_stencil_packed_buffer_ != GL_NONE); |
| } else { |
| // Setup the depth attachment if necessary |
| |
| if (config.depth_bits != 0) { |
| glGenRenderbuffers(1, &depthbuffer_); |
| DCHECK(depthbuffer_ != GL_NONE); |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, depthbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_ATTACHMENT, |
| GL_RENDERBUFFER, depthbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| if (config.stencil_bits != 0) { |
| // Setup the stencil attachment if necessary |
| |
| glGenRenderbuffers(1, &stencilbuffer_); |
| DCHECK(stencilbuffer_ != GL_NONE); |
| |
| glBindRenderbuffer(GL_RENDERBUFFER, stencilbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| glFramebufferRenderbuffer(GL_FRAMEBUFFER, GL_STENCIL_ATTACHMENT, |
| GL_RENDERBUFFER, stencilbuffer_); |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| } |
| |
| // The default is RGBA |
| NSString *drawableColorFormat = kEAGLColorFormatRGBA8; |
| |
| if (config.red_bits <= 5 |
| && config.green_bits <= 6 |
| && config.blue_bits <= 5 |
| && config.alpha_bits == 0) { |
| drawableColorFormat = kEAGLColorFormatRGB565; |
| } |
| |
| WIDGET_AS_LAYER.drawableProperties = @{ |
| kEAGLDrawablePropertyColorFormat : drawableColorFormat, |
| kEAGLDrawablePropertyRetainedBacking : @(NO), |
| }; |
| |
| framebuffer_setup_complete_ = true; |
| } |
| |
| bool GLSurfaceIOS::SwapBuffers() { |
| glBindRenderbuffer(GL_RENDERBUFFER, colorbuffer_); |
| return [[EAGLContext currentContext] presentRenderbuffer:GL_RENDERBUFFER]; |
| } |
| |
| unsigned int GLSurfaceIOS::GetBackingFrameBufferObject() { |
| return framebuffer_; |
| } |
| |
| void GLSurfaceIOS::Destroy() { |
| DCHECK(glGetError() == GL_NO_ERROR); |
| |
| glDeleteFramebuffers(1, &framebuffer_); |
| glDeleteRenderbuffers(1, &colorbuffer_); |
| // Deletes on GL_NONEs are ignored |
| glDeleteRenderbuffers(1, &depthbuffer_); |
| glDeleteRenderbuffers(1, &stencilbuffer_); |
| glDeleteRenderbuffers(1, &depth_stencil_packed_buffer_); |
| |
| DCHECK(glGetError() == GL_NO_ERROR); |
| } |
| |
| bool GLSurfaceIOS::IsOffscreen() { |
| return widget_ == kNullAcceleratedWidget; |
| } |
| |
| gfx::Size GLSurfaceIOS::GetSize() { |
| CGSize layer_size = WIDGET_AS_LAYER.bounds.size; |
| return Size(layer_size.width, layer_size.height); |
| } |
| |
| void* GLSurfaceIOS::GetHandle() { |
| return (void*)widget_; |
| } |
| |
| bool GLSurface::InitializeOneOffInternal() { |
| // On EGL, this method is used to perfom one-time initialization tasks like |
| // initializing the display, setting up config lists, etc. There is no such |
| // setup on iOS. |
| return true; |
| } |
| |
| // static |
| scoped_refptr<GLSurface> GLSurface::CreateViewGLSurface( |
| gfx::AcceleratedWidget window, |
| const gfx::SurfaceConfiguration& requested_configuration) { |
| DCHECK(window != kNullAcceleratedWidget); |
| scoped_refptr<GLSurfaceIOS> surface = |
| new GLSurfaceIOS(window, requested_configuration); |
| |
| if (!surface->Initialize()) |
| return NULL; |
| |
| return surface; |
| } |
| |
| } // namespace gfx |