1// 2// Copyright 2020 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// WindowSurfaceEAGL.cpp: EAGL implementation of egl::Surface 8 9#import "common/platform.h" 10 11#if defined(ANGLE_ENABLE_EAGL) 12 13# import "libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h" 14 15# import "common/debug.h" 16# import "libANGLE/Context.h" 17# import "libANGLE/renderer/gl/FramebufferGL.h" 18# import "libANGLE/renderer/gl/RendererGL.h" 19# import "libANGLE/renderer/gl/StateManagerGL.h" 20# import "libANGLE/renderer/gl/eagl/DisplayEAGL.h" 21# import "libANGLE/renderer/gl/eagl/FunctionsEAGL.h" 22 23# import <QuartzCore/QuartzCore.h> 24 25# if defined(ANGLE_PLATFORM_MACCATALYST) && defined(ANGLE_CPU_ARM64) 26 27// TODO(dino): Necessary because CAEAGLLayer is not in the public QuartzCore headers in this 28// configuration. 29// TODO(dino): Check that this won't cause an application using ANGLE directly to be flagged 30// for non-public API use on Apple's App Store. 31@interface CAEAGLLayer : CALayer 32@end 33 34# endif 35 36@interface SwapLayerEAGL : CAEAGLLayer { 37 EAGLContextObj mDisplayContext; 38 39 bool initialized; 40 rx::SharedSwapState *mSwapState; 41 const rx::FunctionsGL *mFunctions; 42 43 GLuint mReadFramebuffer; 44} 45- (id)initWithSharedState:(rx::SharedSwapState *)swapState 46 withContext:(EAGLContextObj)displayContext 47 withFunctions:(const rx::FunctionsGL *)functions; 48@end 49 50@implementation SwapLayerEAGL 51- (id)initWithSharedState:(rx::SharedSwapState *)swapState 52 withContext:(EAGLContextObj)displayContext 53 withFunctions:(const rx::FunctionsGL *)functions 54{ 55 self = [super init]; 56 if (self != nil) 57 { 58 mDisplayContext = displayContext; 59 60 initialized = false; 61 mSwapState = swapState; 62 mFunctions = functions; 63 64 [self setFrame:CGRectMake(0, 0, mSwapState->textures[0].width, 65 mSwapState->textures[0].height)]; 66 } 67 return self; 68} 69 70- (void)display 71{ 72 pthread_mutex_lock(&mSwapState->mutex); 73 { 74 if (mSwapState->lastRendered->swapId > mSwapState->beingPresented->swapId) 75 { 76 std::swap(mSwapState->lastRendered, mSwapState->beingPresented); 77 } 78 } 79 pthread_mutex_unlock(&mSwapState->mutex); 80 81 [getEAGLContextClass() setCurrentContext:mDisplayContext]; 82 83 if (!initialized) 84 { 85 initialized = true; 86 87 mFunctions->genFramebuffers(1, &mReadFramebuffer); 88 } 89 90 const auto &texture = *mSwapState->beingPresented; 91 92 if ([self frame].size.width != texture.width || [self frame].size.height != texture.height) 93 { 94 [self setFrame:CGRectMake(0, 0, texture.width, texture.height)]; 95 96 // TODO(anglebug.com/4275): If this continues to remain an EAGLLayer, then this is 97 // where we'd probably want to create the renderbuffer storage. 98 [self setNeedsDisplay]; 99 } 100 101 // TODO(cwallez) support 2.1 contexts too that don't have blitFramebuffer nor the 102 // GL_DRAW_FRAMEBUFFER_BINDING query 103 GLint drawFBO; 104 mFunctions->getIntegerv(GL_DRAW_FRAMEBUFFER_BINDING, &drawFBO); 105 106 mFunctions->bindFramebuffer(GL_FRAMEBUFFER, mReadFramebuffer); 107 mFunctions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 108 texture.texture, 0); 109 110 mFunctions->bindFramebuffer(GL_READ_FRAMEBUFFER, mReadFramebuffer); 111 mFunctions->bindFramebuffer(GL_DRAW_FRAMEBUFFER, drawFBO); 112 mFunctions->blitFramebuffer(0, 0, texture.width, texture.height, 0, 0, texture.width, 113 texture.height, GL_COLOR_BUFFER_BIT, GL_NEAREST); 114 115 mFunctions->bindRenderbuffer(GL_RENDERBUFFER, texture.texture); 116 [mDisplayContext presentRenderbuffer:GL_RENDERBUFFER]; 117 [getEAGLContextClass() setCurrentContext:nil]; 118} 119@end 120 121namespace rx 122{ 123 124WindowSurfaceEAGL::WindowSurfaceEAGL(const egl::SurfaceState &state, 125 RendererGL *renderer, 126 EGLNativeWindowType layer, 127 EAGLContextObj context) 128 : SurfaceGL(state), 129 mSwapLayer(nil), 130 mCurrentSwapId(0), 131 mLayer((__bridge CALayer *)layer), 132 mContext(context), 133 mFunctions(renderer->getFunctions()), 134 mStateManager(renderer->getStateManager()), 135 mDSRenderbuffer(0) 136{ 137 pthread_mutex_init(&mSwapState.mutex, nullptr); 138} 139 140WindowSurfaceEAGL::~WindowSurfaceEAGL() 141{ 142 pthread_mutex_destroy(&mSwapState.mutex); 143 144 if (mDSRenderbuffer != 0) 145 { 146 mFunctions->deleteRenderbuffers(1, &mDSRenderbuffer); 147 mDSRenderbuffer = 0; 148 } 149 150 if (mSwapLayer != nil) 151 { 152 [mSwapLayer removeFromSuperlayer]; 153 mSwapLayer = nil; 154 } 155 156 for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) 157 { 158 if (mSwapState.textures[i].texture != 0) 159 { 160 mFunctions->deleteTextures(1, &mSwapState.textures[i].texture); 161 mSwapState.textures[i].texture = 0; 162 } 163 } 164} 165 166egl::Error WindowSurfaceEAGL::initialize(const egl::Display *display) 167{ 168 unsigned width = getWidth(); 169 unsigned height = getHeight(); 170 171 for (size_t i = 0; i < ArraySize(mSwapState.textures); ++i) 172 { 173 mFunctions->genTextures(1, &mSwapState.textures[i].texture); 174 mStateManager->bindTexture(gl::TextureType::_2D, mSwapState.textures[i].texture); 175 mFunctions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 176 GL_UNSIGNED_BYTE, nullptr); 177 mSwapState.textures[i].width = width; 178 mSwapState.textures[i].height = height; 179 mSwapState.textures[i].swapId = 0; 180 } 181 mSwapState.beingRendered = &mSwapState.textures[0]; 182 mSwapState.lastRendered = &mSwapState.textures[1]; 183 mSwapState.beingPresented = &mSwapState.textures[2]; 184 185 mSwapLayer = [[SwapLayerEAGL alloc] initWithSharedState:&mSwapState 186 withContext:mContext 187 withFunctions:mFunctions]; 188 [mLayer addSublayer:mSwapLayer]; 189 [mSwapLayer setContentsScale:[mLayer contentsScale]]; 190 191 mFunctions->genRenderbuffers(1, &mDSRenderbuffer); 192 mStateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); 193 mFunctions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); 194 195 return egl::Error(EGL_SUCCESS); 196} 197 198egl::Error WindowSurfaceEAGL::makeCurrent(const gl::Context *context) 199{ 200 return egl::Error(EGL_SUCCESS); 201} 202 203egl::Error WindowSurfaceEAGL::swap(const gl::Context *context) 204{ 205 const FunctionsGL *functions = GetFunctionsGL(context); 206 StateManagerGL *stateManager = GetStateManagerGL(context); 207 208 functions->flush(); 209 mSwapState.beingRendered->swapId = ++mCurrentSwapId; 210 211 pthread_mutex_lock(&mSwapState.mutex); 212 { 213 std::swap(mSwapState.beingRendered, mSwapState.lastRendered); 214 } 215 pthread_mutex_unlock(&mSwapState.mutex); 216 217 unsigned width = getWidth(); 218 unsigned height = getHeight(); 219 auto &texture = *mSwapState.beingRendered; 220 221 if (texture.width != width || texture.height != height) 222 { 223 stateManager->bindTexture(gl::TextureType::_2D, texture.texture); 224 functions->texImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, 225 GL_UNSIGNED_BYTE, nullptr); 226 227 stateManager->bindRenderbuffer(GL_RENDERBUFFER, mDSRenderbuffer); 228 functions->renderbufferStorage(GL_RENDERBUFFER, GL_DEPTH24_STENCIL8, width, height); 229 230 texture.width = width; 231 texture.height = height; 232 } 233 234 FramebufferGL *framebufferGL = GetImplAs<FramebufferGL>(context->getFramebuffer({0})); 235 stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebufferGL->getFramebufferID()); 236 functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 237 mSwapState.beingRendered->texture, 0); 238 239 return egl::Error(EGL_SUCCESS); 240} 241 242egl::Error WindowSurfaceEAGL::postSubBuffer(const gl::Context *context, 243 EGLint x, 244 EGLint y, 245 EGLint width, 246 EGLint height) 247{ 248 UNIMPLEMENTED(); 249 return egl::Error(EGL_SUCCESS); 250} 251 252egl::Error WindowSurfaceEAGL::querySurfacePointerANGLE(EGLint attribute, void **value) 253{ 254 UNIMPLEMENTED(); 255 return egl::Error(EGL_SUCCESS); 256} 257 258egl::Error WindowSurfaceEAGL::bindTexImage(const gl::Context *context, 259 gl::Texture *texture, 260 EGLint buffer) 261{ 262 UNIMPLEMENTED(); 263 return egl::Error(EGL_SUCCESS); 264} 265 266egl::Error WindowSurfaceEAGL::releaseTexImage(const gl::Context *context, EGLint buffer) 267{ 268 UNIMPLEMENTED(); 269 return egl::Error(EGL_SUCCESS); 270} 271 272void WindowSurfaceEAGL::setSwapInterval(EGLint interval) 273{ 274 // TODO(cwallez) investigate implementing swap intervals other than 0 275} 276 277EGLint WindowSurfaceEAGL::getWidth() const 278{ 279 return static_cast<EGLint>(CGRectGetWidth([mLayer frame]) * [mLayer contentsScale]); 280} 281 282EGLint WindowSurfaceEAGL::getHeight() const 283{ 284 return static_cast<EGLint>(CGRectGetHeight([mLayer frame]) * [mLayer contentsScale]); 285} 286 287EGLint WindowSurfaceEAGL::isPostSubBufferSupported() const 288{ 289 UNIMPLEMENTED(); 290 return EGL_FALSE; 291} 292 293EGLint WindowSurfaceEAGL::getSwapBehavior() const 294{ 295 return EGL_BUFFER_DESTROYED; 296} 297 298FramebufferImpl *WindowSurfaceEAGL::createDefaultFramebuffer(const gl::Context *context, 299 const gl::FramebufferState &state) 300{ 301 const FunctionsGL *functions = GetFunctionsGL(context); 302 StateManagerGL *stateManager = GetStateManagerGL(context); 303 304 GLuint framebuffer = 0; 305 functions->genFramebuffers(1, &framebuffer); 306 stateManager->bindFramebuffer(GL_FRAMEBUFFER, framebuffer); 307 functions->framebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, 308 mSwapState.beingRendered->texture, 0); 309 functions->framebufferRenderbuffer(GL_FRAMEBUFFER, GL_DEPTH_STENCIL_ATTACHMENT, GL_RENDERBUFFER, 310 mDSRenderbuffer); 311 312 return new FramebufferGL(state, framebuffer, true, false); 313} 314 315} // namespace rx 316 317#endif // defined(ANGLE_ENABLE_EAGL) 318