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