• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  ** Copyright 2007, The Android Open Source Project
3  **
4  ** Licensed under the Apache License, Version 2.0 (the "License");
5  ** you may not use this file except in compliance with the License.
6  ** You may obtain a copy of the License at
7  **
8  **     http://www.apache.org/licenses/LICENSE-2.0
9  **
10  ** Unless required by applicable law or agreed to in writing, software
11  ** distributed under the License is distributed on an "AS IS" BASIS,
12  ** WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13  ** See the License for the specific language governing permissions and
14  ** limitations under the License.
15  */
16 
17 #define ATRACE_TAG ATRACE_TAG_GRAPHICS
18 
19 #include "egl_display.h"
20 
21 #include <SurfaceFlingerProperties.h>
22 #include <android-base/properties.h>
23 #include <android/dlext.h>
24 #include <android/hardware/configstore/1.0/ISurfaceFlingerConfigs.h>
25 #include <com_android_graphics_graphicsenv_flags.h>
26 #include <configstore/Utils.h>
27 #include <dlfcn.h>
28 #include <graphicsenv/GraphicsEnv.h>
29 
30 #include "../egl_impl.h"
31 #include "EGL/eglext_angle.h"
32 #include "Loader.h"
33 #include "egl_angle_platform.h"
34 #include "egl_cache.h"
35 #include "egl_object.h"
36 #include "egl_tls.h"
37 #include "private/EGL/display.h"
38 
39 using namespace android::hardware::configstore;
40 using namespace android::hardware::configstore::V1_0;
41 namespace graphicsenv_flags = com::android::graphics::graphicsenv::flags;
42 
43 namespace android {
44 
45 static const char* const sVendorString = "Android";
46 static const char* const sVersionString14 = "1.4 Android META-EGL";
47 static const char* const sVersionString15 = "1.5 Android META-EGL";
48 static const char* const sClientApiString = "OpenGL_ES";
49 
50 extern const char* const gBuiltinExtensionString;
51 extern const char* const gExtensionString;
52 
53 extern void setGLHooksThreadSpecific(gl_hooks_t const* value);
54 
findExtension(const char * exts,const char * name,size_t nameLen)55 bool findExtension(const char* exts, const char* name, size_t nameLen) {
56     if (exts) {
57         if (!nameLen) {
58             nameLen = strlen(name);
59         }
60         for (const char* match = strstr(exts, name); match; match = strstr(match + nameLen, name)) {
61             if (match[nameLen] == '\0' || match[nameLen] == ' ') {
62                 return true;
63             }
64         }
65     }
66     return false;
67 }
68 
egl_get_init_count(EGLDisplay dpy)69 int egl_get_init_count(EGLDisplay dpy) {
70     egl_display_t* eglDisplay = egl_display_t::get(dpy);
71     return eglDisplay ? eglDisplay->getRefsCount() : 0;
72 }
73 
74 std::map<EGLDisplay, std::unique_ptr<egl_display_t>> egl_display_t::displayMap;
75 std::mutex egl_display_t::displayMapLock;
76 
egl_display_t()77 egl_display_t::egl_display_t()
78       : magic('_dpy'),
79         finishOnSwap(false),
80         traceGpuCompletion(false),
81         refs(0),
82         eglIsInitialized(false) {}
83 
~egl_display_t()84 egl_display_t::~egl_display_t() {
85     magic = 0;
86     egl_cache_t::get()->terminate();
87 }
88 
get(EGLDisplay dpy)89 egl_display_t* egl_display_t::get(EGLDisplay dpy) {
90     if (uintptr_t(dpy) == 0) {
91         return nullptr;
92     }
93 
94     const std::lock_guard<std::mutex> lock(displayMapLock);
95     auto search = displayMap.find(dpy);
96     if (search == displayMap.end() || !search->second->isValid()) {
97         return nullptr;
98     }
99     return search->second.get();
100 }
101 
addObject(egl_object_t * object)102 void egl_display_t::addObject(egl_object_t* object) {
103     std::lock_guard<std::mutex> _l(lock);
104     objects.insert(object);
105 }
106 
removeObject(egl_object_t * object)107 void egl_display_t::removeObject(egl_object_t* object) {
108     std::lock_guard<std::mutex> _l(lock);
109     objects.erase(object);
110 }
111 
getObject(egl_object_t * object) const112 bool egl_display_t::getObject(egl_object_t* object) const {
113     std::lock_guard<std::mutex> _l(lock);
114     if (objects.find(object) != objects.end()) {
115         if (object->getDisplay() == this) {
116             object->incRef();
117             return true;
118         }
119     }
120     return false;
121 }
122 
getFromNativeDisplay(EGLNativeDisplayType disp,const EGLAttrib * attrib_list)123 EGLDisplay egl_display_t::getFromNativeDisplay(EGLNativeDisplayType disp,
124                                                const EGLAttrib* attrib_list) {
125     if (uintptr_t(disp) >= NUM_DISPLAYS) return nullptr;
126 
127     return getPlatformDisplay(disp, attrib_list);
128 }
129 
getPlatformDisplayAngle(EGLNativeDisplayType display,egl_connection_t * const cnx,const EGLAttrib * attrib_list,EGLint * error)130 static EGLDisplay getPlatformDisplayAngle(EGLNativeDisplayType display, egl_connection_t* const cnx,
131                                           const EGLAttrib* attrib_list, EGLint* error) {
132     EGLDisplay dpy = EGL_NO_DISPLAY;
133     *error = EGL_NONE;
134 
135     if (cnx->egl.eglGetPlatformDisplay) {
136         std::vector<EGLAttrib> attrs;
137         // These must have the same lifetime as |attrs|, because |attrs| contains pointers to these
138         // variables.
139         std::vector<const char*> enabled;  // ANGLE features to enable
140         std::vector<const char*> disabled; // ANGLE features to disable
141 
142         if (attrib_list) {
143             for (const EGLAttrib* attr = attrib_list; *attr != EGL_NONE; attr += 2) {
144                 attrs.push_back(attr[0]);
145                 attrs.push_back(attr[1]);
146             }
147         }
148 
149         if (graphicsenv_flags::angle_feature_overrides()) {
150             // Get the list of ANGLE features to enable from Global.Settings.
151             const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
152             for (const std::string& eglFeature : eglFeatures) {
153                 enabled.push_back(eglFeature.c_str());
154             }
155 
156             // Get the list of ANGLE features to enable/disable from gpuservice.
157             GraphicsEnv::getInstance().getAngleFeatureOverrides(enabled, disabled);
158             if (!enabled.empty()) {
159                 enabled.push_back(nullptr);
160                 attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
161                 attrs.push_back(reinterpret_cast<EGLAttrib>(enabled.data()));
162             }
163             if (!disabled.empty()) {
164                 disabled.push_back(nullptr);
165                 attrs.push_back(EGL_FEATURE_OVERRIDES_DISABLED_ANGLE);
166                 attrs.push_back(reinterpret_cast<EGLAttrib>(disabled.data()));
167             }
168         } else {
169             const auto& eglFeatures = GraphicsEnv::getInstance().getAngleEglFeatures();
170             if (!eglFeatures.empty()) {
171                 for (const std::string& eglFeature : eglFeatures) {
172                     enabled.push_back(eglFeature.c_str());
173                 }
174                 enabled.push_back(nullptr);
175                 attrs.push_back(EGL_FEATURE_OVERRIDES_ENABLED_ANGLE);
176                 attrs.push_back(reinterpret_cast<EGLAttrib>(enabled.data()));
177             }
178         }
179 
180         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_ANGLE);
181         attrs.push_back(EGL_PLATFORM_ANGLE_TYPE_VULKAN_ANGLE);
182 
183         attrs.push_back(EGL_PLATFORM_ANGLE_DEBUG_LAYERS_ENABLED_ANGLE);
184         attrs.push_back(base::GetBoolProperty("debug.angle.validation", false));
185 
186         attrs.push_back(EGL_NONE);
187 
188         dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANGLE_ANGLE,
189                                              reinterpret_cast<void*>(EGL_DEFAULT_DISPLAY),
190                                              attrs.data());
191         if (dpy == EGL_NO_DISPLAY) {
192             ALOGE("eglGetPlatformDisplay failed!");
193         } else {
194             if (!angle::initializeAnglePlatform(dpy, cnx)) {
195                 ALOGE("initializeAnglePlatform failed!");
196             }
197         }
198     } else {
199         ALOGE("eglGetDisplay(%p) failed: Unable to look up eglGetPlatformDisplay from ANGLE",
200               display);
201     }
202 
203     return dpy;
204 }
205 
getPlatformDisplay(EGLNativeDisplayType display,const EGLAttrib * attrib_list)206 EGLDisplay egl_display_t::getPlatformDisplay(EGLNativeDisplayType display,
207                                              const EGLAttrib* attrib_list) {
208     ATRACE_CALL();
209 
210     // get our driver loader
211     Loader& loader(Loader::getInstance());
212 
213     egl_connection_t* const cnx = &gEGLImpl;
214     if (cnx->dso) {
215         EGLDisplay dpy = EGL_NO_DISPLAY;
216 
217         if (cnx->angleLoaded) {
218             EGLint error;
219             dpy = getPlatformDisplayAngle(display, cnx, attrib_list, &error);
220             if (error != EGL_NONE) {
221                 return setError(error, dpy);
222             }
223         }
224         if (dpy == EGL_NO_DISPLAY) {
225             // NOTE: eglGetPlatformDisplay with a empty attribute list
226             // behaves the same as eglGetDisplay
227             if (cnx->egl.eglGetPlatformDisplay) {
228                 dpy = cnx->egl.eglGetPlatformDisplay(EGL_PLATFORM_ANDROID_KHR, display,
229                                                      attrib_list);
230             }
231 
232             // It is possible that eglGetPlatformDisplay does not have a
233             // working implementation for Android platform; in that case,
234             // one last fallback to eglGetDisplay
235             if (dpy == EGL_NO_DISPLAY) {
236                 if (attrib_list) {
237                     ALOGW("getPlatformDisplay: unexpected attribute list, attributes ignored");
238                 }
239                 dpy = cnx->egl.eglGetDisplay(display);
240             }
241         }
242 
243         if (dpy == EGL_NO_DISPLAY) {
244             loader.close(cnx);
245         } else {
246             const std::lock_guard<std::mutex> lock(displayMapLock);
247             if (displayMap.find(dpy) == displayMap.end()) {
248                 auto d = std::make_unique<egl_display_t>();
249                 d->disp.dpy = dpy;
250                 displayMap[dpy] = std::move(d);
251             }
252             return dpy;
253         }
254     }
255 
256     return nullptr;
257 }
258 
initialize(EGLint * major,EGLint * minor)259 EGLBoolean egl_display_t::initialize(EGLint* major, EGLint* minor) {
260     { // scope for refLock
261         std::unique_lock<std::mutex> _l(refLock);
262         refs++;
263         if (refs > 1) {
264             // We don't know what to report until we know what the
265             // driver supports. Make sure we are initialized before
266             // returning the version info.
267             while (!eglIsInitialized) {
268                 refCond.wait(_l);
269             }
270             egl_connection_t* const cnx = &gEGLImpl;
271 
272             // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
273             // changing the behavior from the past where we always advertise
274             // version 1.4. May need to check that revision is valid
275             // before using cnx->major & cnx->minor
276             if (major != nullptr) *major = cnx->major;
277             if (minor != nullptr) *minor = cnx->minor;
278             return EGL_TRUE;
279         }
280         while (eglIsInitialized) {
281             refCond.wait(_l);
282         }
283     }
284 
285     std::vector<std::string> extensionStrings;
286     { // scope for lock
287         std::lock_guard<std::mutex> _l(lock);
288 
289         setGLHooksThreadSpecific(&gHooksNoContext);
290 
291         // initialize each EGL and
292         // build our own extension string first, based on the extension we know
293         // and the extension supported by our client implementation
294 
295         egl_connection_t* const cnx = &gEGLImpl;
296         cnx->major = -1;
297         cnx->minor = -1;
298         if (cnx->dso) {
299             EGLDisplay idpy = disp.dpy;
300             if (cnx->egl.eglInitialize(idpy, &cnx->major, &cnx->minor)) {
301                 // ALOGD("initialized dpy=%p, ver=%d.%d, cnx=%p",
302                 //        idpy, cnx->major, cnx->minor, cnx);
303 
304                 // display is now initialized
305                 disp.state = egl_display_t::INITIALIZED;
306 
307                 // get the query-strings for this display for each implementation
308                 disp.queryString.vendor = cnx->egl.eglQueryString(idpy, EGL_VENDOR);
309                 disp.queryString.version = cnx->egl.eglQueryString(idpy, EGL_VERSION);
310                 disp.queryString.extensions = cnx->egl.eglQueryString(idpy, EGL_EXTENSIONS);
311                 disp.queryString.clientApi = cnx->egl.eglQueryString(idpy, EGL_CLIENT_APIS);
312 
313             } else {
314                 ALOGW("eglInitialize(%p) failed (%s)", idpy,
315                       egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
316             }
317         }
318 
319         if (cnx->minor == 5) {
320             // full list in egl_entries.in
321             if (!cnx->egl.eglCreateImage || !cnx->egl.eglDestroyImage ||
322                 !cnx->egl.eglGetPlatformDisplay || !cnx->egl.eglCreatePlatformWindowSurface ||
323                 !cnx->egl.eglCreatePlatformPixmapSurface || !cnx->egl.eglCreateSync ||
324                 !cnx->egl.eglDestroySync || !cnx->egl.eglClientWaitSync ||
325                 !cnx->egl.eglGetSyncAttrib || !cnx->egl.eglWaitSync) {
326                 ALOGE("Driver indicates EGL 1.5 support, but does not have "
327                       "a critical API");
328                 cnx->minor = 4;
329             }
330         }
331 
332         // the query strings are per-display
333         mVendorString = sVendorString;
334         mVersionString.clear();
335         cnx->driverVersion = EGL_MAKE_VERSION(1, 4, 0);
336         mVersionString = sVersionString14;
337         if ((cnx->major == 1) && (cnx->minor == 5)) {
338             mVersionString = sVersionString15;
339             cnx->driverVersion = EGL_MAKE_VERSION(1, 5, 0);
340         }
341         if (mVersionString.empty()) {
342             ALOGW("Unexpected driver version: %d.%d, want 1.4 or 1.5", cnx->major, cnx->minor);
343             mVersionString = sVersionString14;
344         }
345         mClientApiString = sClientApiString;
346 
347         // b/269060366 Conditionally enabled EGL_ANDROID_get_frame_timestamps extension if the
348         // device's present timestamps are reliable (which may not be the case on emulators).
349         if (cnx->angleLoaded) {
350             if (android::base::GetBoolProperty("service.sf.present_timestamp", false)) {
351                 extensionStrings.push_back("EGL_ANDROID_get_frame_timestamps");
352             }
353         } else {
354             extensionStrings.push_back("EGL_ANDROID_get_frame_timestamps");
355         }
356 
357         hasColorSpaceSupport = findExtension(disp.queryString.extensions, "EGL_KHR_gl_colorspace");
358 
359         // Note: CDD requires that devices supporting wide color and/or HDR color also support
360         // the EGL_KHR_gl_colorspace extension.
361         bool wideColorBoardConfig = android::sysprop::has_wide_color_display(false);
362 
363         // Add wide-color extensions if device can support wide-color
364         if (wideColorBoardConfig && hasColorSpaceSupport) {
365             std::vector<std::string> wideColorExtensions =
366                     {"EGL_EXT_gl_colorspace_scrgb", "EGL_EXT_gl_colorspace_scrgb_linear",
367                      "EGL_EXT_gl_colorspace_display_p3_linear", "EGL_EXT_gl_colorspace_display_p3",
368                      "EGL_EXT_gl_colorspace_display_p3_passthrough"};
369             extensionStrings.insert(extensionStrings.end(), wideColorExtensions.begin(),
370                                     wideColorExtensions.end());
371         }
372 
373         bool hasHdrBoardConfig = android::sysprop::has_HDR_display(false);
374 
375         if (hasHdrBoardConfig && hasColorSpaceSupport) {
376             // hasHDRBoardConfig indicates the system is capable of supporting HDR content.
377             // Typically that means there is an HDR capable display attached, but could be
378             // support for attaching an HDR display. In either case, advertise support for
379             // HDR color spaces.
380             std::vector<std::string> hdrExtensions = {"EGL_EXT_gl_colorspace_bt2020_hlg",
381                                                       "EGL_EXT_gl_colorspace_bt2020_linear",
382                                                       "EGL_EXT_gl_colorspace_bt2020_pq"};
383             extensionStrings.insert(extensionStrings.end(), hdrExtensions.begin(),
384                                     hdrExtensions.end());
385         }
386 
387         char const* start = gExtensionString;
388         do {
389             // length of the extension name
390             size_t len = strcspn(start, " ");
391             if (len) {
392                 // NOTE: we could avoid the copy if we had strnstr.
393                 const std::string ext(start, len);
394                 if (findExtension(disp.queryString.extensions, ext.c_str(), len)) {
395                     extensionStrings.push_back(ext);
396                 }
397                 // advance to the next extension name, skipping the space.
398                 start += len;
399                 start += (*start == ' ') ? 1 : 0;
400             }
401         } while (*start != '\0');
402 
403         egl_cache_t::get()->initialize(this);
404 
405         finishOnSwap = base::GetBoolProperty("debug.egl.finish", false);
406         traceGpuCompletion = base::GetBoolProperty("debug.egl.traceGpuCompletion", false);
407 
408         // TODO: If device doesn't provide 1.4 or 1.5 then we'll be
409         // changing the behavior from the past where we always advertise
410         // version 1.4. May need to check that revision is valid
411         // before using cnx->major & cnx->minor
412         if (major != nullptr) *major = cnx->major;
413         if (minor != nullptr) *minor = cnx->minor;
414         auto mergeExtensionStrings = [](const std::vector<std::string>& strings) {
415             std::ostringstream combinedStringStream;
416             std::copy(strings.begin(), strings.end(),
417                       std::ostream_iterator<std::string>(combinedStringStream, " "));
418             // gBuiltinExtensionString already has a trailing space so is added here
419             return gBuiltinExtensionString + combinedStringStream.str();
420         };
421         mExtensionString = mergeExtensionStrings(extensionStrings);
422     }
423 
424     { // scope for refLock
425         std::unique_lock<std::mutex> _l(refLock);
426         eglIsInitialized = true;
427         refCond.notify_all();
428     }
429 
430     return EGL_TRUE;
431 }
432 
terminate()433 EGLBoolean egl_display_t::terminate() {
434     { // scope for refLock
435         std::unique_lock<std::mutex> _rl(refLock);
436         if (refs == 0) {
437             /*
438              * From the EGL spec (3.2):
439              * "Termination of a display that has already been terminated,
440              *  (...), is allowed, but the only effect of such a call is
441              *  to return EGL_TRUE (...)
442              */
443             return EGL_TRUE;
444         }
445 
446         // this is specific to Android, display termination is ref-counted.
447         refs--;
448         if (refs > 0) {
449             return EGL_TRUE;
450         }
451     }
452 
453     EGLBoolean res = EGL_FALSE;
454 
455     { // scope for lock
456         std::lock_guard<std::mutex> _l(lock);
457 
458         egl_connection_t* const cnx = &gEGLImpl;
459         if (cnx->dso && disp.state == egl_display_t::INITIALIZED) {
460             // If we're using ANGLE reset any custom DisplayPlatform
461             if (cnx->angleLoaded) {
462                 angle::resetAnglePlatform(disp.dpy, cnx);
463             }
464             if (cnx->egl.eglTerminate(disp.dpy) == EGL_FALSE) {
465                 ALOGW("eglTerminate(%p) failed (%s)", disp.dpy,
466                       egl_tls_t::egl_strerror(cnx->egl.eglGetError()));
467             }
468             // REVISIT: it's unclear what to do if eglTerminate() fails
469             disp.state = egl_display_t::TERMINATED;
470             res = EGL_TRUE;
471         }
472 
473         // Reset the extension string since it will be regenerated if we get
474         // reinitialized.
475         mExtensionString.clear();
476 
477         // Mark all objects remaining in the list as terminated, unless
478         // there are no reference to them, it which case, we're free to
479         // delete them.
480         size_t count = objects.size();
481         ALOGW_IF(count, "eglTerminate() called w/ %zu objects remaining", count);
482         for (auto o : objects) {
483             o->destroy();
484         }
485 
486         // this marks all object handles are "terminated"
487         objects.clear();
488     }
489 
490     { // scope for refLock
491         std::unique_lock<std::mutex> _rl(refLock);
492         eglIsInitialized = false;
493         refCond.notify_all();
494     }
495 
496     return res;
497 }
498 
loseCurrent(egl_context_t * cur_c)499 void egl_display_t::loseCurrent(egl_context_t* cur_c) {
500     if (cur_c) {
501         egl_display_t* display = cur_c->getDisplay();
502         if (display) {
503             display->loseCurrentImpl(cur_c);
504         }
505     }
506 }
507 
loseCurrentImpl(egl_context_t * cur_c)508 void egl_display_t::loseCurrentImpl(egl_context_t* cur_c) {
509     // by construction, these are either 0 or valid (possibly terminated)
510     // it should be impossible for these to be invalid
511     ContextRef _cur_c(cur_c);
512     SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
513     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
514 
515     { // scope for the lock
516         std::lock_guard<std::mutex> _l(lock);
517         cur_c->onLooseCurrent();
518     }
519 
520     // This cannot be called with the lock held because it might end-up
521     // calling back into EGL (in particular when a surface is destroyed
522     // it calls ANativeWindow::disconnect
523     _cur_c.release();
524     _cur_r.release();
525     _cur_d.release();
526 }
527 
makeCurrent(egl_context_t * c,egl_context_t * cur_c,EGLSurface draw,EGLSurface read,EGLContext,EGLSurface impl_draw,EGLSurface impl_read,EGLContext impl_ctx)528 EGLBoolean egl_display_t::makeCurrent(egl_context_t* c, egl_context_t* cur_c, EGLSurface draw,
529                                       EGLSurface read, EGLContext /*ctx*/, EGLSurface impl_draw,
530                                       EGLSurface impl_read, EGLContext impl_ctx) {
531     EGLBoolean result;
532 
533     // by construction, these are either 0 or valid (possibly terminated)
534     // it should be impossible for these to be invalid
535     ContextRef _cur_c(cur_c);
536     SurfaceRef _cur_r(cur_c ? get_surface(cur_c->read) : nullptr);
537     SurfaceRef _cur_d(cur_c ? get_surface(cur_c->draw) : nullptr);
538 
539     { // scope for the lock
540         std::lock_guard<std::mutex> _l(lock);
541         if (c) {
542             result = c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
543             if (result == EGL_TRUE) {
544                 c->onMakeCurrent(draw, read);
545             }
546         } else {
547             result = cur_c->cnx->egl.eglMakeCurrent(disp.dpy, impl_draw, impl_read, impl_ctx);
548             if (result == EGL_TRUE) {
549                 cur_c->onLooseCurrent();
550             }
551         }
552     }
553 
554     if (result == EGL_TRUE) {
555         // This cannot be called with the lock held because it might end-up
556         // calling back into EGL (in particular when a surface is destroyed
557         // it calls ANativeWindow::disconnect
558         _cur_c.release();
559         _cur_r.release();
560         _cur_d.release();
561     }
562 
563     return result;
564 }
565 
haveExtension(const char * name,size_t nameLen) const566 bool egl_display_t::haveExtension(const char* name, size_t nameLen) const {
567     if (!nameLen) {
568         nameLen = strlen(name);
569     }
570     return findExtension(mExtensionString.c_str(), name, nameLen);
571 }
572 
validate_display(EGLDisplay dpy)573 egl_display_t* validate_display(EGLDisplay dpy) {
574     egl_display_t* const dp = get_display(dpy);
575     if (!dp) return setError(EGL_BAD_DISPLAY, (egl_display_t*)nullptr);
576     if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, (egl_display_t*)nullptr);
577 
578     return dp;
579 }
580 
validate_display_connection(EGLDisplay dpy,egl_connection_t ** outCnx)581 egl_display_t* validate_display_connection(EGLDisplay dpy, egl_connection_t** outCnx) {
582     *outCnx = nullptr;
583     egl_display_t* dp = validate_display(dpy);
584     if (!dp) return dp;
585     *outCnx = &gEGLImpl;
586     if ((*outCnx)->dso == nullptr) {
587         return setError(EGL_BAD_CONFIG, (egl_display_t*)nullptr);
588     }
589     return dp;
590 }
591 
592 }; // namespace android
593