SwappyVK: inject a fence before QueuePresent

SwappyVk will use the inhected fence that to determine
the next present time for a given frame.

Test: manual
Change-Id: Icd5194f422de1fefe9b53edd31f204edcab3a13f
diff --git a/src/swappyVk/CMakeLists.txt b/src/swappyVk/CMakeLists.txt
index af4b00f..4887662 100644
--- a/src/swappyVk/CMakeLists.txt
+++ b/src/swappyVk/CMakeLists.txt
@@ -10,6 +10,7 @@
 set( CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-s" )
 
 include_directories( ../../includes )
+include_directories( ${ANDROID_NDK}/sources/third_party/vulkan/src/common )
 
 message( STATUS "Building swappyVk_static to ${CMAKE_CURRENT_BINARY_DIR}/build" )
 add_library( swappyVk_static
diff --git a/src/swappyVk/SwappyVk.cpp b/src/swappyVk/SwappyVk.cpp
index 2c46d8b..9b3a0cc 100644
--- a/src/swappyVk/SwappyVk.cpp
+++ b/src/swappyVk/SwappyVk.cpp
@@ -24,6 +24,7 @@
 #ifdef ANDROID
 #include <mutex>
 #include <pthread.h>
+#include <list>
 #include <android/looper.h>
 #include <android/choreographer.h>
 #include <android/log.h>
@@ -115,6 +116,8 @@
             mPhysicalDevice(physicalDevice), mDevice(device), mRefreshDur(refreshDur),
             mInterval(interval), mSwappyVk(swappyVk), mLibVulkan(libVulkan), mInitialized(false)
     {
+        InitVulkan();
+
         mpfnGetDeviceProcAddr =
                 reinterpret_cast<PFN_vkGetDeviceProcAddr>(
                     dlsym(mLibVulkan, "vkGetDeviceProcAddr"));
@@ -456,13 +459,168 @@
 
     ~SwappyVkGoogleDisplayTimingAndroid() {
         stopChoreographerThread();
+        destroyVkSyncObjects();
     }
 
-    virtual VkResult doQueuePresent(VkQueue                 queue,
-                                    const VkPresentInfoKHR* pPresentInfo) override;
+    virtual bool doGetRefreshCycleDuration(VkSwapchainKHR swapchain,
+                                          uint64_t*      pRefreshDuration) override {
+        bool res = SwappyVkGoogleDisplayTiming::doGetRefreshCycleDuration(swapchain, pRefreshDuration);
+        initializeVkSyncObjects();
+        return res;
+    }
 
+
+
+    virtual VkResult doQueuePresent(VkQueue queue,
+                                    const VkPresentInfoKHR *pPresentInfo) override;
+
+private:
+    VkResult initializeVkSyncObjects();
+    void destroyVkSyncObjects();
+
+    void waitForFenceChoreographer();
+
+    struct VkSync {
+        VkFence fence;
+        VkSemaphore semaphore;
+        VkCommandBuffer command;
+        VkEvent event;
+    };
+
+    std::list<VkSync> mFreeSync;
+    std::list<VkSync> mPendingSync;
+    VkCommandPool mCommandPool;
+
+    static constexpr int MAX_PENDING_FENCES = 1;
 };
 
+VkResult SwappyVkGoogleDisplayTimingAndroid::initializeVkSyncObjects()
+{
+    VkSync sync;
+
+    const VkCommandPoolCreateInfo cmd_pool_info = {
+            .sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
+            .pNext = NULL,
+            .queueFamilyIndex = 0, // adyabr TODO: need to get the queue family index somehow
+            .flags = 0,
+    };
+
+    VkResult res = vkCreateCommandPool(mDevice, &cmd_pool_info, NULL, &mCommandPool);
+    if (res) {
+        ALOGE("vkCreateCommandPool failed %d", res);
+        return res;
+    }
+    const VkCommandBufferAllocateInfo present_cmd_info = {
+            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
+            .pNext = NULL,
+            .commandPool = mCommandPool,
+            .level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
+            .commandBufferCount = 1,
+    };
+
+    for(int i = 0; i < MAX_PENDING_FENCES; i++) {
+        VkFenceCreateInfo fence_ci =
+                {.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO, .pNext = NULL, .flags = 0};
+
+        res = vkCreateFence(mDevice, &fence_ci, NULL, &sync.fence);
+        if (res) {
+            ALOGE("failed to create fence: %d", res);
+            return res;
+        }
+
+        VkSemaphoreCreateInfo semaphore_ci =
+                {.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO, .pNext = NULL, .flags = 0};
+
+        res = vkCreateSemaphore(mDevice, &semaphore_ci, NULL, &sync.semaphore);
+        if (res) {
+            ALOGE("failed to create semaphore: %d", res);
+            return res;
+        }
+
+
+        res = vkAllocateCommandBuffers(mDevice, &present_cmd_info, &sync.command);
+        if (res) {
+            ALOGE("vkAllocateCommandBuffers failed %d", res);
+            return res;
+        }
+
+        const VkCommandBufferBeginInfo cmd_buf_info = {
+                .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
+                .pNext = NULL,
+                .flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT,
+                .pInheritanceInfo = NULL,
+        };
+
+        res = vkBeginCommandBuffer(sync.command, &cmd_buf_info);
+        if (res) {
+            ALOGE("vkAllocateCommandBuffers failed %d", res);
+            return res;
+        }
+
+        VkEventCreateInfo event_info = {
+                .sType = VK_STRUCTURE_TYPE_EVENT_CREATE_INFO,
+                .pNext = NULL,
+                .flags = 0,
+        };
+
+        res = vkCreateEvent(mDevice, &event_info, NULL, &sync.event);
+        if (res) {
+            ALOGE("vkCreateEvent failed %d", res);
+            return res;
+        }
+
+        vkCmdSetEvent(sync.command, sync.event, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT);
+
+        res = vkEndCommandBuffer(sync.command);
+        if (res) {
+            ALOGE("vkCreateEvent failed %d", res);
+            return res;
+        }
+
+        mFreeSync.push_back(sync);
+    }
+
+    return VK_SUCCESS;
+}
+
+void SwappyVkGoogleDisplayTimingAndroid::destroyVkSyncObjects() {
+    while (mPendingSync.size() > 0) {
+        VkSync sync = mPendingSync.front();
+        mPendingSync.pop_front();
+        vkWaitForFences(mDevice, 1, &sync.fence, VK_TRUE, UINT64_MAX);
+        mFreeSync.push_back(sync);
+    }
+
+    while (mFreeSync.size() > 0) {
+        VkSync sync = mPendingSync.front();
+        mPendingSync.pop_front();
+        vkFreeCommandBuffers(mDevice, mCommandPool, 1, &sync.command);
+        vkDestroyEvent(mDevice, sync.event, NULL);
+        vkDestroySemaphore(mDevice, sync.semaphore, NULL);
+        vkDestroyFence(mDevice, sync.fence, NULL);
+    }
+
+    vkDestroyCommandPool(mDevice, mCommandPool, NULL);
+}
+
+void SwappyVkGoogleDisplayTimingAndroid::waitForFenceChoreographer()
+{
+    std::unique_lock<std::mutex> lock(mWaitingMutex);
+    VkSync sync = mPendingSync.front();
+    mPendingSync.pop_front();
+    ALOGE("wait fence=%llu", sync.fence);
+    mWaitingCondition.wait(lock, [&]() {
+        if (vkWaitForFences(mDevice, 1, &sync.fence, VK_TRUE, 0) == VK_TIMEOUT) {
+            AChoreographer_postFrameCallbackDelayed(mChoreographer, frameCallback, this, 1);
+            return false;
+        }
+        return true;
+    });
+
+    vkResetFences(mDevice, 1, &sync.fence);
+    mFreeSync.push_back(sync);
+}
+
 VkResult SwappyVkGoogleDisplayTimingAndroid::doQueuePresent(VkQueue                 queue,
                                                      const VkPresentInfoKHR* pPresentInfo)
 {
@@ -485,12 +643,16 @@
             return true;
         });
     }
