xref: /aosp_15_r20/external/angle/src/libANGLE/renderer/gl/cgl/WindowSurfaceCGL.mm (revision 8975f5c5ed3d1c378011245431ada316dfb6f244)
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