void QVGEGLWindowSurfaceVGImage::beginPaint(QWidget *widget)
{
    QEglContext *context = ensureContext(widget);
    if (context) {
        if (recreateBackBuffer || backBufferSurface == EGL_NO_SURFACE) {
            // Create a VGImage object to act as the back buffer
            // for this window.  We have to create the VGImage with a
            // current context, so activate the main surface for the window.
            context->makeCurrent(mainSurface());
            recreateBackBuffer = false;
            if (backBufferSurface != EGL_NO_SURFACE) {
                eglDestroySurface(QEgl::display(), backBufferSurface);
                backBufferSurface = EGL_NO_SURFACE;
            }
            if (backBuffer != VG_INVALID_HANDLE) {
                vgDestroyImage(backBuffer);
            }
            VGImageFormat format = qt_vg_config_to_vg_format(context);
            backBuffer = vgCreateImage
                (format, size.width(), size.height(),
                 VG_IMAGE_QUALITY_FASTER);
            if (backBuffer != VG_INVALID_HANDLE) {
                // Create an EGL surface for rendering into the VGImage.
                backBufferSurface = eglCreatePbufferFromClientBuffer
                    (QEgl::display(), EGL_OPENVG_IMAGE,
                     (EGLClientBuffer)(backBuffer),
                     context->config(), NULL);
                if (backBufferSurface == EGL_NO_SURFACE) {
                    vgDestroyImage(backBuffer);
                    backBuffer = VG_INVALID_HANDLE;
                }
            }
        }
        if (backBufferSurface != EGL_NO_SURFACE)
            context->makeCurrent(backBufferSurface);
        else
            context->makeCurrent(mainSurface());
        isPaintingActive = true;
    }
}
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;
    GrSemaphoresSubmitted submitted = mainSurface->flushAndSignalSemaphores(1, &semaphore);
    REPORTER_ASSERT(reporter, GrSemaphoresSubmitted::kYes == submitted);

    if (kOpenGL_GrBackend == ctxInfo.backend()) {
        GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->contextPriv().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->contextPriv().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) {
            SK_ABORT("Waiting on semaphore indefinitely");
        }
    }
#endif
}
void surface_semaphore_test(skiatest::Reporter* reporter,
                            const sk_gpu_test::ContextInfo& mainInfo,
                            const sk_gpu_test::ContextInfo& childInfo1,
                            const sk_gpu_test::ContextInfo& childInfo2,
                            bool flushContext) {
    GrContext* mainCtx = mainInfo.grContext();
    if (!mainCtx->caps()->fenceSyncSupport()) {
        return;
    }

    const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType,
                                             kPremul_SkAlphaType);

    sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget(mainCtx, SkBudgeted::kNo,
                                                             ii, 0, kTopLeft_GrSurfaceOrigin,
                                                             nullptr));
    SkCanvas* mainCanvas = mainSurface->getCanvas();
    mainCanvas->clear(SK_ColorBLUE);

    SkAutoTArray<GrBackendSemaphore> semaphores(2);
#ifdef SK_VULKAN
    if (kVulkan_GrBackend == mainInfo.backend()) {
        // Initialize the secondary semaphore instead of having Ganesh create one internally
        GrVkGpu* gpu = static_cast<GrVkGpu*>(mainCtx->contextPriv().getGpu());
        const GrVkInterface* interface = gpu->vkInterface();
        VkDevice device = gpu->device();

        VkSemaphore vkSem;

        VkSemaphoreCreateInfo createInfo;
        createInfo.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
        createInfo.pNext = nullptr;
        createInfo.flags = 0;
        GR_VK_CALL_ERRCHECK(interface, CreateSemaphore(device, &createInfo, nullptr, &vkSem));

        semaphores[1].initVulkan(vkSem);
    }
#endif

    if (flushContext) {
        mainCtx->flushAndSignalSemaphores(2, semaphores.get());
    } else {
        mainSurface->flushAndSignalSemaphores(2, semaphores.get());
    }

    sk_sp<SkImage> mainImage = mainSurface->makeImageSnapshot();
    GrBackendTexture backendTexture = mainImage->getBackendTexture(false);

    draw_child(reporter, childInfo1, backendTexture, semaphores[0]);

#ifdef SK_VULKAN
    if (kVulkan_GrBackend == mainInfo.backend()) {
        // In Vulkan we need to make sure we are sending the correct VkImageLayout in with the
        // backendImage. After the first child draw the layout gets changed to SHADER_READ, so
        // we just manually set that here.
        GrVkImageInfo vkInfo;
        SkAssertResult(backendTexture.getVkImageInfo(&vkInfo));
        vkInfo.updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
    }
#endif

    draw_child(reporter, childInfo2, backendTexture, semaphores[1]);
}