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