Add test for flushing empty surface with semaphores

Bug: skia:
Change-Id: I8d7dcb29c5b4c460aa5137842caf6563448ba5d3
Reviewed-on: https://skia-review.googlesource.com/25181
Commit-Queue: Greg Daniel <egdaniel@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/tests/SurfaceSemaphoreTest.cpp b/tests/SurfaceSemaphoreTest.cpp
index 72e8e05..7b22e1c 100644
--- a/tests/SurfaceSemaphoreTest.cpp
+++ b/tests/SurfaceSemaphoreTest.cpp
@@ -17,8 +17,18 @@
 #include "SkCanvas.h"
 #include "SkSurface.h"
 
+#include "gl/GrGLGpu.h"
+#include "gl/GrGLUtil.h"
+
 #ifdef SK_VULKAN
+#include "vk/GrVkGpu.h"
 #include "vk/GrVkTypes.h"
+#include "vk/GrVkUtil.h"
+
+#ifdef VK_USE_PLATFORM_WIN32_KHR
+// windows wants to define this as CreateSemaphoreA or CreateSemaphoreW
+ #undef CreateSemaphore
+ #endif
 #endif
 
 static const int MAIN_W = 8, MAIN_H = 16;
@@ -172,4 +182,105 @@
     }
 }
 
+DEF_GPUTEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest, reporter, ctxInfo) {
+    GrContext* ctx = ctxInfo.grContext();
+    if (!ctx->caps()->fenceSyncSupport()) {
+        return;
+    }
+
+    const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
+                                             kPremul_SkAlphaType);
+
+    sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(ctx, SkBudgeted::kNo,
+                                                             ii, 0, kTopLeft_GrSurfaceOrigin,
+                                                             nullptr));
+
+    // Flush surface once without semaphores to make sure there is no peneding IO for it.
+    mainSurface->flush();
+
+    GrBackendSemaphore semaphore;
+    REPORTER_ASSERT(reporter, mainSurface->flushAndSignalSemaphores(1, &semaphore));
+
+    if (kOpenGL_GrBackend == ctxInfo.backend()) {
+        GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->getGpu());
+        const GrGLInterface* interface = gpu->glInterface();
+        GrGLsync sync = semaphore.glSync();
+        REPORTER_ASSERT(reporter, sync);
+        bool result;
+        GR_GL_CALL_RET(interface, result, IsSync(sync));
+        REPORTER_ASSERT(reporter, result);
+    }
+
+#ifdef SK_VULKAN
+    if (kVulkan_GrBackend == ctxInfo.backend()) {
+        GrVkGpu* gpu = static_cast<GrVkGpu*>(ctx->getGpu());
+        const GrVkInterface* interface = gpu->vkInterface();
+        VkDevice device = gpu->device();
+        VkQueue queue = gpu->queue();
+        VkCommandPool cmdPool = gpu->cmdPool();
+        VkCommandBuffer cmdBuffer;
+
+        // Create Command Buffer
+        const VkCommandBufferAllocateInfo cmdInfo = {
+            VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType
+            nullptr,                                          // pNext
+            cmdPool,                                          // commandPool
+            VK_COMMAND_BUFFER_LEVEL_PRIMARY,                  // level
+            1                                                 // bufferCount
+        };
+
+        VkResult err = GR_VK_CALL(interface, AllocateCommandBuffers(device, &cmdInfo, &cmdBuffer));
+        if (err) {
+            return;
+        }
+
+        VkCommandBufferBeginInfo cmdBufferBeginInfo;
+        memset(&cmdBufferBeginInfo, 0, sizeof(VkCommandBufferBeginInfo));
+        cmdBufferBeginInfo.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;
+        cmdBufferBeginInfo.pNext = nullptr;
+        cmdBufferBeginInfo.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;
+        cmdBufferBeginInfo.pInheritanceInfo = nullptr;
+
+        GR_VK_CALL_ERRCHECK(interface, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo));
+        GR_VK_CALL_ERRCHECK(interface, EndCommandBuffer(cmdBuffer));
+
+        VkFenceCreateInfo fenceInfo;
+        VkFence fence;
+
+        memset(&fenceInfo, 0, sizeof(VkFenceCreateInfo));
+        fenceInfo.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO;
+        err = GR_VK_CALL(interface, CreateFence(device, &fenceInfo, nullptr, &fence));
+        SkASSERT(!err);
+
+        VkPipelineStageFlags waitStages = VK_PIPELINE_STAGE_ALL_COMMANDS_BIT;
+        VkSubmitInfo submitInfo;
+        memset(&submitInfo, 0, sizeof(VkSubmitInfo));
+        submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;
+        submitInfo.pNext = nullptr;
+        submitInfo.waitSemaphoreCount = 1;
+        VkSemaphore vkSem = semaphore.vkSemaphore();
+        submitInfo.pWaitSemaphores = &vkSem;
+        submitInfo.pWaitDstStageMask = &waitStages;
+        submitInfo.commandBufferCount = 1;
+        submitInfo.pCommandBuffers = &cmdBuffer;
+        submitInfo.signalSemaphoreCount = 0;
+        submitInfo.pSignalSemaphores = nullptr;
+        GR_VK_CALL_ERRCHECK(interface, QueueSubmit(queue, 1, &submitInfo, fence));
+
+        err = GR_VK_CALL(interface, WaitForFences(device, 1, &fence, true, 3000000000));
+
+        REPORTER_ASSERT(reporter, err != VK_TIMEOUT);
+
+        GR_VK_CALL(interface, DestroyFence(device, fence, nullptr));
+        GR_VK_CALL(interface, DestroySemaphore(device, vkSem, nullptr));
+        // If the above test fails the wait semaphore will never be signaled which can cause the
+        // device to hang when tearing down (even if just tearing down GL). So we Fail here to
+        // kill things.
+        if (err == VK_TIMEOUT) {
+            SkFAIL("Waiting on semaphore indefinitely");
+        }
+    }
+#endif
+}
+
 #endif