| // |
| // Copyright 2020 The ANGLE Project Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| // |
| |
| // WindowSurfaceEAGL.cpp: EAGL implementation of egl::Surface |
| |
| #import "common/platform.h" |
| |
| #if defined(ANGLE_ENABLE_EAGL) |
| |
| # import "libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h" |
| |
| # import "common/debug.h" |
| # import "libANGLE/Context.h" |
| # import "libANGLE/renderer/gl/FramebufferGL.h" |
| # import "libANGLE/renderer/gl/RendererGL.h" |
| # import "libANGLE/renderer/gl/StateManagerGL.h" |
| # import "libANGLE/renderer/gl/eagl/DisplayEAGL.h" |
| # import "libANGLE/renderer/gl/eagl/FunctionsEAGL.h" |
| |
| # import <QuartzCore/QuartzCore.h> |
| |
| # if defined(ANGLE_PLATFORM_MACCATALYST) && defined(ANGLE_CPU_ARM64) |
| |
| // TODO(dino): Necessary because CAEAGLLayer is not in the public QuartzCore headers in this |
| // configuration. |
| // TODO(dino): Check that this won't cause an application using ANGLE directly to be flagged |
| // for non-public API use on Apple's App Store. |
| @interface CAEAGLLayer : CALayer |
| @end |
| |
| # endif |
| |
| @interface WebSwapLayerEAGL : CAEAGLLayer { |
| EAGLContextObj mDisplayContext; |
| |
| bool initialized; |
| rx::SharedSwapState *mSwapState; |
| const rx::FunctionsGL *mFunctions; |
| |
| GLuint mReadFramebuffer; |
| } |
| - (id)initWithSharedState:(rx::SharedSwapState *)swapState |
| withContext:(EAGLContextObj)displayContext |
| withFunctions:(const rx::FunctionsGL *)functions; |
| @end |
| |
| @implementation WebSwapLayerEAGL |
| - (id)initWithSharedState:(rx::SharedSwapState *)swapState |
| withContext:(EAGLContextObj)displayContext |
| withFunctions:(const rx::FunctionsGL *)functions |
| { |
| self = [super init]; |
| if (self != nil) |
| { |
| mDisplayContext = displayContext; |
| |
| initialized = false; |
| mSwapState = swapState; |
| mFunctions = functions; |
| |
| [self setFrame:CGRectMake(0, 0, mSwapState->textures[0].width, |
| mSwapState->textures[0].height)]; |
| } |
| return self; |
| } |
| |
| - (void)display |
| { |
| pthread_mutex_lock(&mSwapState->mutex); |
| { |
| if (mSwapState->lastRendered->swapId > mSwapState->beingPresented->swapId) |
| { |
| std::swap(mSwapState->lastRendered, mSwapState->beingPresented); |
| } |
| } |
| pthread_mutex_unlock(&mSwapState->mutex); |
| |
| [getEAGLContextClass() setCurrentContext:mDisplayContext]; |
| |
| if (!initialized) |
| { |
| initialized = true; |
| |
| mFunctions->genFramebuffers(1, &mReadFramebuffer); |
| } |
| |
| const auto &texture = *mSwapState->beingPresented; |
| |
| if ([self frame].size.width != texture.width || [self frame].size.height != texture.height) |
| { |
| [self setFrame:CGRectMake(0, 0, texture.width, texture.height)]; |
| |
| // TODO(anglebug.com/4275): If this continues to remain an EAGLLayer, then this is |
| // where we'd probably want to create the renderbuffer storage. |
| [self setNeedsDisplay]; |
| } |
| |
| // TODO(cwallez) support 2.1 contexts too that don't have blitFramebuffer nor the |
| // GL_DRAW_FRAMEBUFFER_BINDING query |
| GLint drawFBO; |
| mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO); |
| |
| mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer); |
| mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| texture.texture, 0); |
| |
| mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer); |
| mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO); |
| mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, |
| texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); |
| |
| mFunctions->bindRenderbuffer(GL_RENDERBUFFER, texture.texture); |
| [mDisplayContext presentRenderbuffer:GL_RENDERBUFFER]; |
| [getEAGLContextClass() setCurrentContext:nil]; |
| } |
| @end |
| |
| namespace rx |
| { |
| |
| WindowSurfaceEAGL::WindowSurfaceEAGL(const egl::SurfaceState &state, |
| RendererGL *renderer, |
| EGLNativeWindowType layer, |
| EAGLContextObj context) |
| : SurfaceGL(state), |
| mSwapLayer(nil), |
| mCurrentSwapId(0), |
| mLayer((__bridge CALayer *)layer), |
| mContext(context), |
| mFunctions(renderer->getFunctions()), |
| mStateManager(renderer->getStateManager()), |
| mDSRenderbuffer(0) |
| { |
| pthread_mutex_init(&mSwapState.mutex, nullptr); |
| } |
| |
| WindowSurfaceEAGL::~WindowSurfaceEAGL() |
| { |
| pthread_mutex_destroy(&mSwapState.mutex); |
| |
| if (mDSRenderbuffer != 0) |
| { |
| mFunctions->deleteRenderbuffers(1, &mDSRenderbuffer); |
| mDSRenderbuffer = 0; |
| } |
| |
| if (mSwapLayer != nil) |
| { |
| [mSwapLayer removeFromSuperlayer]; |
| mSwapLayer = nil; |
| } |
| |
| for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) |
| { |
| if (mSwapState.textures[i].texture != 0) |
| { |
| mFunctions->deleteTextures(1, &mSwapState.textures[i].texture); |
| mSwapState.textures[i].texture = 0; |
| } |
| } |
| } |
| |
| egl::Error WindowSurfaceEAGL::initialize(const egl::Display *display) |
| { |
| unsigned width = getWidth(); |
| unsigned height = getHeight(); |
| |
| for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) |
| { |
| mFunctions->genTextures(1, &mSwapState.textures[i].texture); |
| mStateManager->bindTexture(gl::TextureType::_2D, mSwapState.textures[i].texture); |
| mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| mSwapState.textures[i].width = width; |
| mSwapState.textures[i].height = height; |
| mSwapState.textures[i].swapId = 0; |
| } |
| mSwapState.beingRendered = &mSwapState.textures[0]; |
| mSwapState.lastRendered = &mSwapState.textures[1]; |
| mSwapState.beingPresented = &mSwapState.textures[2]; |
| |
| mSwapLayer = [[WebSwapLayerEAGL alloc] initWithSharedState:&mSwapState |
| withContext:mContext |
| withFunctions:mFunctions]; |
| [mLayer addSublayer:mSwapLayer]; |
| [mSwapLayer setContentsScale:[mLayer contentsScale]]; |
| |
| mFunctions->genRenderbuffers(1, &mDSRenderbuffer); |
| mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); |
| mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::makeCurrent(const gl::Context *context) |
| { |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::swap(const gl::Context *context) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| functions->flush(); |
| mSwapState.beingRendered->swapId = ++mCurrentSwapId; |
| |
| pthread_mutex_lock(&mSwapState.mutex); |
| { |
| std::swap(mSwapState.beingRendered, mSwapState.lastRendered); |
| } |
| pthread_mutex_unlock(&mSwapState.mutex); |
| |
| unsigned width = getWidth(); |
| unsigned height = getHeight(); |
| auto &texture = *mSwapState.beingRendered; |
| |
| if (texture.width != width || texture.height != height) |
| { |
| stateManager->bindTexture(gl::TextureType::_2D, texture.texture); |
| functions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, |
| GL_UNSIGNED_BYTE, nullptr); |
| |
| stateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); |
| functions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); |
| |
| texture.width = width; |
| texture.height = height; |
| } |
| |
| FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(context->getFramebuffer({0})); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferGL->getFramebufferID()); |
| functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| mSwapState.beingRendered->texture, 0); |
| |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::postSubBuffer(const gl::Context *context, |
| EGLint x, |
| EGLint y, |
| EGLint width, |
| EGLint height) |
| { |
| UNIMPLEMENTED(); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::querySurfacePointerANGLE(EGLint attribute, void **value) |
| { |
| UNIMPLEMENTED(); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::bindTexImage(const gl::Context *context, |
| gl::Texture *texture, |
| EGLint buffer) |
| { |
| UNIMPLEMENTED(); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| egl::Error WindowSurfaceEAGL::releaseTexImage(const gl::Context *context, EGLint buffer) |
| { |
| UNIMPLEMENTED(); |
| return egl::Error(EGL_SUCCESS); |
| } |
| |
| void WindowSurfaceEAGL::setSwapInterval(EGLint interval) |
| { |
| // TODO(cwallez) investigate implementing swap intervals other than 0 |
| } |
| |
| EGLint WindowSurfaceEAGL::getWidth() const |
| { |
| return static_cast<EGLint>(CGRectGetWidth([mLayer frame]) * [mLayer contentsScale]); |
| } |
| |
| EGLint WindowSurfaceEAGL::getHeight() const |
| { |
| return static_cast<EGLint>(CGRectGetHeight([mLayer frame]) * [mLayer contentsScale]); |
| } |
| |
| EGLint WindowSurfaceEAGL::isPostSubBufferSupported() const |
| { |
| UNIMPLEMENTED(); |
| return EGL_FALSE; |
| } |
| |
| EGLint WindowSurfaceEAGL::getSwapBehavior() const |
| { |
| return EGL_BUFFER_DESTROYED; |
| } |
| |
| FramebufferImpl *WindowSurfaceEAGL::createDefaultFramebuffer(const gl::Context *context, |
| const gl::FramebufferState &state) |
| { |
| const FunctionsGL *functions = GetFunctionsGL(context); |
| StateManagerGL *stateManager = GetStateManagerGL(context); |
| |
| GLuint framebuffer = 0; |
| functions->genFramebuffers(1, &framebuffer); |
| stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); |
| functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, |
| mSwapState.beingRendered->texture, 0); |
| functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, |
| mDSRenderbuffer); |
| |
| return new FramebufferGL(state, framebuffer, true, false); |
| } |
| |
| } // namespace rx |
| |
| #endif // defined(ANGLE_ENABLE_EAGL) |