• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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// DisplayEAGL.cpp: EAGL implementation of egl::Display
8
9#import "common/platform.h"
10
11#if defined(ANGLE_ENABLE_EAGL)
12
13#    import "libANGLE/renderer/gl/eagl/DisplayEAGL.h"
14
15#    import "common/debug.h"
16#    import "gpu_info_util/SystemInfo.h"
17#    import "libANGLE/Display.h"
18#    import "libANGLE/renderer/gl/eagl/ContextEAGL.h"
19#    import "libANGLE/renderer/gl/eagl/DeviceEAGL.h"
20#    import "libANGLE/renderer/gl/eagl/FunctionsEAGL.h"
21#    import "libANGLE/renderer/gl/eagl/IOSurfaceSurfaceEAGL.h"
22#    import "libANGLE/renderer/gl/eagl/PbufferSurfaceEAGL.h"
23#    import "libANGLE/renderer/gl/eagl/RendererEAGL.h"
24#    import "libANGLE/renderer/gl/eagl/WindowSurfaceEAGL.h"
25
26#    import <Foundation/Foundation.h>
27#    import <QuartzCore/QuartzCore.h>
28#    import <dlfcn.h>
29
30namespace
31{
32
33const char *kOpenGLESDylibName = "/System/Library/Frameworks/OpenGLES.framework/OpenGLES";
34
35}
36
37namespace rx
38{
39
40class FunctionsGLEAGL : public FunctionsGL
41{
42  public:
43    FunctionsGLEAGL(void *dylibHandle) : mDylibHandle(dylibHandle) {}
44
45    ~FunctionsGLEAGL() override { dlclose(mDylibHandle); }
46
47  private:
48    void *loadProcAddress(const std::string &function) const override
49    {
50        return dlsym(mDylibHandle, function.c_str());
51    }
52
53    void *mDylibHandle;
54};
55
56DisplayEAGL::DisplayEAGL(const egl::DisplayState &state)
57    : DisplayGL(state), mEGLDisplay(nullptr), mContext(nullptr)
58{}
59
60DisplayEAGL::~DisplayEAGL() {}
61
62egl::Error DisplayEAGL::initialize(egl::Display *display)
63{
64    mEGLDisplay = display;
65
66    angle::SystemInfo info;
67    // It's legal for GetSystemInfo to return false and thereby
68    // contain incomplete information.
69    (void)angle::GetSystemInfo(&info);
70
71    mContext = [allocEAGLContextInstance() initWithAPI:kEAGLRenderingAPIOpenGLES3];
72    if (mContext == nullptr)
73    {
74        return egl::EglNotInitialized() << "Could not create the EAGL context.";
75    }
76
77    if (![getEAGLContextClass() setCurrentContext:mContext])
78    {
79        return egl::EglNotInitialized() << "Could set the EAGL context current.";
80    }
81    mThreadsWithContextCurrent.insert(std::this_thread::get_id());
82
83    // There is no equivalent getProcAddress in EAGL so we open the dylib directly
84    void *handle = dlopen(kOpenGLESDylibName, RTLD_NOW);
85    if (!handle)
86    {
87        return egl::EglNotInitialized() << "Could not open the OpenGLES Framework.";
88    }
89
90    std::unique_ptr<FunctionsGL> functionsGL(new FunctionsGLEAGL(handle));
91    functionsGL->initialize(display->getAttributeMap());
92
93    mRenderer.reset(new RendererEAGL(std::move(functionsGL), display->getAttributeMap(), this));
94
95    const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion();
96    if (maxVersion < gl::Version(2, 0))
97    {
98        return egl::EglNotInitialized() << "OpenGL ES 2.0 is not supportable.";
99    }
100
101    auto &attributes = display->getAttributeMap();
102    mDeviceContextIsVolatile =
103        attributes.get(EGL_PLATFORM_ANGLE_DEVICE_CONTEXT_VOLATILE_EAGL_ANGLE, GL_FALSE);
104
105    return DisplayGL::initialize(display);
106}
107
108void DisplayEAGL::terminate()
109{
110    DisplayGL::terminate();
111
112    mRenderer.reset();
113    if (mContext != nullptr)
114    {
115        [getEAGLContextClass() setCurrentContext:nil];
116        [mContext release];
117        mContext = nullptr;
118        mThreadsWithContextCurrent.clear();
119    }
120}
121
122egl::Error DisplayEAGL::prepareForCall()
123{
124    auto threadId = std::this_thread::get_id();
125    if (mDeviceContextIsVolatile ||
126        mThreadsWithContextCurrent.find(threadId) == mThreadsWithContextCurrent.end())
127    {
128        if (![getEAGLContextClass() setCurrentContext:mContext])
129        {
130            return egl::EglBadAlloc() << "Could not make device EAGL context current.";
131        }
132        mThreadsWithContextCurrent.insert(threadId);
133    }
134    return egl::NoError();
135}
136
137egl::Error DisplayEAGL::releaseThread()
138{
139    auto threadId = std::this_thread::get_id();
140    if (mThreadsWithContextCurrent.find(threadId) != mThreadsWithContextCurrent.end())
141    {
142        if (![getEAGLContextClass() setCurrentContext:nil])
143        {
144            return egl::EglBadAlloc() << "Could not release device EAGL context.";
145        }
146        mThreadsWithContextCurrent.erase(threadId);
147    }
148    return egl::NoError();
149}
150
151SurfaceImpl *DisplayEAGL::createWindowSurface(const egl::SurfaceState &state,
152                                              EGLNativeWindowType window,
153                                              const egl::AttributeMap &attribs)
154{
155    return new WindowSurfaceEAGL(state, mRenderer.get(), window, mContext);
156}
157
158SurfaceImpl *DisplayEAGL::createPbufferSurface(const egl::SurfaceState &state,
159                                               const egl::AttributeMap &attribs)
160{
161    EGLint width  = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0));
162    EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0));
163    return new PbufferSurfaceEAGL(state, mRenderer.get(), width, height);
164}
165
166SurfaceImpl *DisplayEAGL::createPbufferFromClientBuffer(const egl::SurfaceState &state,
167                                                        EGLenum buftype,
168                                                        EGLClientBuffer clientBuffer,
169                                                        const egl::AttributeMap &attribs)
170{
171    ASSERT(buftype == EGL_IOSURFACE_ANGLE);
172    return new IOSurfaceSurfaceEAGL(state, mContext, clientBuffer, attribs);
173}
174
175SurfaceImpl *DisplayEAGL::createPixmapSurface(const egl::SurfaceState &state,
176                                              NativePixmapType nativePixmap,
177                                              const egl::AttributeMap &attribs)
178{
179    UNIMPLEMENTED();
180    return nullptr;
181}
182
183ContextImpl *DisplayEAGL::createContext(const gl::State &state,
184                                        gl::ErrorSet *errorSet,
185                                        const egl::Config *configuration,
186                                        const gl::Context *shareContext,
187                                        const egl::AttributeMap &attribs)
188{
189    return new ContextEAGL(state, errorSet, mRenderer);
190}
191
192DeviceImpl *DisplayEAGL::createDevice()
193{
194    return new DeviceEAGL();
195}
196
197egl::ConfigSet DisplayEAGL::generateConfigs()
198{
199    // TODO(cwallez): generate more config permutations
200    egl::ConfigSet configs;
201
202    const gl::Version &maxVersion = getMaxSupportedESVersion();
203    ASSERT(maxVersion >= gl::Version(2, 0));
204    bool supportsES3 = maxVersion >= gl::Version(3, 0);
205
206    egl::Config config;
207
208    // Native stuff
209    config.nativeVisualID   = 0;
210    config.nativeVisualType = 0;
211    config.nativeRenderable = EGL_TRUE;
212
213    // Buffer sizes
214    config.redSize     = 8;
215    config.greenSize   = 8;
216    config.blueSize    = 8;
217    config.alphaSize   = 8;
218    config.depthSize   = 24;
219    config.stencilSize = 8;
220
221    config.colorBufferType = EGL_RGB_BUFFER;
222    config.luminanceSize   = 0;
223    config.alphaMaskSize   = 0;
224
225    config.bufferSize = config.redSize + config.greenSize + config.blueSize + config.alphaSize;
226
227    config.transparentType = EGL_NONE;
228
229    // Pbuffer
230    config.maxPBufferWidth  = 4096;
231    config.maxPBufferHeight = 4096;
232    config.maxPBufferPixels = 4096 * 4096;
233
234    // Caveat
235    config.configCaveat = EGL_NONE;
236
237    // Misc
238    config.sampleBuffers     = 0;
239    config.samples           = 0;
240    config.level             = 0;
241    config.bindToTextureRGB  = EGL_FALSE;
242    config.bindToTextureRGBA = EGL_FALSE;
243
244    config.bindToTextureTarget = EGL_TEXTURE_2D;
245
246    config.surfaceType = EGL_WINDOW_BIT | EGL_PBUFFER_BIT;
247
248    config.minSwapInterval = 1;
249    config.maxSwapInterval = 1;
250
251    config.renderTargetFormat = GL_RGBA8;
252    config.depthStencilFormat = GL_DEPTH24_STENCIL8;
253
254    config.conformant     = EGL_OPENGL_ES2_BIT | (supportsES3 ? EGL_OPENGL_ES3_BIT_KHR : 0);
255    config.renderableType = config.conformant;
256
257    config.matchNativePixmap = EGL_NONE;
258
259    config.colorComponentType = EGL_COLOR_COMPONENT_TYPE_FIXED_EXT;
260
261    configs.add(config);
262    return configs;
263}
264
265bool DisplayEAGL::testDeviceLost()
266{
267    // TODO(cwallez) investigate implementing this
268    return false;
269}
270
271egl::Error DisplayEAGL::restoreLostDevice(const egl::Display *display)
272{
273    UNIMPLEMENTED();
274    return egl::EglBadDisplay();
275}
276
277bool DisplayEAGL::isValidNativeWindow(EGLNativeWindowType window) const
278{
279    NSObject *layer = reinterpret_cast<NSObject *>(window);
280    return [layer isKindOfClass:[CALayer class]];
281}
282
283egl::Error DisplayEAGL::validateClientBuffer(const egl::Config *configuration,
284                                             EGLenum buftype,
285                                             EGLClientBuffer clientBuffer,
286                                             const egl::AttributeMap &attribs) const
287{
288    ASSERT(buftype == EGL_IOSURFACE_ANGLE);
289
290    if (!IOSurfaceSurfaceEAGL::validateAttributes(clientBuffer, attribs))
291    {
292        return egl::EglBadAttribute();
293    }
294
295    return egl::NoError();
296}
297
298EAGLContextObj DisplayEAGL::getEAGLContext() const
299{
300    return mContext;
301}
302
303void DisplayEAGL::generateExtensions(egl::DisplayExtensions *outExtensions) const
304{
305    outExtensions->iosurfaceClientBuffer = true;
306    outExtensions->surfacelessContext    = true;
307
308    // Contexts are virtualized so textures and semaphores can be shared globally
309    outExtensions->displayTextureShareGroup   = true;
310    outExtensions->displaySemaphoreShareGroup = true;
311
312    DisplayGL::generateExtensions(outExtensions);
313}
314
315void DisplayEAGL::generateCaps(egl::Caps *outCaps) const
316{
317    outCaps->textureNPOT = true;
318}
319
320egl::Error DisplayEAGL::waitClient(const gl::Context *context)
321{
322    // TODO(cwallez) UNIMPLEMENTED()
323    return egl::NoError();
324}
325
326egl::Error DisplayEAGL::waitNative(const gl::Context *context, EGLint engine)
327{
328    // TODO(cwallez) UNIMPLEMENTED()
329    return egl::NoError();
330}
331
332gl::Version DisplayEAGL::getMaxSupportedESVersion() const
333{
334    return mRenderer->getMaxSupportedESVersion();
335}
336
337egl::Error DisplayEAGL::makeCurrentSurfaceless(gl::Context *context)
338{
339    // We have nothing to do as mContext is always current, and that EAGL is surfaceless by
340    // default.
341    return egl::NoError();
342}
343
344class WorkerContextEAGL final : public WorkerContext
345{
346  public:
347    WorkerContextEAGL(EAGLContextObj context);
348    ~WorkerContextEAGL() override;
349
350    bool makeCurrent() override;
351    void unmakeCurrent() override;
352
353  private:
354    EAGLContextObj mContext;
355};
356
357WorkerContextEAGL::WorkerContextEAGL(EAGLContextObj context) : mContext(context) {}
358
359WorkerContextEAGL::~WorkerContextEAGL()
360{
361    [getEAGLContextClass() setCurrentContext:nil];
362    mContext = nullptr;
363}
364
365bool WorkerContextEAGL::makeCurrent()
366{
367    if (![getEAGLContextClass() setCurrentContext:static_cast<EAGLContext *>(mContext)])
368    {
369        ERR() << "Unable to make gl context current.";
370        return false;
371    }
372    return true;
373}
374
375void WorkerContextEAGL::unmakeCurrent()
376{
377    [getEAGLContextClass() setCurrentContext:nil];
378}
379
380WorkerContext *DisplayEAGL::createWorkerContext(std::string *infoLog)
381{
382    EAGLContextObj context = nullptr;
383    context                = [allocEAGLContextInstance() initWithAPI:kEAGLRenderingAPIOpenGLES3];
384    if (!context)
385    {
386        *infoLog += "Could not create the EAGL context.";
387        return nullptr;
388    }
389
390    return new WorkerContextEAGL(context);
391}
392
393void DisplayEAGL::initializeFrontendFeatures(angle::FrontendFeatures *features) const
394{
395    mRenderer->initializeFrontendFeatures(features);
396}
397
398void DisplayEAGL::populateFeatureList(angle::FeatureList *features)
399{
400    mRenderer->getFeatures().populateFeatureList(features);
401}
402
403RendererGL *DisplayEAGL::getRenderer() const
404{
405    return mRenderer.get();
406}
407
408}
409
410#endif  // defined(ANGLE_ENABLE_EAGL)
411