* limitations under the License.
*/
+#include <assert.h>
#include "HostConnection.h"
#include "ThreadInfo.h"
#include "eglDisplay.h"
+#include "eglSync.h"
#include "egl_ftable.h"
#include <cutils/log.h>
+#include <cutils/properties.h>
+#include "goldfish_sync.h"
#include "gralloc_cb.h"
#include "GLClientState.h"
#include "GLSharedGroup.h"
#include "eglContext.h"
#include "ClientAPIExts.h"
+#include "EGLImage.h"
+#include "ProcessPipe.h"
#include "GLEncoder.h"
#ifdef WITH_GLES2
#include "GL2Encoder.h"
#endif
+#include <GLES3/gl31.h>
+
+#if PLATFORM_SDK_VERSION >= 16
#include <system/window.h>
+#else // PLATFORM_SDK_VERSION >= 16
+#include <private/ui/android_natives_priv.h>
+#endif // PLATFORM_SDK_VERSION >= 16
+
+#if PLATFORM_SDK_VERSION <= 16
+#define queueBuffer_DEPRECATED queueBuffer
+#define dequeueBuffer_DEPRECATED dequeueBuffer
+#define cancelBuffer_DEPRECATED cancelBuffer
+#endif // PLATFORM_SDK_VERSION <= 16
+
+#define DEBUG_EGL 0
+
+#if DEBUG_EGL
+#define DPRINT(fmt,...) ALOGD("%s: " fmt, __FUNCTION__, ##__VA_ARGS__);
+#else
+#define DPRINT(...)
+#endif
template<typename T>
static T setErrorFunc(GLint error, T returnValue) {
#endif //LOG_EGL_ERRORS
#define VALIDATE_CONFIG(cfg,ret) \
- if(((intptr_t)cfg<0)||((intptr_t)cfg>s_display.getNumConfigs())) { \
+ if(((intptr_t)(cfg)<0)||((intptr_t)(cfg)>s_display.getNumConfigs())) { \
RETURN_ERROR(ret,EGL_BAD_CONFIG); \
}
#define DEFINE_HOST_CONNECTION \
HostConnection *hostCon = HostConnection::get(); \
- renderControl_encoder_context_t *rcEnc = (hostCon ? hostCon->rcEncoder() : NULL)
+ ExtendedRCEncoderContext *rcEnc = (hostCon ? hostCon->rcEncoder() : NULL)
#define DEFINE_AND_VALIDATE_HOST_CONNECTION(ret) \
HostConnection *hostCon = HostConnection::get(); \
ALOGE("egl: Failed to get host connection\n"); \
return ret; \
} \
- renderControl_encoder_context_t *rcEnc = hostCon->rcEncoder(); \
+ ExtendedRCEncoderContext *rcEnc = hostCon->rcEncoder(); \
if (!rcEnc) { \
ALOGE("egl: Failed to get renderControl encoder context\n"); \
return ret; \
}
-#define VALIDATE_CONTEXT_RETURN(context,ret) \
- if (!context) { \
+#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) { \
+ 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); \
}
+// The one and only supported display object.
+static eglDisplay s_display;
-EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* shareCtx) :
+EGLContext_t::EGLContext_t(EGLDisplay dpy, EGLConfig config, EGLContext_t* shareCtx, int maj, int min) :
dpy(dpy),
config(config),
read(EGL_NO_SURFACE),
shareCtx(shareCtx),
rcContext(0),
versionString(NULL),
+ majorVersion(maj),
+ minorVersion(min),
vendorString(NULL),
rendererString(NULL),
+ shaderVersionString(NULL),
extensionString(NULL),
- deletePending(0)
+ deletePending(0),
+ goldfishSyncFd(-1)
{
flags = 0;
- version = 1;
- clientState = new GLClientState();
- if (shareCtx)
+ clientState = new GLClientState(majorVersion, minorVersion);
+ if (shareCtx)
sharedGroup = shareCtx->getSharedGroup();
else
sharedGroup = GLSharedGroupPtr(new GLSharedGroup());
+ assert(dpy == (EGLDisplay)&s_display);
+ s_display.onCreateContext((EGLContext)this);
};
+int EGLContext_t::getGoldfishSyncFd() {
+ if (goldfishSyncFd < 0) {
+ goldfishSyncFd = goldfish_sync_open();
+ }
+ return goldfishSyncFd;
+}
+
EGLContext_t::~EGLContext_t()
{
+ if (goldfishSyncFd > 0) {
+ goldfish_sync_close(goldfishSyncFd);
+ goldfishSyncFd = -1;
+ }
+ assert(dpy == (EGLDisplay)&s_display);
+ s_display.onDestroyContext((EGLContext)this);
delete clientState;
delete [] versionString;
delete [] vendorString;
delete [] rendererString;
+ delete [] shaderVersionString;
delete [] extensionString;
}
EGLint getWidth(){ return width; }
EGLint getHeight(){ return height; }
+ EGLint getNativeWidth(){ return nativeWidth; }
+ EGLint getNativeHeight(){ return nativeHeight; }
void setTextureFormat(EGLint _texFormat) { texFormat = _texFormat; }
EGLint getTextureFormat() { return texFormat; }
void setTextureTarget(EGLint _texTarget) { texTarget = _texTarget; }
EGLint texFormat;
EGLint texTarget;
+ // Width of the actual window being presented (not the EGL texture)
+ // Give it some default values.
+ int nativeWidth;
+ int nativeHeight;
+
protected:
void setWidth(EGLint w) { width = w; }
void setHeight(EGLint h) { height = h; }
+ void setNativeWidth(int w) { nativeWidth = w; }
+ void setNativeHeight(int h) { nativeHeight = h; }
EGLint surfaceType;
uint32_t rcSurface; //handle to surface created via remote control
{
width = 0;
height = 0;
+ // prevent div by 0 in EGL_(HORIZONTAL|VERTICAL)_RESOLUTION queries.
+ nativeWidth = 1;
+ nativeHeight = 1;
texFormat = EGL_NO_TEXTURE;
texTarget = EGL_NO_TEXTURE;
+ assert(dpy == (EGLDisplay)&s_display);
+ s_display.onCreateSurface((EGLSurface)this);
}
EGLint egl_surface_t::getSwapBehavior() const {
egl_surface_t::~egl_surface_t()
{
+ assert(dpy == (EGLDisplay)&s_display);
+ s_display.onDestroySurface((EGLSurface)this);
}
// ----------------------------------------------------------------------------
{
// 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);
}
+ setWidth(buffer->width);
+ setHeight(buffer->height);
+
+ int nativeWidth, nativeHeight;
+ nativeWindow->query(nativeWindow, NATIVE_WINDOW_WIDTH, &nativeWidth);
+ nativeWindow->query(nativeWindow, NATIVE_WINDOW_HEIGHT, &nativeHeight);
+
+ setNativeWidth(nativeWidth);
+ setNativeHeight(nativeHeight);
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
rcSurface = rcEnc->rcCreateWindowSurface(rcEnc, (uintptr_t)config,
nativeWindow->setSwapInterval(nativeWindow, interval);
}
+// createNativeSync() creates an OpenGL sync object on the host
+// using rcCreateSyncKHR. If necessary, a native fence FD will
+// also be created through the goldfish sync device.
+// Returns a handle to the host-side FenceSync object.
+static uint64_t createNativeSync(EGLenum type,
+ const EGLint* attrib_list,
+ int num_actual_attribs,
+ bool destroy_when_signaled,
+ int fd_in,
+ int* fd_out) {
+ DEFINE_HOST_CONNECTION;
+
+ uint64_t sync_handle;
+ uint64_t thread_handle;
+
+ EGLint* actual_attribs =
+ (EGLint*)(num_actual_attribs == 0 ? NULL : attrib_list);
+
+ rcEnc->rcCreateSyncKHR(rcEnc, type,
+ actual_attribs,
+ num_actual_attribs * sizeof(EGLint),
+ destroy_when_signaled,
+ &sync_handle,
+ &thread_handle);
+
+ if (type == EGL_SYNC_NATIVE_FENCE_ANDROID && fd_in < 0) {
+ int queue_work_err =
+ goldfish_sync_queue_work(
+ getEGLThreadInfo()->currentContext->getGoldfishSyncFd(),
+ sync_handle,
+ thread_handle,
+ fd_out);
+
+ DPRINT("got native fence fd=%d queue_work_err=%d",
+ *fd_out, queue_work_err);
+ }
+
+ return sync_handle;
+}
+
+// createGoldfishOpenGLNativeSync() is for creating host-only sync objects
+// that are needed by only this goldfish opengl driver,
+// such as in swapBuffers().
+// The guest will not see any of these, and these sync objects will be
+// destroyed on the host when signaled.
+// A native fence FD is possibly returned.
+static void createGoldfishOpenGLNativeSync(int* fd_out) {
+ createNativeSync(EGL_SYNC_NATIVE_FENCE_ANDROID,
+ NULL /* empty attrib list */,
+ 0 /* 0 attrib count */,
+ true /* destroy when signaled. this is host-only
+ and there will only be one waiter */,
+ -1 /* we want a new fd */,
+ fd_out);
+}
+
EGLBoolean egl_window_surface_t::swapBuffers()
{
+
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
+ // Follow up flushWindowColorBuffer with a fence command.
+ // When the fence command finishes,
+ // we're sure that the buffer on the host
+ // has been blitted.
+ //
+ // |presentFenceFd| guards the presentation of the
+ // current frame with a goldfish sync fence fd.
+ //
+ // When |presentFenceFd| is signaled, the recipient
+ // of the buffer that was sent through queueBuffer
+ // can be sure that the buffer is current.
+ //
+ // If we don't take care of this synchronization,
+ // an old frame can be processed by surfaceflinger,
+ // resulting in out of order frames.
+
+ int presentFenceFd = -1;
+
+ if (buffer == NULL) {
+ ALOGE("egl_window_surface_t::swapBuffers called with NULL buffer");
+ setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
+#if PLATFORM_SDK_VERSION <= 16
rcEnc->rcFlushWindowColorBuffer(rcEnc, rcSurface);
+ // equivalent to glFinish if no native sync
+ eglWaitClient();
+ nativeWindow->queueBuffer(nativeWindow, buffer);
+#else
+ if (rcEnc->hasNativeSync()) {
+ rcEnc->rcFlushWindowColorBufferAsync(rcEnc, rcSurface);
+ createGoldfishOpenGLNativeSync(&presentFenceFd);
+ } else {
+ rcEnc->rcFlushWindowColorBuffer(rcEnc, rcSurface);
+ // equivalent to glFinish if no native sync
+ eglWaitClient();
+ }
+
+ DPRINT("queueBuffer with fence %d", presentFenceFd);
+ nativeWindow->queueBuffer(nativeWindow, buffer, presentFenceFd);
+#endif
+
+ DPRINT("calling dequeueBuffer...");
- nativeWindow->queueBuffer_DEPRECATED(nativeWindow, buffer);
- if (nativeWindow->dequeueBuffer_DEPRECATED(nativeWindow, &buffer)) {
+#if PLATFORM_SDK_VERSION <= 16
+ if (nativeWindow->dequeueBuffer(nativeWindow, &buffer)) {
buffer = NULL;
- setErrorReturn(EGL_BAD_ALLOC, EGL_FALSE);
+ setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+#else
+ int acquireFenceFd = -1;
+ if (nativeWindow->dequeueBuffer(nativeWindow, &buffer, &acquireFenceFd)) {
+ buffer = NULL;
+ setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
}
+ DPRINT("dequeueBuffer with fence %d", acquireFenceFd);
+
+ if (acquireFenceFd > 0) {
+ close(acquireFenceFd);
+ }
+#endif
+
rcEnc->rcSetWindowColorBuffer(rcEnc, rcSurface,
((cb_handle_t *)(buffer->handle))->hostHandle);
+ setWidth(buffer->width);
+ setHeight(buffer->height);
+
return EGL_TRUE;
}
return EGL_FALSE;
}
- rcColorBuffer = rcEnc->rcCreateColorBuffer(rcEnc, getWidth(), getHeight(),
- pixelFormat);
+ rcColorBuffer = rcEnc->rcCreateColorBuffer(rcEnc, getWidth(), getHeight(), pixelFormat);
if (!rcColorBuffer) {
ALOGE("rcCreateColorBuffer returned 0");
return EGL_FALSE;
#define GL_VENDOR 0x1F00
#define GL_RENDERER 0x1F01
#define GL_VERSION 0x1F02
+#define GL_SHADING_LANGUAGE_VERSION 0x8B8C
#define GL_EXTENSIONS 0x1F03
switch(glEnum) {
case GL_RENDERER:
strPtr = &tInfo->currentContext->rendererString;
break;
+ case GL_SHADING_LANGUAGE_VERSION:
+ strPtr = &tInfo->currentContext->shaderVersionString;
+ break;
case GL_EXTENSIONS:
strPtr = &tInfo->currentContext->extensionString;
break;
return NULL;
}
- if (*strPtr != NULL) {
- //
- // string is already cached
- //
- return *strPtr;
- }
-
//
// first query of that string - need to query host
//
// ----------------------------------------------------------------------------
-// The one and only supported display object.
-static eglDisplay s_display;
-
static EGLClient_eglInterface s_eglIface = {
getThreadInfo: getEGLThreadInfo,
getGLString: getGLString
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
+ if (!num_config) {
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+ }
+
int attribs_size = 0;
if (attrib_list) {
const EGLint * attrib_p = attrib_list;
attribs_size++; //for the terminating EGL_NONE
}
+ // API 19 passes EGL_SWAP_BEHAVIOR_PRESERVED_BIT to surface type,
+ // while the host never supports it.
+ // We remove the bit here.
+ EGLint* local_attrib_list = NULL;
+ if (PLATFORM_SDK_VERSION <= 19) {
+ local_attrib_list = new EGLint[attribs_size];
+ memcpy(local_attrib_list, attrib_list, attribs_size * sizeof(EGLint));
+ EGLint* local_attrib_p = local_attrib_list;
+ while (local_attrib_p[0] != EGL_NONE) {
+ if (local_attrib_p[0] == EGL_SURFACE_TYPE) {
+ local_attrib_p[1] &= ~(EGLint)EGL_SWAP_BEHAVIOR_PRESERVED_BIT;
+ }
+ local_attrib_p += 2;
+ }
+ }
+
uint32_t* tempConfigs[config_size];
DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
- *num_config = rcEnc->rcChooseConfig(rcEnc, (EGLint*)attrib_list, attribs_size * sizeof(EGLint), (uint32_t*)tempConfigs, config_size);
+ *num_config = rcEnc->rcChooseConfig(rcEnc,
+ local_attrib_list ? local_attrib_list:(EGLint*)attrib_list,
+ attribs_size * sizeof(EGLint), (uint32_t*)tempConfigs, config_size);
+
+ if (local_attrib_list) delete [] local_attrib_list;
+ if (*num_config <= 0) {
+ EGLint err = -(*num_config);
+ *num_config = 0;
+ switch (err) {
+ case EGL_BAD_ATTRIBUTE:
+ setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+ default:
+ return EGL_FALSE;
+ }
+ }
+
if (configs!=NULL) {
EGLint i=0;
for (i=0;i<(*num_config);i++) {
}
}
- if (*num_config <= 0)
- return EGL_FALSE;
return EGL_TRUE;
}
}
else
{
+ ALOGD("%s: bad attrib 0x%x", __FUNCTION__, attribute);
RETURN_ERROR(EGL_FALSE, EGL_BAD_ATTRIBUTE);
}
}
}
egl_surface_t* surface = egl_window_surface_t::create(
- &s_display, config, surfaceType, static_cast<ANativeWindow*>(win));
+ &s_display, config, EGL_WINDOW_BIT, static_cast<ANativeWindow*>(win));
if (!surface) {
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE);
}
int32_t h = 0;
EGLint texFormat = EGL_NO_TEXTURE;
EGLint texTarget = EGL_NO_TEXTURE;
- while (attrib_list[0]) {
+ while (attrib_list[0] != EGL_NONE) {
switch (attrib_list[0]) {
case EGL_WIDTH:
w = attrib_list[1];
+ if (w < 0) setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
break;
case EGL_HEIGHT:
h = attrib_list[1];
+ if (h < 0) setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_SURFACE);
break;
case EGL_TEXTURE_FORMAT:
texFormat = attrib_list[1];
case EGL_TEXTURE_TARGET:
texTarget = attrib_list[1];
break;
- default:
+ // the followings are not supported
+ case EGL_LARGEST_PBUFFER:
+ case EGL_MIPMAP_TEXTURE:
+ case EGL_VG_ALPHA_FORMAT:
+ case EGL_VG_COLORSPACE:
break;
+ default:
+ setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SURFACE);
};
attrib_list+=2;
}
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SURFACE);
egl_surface_t* surface = egl_pbuffer_surface_t::create(dpy, config,
- surfaceType, w, h, pixelFormat);
+ EGL_PBUFFER_BIT, w, h, pixelFormat);
if (!surface) {
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_SURFACE);
}
// 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 ...
+ VALIDATE_CONFIG(config, EGL_FALSE);
(void)dpy;
- (void)config;
(void)pixmap;
(void)attrib_list;
return EGL_NO_SURFACE;
return EGL_TRUE;
}
+static float s_getNativeDpi() {
+ float nativeDPI = 560.0f;
+ const char* dpiPropName = "qemu.sf.lcd_density";
+ char dpiProp[PROPERTY_VALUE_MAX];
+ if (property_get(dpiPropName, dpiProp, NULL) > 0) {
+ nativeDPI = atof(dpiProp);
+ }
+ return nativeDPI;
+}
+
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) );
+
+ // Parameters involved in queries of EGL_(HORIZONTAL|VERTICAL)_RESOLUTION
+ float currWidth, currHeight, scaledResolution, effectiveSurfaceDPI;
EGLBoolean ret = EGL_TRUE;
switch (attribute) {
case EGL_CONFIG_ID:
*value = surface->getHeight();
break;
case EGL_TEXTURE_FORMAT:
- *value = surface->getTextureFormat();
+ if (surface->getSurfaceType() & EGL_PBUFFER_BIT) {
+ *value = surface->getTextureFormat();
+ }
break;
case EGL_TEXTURE_TARGET:
- *value = surface->getTextureTarget();
+ if (surface->getSurfaceType() & EGL_PBUFFER_BIT) {
+ *value = surface->getTextureTarget();
+ }
break;
case EGL_SWAP_BEHAVIOR:
- *value = surface->getSwapBehavior();
+ {
+ EGLint surfaceType;
+ ret = s_display.getConfigAttrib(surface->config, EGL_SURFACE_TYPE,
+ &surfaceType);
+ if (ret == EGL_TRUE) {
+ if (surfaceType & EGL_SWAP_BEHAVIOR_PRESERVED_BIT) {
+ *value = EGL_BUFFER_PRESERVED;
+ } else {
+ *value = EGL_BUFFER_DESTROYED;
+ }
+ }
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;
+ case EGL_MIPMAP_TEXTURE:
+ // not modified for a window or pixmap surface
+ // and we ignore it when creating a PBuffer surface (default is 0)
+ if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = false;
+ break;
+ case EGL_MIPMAP_LEVEL:
+ // not modified for a window or pixmap surface
+ // and we ignore it when creating a PBuffer surface (default is 0)
+ if (surface->getSurfaceType() & EGL_PBUFFER_BIT) *value = 0;
+ break;
+ case EGL_MULTISAMPLE_RESOLVE:
+ // ignored when creating the surface, return default
+ *value = EGL_MULTISAMPLE_RESOLVE_DEFAULT;
+ break;
+ case EGL_HORIZONTAL_RESOLUTION:
+ // pixel/mm * EGL_DISPLAY_SCALING
+ // TODO: get the DPI from avd config
+ currWidth = surface->getWidth();
+ scaledResolution = currWidth / surface->getNativeWidth();
+ effectiveSurfaceDPI =
+ scaledResolution * s_getNativeDpi() * EGL_DISPLAY_SCALING;
+ *value = (EGLint)(effectiveSurfaceDPI);
+ break;
+ case EGL_VERTICAL_RESOLUTION:
+ // pixel/mm * EGL_DISPLAY_SCALING
+ // TODO: get the real DPI from avd config
+ currHeight = surface->getHeight();
+ scaledResolution = currHeight / surface->getNativeHeight();
+ effectiveSurfaceDPI =
+ scaledResolution * s_getNativeDpi() * EGL_DISPLAY_SCALING;
+ *value = (EGLint)(effectiveSurfaceDPI);
+ break;
+ case EGL_PIXEL_ASPECT_RATIO:
+ // w / h * EGL_DISPLAY_SCALING
+ // Please don't ask why * EGL_DISPLAY_SCALING, the document says it
+ *value = 1 * EGL_DISPLAY_SCALING;
+ break;
+ case EGL_RENDER_BUFFER:
+ switch (surface->getSurfaceType()) {
+ case EGL_PBUFFER_BIT:
+ *value = EGL_BACK_BUFFER;
+ break;
+ case EGL_PIXMAP_BIT:
+ *value = EGL_SINGLE_BUFFER;
+ break;
+ case EGL_WINDOW_BIT:
+ // ignored when creating the surface, return default
+ *value = EGL_BACK_BUFFER;
+ break;
+ default:
+ ALOGE("eglQuerySurface %x unknown surface type %x",
+ attribute, surface->getSurfaceType());
+ ret = setErrorFunc(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+ break;
+ }
+ break;
+ case EGL_VG_COLORSPACE:
+ // ignored when creating the surface, return default
+ *value = EGL_VG_COLORSPACE_sRGB;
+ break;
+ case EGL_VG_ALPHA_FORMAT:
+ // ignored when creating the surface, return default
+ *value = EGL_VG_ALPHA_FORMAT_NONPRE;
+ break;
//TODO: complete other attributes
default:
ALOGE("eglQuerySurface %x EGL_BAD_ATTRIBUTE", attribute);
EGLBoolean eglReleaseThread()
{
EGLThreadInfo *tInfo = getEGLThreadInfo();
- if (tInfo && tInfo->currentContext) {
- return eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE);
+ if (tInfo) {
+ tInfo->eglError = EGL_SUCCESS;
+ EGLContext_t* context = tInfo->currentContext;
+ if (context) {
+ // The following code is doing pretty much the same thing as
+ // eglMakeCurrent(&s_display, EGL_NO_CONTEXT, EGL_NO_SURFACE, EGL_NO_SURFACE)
+ // with the only issue that we do not require a valid display here.
+ DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
+ rcEnc->rcMakeCurrent(rcEnc, 0, 0, 0);
+ if (context->majorVersion > 1) {
+ hostCon->gl2Encoder()->setClientState(NULL);
+ hostCon->gl2Encoder()->setSharedGroup(GLSharedGroupPtr());
+ }
+ else {
+ hostCon->glEncoder()->setClientState(NULL);
+ hostCon->glEncoder()->setSharedGroup(GLSharedGroupPtr());
+ }
+ context->flags &= ~EGLContext_t::IS_CURRENT;
+
+ if (context->deletePending) {
+ if (context->rcContext) {
+ rcEnc->rcDestroyContext(rcEnc, context->rcContext);
+ context->rcContext = 0;
+ }
+ delete context;
+ }
+ tInfo->currentContext = 0;
+ }
}
return EGL_TRUE;
}
EGLBoolean eglSurfaceAttrib(EGLDisplay dpy, EGLSurface surface, EGLint attribute, EGLint value)
{
- //TODO
- (void)dpy;
- (void)surface;
- (void)attribute;
+ // Right now we don't do anything when using host GPU.
+ // This is purely just to pass the data through
+ // without issuing a warning. We may benefit from validating the
+ // display and surface for debug purposes.
+ // TODO: Find cases where we actually need to do something.
+ VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
+ VALIDATE_SURFACE_RETURN(surface, EGL_FALSE);
+ if (surface == EGL_NO_SURFACE) {
+ setErrorReturn(EGL_BAD_SURFACE, EGL_FALSE);
+ }
+
(void)value;
- ALOGW("%s not implemented", __FUNCTION__);
- return 0;
+
+ egl_surface_t* p_surface( static_cast<egl_surface_t*>(surface) );
+ switch (attribute) {
+ case EGL_MIPMAP_LEVEL:
+ return true;
+ break;
+ case EGL_MULTISAMPLE_RESOLVE:
+ {
+ if (value == EGL_MULTISAMPLE_RESOLVE_BOX) {
+ EGLint surface_type;
+ s_display.getConfigAttrib(p_surface->config, EGL_SURFACE_TYPE, &surface_type);
+ if (0 == (surface_type & EGL_MULTISAMPLE_RESOLVE_BOX_BIT)) {
+ setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
+ }
+ }
+ return true;
+ break;
+ }
+ case EGL_SWAP_BEHAVIOR:
+ if (value == EGL_BUFFER_PRESERVED) {
+ EGLint surface_type;
+ s_display.getConfigAttrib(p_surface->config, EGL_SURFACE_TYPE, &surface_type);
+ if (0 == (surface_type & EGL_SWAP_BEHAVIOR_PRESERVED_BIT)) {
+ setErrorReturn(EGL_BAD_MATCH, EGL_FALSE);
+ }
+ }
+ return true;
+ break;
+ default:
+ ALOGW("%s: attr=0x%x not implemented", __FUNCTION__, attribute);
+ setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
+ }
+ return false;
}
EGLBoolean eglBindTexImage(EGLDisplay dpy, EGLSurface eglSurface, EGLint buffer)
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];
+ EGLint majorVersion = 1; //default
+ EGLint minorVersion = 0;
+ EGLint context_flags = 0;
+ EGLint profile_mask = 0;
+ EGLint reset_notification_strategy = 0;
+
+ bool wantedMajorVersion = false;
+ bool wantedMinorVersion = false;
+
+ while (attrib_list && attrib_list[0] != EGL_NONE) {
+ EGLint attrib_val = attrib_list[1];
+ switch(attrib_list[0]) {
+ case EGL_CONTEXT_MAJOR_VERSION_KHR:
+ majorVersion = attrib_val;
+ wantedMajorVersion = true;
+ break;
+ case EGL_CONTEXT_MINOR_VERSION_KHR:
+ minorVersion = attrib_val;
+ wantedMinorVersion = true;
+ break;
+ case EGL_CONTEXT_FLAGS_KHR:
+ if ((attrib_val | EGL_CONTEXT_OPENGL_DEBUG_BIT_KHR) ||
+ (attrib_val | EGL_CONTEXT_OPENGL_FORWARD_COMPATIBLE_BIT_KHR) ||
+ (attrib_val | EGL_CONTEXT_OPENGL_ROBUST_ACCESS_BIT_KHR)) {
+ context_flags = attrib_val;
+ } else {
+ RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
+ }
+ break;
+ case EGL_CONTEXT_OPENGL_PROFILE_MASK_KHR:
+ if ((attrib_val | EGL_CONTEXT_OPENGL_CORE_PROFILE_BIT_KHR) ||
+ (attrib_val | EGL_CONTEXT_OPENGL_COMPATIBILITY_PROFILE_BIT_KHR)) {
+ profile_mask = attrib_val;
+ } else {
+ RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
+ }
+ break;
+ case EGL_CONTEXT_OPENGL_RESET_NOTIFICATION_STRATEGY_KHR:
+ switch (attrib_val) {
+ case EGL_NO_RESET_NOTIFICATION_KHR:
+ case EGL_LOSE_CONTEXT_ON_RESET_KHR:
+ break;
+ default:
+ RETURN_ERROR(EGL_NO_CONTEXT,EGL_BAD_ATTRIBUTE);
+ }
+ reset_notification_strategy = attrib_val;
+ break;
+ default:
+ setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_CONTEXT);
+ }
attrib_list+=2;
}
+ if (!wantedMajorVersion) {
+ majorVersion = 1;
+ }
+
+ // Support up to GLES 3.2 depending on advertised version from the host system.
+ DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_CONTEXT);
+ switch (majorVersion) {
+ case 1:
+ case 2:
+ break;
+ case 3:
+ if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_0) {
+ ALOGE("%s: EGL_BAD_CONFIG: no ES 3 support", __FUNCTION__);
+ setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
+ }
+ switch (minorVersion) {
+ case 0:
+ break;
+ case 1:
+ if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_1) {
+ ALOGE("%s: EGL_BAD_CONFIG: no ES 3.1 support", __FUNCTION__);
+ setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
+ }
+ break;
+ case 2:
+ if (rcEnc->getGLESMaxVersion() < GLES_MAX_VERSION_3_2) {
+ ALOGE("%s: EGL_BAD_CONFIG: no ES 3.2 support", __FUNCTION__);
+ setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
+ }
+ break;
+ default:
+ ALOGE("%s: EGL_BAD_CONFIG: Unknown ES version %d.%d",
+ __FUNCTION__, majorVersion, minorVersion);
+ setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
+ }
+ break;
+ default:
+ setErrorReturn(EGL_BAD_CONFIG, EGL_NO_CONTEXT);
+ }
+
uint32_t rcShareCtx = 0;
EGLContext_t * shareCtx = NULL;
if (share_context) {
setErrorReturn(EGL_BAD_MATCH, EGL_NO_CONTEXT);
}
- DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_CONTEXT);
- uint32_t rcContext = rcEnc->rcCreateContext(rcEnc, (uintptr_t)config, rcShareCtx, version);
+ // We've created EGL context. Disconnecting
+ // would be dangerous at this point.
+ hostCon->setGrallocOnly(false);
+
+ int rcMajorVersion = majorVersion;
+ if (majorVersion == 3 && minorVersion == 1) {
+ rcMajorVersion = 4;
+ }
+ if (majorVersion == 3 && minorVersion == 2) {
+ rcMajorVersion = 4;
+ }
+ uint32_t rcContext = rcEnc->rcCreateContext(rcEnc, (uintptr_t)config, rcShareCtx, rcMajorVersion);
if (!rcContext) {
ALOGE("rcCreateContext returned 0");
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
}
- EGLContext_t * context = new EGLContext_t(dpy, config, shareCtx);
- if (!context)
+ EGLContext_t * context = new EGLContext_t(dpy, config, shareCtx, majorVersion, minorVersion);
+ ALOGD("%s: %p: maj %d min %d rcv %d", __FUNCTION__, context, majorVersion, minorVersion, rcMajorVersion);
+ if (!context) {
+ ALOGE("could not alloc egl context!");
setErrorReturn(EGL_BAD_ALLOC, EGL_NO_CONTEXT);
+ }
- context->version = version;
context->rcContext = rcContext;
-
-
return context;
}
//Now make the local bind
if (context) {
+
+ ALOGD("%s: %p: ver %d %d", __FUNCTION__, context, context->majorVersion, context->minorVersion);
+ // This is a nontrivial context.
+ // The thread cannot be gralloc-only anymore.
+ hostCon->setGrallocOnly(false);
context->draw = draw;
context->read = read;
context->flags |= EGLContext_t::IS_CURRENT;
+ GLClientState* contextState =
+ context->getClientState();
+
+ if (!hostCon->gl2Encoder()->isInitialized()) {
+ s_display.gles2_iface()->init();
+ hostCon->gl2Encoder()->setInitialized();
+ ClientAPIExts::initClientFuncs(s_display.gles2_iface(), 1);
+ }
+ if (contextState->needsInitFromCaps()) {
+ // Get caps for indexed buffers from host.
+ // Some need a current context.
+ int max_transform_feedback_separate_attribs = 0;
+ int max_uniform_buffer_bindings = 0;
+ int max_atomic_counter_buffer_bindings = 0;
+ int max_shader_storage_buffer_bindings = 0;
+ int max_vertex_attrib_bindings = 0;
+ int max_color_attachments = 1;
+ int max_draw_buffers = 1;
+ if (context->majorVersion > 2) {
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_TRANSFORM_FEEDBACK_SEPARATE_ATTRIBS, &max_transform_feedback_separate_attribs);
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_UNIFORM_BUFFER_BINDINGS, &max_uniform_buffer_bindings);
+ if (context->minorVersion > 0) {
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_ATOMIC_COUNTER_BUFFER_BINDINGS, &max_atomic_counter_buffer_bindings);
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_SHADER_STORAGE_BUFFER_BINDINGS, &max_shader_storage_buffer_bindings);
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_VERTEX_ATTRIB_BINDINGS, &max_vertex_attrib_bindings);
+ }
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_COLOR_ATTACHMENTS, &max_color_attachments);
+ s_display.gles2_iface()->getIntegerv(
+ GL_MAX_DRAW_BUFFERS, &max_draw_buffers);
+ }
+ contextState->initFromCaps(
+ max_transform_feedback_separate_attribs,
+ max_uniform_buffer_bindings,
+ max_atomic_counter_buffer_bindings,
+ max_shader_storage_buffer_bindings,
+ max_vertex_attrib_bindings,
+ max_color_attachments,
+ max_draw_buffers);
+ }
+
//set the client state
- if (context->version == 2) {
- hostCon->gl2Encoder()->setClientState(context->getClientState());
+ if (context->majorVersion > 1) {
+ hostCon->gl2Encoder()->setClientStateMakeCurrent(
+ contextState,
+ context->majorVersion,
+ context->minorVersion);
hostCon->gl2Encoder()->setSharedGroup(context->getSharedGroup());
}
else {
hostCon->glEncoder()->setClientState(context->getClientState());
hostCon->glEncoder()->setSharedGroup(context->getSharedGroup());
}
- }
+ }
else if (tInfo->currentContext) {
//release ClientState & SharedGroup
- if (tInfo->currentContext->version == 2) {
+ if (tInfo->currentContext->majorVersion > 1) {
hostCon->gl2Encoder()->setClientState(NULL);
hostCon->gl2Encoder()->setSharedGroup(GLSharedGroupPtr(NULL));
}
//Check maybe we need to init the encoder, if it's first eglMakeCurrent
if (tInfo->currentContext) {
- if (tInfo->currentContext->version == 2) {
+ if (tInfo->currentContext->majorVersion > 1) {
if (!hostCon->gl2Encoder()->isInitialized()) {
s_display.gles2_iface()->init();
hostCon->gl2Encoder()->setInitialized();
*value = EGL_OPENGL_ES_API;
break;
case EGL_CONTEXT_CLIENT_VERSION:
- *value = context->version;
+ *value = context->majorVersion;
break;
case EGL_RENDER_BUFFER:
if (!context->draw)
return EGL_FALSE;
}
- if (tInfo->currentContext->version == 2) {
+ if (tInfo->currentContext->majorVersion > 1) {
s_display.gles2_iface()->finish();
}
else {
setErrorReturn(EGL_BAD_DISPLAY, EGL_FALSE);
// post the surface
- d->swapBuffers();
+ EGLBoolean ret = d->swapBuffers();
hostCon->flush();
- return EGL_TRUE;
+ return ret;
}
EGLBoolean eglCopyBuffers(EGLDisplay dpy, EGLSurface surface, EGLNativePixmapType target)
(void)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 (target == EGL_NATIVE_BUFFER_ANDROID) {
+ if (ctx != EGL_NO_CONTEXT) {
+ setErrorReturn(EGL_BAD_CONTEXT, EGL_NO_IMAGE_KHR);
+ }
- if (native_buffer->common.version != sizeof(android_native_buffer_t))
- setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+ android_native_buffer_t* native_buffer = (android_native_buffer_t*)buffer;
- cb_handle_t *cb = (cb_handle_t *)(native_buffer->handle);
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
- 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:
- break;
- default:
+ 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_YV12:
+ case HAL_PIXEL_FORMAT_BGRA_8888:
+ break;
+ default:
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
+ }
+
+ native_buffer->common.incRef(&native_buffer->common);
+
+ EGLImage_t *image = new EGLImage_t();
+ image->dpy = dpy;
+ image->target = target;
+ image->native_buffer = native_buffer;
+
+ return (EGLImageKHR)image;
}
+ else if (target == EGL_GL_TEXTURE_2D_KHR) {
+ VALIDATE_CONTEXT_RETURN(ctx, EGL_NO_IMAGE_KHR);
- native_buffer->common.incRef(&native_buffer->common);
- return (EGLImageKHR)native_buffer;
+ EGLContext_t *context = static_cast<EGLContext_t*>(ctx);
+ DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_NO_IMAGE_KHR);
+
+ uint32_t ctxHandle = (context) ? context->rcContext : 0;
+ GLuint texture = (GLuint)reinterpret_cast<uintptr_t>(buffer);
+ uint32_t img = rcEnc->rcCreateClientImage(rcEnc, ctxHandle, target, texture);
+ EGLImage_t *image = new EGLImage_t();
+ image->dpy = dpy;
+ image->target = target;
+ image->host_egl_image = img;
+
+ return (EGLImageKHR)image;
+ }
+
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_NO_IMAGE_KHR);
}
EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
{
VALIDATE_DISPLAY_INIT(dpy, EGL_FALSE);
- android_native_buffer_t* native_buffer = (android_native_buffer_t*)img;
+ EGLImage_t *image = (EGLImage_t*)img;
- if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
- setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+ if (!image || image->dpy != dpy) {
+ RETURN_ERROR(EGL_FALSE, EGL_BAD_PARAMETER);
+ }
- if (native_buffer->common.version != sizeof(android_native_buffer_t))
- setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+ if (image->target == EGL_NATIVE_BUFFER_ANDROID) {
+ android_native_buffer_t* native_buffer = image->native_buffer;
- native_buffer->common.decRef(&native_buffer->common);
+ if (native_buffer->common.magic != ANDROID_NATIVE_BUFFER_MAGIC)
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
- return EGL_TRUE;
+ if (native_buffer->common.version != sizeof(android_native_buffer_t))
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+
+ native_buffer->common.decRef(&native_buffer->common);
+ delete image;
+
+ return EGL_TRUE;
+ }
+ else if (image->target == EGL_GL_TEXTURE_2D_KHR) {
+ uint32_t host_egl_image = image->host_egl_image;
+ delete image;
+ DEFINE_AND_VALIDATE_HOST_CONNECTION(EGL_FALSE);
+ return rcEnc->rcDestroyClientImage(rcEnc, host_egl_image);
+ }
+
+ setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
}
#define FENCE_SYNC_HANDLE (EGLSyncKHR)0xFE4CE
+#define MAX_EGL_SYNC_ATTRIBS 10
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);
+ DPRINT("type for eglCreateSyncKHR: 0x%x", type);
+
+ DEFINE_HOST_CONNECTION;
- if (type != EGL_SYNC_FENCE_KHR ||
- (attrib_list != NULL && attrib_list[0] != EGL_NONE)) {
+ if ((type != EGL_SYNC_FENCE_KHR &&
+ type != EGL_SYNC_NATIVE_FENCE_ANDROID) ||
+ (type != EGL_SYNC_FENCE_KHR &&
+ !rcEnc->hasNativeSync())) {
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
}
setErrorReturn(EGL_BAD_MATCH, EGL_NO_SYNC_KHR);
}
- if (tInfo->currentContext->version == 2) {
- s_display.gles2_iface()->finish();
+ int num_actual_attribs = 0;
+
+ // If attrib_list is not NULL,
+ // ensure attrib_list contains (key, value) pairs
+ // followed by a single EGL_NONE.
+ // Also validate attribs.
+ int inputFenceFd = -1;
+ if (attrib_list) {
+ for (int i = 0; i < MAX_EGL_SYNC_ATTRIBS; i += 2) {
+ if (attrib_list[i] == EGL_NONE) {
+ num_actual_attribs = i;
+ break;
+ }
+ if (i + 1 == MAX_EGL_SYNC_ATTRIBS) {
+ DPRINT("ERROR: attrib list without EGL_NONE");
+ setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_NO_SYNC_KHR);
+ }
+ }
+
+ // Validate and input attribs
+ for (int i = 0; i < num_actual_attribs; i += 2) {
+ if (attrib_list[i] == EGL_SYNC_TYPE_KHR) {
+ DPRINT("ERROR: attrib key = EGL_SYNC_TYPE_KHR");
+ }
+ if (attrib_list[i] == EGL_SYNC_STATUS_KHR) {
+ DPRINT("ERROR: attrib key = EGL_SYNC_STATUS_KHR");
+ }
+ if (attrib_list[i] == EGL_SYNC_CONDITION_KHR) {
+ DPRINT("ERROR: attrib key = EGL_SYNC_CONDITION_KHR");
+ }
+ EGLint attrib_key = attrib_list[i];
+ EGLint attrib_val = attrib_list[i + 1];
+ if (attrib_key == EGL_SYNC_NATIVE_FENCE_FD_ANDROID) {
+ if (attrib_val != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ inputFenceFd = attrib_val;
+ }
+ }
+ DPRINT("attrib: 0x%x : 0x%x", attrib_key, attrib_val);
+ }
+ }
+
+ uint64_t sync_handle = 0;
+ int newFenceFd = -1;
+
+ if (rcEnc->hasNativeSync()) {
+ sync_handle =
+ createNativeSync(type, attrib_list, num_actual_attribs,
+ false /* don't destroy when signaled on the host;
+ let the guest clean this up,
+ because the guest called eglCreateSyncKHR. */,
+ inputFenceFd,
+ &newFenceFd);
+
} else {
- s_display.gles_iface()->finish();
+ // Just trigger a glFinish if the native sync on host
+ // is unavailable.
+ eglWaitClient();
}
- return FENCE_SYNC_HANDLE;
+ EGLSync_t* syncRes = new EGLSync_t(sync_handle);
+
+ if (type == EGL_SYNC_NATIVE_FENCE_ANDROID) {
+ syncRes->type = EGL_SYNC_NATIVE_FENCE_ANDROID;
+
+ if (inputFenceFd < 0) {
+ syncRes->android_native_fence_fd = newFenceFd;
+ } else {
+ DPRINT("has input fence fd %d",
+ inputFenceFd);
+ syncRes->android_native_fence_fd = inputFenceFd;
+ }
+ } else {
+ syncRes->type = EGL_SYNC_FENCE_KHR;
+ syncRes->android_native_fence_fd = -1;
+ if (!rcEnc->hasNativeSync()) {
+ syncRes->status = EGL_SIGNALED_KHR;
+ }
+ }
+
+ return (EGLSyncKHR)syncRes;
}
-EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR sync)
+EGLBoolean eglDestroySyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync)
{
(void)dpy;
- if (sync != FENCE_SYNC_HANDLE) {
- setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+ if (!eglsync) {
+ DPRINT("WARNING: null sync object")
+ return EGL_TRUE;
+ }
+
+ EGLSync_t* sync = static_cast<EGLSync_t*>(eglsync);
+
+ if (sync && sync->android_native_fence_fd > 0) {
+ close(sync->android_native_fence_fd);
+ sync->android_native_fence_fd = -1;
+ }
+
+ if (sync) {
+ DEFINE_HOST_CONNECTION;
+ if (rcEnc->hasNativeSync()) {
+ rcEnc->rcDestroySyncKHR(rcEnc, sync->handle);
+ }
+ delete sync;
}
return EGL_TRUE;
}
-EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR sync, EGLint flags,
+EGLint eglClientWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync, EGLint flags,
EGLTimeKHR timeout)
{
(void)dpy;
- (void)flags;
- (void)timeout;
- if (sync != FENCE_SYNC_HANDLE) {
- setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
+ if (!eglsync) {
+ DPRINT("WARNING: null sync object");
+ return EGL_CONDITION_SATISFIED_KHR;
}
- return EGL_CONDITION_SATISFIED_KHR;
+ EGLSync_t* sync = (EGLSync_t*)eglsync;
+
+ DPRINT("sync=0x%lx (handle=0x%lx) flags=0x%x timeout=0x%llx",
+ sync, sync->handle, flags, timeout);
+
+ DEFINE_HOST_CONNECTION;
+
+ EGLint retval;
+ if (rcEnc->hasNativeSync()) {
+ retval = rcEnc->rcClientWaitSyncKHR
+ (rcEnc, sync->handle, flags, timeout);
+ } else {
+ retval = EGL_CONDITION_SATISFIED_KHR;
+ }
+ EGLint res_status;
+ switch (sync->type) {
+ case EGL_SYNC_FENCE_KHR:
+ res_status = EGL_SIGNALED_KHR;
+ break;
+ case EGL_SYNC_NATIVE_FENCE_ANDROID:
+ res_status = EGL_SYNC_NATIVE_FENCE_SIGNALED_ANDROID;
+ break;
+ default:
+ res_status = EGL_SIGNALED_KHR;
+ }
+ sync->status = res_status;
+ return retval;
}
-EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
+EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR eglsync,
EGLint attribute, EGLint *value)
{
(void)dpy;
- if (sync != FENCE_SYNC_HANDLE) {
- setErrorReturn(EGL_BAD_PARAMETER, EGL_FALSE);
- }
+ EGLSync_t* sync = (EGLSync_t*)eglsync;
switch (attribute) {
case EGL_SYNC_TYPE_KHR:
- *value = EGL_SYNC_FENCE_KHR;
+ *value = sync->type;
return EGL_TRUE;
case EGL_SYNC_STATUS_KHR:
- *value = EGL_SIGNALED_KHR;
+ *value = sync->status;
return EGL_TRUE;
case EGL_SYNC_CONDITION_KHR:
*value = EGL_SYNC_PRIOR_COMMANDS_COMPLETE_KHR;
setErrorReturn(EGL_BAD_ATTRIBUTE, EGL_FALSE);
}
}
+
+int eglDupNativeFenceFDANDROID(EGLDisplay dpy, EGLSyncKHR eglsync) {
+ (void)dpy;
+
+ DPRINT("call");
+
+ EGLSync_t* sync = (EGLSync_t*)eglsync;
+ if (sync && sync->android_native_fence_fd > 0) {
+ int res = dup(sync->android_native_fence_fd);
+ return res;
+ } else {
+ return -1;
+ }
+}
+
+// TODO: Implement EGL_KHR_wait_sync
+EGLint eglWaitSyncKHR(EGLDisplay dpy, EGLSyncKHR eglsync, EGLint flags) {
+ (void)dpy;
+ (void)eglsync;
+ (void)flags;
+ return EGL_TRUE;
+}