CCPR: Rewrite path parsing
Creates a GrCCPRGeometry class that chops contours up into simple
segments that ccpr can render, and rewrites the GPU buffer creation to
be able to handle arbitrary lengths of ccpr geometry.
Bug: skia:
Change-Id: Iaa173a02729e177b0ed7ef7fbb9195d349be689d
Reviewed-on: https://skia-review.googlesource.com/41963
Reviewed-by: Brian Salomon <bsalomon@google.com>
Commit-Queue: Chris Dalton <csmartdalton@google.com>
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index 596ec5e..2bec4ff 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -72,6 +72,7 @@
, fOwningRTPendingOps(nullptr) {
SkDEBUGCODE(fBaseInstance = -1);
SkDEBUGCODE(fDebugInstanceCount = 1;)
+ SkDEBUGCODE(fDebugSkippedInstances = 0;)
GrRenderTargetContext* const rtc = args.fRenderTargetContext;
@@ -115,9 +116,12 @@
SkASSERT(owningRTPendingOps == fOwningRTPendingOps);
owningRTPendingOps->fOpList.remove(that);
} else {
- // wasRecorded is not called when the op gets combined first. Count path items here instead.
- SingleDraw& onlyDraw = that->getOnlyPathDraw();
- fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
+ // The Op is being combined immediately after creation, before a call to wasRecorded. In
+ // this case wasRecorded will not be called. So we count its path here instead.
+ const SingleDraw& onlyDraw = that->getOnlyPathDraw();
+ ++fOwningRTPendingOps->fNumTotalPaths;
+ fOwningRTPendingOps->fNumSkPoints += onlyDraw.fPath.countPoints();
+ fOwningRTPendingOps->fNumSkVerbs += onlyDraw.fPath.countVerbs();
}
fTailDraw->fNext = &fOwningRTPendingOps->fDrawsAllocator.push_back(that->fHeadDraw);
@@ -132,21 +136,17 @@
void DrawPathsOp::wasRecorded(GrRenderTargetOpList* opList) {
SkASSERT(!fOwningRTPendingOps);
- SingleDraw& onlyDraw = this->getOnlyPathDraw();
+ const SingleDraw& onlyDraw = this->getOnlyPathDraw();
fOwningRTPendingOps = &fCCPR->fRTPendingOpsMap[opList->uniqueID()];
+ ++fOwningRTPendingOps->fNumTotalPaths;
+ fOwningRTPendingOps->fNumSkPoints += onlyDraw.fPath.countPoints();
+ fOwningRTPendingOps->fNumSkVerbs += onlyDraw.fPath.countVerbs();
fOwningRTPendingOps->fOpList.addToTail(this);
- fOwningRTPendingOps->fMaxBufferItems.countPathItems(onlyDraw.fScissorMode, onlyDraw.fPath);
}
void GrCoverageCountingPathRenderer::preFlush(GrOnFlushResourceProvider* onFlushRP,
const uint32_t* opListIDs, int numOpListIDs,
SkTArray<sk_sp<GrRenderTargetContext>>* results) {
- using PathInstance = GrCCPRPathProcessor::Instance;
-
- SkASSERT(!fPerFlushIndexBuffer);
- SkASSERT(!fPerFlushVertexBuffer);
- SkASSERT(!fPerFlushInstanceBuffer);
- SkASSERT(fPerFlushAtlases.empty());
SkASSERT(!fFlushing);
SkDEBUGCODE(fFlushing = true;)
@@ -154,8 +154,29 @@
return; // Nothing to draw.
}
+ this->setupPerFlushResources(onFlushRP, opListIDs, numOpListIDs, results);
+
+ // Erase these last, once we are done accessing data from the SingleDraw allocators.
+ for (int i = 0; i < numOpListIDs; ++i) {
+ fRTPendingOpsMap.erase(opListIDs[i]);
+ }
+}
+
+void GrCoverageCountingPathRenderer::setupPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
+ const uint32_t* opListIDs,
+ int numOpListIDs,
+ SkTArray<sk_sp<GrRenderTargetContext>>* results) {
+ using PathInstance = GrCCPRPathProcessor::Instance;
+
+ SkASSERT(!fPerFlushIndexBuffer);
+ SkASSERT(!fPerFlushVertexBuffer);
+ SkASSERT(!fPerFlushInstanceBuffer);
+ SkASSERT(fPerFlushAtlases.empty());
+
+ fPerFlushResourcesAreValid = false;
+
SkTInternalLList<DrawPathsOp> flushingOps;
- GrCCPRCoverageOpsBuilder::MaxBufferItems maxBufferItems;
+ int maxTotalPaths = 0, numSkPoints = 0, numSkVerbs = 0;
for (int i = 0; i < numOpListIDs; ++i) {
auto it = fRTPendingOpsMap.find(opListIDs[i]);
@@ -163,13 +184,15 @@
RTPendingOps& rtPendingOps = it->second;
SkASSERT(!rtPendingOps.fOpList.isEmpty());
flushingOps.concat(std::move(rtPendingOps.fOpList));
- maxBufferItems += rtPendingOps.fMaxBufferItems;
+ maxTotalPaths += rtPendingOps.fNumTotalPaths;
+ numSkPoints += rtPendingOps.fNumSkPoints;
+ numSkVerbs += rtPendingOps.fNumSkVerbs;
}
}
- SkASSERT(flushingOps.isEmpty() == !maxBufferItems.fMaxPaths);
+ SkASSERT(flushingOps.isEmpty() == !maxTotalPaths);
if (flushingOps.isEmpty()) {
- return; // Still nothing to draw.
+ return; // Nothing to draw.
}
fPerFlushIndexBuffer = GrCCPRPathProcessor::FindOrMakeIndexBuffer(onFlushRP);
@@ -184,14 +207,8 @@
return;
}
- GrCCPRCoverageOpsBuilder atlasOpsBuilder;
- if (!atlasOpsBuilder.init(onFlushRP, maxBufferItems)) {
- SkDebugf("WARNING: failed to allocate buffers for coverage ops. No paths will be drawn.\n");
- return;
- }
-
fPerFlushInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
- maxBufferItems.fMaxPaths * sizeof(PathInstance));
+ maxTotalPaths * sizeof(PathInstance));
if (!fPerFlushInstanceBuffer) {
SkDebugf("WARNING: failed to allocate path instance buffer. No paths will be drawn.\n");
return;
@@ -201,29 +218,29 @@
SkASSERT(pathInstanceData);
int pathInstanceIdx = 0;
+ GrCCPRCoverageOpsBuilder atlasOpsBuilder(maxTotalPaths, numSkPoints, numSkVerbs);
GrCCPRAtlas* atlas = nullptr;
- SkDEBUGCODE(int skippedPaths = 0;)
+ SkDEBUGCODE(int skippedTotalPaths = 0;)
SkTInternalLList<DrawPathsOp>::Iter iter;
iter.init(flushingOps, SkTInternalLList<DrawPathsOp>::Iter::kHead_IterStart);
- while (DrawPathsOp* op = iter.get()) {
- SkASSERT(op->fDebugInstanceCount > 0);
- SkASSERT(-1 == op->fBaseInstance);
- op->fBaseInstance = pathInstanceIdx;
+ while (DrawPathsOp* drawPathOp = iter.get()) {
+ SkASSERT(drawPathOp->fDebugInstanceCount > 0);
+ SkASSERT(-1 == drawPathOp->fBaseInstance);
+ drawPathOp->fBaseInstance = pathInstanceIdx;
- for (const DrawPathsOp::SingleDraw* draw = &op->fHeadDraw; draw; draw = draw->fNext) {
+ for (const auto* draw = &drawPathOp->fHeadDraw; draw; draw = draw->fNext) {
// parsePath 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.
SkRect devBounds, devBounds45;
- atlasOpsBuilder.parsePath(draw->fScissorMode, draw->fMatrix, draw->fPath, &devBounds,
- &devBounds45);
+ atlasOpsBuilder.parsePath(draw->fMatrix, draw->fPath, &devBounds, &devBounds45);
SkRect clippedDevBounds = devBounds;
if (ScissorMode::kScissored == draw->fScissorMode &&
!clippedDevBounds.intersect(devBounds, SkRect::Make(draw->fClipBounds))) {
- SkDEBUGCODE(--op->fDebugInstanceCount);
- SkDEBUGCODE(++skippedPaths;)
+ SkDEBUGCODE(++drawPathOp->fDebugSkippedInstances);
+ atlasOpsBuilder.discardParsedPath();
continue;
}
@@ -234,12 +251,9 @@
SkIPoint16 atlasLocation;
if (atlas && !atlas->addRect(w, h, &atlasLocation)) {
// The atlas is out of room and can't grow any bigger.
- auto atlasOp = atlasOpsBuilder.createIntermediateOp(atlas->drawBounds());
- if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
- results->push_back(std::move(rtc));
- }
- if (pathInstanceIdx > op->fBaseInstance) {
- op->addAtlasBatch(atlas, pathInstanceIdx);
+ atlasOpsBuilder.emitOp(atlas->drawBounds());
+ if (pathInstanceIdx > drawPathOp->fBaseInstance) {
+ drawPathOp->addAtlasBatch(atlas, pathInstanceIdx);
}
atlas = nullptr;
}
@@ -262,34 +276,54 @@
draw->fColor
};
- atlasOpsBuilder.saveParsedPath(clippedDevIBounds, offsetX, offsetY);
+ atlasOpsBuilder.saveParsedPath(draw->fScissorMode, clippedDevIBounds, offsetX, offsetY);
}
- SkASSERT(pathInstanceIdx == op->fBaseInstance + op->fDebugInstanceCount);
- op->addAtlasBatch(atlas, pathInstanceIdx);
+ SkASSERT(pathInstanceIdx == drawPathOp->fBaseInstance + drawPathOp->fDebugInstanceCount -
+ drawPathOp->fDebugSkippedInstances);
+ if (pathInstanceIdx > drawPathOp->fBaseInstance) {
+ drawPathOp->addAtlasBatch(atlas, pathInstanceIdx);
+ }
iter.next();
+ SkDEBUGCODE(skippedTotalPaths += drawPathOp->fDebugSkippedInstances;)
+ }
+ SkASSERT(pathInstanceIdx == maxTotalPaths - skippedTotalPaths);
+
+ if (atlas) {
+ atlasOpsBuilder.emitOp(atlas->drawBounds());
}
- SkASSERT(pathInstanceIdx == maxBufferItems.fMaxPaths - skippedPaths);
fPerFlushInstanceBuffer->unmap();
- std::unique_ptr<GrDrawOp> atlasOp = atlasOpsBuilder.finalize(atlas->drawBounds());
- if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
- results->push_back(std::move(rtc));
+ // Draw the coverage ops into their respective atlases.
+ SkSTArray<4, std::unique_ptr<GrCCPRCoverageOp>> atlasOps(fPerFlushAtlases.count());
+ if (!atlasOpsBuilder.finalize(onFlushRP, &atlasOps)) {
+ SkDebugf("WARNING: failed to allocate ccpr atlas buffers. No paths will be drawn.\n");
+ return;
}
+ SkASSERT(atlasOps.count() == fPerFlushAtlases.count());
- // Erase these last, once we are done accessing data from the SingleDraw allocators.
- for (int i = 0; i < numOpListIDs; ++i) {
- fRTPendingOpsMap.erase(opListIDs[i]);
+ GrTAllocator<GrCCPRAtlas>::Iter atlasIter(&fPerFlushAtlases);
+ for (std::unique_ptr<GrCCPRCoverageOp>& atlasOp : atlasOps) {
+ SkAssertResult(atlasIter.next());
+ GrCCPRAtlas* atlas = atlasIter.get();
+ SkASSERT(atlasOp->bounds() == SkRect::MakeIWH(atlas->drawBounds().width(),
+ atlas->drawBounds().height()));
+ if (auto rtc = atlas->finalize(onFlushRP, std::move(atlasOp))) {
+ results->push_back(std::move(rtc));
+ }
}
+ SkASSERT(!atlasIter.next());
+
+ fPerFlushResourcesAreValid = true;
}
void DrawPathsOp::onExecute(GrOpFlushState* flushState) {
SkASSERT(fCCPR->fFlushing);
SkASSERT(flushState->rtCommandBuffer());
- if (!fCCPR->fPerFlushInstanceBuffer) {
+ if (!fCCPR->fPerFlushResourcesAreValid) {
return; // Setup failed.
}
@@ -323,7 +357,7 @@
flushState->rtCommandBuffer()->draw(pipeline, coverProc, &mesh, nullptr, 1, this->bounds());
}
- SkASSERT(baseInstance == fBaseInstance + fDebugInstanceCount);
+ SkASSERT(baseInstance == fBaseInstance + fDebugInstanceCount - fDebugSkippedInstances);
}
void GrCoverageCountingPathRenderer::postFlush() {