For composition fences, wait for next vsync

Bug: 191276579

Also, gate the functionality behind a flag.

Test: android.view.cts.ASurfaceControlTest#testSurfaceTransaction_setDesiredPresentTime_100ms
passes more often

Change-Id: Ib14ef03216d2168bba4460b1eeee2a9a4c1812fa
diff --git a/stream-servers/FrameBuffer.cpp b/stream-servers/FrameBuffer.cpp
index 3c61626..bd6c109 100644
--- a/stream-servers/FrameBuffer.cpp
+++ b/stream-servers/FrameBuffer.cpp
@@ -3775,3 +3775,13 @@
         m_vsyncThread->setPeriod(kOneSecondNs / (uint64_t)m_vsyncHz);
     }
 }
+
+void FrameBuffer::scheduleVsyncTask(VsyncThread::VsyncTask task) {
+    if (!m_vsyncThread) {
+        fprintf(stderr, "%s: warning: no vsync thread exists\n", __func__);
+        task(0);
+        return;
+    }
+
+    m_vsyncThread->schedule(task);
+}
diff --git a/stream-servers/FrameBuffer.h b/stream-servers/FrameBuffer.h
index fe83b72..3e9aea8 100644
--- a/stream-servers/FrameBuffer.h
+++ b/stream-servers/FrameBuffer.h
@@ -655,6 +655,8 @@
                               std::optional<uint64_t> allocationSize = std::nullopt);
 
     void setVsyncHz(int vsyncHz);
+    void scheduleVsyncTask(VsyncThread::VsyncTask task);
+
    private:
     FrameBuffer(int p_width, int p_height, bool useSubWindow);
     // Requires the caller to hold the m_colorBufferMapLock until the new handle is inserted into of
diff --git a/stream-servers/RenderControl.cpp b/stream-servers/RenderControl.cpp
index f4a5e99..2bac20b 100644
--- a/stream-servers/RenderControl.cpp
+++ b/stream-servers/RenderControl.cpp
@@ -352,6 +352,10 @@
           feature_is_enabled(kFeature_VirtioGpuFenceContexts)));
 }
 
+static bool shouldEnableVsyncGatedSyncFences() {
+    return shouldEnableAsyncSwap();
+}
+
 const char* maxVersionToFeatureString(GLESDispatchMaxVersion version) {
     switch (version) {
         case GLES_DISPATCH_MAX_VERSION_2:
@@ -1127,11 +1131,22 @@
                                          timeline);
     } else {
         EmulatedEglFenceSync* fenceSync = reinterpret_cast<EmulatedEglFenceSync*>(eglsync_ptr);
-        EGLSYNC_DPRINT(
-                "eglsync=0x%llx fenceSync=%p thread_ptr=0x%llx "
-                "timeline=0x%llx",
-                eglsync_ptr, fenceSync, thread_ptr, timeline);
-        SyncThread::get()->triggerWait(fenceSync, timeline);
+        FrameBuffer *fb = FrameBuffer::getFB();
+        if (fb && fenceSync->isCompositionFence()) {
+            fb->scheduleVsyncTask([eglsync_ptr, fenceSync, timeline](uint64_t) {
+                EGLSYNC_DPRINT(
+                    "vsync: eglsync=0x%llx fenceSync=%p thread_ptr=0x%llx "
+                    "timeline=0x%llx",
+                    eglsync_ptr, fenceSync, thread_ptr, timeline);
+                SyncThread::get()->triggerWait(fenceSync, timeline);
+            });
+        } else {
+            EGLSYNC_DPRINT(
+                    "eglsync=0x%llx fenceSync=%p thread_ptr=0x%llx "
+                    "timeline=0x%llx",
+                    eglsync_ptr, fenceSync, thread_ptr, timeline);
+            SyncThread::get()->triggerWait(fenceSync, timeline);
+        }
     }
 }
 
@@ -1157,6 +1172,12 @@
                                                      destroyWhenSignaled,
                                                      outSync,
                                                      outSyncThread);
+
+    RenderThreadInfo* tInfo = RenderThreadInfo::get();
+    if (tInfo && outSync && shouldEnableVsyncGatedSyncFences()) {
+        auto fenceSync = reinterpret_cast<EmulatedEglFenceSync*>(outSync);
+        fenceSync->setIsCompositionFence(tInfo->m_isCompositionThread);
+    }
 }
 
 // |rcClientWaitSyncKHR| implements |eglClientWaitSyncKHR|
@@ -1247,6 +1268,9 @@
 }
 
 static int rcCompose(uint32_t bufferSize, void* buffer) {
+    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    if (tInfo) tInfo->m_isCompositionThread = true;
+
     FrameBuffer *fb = FrameBuffer::getFB();
     if (!fb) {
         return -1;
@@ -1255,6 +1279,9 @@
 }
 
 static int rcComposeWithoutPost(uint32_t bufferSize, void* buffer) {
+    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    if (tInfo) tInfo->m_isCompositionThread = true;
+
     FrameBuffer *fb = FrameBuffer::getFB();
     if (!fb) {
         return -1;
@@ -1494,6 +1521,9 @@
 }
 
 static void rcComposeAsync(uint32_t bufferSize, void* buffer) {
+    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    if (tInfo) tInfo->m_isCompositionThread = true;
+
     FrameBuffer* fb = FrameBuffer::getFB();
     if (!fb) {
         return;
@@ -1502,6 +1532,9 @@
 }
 
 static void rcComposeAsyncWithoutPost(uint32_t bufferSize, void* buffer) {
+    RenderThreadInfo *tInfo = RenderThreadInfo::get();
+    if (tInfo) tInfo->m_isCompositionThread = true;
+
     FrameBuffer *fb = FrameBuffer::getFB();
     if (!fb) {
         return;
diff --git a/stream-servers/RenderThreadInfo.h b/stream-servers/RenderThreadInfo.h
index d37e28a..f392bd7 100644
--- a/stream-servers/RenderThreadInfo.h
+++ b/stream-servers/RenderThreadInfo.h
@@ -52,6 +52,9 @@
     std::optional<RenderThreadInfoGl> m_glInfo;
     std::optional<goldfish_vk::RenderThreadInfoVk> m_vkInfo;
 
+    // Whether this thread was used to perform composition.
+    bool m_isCompositionThread = false;
+
     // Functions to save / load a snapshot
     // They must be called after Framebuffer snapshot
     void onSave(android::base::Stream* stream);
diff --git a/stream-servers/gl/EmulatedEglFenceSync.h b/stream-servers/gl/EmulatedEglFenceSync.h
index f53374a..6cef8f5 100644
--- a/stream-servers/gl/EmulatedEglFenceSync.h
+++ b/stream-servers/gl/EmulatedEglFenceSync.h
@@ -120,6 +120,14 @@
         return false;
     }
 
+    void setIsCompositionFence(bool isComposition) {
+        mIsCompositionFence = isComposition;
+    }
+
+    bool isCompositionFence() const {
+        return mIsCompositionFence;
+    }
+
     // Tracks current active set of fences. Useful for snapshotting.
     void addToRegistry();
     void removeFromRegistry();
@@ -143,6 +151,10 @@
     EGLDisplay mDisplay;
     EGLSyncKHR mSync;
 
+    // Whether this fence was against composition, in which case
+    // we should make this wait till next vsync.
+    bool mIsCompositionFence = false;
+
     // destroy() wraps eglDestroySyncKHR. This is private, because we need
     // careful control of when eglDestroySyncKHR is actually called.
     void destroy();