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