b3c9745911
The majority of our gm testing has been disabling nvpr, which doesn't match our real-world behavior where we use nvpr whenever available. This CL fixes the issue by completely removing the explicit nvpr configs. Now if we have nvpr, you get it. This CL also lowers the nvpr priority in the path renderer chain and adds a "NonNVPR" job on Quadro where we can continue to test our non-nvpr codepaths on NVIDIA. Bug: skia: Change-Id: I6a36f1101c8218adcaaf10cab25d2c28e70371f0 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/223828 Reviewed-by: Brian Salomon <bsalomon@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
293 lines
12 KiB
C++
293 lines
12 KiB
C++
/*
|
|
* Copyright 2018 Google Inc.
|
|
*
|
|
* Use of this source code is governed by a BSD-style license that can be
|
|
* found in the LICENSE file.
|
|
*/
|
|
|
|
// This is a GPU-backend specific test. It relies on static intializers to work
|
|
|
|
#include "include/core/SkTypes.h"
|
|
|
|
#if SK_SUPPORT_GPU && defined(SK_VULKAN)
|
|
|
|
#include "include/gpu/vk/GrVkVulkan.h"
|
|
|
|
#include "include/core/SkDrawable.h"
|
|
#include "include/core/SkSurface.h"
|
|
#include "include/gpu/GrBackendDrawableInfo.h"
|
|
#include "src/gpu/GrContextPriv.h"
|
|
#include "src/gpu/vk/GrVkGpu.h"
|
|
#include "src/gpu/vk/GrVkInterface.h"
|
|
#include "src/gpu/vk/GrVkMemory.h"
|
|
#include "src/gpu/vk/GrVkSecondaryCBDrawContext.h"
|
|
#include "src/gpu/vk/GrVkUtil.h"
|
|
#include "tests/Test.h"
|
|
#include "tools/gpu/GrContextFactory.h"
|
|
|
|
using sk_gpu_test::GrContextFactory;
|
|
|
|
static const int DEV_W = 16, DEV_H = 16;
|
|
|
|
class TestDrawable : public SkDrawable {
|
|
public:
|
|
TestDrawable(const GrVkInterface* interface, GrContext* context, int32_t width, int32_t height)
|
|
: INHERITED()
|
|
, fInterface(interface)
|
|
, fContext(context)
|
|
, fWidth(width)
|
|
, fHeight(height) {}
|
|
|
|
~TestDrawable() override {}
|
|
|
|
class DrawHandlerBasic : public GpuDrawHandler {
|
|
public:
|
|
DrawHandlerBasic(const GrVkInterface* interface, int32_t width, int32_t height)
|
|
: INHERITED()
|
|
, fInterface(interface)
|
|
, fWidth(width)
|
|
, fHeight(height) {}
|
|
~DrawHandlerBasic() override {}
|
|
|
|
void draw(const GrBackendDrawableInfo& info) override {
|
|
GrVkDrawableInfo vkInfo;
|
|
SkAssertResult(info.getVkDrawableInfo(&vkInfo));
|
|
|
|
// Clear to Red
|
|
VkClearColorValue vkColor;
|
|
vkColor.float32[0] = 1.0f; // r
|
|
vkColor.float32[1] = 0.0f; // g
|
|
vkColor.float32[2] = 0.0f; // b
|
|
vkColor.float32[3] = 1.0f; // a
|
|
|
|
// Clear right half of render target
|
|
VkClearRect clearRect;
|
|
clearRect.rect.offset = { fWidth / 2, 0 };
|
|
clearRect.rect.extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
|
|
clearRect.baseArrayLayer = 0;
|
|
clearRect.layerCount = 1;
|
|
|
|
VkClearAttachment attachment;
|
|
attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;
|
|
attachment.colorAttachment = vkInfo.fColorAttachmentIndex;
|
|
attachment.clearValue.color = vkColor;
|
|
|
|
GR_VK_CALL(fInterface, CmdClearAttachments(vkInfo.fSecondaryCommandBuffer,
|
|
1,
|
|
&attachment,
|
|
1,
|
|
&clearRect));
|
|
vkInfo.fDrawBounds->offset = { fWidth / 2, 0 };
|
|
vkInfo.fDrawBounds->extent = { (uint32_t)fWidth / 2, (uint32_t)fHeight };
|
|
}
|
|
private:
|
|
const GrVkInterface* fInterface;
|
|
int32_t fWidth;
|
|
int32_t fHeight;
|
|
|
|
typedef GpuDrawHandler INHERITED;
|
|
};
|
|
|
|
typedef void (*DrawProc)(TestDrawable*, const SkMatrix&, const SkIRect&,
|
|
const SkImageInfo&, const GrVkDrawableInfo&);
|
|
typedef void (*SubmitProc)(TestDrawable*);
|
|
|
|
// Exercises the exporting of a secondary command buffer from one GrContext and then importing
|
|
// it into a second GrContext. We then draw to the secondary command buffer from the second
|
|
// GrContext.
|
|
class DrawHandlerImport : public GpuDrawHandler {
|
|
public:
|
|
DrawHandlerImport(TestDrawable* td, DrawProc drawProc, SubmitProc submitProc,
|
|
const SkMatrix& matrix,
|
|
const SkIRect& clipBounds,
|
|
const SkImageInfo& bufferInfo)
|
|
: INHERITED()
|
|
, fTestDrawable(td)
|
|
, fDrawProc(drawProc)
|
|
, fSubmitProc(submitProc)
|
|
, fMatrix(matrix)
|
|
, fClipBounds(clipBounds)
|
|
, fBufferInfo(bufferInfo) {}
|
|
~DrawHandlerImport() override {
|
|
fSubmitProc(fTestDrawable);
|
|
}
|
|
|
|
void draw(const GrBackendDrawableInfo& info) override {
|
|
GrVkDrawableInfo vkInfo;
|
|
SkAssertResult(info.getVkDrawableInfo(&vkInfo));
|
|
|
|
fDrawProc(fTestDrawable, fMatrix, fClipBounds, fBufferInfo, vkInfo);
|
|
}
|
|
private:
|
|
TestDrawable* fTestDrawable;
|
|
DrawProc fDrawProc;
|
|
SubmitProc fSubmitProc;
|
|
const SkMatrix fMatrix;
|
|
const SkIRect fClipBounds;
|
|
const SkImageInfo fBufferInfo;
|
|
|
|
typedef GpuDrawHandler INHERITED;
|
|
};
|
|
|
|
// Helper function to test drawing to a secondary command buffer that we imported into the
|
|
// GrContext using a GrVkSecondaryCBDrawContext.
|
|
static void ImportDraw(TestDrawable* td, const SkMatrix& matrix, const SkIRect& clipBounds,
|
|
const SkImageInfo& bufferInfo, const GrVkDrawableInfo& info) {
|
|
td->fDrawContext = GrVkSecondaryCBDrawContext::Make(td->fContext, bufferInfo, info, nullptr);
|
|
if (!td->fDrawContext) {
|
|
return;
|
|
}
|
|
|
|
SkCanvas* canvas = td->fDrawContext->getCanvas();
|
|
canvas->clipRect(SkRect::Make(clipBounds));
|
|
canvas->setMatrix(matrix);
|
|
|
|
SkIRect rect = SkIRect::MakeXYWH(td->fWidth/2, 0, td->fWidth/4, td->fHeight);
|
|
SkPaint paint;
|
|
paint.setColor(SK_ColorRED);
|
|
canvas->drawIRect(rect, paint);
|
|
|
|
// Draw to an offscreen target so that we end up with a mix of "real" secondary command
|
|
// buffers and the imported secondary command buffer.
|
|
sk_sp<SkSurface> surf = SkSurface::MakeRenderTarget(td->fContext, SkBudgeted::kYes,
|
|
bufferInfo);
|
|
surf->getCanvas()->clear(SK_ColorRED);
|
|
|
|
SkRect dstRect = SkRect::MakeXYWH(3*td->fWidth/4, 0, td->fWidth/4, td->fHeight);
|
|
SkIRect srcRect = SkIRect::MakeWH(td->fWidth/4, td->fHeight);
|
|
canvas->drawImageRect(surf->makeImageSnapshot(), srcRect, dstRect, &paint);
|
|
|
|
td->fDrawContext->flush();
|
|
}
|
|
|
|
// Helper function to test waiting for the imported secondary command buffer to be submitted on
|
|
// its original context and then cleaning up the GrVkSecondaryCBDrawContext from this GrContext.
|
|
static void ImportSubmitted(TestDrawable* td) {
|
|
// Typical use case here would be to create a fence that we submit to the gpu and then wait
|
|
// on before releasing the GrVkSecondaryCBDrawContext resources. To simulate that for this
|
|
// test (and since we are running single threaded anyways), we will just force a sync of
|
|
// the gpu and cpu here.
|
|
td->fContext->priv().getGpu()->testingOnly_flushGpuAndSync();
|
|
|
|
td->fDrawContext->releaseResources();
|
|
// We release the GrContext here manually to test that we waited long enough before
|
|
// releasing the GrVkSecondaryCBDrawContext. This simulates when a client is able to delete
|
|
// the GrContext it used to imported the secondary command buffer. If we had released the
|
|
// GrContext's resources earlier (before waiting on the gpu above), we would get vulkan
|
|
// validation layer errors saying we freed some vulkan objects while they were still in use
|
|
// on the GPU.
|
|
td->fContext->releaseResourcesAndAbandonContext();
|
|
}
|
|
|
|
|
|
std::unique_ptr<GpuDrawHandler> onSnapGpuDrawHandler(GrBackendApi backendApi,
|
|
const SkMatrix& matrix,
|
|
const SkIRect& clipBounds,
|
|
const SkImageInfo& bufferInfo) override {
|
|
if (backendApi != GrBackendApi::kVulkan) {
|
|
return nullptr;
|
|
}
|
|
std::unique_ptr<GpuDrawHandler> draw;
|
|
if (fContext) {
|
|
draw.reset(new DrawHandlerImport(this, ImportDraw, ImportSubmitted, matrix,
|
|
clipBounds, bufferInfo));
|
|
} else {
|
|
draw.reset(new DrawHandlerBasic(fInterface, fWidth, fHeight));
|
|
}
|
|
return draw;
|
|
}
|
|
|
|
SkRect onGetBounds() override {
|
|
return SkRect::MakeLTRB(fWidth / 2, 0, fWidth, fHeight);
|
|
}
|
|
|
|
void onDraw(SkCanvas*) override {
|
|
SkASSERT(false);
|
|
}
|
|
|
|
private:
|
|
const GrVkInterface* fInterface;
|
|
GrContext* fContext;
|
|
sk_sp<GrVkSecondaryCBDrawContext> fDrawContext;
|
|
int32_t fWidth;
|
|
int32_t fHeight;
|
|
|
|
typedef SkDrawable INHERITED;
|
|
};
|
|
|
|
void draw_drawable_test(skiatest::Reporter* reporter, GrContext* context, GrContext* childContext) {
|
|
GrVkGpu* gpu = static_cast<GrVkGpu*>(context->priv().getGpu());
|
|
|
|
const SkImageInfo ii = SkImageInfo::Make(DEV_W, DEV_H, kRGBA_8888_SkColorType,
|
|
kPremul_SkAlphaType);
|
|
sk_sp<SkSurface> surface(SkSurface::MakeRenderTarget(context, SkBudgeted::kNo,
|
|
ii, 0, kTopLeft_GrSurfaceOrigin, nullptr));
|
|
SkCanvas* canvas = surface->getCanvas();
|
|
canvas->clear(SK_ColorBLUE);
|
|
|
|
sk_sp<TestDrawable> drawable(new TestDrawable(gpu->vkInterface(), childContext, DEV_W, DEV_H));
|
|
canvas->drawDrawable(drawable.get());
|
|
|
|
SkPaint paint;
|
|
paint.setColor(SK_ColorGREEN);
|
|
SkIRect rect = SkIRect::MakeLTRB(0, DEV_H/2, DEV_W, DEV_H);
|
|
canvas->drawIRect(rect, paint);
|
|
|
|
// read pixels
|
|
SkBitmap bitmap;
|
|
bitmap.allocPixels(ii);
|
|
canvas->readPixels(bitmap, 0, 0);
|
|
|
|
const uint32_t* canvasPixels = static_cast<const uint32_t*>(bitmap.getPixels());
|
|
bool failureFound = false;
|
|
SkPMColor expectedPixel;
|
|
for (int cy = 0; cy < DEV_H && !failureFound; ++cy) {
|
|
for (int cx = 0; cx < DEV_W && !failureFound; ++cx) {
|
|
SkPMColor canvasPixel = canvasPixels[cy * DEV_W + cx];
|
|
if (cy < DEV_H / 2) {
|
|
if (cx < DEV_W / 2) {
|
|
expectedPixel = 0xFFFF0000; // Blue
|
|
} else {
|
|
expectedPixel = 0xFF0000FF; // Red
|
|
}
|
|
} 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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEF_GPUTEST_FOR_VULKAN_CONTEXT(VkDrawableTest, reporter, ctxInfo) {
|
|
draw_drawable_test(reporter, ctxInfo.grContext(), nullptr);
|
|
}
|
|
|
|
DEF_GPUTEST(VkDrawableImportTest, reporter, options) {
|
|
for (int typeInt = 0; typeInt < sk_gpu_test::GrContextFactory::kContextTypeCnt; ++typeInt) {
|
|
sk_gpu_test::GrContextFactory::ContextType contextType =
|
|
(sk_gpu_test::GrContextFactory::ContextType) typeInt;
|
|
if (contextType != sk_gpu_test::GrContextFactory::kVulkan_ContextType) {
|
|
continue;
|
|
}
|
|
sk_gpu_test::GrContextFactory factory(options);
|
|
sk_gpu_test::ContextInfo ctxInfo = factory.getContextInfo(contextType);
|
|
skiatest::ReporterContext ctx(
|
|
reporter, SkString(sk_gpu_test::GrContextFactory::ContextTypeName(contextType)));
|
|
if (ctxInfo.grContext()) {
|
|
sk_gpu_test::ContextInfo child =
|
|
factory.getSharedContextInfo(ctxInfo.grContext(), 0);
|
|
if (!child.grContext()) {
|
|
continue;
|
|
}
|
|
|
|
draw_drawable_test(reporter, ctxInfo.grContext(), child.grContext());
|
|
}
|
|
}
|
|
}
|
|
|
|
#endif
|