blob: 6637e8b79fc458eff975b8da44173fbf8a71a46a [file] [log] [blame]
/*
* Copyright 2026 Google LLC
*
* 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/SkBlendMode.h"
#include "src/gpu/SkBackingFit.h"
#include "src/gpu/graphite/ContextPriv.h"
#include "src/gpu/graphite/ContextUtils.h"
#include "src/gpu/graphite/Device.h"
#include "src/gpu/graphite/Renderer.h"
#include "src/gpu/graphite/TextureInfoPriv.h"
using namespace skgpu;
using namespace skgpu::graphite;
// These are unit tests that ensure the secondary "inner fill" draw is recorded when possible.
DEF_GRAPHITE_TEST_FOR_ALL_CONTEXTS(InnerFillTest, reporter, context,
CtsEnforcement::kNextRelease) {
std::unique_ptr<Recorder> recorder = context->makeRecorder();
sk_sp<Device> device = Device::Make(recorder.get(),
SkImageInfo::Make(512, 512,
kRGBA_8888_SkColorType,
kPremul_SkAlphaType),
Budgeted::kYes,
Mipmapped::kNo,
SkBackingFit::kExact,
/*surfaceProps=*/{},
LoadOp::kClear,
"inner-fill");
// Default rrect size is sufficient to avoid the small-size exclusion criteria
auto testRRect = [&](const SkPaint& paint, bool innerFillExpected, float rrectSize = 128.f) {
SkRRect rrect = SkRRect::MakeRectXY(SkRect::MakeXYWH(0.f, 0.f, rrectSize, rrectSize),
0.1f * rrectSize, 0.1f * rrectSize);
int initialRenderSteps = device->testingOnly_pendingRenderSteps();
device->drawRRect(rrect, paint);
int actualRenderSteps = device->testingOnly_pendingRenderSteps();
if (actualRenderSteps == 1) {
// This either represents the first draw of the tests, or it represents the draw after
// a dst-read texture copy flush (which should only happen if we are not expecting an
// inner fill draw as well).
SkASSERT(initialRenderSteps == 0 || !innerFillExpected);
initialRenderSteps = 0;
}
// TODO(michaelludwig): After migrating to the layer draw tracking system, having a way to
// traverse the recorded draws and inspect them could let these checks be more robust, e.g.
// one of the steps is actually the CoverBoundsRenderStep AND it's in the front.
int expectedRenderSteps = initialRenderSteps + (innerFillExpected ? 2 : 1);
REPORTER_ASSERT(reporter, actualRenderSteps == expectedRenderSteps,
"Expected (%d) vs Actual (%d)", expectedRenderSteps, actualRenderSteps);
};
const bool srcWithCoverageIsHW =
CanUseHardwareBlending(context->priv().caps(),
TextureInfoPriv::ViewFormat(device->target()->textureInfo()),
SkBlendMode::kSrc,
Coverage::kSingleChannel);
// ** Test cases that should not produce an inner fill:
// 1. A transparent paint, so the draw is not eligible for an inner fill
{
skiatest::ReporterContext label{reporter, "transparent paint"};
SkPaint transparentPaint;
transparentPaint.setAlphaf(0.5f);
testRRect(transparentPaint, /*innerFillExpected=*/false);
}
// 2. A very small rounded rectangle that would otherwise have an inner fill
{
skiatest::ReporterContext label{reporter, "small rrect"};
testRRect(SkPaint(), /*innerFillExpected=*/false, /*rrectSize=*/8.f);
}
// 3. An opaque paint that has a blend mode that still mixes with the dst
{
skiatest::ReporterContext label{reporter, "fancy blend"};
SkPaint opaqueBlendedPaint;
opaqueBlendedPaint.setBlendMode(SkBlendMode::kSrcIn);
testRRect(opaqueBlendedPaint, /*innerFillExpected=*/false);
}
// 4. An opaque paint but with an analytic clip
{
skiatest::ReporterContext label{reporter, "analytic clip"};
device->pushClipStack();
device->clipShader(SkShaders::Color({0.1f, 0.2f, 0.3f, 0.5f}, nullptr),
SkClipOp::kIntersect);
testRRect(SkPaint(), /*innerFillExpected=*/false);
device->popClipStack();
}
// 5. A kSrc paint but the device requires shader blending since there's coverage
if (!srcWithCoverageIsHW) {
skiatest::ReporterContext label{reporter, "src w/o dual-src blending"};
SkPaint srcPaint;
srcPaint.setBlendMode(SkBlendMode::kSrc);
testRRect(srcPaint, /*innerFillExpected=*/false);
}
// ** Test cases that should produce an inner fill:
// 1. A kSrcOver paint with opaque color (or shader)
{
skiatest::ReporterContext label{reporter, "opaque src-over"};
testRRect(SkPaint(), /*innerFillExpected=*/true);
}
// 2. A kSrc paint when the device supports HW blending regardless of coverage
if (srcWithCoverageIsHW) {
skiatest::ReporterContext label{reporter, "src w/ dual-src blending"};
SkPaint srcPaint;
srcPaint.setBlendMode(SkBlendMode::kSrc);
srcPaint.setAlphaf(0.5f); // emphasize it's src color is not opaque
testRRect(srcPaint, /*innerFillExpected=*/true);
}
// We don't actually care about rendering, so just throw everything away
}