skia2/tests/DMSAATest.cpp
Brian Osman 3695bdb587 Refactor SkMatrixProvider slightly
There was only one virtual method, so switch that to a bool stored in
the base class. The derived types exist as hints for the reader, and an
easy way to adjust how the new localToDevice is constructed.

With this change, we don't need SkSimpleMatrixProvider. SkMatrixProvider
is concrete, so we can use it directly. SkOverrideDeviceMatrixProvider
no longer needs the original provider for anything, so remove that
parameter. It now exists solely to inhibit the hitsPixelCenters flag.

Fix a few spots (SkParticleBinding, some sites in SkRuntimeEffect) where
we used SkSimpleMatrixProvider, even though the local coordinates being
passed did not obey the hits-pixel-centers constraints.

Most importantly, document how localToDeviceHitsPixelCenters works.

Change-Id: Ibe9060bac0822d0edf52a507d390bd198d8e6dbd
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/482176
Reviewed-by: John Stiles <johnstiles@google.com>
Commit-Queue: Brian Osman <brianosman@google.com>
2021-12-09 20:10:58 +00:00

325 lines
15 KiB
C++

/*
* Copyright 2021 Google Inc.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "tests/Test.h"
#include "include/core/SkVertices.h"
#include "src/core/SkBlendModePriv.h"
#include "src/core/SkMatrixProvider.h"
#include "src/core/SkSurfacePriv.h"
#include "src/gpu/GrStyle.h"
#include "src/gpu/v1/SurfaceDrawContext_v1.h"
namespace {
static SkSurfaceProps kDMSAAProps(SkSurfaceProps::kDynamicMSAA_Flag, kUnknown_SkPixelGeometry);
static SkSurfaceProps kBasicProps(0, kUnknown_SkPixelGeometry);
constexpr static SkPMColor4f kTransYellow = {.5f,.5f,.0f,.5f};
constexpr static SkPMColor4f kTransCyan = {.0f,.5f,.5f,.5f};
constexpr static int kWidth=10, kHeight=10;
}
static void draw_paint_with_aa(skgpu::v1::SurfaceDrawContext* sdc,
const SkPMColor4f& color,
SkBlendMode blendMode) {
GrPaint paint;
paint.setColor4f(color);
paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
sdc->drawRect(nullptr, std::move(paint), GrAA::kYes, SkMatrix::I(),
SkRect::MakeIWH(kWidth, kHeight), nullptr);
}
static void draw_paint_with_dmsaa(skgpu::v1::SurfaceDrawContext* sdc,
const SkPMColor4f& color,
SkBlendMode blendMode) {
// drawVertices should always trigger dmsaa, but draw something non-rectangular just to be 100%
// certain.
static const SkPoint kVertices[3] = {{-.5f,-.5f}, {kWidth * 2.1f, 0}, {0, kHeight * 2.1f}};
SkVertices::Builder builder(SkVertices::kTriangles_VertexMode, 3, 0, 0);
memcpy(builder.positions(), kVertices, sizeof(kVertices));
auto vertices = builder.detach();
GrPaint paint;
paint.setColor4f(color);
paint.setXPFactory(SkBlendMode_AsXPFactory(blendMode));
sdc->drawVertices(nullptr, std::move(paint), SkMatrixProvider(SkMatrix::I()), vertices);
}
static bool fuzzy_equals(const float a[4], const SkPMColor4f& b) {
constexpr static float kTolerance = 2.5f / 256;
for (int i = 0; i < 4; ++i) {
if (!SkScalarNearlyEqual(a[i], b.vec()[i], kTolerance)) {
return false;
}
}
return true;
}
static void check_sdc_color(skiatest::Reporter* reporter,
skgpu::v1::SurfaceDrawContext* sdc,
GrDirectContext* ctx,
const SkPMColor4f& color) {
auto info = SkImageInfo::Make(kWidth, kHeight, kRGBA_F32_SkColorType, kPremul_SkAlphaType);
GrPixmap pixmap = GrPixmap::Allocate(info);
sdc->readPixels(ctx, pixmap, {0, 0});
auto pix = static_cast<const float*>(pixmap.addr());
for (int y = 0; y < kHeight; ++y) {
for (int x = 0; x < kWidth; ++x) {
if (!fuzzy_equals(pix, color)) {
ERRORF(reporter, "SDC color mismatch.\n"
"Got [%0.3f, %0.3f, %0.3f, %0.3f]\n"
"Expected [%0.3f, %0.3f, %0.3f, %0.3f]",
pix[0], pix[1], pix[2], pix[3], color.fR, color.fG, color.fB, color.fA);
return;
}
pix += 4;
}
}
}
DEF_GPUTEST_FOR_CONTEXTS(DMSAA_preserve_contents,
&sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
nullptr) {
auto dContext = ctxInfo.directContext();
auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
SkBackingFit::kApprox, {kWidth, kHeight},
kDMSAAProps);
// Initialize the texture and dmsaa attachment with transparent.
draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
// Clear the main texture to yellow.
sdc->clear(kTransYellow);
// Close the opsTask by doing a readback.
check_sdc_color(reporter, sdc.get(), dContext, kTransYellow);
// Now the DMSAA attachment is clear and the texture is yellow. Blend cyan into the DMSAA
// attachment. This will fail if the yellow from the main texture doesn't get copied into the
// DMSAA attachment before the renderPass.
draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver);
SkPMColor4f dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, kTransYellow);
check_sdc_color(reporter, sdc.get(), dContext, dstColor);
}
static void require_dst_reads(GrContextOptions* options) {
options->fSuppressAdvancedBlendEquations = true;
options->fSuppressFramebufferFetch = true;
}
DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read, &sk_gpu_test::GrContextFactory::IsRenderingContext,
reporter, ctxInfo, require_dst_reads) {
auto dContext = ctxInfo.directContext();
auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
SkBackingFit::kApprox, {kWidth, kHeight},
kDMSAAProps);
// Initialize the texture and dmsaa attachment with transparent.
draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
sdc->clear(SK_PMColor4fWHITE);
SkPMColor4f dstColor = SK_PMColor4fWHITE;
draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kDarken);
dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor);
check_sdc_color(reporter, sdc.get(), dContext, dstColor);
}
DEF_GPUTEST_FOR_CONTEXTS(DMSAA_aa_dst_read_after_dmsaa,
&sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
require_dst_reads) {
auto dContext = ctxInfo.directContext();
auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
SkBackingFit::kApprox, {kWidth, kHeight},
kDMSAAProps);
// Initialize the texture and dmsaa attachment with transparent.
draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
sdc->clear(SK_PMColor4fWHITE);
SkPMColor4f dstColor = SK_PMColor4fWHITE;
draw_paint_with_dmsaa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
// Draw with aa after dmsaa. This should break up the render pass and issue a texture barrier.
draw_paint_with_aa(sdc.get(), kTransCyan, SkBlendMode::kDarken);
dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransCyan, dstColor);
check_sdc_color(reporter, sdc.get(), dContext, dstColor);
}
DEF_GPUTEST_FOR_CONTEXTS(DMSAA_dst_read_with_existing_barrier,
&sk_gpu_test::GrContextFactory::IsRenderingContext, reporter, ctxInfo,
require_dst_reads) {
auto dContext = ctxInfo.directContext();
auto sdc = skgpu::v1::SurfaceDrawContext::Make(dContext, GrColorType::kRGBA_8888, nullptr,
SkBackingFit::kApprox, {kWidth, kHeight},
kDMSAAProps);
// Initialize the texture and dmsaa attachment with transparent.
draw_paint_with_dmsaa(sdc.get(), SK_PMColor4fTRANSPARENT, SkBlendMode::kSrc);
check_sdc_color(reporter, sdc.get(), dContext, SK_PMColor4fTRANSPARENT);
sdc->clear(SK_PMColor4fWHITE);
SkPMColor4f dstColor = SK_PMColor4fWHITE;
// Blend to the texture (not the dmsaa attachment) with a dst read. This creates a texture
// barrier.
draw_paint_with_aa(sdc.get(), kTransYellow, SkBlendMode::kDarken);
dstColor = SkBlendMode_Apply(SkBlendMode::kDarken, kTransYellow, dstColor);
// Blend to the msaa attachment _without_ a dst read. This ensures we respect the prior texture
// barrier by splitting the opsTask.
draw_paint_with_dmsaa(sdc.get(), kTransCyan, SkBlendMode::kSrcOver);
dstColor = SkBlendMode_Apply(SkBlendMode::kSrcOver, kTransCyan, dstColor);
check_sdc_color(reporter, sdc.get(), dContext, dstColor);
}
// This test is used to test for crbug.com/1241134. The bug appears on Adreno5xx devices with OS
// PQ3A. It does not repro on the earlier PPR1 version since the extend blend func extension was not
// present on the older driver.
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(DMSAA_dual_source_blend_disable, reporter, ctxInfo) {
SkISize surfaceDims = {100, 100};
SkISize texDims = {50, 50};
auto context = ctxInfo.directContext();
auto sourceTexture = context->createBackendTexture(texDims.width(),
texDims.height(),
kRGBA_8888_SkColorType,
SkColors::kBlue,
GrMipMapped::kNo,
GrRenderable::kYes,
GrProtected::kNo);
auto sourceImage = SkImage::MakeFromTexture(context,
sourceTexture,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr);
auto texture1 = context->createBackendTexture(surfaceDims.width(),
surfaceDims.height(),
kRGBA_8888_SkColorType,
SkColors::kRed,
GrMipMapped::kNo,
GrRenderable::kYes,
GrProtected::kNo);
auto texture2 = context->createBackendTexture(surfaceDims.width(),
surfaceDims.height(),
kRGBA_8888_SkColorType,
SkColors::kYellow,
GrMipMapped::kNo,
GrRenderable::kYes,
GrProtected::kNo);
SkPaint paint;
paint.setBlendMode(SkBlendMode::kSrc);
SkRect srcRect = SkRect::MakeIWH(texDims.width(), texDims.height());
SkRect dstRect = SkRect::MakeXYWH(texDims.width()/2, texDims.height()/2,
texDims.width(), texDims.height());
// First we do an image draw to a DMSAA surface with kSrc blend mode. This will trigger us to
// use dual source blending if supported.
// Note: The draw here doesn't actually use the dmsaa multisampled buffer. However, by using
// a dmsaa surface it forces us to use the FillRRectOp instead of the normal FillQuad path. It
// is unclear why, but using the FillRRectOp is required to repro the bug.
{
auto surface = SkSurface::MakeFromBackendTexture(context,
texture1,
kTopLeft_GrSurfaceOrigin,
1,
kRGBA_8888_SkColorType,
nullptr,
&kDMSAAProps);
surface->getCanvas()->drawImageRect(sourceImage,
srcRect,
dstRect,
SkSamplingOptions(),
&paint,
SkCanvas::kStrict_SrcRectConstraint);
// Make sure there isn't any batching
surface->flushAndSubmit();
}
// Next we do an image draw to a different surface that doesn't have the dmsaa flag. This will
// trigger use to disable blending. However, when the bug is present the driver still seems to
// try and use a "src2" blend value and ends up just writing the original dst color of yellow.
{
auto surface = SkSurface::MakeFromBackendTexture(context,
texture2,
kTopLeft_GrSurfaceOrigin,
1,
kRGBA_8888_SkColorType,
nullptr,
&kBasicProps);
surface->getCanvas()->drawImageRect(sourceImage,
srcRect,
dstRect,
SkSamplingOptions(),
&paint,
SkCanvas::kStrict_SrcRectConstraint);
surface->flushAndSubmit();
}
{
auto readImage = SkImage::MakeFromTexture(context,
texture2,
kTopLeft_GrSurfaceOrigin,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr);
SkImageInfo dstIInfo = SkImageInfo::Make(texDims.width(),
texDims.height(),
kRGBA_8888_SkColorType,
kPremul_SkAlphaType,
nullptr);
SkBitmap bitmap;
bitmap.allocPixels(dstIInfo);
bool success = readImage->readPixels(context, bitmap.pixmap(), dstRect.fLeft, dstRect.fTop);
if (!success) {
ERRORF(reporter, "Failed to read pixels");
return;
}
auto pix = static_cast<const uint32_t*>(bitmap.getAddr(0, 0));
for (int x = 0; x < 50; ++x) {
for (int y = 0; y < 50; ++y) {
uint32_t pixColor = pix[x + y * 50];
if (pixColor != 0xFFFF0000) {
ERRORF(reporter, "Didn't get a blue pixel at %d, %d. Got 0x%8X",
x, y, pixColor);
continue;
}
}
}
}
sourceImage.reset();
// Need to make sure the gpu is fully finished before deleting the textures
context->flushAndSubmit(true);
context->deleteBackendTexture(sourceTexture);
context->deleteBackendTexture(texture1);
context->deleteBackendTexture(texture2);
}