1 //
2 // Copyright 2016 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 // DisplayAndroid.cpp: Android implementation of egl::Display
8 
9 #include "libANGLE/renderer/gl/egl/android/DisplayAndroid.h"
10 
11 #include <android/log.h>
12 #include <android/native_window.h>
13 
14 #include "common/debug.h"
15 #include "libANGLE/Context.h"
16 #include "libANGLE/Display.h"
17 #include "libANGLE/Surface.h"
18 #include "libANGLE/renderer/gl/ContextGL.h"
19 #include "libANGLE/renderer/gl/RendererGL.h"
20 #include "libANGLE/renderer/gl/egl/ContextEGL.h"
21 #include "libANGLE/renderer/gl/egl/FunctionsEGLDL.h"
22 #include "libANGLE/renderer/gl/egl/RendererEGL.h"
23 #include "libANGLE/renderer/gl/egl/SurfaceEGL.h"
24 #include "libANGLE/renderer/gl/egl/android/NativeBufferImageSiblingAndroid.h"
25 #include "libANGLE/renderer/gl/renderergl_utils.h"
26 
27 namespace
28 {
GetEGLPath()29 const char *GetEGLPath()
30 {
31 #if defined(__LP64__)
32     return "/system/lib64/libEGL.so";
33 #else
34     return "/system/lib/libEGL.so";
35 #endif
36 }
37 }  // namespace
38 
39 namespace rx
40 {
41 
42 static constexpr bool kDefaultEGLVirtualizedContexts = true;
43 
DisplayAndroid(const egl::DisplayState & state)44 DisplayAndroid::DisplayAndroid(const egl::DisplayState &state)
45     : DisplayEGL(state),
46       mVirtualizedContexts(kDefaultEGLVirtualizedContexts),
47       mSupportsSurfaceless(false),
48       mMockPbuffer(EGL_NO_SURFACE)
49 {}
50 
~DisplayAndroid()51 DisplayAndroid::~DisplayAndroid() {}
52 
initialize(egl::Display * display)53 egl::Error DisplayAndroid::initialize(egl::Display *display)
54 {
55     mDisplayAttributes = display->getAttributeMap();
56     mVirtualizedContexts =
57         ShouldUseVirtualizedContexts(mDisplayAttributes, kDefaultEGLVirtualizedContexts);
58 
59     FunctionsEGLDL *egl = new FunctionsEGLDL();
60     mEGL                = egl;
61     void *eglHandle =
62         reinterpret_cast<void *>(mDisplayAttributes.get(EGL_PLATFORM_ANGLE_EGL_HANDLE_ANGLE, 0));
63     ANGLE_TRY(egl->initialize(display->getNativeDisplayId(), GetEGLPath(), eglHandle));
64 
65     gl::Version eglVersion(mEGL->majorVersion, mEGL->minorVersion);
66     ASSERT(eglVersion >= gl::Version(1, 4));
67 
68     std::vector<EGLint> renderableTypes;
69     static_assert(EGL_OPENGL_ES3_BIT == EGL_OPENGL_ES3_BIT_KHR, "Extension define must match core");
70     if (eglVersion >= gl::Version(1, 5) || mEGL->hasExtension("EGL_KHR_create_context"))
71     {
72         renderableTypes.push_back(EGL_OPENGL_ES3_BIT);
73     }
74     renderableTypes.push_back(EGL_OPENGL_ES2_BIT);
75 
76     egl::AttributeMap baseConfigAttribs;
77     baseConfigAttribs.insert(EGL_COLOR_BUFFER_TYPE, EGL_RGB_BUFFER);
78     // Android doesn't support pixmaps
79     baseConfigAttribs.insert(EGL_SURFACE_TYPE, EGL_WINDOW_BIT | EGL_PBUFFER_BIT);
80 
81     egl::AttributeMap configAttribsWithFormat(baseConfigAttribs);
82     // Choose RGBA8888
83     configAttribsWithFormat.insert(EGL_RED_SIZE, 8);
84     configAttribsWithFormat.insert(EGL_GREEN_SIZE, 8);
85     configAttribsWithFormat.insert(EGL_BLUE_SIZE, 8);
86     configAttribsWithFormat.insert(EGL_ALPHA_SIZE, 8);
87 
88     // Choose D24S8
89     // EGL1.5 spec Section 2.2 says that depth, multisample and stencil buffer depths
90     // must match for contexts to be compatible.
91     configAttribsWithFormat.insert(EGL_DEPTH_SIZE, 24);
92     configAttribsWithFormat.insert(EGL_STENCIL_SIZE, 8);
93 
94     EGLConfig configWithFormat = EGL_NO_CONFIG_KHR;
95     for (EGLint renderableType : renderableTypes)
96     {
97         baseConfigAttribs.insert(EGL_RENDERABLE_TYPE, renderableType);
98         configAttribsWithFormat.insert(EGL_RENDERABLE_TYPE, renderableType);
99 
100         std::vector<EGLint> attribVector = configAttribsWithFormat.toIntVector();
101 
102         EGLint numConfig = 0;
103         if (mEGL->chooseConfig(attribVector.data(), &configWithFormat, 1, &numConfig) == EGL_TRUE)
104         {
105             break;
106         }
107     }
108 
109     if (configWithFormat == EGL_NO_CONFIG_KHR)
110     {
111         return egl::EglNotInitialized()
112                << "eglChooseConfig failed with " << egl::Error(mEGL->getError());
113     }
114 
115     // A mock pbuffer is only needed if surfaceless contexts are not supported.
116     mSupportsSurfaceless = mEGL->hasExtension("EGL_KHR_surfaceless_context");
117     if (!mSupportsSurfaceless)
118     {
119         int mockPbufferAttribs[] = {
120             EGL_WIDTH, 1, EGL_HEIGHT, 1, EGL_NONE,
121         };
122         mMockPbuffer = mEGL->createPbufferSurface(configWithFormat, mockPbufferAttribs);
123         if (mMockPbuffer == EGL_NO_SURFACE)
124         {
125             return egl::EglNotInitialized()
126                    << "eglCreatePbufferSurface failed with " << egl::Error(mEGL->getError());
127         }
128     }
129 
130     // Create mMockPbuffer with a normal config, but create a no_config mContext, if possible
131     if (mEGL->hasExtension("EGL_KHR_no_config_context"))
132     {
133         mConfigAttribList = baseConfigAttribs.toIntVector();
134         mConfig           = EGL_NO_CONFIG_KHR;
135     }
136     else
137     {
138         mConfigAttribList = configAttribsWithFormat.toIntVector();
139         mConfig           = configWithFormat;
140     }
141 
142     ANGLE_TRY(createRenderer(EGL_NO_CONTEXT, true, false, &mRenderer));
143 
144     const gl::Version &maxVersion = mRenderer->getMaxSupportedESVersion();
145     if (maxVersion < gl::Version(2, 0))
146     {
147         return egl::EglNotInitialized() << "OpenGL ES 2.0 is not supportable.";
148     }
149 
150     ANGLE_TRY(DisplayGL::initialize(display));
151 
152     std::string rendererDescription = getRendererDescription();
153     __android_log_print(ANDROID_LOG_INFO, "ANGLE", "%s", rendererDescription.c_str());
154     return egl::NoError();
155 }
156 
terminate()157 void DisplayAndroid::terminate()
158 {
159     DisplayGL::terminate();
160 
161     EGLBoolean success = mEGL->makeCurrent(EGL_NO_SURFACE, EGL_NO_CONTEXT);
162     if (success == EGL_FALSE)
163     {
164         ERR() << "eglMakeCurrent error " << egl::Error(mEGL->getError());
165     }
166 
167     if (mMockPbuffer != EGL_NO_SURFACE)
168     {
169         success      = mEGL->destroySurface(mMockPbuffer);
170         mMockPbuffer = EGL_NO_SURFACE;
171         if (success == EGL_FALSE)
172         {
173             ERR() << "eglDestroySurface error " << egl::Error(mEGL->getError());
174         }
175     }
176 
177     mRenderer.reset();
178 
179     egl::Error result = mEGL->terminate();
180     if (result.isError())
181     {
182         ERR() << "eglTerminate error " << result;
183     }
184 
185     SafeDelete(mEGL);
186 }
187 
createContext(const gl::State & state,gl::ErrorSet * errorSet,const egl::Config * configuration,const gl::Context * shareContext,const egl::AttributeMap & attribs)188 ContextImpl *DisplayAndroid::createContext(const gl::State &state,
189                                            gl::ErrorSet *errorSet,
190                                            const egl::Config *configuration,
191                                            const gl::Context *shareContext,
192                                            const egl::AttributeMap &attribs)
193 {
194     std::shared_ptr<RendererEGL> renderer;
195     bool usingExternalContext = attribs.get(EGL_EXTERNAL_CONTEXT_ANGLE, EGL_FALSE) == EGL_TRUE;
196     if (mVirtualizedContexts && !usingExternalContext)
197     {
198         renderer = mRenderer;
199     }
200     else
201     {
202         EGLContext nativeShareContext = EGL_NO_CONTEXT;
203         if (usingExternalContext)
204         {
205             ASSERT(!shareContext);
206         }
207         else if (shareContext)
208         {
209             ContextEGL *shareContextEGL = GetImplAs<ContextEGL>(shareContext);
210             nativeShareContext          = shareContextEGL->getContext();
211         }
212 
213         // Create a new renderer for this context.  It only needs to share with the user's requested
214         // share context because there are no internal resources in DisplayAndroid that are shared
215         // at the GL level.
216         egl::Error error =
217             createRenderer(nativeShareContext, false, usingExternalContext, &renderer);
218         if (error.isError())
219         {
220             ERR() << "Failed to create a shared renderer: " << error.getMessage();
221             return nullptr;
222         }
223     }
224 
225     return new ContextEGL(state, errorSet, renderer,
226                           RobustnessVideoMemoryPurgeStatus::NOT_REQUESTED);
227 }
228 
229 class ExternalSurfaceEGL : public SurfaceEGL
230 {
231   public:
ExternalSurfaceEGL(const egl::SurfaceState & state,const FunctionsEGL * egl,EGLConfig config,EGLint width,EGLint height)232     ExternalSurfaceEGL(const egl::SurfaceState &state,
233                        const FunctionsEGL *egl,
234                        EGLConfig config,
235                        EGLint width,
236                        EGLint height)
237         : SurfaceEGL(state, egl, config), mWidth(width), mHeight(height)
238     {}
239     ~ExternalSurfaceEGL() override = default;
240 
initialize(const egl::Display * display)241     egl::Error initialize(const egl::Display *display) override { return egl::NoError(); }
getSwapBehavior() const242     EGLint getSwapBehavior() const override { return EGL_BUFFER_DESTROYED; }
getWidth() const243     EGLint getWidth() const override { return mWidth; }
getHeight() const244     EGLint getHeight() const override { return mHeight; }
isExternal() const245     bool isExternal() const override { return true; }
246 
247   private:
248     const EGLint mWidth;
249     const EGLint mHeight;
250 };
251 
createPbufferFromClientBuffer(const egl::SurfaceState & state,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs)252 SurfaceImpl *DisplayAndroid::createPbufferFromClientBuffer(const egl::SurfaceState &state,
253                                                            EGLenum buftype,
254                                                            EGLClientBuffer clientBuffer,
255                                                            const egl::AttributeMap &attribs)
256 {
257     if (buftype == EGL_EXTERNAL_SURFACE_ANGLE)
258     {
259         ASSERT(clientBuffer == nullptr);
260 
261         EGLint width  = static_cast<EGLint>(attribs.get(EGL_WIDTH, 0));
262         EGLint height = static_cast<EGLint>(attribs.get(EGL_HEIGHT, 0));
263 
264         // Use the ExternalSurfaceEGL, so ANGLE can know the framebuffer size.
265         return new ExternalSurfaceEGL(state, mEGL, EGL_NO_CONFIG_KHR, width, height);
266     }
267 
268     return DisplayEGL::createPbufferFromClientBuffer(state, buftype, clientBuffer, attribs);
269 }
270 
isValidNativeWindow(EGLNativeWindowType window) const271 bool DisplayAndroid::isValidNativeWindow(EGLNativeWindowType window) const
272 {
273     return ANativeWindow_getFormat(window) >= 0;
274 }
275 
validateClientBuffer(const egl::Config * configuration,EGLenum buftype,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs) const276 egl::Error DisplayAndroid::validateClientBuffer(const egl::Config *configuration,
277                                                 EGLenum buftype,
278                                                 EGLClientBuffer clientBuffer,
279                                                 const egl::AttributeMap &attribs) const
280 {
281 
282     if (buftype == EGL_EXTERNAL_SURFACE_ANGLE)
283     {
284         ASSERT(clientBuffer == nullptr);
285         return egl::NoError();
286     }
287     return DisplayEGL::validateClientBuffer(configuration, buftype, clientBuffer, attribs);
288 }
289 
validateImageClientBuffer(const gl::Context * context,EGLenum target,EGLClientBuffer clientBuffer,const egl::AttributeMap & attribs) const290 egl::Error DisplayAndroid::validateImageClientBuffer(const gl::Context *context,
291                                                      EGLenum target,
292                                                      EGLClientBuffer clientBuffer,
293                                                      const egl::AttributeMap &attribs) const
294 {
295     switch (target)
296     {
297         case EGL_NATIVE_BUFFER_ANDROID:
298             return egl::NoError();
299 
300         default:
301             return DisplayEGL::validateImageClientBuffer(context, target, clientBuffer, attribs);
302     }
303 }
304 
createExternalImageSibling(const gl::Context * context,EGLenum target,EGLClientBuffer buffer,const egl::AttributeMap & attribs)305 ExternalImageSiblingImpl *DisplayAndroid::createExternalImageSibling(
306     const gl::Context *context,
307     EGLenum target,
308     EGLClientBuffer buffer,
309     const egl::AttributeMap &attribs)
310 {
311     switch (target)
312     {
313         case EGL_NATIVE_BUFFER_ANDROID:
314             return new NativeBufferImageSiblingAndroid(buffer);
315 
316         default:
317             return DisplayEGL::createExternalImageSibling(context, target, buffer, attribs);
318     }
319 }
320 
makeCurrent(egl::Display * display,egl::Surface * drawSurface,egl::Surface * readSurface,gl::Context * context)321 egl::Error DisplayAndroid::makeCurrent(egl::Display *display,
322                                        egl::Surface *drawSurface,
323                                        egl::Surface *readSurface,
324                                        gl::Context *context)
325 {
326     CurrentNativeContext &currentContext = mCurrentNativeContexts[std::this_thread::get_id()];
327 
328     EGLSurface newSurface = EGL_NO_SURFACE;
329     if (drawSurface)
330     {
331         SurfaceEGL *drawSurfaceEGL = GetImplAs<SurfaceEGL>(drawSurface);
332         newSurface                 = drawSurfaceEGL->getSurface();
333     }
334 
335     EGLContext newContext = EGL_NO_CONTEXT;
336     if (context)
337     {
338         ContextEGL *contextEGL = GetImplAs<ContextEGL>(context);
339         newContext             = contextEGL->getContext();
340     }
341 
342     if (currentContext.isExternalContext || (context && context->isExternal()))
343     {
344         ASSERT(currentContext.surface == EGL_NO_SURFACE);
345         if (!currentContext.isExternalContext)
346         {
347             // Switch to an ANGLE external context.
348             ASSERT(context);
349             ASSERT(currentContext.context == EGL_NO_CONTEXT);
350             currentContext.context           = newContext;
351             currentContext.isExternalContext = true;
352 
353             // We only support using external surface with external context.
354             ASSERT(GetImplAs<SurfaceEGL>(drawSurface)->isExternal());
355             ASSERT(GetImplAs<SurfaceEGL>(drawSurface)->getSurface() == EGL_NO_SURFACE);
356         }
357         else if (context)
358         {
359             // Switch surface but not context.
360             ASSERT(currentContext.context == newContext);
361             ASSERT(newSurface == EGL_NO_SURFACE);
362             ASSERT(newContext != EGL_NO_CONTEXT);
363             // We only support using external surface with external context.
364             ASSERT(GetImplAs<SurfaceEGL>(drawSurface)->isExternal());
365             ASSERT(GetImplAs<SurfaceEGL>(drawSurface)->getSurface() == EGL_NO_SURFACE);
366         }
367         else
368         {
369             // Release the ANGLE external context.
370             ASSERT(newSurface == EGL_NO_SURFACE);
371             ASSERT(newContext == EGL_NO_CONTEXT);
372             ASSERT(currentContext.context != EGL_NO_CONTEXT);
373             currentContext.context           = EGL_NO_CONTEXT;
374             currentContext.isExternalContext = false;
375         }
376 
377         // Do not need to call eglMakeCurrent(), since we don't support swtiching EGLSurface for
378         // external context.
379         return DisplayGL::makeCurrent(display, drawSurface, readSurface, context);
380     }
381 
382     // The context should never change when context virtualization is being used unless binding a
383     // null context.
384     if (mVirtualizedContexts && newContext != EGL_NO_CONTEXT)
385     {
386         ASSERT(currentContext.context == EGL_NO_CONTEXT || newContext == currentContext.context);
387 
388         newContext = mRenderer->getContext();
389 
390         // If we know that we're only running on one thread (mVirtualizedContexts == true) and
391         // EGL_NO_SURFACE is going to be bound, we can optimize this case by not changing the
392         // surface binding and emulate the surfaceless extension in the frontend.
393         if (newSurface == EGL_NO_SURFACE)
394         {
395             newSurface = currentContext.surface;
396         }
397 
398         // It's possible that no surface has been created yet and the driver doesn't support
399         // surfaceless, bind the mock pbuffer.
400         if (newSurface == EGL_NO_SURFACE && !mSupportsSurfaceless)
401         {
402             newSurface = mMockPbuffer;
403             ASSERT(newSurface != EGL_NO_SURFACE);
404         }
405     }
406 
407     if (newSurface != currentContext.surface || newContext != currentContext.context)
408     {
409         if (mEGL->makeCurrent(newSurface, newContext) == EGL_FALSE)
410         {
411             return egl::Error(mEGL->getError(), "eglMakeCurrent failed");
412         }
413         currentContext.surface = newSurface;
414         currentContext.context = newContext;
415     }
416 
417     return DisplayGL::makeCurrent(display, drawSurface, readSurface, context);
418 }
419 
destroyNativeContext(EGLContext context)420 void DisplayAndroid::destroyNativeContext(EGLContext context)
421 {
422     DisplayEGL::destroyNativeContext(context);
423 }
424 
generateExtensions(egl::DisplayExtensions * outExtensions) const425 void DisplayAndroid::generateExtensions(egl::DisplayExtensions *outExtensions) const
426 {
427     DisplayEGL::generateExtensions(outExtensions);
428 
429     // Surfaceless can be support if the native driver supports it or we know that we are running on
430     // a single thread (mVirtualizedContexts == true)
431     outExtensions->surfacelessContext = mSupportsSurfaceless || mVirtualizedContexts;
432 
433     outExtensions->externalContextAndSurface = true;
434 }
435 
createRenderer(EGLContext shareContext,bool makeNewContextCurrent,bool isExternalContext,std::shared_ptr<RendererEGL> * outRenderer)436 egl::Error DisplayAndroid::createRenderer(EGLContext shareContext,
437                                           bool makeNewContextCurrent,
438                                           bool isExternalContext,
439                                           std::shared_ptr<RendererEGL> *outRenderer)
440 {
441     EGLContext context = EGL_NO_CONTEXT;
442     native_egl::AttributeVector attribs;
443 
444     // If isExternalContext is true, the external context is current, so we don't need to make the
445     // mMockPbuffer current.
446     if (isExternalContext)
447     {
448         ASSERT(shareContext == EGL_NO_CONTEXT);
449         ASSERT(!makeNewContextCurrent);
450         // Should we consider creating a share context to avoid querying and restoring GL context
451         // state?
452         context = mEGL->getCurrentContext();
453         ASSERT(context != EGL_NO_CONTEXT);
454         // TODO: get the version from the current context.
455         attribs = {EGL_CONTEXT_MAJOR_VERSION, 2, EGL_CONTEXT_MINOR_VERSION, 0, EGL_NONE};
456     }
457     else
458     {
459         ANGLE_TRY(initializeContext(shareContext, mDisplayAttributes, &context, &attribs));
460         if (mEGL->makeCurrent(mMockPbuffer, context) == EGL_FALSE)
461         {
462             return egl::EglNotInitialized()
463                    << "eglMakeCurrent failed with " << egl::Error(mEGL->getError());
464         }
465     }
466 
467     std::unique_ptr<FunctionsGL> functionsGL(mEGL->makeFunctionsGL());
468     functionsGL->initialize(mDisplayAttributes);
469 
470     outRenderer->reset(new RendererEGL(std::move(functionsGL), mDisplayAttributes, this, context,
471                                        attribs, isExternalContext));
472 
473     CurrentNativeContext &currentContext = mCurrentNativeContexts[std::this_thread::get_id()];
474     if (makeNewContextCurrent)
475     {
476         currentContext.surface = mMockPbuffer;
477         currentContext.context = context;
478     }
479     else if (!isExternalContext)
480     {
481         // Reset the current context back to the previous state
482         if (mEGL->makeCurrent(currentContext.surface, currentContext.context) == EGL_FALSE)
483         {
484             return egl::EglNotInitialized()
485                    << "eglMakeCurrent failed with " << egl::Error(mEGL->getError());
486         }
487     }
488 
489     return egl::NoError();
490 }
491 
492 class WorkerContextAndroid final : public WorkerContext
493 {
494   public:
495     WorkerContextAndroid(EGLContext context, FunctionsEGL *functions, EGLSurface pbuffer);
496     ~WorkerContextAndroid() override;
497 
498     bool makeCurrent() override;
499     void unmakeCurrent() override;
500 
501   private:
502     EGLContext mContext;
503     FunctionsEGL *mFunctions;
504     EGLSurface mPbuffer;
505 };
506 
WorkerContextAndroid(EGLContext context,FunctionsEGL * functions,EGLSurface pbuffer)507 WorkerContextAndroid::WorkerContextAndroid(EGLContext context,
508                                            FunctionsEGL *functions,
509                                            EGLSurface pbuffer)
510     : mContext(context), mFunctions(functions), mPbuffer(pbuffer)
511 {}
512 
~WorkerContextAndroid()513 WorkerContextAndroid::~WorkerContextAndroid()
514 {
515     mFunctions->destroyContext(mContext);
516 }
517 
makeCurrent()518 bool WorkerContextAndroid::makeCurrent()
519 {
520     if (mFunctions->makeCurrent(mPbuffer, mContext) == EGL_FALSE)
521     {
522         ERR() << "Unable to make the EGL context current.";
523         return false;
524     }
525     return true;
526 }
527 
unmakeCurrent()528 void WorkerContextAndroid::unmakeCurrent()
529 {
530     mFunctions->makeCurrent(EGL_NO_SURFACE, EGL_NO_CONTEXT);
531 }
532 
createWorkerContext(std::string * infoLog,EGLContext sharedContext,const native_egl::AttributeVector workerAttribs)533 WorkerContext *DisplayAndroid::createWorkerContext(std::string *infoLog,
534                                                    EGLContext sharedContext,
535                                                    const native_egl::AttributeVector workerAttribs)
536 {
537     EGLContext context = mEGL->createContext(mConfig, sharedContext, workerAttribs.data());
538     if (context == EGL_NO_CONTEXT)
539     {
540         *infoLog += "Unable to create the EGL context.";
541         return nullptr;
542     }
543     return new WorkerContextAndroid(context, mEGL, mMockPbuffer);
544 }
545 
546 }  // namespace rx
547