1// 2// Copyright (c) 2015 The ANGLE Project Authors. All rights reserved. 3// Use of this source code is governed by a BSD-style license that can be 4// found in the LICENSE file. 5// 6 7// WindowSurfaceCGL.cpp: CGL implementation of egl::Surface for windows 8 9#include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h" 10 11#import <Cocoa/Cocoa.h> 12#include <OpenGL/OpenGL.h> 13#import <QuartzCore/QuartzCore.h> 14 15#include "common/debug.h" 16#include "libANGLE/renderer/gl/cgl/DisplayCGL.h" 17#include "libANGLE/renderer/gl/FramebufferGL.h" 18#include "libANGLE/renderer/gl/RendererGL.h" 19#include "libANGLE/renderer/gl/StateManagerGL.h" 20 21@interface SwapLayer : CAOpenGLLayer 22{ 23 CGLContextObj mDisplayContext; 24 25 bool initialized; 26 rx::SharedSwapState *mSwapState; 27 const rx::FunctionsGL *mFunctions; 28 29 GLuint mReadFramebuffer; 30} 31- (id)initWithSharedState:(rx::SharedSwapState *)swapState 32 withContext:(CGLContextObj)displayContext 33 withFunctions:(const rx::FunctionsGL *)functions; 34@end 35 36@implementation SwapLayer 37- (id)initWithSharedState:(rx::SharedSwapState *)swapState 38 withContext:(CGLContextObj)displayContext 39 withFunctions:(const rx::FunctionsGL *)functions 40 { 41 self = [super init]; 42 if (self != nil) 43 { 44 self.asynchronous = YES; 45 mDisplayContext = displayContext; 46 47 initialized = false; 48 mSwapState = swapState; 49 mFunctions = functions; 50 51 [self setFrame:CGRectMake(0, 0, mSwapState->textures[0].width, 52 mSwapState->textures[0].height)]; 53 } 54 return self; 55 } 56 57 - (CGLPixelFormatObj)copyCGLPixelFormatForDisplayMask:(uint32_t)mask 58 { 59 CGLPixelFormatAttribute attribs[] = { 60 kCGLPFADisplayMask, static_cast<CGLPixelFormatAttribute>(mask), kCGLPFAOpenGLProfile, 61 static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core), 62 static_cast<CGLPixelFormatAttribute>(0)}; 63 64 CGLPixelFormatObj pixelFormat = nullptr; 65 GLint numFormats = 0; 66 CGLChoosePixelFormat(attribs, &pixelFormat, &numFormats); 67 68 return pixelFormat; 69 } 70 71 - (CGLContextObj)copyCGLContextForPixelFormat:(CGLPixelFormatObj)pixelFormat 72 { 73 CGLContextObj context = nullptr; 74 CGLCreateContext(pixelFormat, mDisplayContext, &context); 75 return context; 76 } 77 78 - (BOOL)canDrawInCGLContext:(CGLContextObj)glContext 79 pixelFormat:(CGLPixelFormatObj)pixelFormat 80 forLayerTime:(CFTimeInterval)timeInterval 81 displayTime:(const CVTimeStamp *)timeStamp 82 { 83 BOOL result = NO; 84 85 pthread_mutex_lock(&mSwapState->mutex); 86 { 87 if (mSwapState->lastRendered->swapId > mSwapState->beingPresented->swapId) 88 { 89 std::swap(mSwapState->lastRendered, mSwapState->beingPresented); 90 result = YES; 91 } 92 } 93 pthread_mutex_unlock(&mSwapState->mutex); 94 95 return result; 96 } 97 98 - (void)drawInCGLContext:(CGLContextObj)glContext 99 pixelFormat:(CGLPixelFormatObj)pixelFormat 100 forLayerTime:(CFTimeInterval)timeInterval 101 displayTime:(const CVTimeStamp *)timeStamp 102 { 103 CGLSetCurrentContext(glContext); 104 if (!initialized) 105 { 106 initialized = true; 107 108 mFunctions->genFramebuffers(1, &mReadFramebuffer); 109 } 110 111 const auto &texture = *mSwapState->beingPresented; 112 if ([self frame].size.width != texture.width || [self frame].size.height != texture.height) 113 { 114 [self setFrame:CGRectMake(0, 0, texture.width, texture.height)]; 115 116 // Without this, the OSX compositor / window system doesn't see the resize. 117 [self setNeedsDisplay]; 118 } 119 120 // TODO(cwallez) support 2.1 contexts too that don't have blitFramebuffer nor the 121 // GL_DRAW_FRAMEBUFFER_BINDING query 122 GLint drawFBO; 123 mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO); 124 125 mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer); 126 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 127 texture.texture, 0); 128 129 mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer); 130 mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO); 131 mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, 132 texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 133 134 // Call the super method to flush the context 135 [super drawInCGLContext:glContext 136 pixelFormat:pixelFormat 137 forLayerTime:timeInterval 138 displayTime:timeStamp]; 139 } 140 @end 141 142 namespace rx 143 { 144 145 WindowSurfaceCGL::WindowSurfaceCGL(const egl::SurfaceState &state, 146 RendererGL *renderer, 147 CALayer *layer, 148 const FunctionsGL *functions, 149 CGLContextObj context) 150 : SurfaceGL(state, renderer), 151 mSwapLayer(nil), 152 mCurrentSwapId(0), 153 mLayer(layer), 154 mContext(context), 155 mFunctions(functions), 156 mStateManager(renderer->getStateManager()), 157 mRenderer(renderer), 158 mWorkarounds(renderer->getWorkarounds()), 159 mFramebuffer(0), 160 mDSRenderbuffer(0) 161 { 162 pthread_mutex_init(&mSwapState.mutex, nullptr); 163} 164 165WindowSurfaceCGL::~WindowSurfaceCGL() 166{ 167 pthread_mutex_destroy(&mSwapState.mutex); 168 if (mFramebuffer != 0) 169 { 170 mFunctions->deleteFramebuffers(1, &mFramebuffer); 171 mFramebuffer = 0; 172 } 173 174 if (mDSRenderbuffer != 0) 175 { 176 mFunctions->deleteRenderbuffers(1, &mDSRenderbuffer); 177 mDSRenderbuffer = 0; 178 } 179 180 if (mSwapLayer != nil) 181 { 182 [mSwapLayer removeFromSuperlayer]; 183 [mSwapLayer release]; 184 mSwapLayer = nil; 185 } 186 187 for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) 188 { 189 if (mSwapState.textures[i].texture != 0) 190 { 191 mFunctions->deleteTextures(1, &mSwapState.textures[i].texture); 192 mSwapState.textures[i].texture = 0; 193 } 194 } 195} 196 197egl::Error WindowSurfaceCGL::initialize() 198{ 199 unsigned width = getWidth(); 200 unsigned height = getHeight(); 201 202 for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) 203 { 204 mFunctions->genTextures(1, &mSwapState.textures[i].texture); 205 mStateManager->bindTexture(GL_TEXTURE_2D, mSwapState.textures[i].texture); 206 mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 207 GL_UNSIGNED_BYTE, nullptr); 208 mSwapState.textures[i].width = width; 209 mSwapState.textures[i].height = height; 210 mSwapState.textures[i].swapId = 0; 211 } 212 mSwapState.beingRendered = &mSwapState.textures[0]; 213 mSwapState.lastRendered = &mSwapState.textures[1]; 214 mSwapState.beingPresented = &mSwapState.textures[2]; 215 216 mSwapLayer = [[SwapLayer alloc] initWithSharedState:&mSwapState 217 withContext:mContext 218 withFunctions:mFunctions]; 219 [mLayer addSublayer:mSwapLayer]; 220 221 mFunctions->genRenderbuffers(1, &mDSRenderbuffer); 222 mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); 223 mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); 224 225 mFunctions->genFramebuffers(1, &mFramebuffer); 226 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); 227 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 228 mSwapState.beingRendered->texture, 0); 229 mFunctions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 230 mDSRenderbuffer); 231 232 return egl::Error(EGL_SUCCESS); 233} 234 235egl::Error WindowSurfaceCGL::makeCurrent() 236{ 237 return egl::Error(EGL_SUCCESS); 238} 239 240egl::Error WindowSurfaceCGL::swap() 241{ 242 mFunctions->flush(); 243 mSwapState.beingRendered->swapId = ++mCurrentSwapId; 244 245 pthread_mutex_lock(&mSwapState.mutex); 246 { 247 std::swap(mSwapState.beingRendered, mSwapState.lastRendered); 248 } 249 pthread_mutex_unlock(&mSwapState.mutex); 250 251 unsigned width = getWidth(); 252 unsigned height = getHeight(); 253 auto &texture = *mSwapState.beingRendered; 254 255 if (texture.width != width || texture.height != height) 256 { 257 mStateManager->bindTexture(GL_TEXTURE_2D, texture.texture); 258 mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 259 GL_UNSIGNED_BYTE, nullptr); 260 261 mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); 262 mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); 263 264 texture.width = width; 265 texture.height = height; 266 } 267 268 mStateManager->bindFramebuffer(GL_FRAMEBUFFER, mFramebuffer); 269 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 270 mSwapState.beingRendered->texture, 0); 271 272 return egl::Error(EGL_SUCCESS); 273} 274 275egl::Error WindowSurfaceCGL::postSubBuffer(EGLint x, EGLint y, EGLint width, EGLint height) 276{ 277 UNIMPLEMENTED(); 278 return egl::Error(EGL_SUCCESS); 279} 280 281egl::Error WindowSurfaceCGL::querySurfacePointerANGLE(EGLint attribute, void **value) 282{ 283 UNIMPLEMENTED(); 284 return egl::Error(EGL_SUCCESS); 285} 286 287egl::Error WindowSurfaceCGL::bindTexImage(gl::Texture *texture, EGLint buffer) 288{ 289 UNIMPLEMENTED(); 290 return egl::Error(EGL_SUCCESS); 291} 292 293egl::Error WindowSurfaceCGL::releaseTexImage(EGLint buffer) 294{ 295 UNIMPLEMENTED(); 296 return egl::Error(EGL_SUCCESS); 297} 298 299void WindowSurfaceCGL::setSwapInterval(EGLint interval) 300{ 301 // TODO(cwallez) investigate implementing swap intervals other than 0 302} 303 304EGLint WindowSurfaceCGL::getWidth() const 305{ 306 return CGRectGetWidth([mLayer frame]); 307} 308 309EGLint WindowSurfaceCGL::getHeight() const 310{ 311 return CGRectGetHeight([mLayer frame]); 312} 313 314EGLint WindowSurfaceCGL::isPostSubBufferSupported() const 315{ 316 UNIMPLEMENTED(); 317 return EGL_FALSE; 318} 319 320EGLint WindowSurfaceCGL::getSwapBehavior() const 321{ 322 return EGL_BUFFER_DESTROYED; 323} 324 325FramebufferImpl *WindowSurfaceCGL::createDefaultFramebuffer(const gl::FramebufferState &state) 326{ 327 // TODO(cwallez) assert it happens only once? 328 return new FramebufferGL(mFramebuffer, state, mFunctions, mWorkarounds, mRenderer->getBlitter(), 329 mStateManager); 330} 331 332} 333