surfaceflinger: add more sync operations to RenderEngine
Add RenderEngine::finish and RenderEngine::waitFence. Rework flush
not to fall back to finish.
Test: SurfaceFlinger_test
Change-Id: I2e5738f72b4aa1186d45d23cab9055f96d90ff23
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.cpp b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
index b7f8664..81af21f 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.cpp
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.cpp
@@ -89,6 +89,16 @@
hasEGLExtension("EGL_KHR_no_config_context")) {
mHasNoConfigContext = true;
}
+
+ if (hasEGLExtension("EGL_ANDROID_native_fence_sync")) {
+ mHasNativeFenceSync = true;
+ }
+ if (hasEGLExtension("EGL_KHR_fence_sync")) {
+ mHasFenceSync = true;
+ }
+ if (hasEGLExtension("EGL_KHR_wait_sync")) {
+ mHasWaitSync = true;
+ }
}
char const* GLExtensions::getEGLVersion() const {
diff --git a/services/surfaceflinger/RenderEngine/GLExtensions.h b/services/surfaceflinger/RenderEngine/GLExtensions.h
index 47f2f67..1ec0fd9 100644
--- a/services/surfaceflinger/RenderEngine/GLExtensions.h
+++ b/services/surfaceflinger/RenderEngine/GLExtensions.h
@@ -36,6 +36,9 @@
friend class Singleton<GLExtensions>;
bool mHasNoConfigContext = false;
+ bool mHasNativeFenceSync = false;
+ bool mHasFenceSync = false;
+ bool mHasWaitSync = false;
String8 mVendor;
String8 mRenderer;
@@ -57,6 +60,9 @@
public:
bool hasNoConfigContext() const { return mHasNoConfigContext; }
+ bool hasNativeFenceSync() const { return mHasNativeFenceSync; }
+ bool hasFenceSync() const { return mHasFenceSync; }
+ bool hasWaitSync() const { return mHasWaitSync; }
void initWithGLStrings(GLubyte const* vendor, GLubyte const* renderer, GLubyte const* version,
GLubyte const* extensions);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.cpp b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
index d3bda17..d5471ad 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.cpp
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.cpp
@@ -169,6 +169,88 @@
eglMakeCurrent(mEGLDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT);
}
+base::unique_fd RenderEngine::flush() {
+ if (!GLExtensions::getInstance().hasNativeFenceSync()) {
+ return base::unique_fd();
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL native fence sync: %#x", eglGetError());
+ return base::unique_fd();
+ }
+
+ // native fence fd will not be populated until flush() is done.
+ glFlush();
+
+ // get the fence fd
+ base::unique_fd fenceFd(eglDupNativeFenceFDANDROID(mEGLDisplay, sync));
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (fenceFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
+ ALOGW("failed to dup EGL native fence sync: %#x", eglGetError());
+ }
+
+ return fenceFd;
+}
+
+bool RenderEngine::finish() {
+ if (!GLExtensions::getInstance().hasFenceSync()) {
+ ALOGW("no synchronization support");
+ return false;
+ }
+
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGW("failed to create EGL fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
+ 2000000000 /*2 sec*/);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (result != EGL_CONDITION_SATISFIED_KHR) {
+ if (result == EGL_TIMEOUT_EXPIRED_KHR) {
+ ALOGW("fence wait timed out");
+ } else {
+ ALOGW("error waiting on EGL fence: %#x", error);
+ }
+ return false;
+ }
+
+ return true;
+}
+
+bool RenderEngine::waitFence(base::unique_fd fenceFd) {
+ if (!GLExtensions::getInstance().hasNativeFenceSync() ||
+ !GLExtensions::getInstance().hasWaitSync()) {
+ return false;
+ }
+
+ EGLint attribs[] = {EGL_SYNC_NATIVE_FENCE_FD_ANDROID, fenceFd, EGL_NONE};
+ EGLSyncKHR sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, attribs);
+ if (sync == EGL_NO_SYNC_KHR) {
+ ALOGE("failed to create EGL native fence sync: %#x", eglGetError());
+ return false;
+ }
+
+ // fenceFd is now owned by EGLSync
+ (void)fenceFd.release();
+
+ // XXX: The spec draft is inconsistent as to whether this should return an
+ // EGLint or void. Ignore the return value for now, as it's not strictly
+ // needed.
+ eglWaitSyncKHR(mEGLDisplay, sync, 0);
+ EGLint error = eglGetError();
+ eglDestroySyncKHR(mEGLDisplay, sync);
+ if (error != EGL_SUCCESS) {
+ ALOGE("failed to wait for EGL native fence sync: %#x", error);
+ return false;
+ }
+
+ return true;
+}
+
void RenderEngine::checkErrors() const {
do {
// there could be more than one error flag
@@ -220,52 +302,6 @@
drawMesh(mesh);
}
-int RenderEngine::flush(bool wait) {
- // Attempt to create a sync khr object that can produce a sync point. If that
- // isn't available, create a non-dupable sync object in the fallback path and
- // wait on it directly.
- EGLSyncKHR sync;
- if (!wait) {
- EGLint syncFd = EGL_NO_NATIVE_FENCE_FD_ANDROID;
-
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_NATIVE_FENCE_ANDROID, NULL);
- if (sync != EGL_NO_SYNC_KHR) {
- // native fence fd will not be populated until flush() is done.
- glFlush();
-
- // get the sync fd
- syncFd = eglDupNativeFenceFDANDROID(mEGLDisplay, sync);
- if (syncFd == EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- ALOGW("failed to dup sync khr object");
- }
-
- eglDestroySyncKHR(mEGLDisplay, sync);
- }
-
- if (syncFd != EGL_NO_NATIVE_FENCE_FD_ANDROID) {
- return syncFd;
- }
- }
-
- // fallback or explicit wait
- sync = eglCreateSyncKHR(mEGLDisplay, EGL_SYNC_FENCE_KHR, NULL);
- if (sync != EGL_NO_SYNC_KHR) {
- EGLint result = eglClientWaitSyncKHR(mEGLDisplay, sync, EGL_SYNC_FLUSH_COMMANDS_BIT_KHR,
- 2000000000 /*2 sec*/);
- EGLint eglErr = eglGetError();
- if (result == EGL_TIMEOUT_EXPIRED_KHR) {
- ALOGW("fence wait timed out");
- } else {
- ALOGW_IF(eglErr != EGL_SUCCESS, "error waiting on EGL fence: %#x", eglErr);
- }
- eglDestroySyncKHR(mEGLDisplay, sync);
- } else {
- ALOGW("error creating EGL fence: %#x", eglGetError());
- }
-
- return -1;
-}
-
void RenderEngine::clearWithColor(float red, float green, float blue, float alpha) {
glClearColor(red, green, blue, alpha);
glClear(GL_COLOR_BUFFER_BIT);
diff --git a/services/surfaceflinger/RenderEngine/RenderEngine.h b/services/surfaceflinger/RenderEngine/RenderEngine.h
index 3847347..32c669f 100644
--- a/services/surfaceflinger/RenderEngine/RenderEngine.h
+++ b/services/surfaceflinger/RenderEngine/RenderEngine.h
@@ -25,6 +25,7 @@
#include <EGL/egl.h>
#include <EGL/eglext.h>
#include <Transform.h>
+#include <android-base/unique_fd.h>
#include <gui/SurfaceControl.h>
#include <math/mat4.h>
@@ -82,9 +83,20 @@
// dump the extension strings. always call the base class.
virtual void dump(String8& result);
+ // synchronization
+
+ // flush submits RenderEngine command stream for execution and returns a
+ // native fence fd that is signaled when the execution has completed. It
+ // returns -1 on errors.
+ base::unique_fd flush();
+ // finish waits until RenderEngine command stream has been executed. It
+ // returns false on errors.
+ bool finish();
+ // waitFence inserts a wait on an external fence fd to RenderEngine
+ // command stream. It returns false on errors.
+ bool waitFence(base::unique_fd fenceFd);
+
// helpers
- // flush returns -1 or a valid native fence fd owned by the caller
- int flush(bool wait);
void clearWithColor(float red, float green, float blue, float alpha);
void fillRegionWithColor(const Region& region, uint32_t height, float red, float green,
float blue, float alpha);
diff --git a/services/surfaceflinger/SurfaceFlinger.cpp b/services/surfaceflinger/SurfaceFlinger.cpp
index 44d5ce4..a5b92b5 100644
--- a/services/surfaceflinger/SurfaceFlinger.cpp
+++ b/services/surfaceflinger/SurfaceFlinger.cpp
@@ -4538,9 +4538,10 @@
// dependent on the context's EGLConfig.
renderScreenImplLocked(renderArea, traverseLayers, true, useIdentityTransform);
- *outSyncFd = getRenderEngine().flush(DEBUG_SCREENSHOTS);
-
if (DEBUG_SCREENSHOTS) {
+ getRenderEngine().finish();
+ *outSyncFd = -1;
+
const auto reqWidth = renderArea.getReqWidth();
const auto reqHeight = renderArea.getReqHeight();
@@ -4548,6 +4549,12 @@
getRenderEngine().readPixels(0, 0, reqWidth, reqHeight, pixels);
checkScreenshot(reqWidth, reqHeight, reqWidth, pixels, traverseLayers);
delete [] pixels;
+ } else {
+ base::unique_fd syncFd = getRenderEngine().flush();
+ if (syncFd < 0) {
+ getRenderEngine().finish();
+ }
+ *outSyncFd = syncFd.release();
}
return NO_ERROR;
@@ -4618,4 +4625,4 @@
#if defined(__gl2_h_)
#error "don't include gl2/gl2.h in this file"
-#endif
\ No newline at end of file
+#endif