skia2/tests/SurfaceSemaphoreTest.cpp
Brian Salomon dcfca431e3 Use GrContextFactories that produce a single GrContext in unit tests.
This is to alleviate problems due to the command buffer getting bent out of shape when the current
OpenGL context is switched out from under it (because we ran a test with a native GL context). This,
however is not a full solution. More changes will be required to ensure that after running each
command buffer or native test we bind the null context. This does allow us to take a step in that
direction without breaking anything too badly. Moreover, there is no real benefit to reusing a
GrContextFactory.

Modifies DEF_GPUTEST to take GrContextOptions rather than a factory to use. Tests were already using
their own factories anyway.

In tests that use GrContextFactory the factory instance is moved to the inner loop.

Modifies gpucts and skia_test to not use persistent GrContextFactories.

Change-Id: Ie7a36793545c775f2f30653ead6fec93a3d22717
Reviewed-on: https://skia-review.googlesource.com/71861
Reviewed-by: Brian Osman <brianosman@google.com>
Commit-Queue: Brian Salomon <bsalomon@google.com>
2017-11-15 22:03:07 +00:00

317 lines
13 KiB
C++

/*
* 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 "SkTypes.h"
#if SK_SUPPORT_GPU
#include "GrContextFactory.h"
#include "GrTest.h"
#include "Test.h"
#include "GrBackendSemaphore.h"
#include "GrBackendSurface.h"
#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;
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 GrBackendObject& backendImage,
const GrBackendSemaphore& semaphore) {
GrBackendTexture backendTexture = GrTest::CreateBackendTexture(childInfo.backend(),
MAIN_W, MAIN_H,
kRGBA_8888_GrPixelConfig,
GrMipMapped::kNo,
backendImage);
childInfo.testContext()->makeCurrent();
const SkImageInfo childII = SkImageInfo::Make(CHILD_W, CHILD_H, kRGBA_8888_SkColorType,
kPremul_SkAlphaType);
GrContext* childCtx = childInfo.grContext();
sk_sp<SkSurface> childSurface(SkSurface::MakeRenderTarget(childCtx, SkBudgeted::kNo,
childII, 0, kTopLeft_GrSurfaceOrigin,
nullptr));
sk_sp<SkImage> childImage = SkImage::MakeFromTexture(childCtx,
backendTexture,
kTopLeft_GrSurfaceOrigin,
kPremul_SkAlphaType,
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);
}
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->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();
GrBackendObject backendImage = mainImage->getTextureHandle(false);
draw_child(reporter, childInfo1, backendImage, 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 = (GrVkImageInfo*)backendImage;
vkInfo->updateImageLayout(VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL);
}
#endif
draw_child(reporter, childInfo2, backendImage, semaphores[1]);
}
DEF_GPUTEST(SurfaceSemaphores, reporter, options) {
#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 flushContext : { false, true }) {
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, sk_gpu_test::GrContextFactory::ContextOverrides::kDisableNVPR);
if (!sk_gpu_test::GrContextFactory::IsRenderingContext(contextType)) {
continue;
}
skiatest::ReporterContext ctx(
reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
if (ctxInfo.grContext()) {
sk_gpu_test::ContextInfo child1 =
factory.getSharedContextInfo(ctxInfo.grContext(), 0);
sk_gpu_test::ContextInfo child2 =
factory.getSharedContextInfo(ctxInfo.grContext(), 1);
if (!child1.grContext() || !child2.grContext()) {
continue;
}
surface_semaphore_test(reporter, ctxInfo, child1, child2, flushContext);
}
}
}
}
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->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) {
SK_ABORT("Waiting on semaphore indefinitely");
}
}
#endif
}
#endif