diff options
Diffstat (limited to 'gfx/angle/src/libANGLE/renderer/gl/cgl')
6 files changed, 992 insertions, 0 deletions
diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h new file mode 100755 index 000000000..cc1b17bb7 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.h @@ -0,0 +1,77 @@ +// +// 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. +// + +// DisplayCGL.h: CGL implementation of egl::Display + +#ifndef LIBANGLE_RENDERER_GL_CGL_DISPLAYCGL_H_ +#define LIBANGLE_RENDERER_GL_CGL_DISPLAYCGL_H_ + +#include "libANGLE/renderer/gl/DisplayGL.h" + +struct _CGLContextObject; +typedef _CGLContextObject *CGLContextObj; + +namespace rx +{ + +class DisplayCGL : public DisplayGL +{ + public: + DisplayCGL(); + ~DisplayCGL() override; + + egl::Error initialize(egl::Display *display) override; + void terminate() override; + + SurfaceImpl *createWindowSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLNativeWindowType window, + const egl::AttributeMap &attribs) override; + SurfaceImpl *createPbufferSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + const egl::AttributeMap &attribs) override; + SurfaceImpl *createPbufferFromClientBuffer(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLenum buftype, + EGLClientBuffer clientBuffer, + const egl::AttributeMap &attribs) override; + SurfaceImpl *createPixmapSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + NativePixmapType nativePixmap, + const egl::AttributeMap &attribs) override; + + egl::ConfigSet generateConfigs() override; + + bool testDeviceLost() override; + egl::Error restoreLostDevice() override; + + bool isValidNativeWindow(EGLNativeWindowType window) const override; + + egl::Error getDevice(DeviceImpl **device) override; + + std::string getVendorString() const override; + + egl::Error waitClient() const override; + egl::Error waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const override; + + egl::Error getDriverVersion(std::string *version) const override; + + private: + const FunctionsGL *getFunctionsGL() const override; + + void generateExtensions(egl::DisplayExtensions *outExtensions) const override; + void generateCaps(egl::Caps *outCaps) const override; + + egl::Display *mEGLDisplay; + FunctionsGL *mFunctions; + CGLContextObj mContext; +}; + +} + +#endif // LIBANGLE_RENDERER_GL_CGL_DISPLAYCGL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm new file mode 100755 index 000000000..f87134c82 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/DisplayCGL.mm @@ -0,0 +1,275 @@ +// +// 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. +// + +// DisplayCGL.mm: CGL implementation of egl::Display + +#include "libANGLE/renderer/gl/cgl/DisplayCGL.h" + +#import <Cocoa/Cocoa.h> +#include <dlfcn.h> +#include <EGL/eglext.h> + +#include "common/debug.h" +#include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h" +#include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" + +namespace +{ + +const char *kDefaultOpenGLDylibName = + "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib"; +const char *kFallbackOpenGLDylibName = "GL"; + +} + +namespace rx +{ + +class FunctionsGLCGL : public FunctionsGL +{ + public: + FunctionsGLCGL(void *dylibHandle) : mDylibHandle(dylibHandle) {} + + ~FunctionsGLCGL() override { dlclose(mDylibHandle); } + + private: + void *loadProcAddress(const std::string &function) override + { + return dlsym(mDylibHandle, function.c_str()); + } + + void *mDylibHandle; +}; + +DisplayCGL::DisplayCGL() : DisplayGL(), mEGLDisplay(nullptr), mFunctions(nullptr), mContext(nullptr) +{ +} + +DisplayCGL::~DisplayCGL() +{ +} + +egl::Error DisplayCGL::initialize(egl::Display *display) +{ + mEGLDisplay = display; + + CGLPixelFormatObj pixelFormat; + { + // TODO(cwallez) investigate which pixel format we want + CGLPixelFormatAttribute attribs[] = { + kCGLPFAOpenGLProfile, static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core), + static_cast<CGLPixelFormatAttribute>(0)}; + GLint nVirtualScreens = 0; + CGLChoosePixelFormat(attribs, &pixelFormat, &nVirtualScreens); + + if (pixelFormat == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create the context's pixel format."); + } + } + + CGLCreateContext(pixelFormat, nullptr, &mContext); + if (mContext == nullptr) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not create the CGL context."); + } + CGLSetCurrentContext(mContext); + + // There is no equivalent getProcAddress in CGL so we open the dylib directly + void *handle = dlopen(kDefaultOpenGLDylibName, RTLD_NOW); + if (!handle) + { + handle = dlopen(kFallbackOpenGLDylibName, RTLD_NOW); + } + if (!handle) + { + return egl::Error(EGL_NOT_INITIALIZED, "Could not open the OpenGL Framework."); + } + + mFunctions = new FunctionsGLCGL(handle); + mFunctions->initialize(); + + return DisplayGL::initialize(display); +} + +void DisplayCGL::terminate() +{ + DisplayGL::terminate(); + + if (mContext != nullptr) + { + CGLSetCurrentContext(nullptr); + CGLReleaseContext(mContext); + mContext = nullptr; + } + + SafeDelete(mFunctions); +} + +SurfaceImpl *DisplayCGL::createWindowSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLNativeWindowType window, + const egl::AttributeMap &attribs) +{ + return new WindowSurfaceCGL(state, this->getRenderer(), window, mFunctions, mContext); +} + +SurfaceImpl *DisplayCGL::createPbufferSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + const egl::AttributeMap &attribs) +{ + EGLint width = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0)); + EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0)); + return new PbufferSurfaceCGL(state, this->getRenderer(), width, height, mFunctions); +} + +SurfaceImpl *DisplayCGL::createPbufferFromClientBuffer(const egl::SurfaceState &state, + const egl::Config *configuration, + EGLenum buftype, + EGLClientBuffer clientBuffer, + const egl::AttributeMap &attribs) +{ + UNIMPLEMENTED(); + return nullptr; +} + +SurfaceImpl *DisplayCGL::createPixmapSurface(const egl::SurfaceState &state, + const egl::Config *configuration, + NativePixmapType nativePixmap, + const egl::AttributeMap &attribs) +{ + UNIMPLEMENTED(); + return nullptr; +} + +egl::Error DisplayCGL::getDevice(DeviceImpl **device) +{ + UNIMPLEMENTED(); + return egl::Error(EGL_BAD_DISPLAY); +} + +egl::ConfigSet DisplayCGL::generateConfigs() +{ + // TODO(cwallez): generate more config permutations + egl::ConfigSet configs; + + const gl::Version &maxVersion = getMaxSupportedESVersion(); + ASSERT(maxVersion >= gl::Version(2, 0)); + bool supportsES3 = maxVersion >= gl::Version(3, 0); + + egl::Config config; + + // Native stuff + config.nativeVisualID = 0; + config.nativeVisualType = 0; + config.nativeRenderable = EGL_TRUE; + + // Buffer sizes + config.redSize = 8; + config.greenSize = 8; + config.blueSize = 8; + config.alphaSize = 8; + config.depthSize = 24; + config.stencilSize = 8; + + config.colorBufferType = EGL_RGB_BUFFER; + config.luminanceSize = 0; + config.alphaMaskSize = 0; + + config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize; + + config.transparentType = EGL_NONE; + + // Pbuffer + config.maxPBufferWidth = 4096; + config.maxPBufferHeight = 4096; + config.maxPBufferPixels = 4096 * 4096; + + // Caveat + config.configCaveat = EGL_NONE; + + // Misc + config.sampleBuffers = 0; + config.samples = 0; + config.level = 0; + config.bindToTextureRGB = EGL_FALSE; + config.bindToTextureRGBA = EGL_FALSE; + + config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT; + + config.minSwapInterval = 1; + config.maxSwapInterval = 1; + + config.renderTargetFormat = GL_RGBA8; + config.depthStencilFormat = GL_DEPTH24_STENCIL8; + + config.conformant = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0); + config.renderableType = config.conformant; + + config.matchNativePixmap = EGL_NONE; + + configs.add(config); + return configs; +} + +bool DisplayCGL::testDeviceLost() +{ + // TODO(cwallez) investigate implementing this + return false; +} + +egl::Error DisplayCGL::restoreLostDevice() +{ + UNIMPLEMENTED(); + return egl::Error(EGL_BAD_DISPLAY); +} + +bool DisplayCGL::isValidNativeWindow(EGLNativeWindowType window) const +{ + // TODO(cwallez) investigate implementing this + return true; +} + +std::string DisplayCGL::getVendorString() const +{ + // TODO(cwallez) find a useful vendor string + return ""; +} + +const FunctionsGL *DisplayCGL::getFunctionsGL() const +{ + return mFunctions; +} + +void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const +{ +} + +void DisplayCGL::generateCaps(egl::Caps *outCaps) const +{ + outCaps->textureNPOT = true; +} + +egl::Error DisplayCGL::waitClient() const +{ + // TODO(cwallez) UNIMPLEMENTED() + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayCGL::waitNative(EGLint engine, + egl::Surface *drawSurface, + egl::Surface *readSurface) const +{ + // TODO(cwallez) UNIMPLEMENTED() + return egl::Error(EGL_SUCCESS); +} + +egl::Error DisplayCGL::getDriverVersion(std::string *version) const +{ + *version = ""; + return egl::Error(EGL_SUCCESS); +} +} diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h new file mode 100755 index 000000000..7cbb74da4 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h @@ -0,0 +1,65 @@ +// +// 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. +// + +// PBufferSurfaceCGL.h: an implementation of egl::Surface for PBuffers for the CLG backend, +// currently implemented using renderbuffers + +#ifndef LIBANGLE_RENDERER_GL_CGL_PBUFFERSURFACECGL_H_ +#define LIBANGLE_RENDERER_GL_CGL_PBUFFERSURFACECGL_H_ + +#include "libANGLE/renderer/gl/SurfaceGL.h" + +namespace rx +{ + +class FunctionsGL; +class StateManagerGL; +struct WorkaroundsGL; + +class PbufferSurfaceCGL : public SurfaceGL +{ + public: + PbufferSurfaceCGL(const egl::SurfaceState &state, + RendererGL *renderer, + EGLint width, + EGLint height, + const FunctionsGL *functions); + ~PbufferSurfaceCGL() override; + + egl::Error initialize() override; + egl::Error makeCurrent() override; + + egl::Error swap() override; + egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; + egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; + egl::Error releaseTexImage(EGLint buffer) override; + void setSwapInterval(EGLint interval) override; + + EGLint getWidth() const override; + EGLint getHeight() const override; + + EGLint isPostSubBufferSupported() const override; + EGLint getSwapBehavior() const override; + + FramebufferImpl *createDefaultFramebuffer(const gl::FramebufferState &state) override; + + private: + unsigned mWidth; + unsigned mHeight; + + const FunctionsGL *mFunctions; + StateManagerGL *mStateManager; + RendererGL *mRenderer; + + GLuint mFramebuffer; + GLuint mColorRenderbuffer; + GLuint mDSRenderbuffer; +}; + +} + +#endif // LIBANGLE_RENDERER_GL_CGL_PBUFFERSURFACECGL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm new file mode 100755 index 000000000..c03d3836f --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.mm @@ -0,0 +1,143 @@ +// +// 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. +// + +// PBufferSurfaceCGL.cpp: an implementation of egl::Surface for PBuffers for the CLG backend, +// currently implemented using renderbuffers + +#include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h" + +#include "common/debug.h" +#include "libANGLE/renderer/gl/FunctionsGL.h" +#include "libANGLE/renderer/gl/FramebufferGL.h" +#include "libANGLE/renderer/gl/RendererGL.h" +#include "libANGLE/renderer/gl/StateManagerGL.h" + +namespace rx +{ + +PbufferSurfaceCGL::PbufferSurfaceCGL(const egl::SurfaceState &state, + RendererGL *renderer, + EGLint width, + EGLint height, + const FunctionsGL *functions) + : SurfaceGL(state, renderer), + mWidth(width), + mHeight(height), + mFunctions(functions), + mStateManager(renderer->getStateManager()), + mRenderer(renderer), + mFramebuffer(0), + mColorRenderbuffer(0), + mDSRenderbuffer(0) +{ +} + +PbufferSurfaceCGL::~PbufferSurfaceCGL() +{ + if (mFramebuffer != 0) + { + mFunctions->deleteFramebuffers(1, &mFramebuffer); + mFramebuffer = 0; + } + + if (mColorRenderbuffer != 0) + { + mFunctions->deleteRenderbuffers(1, &mColorRenderbuffer); + mColorRenderbuffer = 0; + } + if (mDSRenderbuffer != 0) + { + mFunctions->deleteRenderbuffers(1, &mDSRenderbuffer); + mDSRenderbuffer = 0; + } +} + +egl::Error PbufferSurfaceCGL::initialize() +{ + mFunctions->genRenderbuffers(1, &mColorRenderbuffer); + mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mColorRenderbuffer); + mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_RGBA8, mWidth, mHeight); + + mFunctions->genRenderbuffers(1, &mDSRenderbuffer); + mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); + mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, mWidth, mHeight); + + mFunctions->genFramebuffers(1, &mFramebuffer); + mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); + mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_RENDERBUFFER, + mColorRenderbuffer); + mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, + GL_RENDERBUFFER, mDSRenderbuffer); + + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::makeCurrent() +{ + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::swap() +{ + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) +{ + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **value) +{ + UNIMPLEMENTED(); + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::bindTexImage(gl::Texture *texture, EGLint buffer) +{ + UNIMPLEMENTED(); + return egl::Error(EGL_SUCCESS); +} + +egl::Error PbufferSurfaceCGL::releaseTexImage(EGLint buffer) +{ + UNIMPLEMENTED(); + return egl::Error(EGL_SUCCESS); +} + +void PbufferSurfaceCGL::setSwapInterval(EGLint interval) +{ +} + +EGLint PbufferSurfaceCGL::getWidth() const +{ + return mWidth; +} + +EGLint PbufferSurfaceCGL::getHeight() const +{ + return mHeight; +} + +EGLint PbufferSurfaceCGL::isPostSubBufferSupported() const +{ + UNIMPLEMENTED(); + return EGL_FALSE; +} + +EGLint PbufferSurfaceCGL::getSwapBehavior() const +{ + return EGL_BUFFER_PRESERVED; +} + +FramebufferImpl *PbufferSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state) +{ + // TODO(cwallez) assert it happens only once? + return new FramebufferGL(mFramebuffer, state, mFunctions, mRenderer->getWorkarounds(), + mRenderer->getBlitter(), mStateManager); +} + +} diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h new file mode 100755 index 000000000..165ab0486 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h @@ -0,0 +1,100 @@ +// +// 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.h: CGL implementation of egl::Surface for windows + +#ifndef LIBANGLE_RENDERER_GL_CGL_WINDOWSURFACECGL_H_ +#define LIBANGLE_RENDERER_GL_CGL_WINDOWSURFACECGL_H_ + +#include "libANGLE/renderer/gl/SurfaceGL.h" + +struct _CGLContextObject; +typedef _CGLContextObject *CGLContextObj; +@class CALayer; +struct __IOSurface; +typedef __IOSurface *IOSurfaceRef; + +@class SwapLayer; + +namespace rx +{ + +class DisplayCGL; +class FramebufferGL; +class FunctionsGL; +class StateManagerGL; +struct WorkaroundsGL; + +struct SharedSwapState +{ + struct SwapTexture + { + GLuint texture; + unsigned int width; + unsigned int height; + uint64_t swapId; + }; + + SwapTexture textures[3]; + + // This code path is not going to be used by Chrome so we take the liberty + // to use pthreads directly instead of using mutexes and condition variables + // via the Platform API. + pthread_mutex_t mutex; + // The following members should be accessed only when holding the mutex + // (or doing construction / destruction) + SwapTexture *beingRendered; + SwapTexture *lastRendered; + SwapTexture *beingPresented; +}; + +class WindowSurfaceCGL : public SurfaceGL +{ + public: + WindowSurfaceCGL(const egl::SurfaceState &state, + RendererGL *renderer, + CALayer *layer, + const FunctionsGL *functions, + CGLContextObj context); + ~WindowSurfaceCGL() override; + + egl::Error initialize() override; + egl::Error makeCurrent() override; + + egl::Error swap() override; + egl::Error postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) override; + egl::Error querySurfacePointerANGLE(EGLint attribute, void **value) override; + egl::Error bindTexImage(gl::Texture *texture, EGLint buffer) override; + egl::Error releaseTexImage(EGLint buffer) override; + void setSwapInterval(EGLint interval) override; + + EGLint getWidth() const override; + EGLint getHeight() const override; + + EGLint isPostSubBufferSupported() const override; + EGLint getSwapBehavior() const override; + + FramebufferImpl *createDefaultFramebuffer(const gl::FramebufferState &state) override; + + private: + SwapLayer *mSwapLayer; + SharedSwapState mSwapState; + uint64_t mCurrentSwapId; + + CALayer *mLayer; + CGLContextObj mContext; + const FunctionsGL *mFunctions; + StateManagerGL *mStateManager; + RendererGL *mRenderer; + const WorkaroundsGL &mWorkarounds; + + GLuint mFramebuffer; + GLuint mDSRenderbuffer; +}; + +} + +#endif // LIBANGLE_RENDERER_GL_CGL_WINDOWSURFACECGL_H_ diff --git a/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm new file mode 100755 index 000000000..c2ac4dca4 --- /dev/null +++ b/gfx/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm @@ -0,0 +1,332 @@ +// +// 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, + CALayer *layer, + const FunctionsGL *functions, + CGLContextObj context) + : SurfaceGL(state, renderer), + mSwapLayer(nil), + mCurrentSwapId(0), + mLayer(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() +{ + 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, mRenderer->getBlitter(), + mStateManager); +} + +} |