Reland "ccpr: Rework the path cache to support sporadic flushing"
This is a reland of d6fa45472cb82b7d8e58d0437f7723c672488b8b
Original change's description:
> ccpr: Rework the path cache to support sporadic flushing
>
> Removes the notion of a stashed atlas that we store from the previous
> flush. Now we just cache every atlas we ever render. Cached atlases
> can either be 16-bit or 8-bit.
>
> The "reuse" and "animation" cases should both behave exactly the same
> as before: Where before we would copy from the stashed atlas to 8-bit
> atlases, we now copy from a cached 16-bit atlas and then invalidate
> it. Where before we would recycle the stashed atlas's backing texture
> object, we now recycle this same texture object from an invalidated
> 16-bit cached atlas.
>
> The main difference is that cases like tiled rendering now work. If
> you draw your whole scene in one flush, you still get one big 16-bit
> cached atlas, just like the "stashed atlas" implementation. But if you
> draw your scene in tiles, you now get lots of little cached 16-bit
> atlases, which can be reused and eventually copied to 8-bit atlases.
>
> Bug: skia:8462
> Change-Id: Ibae65febb948230aaaf1f1361eef9c8f06ebef18
> Reviewed-on: https://skia-review.googlesource.com/c/179991
> Commit-Queue: Chris Dalton <csmartdalton@google.com>
> Reviewed-by: Robert Phillips <robertphillips@google.com>
Bug: skia:8462
Change-Id: I2f64b0c37e2cd644a202dfc786366dda5d238391
Reviewed-on: https://skia-review.googlesource.com/c/181450
Reviewed-by: Robert Phillips <robertphillips@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCCDrawPathsOp.cpp b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
index c1384fe..ba16f58 100644
--- a/src/gpu/ccpr/GrCCDrawPathsOp.cpp
+++ b/src/gpu/ccpr/GrCCDrawPathsOp.cpp
@@ -6,6 +6,7 @@
*/
#include "GrCCDrawPathsOp.h"
+
#include "GrContext.h"
#include "GrContextPriv.h"
#include "GrMemoryPool.h"
@@ -157,13 +158,6 @@
#endif
}
-GrCCDrawPathsOp::SingleDraw::~SingleDraw() {
- if (fCacheEntry) {
- // All currFlushAtlas references must be reset back to null before the flush is finished.
- fCacheEntry->setCurrFlushAtlas(nullptr);
- }
-}
-
GrDrawOp::RequiresDstTexture GrCCDrawPathsOp::finalize(const GrCaps& caps,
const GrAppliedClip* clip) {
SkASSERT(1 == fNumDraws); // There should only be one single path draw in this Op right now.
@@ -233,10 +227,10 @@
void GrCCDrawPathsOp::accountForOwnPaths(GrCCPathCache* pathCache,
GrOnFlushResourceProvider* onFlushRP,
- const GrUniqueKey& stashedAtlasKey,
GrCCPerFlushResourceSpecs* specs) {
using CreateIfAbsent = GrCCPathCache::CreateIfAbsent;
using MaskTransform = GrCCPathCache::MaskTransform;
+ using CoverageType = GrCCAtlas::CoverageType;
for (SingleDraw& draw : fDraws) {
SkPath path;
@@ -247,41 +241,32 @@
if (pathCache) {
MaskTransform m(draw.fMatrix, &draw.fCachedMaskShift);
bool canStashPathMask = draw.fMaskVisibility >= Visibility::kMostlyComplete;
- draw.fCacheEntry = pathCache->find(draw.fShape, m, CreateIfAbsent(canStashPathMask));
+ draw.fCacheEntry =
+ pathCache->find(onFlushRP, draw.fShape, m, CreateIfAbsent(canStashPathMask));
}
- if (auto cacheEntry = draw.fCacheEntry.get()) {
- SkASSERT(!cacheEntry->currFlushAtlas()); // Shouldn't be set until setupResources().
-
- if (cacheEntry->atlasKey().isValid()) {
- // Does the path already exist in a cached atlas?
- if (cacheEntry->hasCachedAtlas() &&
- (draw.fCachedAtlasProxy = onFlushRP->findOrCreateProxyByUniqueKey(
- cacheEntry->atlasKey(),
- GrCCAtlas::kTextureOrigin))) {
+ if (draw.fCacheEntry) {
+ if (const GrCCCachedAtlas* cachedAtlas = draw.fCacheEntry->cachedAtlas()) {
+ SkASSERT(cachedAtlas->getOnFlushProxy());
+ if (CoverageType::kA8_LiteralCoverage == cachedAtlas->coverageType()) {
++specs->fNumCachedPaths;
- continue;
- }
-
- // Does the path exist in the atlas that we stashed away from last flush? If so we
- // can copy it into a new 8-bit atlas and keep it in the resource cache.
- if (stashedAtlasKey.isValid() && stashedAtlasKey == cacheEntry->atlasKey()) {
- SkASSERT(!cacheEntry->hasCachedAtlas());
+ } else {
+ // Suggest that this path be copied to a literal coverage atlas, to save memory.
+ // (The client may decline this copy via DoCopiesToA8Coverage::kNo.)
int idx = (draw.fShape.style().strokeRec().isFillStyle())
? GrCCPerFlushResourceSpecs::kFillIdx
: GrCCPerFlushResourceSpecs::kStrokeIdx;
++specs->fNumCopiedPaths[idx];
specs->fCopyPathStats[idx].statPath(path);
- specs->fCopyAtlasSpecs.accountForSpace(cacheEntry->width(),
- cacheEntry->height());
- continue;
+ specs->fCopyAtlasSpecs.accountForSpace(
+ draw.fCacheEntry->width(), draw.fCacheEntry->height());
+ draw.fDoCopyToA8Coverage = true;
}
-
- // Whatever atlas the path used to reside in, it no longer exists.
- cacheEntry->resetAtlasKeyAndInfo();
+ continue;
}
- if (Visibility::kMostlyComplete == draw.fMaskVisibility && cacheEntry->hitCount() > 1) {
+ if (Visibility::kMostlyComplete == draw.fMaskVisibility &&
+ draw.fCacheEntry->hitCount() > 1) {
int shapeSize = SkTMax(draw.fShapeConservativeIBounds.height(),
draw.fShapeConservativeIBounds.width());
if (shapeSize <= onFlushRP->caps()->maxRenderTargetSize()) {
@@ -303,8 +288,9 @@
}
}
-void GrCCDrawPathsOp::setupResources(GrOnFlushResourceProvider* onFlushRP,
- GrCCPerFlushResources* resources, DoCopiesToCache doCopies) {
+void GrCCDrawPathsOp::setupResources(
+ GrCCPathCache* pathCache, GrOnFlushResourceProvider* onFlushRP,
+ GrCCPerFlushResources* resources, DoCopiesToA8Coverage doCopies) {
using DoEvenOddFill = GrCCPathProcessor::DoEvenOddFill;
SkASSERT(fNumDraws > 0);
SkASSERT(-1 == fBaseInstance);
@@ -321,51 +307,29 @@
if (auto cacheEntry = draw.fCacheEntry.get()) {
// Does the path already exist in a cached atlas texture?
- if (auto proxy = draw.fCachedAtlasProxy.get()) {
- SkASSERT(!cacheEntry->currFlushAtlas());
- this->recordInstance(proxy, resources->nextPathInstanceIdx());
+ if (cacheEntry->cachedAtlas()) {
+ SkASSERT(cacheEntry->cachedAtlas()->getOnFlushProxy());
+ if (DoCopiesToA8Coverage::kYes == doCopies && draw.fDoCopyToA8Coverage) {
+ resources->upgradeEntryToLiteralCoverageAtlas(pathCache, onFlushRP, cacheEntry,
+ doEvenOddFill);
+ SkASSERT(cacheEntry->cachedAtlas());
+ SkASSERT(GrCCAtlas::CoverageType::kA8_LiteralCoverage
+ == cacheEntry->cachedAtlas()->coverageType());
+ SkASSERT(cacheEntry->cachedAtlas()->getOnFlushProxy());
+ }
+ this->recordInstance(cacheEntry->cachedAtlas()->getOnFlushProxy(),
+ resources->nextPathInstanceIdx());
// TODO4F: Preserve float colors
resources->appendDrawPathInstance().set(*cacheEntry, draw.fCachedMaskShift,
draw.fColor.toBytes_RGBA());
continue;
}
-
- // Have we already encountered this path during the flush? (i.e. was the same SkPath
- // drawn more than once during the same flush, with a compatible matrix?)
- if (auto atlas = cacheEntry->currFlushAtlas()) {
- this->recordInstance(atlas->textureProxy(), resources->nextPathInstanceIdx());
- // TODO4F: Preserve float colors
- resources->appendDrawPathInstance().set(
- *cacheEntry, draw.fCachedMaskShift, draw.fColor.toBytes_RGBA(),
- cacheEntry->hasCachedAtlas() ? DoEvenOddFill::kNo : doEvenOddFill);
- continue;
- }
-
- // If the cache entry still has a valid atlas key at this point, it means the path
- // exists in the atlas that we stashed away from last flush. Copy it into a permanent
- // 8-bit atlas in the resource cache.
- if (DoCopiesToCache::kYes == doCopies && cacheEntry->atlasKey().isValid()) {
- SkIVector newOffset;
- GrCCAtlas* atlas =
- resources->copyPathToCachedAtlas(*cacheEntry, doEvenOddFill, &newOffset);
- cacheEntry->updateToCachedAtlas(
- atlas->getOrAssignUniqueKey(onFlushRP), newOffset,
- atlas->refOrMakeCachedAtlasInfo(onFlushRP->contextUniqueID()));
- this->recordInstance(atlas->textureProxy(), resources->nextPathInstanceIdx());
- // TODO4F: Preserve float colors
- resources->appendDrawPathInstance().set(*cacheEntry, draw.fCachedMaskShift,
- draw.fColor.toBytes_RGBA());
- // Remember this atlas in case we encounter the path again during the same flush.
- cacheEntry->setCurrFlushAtlas(atlas);
- continue;
- }
}
- // Render the raw path into a coverage count atlas. renderPathInAtlas() gives us two tight
+ // Render the raw path into a coverage count atlas. renderShapeInAtlas() gives us two tight
// bounding boxes: One in device space, as well as a second one rotated an additional 45
// degrees. The path vertex shader uses these two bounding boxes to generate an octagon that
// circumscribes the path.
- SkASSERT(!draw.fCachedAtlasProxy);
SkRect devBounds, devBounds45;
SkIRect devIBounds;
SkIVector devToAtlasOffset;
@@ -380,7 +344,7 @@
// If we have a spot in the path cache, try to make a note of where this mask is so we
// can reuse it in the future.
if (auto cacheEntry = draw.fCacheEntry.get()) {
- SkASSERT(!cacheEntry->hasCachedAtlas());
+ SkASSERT(!cacheEntry->cachedAtlas());
if (Visibility::kComplete != draw.fMaskVisibility || cacheEntry->hitCount() <= 1) {
// Don't cache a path mask unless it's completely visible with a hit count > 1.
@@ -390,19 +354,9 @@
continue;
}
- if (resources->nextAtlasToStash() != atlas) {
- // This mask does not belong to the atlas that will be stashed for next flush.
- continue;
- }
-
- const GrUniqueKey& atlasKey =
- resources->nextAtlasToStash()->getOrAssignUniqueKey(onFlushRP);
- cacheEntry->initAsStashedAtlas(atlasKey, devToAtlasOffset, devBounds, devBounds45,
- devIBounds, draw.fCachedMaskShift);
- // Remember this atlas in case we encounter the path again during the same flush.
- cacheEntry->setCurrFlushAtlas(atlas);
+ cacheEntry->setCoverageCountAtlas(onFlushRP, atlas, devToAtlasOffset, devBounds,
+ devBounds45, devIBounds, draw.fCachedMaskShift);
}
- continue;
}
}