aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorLingfeng Yang <lfy@google.com>2016-07-13 17:44:03 -0700
committerLingfeng Yang <lfy@google.com>2016-07-28 10:05:28 -0700
commit7f5385ad2ac0f1870a5e71ed5f5b606d829d4c2c (patch)
tree47ed2567e4bc5cb19b3748e3c3812730fd5dc0a6
parentdfcf87d2cce4c5d6a3632d4577221b92a2178172 (diff)
downloadgoldfish-opengl-7f5385ad2ac0f1870a5e71ed5f5b606d829d4c2c.tar.gz
Implement EGL_KHR_fence_sync/EGL_ANDROID_native_fence_sync for emulator
The core purpose of these two extensions is to provide a way for one thread to know when an EGL command has finished processing in another thread, so that it is safe to do work that touches common memory regions, such as frame buffers in a concurrent draw/present setup. eglCreateSyncKHR inserts a fence command into the current context's command queue, and returns a handle to that command. eglClientWaitSyncKHR takes such a handle as input and blocks the CPU until that fence command is completed. For the emulator, we implement these commands by wrapping underlying host implementations. That is enough for EGL_KHR_fence_sync, but we also include an interface to the Goldfish Sync driver in order to have EGL_ANDROID_native_fence_sync. Goldfish Sync driver adds the ability for these fence commands and their completed/not completed state to be represented as FDs, which the rest of the Android platform uses for doing graphics asynchronously. Related specs: https://www.khronos.org/registry/vg/extensions/KHR/EGL_KHR_fence_sync.txt https://www.khronos.org/registry/egl/extensions/ANDROID/EGL_ANDROID_native_fence_sync.txt This is part of a sequential, multi-CL change. There is also a corresponding multi-CL change on the host side: https://android-review.googlesource.com/#/q/topic:emu-glsync-host The changes in the system image are as follows: platform/build: https://googleplex-android-review.git.corp.google.com/1024926 device/generic/goldfish: https://googleplex-android-review.git.corp.google.com/1230942 device/generic/goldfish-opengl: https://googleplex-android-review.git.corp.google.com/1219535 https://googleplex-android-review.git.corp.google.com/1219536 https://googleplex-android-review.git.corp.google.com/1219537 https://googleplex-android-review.git.corp.google.com/1219538 https://googleplex-android-review.git.corp.google.com/1219539 https://googleplex-android-review.git.corp.google.com/1219570 <- uses this All changes above, including host-side, are needed for this to work. The following CL uses the functionality in this one: https://googleplex-android-review.git.corp.google.com/1219571 Change-Id: I9bf0ba0994b2e8689a6bed9c5eeee206a5e21f45
-rw-r--r--system/egl/egl.cpp201
1 files changed, 175 insertions, 26 deletions
diff --git a/system/egl/egl.cpp b/system/egl/egl.cpp
index 2b309f77..cbd5d051 100644
--- a/system/egl/egl.cpp
+++ b/system/egl/egl.cpp
@@ -18,6 +18,7 @@
#include "HostConnection.h"
#include "ThreadInfo.h"
#include "eglDisplay.h"
+#include "eglSync.h"
#include "egl_ftable.h"
#include <cutils/log.h>
#include "goldfish_sync.h"
@@ -188,6 +189,10 @@ int EGLContext_t::getGoldfishSyncFd() {
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;
@@ -1372,19 +1377,21 @@ EGLBoolean eglDestroyImageKHR(EGLDisplay dpy, EGLImageKHR img)
}
#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);
}
@@ -1393,55 +1400,175 @@ EGLSyncKHR eglCreateSyncKHR(EGLDisplay dpy, EGLenum type,
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 native_fence_fd = -1;
+
+ EGLContext_t* cxt = getEGLThreadInfo()->currentContext;
+ if (rcEnc->hasNativeSync()) {
+ uint64_t thread_out = 0;
+
+ rcEnc->rcCreateSyncKHR(rcEnc,
+ type,
+ (EGLint*)(num_actual_attribs == 0 ? NULL : attrib_list),
+ num_actual_attribs * sizeof(EGLint),
+ &sync_handle,
+ &thread_out);
+
+ if (sync_handle && thread_out &&
+ (type == EGL_SYNC_NATIVE_FENCE_ANDROID) &&
+ (inputFenceFd < 0)) {
+
+ int goldfish_sync_fd = cxt->getGoldfishSyncFd();
+ int queue_work_err =
+ goldfish_sync_queue_work(goldfish_sync_fd,
+ sync_handle,
+ thread_out,
+ &native_fence_fd);
+
+ DPRINT("got native fence fd=%d queue_work_err=%d",
+ native_fence_fd, queue_work_err);
+ }
+
} else {
- s_display.gles_iface()->finish();
+ // Just trigger a glFinish if the native sync on host
+ // is unavailable.
+ eglWaitClient();
+ }
+
+ 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 = native_fence_fd;
+ } 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;
}
- return FENCE_SYNC_HANDLE;
+ 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) 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;
@@ -1450,3 +1577,25 @@ EGLBoolean eglGetSyncAttribKHR(EGLDisplay dpy, EGLSyncKHR sync,
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;
+}