diff options
author | Lingfeng Yang <lfy@google.com> | 2016-07-13 17:44:03 -0700 |
---|---|---|
committer | Lingfeng Yang <lfy@google.com> | 2016-07-28 10:05:28 -0700 |
commit | 7f5385ad2ac0f1870a5e71ed5f5b606d829d4c2c (patch) | |
tree | 47ed2567e4bc5cb19b3748e3c3812730fd5dc0a6 | |
parent | dfcf87d2cce4c5d6a3632d4577221b92a2178172 (diff) | |
download | goldfish-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.cpp | 201 |
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; +} |