• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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// DisplayCGL.mm: CGL implementation of egl::Display
8
9#include "common/platform.h"
10
11#if defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
12
13#    include "libANGLE/renderer/gl/cgl/DisplayCGL.h"
14
15#    import <Cocoa/Cocoa.h>
16#    include <EGL/eglext.h>
17#    include <dlfcn.h>
18
19#    include "common/debug.h"
20#    include "gpu_info_util/SystemInfo.h"
21#    include "libANGLE/Display.h"
22#    include "libANGLE/renderer/gl/cgl/ContextCGL.h"
23#    include "libANGLE/renderer/gl/cgl/DeviceCGL.h"
24#    include "libANGLE/renderer/gl/cgl/IOSurfaceSurfaceCGL.h"
25#    include "libANGLE/renderer/gl/cgl/PbufferSurfaceCGL.h"
26#    include "libANGLE/renderer/gl/cgl/RendererCGL.h"
27#    include "libANGLE/renderer/gl/cgl/WindowSurfaceCGL.h"
28
29namespace
30{
31
32const char *kDefaultOpenGLDylibName =
33    "/System/Library/Frameworks/OpenGL.framework/Libraries/libGL.dylib";
34const char *kFallbackOpenGLDylibName = "GL";
35
36}
37
38namespace rx
39{
40
41class FunctionsGLCGL : public FunctionsGL
42{
43  public:
44    FunctionsGLCGL(void *dylibHandle) : mDylibHandle(dylibHandle) {}
45
46    ~FunctionsGLCGL() override { dlclose(mDylibHandle); }
47
48  private:
49    void *loadProcAddress(const std::string &function) const override
50    {
51        return dlsym(mDylibHandle, function.c_str());
52    }
53
54    void *mDylibHandle;
55};
56
57DisplayCGL::DisplayCGL(const egl::DisplayState &state)
58    : DisplayGL(state),
59      mEGLDisplay(nullptr),
60      mContext(nullptr),
61      mPixelFormat(nullptr),
62      mSupportsGPUSwitching(false),
63      mDiscreteGPUPixelFormat(nullptr),
64      mDiscreteGPURefs(0)
65{}
66
67DisplayCGL::~DisplayCGL() {}
68
69egl::Error DisplayCGL::initialize(egl::Display *display)
70{
71    mEGLDisplay = display;
72
73    angle::SystemInfo info;
74    if (!angle::GetSystemInfo(&info))
75    {
76        return egl::EglNotInitialized() << "Unable to query ANGLE's SystemInfo.";
77    }
78    mSupportsGPUSwitching = info.isMacSwitchable;
79
80    {
81        // TODO(cwallez) investigate which pixel format we want
82        std::vector<CGLPixelFormatAttribute> attribs;
83        attribs.push_back(kCGLPFAOpenGLProfile);
84        attribs.push_back(static_cast<CGLPixelFormatAttribute>(kCGLOGLPVersion_3_2_Core));
85        attribs.push_back(kCGLPFAAllowOfflineRenderers);
86        attribs.push_back(static_cast<CGLPixelFormatAttribute>(0));
87        GLint nVirtualScreens = 0;
88        CGLChoosePixelFormat(attribs.data(), &mPixelFormat, &nVirtualScreens);
89
90        if (mPixelFormat == nullptr)
91        {
92            return egl::EglNotInitialized() << "Could not create the context's pixel format.";
93        }
94    }
95
96    CGLCreateContext(mPixelFormat, nullptr, &mContext);
97    if (mContext == nullptr)
98    {
99        return egl::EglNotInitialized() << "Could not create the CGL context.";
100    }
101    CGLSetCurrentContext(mContext);
102
103    // There is no equivalent getProcAddress in CGL so we open the dylib directly
104    void *handle = dlopen(kDefaultOpenGLDylibName, RTLD_NOW);
105    if (!handle)
106    {
107        handle = dlopen(kFallbackOpenGLDylibName, RTLD_NOW);
108    }
109    if (!handle)
110    {
111        return egl::EglNotInitialized() << "Could not open the OpenGL Framework.";
112    }
113
114    std::unique_ptr<FunctionsGL> functionsGL(new FunctionsGLCGL(handle));
115    functionsGL->initialize(display->getAttributeMap());
116
117    mRenderer.reset(new RendererCGL(std::move(functionsGL), display->getAttributeMap(), this));
118
119    const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion();
120    if (maxVersion < gl::Version(2, 0))
121    {
122        return egl::EglNotInitialized() << "OpenGL ES 2.0 is not supportable.";
123    }
124
125    return DisplayGL::initialize(display);
126}
127
128void DisplayCGL::terminate()
129{
130    DisplayGL::terminate();
131
132    mRenderer.reset();
133    if (mPixelFormat != nullptr)
134    {
135        CGLDestroyPixelFormat(mPixelFormat);
136        mPixelFormat = nullptr;
137    }
138    if (mContext != nullptr)
139    {
140        CGLSetCurrentContext(nullptr);
141        CGLReleaseContext(mContext);
142        mContext = nullptr;
143    }
144}
145
146SurfaceImpl *DisplayCGL::createWindowSurface(const egl::SurfaceState &state,
147                                             EGLNativeWindowType window,
148                                             const egl::AttributeMap &attribs)
149{
150    return new WindowSurfaceCGL(state, mRenderer.get(), window, mContext);
151}
152
153SurfaceImpl *DisplayCGL::createPbufferSurface(const egl::SurfaceState &state,
154                                              const egl::AttributeMap &attribs)
155{
156    EGLint width  = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0));
157    EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0));
158    return new PbufferSurfaceCGL(state, mRenderer.get(), width, height);
159}
160
161SurfaceImpl *DisplayCGL::createPbufferFromClientBuffer(const egl::SurfaceState &state,
162                                                       EGLenum buftype,
163                                                       EGLClientBuffer clientBuffer,
164                                                       const egl::AttributeMap &attribs)
165{
166    ASSERT(buftype == EGL_IOSURFACE_ANGLE);
167
168    return new IOSurfaceSurfaceCGL(state, mContext, clientBuffer, attribs);
169}
170
171SurfaceImpl *DisplayCGL::createPixmapSurface(const egl::SurfaceState &state,
172                                             NativePixmapType nativePixmap,
173                                             const egl::AttributeMap &attribs)
174{
175    UNIMPLEMENTED();
176    return nullptr;
177}
178
179ContextImpl *DisplayCGL::createContext(const gl::State &state,
180                                       gl::ErrorSet *errorSet,
181                                       const egl::Config *configuration,
182                                       const gl::Context *shareContext,
183                                       const egl::AttributeMap &attribs)
184{
185    bool usesDiscreteGPU = false;
186
187    if (attribs.get(EGL_POWER_PREFERENCE_ANGLE, EGL_LOW_POWER_ANGLE) == EGL_HIGH_POWER_ANGLE)
188    {
189        // Should have been rejected by validation if not supported.
190        ASSERT(mSupportsGPUSwitching);
191        // Create discrete pixel format if necessary.
192        if (!mDiscreteGPUPixelFormat)
193        {
194            CGLPixelFormatAttribute discreteAttribs[] = {static_cast<CGLPixelFormatAttribute>(0)};
195            GLint numPixelFormats                     = 0;
196            if (CGLChoosePixelFormat(discreteAttribs, &mDiscreteGPUPixelFormat, &numPixelFormats) !=
197                kCGLNoError)
198            {
199                ERR() << "Error choosing discrete pixel format.";
200                return nullptr;
201            }
202        }
203        ++mDiscreteGPURefs;
204        usesDiscreteGPU = true;
205    }
206
207    return new ContextCGL(state, errorSet, mRenderer, usesDiscreteGPU);
208}
209
210DeviceImpl *DisplayCGL::createDevice()
211{
212    return new DeviceCGL();
213}
214
215egl::ConfigSet DisplayCGL::generateConfigs()
216{
217    // TODO(cwallez): generate more config permutations
218    egl::ConfigSet configs;
219
220    const gl::Version &maxVersion = getMaxSupportedESVersion();
221    ASSERT(maxVersion >= gl::Version(2, 0));
222    bool supportsES3 = maxVersion >= gl::Version(3, 0);
223
224    egl::Config config;
225
226    // Native stuff
227    config.nativeVisualID   = 0;
228    config.nativeVisualType = 0;
229    config.nativeRenderable = EGL_TRUE;
230
231    // Buffer sizes
232    config.redSize     = 8;
233    config.greenSize   = 8;
234    config.blueSize    = 8;
235    config.alphaSize   = 8;
236    config.depthSize   = 24;
237    config.stencilSize = 8;
238
239    config.colorBufferType = EGL_RGB_BUFFER;
240    config.luminanceSize   = 0;
241    config.alphaMaskSize   = 0;
242
243    config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
244
245    config.transparentType = EGL_NONE;
246
247    // Pbuffer
248    config.maxPBufferWidth  = 4096;
249    config.maxPBufferHeight = 4096;
250    config.maxPBufferPixels = 4096 * 4096;
251
252    // Caveat
253    config.configCaveat = EGL_NONE;
254
255    // Misc
256    config.sampleBuffers     = 0;
257    config.samples           = 0;
258    config.level             = 0;
259    config.bindToTextureRGB  = EGL_FALSE;
260    config.bindToTextureRGBA = EGL_FALSE;
261
262    config.bindToTextureTarget = EGL_TEXTURE_RECTANGLE_ANGLE;
263
264    config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
265
266    config.minSwapInterval = 1;
267    config.maxSwapInterval = 1;
268
269    config.renderTargetFormat = GL_RGBA8;
270    config.depthStencilFormat = GL_DEPTH24_STENCIL8;
271
272    config.conformant     = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0);
273    config.renderableType = config.conformant;
274
275    config.matchNativePixmap = EGL_NONE;
276
277    config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
278
279    configs.add(config);
280    return configs;
281}
282
283bool DisplayCGL::testDeviceLost()
284{
285    // TODO(cwallez) investigate implementing this
286    return false;
287}
288
289egl::Error DisplayCGL::restoreLostDevice(const egl::Display *display)
290{
291    UNIMPLEMENTED();
292    return egl::EglBadDisplay();
293}
294
295bool DisplayCGL::isValidNativeWindow(EGLNativeWindowType window) const
296{
297    NSObject *layer = (__bridge NSObject *)window;
298    return [layer isKindOfClass:[CALayer class]];
299}
300
301egl::Error DisplayCGL::validateClientBuffer(const egl::Config *configuration,
302                                            EGLenum buftype,
303                                            EGLClientBuffer clientBuffer,
304                                            const egl::AttributeMap &attribs) const
305{
306    ASSERT(buftype == EGL_IOSURFACE_ANGLE);
307
308    if (!IOSurfaceSurfaceCGL::validateAttributes(clientBuffer, attribs))
309    {
310        return egl::EglBadAttribute();
311    }
312
313    return egl::NoError();
314}
315
316std::string DisplayCGL::getVendorString() const
317{
318    // TODO(cwallez) find a useful vendor string
319    return "";
320}
321
322CGLContextObj DisplayCGL::getCGLContext() const
323{
324    return mContext;
325}
326
327CGLPixelFormatObj DisplayCGL::getCGLPixelFormat() const
328{
329    return mPixelFormat;
330}
331
332void DisplayCGL::generateExtensions(egl::DisplayExtensions *outExtensions) const
333{
334    outExtensions->iosurfaceClientBuffer = true;
335    outExtensions->surfacelessContext    = true;
336    outExtensions->deviceQuery           = true;
337
338    // Contexts are virtualized so textures can be shared globally
339    outExtensions->displayTextureShareGroup = true;
340
341    if (mSupportsGPUSwitching)
342    {
343        outExtensions->powerPreference = true;
344    }
345
346    DisplayGL::generateExtensions(outExtensions);
347}
348
349void DisplayCGL::generateCaps(egl::Caps *outCaps) const
350{
351    outCaps->textureNPOT = true;
352}
353
354egl::Error DisplayCGL::waitClient(const gl::Context *context)
355{
356    // TODO(cwallez) UNIMPLEMENTED()
357    return egl::NoError();
358}
359
360egl::Error DisplayCGL::waitNative(const gl::Context *context, EGLint engine)
361{
362    // TODO(cwallez) UNIMPLEMENTED()
363    return egl::NoError();
364}
365
366gl::Version DisplayCGL::getMaxSupportedESVersion() const
367{
368    return mRenderer->getMaxSupportedESVersion();
369}
370
371egl::Error DisplayCGL::makeCurrentSurfaceless(gl::Context *context)
372{
373    // We have nothing to do as mContext is always current, and that CGL is surfaceless by
374    // default.
375    return egl::NoError();
376}
377
378class WorkerContextCGL final : public WorkerContext
379{
380  public:
381    WorkerContextCGL(CGLContextObj context);
382    ~WorkerContextCGL() override;
383
384    bool makeCurrent() override;
385    void unmakeCurrent() override;
386
387  private:
388    CGLContextObj mContext;
389};
390
391WorkerContextCGL::WorkerContextCGL(CGLContextObj context) : mContext(context) {}
392
393WorkerContextCGL::~WorkerContextCGL()
394{
395    CGLSetCurrentContext(nullptr);
396    CGLReleaseContext(mContext);
397    mContext = nullptr;
398}
399
400bool WorkerContextCGL::makeCurrent()
401{
402    CGLError error = CGLSetCurrentContext(mContext);
403    if (error != kCGLNoError)
404    {
405        ERR() << "Unable to make gl context current.";
406        return false;
407    }
408    return true;
409}
410
411void WorkerContextCGL::unmakeCurrent()
412{
413    CGLSetCurrentContext(nullptr);
414}
415
416WorkerContext *DisplayCGL::createWorkerContext(std::string *infoLog)
417{
418    CGLContextObj context = nullptr;
419    CGLCreateContext(mPixelFormat, mContext, &context);
420    if (context == nullptr)
421    {
422        *infoLog += "Could not create the CGL context.";
423        return nullptr;
424    }
425
426    return new WorkerContextCGL(context);
427}
428
429void DisplayCGL::unreferenceDiscreteGPU()
430{
431    ASSERT(mDiscreteGPURefs > 0);
432    if (--mDiscreteGPURefs == 0)
433    {
434        CGLDestroyPixelFormat(mDiscreteGPUPixelFormat);
435        mDiscreteGPUPixelFormat = nullptr;
436    }
437}
438
439void DisplayCGL::initializeFrontendFeatures(angle::FrontendFeatures *features) const
440{
441    mRenderer->initializeFrontendFeatures(features);
442}
443
444void DisplayCGL::populateFeatureList(angle::FeatureList *features)
445{
446    mRenderer->getFeatures().populateFeatureList(features);
447}
448}
449
450#endif  // defined(ANGLE_PLATFORM_MACOS) || defined(ANGLE_PLATFORM_MACCATALYST)
451