eglApi.cpp revision 7284145d564fa8a422a8e564a38c730fb4a2962b
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 <ctype.h>
20#include <stdlib.h>
21#include <string.h>
22
23#include <hardware/gralloc.h>
24#include <system/window.h>
25
26#include <EGL/egl.h>
27#include <EGL/eglext.h>
28
29#include <cutils/log.h>
30#include <cutils/atomic.h>
31#include <cutils/compiler.h>
32#include <cutils/properties.h>
33#include <cutils/memory.h>
34
35#include <utils/KeyedVector.h>
36#include <utils/SortedVector.h>
37#include <utils/String8.h>
38#include <utils/Trace.h>
39
40#include "egl_impl.h"
41#include "egl_tls.h"
42#include "glestrace.h"
43#include "hooks.h"
44
45#include "egl_display.h"
46#include "egl_impl.h"
47#include "egl_object.h"
48#include "egl_tls.h"
49#include "egldefs.h"
50
51using namespace android;
52
53// ----------------------------------------------------------------------------
54
55#define EGL_VERSION_HW_ANDROID  0x3143
56
57struct extention_map_t {
58    const char* name;
59    __eglMustCastToProperFunctionPointerType address;
60};
61
62static const extention_map_t sExtentionMap[] = {
63    { "eglLockSurfaceKHR",
64            (__eglMustCastToProperFunctionPointerType)&eglLockSurfaceKHR },
65    { "eglUnlockSurfaceKHR",
66            (__eglMustCastToProperFunctionPointerType)&eglUnlockSurfaceKHR },
67    { "eglCreateImageKHR",
68            (__eglMustCastToProperFunctionPointerType)&eglCreateImageKHR },
69    { "eglDestroyImageKHR",
70            (__eglMustCastToProperFunctionPointerType)&eglDestroyImageKHR },
71    { "eglGetSystemTimeFrequencyNV",
72            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeFrequencyNV },
73    { "eglGetSystemTimeNV",
74            (__eglMustCastToProperFunctionPointerType)&eglGetSystemTimeNV },
75};
76
77// accesses protected by sExtensionMapMutex
78static DefaultKeyedVector<String8, __eglMustCastToProperFunctionPointerType> sGLExtentionMap;
79static int sGLExtentionSlot = 0;
80static pthread_mutex_t sExtensionMapMutex = PTHREAD_MUTEX_INITIALIZER;
81
82static void(*findProcAddress(const char* name,
83        const extention_map_t* map, size_t n))() {
84    for (uint32_t i=0 ; i<n ; i++) {
85        if (!strcmp(name, map[i].name)) {
86            return map[i].address;
87        }
88    }
89    return NULL;
90}
91
92// ----------------------------------------------------------------------------
93
94namespace android {
95extern void setGLHooksThreadSpecific(gl_hooks_t const *value);
96extern EGLBoolean egl_init_drivers();
97extern const __eglMustCastToProperFunctionPointerType gExtensionForwarders[MAX_NUMBER_OF_GL_EXTENSIONS];
98extern int getEGLDebugLevel();
99extern void setEGLDebugLevel(int level);
100extern gl_hooks_t gHooksTrace;
101} // namespace android;
102
103// ----------------------------------------------------------------------------
104
105static inline void clearError() { egl_tls_t::clearError(); }
106static inline EGLContext getContext() { return egl_tls_t::getContext(); }
107
108// ----------------------------------------------------------------------------
109
110EGLDisplay eglGetDisplay(EGLNativeDisplayType display)
111{
112    clearError();
113
114    uint32_t index = uint32_t(display);
115    if (index >= NUM_DISPLAYS) {
116        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
117    }
118
119    if (egl_init_drivers() == EGL_FALSE) {
120        return setError(EGL_BAD_PARAMETER, EGL_NO_DISPLAY);
121    }
122
123    EGLDisplay dpy = egl_display_t::getFromNativeDisplay(display);
124    return dpy;
125}
126
127// ----------------------------------------------------------------------------
128// Initialization
129// ----------------------------------------------------------------------------
130
131EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor)
132{
133    clearError();
134
135    egl_display_ptr dp = get_display(dpy);
136    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
137
138    EGLBoolean res = dp->initialize(major, minor);
139
140    return res;
141}
142
143EGLBoolean eglTerminate(EGLDisplay dpy)
144{
145    // NOTE: don't unload the drivers b/c some APIs can be called
146    // after eglTerminate() has been called. eglTerminate() only
147    // terminates an EGLDisplay, not a EGL itself.
148
149    clearError();
150
151    egl_display_ptr dp = get_display(dpy);
152    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
153
154    EGLBoolean res = dp->terminate();
155
156    return res;
157}
158
159// ----------------------------------------------------------------------------
160// configuration
161// ----------------------------------------------------------------------------
162
163EGLBoolean eglGetConfigs(   EGLDisplay dpy,
164                            EGLConfig *configs,
165                            EGLint config_size, EGLint *num_config)
166{
167    clearError();
168
169    const egl_display_ptr dp = validate_display(dpy);
170    if (!dp) return EGL_FALSE;
171
172    if (num_config==0) {
173        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
174    }
175
176    EGLBoolean res = EGL_FALSE;
177    *num_config = 0;
178
179    egl_connection_t* const cnx = &gEGLImpl;
180    if (cnx->dso) {
181        res = cnx->egl.eglGetConfigs(
182                dp->disp.dpy, configs, config_size, num_config);
183    }
184
185    return res;
186}
187
188EGLBoolean eglChooseConfig( EGLDisplay dpy, const EGLint *attrib_list,
189                            EGLConfig *configs, EGLint config_size,
190                            EGLint *num_config)
191{
192    clearError();
193
194    const egl_display_ptr dp = validate_display(dpy);
195    if (!dp) return EGL_FALSE;
196
197    if (num_config==0) {
198        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
199    }
200
201    EGLBoolean res = EGL_FALSE;
202    *num_config = 0;
203
204    egl_connection_t* const cnx = &gEGLImpl;
205    if (cnx->dso) {
206        if (attrib_list) {
207            char value[PROPERTY_VALUE_MAX];
208            property_get("debug.egl.force_msaa", value, "false");
209
210            if (!strcmp(value, "true")) {
211                size_t attribCount = 0;
212                EGLint attrib = attrib_list[0];
213
214                // Only enable MSAA if the context is OpenGL ES 2.0 and
215                // if no caveat is requested
216                const EGLint *attribRendererable = NULL;
217                const EGLint *attribCaveat = NULL;
218
219                // Count the number of attributes and look for
220                // EGL_RENDERABLE_TYPE and EGL_CONFIG_CAVEAT
221                while (attrib != EGL_NONE) {
222                    attrib = attrib_list[attribCount];
223                    switch (attrib) {
224                        case EGL_RENDERABLE_TYPE:
225                            attribRendererable = &attrib_list[attribCount];
226                            break;
227                        case EGL_CONFIG_CAVEAT:
228                            attribCaveat = &attrib_list[attribCount];
229                            break;
230                    }
231                    attribCount++;
232                }
233
234                if (attribRendererable && attribRendererable[1] == EGL_OPENGL_ES2_BIT &&
235                        (!attribCaveat || attribCaveat[1] != EGL_NONE)) {
236
237                    // Insert 2 extra attributes to force-enable MSAA 4x
238                    EGLint aaAttribs[attribCount + 4];
239                    aaAttribs[0] = EGL_SAMPLE_BUFFERS;
240                    aaAttribs[1] = 1;
241                    aaAttribs[2] = EGL_SAMPLES;
242                    aaAttribs[3] = 4;
243
244                    memcpy(&aaAttribs[4], attrib_list, attribCount * sizeof(EGLint));
245
246                    EGLint numConfigAA;
247                    EGLBoolean resAA = cnx->egl.eglChooseConfig(
248                            dp->disp.dpy, aaAttribs, configs, config_size, &numConfigAA);
249
250                    if (resAA == EGL_TRUE && numConfigAA > 0) {
251                        ALOGD("Enabling MSAA 4x");
252                        *num_config = numConfigAA;
253                        return resAA;
254                    }
255                }
256            }
257        }
258
259        res = cnx->egl.eglChooseConfig(
260                dp->disp.dpy, attrib_list, configs, config_size, num_config);
261    }
262    return res;
263}
264
265EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config,
266        EGLint attribute, EGLint *value)
267{
268    clearError();
269
270    egl_connection_t* cnx = NULL;
271    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
272    if (!dp) return EGL_FALSE;
273
274    return cnx->egl.eglGetConfigAttrib(
275            dp->disp.dpy, config, attribute, value);
276}
277
278// ----------------------------------------------------------------------------
279// surfaces
280// ----------------------------------------------------------------------------
281
282EGLSurface eglCreateWindowSurface(  EGLDisplay dpy, EGLConfig config,
283                                    NativeWindowType window,
284                                    const EGLint *attrib_list)
285{
286    clearError();
287
288    egl_connection_t* cnx = NULL;
289    egl_display_ptr dp = validate_display_connection(dpy, cnx);
290    if (dp) {
291        EGLDisplay iDpy = dp->disp.dpy;
292        EGLint format;
293
294        if (native_window_api_connect(window, NATIVE_WINDOW_API_EGL) != OK) {
295            ALOGE("EGLNativeWindowType %p already connected to another API",
296                    window);
297            return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
298        }
299
300        // set the native window's buffers format to match this config
301        if (cnx->egl.eglGetConfigAttrib(iDpy,
302                config, EGL_NATIVE_VISUAL_ID, &format)) {
303            if (format != 0) {
304                int err = native_window_set_buffers_format(window, format);
305                if (err != 0) {
306                    ALOGE("error setting native window pixel format: %s (%d)",
307                            strerror(-err), err);
308                    native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
309                    return setError(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE);
310                }
311            }
312        }
313
314        // the EGL spec requires that a new EGLSurface default to swap interval
315        // 1, so explicitly set that on the window here.
316        ANativeWindow* anw = reinterpret_cast<ANativeWindow*>(window);
317        anw->setSwapInterval(anw, 1);
318
319        EGLSurface surface = cnx->egl.eglCreateWindowSurface(
320                iDpy, config, window, attrib_list);
321        if (surface != EGL_NO_SURFACE) {
322            egl_surface_t* s = new egl_surface_t(dp.get(), config, window,
323                    surface, cnx);
324            return s;
325        }
326
327        // EGLSurface creation failed
328        native_window_set_buffers_format(window, 0);
329        native_window_api_disconnect(window, NATIVE_WINDOW_API_EGL);
330    }
331    return EGL_NO_SURFACE;
332}
333
334EGLSurface eglCreatePixmapSurface(  EGLDisplay dpy, EGLConfig config,
335                                    NativePixmapType pixmap,
336                                    const EGLint *attrib_list)
337{
338    clearError();
339
340    egl_connection_t* cnx = NULL;
341    egl_display_ptr dp = validate_display_connection(dpy, cnx);
342    if (dp) {
343        EGLSurface surface = cnx->egl.eglCreatePixmapSurface(
344                dp->disp.dpy, config, pixmap, attrib_list);
345        if (surface != EGL_NO_SURFACE) {
346            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
347                    surface, cnx);
348            return s;
349        }
350    }
351    return EGL_NO_SURFACE;
352}
353
354EGLSurface eglCreatePbufferSurface( EGLDisplay dpy, EGLConfig config,
355                                    const EGLint *attrib_list)
356{
357    clearError();
358
359    egl_connection_t* cnx = NULL;
360    egl_display_ptr dp = validate_display_connection(dpy, cnx);
361    if (dp) {
362        EGLSurface surface = cnx->egl.eglCreatePbufferSurface(
363                dp->disp.dpy, config, attrib_list);
364        if (surface != EGL_NO_SURFACE) {
365            egl_surface_t* s = new egl_surface_t(dp.get(), config, NULL,
366                    surface, cnx);
367            return s;
368        }
369    }
370    return EGL_NO_SURFACE;
371}
372
373EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface surface)
374{
375    clearError();
376
377    const egl_display_ptr dp = validate_display(dpy);
378    if (!dp) return EGL_FALSE;
379
380    SurfaceRef _s(dp.get(), surface);
381    if (!_s.get())
382        return setError(EGL_BAD_SURFACE, EGL_FALSE);
383
384    egl_surface_t * const s = get_surface(surface);
385    EGLBoolean result = s->cnx->egl.eglDestroySurface(dp->disp.dpy, s->surface);
386    if (result == EGL_TRUE) {
387        _s.terminate();
388    }
389    return result;
390}
391
392EGLBoolean eglQuerySurface( EGLDisplay dpy, EGLSurface surface,
393                            EGLint attribute, EGLint *value)
394{
395    clearError();
396
397    const egl_display_ptr dp = validate_display(dpy);
398    if (!dp) return EGL_FALSE;
399
400    SurfaceRef _s(dp.get(), surface);
401    if (!_s.get())
402        return setError(EGL_BAD_SURFACE, EGL_FALSE);
403
404    egl_surface_t const * const s = get_surface(surface);
405    return s->cnx->egl.eglQuerySurface(
406            dp->disp.dpy, s->surface, attribute, value);
407}
408
409void EGLAPI eglBeginFrame(EGLDisplay dpy, EGLSurface surface) {
410    ATRACE_CALL();
411    clearError();
412
413    const egl_display_ptr dp = validate_display(dpy);
414    if (!dp) {
415        return;
416    }
417
418    SurfaceRef _s(dp.get(), surface);
419    if (!_s.get()) {
420        setError(EGL_BAD_SURFACE, EGL_FALSE);
421        return;
422    }
423
424    int64_t timestamp = systemTime(SYSTEM_TIME_MONOTONIC);
425
426    egl_surface_t const * const s = get_surface(surface);
427    native_window_set_buffers_timestamp(s->win.get(), timestamp);
428}
429
430// ----------------------------------------------------------------------------
431// Contexts
432// ----------------------------------------------------------------------------
433
434EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config,
435                            EGLContext share_list, const EGLint *attrib_list)
436{
437    clearError();
438
439    egl_connection_t* cnx = NULL;
440    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
441    if (dpy) {
442        if (share_list != EGL_NO_CONTEXT) {
443            egl_context_t* const c = get_context(share_list);
444            share_list = c->context;
445        }
446        EGLContext context = cnx->egl.eglCreateContext(
447                dp->disp.dpy, config, share_list, attrib_list);
448        if (context != EGL_NO_CONTEXT) {
449            // figure out if it's a GLESv1 or GLESv2
450            int version = 0;
451            if (attrib_list) {
452                while (*attrib_list != EGL_NONE) {
453                    GLint attr = *attrib_list++;
454                    GLint value = *attrib_list++;
455                    if (attr == EGL_CONTEXT_CLIENT_VERSION) {
456                        if (value == 1) {
457                            version = egl_connection_t::GLESv1_INDEX;
458                        } else if (value == 2 || value == 3) {
459                            version = egl_connection_t::GLESv2_INDEX;
460                        }
461                    }
462                };
463            }
464            egl_context_t* c = new egl_context_t(dpy, context, config, cnx,
465                    version);
466#if EGL_TRACE
467            if (getEGLDebugLevel() > 0)
468                GLTrace_eglCreateContext(version, c);
469#endif
470            return c;
471        } else {
472            EGLint error = eglGetError();
473            ALOGE_IF(error == EGL_SUCCESS,
474                    "eglCreateContext(%p, %p, %p, %p) returned EGL_NO_CONTEXT "
475                    "but no EGL error!",
476                    dpy, config, share_list, attrib_list);
477        }
478    }
479    return EGL_NO_CONTEXT;
480}
481
482EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx)
483{
484    clearError();
485
486    const egl_display_ptr dp = validate_display(dpy);
487    if (!dp)
488        return EGL_FALSE;
489
490    ContextRef _c(dp.get(), ctx);
491    if (!_c.get())
492        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
493
494    egl_context_t * const c = get_context(ctx);
495    EGLBoolean result = c->cnx->egl.eglDestroyContext(dp->disp.dpy, c->context);
496    if (result == EGL_TRUE) {
497        _c.terminate();
498    }
499    return result;
500}
501
502EGLBoolean eglMakeCurrent(  EGLDisplay dpy, EGLSurface draw,
503                            EGLSurface read, EGLContext ctx)
504{
505    clearError();
506
507    egl_display_ptr dp = validate_display(dpy);
508    if (!dp) return setError(EGL_BAD_DISPLAY, EGL_FALSE);
509
510    // If ctx is not EGL_NO_CONTEXT, read is not EGL_NO_SURFACE, or draw is not
511    // EGL_NO_SURFACE, then an EGL_NOT_INITIALIZED error is generated if dpy is
512    // a valid but uninitialized display.
513    if ( (ctx != EGL_NO_CONTEXT) || (read != EGL_NO_SURFACE) ||
514         (draw != EGL_NO_SURFACE) ) {
515        if (!dp->isReady()) return setError(EGL_NOT_INITIALIZED, EGL_FALSE);
516    }
517
518    // get a reference to the object passed in
519    ContextRef _c(dp.get(), ctx);
520    SurfaceRef _d(dp.get(), draw);
521    SurfaceRef _r(dp.get(), read);
522
523    // validate the context (if not EGL_NO_CONTEXT)
524    if ((ctx != EGL_NO_CONTEXT) && !_c.get()) {
525        // EGL_NO_CONTEXT is valid
526        return EGL_FALSE;
527    }
528
529    // these are the underlying implementation's object
530    EGLContext impl_ctx  = EGL_NO_CONTEXT;
531    EGLSurface impl_draw = EGL_NO_SURFACE;
532    EGLSurface impl_read = EGL_NO_SURFACE;
533
534    // these are our objects structs passed in
535    egl_context_t       * c = NULL;
536    egl_surface_t const * d = NULL;
537    egl_surface_t const * r = NULL;
538
539    // these are the current objects structs
540    egl_context_t * cur_c = get_context(getContext());
541
542    if (ctx != EGL_NO_CONTEXT) {
543        c = get_context(ctx);
544        impl_ctx = c->context;
545    } else {
546        // no context given, use the implementation of the current context
547        if (cur_c == NULL) {
548            // no current context
549            if (draw != EGL_NO_SURFACE || read != EGL_NO_SURFACE) {
550                // calling eglMakeCurrent( ..., !=0, !=0, EGL_NO_CONTEXT);
551                return setError(EGL_BAD_MATCH, EGL_FALSE);
552            }
553            // not an error, there is just no current context.
554            return EGL_TRUE;
555        }
556    }
557
558    // retrieve the underlying implementation's draw EGLSurface
559    if (draw != EGL_NO_SURFACE) {
560        d = get_surface(draw);
561        impl_draw = d->surface;
562    }
563
564    // retrieve the underlying implementation's read EGLSurface
565    if (read != EGL_NO_SURFACE) {
566        r = get_surface(read);
567        impl_read = r->surface;
568    }
569
570
571    EGLBoolean result = dp->makeCurrent(c, cur_c,
572            draw, read, ctx,
573            impl_draw, impl_read, impl_ctx);
574
575    if (result == EGL_TRUE) {
576        if (c) {
577            setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
578            egl_tls_t::setContext(ctx);
579#if EGL_TRACE
580            if (getEGLDebugLevel() > 0)
581                GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
582#endif
583            _c.acquire();
584            _r.acquire();
585            _d.acquire();
586        } else {
587            setGLHooksThreadSpecific(&gHooksNoContext);
588            egl_tls_t::setContext(EGL_NO_CONTEXT);
589        }
590    } else {
591        // this will ALOGE the error
592        result = setError(c->cnx->egl.eglGetError(), EGL_FALSE);
593    }
594    return result;
595}
596
597
598EGLBoolean eglQueryContext( EGLDisplay dpy, EGLContext ctx,
599                            EGLint attribute, EGLint *value)
600{
601    clearError();
602
603    const egl_display_ptr dp = validate_display(dpy);
604    if (!dp) return EGL_FALSE;
605
606    ContextRef _c(dp.get(), ctx);
607    if (!_c.get()) return setError(EGL_BAD_CONTEXT, EGL_FALSE);
608
609    egl_context_t * const c = get_context(ctx);
610    return c->cnx->egl.eglQueryContext(
611            dp->disp.dpy, c->context, attribute, value);
612
613}
614
615EGLContext eglGetCurrentContext(void)
616{
617    // could be called before eglInitialize(), but we wouldn't have a context
618    // then, and this function would correctly return EGL_NO_CONTEXT.
619
620    clearError();
621
622    EGLContext ctx = getContext();
623    return ctx;
624}
625
626EGLSurface eglGetCurrentSurface(EGLint readdraw)
627{
628    // could be called before eglInitialize(), but we wouldn't have a context
629    // then, and this function would correctly return EGL_NO_SURFACE.
630
631    clearError();
632
633    EGLContext ctx = getContext();
634    if (ctx) {
635        egl_context_t const * const c = get_context(ctx);
636        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
637        switch (readdraw) {
638            case EGL_READ: return c->read;
639            case EGL_DRAW: return c->draw;
640            default: return setError(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
641        }
642    }
643    return EGL_NO_SURFACE;
644}
645
646EGLDisplay eglGetCurrentDisplay(void)
647{
648    // could be called before eglInitialize(), but we wouldn't have a context
649    // then, and this function would correctly return EGL_NO_DISPLAY.
650
651    clearError();
652
653    EGLContext ctx = getContext();
654    if (ctx) {
655        egl_context_t const * const c = get_context(ctx);
656        if (!c) return setError(EGL_BAD_CONTEXT, EGL_NO_SURFACE);
657        return c->dpy;
658    }
659    return EGL_NO_DISPLAY;
660}
661
662EGLBoolean eglWaitGL(void)
663{
664    clearError();
665
666    egl_connection_t* const cnx = &gEGLImpl;
667    if (!cnx->dso)
668        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
669
670    return cnx->egl.eglWaitGL();
671}
672
673EGLBoolean eglWaitNative(EGLint engine)
674{
675    clearError();
676
677    egl_connection_t* const cnx = &gEGLImpl;
678    if (!cnx->dso)
679        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
680
681    return cnx->egl.eglWaitNative(engine);
682}
683
684EGLint eglGetError(void)
685{
686    EGLint err = EGL_SUCCESS;
687    egl_connection_t* const cnx = &gEGLImpl;
688    if (cnx->dso) {
689        err = cnx->egl.eglGetError();
690    }
691    if (err == EGL_SUCCESS) {
692        err = egl_tls_t::getError();
693    }
694    return err;
695}
696
697__eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname)
698{
699    // eglGetProcAddress() could be the very first function called
700    // in which case we must make sure we've initialized ourselves, this
701    // happens the first time egl_get_display() is called.
702
703    clearError();
704
705    if (egl_init_drivers() == EGL_FALSE) {
706        setError(EGL_BAD_PARAMETER, NULL);
707        return  NULL;
708    }
709
710    // These extensions should not be exposed to applications. They're used
711    // internally by the Android EGL layer.
712    if (!strcmp(procname, "eglSetBlobCacheFuncsANDROID") ||
713        !strcmp(procname, "eglDupNativeFenceFDANDROID") ||
714        !strcmp(procname, "eglWaitSyncANDROID") ||
715        !strcmp(procname, "eglHibernateProcessIMG") ||
716        !strcmp(procname, "eglAwakenProcessIMG")) {
717        return NULL;
718    }
719
720    __eglMustCastToProperFunctionPointerType addr;
721    addr = findProcAddress(procname, sExtentionMap, NELEM(sExtentionMap));
722    if (addr) return addr;
723
724
725    // this protects accesses to sGLExtentionMap and sGLExtentionSlot
726    pthread_mutex_lock(&sExtensionMapMutex);
727
728        /*
729         * Since eglGetProcAddress() is not associated to anything, it needs
730         * to return a function pointer that "works" regardless of what
731         * the current context is.
732         *
733         * For this reason, we return a "forwarder", a small stub that takes
734         * care of calling the function associated with the context
735         * currently bound.
736         *
737         * We first look for extensions we've already resolved, if we're seeing
738         * this extension for the first time, we go through all our
739         * implementations and call eglGetProcAddress() and record the
740         * result in the appropriate implementation hooks and return the
741         * address of the forwarder corresponding to that hook set.
742         *
743         */
744
745        const String8 name(procname);
746        addr = sGLExtentionMap.valueFor(name);
747        const int slot = sGLExtentionSlot;
748
749        ALOGE_IF(slot >= MAX_NUMBER_OF_GL_EXTENSIONS,
750                "no more slots for eglGetProcAddress(\"%s\")",
751                procname);
752
753#if EGL_TRACE
754        gl_hooks_t *debugHooks = GLTrace_getGLHooks();
755#endif
756
757        if (!addr && (slot < MAX_NUMBER_OF_GL_EXTENSIONS)) {
758            bool found = false;
759
760            egl_connection_t* const cnx = &gEGLImpl;
761            if (cnx->dso && cnx->egl.eglGetProcAddress) {
762                // Extensions are independent of the bound context
763                addr =
764                cnx->hooks[egl_connection_t::GLESv1_INDEX]->ext.extensions[slot] =
765                cnx->hooks[egl_connection_t::GLESv2_INDEX]->ext.extensions[slot] =
766#if EGL_TRACE
767                debugHooks->ext.extensions[slot] =
768                gHooksTrace.ext.extensions[slot] =
769#endif
770                        cnx->egl.eglGetProcAddress(procname);
771                if (addr) found = true;
772            }
773
774            if (found) {
775#if USE_FAST_TLS_KEY
776                addr = gExtensionForwarders[slot];
777#endif
778                sGLExtentionMap.add(name, addr);
779                sGLExtentionSlot++;
780            }
781        }
782
783    pthread_mutex_unlock(&sExtensionMapMutex);
784    return addr;
785}
786
787class FrameCompletionThread : public Thread {
788public:
789
790    static void queueSync(EGLSyncKHR sync) {
791        static sp<FrameCompletionThread> thread(new FrameCompletionThread);
792        static bool running = false;
793        if (!running) {
794            thread->run("GPUFrameCompletion");
795            running = true;
796        }
797        {
798            Mutex::Autolock lock(thread->mMutex);
799            ScopedTrace st(ATRACE_TAG, String8::format("kicked off frame %d",
800                    thread->mFramesQueued).string());
801            thread->mQueue.push_back(sync);
802            thread->mCondition.signal();
803            thread->mFramesQueued++;
804            ATRACE_INT("GPU Frames Outstanding", thread->mQueue.size());
805        }
806    }
807
808private:
809    FrameCompletionThread() : mFramesQueued(0), mFramesCompleted(0) {}
810
811    virtual bool threadLoop() {
812        EGLSyncKHR sync;
813        uint32_t frameNum;
814        {
815            Mutex::Autolock lock(mMutex);
816            while (mQueue.isEmpty()) {
817                mCondition.wait(mMutex);
818            }
819            sync = mQueue[0];
820            frameNum = mFramesCompleted;
821        }
822        EGLDisplay dpy = eglGetDisplay(EGL_DEFAULT_DISPLAY);
823        {
824            ScopedTrace st(ATRACE_TAG, String8::format("waiting for frame %d",
825                    frameNum).string());
826            EGLint result = eglClientWaitSyncKHR(dpy, sync, 0, EGL_FOREVER_KHR);
827            if (result == EGL_FALSE) {
828                ALOGE("FrameCompletion: error waiting for fence: %#x", eglGetError());
829            } else if (result == EGL_TIMEOUT_EXPIRED_KHR) {
830                ALOGE("FrameCompletion: timeout waiting for fence");
831            }
832            eglDestroySyncKHR(dpy, sync);
833        }
834        {
835            Mutex::Autolock lock(mMutex);
836            mQueue.removeAt(0);
837            mFramesCompleted++;
838            ATRACE_INT("GPU Frames Outstanding", mQueue.size());
839        }
840        return true;
841    }
842
843    uint32_t mFramesQueued;
844    uint32_t mFramesCompleted;
845    Vector<EGLSyncKHR> mQueue;
846    Condition mCondition;
847    Mutex mMutex;
848};
849
850EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface draw)
851{
852    ATRACE_CALL();
853    clearError();
854
855    const egl_display_ptr dp = validate_display(dpy);
856    if (!dp) return EGL_FALSE;
857
858    SurfaceRef _s(dp.get(), draw);
859    if (!_s.get())
860        return setError(EGL_BAD_SURFACE, EGL_FALSE);
861
862#if EGL_TRACE
863    gl_hooks_t const *trace_hooks = getGLTraceThreadSpecific();
864    if (getEGLDebugLevel() > 0) {
865        if (trace_hooks == NULL) {
866            if (GLTrace_start() < 0) {
867                ALOGE("Disabling Tracer for OpenGL ES");
868                setEGLDebugLevel(0);
869            } else {
870                // switch over to the trace version of hooks
871                EGLContext ctx = egl_tls_t::getContext();
872                egl_context_t * const c = get_context(ctx);
873                if (c) {
874                    setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
875                    GLTrace_eglMakeCurrent(c->version, c->cnx->hooks[c->version], ctx);
876                }
877            }
878        }
879
880        GLTrace_eglSwapBuffers(dpy, draw);
881    } else if (trace_hooks != NULL) {
882        // tracing is now disabled, so switch back to the non trace version
883        EGLContext ctx = egl_tls_t::getContext();
884        egl_context_t * const c = get_context(ctx);
885        if (c) setGLHooksThreadSpecific(c->cnx->hooks[c->version]);
886        GLTrace_stop();
887    }
888#endif
889
890    egl_surface_t const * const s = get_surface(draw);
891
892    if (CC_UNLIKELY(dp->finishOnSwap)) {
893        uint32_t pixel;
894        egl_context_t * const c = get_context( egl_tls_t::getContext() );
895        if (c) {
896            // glReadPixels() ensures that the frame is complete
897            s->cnx->hooks[c->version]->gl.glReadPixels(0,0,1,1,
898                    GL_RGBA,GL_UNSIGNED_BYTE,&pixel);
899        }
900    }
901
902    EGLBoolean result = s->cnx->egl.eglSwapBuffers(dp->disp.dpy, s->surface);
903
904    if (CC_UNLIKELY(dp->traceGpuCompletion)) {
905        EGLSyncKHR sync = EGL_NO_SYNC_KHR;
906        {
907            sync = eglCreateSyncKHR(dpy, EGL_SYNC_FENCE_KHR, NULL);
908        }
909        if (sync != EGL_NO_SYNC_KHR) {
910            FrameCompletionThread::queueSync(sync);
911        }
912    }
913
914    return result;
915}
916
917EGLBoolean eglCopyBuffers(  EGLDisplay dpy, EGLSurface surface,
918                            NativePixmapType target)
919{
920    clearError();
921
922    const egl_display_ptr dp = validate_display(dpy);
923    if (!dp) return EGL_FALSE;
924
925    SurfaceRef _s(dp.get(), surface);
926    if (!_s.get())
927        return setError(EGL_BAD_SURFACE, EGL_FALSE);
928
929    egl_surface_t const * const s = get_surface(surface);
930    return s->cnx->egl.eglCopyBuffers(dp->disp.dpy, s->surface, target);
931}
932
933const char* eglQueryString(EGLDisplay dpy, EGLint name)
934{
935    clearError();
936
937    const egl_display_ptr dp = validate_display(dpy);
938    if (!dp) return (const char *) NULL;
939
940    switch (name) {
941        case EGL_VENDOR:
942            return dp->getVendorString();
943        case EGL_VERSION:
944            return dp->getVersionString();
945        case EGL_EXTENSIONS:
946            return dp->getExtensionString();
947        case EGL_CLIENT_APIS:
948            return dp->getClientApiString();
949        case EGL_VERSION_HW_ANDROID:
950            return dp->disp.queryString.version;
951    }
952    return setError(EGL_BAD_PARAMETER, (const char *)0);
953}
954
955
956// ----------------------------------------------------------------------------
957// EGL 1.1
958// ----------------------------------------------------------------------------
959
960EGLBoolean eglSurfaceAttrib(
961        EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
962{
963    clearError();
964
965    const egl_display_ptr dp = validate_display(dpy);
966    if (!dp) return EGL_FALSE;
967
968    SurfaceRef _s(dp.get(), surface);
969    if (!_s.get())
970        return setError(EGL_BAD_SURFACE, EGL_FALSE);
971
972    egl_surface_t const * const s = get_surface(surface);
973    if (s->cnx->egl.eglSurfaceAttrib) {
974        return s->cnx->egl.eglSurfaceAttrib(
975                dp->disp.dpy, s->surface, attribute, value);
976    }
977    return setError(EGL_BAD_SURFACE, EGL_FALSE);
978}
979
980EGLBoolean eglBindTexImage(
981        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
982{
983    clearError();
984
985    const egl_display_ptr dp = validate_display(dpy);
986    if (!dp) return EGL_FALSE;
987
988    SurfaceRef _s(dp.get(), surface);
989    if (!_s.get())
990        return setError(EGL_BAD_SURFACE, EGL_FALSE);
991
992    egl_surface_t const * const s = get_surface(surface);
993    if (s->cnx->egl.eglBindTexImage) {
994        return s->cnx->egl.eglBindTexImage(
995                dp->disp.dpy, s->surface, buffer);
996    }
997    return setError(EGL_BAD_SURFACE, EGL_FALSE);
998}
999
1000EGLBoolean eglReleaseTexImage(
1001        EGLDisplay dpy, EGLSurface surface, EGLint buffer)
1002{
1003    clearError();
1004
1005    const egl_display_ptr dp = validate_display(dpy);
1006    if (!dp) return EGL_FALSE;
1007
1008    SurfaceRef _s(dp.get(), surface);
1009    if (!_s.get())
1010        return setError(EGL_BAD_SURFACE, EGL_FALSE);
1011
1012    egl_surface_t const * const s = get_surface(surface);
1013    if (s->cnx->egl.eglReleaseTexImage) {
1014        return s->cnx->egl.eglReleaseTexImage(
1015                dp->disp.dpy, s->surface, buffer);
1016    }
1017    return setError(EGL_BAD_SURFACE, EGL_FALSE);
1018}
1019
1020EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval)
1021{
1022    clearError();
1023
1024    const egl_display_ptr dp = validate_display(dpy);
1025    if (!dp) return EGL_FALSE;
1026
1027    EGLBoolean res = EGL_TRUE;
1028    egl_connection_t* const cnx = &gEGLImpl;
1029    if (cnx->dso && cnx->egl.eglSwapInterval) {
1030        res = cnx->egl.eglSwapInterval(dp->disp.dpy, interval);
1031    }
1032
1033    return res;
1034}
1035
1036
1037// ----------------------------------------------------------------------------
1038// EGL 1.2
1039// ----------------------------------------------------------------------------
1040
1041EGLBoolean eglWaitClient(void)
1042{
1043    clearError();
1044
1045    egl_connection_t* const cnx = &gEGLImpl;
1046    if (!cnx->dso)
1047        return setError(EGL_BAD_CONTEXT, EGL_FALSE);
1048
1049    EGLBoolean res;
1050    if (cnx->egl.eglWaitClient) {
1051        res = cnx->egl.eglWaitClient();
1052    } else {
1053        res = cnx->egl.eglWaitGL();
1054    }
1055    return res;
1056}
1057
1058EGLBoolean eglBindAPI(EGLenum api)
1059{
1060    clearError();
1061
1062    if (egl_init_drivers() == EGL_FALSE) {
1063        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
1064    }
1065
1066    // bind this API on all EGLs
1067    EGLBoolean res = EGL_TRUE;
1068    egl_connection_t* const cnx = &gEGLImpl;
1069    if (cnx->dso && cnx->egl.eglBindAPI) {
1070        res = cnx->egl.eglBindAPI(api);
1071    }
1072    return res;
1073}
1074
1075EGLenum eglQueryAPI(void)
1076{
1077    clearError();
1078
1079    if (egl_init_drivers() == EGL_FALSE) {
1080        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
1081    }
1082
1083    egl_connection_t* const cnx = &gEGLImpl;
1084    if (cnx->dso && cnx->egl.eglQueryAPI) {
1085        return cnx->egl.eglQueryAPI();
1086    }
1087
1088    // or, it can only be OpenGL ES
1089    return EGL_OPENGL_ES_API;
1090}
1091
1092EGLBoolean eglReleaseThread(void)
1093{
1094    clearError();
1095
1096    // If there is context bound to the thread, release it
1097    egl_display_t::loseCurrent(get_context(getContext()));
1098
1099    egl_connection_t* const cnx = &gEGLImpl;
1100    if (cnx->dso && cnx->egl.eglReleaseThread) {
1101        cnx->egl.eglReleaseThread();
1102    }
1103
1104    egl_tls_t::clearTLS();
1105#if EGL_TRACE
1106    if (getEGLDebugLevel() > 0)
1107        GLTrace_eglReleaseThread();
1108#endif
1109    return EGL_TRUE;
1110}
1111
1112EGLSurface eglCreatePbufferFromClientBuffer(
1113          EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer,
1114          EGLConfig config, const EGLint *attrib_list)
1115{
1116    clearError();
1117
1118    egl_connection_t* cnx = NULL;
1119    const egl_display_ptr dp = validate_display_connection(dpy, cnx);
1120    if (!dp) return EGL_FALSE;
1121    if (cnx->egl.eglCreatePbufferFromClientBuffer) {
1122        return cnx->egl.eglCreatePbufferFromClientBuffer(
1123                dp->disp.dpy, buftype, buffer, config, attrib_list);
1124    }
1125    return setError(EGL_BAD_CONFIG, EGL_NO_SURFACE);
1126}
1127
1128// ----------------------------------------------------------------------------
1129// EGL_EGLEXT_VERSION 3
1130// ----------------------------------------------------------------------------
1131
1132EGLBoolean eglLockSurfaceKHR(EGLDisplay dpy, EGLSurface surface,
1133        const EGLint *attrib_list)
1134{
1135    clearError();
1136
1137    const egl_display_ptr dp = validate_display(dpy);
1138    if (!dp) return EGL_FALSE;
1139
1140    SurfaceRef _s(dp.get(), surface);
1141    if (!_s.get())
1142        return setError(EGL_BAD_SURFACE, EGL_FALSE);
1143
1144    egl_surface_t const * const s = get_surface(surface);
1145    if (s->cnx->egl.eglLockSurfaceKHR) {
1146        return s->cnx->egl.eglLockSurfaceKHR(
1147                dp->disp.dpy, s->surface, attrib_list);
1148    }
1149    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
1150}
1151
1152EGLBoolean eglUnlockSurfaceKHR(EGLDisplay dpy, EGLSurface surface)
1153{
1154    clearError();
1155
1156    const egl_display_ptr dp = validate_display(dpy);
1157    if (!dp) return EGL_FALSE;
1158
1159    SurfaceRef _s(dp.get(), surface);
1160    if (!_s.get())
1161        return setError(EGL_BAD_SURFACE, EGL_FALSE);
1162
1163    egl_surface_t const * const s = get_surface(surface);
1164    if (s->cnx->egl.eglUnlockSurfaceKHR) {
1165        return s->cnx->egl.eglUnlockSurfaceKHR(dp->disp.dpy, s->surface);
1166    }
1167    return setError(EGL_BAD_DISPLAY, EGL_FALSE);
1168}
1169
1170EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target,
1171        EGLClientBuffer buffer, const EGLint *attrib_list)
1172{
1173    clearError();
1174
1175    const egl_display_ptr dp = validate_display(dpy);
1176    if (!dp) return EGL_NO_IMAGE_KHR;
1177
1178    ContextRef _c(dp.get(), ctx);
1179    egl_context_t * const c = _c.get();
1180
1181    EGLImageKHR result = EGL_NO_IMAGE_KHR;
1182    egl_connection_t* const cnx = &gEGLImpl;
1183    if (cnx->dso && cnx->egl.eglCreateImageKHR) {
1184        result = cnx->egl.eglCreateImageKHR(
1185                dp->disp.dpy,
1186                c ? c->context : EGL_NO_CONTEXT,
1187                target, buffer, attrib_list);
1188    }
1189    return result;
1190}
1191
1192EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
1193{
1194    clearError();
1195
1196    const egl_display_ptr dp = validate_display(dpy);
1197    if (!dp) return EGL_FALSE;
1198
1199    EGLBoolean result = EGL_FALSE;
1200    egl_connection_t* const cnx = &gEGLImpl;
1201    if (cnx->dso && cnx->egl.eglDestroyImageKHR) {
1202        result = cnx->egl.eglDestroyImageKHR(dp->disp.dpy, img);
1203    }
1204    return result;
1205}
1206
1207// ----------------------------------------------------------------------------
1208// EGL_EGLEXT_VERSION 5
1209// ----------------------------------------------------------------------------
1210
1211
1212EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list)
1213{
1214    clearError();
1215
1216    const egl_display_ptr dp = validate_display(dpy);
1217    if (!dp) return EGL_NO_SYNC_KHR;
1218
1219    EGLSyncKHR result = EGL_NO_SYNC_KHR;
1220    egl_connection_t* const cnx = &gEGLImpl;
1221    if (cnx->dso && cnx->egl.eglCreateSyncKHR) {
1222        result = cnx->egl.eglCreateSyncKHR(dp->disp.dpy, type, attrib_list);
1223    }
1224    return result;
1225}
1226
1227EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
1228{
1229    clearError();
1230
1231    const egl_display_ptr dp = validate_display(dpy);
1232    if (!dp) return EGL_FALSE;
1233
1234    EGLBoolean result = EGL_FALSE;
1235    egl_connection_t* const cnx = &gEGLImpl;
1236    if (cnx->dso && cnx->egl.eglDestroySyncKHR) {
1237        result = cnx->egl.eglDestroySyncKHR(dp->disp.dpy, sync);
1238    }
1239    return result;
1240}
1241
1242EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync,
1243        EGLint flags, EGLTimeKHR timeout)
1244{
1245    clearError();
1246
1247    const egl_display_ptr dp = validate_display(dpy);
1248    if (!dp) return EGL_FALSE;
1249
1250    EGLBoolean result = EGL_FALSE;
1251    egl_connection_t* const cnx = &gEGLImpl;
1252    if (cnx->dso && cnx->egl.eglClientWaitSyncKHR) {
1253        result = cnx->egl.eglClientWaitSyncKHR(
1254                dp->disp.dpy, sync, flags, timeout);
1255    }
1256    return result;
1257}
1258
1259EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
1260        EGLint attribute, EGLint *value)
1261{
1262    clearError();
1263
1264    const egl_display_ptr dp = validate_display(dpy);
1265    if (!dp) return EGL_FALSE;
1266
1267    EGLBoolean result = EGL_FALSE;
1268    egl_connection_t* const cnx = &gEGLImpl;
1269    if (cnx->dso && cnx->egl.eglGetSyncAttribKHR) {
1270        result = cnx->egl.eglGetSyncAttribKHR(
1271                dp->disp.dpy, sync, attribute, value);
1272    }
1273    return result;
1274}
1275
1276// ----------------------------------------------------------------------------
1277// ANDROID extensions
1278// ----------------------------------------------------------------------------
1279
1280EGLint eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR sync)
1281{
1282    clearError();
1283
1284    const egl_display_ptr dp = validate_display(dpy);
1285    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
1286
1287    EGLint result = EGL_NO_NATIVE_FENCE_FD_ANDROID;
1288    egl_connection_t* const cnx = &gEGLImpl;
1289    if (cnx->dso && cnx->egl.eglDupNativeFenceFDANDROID) {
1290        result = cnx->egl.eglDupNativeFenceFDANDROID(dp->disp.dpy, sync);
1291    }
1292    return result;
1293}
1294
1295EGLint eglWaitSyncANDROID(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags)
1296{
1297    clearError();
1298
1299    const egl_display_ptr dp = validate_display(dpy);
1300    if (!dp) return EGL_NO_NATIVE_FENCE_FD_ANDROID;
1301
1302    EGLint result = EGL_FALSE;
1303    egl_connection_t* const cnx = &gEGLImpl;
1304    if (cnx->dso && cnx->egl.eglWaitSyncANDROID) {
1305        result = cnx->egl.eglWaitSyncANDROID(dp->disp.dpy, sync, flags);
1306    }
1307    return result;
1308}
1309
1310EGLBoolean eglPresentationTimeANDROID(EGLDisplay dpy, EGLSurface surface,
1311        EGLnsecsANDROID time)
1312{
1313    clearError();
1314
1315    const egl_display_ptr dp = validate_display(dpy);
1316    if (!dp) {
1317        return EGL_FALSE;
1318    }
1319
1320    SurfaceRef _s(dp.get(), surface);
1321    if (!_s.get()) {
1322        setError(EGL_BAD_SURFACE, EGL_FALSE);
1323        return EGL_FALSE;
1324    }
1325
1326    egl_surface_t const * const s = get_surface(surface);
1327    native_window_set_buffers_timestamp(s->win.get(), time);
1328
1329    return EGL_TRUE;
1330}
1331
1332// ----------------------------------------------------------------------------
1333// NVIDIA extensions
1334// ----------------------------------------------------------------------------
1335EGLuint64NV eglGetSystemTimeFrequencyNV()
1336{
1337    clearError();
1338
1339    if (egl_init_drivers() == EGL_FALSE) {
1340        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
1341    }
1342
1343    EGLuint64NV ret = 0;
1344    egl_connection_t* const cnx = &gEGLImpl;
1345
1346    if (cnx->dso && cnx->egl.eglGetSystemTimeFrequencyNV) {
1347        return cnx->egl.eglGetSystemTimeFrequencyNV();
1348    }
1349
1350    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
1351}
1352
1353EGLuint64NV eglGetSystemTimeNV()
1354{
1355    clearError();
1356
1357    if (egl_init_drivers() == EGL_FALSE) {
1358        return setError(EGL_BAD_PARAMETER, EGL_FALSE);
1359    }
1360
1361    EGLuint64NV ret = 0;
1362    egl_connection_t* const cnx = &gEGLImpl;
1363
1364    if (cnx->dso && cnx->egl.eglGetSystemTimeNV) {
1365        return cnx->egl.eglGetSystemTimeNV();
1366    }
1367
1368    return setErrorQuiet(EGL_BAD_DISPLAY, 0);
1369}
1370