skia2/gm/preservefillrule.cpp
Chris Dalton 1e7b2e5e17 Re-enable ccpr clipping for small paths
For things like path rendering we can (and should) do better than an
atlas. But for use cases like "many small paths clipped by many small
paths," an atlas is probably still the best solution we have.

Change-Id: Iff5d250d0d6ccba9235f3fe652891554e3fdfc24
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/380856
Reviewed-by: Michael Ludwig <michaelludwig@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
2021-03-10 16:22:58 +00:00

167 lines
6.2 KiB
C++

/*
* Copyright 2019 Google LLC.
*
* Use of this source code is governed by a BSD-style license that can be
* found in the LICENSE file.
*/
#include "gm/gm.h"
#include "include/core/SkPath.h"
#include "include/gpu/GrContextOptions.h"
#include "include/gpu/GrRecordingContext.h"
#include "src/gpu/GrDirectContextPriv.h"
#include "src/gpu/GrDrawingManager.h"
#include "src/gpu/GrRecordingContextPriv.h"
#include "src/gpu/GrSurfaceDrawContext.h"
#include "src/gpu/ccpr/GrCCPathCache.h"
#include "src/gpu/ccpr/GrCoverageCountingPathRenderer.h"
#include "tools/ToolUtils.h"
namespace skiagm {
#define ERR_MSG_ASSERT(COND) \
do { \
if (!(COND)) { \
errorMsg->printf("preservefillrule.cpp(%i): assert(%s)", \
__LINE__, #COND); \
return DrawResult::kFail; \
} \
} while (false)
/**
* This test ensures that the ccpr path cache preserves fill rules properly, both in the case where
* we copy paths into a8 literal coverage atlases, as well as in the case where we just reuse a
* stashed fp16 coverage count atlas.
*/
class PreserveFillRuleGM : public GpuGM {
public:
// fStarSize affects whether ccpr copies the paths to an a8 literal coverage atlas, or just
// leaves them stashed in an fp16 coverage count atlas. The threshold for copying to a8 is
// currently 256x256 total pixels copied. If this ever changes, there is code in onDraw that
// will detect the unexpected behavior and draw a failure message.
PreserveFillRuleGM(bool literalCoverageAtlas)
: fLiteralCoverageAtlas(literalCoverageAtlas)
, fStarSize((fLiteralCoverageAtlas) ? 200 : 20) {
}
private:
SkString onShortName() override {
SkString name("preservefillrule");
name += (fLiteralCoverageAtlas) ? "_big" : "_little";
return name;
}
SkISize onISize() override { return SkISize::Make(fStarSize * 2, fStarSize * 2); }
void modifyGrContextOptions(GrContextOptions* ctxOptions) override {
ctxOptions->fGpuPathRenderers = GpuPathRenderers::kCoverageCounting;
ctxOptions->fAllowPathMaskCaching = true;
}
DrawResult onDraw(GrRecordingContext* rContext, GrSurfaceDrawContext* rtc, SkCanvas* canvas,
SkString* errorMsg) override {
using CoverageType = GrCCAtlas::CoverageType;
if (rtc->numSamples() > 1) {
errorMsg->set("ccpr is currently only used for coverage AA");
return DrawResult::kSkip;
}
auto* ccpr = rContext->priv().drawingManager()->getCoverageCountingPathRenderer();
if (!ccpr) {
errorMsg->set("ccpr only");
return DrawResult::kSkip;
}
auto pathCache = ccpr->testingOnly_getPathCache();
if (!pathCache) {
errorMsg->set("ccpr is not in caching mode. "
"Are you using viewer? Launch with \"--cachePathMasks true\".");
return DrawResult::kFail;
}
auto dContext = GrAsDirectContext(rContext);
if (!dContext) {
*errorMsg = "Requires a direct context.";
return skiagm::DrawResult::kSkip;
}
auto starRect = SkRect::MakeWH(fStarSize, fStarSize);
SkPath star7_winding = ToolUtils::make_star(starRect, 7);
star7_winding.setFillType(SkPathFillType::kWinding);
SkPath star7_evenOdd = star7_winding;
star7_evenOdd.transform(SkMatrix::Translate(0, fStarSize));
star7_evenOdd.setFillType(SkPathFillType::kEvenOdd);
SkPath star5_winding = ToolUtils::make_star(starRect, 5);
star5_winding.transform(SkMatrix::Translate(fStarSize, 0));
star5_winding.setFillType(SkPathFillType::kWinding);
SkPath star5_evenOdd = star5_winding;
star5_evenOdd.transform(SkMatrix::Translate(0, fStarSize));
star5_evenOdd.setFillType(SkPathFillType::kEvenOdd);
SkPaint paint;
paint.setColor(SK_ColorGREEN);
paint.setAntiAlias(true);
for (int i = 0; i < 3; ++i) {
canvas->clear(SK_ColorWHITE);
canvas->drawPath(star7_winding, paint);
canvas->drawPath(star7_evenOdd, paint);
canvas->drawPath(star5_winding, paint);
canvas->drawPath(star5_evenOdd, paint);
dContext->priv().flushSurface(rtc->asSurfaceProxy());
// Ensure the path cache is behaving in such a way that we are actually testing what we
// think we are.
int numCachedPaths = 0;
for (GrCCPathCacheEntry* entry : pathCache->testingOnly_getLRU()) {
if (0 == i) {
// We don't cache an atlas on the first hit.
ERR_MSG_ASSERT(!entry->cachedAtlas());
} else {
// The stars should be cached in an atlas now.
ERR_MSG_ASSERT(entry->cachedAtlas());
CoverageType atlasCoverageType = entry->cachedAtlas()->coverageType();
if (i < 2) {
// We never copy to an a8 atlas before the second hit.
ERR_MSG_ASSERT(ccpr->coverageType() == atlasCoverageType);
} else if (fLiteralCoverageAtlas) {
// Verify fStarSize is large enough that the paths got copied to an a8
// atlas.
ERR_MSG_ASSERT(CoverageType::kA8_LiteralCoverage == atlasCoverageType);
} else {
// Verify fStarSize is small enough that the paths did *NOT* get copied to
// an a8 atlas.
ERR_MSG_ASSERT(ccpr->coverageType() == atlasCoverageType);
}
}
++numCachedPaths;
}
// CCPR is currently disabled.
#if 0
if (dContext) {
// Verify all 4 paths are tracked by the path cache.
ERR_MSG_ASSERT(4 == numCachedPaths);
}
#endif
}
return DrawResult::kOk;
}
private:
const bool fLiteralCoverageAtlas;
const int fStarSize;
};
DEF_GM( return new PreserveFillRuleGM(true); )
DEF_GM( return new PreserveFillRuleGM(false); )
} // namespace skiagm