// // Copyright (c) 2015 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. // // WindowSurfaceCGL.cpp: CGL implementation of egl::Surface for windows #include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" #import #include #import #include "common/debug.h" #include "libANGLE/renderer/gl/cgl/DisplayCGL.h" #include "libANGLE/renderer/gl/FramebufferGL.h" #include "libANGLE/renderer/gl/RendererGL.h" #include "libANGLE/renderer/gl/StateManagerGL.h" @interface SwapLayer : CAOpenGLLayer { CGLContextObj mDisplayContext; bool initialized; rx::SharedSwapState *mSwapState; const rx::FunctionsGL *mFunctions; GLuint mReadFramebuffer; } - (id)initWithSharedState:(rx::SharedSwapState *)swapState withContext:(CGLContextObj)displayContext withFunctions:(const rx::FunctionsGL *)functions; @end @implementation SwapLayer - (id)initWithSharedState:(rx::SharedSwapState *)swapState withContext:(CGLContextObj)displayContext withFunctions:(const rx::FunctionsGL *)functions { self = [super init]; if (self != nil) { self.asynchronous = YES; mDisplayContext = displayContext; initialized = false; mSwapState = swapState; mFunctions = functions; [self setFrame:CGRectMake(0, 0, mSwapState->textures[0].width, mSwapState->textures[0].height)]; } return self; } - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask { CGLPixelFormatAttribute attribs[] = { kCGLPFADisplayMask, static_cast(mask), kCGLPFAOpenGLProfile, static_cast(kCGLOGLPVersion_3_2_Core), static_cast(0)}; CGLPixelFormatObj pixelFormat = nullptr; GLint numFormats = 0; CGLChoosePixelFormat(attribs, &pixelFormat, &numFormats); return pixelFormat; } - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat { CGLContextObj context = nullptr; CGLCreateContext(pixelFormat, mDisplayContext, &context); return context; } - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp { BOOL result = NO; pthread_mutex_lock(&mSwapState->mutex); { if (mSwapState->lastRendered->swapId > mSwapState->beingPresented->swapId) { std::swap(mSwapState->lastRendered, mSwapState->beingPresented); result = YES; } } pthread_mutex_unlock(&mSwapState->mutex); return result; } - (void)drawInCGLContext:(CGLContextObj)glContext pixelFormat:(CGLPixelFormatObj)pixelFormat forLayerTime:(CFTimeInterval)timeInterval displayTime:(const CVTimeStamp *)timeStamp { CGLSetCurrentContext(glContext); 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)]; // Without this, the OSX compositor / window system doesn't see the resize. [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); // Call the super method to flush the context [super drawInCGLContext:glContext pixelFormat:pixelFormat forLayerTime:timeInterval displayTime:timeStamp]; } @end namespace rx { WindowSurfaceCGL::WindowSurfaceCGL(const egl::SurfaceState &state, RendererGL *renderer, CALayer *layer, const FunctionsGL *functions, CGLContextObj context) : SurfaceGL(state, renderer), mSwapLayer(nil), mCurrentSwapId(0), mLayer(layer), mContext(context), mFunctions(functions), mStateManager(renderer->getStateManager()), mWorkarounds(renderer->getWorkarounds()), mFramebuffer(0), mDSRenderbuffer(0) { pthread_mutex_init(&mSwapState.mutex, nullptr); } WindowSurfaceCGL::~WindowSurfaceCGL() { pthread_mutex_destroy(&mSwapState.mutex); if (mFramebuffer != 0) { mFunctions->deleteFramebuffers(1, &mFramebuffer); mFramebuffer = 0; } if (mDSRenderbuffer != 0) { mFunctions->deleteRenderbuffers(1, &mDSRenderbuffer); mDSRenderbuffer = 0; } if (mSwapLayer != nil) { [mSwapLayer removeFromSuperlayer]; [mSwapLayer release]; 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 WindowSurfaceCGL::initialize() { 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_TEXTURE_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 = [[SwapLayer alloc] initWithSharedState:&mSwapState withContext:mContext withFunctions:mFunctions]; [mLayer addSublayer:mSwapLayer]; mFunctions->genRenderbuffers(1, &mDSRenderbuffer); mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); mFunctions->genFramebuffers(1, &mFramebuffer); mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSwapState.beingRendered->texture, 0); mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, mDSRenderbuffer); return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::makeCurrent() { return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::swap() { mFunctions->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) { mStateManager->bindTexture(GL_TEXTURE_2D, texture.texture); mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, nullptr); mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); texture.width = width; texture.height = height; } mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, mSwapState.beingRendered->texture, 0); return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **value) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::bindTexImage(gl::Texture *texture, EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); } egl::Error WindowSurfaceCGL::releaseTexImage(EGLint buffer) { UNIMPLEMENTED(); return egl::Error(EGL_SUCCESS); } void WindowSurfaceCGL::setSwapInterval(EGLint interval) { // TODO(cwallez) investigate implementing swap intervals other than 0 } EGLint WindowSurfaceCGL::getWidth() const { return CGRectGetWidth([mLayer frame]); } EGLint WindowSurfaceCGL::getHeight() const { return CGRectGetHeight([mLayer frame]); } EGLint WindowSurfaceCGL::isPostSubBufferSupported() const { UNIMPLEMENTED(); return EGL_FALSE; } EGLint WindowSurfaceCGL::getSwapBehavior() const { return EGL_BUFFER_DESTROYED; } FramebufferImpl *WindowSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state) { // TODO(cwallez) assert it happens only once? return new FramebufferGL(mFramebuffer, state, mFunctions, mWorkarounds, mStateManager); } }