blob: ea8791983f982bb4a856a92ea067fa3e58af9db3 [file] [log] [blame]
//
// 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 <Cocoa/Cocoa.h>
#include <OpenGL/OpenGL.h>
#import <QuartzCore/QuartzCore.h>
#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<CGLPixelFormatAttribute>(mask), kCGLPFAOpenGLProfile,
static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core),
static_cast<CGLPixelFormatAttribute>(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,
EGLNativeWindowType layer,
const FunctionsGL *functions,
CGLContextObj context)
: SurfaceGL(state, renderer),
mSwapLayer(nil),
mCurrentSwapId(0),
mLayer(reinterpret_cast<CALayer *>(layer)),
mContext(context),
mFunctions(functions),
mStateManager(renderer->getStateManager()),
mRenderer(renderer),
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(const DisplayImpl *displayImpl)
{
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(const DisplayImpl *displayImpl)
{
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, mRenderer->getBlitter(),
mStateManager);
}
} // namespace rx