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