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/include/gpu/gl/GrGLFunctions.h b/include/gpu/gl/GrGLFunctions.h
index 5166a4b..c1b3d0d 100644
--- a/include/gpu/gl/GrGLFunctions.h
+++ b/include/gpu/gl/GrGLFunctions.h
@@ -353,6 +353,7 @@
 
 /* ARB_sync */
 typedef GrGLsync (GR_GL_FUNCTION_TYPE* GrGLFenceSyncProc)(GrGLenum condition, GrGLbitfield flags);
+typedef GrGLboolean (GR_GL_FUNCTION_TYPE* GrGLIsSyncProc)(GrGLsync sync);
 typedef GrGLenum (GR_GL_FUNCTION_TYPE* GrGLClientWaitSyncProc)(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLWaitSyncProc)(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout);
 typedef GrGLvoid (GR_GL_FUNCTION_TYPE* GrGLDeleteSyncProc)(GrGLsync sync);
diff --git a/include/gpu/gl/GrGLInterface.h b/include/gpu/gl/GrGLInterface.h
index 1088047..56e655d 100644
--- a/include/gpu/gl/GrGLInterface.h
+++ b/include/gpu/gl/GrGLInterface.h
@@ -456,6 +456,7 @@
 
         /* ARB_sync */
         GrGLFunction<GrGLFenceSyncProc> fFenceSync;
+        GrGLFunction<GrGLIsSyncProc> fIsSync;
         GrGLFunction<GrGLClientWaitSyncProc> fClientWaitSync;
         GrGLFunction<GrGLWaitSyncProc> fWaitSync;
         GrGLFunction<GrGLDeleteSyncProc> fDeleteSync;
diff --git a/src/gpu/gl/GrGLAssembleInterface.cpp b/src/gpu/gl/GrGLAssembleInterface.cpp
index 64bed32..6b0234a 100644
--- a/src/gpu/gl/GrGLAssembleInterface.cpp
+++ b/src/gpu/gl/GrGLAssembleInterface.cpp
@@ -516,6 +516,7 @@
 
     if (glVer >= GR_GL_VER(3, 2) || extensions.has("GL_ARB_sync")) {
         GET_PROC(FenceSync);
+        GET_PROC(IsSync);
         GET_PROC(ClientWaitSync);
         GET_PROC(WaitSync);
         GET_PROC(DeleteSync);
@@ -940,11 +941,13 @@
 
     if (version >= GR_GL_VER(3, 0)) {
         GET_PROC(FenceSync);
+        GET_PROC(IsSync);
         GET_PROC(ClientWaitSync);
         GET_PROC(WaitSync);
         GET_PROC(DeleteSync);
     } else if (extensions.has("GL_APPLE_sync")) {
         GET_PROC_SUFFIX(FenceSync, APPLE);
+        GET_PROC_SUFFIX(IsSync, APPLE);
         GET_PROC_SUFFIX(ClientWaitSync, APPLE);
         GET_PROC_SUFFIX(WaitSync, APPLE);
         GET_PROC_SUFFIX(DeleteSync, APPLE);
diff --git a/src/gpu/gl/GrGLInterface.cpp b/src/gpu/gl/GrGLInterface.cpp
index 09d7a4f..dcad07e 100644
--- a/src/gpu/gl/GrGLInterface.cpp
+++ b/src/gpu/gl/GrGLInterface.cpp
@@ -757,6 +757,8 @@
     if (kGL_GrGLStandard == fStandard) {
         if (glVer >= GR_GL_VER(3, 2) || fExtensions.has("GL_ARB_sync")) {
             if (nullptr == fFunctions.fFenceSync ||
+                // Is sync needs to be added to chrome before uncommenting
+                // nullptr == fFunctions.fIsSync ||
                 nullptr == fFunctions.fClientWaitSync ||
                 nullptr == fFunctions.fWaitSync ||
                 nullptr == fFunctions.fDeleteSync) {
@@ -766,6 +768,8 @@
     } else if (kGLES_GrGLStandard == fStandard) {
         if (glVer >= GR_GL_VER(3, 0) || fExtensions.has("GL_APPLE_sync")) {
             if (nullptr == fFunctions.fFenceSync ||
+                // Is sync needs to be added to chrome before uncommenting
+                // nullptr == fFunctions.fIsSync ||
                 nullptr == fFunctions.fClientWaitSync ||
                 nullptr == fFunctions.fWaitSync ||
                 nullptr == fFunctions.fDeleteSync) {
diff --git a/src/gpu/gl/GrGLTestInterface.cpp b/src/gpu/gl/GrGLTestInterface.cpp
index ef86ea7..45482e0 100644
--- a/src/gpu/gl/GrGLTestInterface.cpp
+++ b/src/gpu/gl/GrGLTestInterface.cpp
@@ -314,6 +314,7 @@
     fFunctions.fFlushMappedNamedBufferRange = bind_to_member(this, &GrGLTestInterface::flushMappedNamedBufferRange);
     fFunctions.fTextureBuffer = bind_to_member(this, &GrGLTestInterface::textureBuffer);
     fFunctions.fFenceSync = bind_to_member(this, &GrGLTestInterface::fenceSync);
+    fFunctions.fIsSync = bind_to_member(this, &GrGLTestInterface::isSync);
     fFunctions.fClientWaitSync = bind_to_member(this, &GrGLTestInterface::clientWaitSync);
     fFunctions.fWaitSync = bind_to_member(this, &GrGLTestInterface::waitSync);
     fFunctions.fDeleteSync = bind_to_member(this, &GrGLTestInterface::deleteSync);
diff --git a/src/gpu/gl/GrGLTestInterface.h b/src/gpu/gl/GrGLTestInterface.h
index 69427f8..1ebf90b 100644
--- a/src/gpu/gl/GrGLTestInterface.h
+++ b/src/gpu/gl/GrGLTestInterface.h
@@ -319,6 +319,7 @@
     virtual GrGLvoid flushMappedNamedBufferRange(GrGLuint buffer, GrGLintptr offset, GrGLsizeiptr length) {}
     virtual GrGLvoid textureBuffer(GrGLuint texture, GrGLenum target, GrGLenum internalformat, GrGLuint buffer) {}
     virtual GrGLsync fenceSync(GrGLenum condition, GrGLbitfield flags) { return nullptr;  }
+    virtual GrGLboolean isSync(GrGLsync) { return false;  }
     virtual GrGLenum clientWaitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) { return GR_GL_WAIT_FAILED;  }
     virtual GrGLvoid waitSync(GrGLsync sync, GrGLbitfield flags, GrGLuint64 timeout) {}
     virtual GrGLvoid deleteSync(GrGLsync sync) {}
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