http://www.dre.vanderbilt.edu/~schmidt/android/android-4.0/development/tools/emulator/opengl/system/egl/egl.cpp
1> 和本地窗口系统(native windowing system)通讯;
2> 查询可用的配置;
3> 创建OpenGL ES可用的“绘图表面”(drawing surface);
4> 同步不同类别的API之间的渲染,比如在OpenGL ES和OpenVG之间同步,或者在OpenGL和本地窗口的绘图命令之间;
5> 管理“渲染资源”,比如纹理映射(rendering map)。
● EGLDisplay
EGL可运行于GNU/Linux的X Window System,Microsoft Windows和MacOS X的Quartz。
EGL把这些平台的显示系统抽象为一个独立的类型:EGLDisplay。
使用EGL的第一步就是初始化一个可用的EGLDisplay:
这里用到了三个EGL函数:
● EGLConfig
初始化过后,要选择一个合适的“绘图表面”。
用例:
可以查询某个配置的某个属性:
让EGL为你选择一个配置:
EGL如果选择了多个配置给你,则按一定规则放到数组里:
1> EGL_CONFIG_CAVEAT
2> EGL_COLOR_BUFFER_TYPE
3> 按color buffer所占位宽
4> EGL_BUFFER_SIZE
5> EGL_SAMPLE_BUFFERS
6> EGL_SAMPLES
7> EGL_DEPTH_SIZE
8> EGL_STENCIL_SIZE
9> EGL_ALPHA_MASK_SIZE
10> EGL_NATIVE_VISUAL_TYPE
11> EGL_CONFIG_ID
● EGLSurface
这里的属性表并非用于OpenGL ES 2.0,而是其它的API,比如OpenVG。我们只需要记住一个:EGL_RENDER_BUFFER [EGL_BACK_BUFFER, EGL_FRONT_BUFFER]。
OpenGL ES 2.0是必须工作于双缓冲窗口系统的。
该属性表当然也可以为NULL,也可以只有一个EGL_NONE。那表示所有属性使用默认值。
如果函数返回EGL_NO_SURFACE,则失败。错误码:
EGL_BAD_MATCH: 属性设置错误。比如EGL_SURFACE_TYPE没有设置EGL_WINDOW_BIT
EGL_BAD_CONFIG: 因为配置错误,图形系统不支持
EGL_BAD_NATIVE_WINDOW: 窗口句柄错误
EGL_BAD_ALLOC: 无法创建绘图表面。比如先前已经创建一个了。
● pixel buffer
OpenGL ES 2.0可以向pixel buffer渲染,同样使用硬件加速。pbuffer经常用来生成纹理映射。如果想渲染到纹理,常用更高效的framebuffer对象。
在EGL_SURFACE_TYPE里使用使用EGL_PBUFFER_BIT可创建pbuffer:
使用到的属性:
EGL_WIDTH, EGL_HEIGHT
EGL_LARGEST_PBUFFER: 如果参数不合适,可使用最大的pbuffer
EGL_TEXTURE_FORMAT: [EGL_NO_TEXTURE] 如果pbuffer要绑定到纹理映射,要指定纹理的格式
EGL_TEXTURE_TARGET: [EGL_NO_TEXTURE, EGL_TEXTURE_2D]
EGL_MIPMAP_TEXTRUE: [EGL_TRUE, EGL_FALSE]
创建失败时返回EGL_NO_SURFACE,错误码:
EGL_BAD_ALLOC: 缺少资源
EGL_BAD_CONFIG: 配置错误
EGL_BAD_PARAMETER: EGL_WIDTH和EGL_HEIGHT为负数
EGL_BAD_MATCH: 配置错误;如果用于纹理映射,则高宽参数错误;EGL_TEXTURE_FORMAT和EGL_TEXTURE_TARGET只有一个不是EGL_NO_TEXTURE
EGL_BAD_ATTRIBUTE: 指定了EGL_TEXTURE_FORMAT、EGL_TEXTURE_TARGET或者EGL_MIPMAP_TEXTRUE,却不指定使用OpenGLES在配置里
使用pbuffer的例子:
pbuffer和普通的窗口渲染最大的不同是不能swap,要么拷贝其值,要么修改其绑定成为纹理。
● EGLContext
● 渲染同步
只使用OpenGL ES 2.0,那么,glFinish即可保证所有的渲染工作进行下去。
但使用OpenVG或本地图形API渲染字体,要比使用OpenGL ES 2.0要容易。所以,你可能要在同一个窗口使用多个库来渲染。
可以用EGL的同步函数:EGLBoolean eglWaitClient() 延迟客户端的执行,等待服务器端完成OpenGL ES 2.0或者OpenVG的渲染。
如果失败,返回错误码:EGL_BAD_CURRENT_SURFACE。
如果要等待本地图形API的渲染完成,使用:EGLBoolean eglWaitNative(EGLint engine)。
engine参数必须是EGL_CORE_NATIVE_ENGINE。其它值都是通过EGL扩展来指定。
如果失败,返回错误码:EGL_BAD_PARAMETER。
/* * 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#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 template 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) \ { \ LOGE("tid %d: %s(%d): error 0x%x (%s)", gettid(), __FUNCTION__, __LINE__, error, eglStrError(error)); \ return setErrorFunc(error, retVal); \ } #define RETURN_ERROR(ret,err) \ LOGE("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) { \ LOGE("egl: Failed to get host connection\n"); \ return ret; \ } \ renderControl_encoder_context_t *rcEnc = hostCon->rcEncoder(); \ if (!rcEnc) { \ LOGE("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 (surface) ); \ if (!s->isValid()) \ setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); \ 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 EGLBoolean rcCreate() = 0; virtual EGLBoolean rcDestroy() = 0; virtual EGLBoolean connect() { return EGL_TRUE; } virtual void disconnect() {} virtual EGLBoolean swapBuffers() { return EGL_TRUE; } virtual EGLint getSwapBehavior() const; void setRcSurface(uint32_t handle){ rcSurface = handle; } uint32_t getRcSurface(){ return rcSurface; } virtual EGLBoolean isValid(){ return valid; } EGLint getSurfaceType(){ return surfaceType; } void setWidth(EGLint w){ width = w; } EGLint getWidth(){ return width; } void setHeight(EGLint h){ height = h; } 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: EGLint surfaceType; EGLBoolean valid; 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), valid(EGL_FALSE), 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 { egl_window_surface_t( EGLDisplay dpy, EGLConfig config, EGLint surfType, ANativeWindow* window); ~egl_window_surface_t(); virtual EGLBoolean rcCreate(); virtual EGLBoolean rcDestroy(); virtual EGLBoolean connect(); virtual void disconnect(); virtual EGLBoolean swapBuffers(); private: 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); } egl_window_surface_t::~egl_window_surface_t() { nativeWindow->common.decRef(&nativeWindow->common); } EGLBoolean egl_window_surface_t::rcCreate() { DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uint32_t)config, getWidth(), getHeight()); if (!rcSurface) { LOGE("rcCreateWindowSurface returned 0"); return EGL_FALSE; } valid = EGL_TRUE; return EGL_TRUE; } EGLBoolean egl_window_surface_t::rcDestroy() { if (!rcSurface) { LOGE("rcDestroy called on invalid rcSurface"); return EGL_FALSE; } DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface); rcSurface = 0; return EGL_TRUE; } EGLBoolean egl_window_surface_t::connect() { // dequeue a buffer if (nativeWindow->dequeueBuffer(nativeWindow, &buffer) != NO_ERROR) { setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE); } buffer->common.incRef(&buffer->common); // lock the buffer nativeWindow->lockBuffer(nativeWindow, buffer); DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, ((cb_handle_t *)(buffer->handle))->hostHandle); return EGL_TRUE; } void egl_window_surface_t::disconnect() { if (buffer) { nativeWindow->queueBuffer(nativeWindow, buffer); buffer->common.decRef(&buffer->common); buffer = 0; } } EGLBoolean egl_window_surface_t::swapBuffers() { if (!buffer) { setErrorReturn(EGL_BAD_ACCESS, EGL_FALSE); } DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcEnc->rcFlushWindowColorBuffer(rcEnc, rcSurface); android_native_buffer_t* prevBuf = buffer; //post the back buffer nativeWindow->queueBuffer(nativeWindow, buffer); buffer->common.incRef(&buffer->common); if (prevBuf) { prevBuf->common.decRef(&prevBuf->common); } // dequeue a new buffer if (nativeWindow->dequeueBuffer(nativeWindow, &buffer)) { setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE); } // lock the buffer nativeWindow->lockBuffer(nativeWindow, buffer); 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 { GLenum format; egl_pbuffer_surface_t( EGLDisplay dpy, EGLConfig config, EGLint surfType, int32_t w, int32_t h, GLenum format); virtual ~egl_pbuffer_surface_t(); virtual EGLBoolean rcCreate(); virtual EGLBoolean rcDestroy(); virtual EGLBoolean connect(); uint32_t getRcColorBuffer(){ return rcColorBuffer; } void setRcColorBuffer(uint32_t colorBuffer){ rcColorBuffer = colorBuffer; } private: uint32_t rcColorBuffer; }; egl_pbuffer_surface_t::egl_pbuffer_surface_t( EGLDisplay dpy, EGLConfig config, EGLint surfType, int32_t w, int32_t h, GLenum pixelFormat) : egl_surface_t(dpy, config, surfType), format(pixelFormat) { setWidth(w); setHeight(h); } egl_pbuffer_surface_t::~egl_pbuffer_surface_t() { rcColorBuffer = 0; } EGLBoolean egl_pbuffer_surface_t::rcCreate() { DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uint32_t)config, getWidth(), getHeight()); if (!rcSurface) { LOGE("rcCreateWindowSurface returned 0"); return EGL_FALSE; } rcColorBuffer = rcEnc->rcCreateColorBuffer(rcEnc, getWidth(), getHeight(), format); if (!rcColorBuffer) { LOGE("rcCreateColorBuffer returned 0"); return EGL_FALSE; } valid = EGL_TRUE; return EGL_TRUE; } EGLBoolean egl_pbuffer_surface_t::rcDestroy() { if ((!rcSurface)||(!rcColorBuffer)) { LOGE("destroyRc called on invalid rcSurface"); return EGL_FALSE; } DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcEnc->rcDestroyWindowSurface(rcEnc, rcSurface); rcEnc->rcDestroyColorBuffer(rcEnc, rcColorBuffer); rcSurface = 0; return EGL_TRUE; } EGLBoolean egl_pbuffer_surface_t::connect() { DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE); rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface, rcColorBuffer); return EGL_TRUE; } 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 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 (win)->common.magic != ANDROID_NATIVE_WINDOW_MAGIC) { setErrorReturn(EGL_BAD_NATIVE_WINDOW, EGL_NO_SURFACE); } egl_surface_t* surface; surface = new egl_window_surface_t(&s_display, config, surfaceType, static_cast (win)); if (!surface) setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE); if (!surface->rcCreate()) { delete 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 = new egl_pbuffer_surface_t(dpy, config, surfaceType, w, h, pixelFormat); if (!surface) setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE); if (!surface->rcCreate()) { delete 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 (eglSurface) ); surface->disconnect(); surface->rcDestroy(); 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 (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: LOGE("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 LOGW("%s not implemented", __FUNCTION__); return 0; } EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value) { //TODO LOGW("%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 (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 LOGW("%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); 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 (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) { LOGE("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 (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 (ctx); uint32_t ctxHandle = (context) ? context->rcContext : 0; egl_surface_t * drawSurf = static_cast (draw); uint32_t drawHandle = (drawSurf) ? drawSurf->getRcSurface() : 0; egl_surface_t * readSurf = static_cast (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) { LOGE("rcMakeCurrent returned EGL_FALSE"); setErrorReturn(EGL_BAD_CONTEXT, EGL_FALSE); } // // Disconnect from the previous drawable // if (tInfo->currentContext && tInfo->currentContext->draw) { egl_surface_t * prevDrawSurf = static_cast (tInfo->currentContext->draw); prevDrawSurf->disconnect(); } //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); } } } //connect the color buffer if (drawSurf) drawSurf->connect(); 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 (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: LOGE("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 (eglSurface); if (!d->isValid()) setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE); 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); switch (native_buffer->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; } EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type, const EGLint *attrib_list) { //TODO later return 0; } EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync) { //TODO later return 0; } EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags, EGLTimeKHR timeout) { //TODO return 0; } EGLBoolean eglSignalSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLenum mode) { //TODO later return 0; } EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint attribute, EGLint *value) { //TODO later return 0; } EGLBoolean eglSetSwapRectangleANDROID(EGLDisplay dpy, EGLSurface draw, EGLint left, EGLint top, EGLint width, EGLint height) { //TODO later return 0; }