| /* |
| * Copyright (C) 2011 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #include "HostConnection.h" |
| #include "ThreadInfo.h" |
| #include "eglDisplay.h" |
| #include "egl_ftable.h" |
| #include <cutils/log.h> |
| #include "gralloc_cb.h" |
| #include "GLClientState.h" |
| #include "GLSharedGroup.h" |
| #include "eglContext.h" |
| #include "ClientAPIExts.h" |
| |
| #include "GLEncoder.h" |
| #ifdef WITH_GLES2 |
| #include "GL2Encoder.h" |
| #endif |
| |
| #include <system/window.h> |
| |
| template<typename T> |
| static T setErrorFunc(GLint error, T returnValue) { |
| getEGLThreadInfo()->eglError = error; |
| return returnValue; |
| } |
| |
| const char * eglStrError(EGLint err) |
| { |
| switch (err){ |
| case EGL_SUCCESS: return "EGL_SUCCESS"; |
| case EGL_NOT_INITIALIZED: return "EGL_NOT_INITIALIZED"; |
| case EGL_BAD_ACCESS: return "EGL_BAD_ACCESS"; |
| case EGL_BAD_ALLOC: return "EGL_BAD_ALLOC"; |
| case EGL_BAD_ATTRIBUTE: return "EGL_BAD_ATTRIBUTE"; |
| case EGL_BAD_CONFIG: return "EGL_BAD_CONFIG"; |
| case EGL_BAD_CONTEXT: return "EGL_BAD_CONTEXT"; |
| case EGL_BAD_CURRENT_SURFACE: return "EGL_BAD_CURRENT_SURFACE"; |
| case EGL_BAD_DISPLAY: return "EGL_BAD_DISPLAY"; |
| case EGL_BAD_MATCH: return "EGL_BAD_MATCH"; |
| case EGL_BAD_NATIVE_PIXMAP: return "EGL_BAD_NATIVE_PIXMAP"; |
| case EGL_BAD_NATIVE_WINDOW: return "EGL_BAD_NATIVE_WINDOW"; |
| case EGL_BAD_PARAMETER: return "EGL_BAD_PARAMETER"; |
| case EGL_BAD_SURFACE: return "EGL_BAD_SURFACE"; |
| case EGL_CONTEXT_LOST: return "EGL_CONTEXT_LOST"; |
| default: return "UNKNOWN"; |
| } |
| } |
| |
| #define LOG_EGL_ERRORS 1 |
| |
| #ifdef LOG_EGL_ERRORS |
| |
| #define setErrorReturn(error, retVal) \ |
| { \ |
| ALOGE("tid %d: %s(%d): error 0x%x (%s)", gettid(), __FUNCTION__, __LINE__, error, eglStrError(error)); \ |
| return setErrorFunc(error, retVal); \ |
| } |
| |
| #define RETURN_ERROR(ret,err) \ |
| ALOGE("tid %d: %s(%d): error 0x%x (%s)", gettid(), __FUNCTION__, __LINE__, err, eglStrError(err)); \ |
| getEGLThreadInfo()->eglError = err; \ |
| return ret; |
| |
| #else //!LOG_EGL_ERRORS |
| |
| #define setErrorReturn(error, retVal) return setErrorFunc(error, retVal); |
| |
| #define RETURN_ERROR(ret,err) \ |
| getEGLThreadInfo()->eglError = err; \ |
| return ret; |
| |
| #endif //LOG_EGL_ERRORS |
| |
| #define VALIDATE_CONFIG(cfg,ret) \ |
| if(((int)cfg<0)||((int)cfg>s_display.getNumConfigs())) { \ |
| RETURN_ERROR(ret,EGL_BAD_CONFIG); \ |
| } |
| |
| #define VALIDATE_DISPLAY(dpy,ret) \ |
| if ((dpy) != (EGLDisplay)&s_display) { \ |
| RETURN_ERROR(ret, EGL_BAD_DISPLAY); \ |
| } |
| |
| #define VALIDATE_DISPLAY_INIT(dpy,ret) \ |
| VALIDATE_DISPLAY(dpy, ret) \ |
| if (!s_display.initialized()) { \ |
| RETURN_ERROR(ret, EGL_NOT_INITIALIZED); \ |
| } |
| |
| #define DEFINE_HOST_CONNECTION \ |
| HostConnection *hostCon = HostConnection::get(); \ |
| renderControl_encoder_context_t *rcEnc = (hostCon ? hostCon->rcEncoder() : NULL) |
| |
| #define DEFINE_AND_VALIDATE_HOST_CONNECTION(ret) \ |
| HostConnection *hostCon = HostConnection::get(); \ |
| if (!hostCon) { \ |
| ALOGE("egl: Failed to get host connection\n"); \ |
| return ret; \ |
| } \ |
| renderControl_encoder_context_t *rcEnc = hostCon->rcEncoder(); \ |
| if (!rcEnc) { \ |
| ALOGE("egl: Failed to get renderControl encoder context\n"); \ |
| return ret; \ |
| } |
| |
| #define VALIDATE_CONTEXT_RETURN(context,ret) \ |
| if (!context) { \ |
| RETURN_ERROR(ret,EGL_BAD_CONTEXT); \ |
| } |
| |
| #define VALIDATE_SURFACE_RETURN(surface, ret) \ |
| if (surface != EGL_NO_SURFACE) { \ |
| egl_surface_t* s( static_cast<egl_surface_t*>(surface) ); \ |
| if (s->dpy != (EGLDisplay)&s_display) \ |
| setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE); \ |
| } |
| |
| |
| EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* shareCtx) : |
| dpy(dpy), |
| config(config), |
| read(EGL_NO_SURFACE), |
| draw(EGL_NO_SURFACE), |
| shareCtx(shareCtx), |
| rcContext(0), |
| versionString(NULL), |
| vendorString(NULL), |
| rendererString(NULL), |
| extensionString(NULL) |
| { |
| flags = 0; |
| version = 1; |
| clientState = new GLClientState(); |
| if (shareCtx) |
| sharedGroup = shareCtx->getSharedGroup(); |
| else |
| sharedGroup = GLSharedGroupPtr(new GLSharedGroup()); |
| }; |
| |
| EGLContext_t::~EGLContext_t() |
| { |
| delete clientState; |
| delete [] versionString; |
| delete [] vendorString; |
| delete [] rendererString; |
| delete [] extensionString; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| //egl_surface_t |
| |
| //we don't need to handle depth since it's handled when window created on the host |
| |
| struct egl_surface_t { |
| |
| EGLDisplay dpy; |
| EGLConfig config; |
| |
| |
| egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceType); |
| virtual ~egl_surface_t(); |
| |
| virtual void setSwapInterval(int interval) = 0; |
| virtual EGLBoolean swapBuffers() = 0; |
| |
| EGLint getSwapBehavior() const; |
| uint32_t getRcSurface() { return rcSurface; } |
| EGLint getSurfaceType() { return surfaceType; } |
| |
| EGLint getWidth(){ return width; } |
| EGLint getHeight(){ return height; } |
| void setTextureFormat(EGLint _texFormat) { texFormat = _texFormat; } |
| EGLint getTextureFormat() { return texFormat; } |
| void setTextureTarget(EGLint _texTarget) { texTarget = _texTarget; } |
| EGLint getTextureTarget() { return texTarget; } |
| |
| private: |
| // |
| //Surface attributes |
| // |
| EGLint width; |
| EGLint height; |
| EGLint texFormat; |
| EGLint texTarget; |
| |
| protected: |
| void setWidth(EGLint w) { width = w; } |
| void setHeight(EGLint h) { height = h; } |
| |
| EGLint surfaceType; |
| uint32_t rcSurface; //handle to surface created via remote control |
| }; |
| |
| egl_surface_t::egl_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfaceType) |
| : dpy(dpy), config(config), surfaceType(surfaceType), rcSurface(0) |
| { |
| width = 0; |
| height = 0; |
| texFormat = EGL_NO_TEXTURE; |
| texTarget = EGL_NO_TEXTURE; |
| } |
| |
| EGLint egl_surface_t::getSwapBehavior() const { |
| return EGL_BUFFER_PRESERVED; |
| } |
| |
| egl_surface_t::~egl_surface_t() |
| { |
| } |
| |
| // ---------------------------------------------------------------------------- |
| // egl_window_surface_t |
| |
| struct egl_window_surface_t : public egl_surface_t { |
| static egl_window_surface_t* create( |
| EGLDisplay dpy, EGLConfig config, EGLint surfType, |
| ANativeWindow* window); |
| |
| virtual ~egl_window_surface_t(); |
| |
| virtual void setSwapInterval(int interval); |
| virtual EGLBoolean swapBuffers(); |
| |
| private: |
| egl_window_surface_t( |
| EGLDisplay dpy, EGLConfig config, EGLint surfType, |
| ANativeWindow* window); |
| EGLBoolean init(); |
| |
| ANativeWindow* nativeWindow; |
| android_native_buffer_t* buffer; |
| }; |
| |
| egl_window_surface_t::egl_window_surface_t ( |
| EGLDisplay dpy, EGLConfig config, EGLint surfType, |
| ANativeWindow* window) |
| : egl_surface_t(dpy, config, surfType), |
| nativeWindow(window), |
| buffer(NULL) |
| { |
| // keep a reference on the window |
| nativeWindow->common.incRef(&nativeWindow->common); |
| EGLint w,h; |
| nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &w); |
| setWidth(w); |
| nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &h); |
| setHeight(h); |
| } |
| |
| EGLBoolean egl_window_surface_t::init() |
| { |
| if (nativeWindow->dequeueBuffer_DEPRECATED(nativeWindow, &buffer) != NO_ERROR) { |
| setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE); |
| } |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uint32_t)config, |
| getWidth(), getHeight()); |
| if (!rcSurface) { |
| ALOGE("rcCreateWindowSurface returned 0"); |
| return EGL_FALSE; |
| } |
| rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, |
| ((cb_handle_t*)(buffer->handle))->hostHandle); |
| |
| return EGL_TRUE; |
| } |
| |
| egl_window_surface_t* egl_window_surface_t::create( |
| EGLDisplay dpy, EGLConfig config, EGLint surfType, |
| ANativeWindow* window) |
| { |
| egl_window_surface_t* wnd = new egl_window_surface_t( |
| dpy, config, surfType, window); |
| if (wnd && !wnd->init()) { |
| delete wnd; |
| wnd = NULL; |
| } |
| return wnd; |
| } |
| |
| egl_window_surface_t::~egl_window_surface_t() { |
| DEFINE_HOST_CONNECTION; |
| if (rcSurface && rcEnc) { |
| rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface); |
| } |
| if (buffer) { |
| nativeWindow->cancelBuffer_DEPRECATED(nativeWindow, buffer); |
| } |
| nativeWindow->common.decRef(&nativeWindow->common); |
| } |
| |
| void egl_window_surface_t::setSwapInterval(int interval) |
| { |
| nativeWindow->setSwapInterval(nativeWindow, interval); |
| } |
| |
| EGLBoolean egl_window_surface_t::swapBuffers() |
| { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| |
| rcEnc->rcFlushWindowColorBuffer(rcEnc, rcSurface); |
| |
| nativeWindow->queueBuffer_DEPRECATED(nativeWindow, buffer); |
| if (nativeWindow->dequeueBuffer_DEPRECATED(nativeWindow, &buffer)) { |
| buffer = NULL; |
| setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE); |
| } |
| |
| rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, |
| ((cb_handle_t *)(buffer->handle))->hostHandle); |
| |
| return EGL_TRUE; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| //egl_pbuffer_surface_t |
| |
| struct egl_pbuffer_surface_t : public egl_surface_t { |
| static egl_pbuffer_surface_t* create(EGLDisplay dpy, EGLConfig config, |
| EGLint surfType, int32_t w, int32_t h, GLenum pixelFormat); |
| |
| virtual ~egl_pbuffer_surface_t(); |
| |
| virtual void setSwapInterval(int interval) {} |
| virtual EGLBoolean swapBuffers() { return EGL_TRUE; } |
| |
| uint32_t getRcColorBuffer() { return rcColorBuffer; } |
| |
| private: |
| egl_pbuffer_surface_t(EGLDisplay dpy, EGLConfig config, EGLint surfType, |
| int32_t w, int32_t h); |
| EGLBoolean init(GLenum format); |
| |
| uint32_t rcColorBuffer; |
| }; |
| |
| egl_pbuffer_surface_t::egl_pbuffer_surface_t(EGLDisplay dpy, EGLConfig config, |
| EGLint surfType, int32_t w, int32_t h) |
| : egl_surface_t(dpy, config, surfType), |
| rcColorBuffer(0) |
| { |
| setWidth(w); |
| setHeight(h); |
| } |
| |
| egl_pbuffer_surface_t::~egl_pbuffer_surface_t() |
| { |
| DEFINE_HOST_CONNECTION; |
| if (rcEnc) { |
| if (rcColorBuffer) rcEnc->rcCloseColorBuffer(rcEnc, rcColorBuffer); |
| if (rcSurface) rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface); |
| } |
| } |
| |
| EGLBoolean egl_pbuffer_surface_t::init(GLenum pixelFormat) |
| { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| |
| rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uint32_t)config, |
| getWidth(), getHeight()); |
| if (!rcSurface) { |
| ALOGE("rcCreateWindowSurface returned 0"); |
| return EGL_FALSE; |
| } |
| |
| rcColorBuffer = rcEnc->rcCreateColorBuffer(rcEnc, getWidth(), getHeight(), |
| pixelFormat); |
| if (!rcColorBuffer) { |
| ALOGE("rcCreateColorBuffer returned 0"); |
| return EGL_FALSE; |
| } |
| |
| rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, rcColorBuffer); |
| |
| return EGL_TRUE; |
| } |
| |
| egl_pbuffer_surface_t* egl_pbuffer_surface_t::create(EGLDisplay dpy, |
| EGLConfig config, EGLint surfType, int32_t w, int32_t h, |
| GLenum pixelFormat) |
| { |
| egl_pbuffer_surface_t* pb = new egl_pbuffer_surface_t(dpy, config, surfType, |
| w, h); |
| if (pb && !pb->init(pixelFormat)) { |
| delete pb; |
| pb = NULL; |
| } |
| return pb; |
| } |
| |
| static const char *getGLString(int glEnum) |
| { |
| EGLThreadInfo *tInfo = getEGLThreadInfo(); |
| if (!tInfo || !tInfo->currentContext) { |
| return NULL; |
| } |
| |
| const char** strPtr = NULL; |
| |
| #define GL_VENDOR 0x1F00 |
| #define GL_RENDERER 0x1F01 |
| #define GL_VERSION 0x1F02 |
| #define GL_EXTENSIONS 0x1F03 |
| |
| switch(glEnum) { |
| case GL_VERSION: |
| strPtr = &tInfo->currentContext->versionString; |
| break; |
| case GL_VENDOR: |
| strPtr = &tInfo->currentContext->vendorString; |
| break; |
| case GL_RENDERER: |
| strPtr = &tInfo->currentContext->rendererString; |
| break; |
| case GL_EXTENSIONS: |
| strPtr = &tInfo->currentContext->extensionString; |
| break; |
| } |
| |
| if (!strPtr) { |
| return NULL; |
| } |
| |
| if (*strPtr != NULL) { |
| // |
| // string is already cached |
| // |
| return *strPtr; |
| } |
| |
| // |
| // first query of that string - need to query host |
| // |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(NULL); |
| char *hostStr = NULL; |
| int n = rcEnc->rcGetGLString(rcEnc, glEnum, NULL, 0); |
| if (n < 0) { |
| hostStr = new char[-n+1]; |
| n = rcEnc->rcGetGLString(rcEnc, glEnum, hostStr, -n); |
| if (n <= 0) { |
| delete [] hostStr; |
| hostStr = NULL; |
| } |
| } |
| |
| // |
| // keep the string in the context and return its value |
| // |
| *strPtr = hostStr; |
| return hostStr; |
| } |
| |
| // ---------------------------------------------------------------------------- |
| |
| // The one and only supported display object. |
| static eglDisplay s_display; |
| |
| static EGLClient_eglInterface s_eglIface = { |
| getThreadInfo: getEGLThreadInfo, |
| getGLString: getGLString |
| }; |
| |
| #define DBG_FUNC DBG("%s\n", __FUNCTION__) |
| EGLDisplay eglGetDisplay(EGLNativeDisplayType display_id) |
| { |
| // |
| // we support only EGL_DEFAULT_DISPLAY. |
| // |
| if (display_id != EGL_DEFAULT_DISPLAY) { |
| return EGL_NO_DISPLAY; |
| } |
| |
| return (EGLDisplay)&s_display; |
| } |
| |
| EGLBoolean eglInitialize(EGLDisplay dpy, EGLint *major, EGLint *minor) |
| { |
| VALIDATE_DISPLAY(dpy,EGL_FALSE); |
| |
| if (!s_display.initialize(&s_eglIface)) { |
| return EGL_FALSE; |
| } |
| if (major!=NULL) |
| *major = s_display.getVersionMajor(); |
| if (minor!=NULL) |
| *minor = s_display.getVersionMinor(); |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglTerminate(EGLDisplay dpy) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| |
| s_display.terminate(); |
| return EGL_TRUE; |
| } |
| |
| EGLint eglGetError() |
| { |
| EGLint error = getEGLThreadInfo()->eglError; |
| getEGLThreadInfo()->eglError = EGL_SUCCESS; |
| return error; |
| } |
| |
| __eglMustCastToProperFunctionPointerType eglGetProcAddress(const char *procname) |
| { |
| // search in EGL function table |
| for (int i=0; i<egl_num_funcs; i++) { |
| if (!strcmp(egl_funcs_by_name[i].name, procname)) { |
| return (__eglMustCastToProperFunctionPointerType)egl_funcs_by_name[i].proc; |
| } |
| } |
| |
| // |
| // Make sure display is initialized before searching in client APIs |
| // |
| if (!s_display.initialized()) { |
| if (!s_display.initialize(&s_eglIface)) { |
| return NULL; |
| } |
| } |
| |
| // look in gles client api's extensions table |
| return (__eglMustCastToProperFunctionPointerType)ClientAPIExts::getProcAddress(procname); |
| |
| // Fail - function not found. |
| return NULL; |
| } |
| |
| const char* eglQueryString(EGLDisplay dpy, EGLint name) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, NULL); |
| |
| return s_display.queryString(name); |
| } |
| |
| EGLBoolean eglGetConfigs(EGLDisplay dpy, EGLConfig *configs, EGLint config_size, EGLint *num_config) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, NULL); |
| |
| if(!num_config) { |
| RETURN_ERROR(EGL_FALSE,EGL_BAD_PARAMETER); |
| } |
| |
| GLint numConfigs = s_display.getNumConfigs(); |
| if (!configs) { |
| *num_config = numConfigs; |
| return EGL_TRUE; |
| } |
| |
| int i=0; |
| for (i=0 ; i<numConfigs && i<config_size ; i++) { |
| *configs++ = (EGLConfig)i; |
| } |
| *num_config = i; |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglChooseConfig(EGLDisplay dpy, const EGLint *attrib_list, EGLConfig *configs, EGLint config_size, EGLint *num_config) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| |
| int attribs_size = 0; |
| if (attrib_list) { |
| const EGLint * attrib_p = attrib_list; |
| while (attrib_p[0] != EGL_NONE) { |
| attribs_size += 2; |
| attrib_p += 2; |
| } |
| attribs_size++; //for the terminating EGL_NONE |
| } |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| *num_config = rcEnc->rcChooseConfig(rcEnc, (EGLint*)attrib_list, attribs_size * sizeof(EGLint), (uint32_t*)configs, config_size); |
| |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglGetConfigAttrib(EGLDisplay dpy, EGLConfig config, EGLint attribute, EGLint *value) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, NULL); |
| VALIDATE_CONFIG(config, EGL_FALSE); |
| |
| if (s_display.getConfigAttrib(config, attribute, value)) |
| { |
| return EGL_TRUE; |
| } |
| else |
| { |
| RETURN_ERROR(EGL_FALSE, EGL_BAD_ATTRIBUTE); |
| } |
| } |
| |
| EGLSurface eglCreateWindowSurface(EGLDisplay dpy, EGLConfig config, EGLNativeWindowType win, const EGLint *attrib_list) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, NULL); |
| VALIDATE_CONFIG(config, EGL_FALSE); |
| if (win == 0) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| } |
| |
| EGLint surfaceType; |
| if (s_display.getConfigAttrib(config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) return EGL_FALSE; |
| |
| if (!(surfaceType & EGL_WINDOW_BIT)) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| } |
| |
| if (static_cast<ANativeWindow*>(win)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) { |
| setErrorReturn(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); |
| } |
| |
| egl_surface_t* surface = egl_window_surface_t::create( |
| &s_display, config, surfaceType, static_cast<ANativeWindow*>(win)); |
| if (!surface) { |
| setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE); |
| } |
| |
| return surface; |
| } |
| |
| EGLSurface eglCreatePbufferSurface(EGLDisplay dpy, EGLConfig config, const EGLint *attrib_list) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, NULL); |
| VALIDATE_CONFIG(config, EGL_FALSE); |
| |
| EGLint surfaceType; |
| if (s_display.getConfigAttrib(config, EGL_SURFACE_TYPE, &surfaceType) == EGL_FALSE) return EGL_FALSE; |
| |
| if (!(surfaceType & EGL_PBUFFER_BIT)) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| } |
| |
| int32_t w = 0; |
| int32_t h = 0; |
| EGLint texFormat = EGL_NO_TEXTURE; |
| EGLint texTarget = EGL_NO_TEXTURE; |
| while (attrib_list[0]) { |
| switch (attrib_list[0]) { |
| case EGL_WIDTH: |
| w = attrib_list[1]; |
| break; |
| case EGL_HEIGHT: |
| h = attrib_list[1]; |
| break; |
| case EGL_TEXTURE_FORMAT: |
| texFormat = attrib_list[1]; |
| break; |
| case EGL_TEXTURE_TARGET: |
| texTarget = attrib_list[1]; |
| break; |
| default: |
| break; |
| }; |
| attrib_list+=2; |
| } |
| if (((texFormat == EGL_NO_TEXTURE)&&(texTarget != EGL_NO_TEXTURE)) || |
| ((texFormat != EGL_NO_TEXTURE)&&(texTarget == EGL_NO_TEXTURE))) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| } |
| // TODO: check EGL_TEXTURE_FORMAT - need to support eglBindTexImage |
| |
| GLenum pixelFormat; |
| if (s_display.getConfigGLPixelFormat(config, &pixelFormat) == EGL_FALSE) |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE); |
| |
| egl_surface_t* surface = egl_pbuffer_surface_t::create(dpy, config, |
| surfaceType, w, h, pixelFormat); |
| if (!surface) { |
| setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE); |
| } |
| |
| //setup attributes |
| surface->setTextureFormat(texFormat); |
| surface->setTextureTarget(texTarget); |
| |
| return surface; |
| } |
| |
| EGLSurface eglCreatePixmapSurface(EGLDisplay dpy, EGLConfig config, EGLNativePixmapType pixmap, const EGLint *attrib_list) |
| { |
| //XXX: Pixmap not supported. The host cannot render to a pixmap resource |
| // located on host. In order to support Pixmaps we should either punt |
| // to s/w rendering -or- let the host render to a buffer that will be |
| // copied back to guest at some sync point. None of those methods not |
| // implemented and pixmaps are not used with OpenGL anyway ... |
| return EGL_NO_SURFACE; |
| } |
| |
| EGLBoolean eglDestroySurface(EGLDisplay dpy, EGLSurface eglSurface) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE); |
| |
| egl_surface_t* surface(static_cast<egl_surface_t*>(eglSurface)); |
| delete surface; |
| |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglQuerySurface(EGLDisplay dpy, EGLSurface eglSurface, EGLint attribute, EGLint *value) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE); |
| |
| egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) ); |
| EGLBoolean ret = EGL_TRUE; |
| switch (attribute) { |
| case EGL_CONFIG_ID: |
| ret = s_display.getConfigAttrib(surface->config, EGL_CONFIG_ID, value); |
| break; |
| case EGL_WIDTH: |
| *value = surface->getWidth(); |
| break; |
| case EGL_HEIGHT: |
| *value = surface->getHeight(); |
| break; |
| case EGL_TEXTURE_FORMAT: |
| *value = surface->getTextureFormat(); |
| break; |
| case EGL_TEXTURE_TARGET: |
| *value = surface->getTextureTarget(); |
| break; |
| case EGL_SWAP_BEHAVIOR: |
| *value = surface->getSwapBehavior(); |
| break; |
| case EGL_LARGEST_PBUFFER: |
| // not modified for a window or pixmap surface |
| // and we ignore it when creating a PBuffer surface (default is EGL_FALSE) |
| if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = EGL_FALSE; |
| break; |
| //TODO: complete other attributes |
| default: |
| ALOGE("eglQuerySurface %x EGL_BAD_ATTRIBUTE", attribute); |
| ret = setErrorFunc(EGL_BAD_ATTRIBUTE, EGL_FALSE); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| EGLBoolean eglBindAPI(EGLenum api) |
| { |
| if (api != EGL_OPENGL_ES_API) |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| return EGL_TRUE; |
| } |
| |
| EGLenum eglQueryAPI() |
| { |
| return EGL_OPENGL_ES_API; |
| } |
| |
| EGLBoolean eglWaitClient() |
| { |
| return eglWaitGL(); |
| } |
| |
| EGLBoolean eglReleaseThread() |
| { |
| EGLThreadInfo *tInfo = getEGLThreadInfo(); |
| if (tInfo && tInfo->currentContext) { |
| return eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE); |
| } |
| return EGL_TRUE; |
| } |
| |
| EGLSurface eglCreatePbufferFromClientBuffer(EGLDisplay dpy, EGLenum buftype, EGLClientBuffer buffer, EGLConfig config, const EGLint *attrib_list) |
| { |
| //TODO |
| ALOGW("%s not implemented", __FUNCTION__); |
| return 0; |
| } |
| |
| EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) |
| { |
| //TODO |
| ALOGW("%s not implemented", __FUNCTION__); |
| return 0; |
| } |
| |
| EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface eglSurface, EGLint buffer) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_SURFACE_RETURN(eglSurface, EGL_FALSE); |
| if (eglSurface == EGL_NO_SURFACE) { |
| setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); |
| } |
| |
| if (buffer != EGL_BACK_BUFFER) { |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| } |
| |
| egl_surface_t* surface( static_cast<egl_surface_t*>(eglSurface) ); |
| |
| if (surface->getTextureFormat() == EGL_NO_TEXTURE) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_FALSE); |
| } |
| |
| if (!(surface->getSurfaceType() & EGL_PBUFFER_BIT)) { |
| setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); |
| } |
| |
| //It's now safe to cast to pbuffer surface |
| egl_pbuffer_surface_t* pbSurface = (egl_pbuffer_surface_t*)surface; |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| rcEnc->rcBindTexture(rcEnc, pbSurface->getRcColorBuffer()); |
| |
| return GL_TRUE; |
| } |
| |
| EGLBoolean eglReleaseTexImage(EGLDisplay dpy, EGLSurface surface, EGLint buffer) |
| { |
| //TODO |
| ALOGW("%s not implemented", __FUNCTION__); |
| return 0; |
| } |
| |
| EGLBoolean eglSwapInterval(EGLDisplay dpy, EGLint interval) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| |
| EGLContext_t* ctx = getEGLThreadInfo()->currentContext; |
| if (!ctx) { |
| setErrorReturn(EGL_BAD_CONTEXT, EGL_FALSE); |
| } |
| if (!ctx->draw) { |
| setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); |
| } |
| egl_surface_t* draw(static_cast<egl_surface_t*>(ctx->draw)); |
| draw->setSwapInterval(interval); |
| |
| rcEnc->rcFBSetSwapInterval(rcEnc, interval); //TODO: implement on the host |
| |
| return EGL_TRUE; |
| } |
| |
| EGLContext eglCreateContext(EGLDisplay dpy, EGLConfig config, EGLContext share_context, const EGLint *attrib_list) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_NO_CONTEXT); |
| VALIDATE_CONFIG(config, EGL_NO_CONTEXT); |
| |
| EGLint version = 1; //default |
| while (attrib_list && attrib_list[0]) { |
| if (attrib_list[0] == EGL_CONTEXT_CLIENT_VERSION) version = attrib_list[1]; |
| attrib_list+=2; |
| } |
| |
| uint32_t rcShareCtx = 0; |
| EGLContext_t * shareCtx = NULL; |
| if (share_context) { |
| shareCtx = static_cast<EGLContext_t*>(share_context); |
| rcShareCtx = shareCtx->rcContext; |
| if (shareCtx->dpy != dpy) |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_CONTEXT); |
| } |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_CONTEXT); |
| uint32_t rcContext = rcEnc->rcCreateContext(rcEnc, (uint32_t)config, rcShareCtx, version); |
| if (!rcContext) { |
| ALOGE("rcCreateContext returned 0"); |
| setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT); |
| } |
| |
| EGLContext_t * context = new EGLContext_t(dpy, config, shareCtx); |
| if (!context) |
| setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT); |
| |
| context->version = version; |
| context->rcContext = rcContext; |
| |
| |
| return context; |
| } |
| |
| EGLBoolean eglDestroyContext(EGLDisplay dpy, EGLContext ctx) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_CONTEXT_RETURN(ctx, EGL_FALSE); |
| |
| EGLContext_t * context = static_cast<EGLContext_t*>(ctx); |
| |
| if (getEGLThreadInfo()->currentContext == context) |
| { |
| eglMakeCurrent(dpy, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE); |
| } |
| |
| if (context->rcContext) { |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| rcEnc->rcDestroyContext(rcEnc, context->rcContext); |
| context->rcContext = 0; |
| } |
| |
| delete context; |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglMakeCurrent(EGLDisplay dpy, EGLSurface draw, EGLSurface read, EGLContext ctx) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_SURFACE_RETURN(draw, EGL_FALSE); |
| VALIDATE_SURFACE_RETURN(read, EGL_FALSE); |
| |
| if ((read == EGL_NO_SURFACE && draw == EGL_NO_SURFACE) && (ctx != EGL_NO_CONTEXT)) |
| setErrorReturn(EGL_BAD_MATCH, EGL_FALSE); |
| if ((read != EGL_NO_SURFACE || draw != EGL_NO_SURFACE) && (ctx == EGL_NO_CONTEXT)) |
| setErrorReturn(EGL_BAD_MATCH, EGL_FALSE); |
| |
| EGLContext_t * context = static_cast<EGLContext_t*>(ctx); |
| uint32_t ctxHandle = (context) ? context->rcContext : 0; |
| egl_surface_t * drawSurf = static_cast<egl_surface_t *>(draw); |
| uint32_t drawHandle = (drawSurf) ? drawSurf->getRcSurface() : 0; |
| egl_surface_t * readSurf = static_cast<egl_surface_t *>(read); |
| uint32_t readHandle = (readSurf) ? readSurf->getRcSurface() : 0; |
| |
| // |
| // Nothing to do if no binding change has made |
| // |
| EGLThreadInfo *tInfo = getEGLThreadInfo(); |
| if (tInfo->currentContext == context && |
| (context == NULL || |
| (context && context->draw == draw && context->read == read))) { |
| return EGL_TRUE; |
| } |
| |
| if (context && (context->flags & EGLContext_t::IS_CURRENT) && (context != tInfo->currentContext)) { |
| //context is current to another thread |
| setErrorReturn(EGL_BAD_ACCESS, EGL_FALSE); |
| } |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| if (rcEnc->rcMakeCurrent(rcEnc, ctxHandle, drawHandle, readHandle) == EGL_FALSE) { |
| ALOGE("rcMakeCurrent returned EGL_FALSE"); |
| setErrorReturn(EGL_BAD_CONTEXT, EGL_FALSE); |
| } |
| |
| //Now make the local bind |
| if (context) { |
| context->draw = draw; |
| context->read = read; |
| context->flags |= EGLContext_t::IS_CURRENT; |
| //set the client state |
| if (context->version == 2) { |
| hostCon->gl2Encoder()->setClientState(context->getClientState()); |
| hostCon->gl2Encoder()->setSharedGroup(context->getSharedGroup()); |
| } |
| else { |
| hostCon->glEncoder()->setClientState(context->getClientState()); |
| hostCon->glEncoder()->setSharedGroup(context->getSharedGroup()); |
| } |
| } |
| else { |
| //release ClientState & SharedGroup |
| if (tInfo->currentContext->version == 2) { |
| hostCon->gl2Encoder()->setClientState(NULL); |
| hostCon->gl2Encoder()->setSharedGroup(GLSharedGroupPtr(NULL)); |
| } |
| else { |
| hostCon->glEncoder()->setClientState(NULL); |
| hostCon->glEncoder()->setSharedGroup(GLSharedGroupPtr(NULL)); |
| } |
| |
| } |
| |
| if (tInfo->currentContext) |
| tInfo->currentContext->flags &= ~EGLContext_t::IS_CURRENT; |
| |
| //Now make current |
| tInfo->currentContext = context; |
| |
| //Check maybe we need to init the encoder, if it's first eglMakeCurrent |
| if (tInfo->currentContext) { |
| if (tInfo->currentContext->version == 2) { |
| if (!hostCon->gl2Encoder()->isInitialized()) { |
| s_display.gles2_iface()->init(); |
| hostCon->gl2Encoder()->setInitialized(); |
| ClientAPIExts::initClientFuncs(s_display.gles2_iface(), 1); |
| } |
| } |
| else { |
| if (!hostCon->glEncoder()->isInitialized()) { |
| s_display.gles_iface()->init(); |
| hostCon->glEncoder()->setInitialized(); |
| ClientAPIExts::initClientFuncs(s_display.gles_iface(), 0); |
| } |
| } |
| } |
| |
| return EGL_TRUE; |
| } |
| |
| EGLContext eglGetCurrentContext() |
| { |
| return getEGLThreadInfo()->currentContext; |
| } |
| |
| EGLSurface eglGetCurrentSurface(EGLint readdraw) |
| { |
| EGLContext_t * context = getEGLThreadInfo()->currentContext; |
| if (!context) |
| return EGL_NO_SURFACE; //not an error |
| |
| switch (readdraw) { |
| case EGL_READ: |
| return context->read; |
| case EGL_DRAW: |
| return context->draw; |
| default: |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE); |
| } |
| } |
| |
| EGLDisplay eglGetCurrentDisplay() |
| { |
| EGLContext_t * context = getEGLThreadInfo()->currentContext; |
| if (!context) |
| return EGL_NO_DISPLAY; //not an error |
| |
| return context->dpy; |
| } |
| |
| EGLBoolean eglQueryContext(EGLDisplay dpy, EGLContext ctx, EGLint attribute, EGLint *value) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| VALIDATE_CONTEXT_RETURN(ctx, EGL_FALSE); |
| |
| EGLContext_t * context = static_cast<EGLContext_t*>(ctx); |
| |
| EGLBoolean ret = EGL_TRUE; |
| switch (attribute) { |
| case EGL_CONFIG_ID: |
| ret = s_display.getConfigAttrib(context->config, EGL_CONFIG_ID, value); |
| break; |
| case EGL_CONTEXT_CLIENT_TYPE: |
| *value = EGL_OPENGL_ES_API; |
| break; |
| case EGL_CONTEXT_CLIENT_VERSION: |
| *value = context->version; |
| break; |
| case EGL_RENDER_BUFFER: |
| if (!context->draw) |
| *value = EGL_NONE; |
| else |
| *value = EGL_BACK_BUFFER; //single buffer not supported |
| break; |
| default: |
| ALOGE("eglQueryContext %x EGL_BAD_ATTRIBUTE", attribute); |
| setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE); |
| break; |
| } |
| |
| return ret; |
| } |
| |
| EGLBoolean eglWaitGL() |
| { |
| EGLThreadInfo *tInfo = getEGLThreadInfo(); |
| if (!tInfo || !tInfo->currentContext) { |
| return EGL_FALSE; |
| } |
| |
| if (tInfo->currentContext->version == 2) { |
| s_display.gles2_iface()->finish(); |
| } |
| else { |
| s_display.gles_iface()->finish(); |
| } |
| |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglWaitNative(EGLint engine) |
| { |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglSwapBuffers(EGLDisplay dpy, EGLSurface eglSurface) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| if (eglSurface == EGL_NO_SURFACE) |
| setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); |
| |
| DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); |
| |
| egl_surface_t* d = static_cast<egl_surface_t*>(eglSurface); |
| if (d->dpy != dpy) |
| setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE); |
| |
| // post the surface |
| d->swapBuffers(); |
| |
| hostCon->flush(); |
| return EGL_TRUE; |
| } |
| |
| EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target) |
| { |
| //TODO :later |
| return 0; |
| } |
| |
| EGLBoolean eglLockSurfaceKHR(EGLDisplay display, EGLSurface surface, const EGLint *attrib_list) |
| { |
| //TODO later |
| return 0; |
| } |
| |
| EGLBoolean eglUnlockSurfaceKHR(EGLDisplay display, EGLSurface surface) |
| { |
| //TODO later |
| return 0; |
| } |
| |
| EGLImageKHR eglCreateImageKHR(EGLDisplay dpy, EGLContext ctx, EGLenum target, EGLClientBuffer buffer, const EGLint *attrib_list) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_NO_IMAGE_KHR); |
| if (ctx != EGL_NO_CONTEXT) { |
| setErrorReturn(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR); |
| } |
| if (target != EGL_NATIVE_BUFFER_ANDROID) { |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); |
| } |
| |
| android_native_buffer_t* native_buffer = (android_native_buffer_t*)buffer; |
| |
| if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); |
| |
| if (native_buffer->common.version != sizeof(android_native_buffer_t)) |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); |
| |
| cb_handle_t *cb = (cb_handle_t *)(native_buffer->handle); |
| |
| switch (cb->format) { |
| case HAL_PIXEL_FORMAT_RGBA_8888: |
| case HAL_PIXEL_FORMAT_RGBX_8888: |
| case HAL_PIXEL_FORMAT_RGB_888: |
| case HAL_PIXEL_FORMAT_RGB_565: |
| case HAL_PIXEL_FORMAT_BGRA_8888: |
| case HAL_PIXEL_FORMAT_RGBA_5551: |
| case HAL_PIXEL_FORMAT_RGBA_4444: |
| break; |
| default: |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR); |
| } |
| |
| native_buffer->common.incRef(&native_buffer->common); |
| return (EGLImageKHR)native_buffer; |
| } |
| |
| EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img) |
| { |
| VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE); |
| android_native_buffer_t* native_buffer = (android_native_buffer_t*)img; |
| |
| if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC) |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| |
| if (native_buffer->common.version != sizeof(android_native_buffer_t)) |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| |
| native_buffer->common.decRef(&native_buffer->common); |
| |
| return EGL_TRUE; |
| } |
| |
| #define FENCE_SYNC_HANDLE (EGLSyncKHR)0xFE4CE |
| |
| EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, |
| const EGLint *attrib_list) |
| { |
| // TODO: This implementation could be faster. We should require the host EGL |
| // to support KHR_fence_sync, or at least pipe the fence command to the host |
| // and wait for it (probably involving a glFinish on the host) in |
| // eglClientWaitSyncKHR. |
| |
| VALIDATE_DISPLAY(dpy, EGL_NO_SYNC_KHR); |
| |
| if (type != EGL_SYNC_FENCE_KHR || |
| (attrib_list != NULL && attrib_list[0] != EGL_NONE)) { |
| setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR); |
| } |
| |
| EGLThreadInfo *tInfo = getEGLThreadInfo(); |
| if (!tInfo || !tInfo->currentContext) { |
| setErrorReturn(EGL_BAD_MATCH, EGL_NO_SYNC_KHR); |
| } |
| |
| if (tInfo->currentContext->version == 2) { |
| s_display.gles2_iface()->finish(); |
| } else { |
| s_display.gles_iface()->finish(); |
| } |
| |
| return FENCE_SYNC_HANDLE; |
| } |
| |
| EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) |
| { |
| if (sync != FENCE_SYNC_HANDLE) { |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| } |
| |
| return EGL_TRUE; |
| } |
| |
| EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, |
| EGLTimeKHR timeout) |
| { |
| if (sync != FENCE_SYNC_HANDLE) { |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| } |
| |
| return EGL_CONDITION_SATISFIED_KHR; |
| } |
| |
| EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, |
| EGLint attribute, EGLint *value) |
| { |
| if (sync != FENCE_SYNC_HANDLE) { |
| setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE); |
| } |
| |
| switch (attribute) { |
| case EGL_SYNC_TYPE_KHR: |
| *value = EGL_SYNC_FENCE_KHR; |
| return EGL_TRUE; |
| case EGL_SYNC_STATUS_KHR: |
| *value = EGL_SIGNALED_KHR; |
| return EGL_TRUE; |
| case EGL_SYNC_CONDITION_KHR: |
| *value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR; |
| return EGL_TRUE; |
| default: |
| setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE); |
| } |
| } |