+
+    if (mPendingSync.size() >= MAX_PENDING_FENCES) {
+        waitForFenceChoreographer();
+    }
+
     clock_gettime(CLOCK_MONOTONIC, &currTime);
     currentTime =
             ((uint64_t) currTime.tv_sec * kBillion) + (uint64_t) currTime.tv_nsec;
     mNextDesiredPresentTime = currentTime + mRefreshDur * mInterval;
 
-
     // Setup the new structures to pass:
     VkPresentTimeGOOGLE pPresentTimes[pPresentInfo->swapchainCount];
     for (uint32_t i = 0 ; i < pPresentInfo->swapchainCount ; i++) {
@@ -499,12 +661,36 @@
     }
     mNextPresentID++;
 
+    VkSync sync = mFreeSync.front();
+    mFreeSync.pop_front();
+    mPendingSync.push_back(sync);
+
+    ALOGE("submit fence=%llu", sync.fence); // adyabr TODO: remove all ALOGE!
+
+    VkPipelineStageFlags pipe_stage_flags;
+    VkSubmitInfo submit_info;
+    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+    submit_info.pNext = NULL;
+    submit_info.pWaitDstStageMask = &pipe_stage_flags;
+    pipe_stage_flags = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;
+    submit_info.waitSemaphoreCount = pPresentInfo->waitSemaphoreCount;
+    submit_info.pWaitSemaphores = pPresentInfo->pWaitSemaphores;
+    submit_info.commandBufferCount = 1;
+    submit_info.pCommandBuffers = &sync.command;
+    submit_info.signalSemaphoreCount = 1;
+    submit_info.pSignalSemaphores = &sync.semaphore;
+    ret = vkQueueSubmit(queue, 1, &submit_info, sync.fence);
+    if (ret) {
+        ALOGE("Failed to vkQueueSubmit %d", ret);
+        return ret;
+    }
+
     VkPresentTimesInfoGOOGLE presentTimesInfo = {VK_STRUCTURE_TYPE_PRESENT_TIMES_INFO_GOOGLE,
                                                  pPresentInfo->pNext, pPresentInfo->swapchainCount,
                                                  pPresentTimes};
     VkPresentInfoKHR replacementPresentInfo = {pPresentInfo->sType, &presentTimesInfo,
-                                               pPresentInfo->waitSemaphoreCount,
-                                               pPresentInfo->pWaitSemaphores,
+                                               1,
+                                               &sync.semaphore,
                                                pPresentInfo->swapchainCount,
                                                pPresentInfo->pSwapchains,
                                                pPresentInfo->pImageIndices, pPresentInfo->pResults};
diff --git a/src/swappyVk/SwappyVk.h b/src/swappyVk/SwappyVk.h
index a3211db..29280eb 100644
--- a/src/swappyVk/SwappyVk.h
+++ b/src/swappyVk/SwappyVk.h
@@ -17,7 +17,11 @@
 #ifndef SWAPPYVK_H
 #define SWAPPYVK_H
 
+#ifndef ANDROID
 #include <vulkan/vulkan.h>
+#else
+#include <vulkan_wrapper.h>
+#endif
 
 #ifdef __cplusplus
 extern "C" {
diff --git a/third_party/cube/app/src/main/cpp/cube.c b/third_party/cube/app/src/main/cpp/cube.c
index 308e456..0c8367f 100644
--- a/third_party/cube/app/src/main/cpp/cube.c
+++ b/third_party/cube/app/src/main/cpp/cube.c
@@ -2270,6 +2270,8 @@
         assert(!err);
     }
 
+    DbgMsg("graphics_queue_family_index=%u", demo->graphics_queue_family_index);
+
     const VkCommandBufferAllocateInfo cmd = {
         .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
         .pNext = NULL,