OSDN Git Service

More efficient swapBuffers
authorLingfeng Yang <lfy@google.com>
Fri, 22 Jul 2016 22:21:58 +0000 (15:21 -0700)
committerLingfeng Yang <lfy@google.com>
Thu, 28 Jul 2016 17:05:28 +0000 (10:05 -0700)
Previously, we were using glFinish() in the host to make sure
that a buffer that we are presenting has been completely written to.
That is bad for performance, because it usually means to block the CPU
until the GPU's command queue is completely empty and all operations
have completed.

This CL uses eglCreateSyncKHR with a native fence FD to guarantee
the draw buffer is finished before presenting. The idea is that
after rcFlushWindowColorBufferAsync has queued up GPU commands to
finish up the draw buffer, eglCreateSyncKHR follows that up with a fence
command.

The fence command in turn is represented by a FD (by providing
EGL_SYNC_NATIVE_FENCE_ANDROID as the sync object type). Giving that FD
to queueBuffer tells SurfaceFlinger/BufferQueue not to do anything
that depends on the contents of that buffer until the fence command
has completed.

Theoretically, we are supposed to wait on the FD coming from dequeueBuffer
as well, to make sure no one else is using it, but in practice,
it matters much less; we will just overwrite the contents anyway, and it
has not proved to be any problem in terms of visual artifacts.

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 <- needed
https://googleplex-android-review.git.corp.google.com/1219571 <- this CL

Change-Id: I3dd1b69c77665ac0143bd5a302e6a5a04736c9ea

system/egl/egl.cpp

index cbd5d05..15d888b 100644 (file)
 #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
@@ -356,15 +350,70 @@ void egl_window_surface_t::setSwapInterval(int interval)
 
 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 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);
+        EGLSyncKHR sync = eglCreateSyncKHR(dpy,
+                                           EGL_SYNC_NATIVE_FENCE_ANDROID,
+                                           NULL);
+        EGLSync_t* syncObj = (EGLSync_t*)sync;
+        presentFenceFd = syncObj->android_native_fence_fd;
+    } 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_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);