|  | /* | 
|  | * Copyright 2017 Google Inc. | 
|  | * | 
|  | * Use of this source code is governed by a BSD-style license that can be | 
|  | * found in the LICENSE file. | 
|  | */ | 
|  |  | 
|  | #include "include/core/SkAlphaType.h" | 
|  | #include "include/core/SkBitmap.h" | 
|  | #include "include/core/SkCanvas.h" | 
|  | #include "include/core/SkColor.h" | 
|  | #include "include/core/SkColorSpace.h" | 
|  | #include "include/core/SkColorType.h" | 
|  | #include "include/core/SkImage.h" | 
|  | #include "include/core/SkImageInfo.h" | 
|  | #include "include/core/SkPaint.h" | 
|  | #include "include/core/SkRect.h" | 
|  | #include "include/core/SkRefCnt.h" | 
|  | #include "include/core/SkString.h" | 
|  | #include "include/core/SkSurface.h" | 
|  | #include "include/core/SkTypes.h" | 
|  | #include "include/gpu/GpuTypes.h" | 
|  | #include "include/gpu/GrBackendSemaphore.h" | 
|  | #include "include/gpu/GrBackendSurface.h" | 
|  | #include "include/gpu/GrDirectContext.h" | 
|  | #include "include/gpu/GrTypes.h" | 
|  | #include "include/private/base/SkTemplates.h" | 
|  | #include "src/gpu/ganesh/GrCaps.h" | 
|  | #include "src/gpu/ganesh/GrDirectContextPriv.h" | 
|  | #include "tests/CtsEnforcement.h" | 
|  | #include "tests/Test.h" | 
|  | #include "tools/gpu/TestContext.h" | 
|  |  | 
|  | #include <string> | 
|  | #include <cstdint> | 
|  | #include <initializer_list> | 
|  |  | 
|  | #ifdef SK_GL | 
|  | #include "include/gpu/gl/GrGLFunctions.h" | 
|  | #include "include/gpu/gl/GrGLInterface.h" | 
|  | #include "include/gpu/gl/GrGLTypes.h" | 
|  | #include "src/gpu/ganesh/gl/GrGLGpu.h" | 
|  | #include "src/gpu/ganesh/gl/GrGLUtil.h" | 
|  | #endif | 
|  |  | 
|  | #ifdef SK_VULKAN | 
|  | #include "src/gpu/ganesh/vk/GrVkCommandPool.h" | 
|  | #include "src/gpu/ganesh/vk/GrVkGpu.h" | 
|  | #include "src/gpu/ganesh/vk/GrVkUtil.h" | 
|  |  | 
|  | #include <vulkan/vulkan_core.h> | 
|  |  | 
|  | namespace skgpu { struct VulkanInterface; } | 
|  |  | 
|  | #ifdef VK_USE_PLATFORM_WIN32_KHR | 
|  | // windows wants to define this as CreateSemaphoreA or CreateSemaphoreW | 
|  | #undef CreateSemaphore | 
|  | #endif | 
|  | #endif | 
|  |  | 
|  | using namespace skia_private; | 
|  |  | 
|  | struct GrContextOptions; | 
|  |  | 
|  | static const int MAIN_W = 8, MAIN_H = 16; | 
|  | static const int CHILD_W = 16, CHILD_H = 16; | 
|  |  | 
|  | void check_pixels(skiatest::Reporter* reporter, const SkBitmap& bitmap) { | 
|  | const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels()); | 
|  |  | 
|  | bool failureFound = false; | 
|  | SkPMColor expectedPixel; | 
|  | for (int cy = 0; cy < CHILD_H && !failureFound; ++cy) { | 
|  | for (int cx = 0; cx < CHILD_W && !failureFound; ++cx) { | 
|  | SkPMColor canvasPixel = canvasPixels[cy * CHILD_W + cx]; | 
|  | if (cy < CHILD_H / 2) { | 
|  | if (cx < CHILD_W / 2) { | 
|  | expectedPixel = 0xFF0000FF; // Red | 
|  | } else { | 
|  | expectedPixel = 0xFFFF0000; // Blue | 
|  | } | 
|  | } else { | 
|  | expectedPixel = 0xFF00FF00; // Green | 
|  | } | 
|  | if (expectedPixel != canvasPixel) { | 
|  | failureFound = true; | 
|  | ERRORF(reporter, "Wrong color at %d, %d. Got 0x%08x when we expected 0x%08x", | 
|  | cx, cy, canvasPixel, expectedPixel); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  |  | 
|  | void draw_child(skiatest::Reporter* reporter, | 
|  | const sk_gpu_test::ContextInfo& childInfo, | 
|  | const GrBackendTexture& backendTexture, | 
|  | const GrBackendSemaphore& semaphore) { | 
|  |  | 
|  | childInfo.testContext()->makeCurrent(); | 
|  |  | 
|  | const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType, | 
|  | kPremul_SkAlphaType); | 
|  |  | 
|  | auto childDContext = childInfo.directContext(); | 
|  | sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget( | 
|  | childDContext, skgpu::Budgeted::kNo, childII, 0, kTopLeft_GrSurfaceOrigin, nullptr)); | 
|  |  | 
|  | sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childDContext, | 
|  | backendTexture, | 
|  | kTopLeft_GrSurfaceOrigin, | 
|  | kRGBA_8888_SkColorType, | 
|  | kPremul_SkAlphaType, | 
|  | nullptr, | 
|  | nullptr, | 
|  | nullptr); | 
|  |  | 
|  | SkCanvas* childCanvas = childSurface->getCanvas(); | 
|  | childCanvas->clear(SK_ColorRED); | 
|  |  | 
|  | childSurface->wait(1, &semaphore); | 
|  |  | 
|  | childCanvas->drawImage(childImage, CHILD_W/2, 0); | 
|  |  | 
|  | SkPaint paint; | 
|  | paint.setColor(SK_ColorGREEN); | 
|  | SkIRect rect = SkIRect::MakeLTRB(0, CHILD_H/2, CHILD_W, CHILD_H); | 
|  | childCanvas->drawIRect(rect, paint); | 
|  |  | 
|  | // read pixels | 
|  | SkBitmap bitmap; | 
|  | bitmap.allocPixels(childII); | 
|  | childSurface->readPixels(bitmap, 0, 0); | 
|  |  | 
|  | check_pixels(reporter, bitmap); | 
|  | } | 
|  |  | 
|  | enum class FlushType { kSurface, kImage, kContext }; | 
|  |  | 
|  | 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, | 
|  | FlushType flushType) { | 
|  | auto mainCtx = mainInfo.directContext(); | 
|  | if (!mainCtx->priv().caps()->semaphoreSupport()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, | 
|  | kPremul_SkAlphaType); | 
|  |  | 
|  | sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget( | 
|  | mainCtx, skgpu::Budgeted::kNo, ii, 0, kTopLeft_GrSurfaceOrigin, nullptr)); | 
|  | SkCanvas* mainCanvas = mainSurface->getCanvas(); | 
|  | auto blueSurface = mainSurface->makeSurface(ii); | 
|  | blueSurface->getCanvas()->clear(SK_ColorBLUE); | 
|  | auto blueImage = blueSurface->makeImageSnapshot(); | 
|  | blueSurface.reset(); | 
|  | mainCanvas->drawImage(blueImage, 0, 0); | 
|  |  | 
|  | AutoTArray<GrBackendSemaphore> semaphores(2); | 
|  | #ifdef SK_VULKAN | 
|  | if (GrBackendApi::kVulkan == mainInfo.backend()) { | 
|  | // Initialize the secondary semaphore instead of having Ganesh create one internally | 
|  | GrVkGpu* gpu = static_cast<GrVkGpu*>(mainCtx->priv().getGpu()); | 
|  | 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(gpu, CreateSemaphore(device, &createInfo, nullptr, &vkSem)); | 
|  |  | 
|  | semaphores[1].initVulkan(vkSem); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | GrFlushInfo info; | 
|  | info.fNumSemaphores = 2; | 
|  | info.fSignalSemaphores = semaphores.get(); | 
|  | switch (flushType) { | 
|  | case FlushType::kSurface: | 
|  | mainSurface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, info); | 
|  | break; | 
|  | case FlushType::kImage: | 
|  | blueImage->flush(mainCtx, info); | 
|  | break; | 
|  | case FlushType::kContext: | 
|  | mainCtx->flush(info); | 
|  | break; | 
|  | } | 
|  | mainCtx->submit(); | 
|  |  | 
|  | GrBackendTexture backendTexture = mainSurface->getBackendTexture( | 
|  | SkSurface::BackendHandleAccess::kFlushRead_BackendHandleAccess); | 
|  |  | 
|  | draw_child(reporter, childInfo1, backendTexture, semaphores[0]); | 
|  |  | 
|  | #ifdef SK_VULKAN | 
|  | if (GrBackendApi::kVulkan == 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. | 
|  | backendTexture.setVkImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | draw_child(reporter, childInfo2, backendTexture, semaphores[1]); | 
|  | } | 
|  |  | 
|  | #ifdef SK_GL | 
|  | DEF_GANESH_TEST(SurfaceSemaphores, reporter, options, CtsEnforcement::kApiLevel_T) { | 
|  | #if defined(SK_BUILD_FOR_UNIX) || defined(SK_BUILD_FOR_WIN) || defined(SK_BUILD_FOR_MAC) | 
|  | static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGL_ContextType; | 
|  | #else | 
|  | static constexpr auto kNativeGLType = sk_gpu_test::GrContextFactory::kGLES_ContextType; | 
|  | #endif | 
|  |  | 
|  | for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) { | 
|  | for (auto flushType : {FlushType::kSurface, FlushType::kImage, FlushType::kContext}) { | 
|  | sk_gpu_test::GrContextFactory::ContextType contextType = | 
|  | (sk_gpu_test::GrContextFactory::ContextType) typeInt; | 
|  | // Use "native" instead of explicitly trying OpenGL and OpenGL ES. Do not use GLES on | 
|  | // desktop since tests do not account for not fixing http://skbug.com/2809 | 
|  | if (contextType == sk_gpu_test::GrContextFactory::kGL_ContextType || | 
|  | contextType == sk_gpu_test::GrContextFactory::kGLES_ContextType) { | 
|  | if (contextType != kNativeGLType) { | 
|  | continue; | 
|  | } | 
|  | } | 
|  | sk_gpu_test::GrContextFactory factory(options); | 
|  | sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType); | 
|  | if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) { | 
|  | continue; | 
|  | } | 
|  | skiatest::ReporterContext ctx( | 
|  | reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType))); | 
|  | if (ctxInfo.directContext()) { | 
|  | sk_gpu_test::ContextInfo child1 = | 
|  | factory.getSharedContextInfo(ctxInfo.directContext(), 0); | 
|  | sk_gpu_test::ContextInfo child2 = | 
|  | factory.getSharedContextInfo(ctxInfo.directContext(), 1); | 
|  | if (!child1.directContext() || !child2.directContext()) { | 
|  | continue; | 
|  | } | 
|  |  | 
|  | surface_semaphore_test(reporter, ctxInfo, child1, child2, flushType); | 
|  | } | 
|  | } | 
|  | } | 
|  | } | 
|  | #endif | 
|  |  | 
|  | DEF_GANESH_TEST_FOR_RENDERING_CONTEXTS(EmptySurfaceSemaphoreTest, | 
|  | reporter, | 
|  | ctxInfo, | 
|  | CtsEnforcement::kApiLevel_T) { | 
|  | auto ctx = ctxInfo.directContext(); | 
|  | if (!ctx->priv().caps()->semaphoreSupport()) { | 
|  | return; | 
|  | } | 
|  |  | 
|  | const SkImageInfo ii = SkImageInfo::Make(MAIN_W, MAIN_H, kRGBA_8888_SkColorType, | 
|  | kPremul_SkAlphaType); | 
|  |  | 
|  | sk_sp<SkSurface> mainSurface(SkSurface::MakeRenderTarget( | 
|  | ctx, skgpu::Budgeted::kNo, ii, 0, kTopLeft_GrSurfaceOrigin, nullptr)); | 
|  |  | 
|  | // Flush surface once without semaphores to make sure there is no peneding IO for it. | 
|  | mainSurface->flushAndSubmit(); | 
|  |  | 
|  | GrBackendSemaphore semaphore; | 
|  | GrFlushInfo flushInfo; | 
|  | flushInfo.fNumSemaphores = 1; | 
|  | flushInfo.fSignalSemaphores = &semaphore; | 
|  | GrSemaphoresSubmitted submitted = | 
|  | mainSurface->flush(SkSurface::BackendSurfaceAccess::kNoAccess, flushInfo); | 
|  | REPORTER_ASSERT(reporter, GrSemaphoresSubmitted::kYes == submitted); | 
|  | ctx->submit(); | 
|  |  | 
|  | #ifdef SK_GL | 
|  | if (GrBackendApi::kOpenGL == ctxInfo.backend()) { | 
|  | GrGLGpu* gpu = static_cast<GrGLGpu*>(ctx->priv().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); | 
|  | } | 
|  | #endif | 
|  |  | 
|  | #ifdef SK_VULKAN | 
|  | if (GrBackendApi::kVulkan == ctxInfo.backend()) { | 
|  | GrVkGpu* gpu = static_cast<GrVkGpu*>(ctx->priv().getGpu()); | 
|  | const skgpu::VulkanInterface* interface = gpu->vkInterface(); | 
|  | VkDevice device = gpu->device(); | 
|  | VkQueue queue = gpu->queue(); | 
|  | GrVkCommandPool* cmdPool = gpu->cmdPool(); | 
|  | VkCommandBuffer cmdBuffer; | 
|  |  | 
|  | // Create Command Buffer | 
|  | const VkCommandBufferAllocateInfo cmdInfo = { | 
|  | VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,   // sType | 
|  | nullptr,                                          // pNext | 
|  | cmdPool->vkCommandPool(),                         // 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(gpu, BeginCommandBuffer(cmdBuffer, &cmdBufferBeginInfo)); | 
|  | GR_VK_CALL_ERRCHECK(gpu, 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(gpu, 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 | 
|  | } |