ccpr: Add an MSAA atlas mode
Adds the option to use a multisampled (or mixed sampled) atlas, and
uses the sample mask and stencil buffer instead of coverage counts.
Bug: skia:
Change-Id: I9fb76d17895ae25208124f6c27e37977ac31b5eb
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/227428
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Brian Salomon <bsalomon@google.com>
diff --git a/src/gpu/ccpr/GrCCFiller.cpp b/src/gpu/ccpr/GrCCFiller.cpp
index ba79a04..79fbc73 100644
--- a/src/gpu/ccpr/GrCCFiller.cpp
+++ b/src/gpu/ccpr/GrCCFiller.cpp
@@ -20,8 +20,10 @@
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-GrCCFiller::GrCCFiller(int numPaths, int numSkPoints, int numSkVerbs, int numConicWeights)
- : fGeometry(numSkPoints, numSkVerbs, numConicWeights)
+GrCCFiller::GrCCFiller(Algorithm algorithm, int numPaths, int numSkPoints, int numSkVerbs,
+ int numConicWeights)
+ : fAlgorithm(algorithm)
+ , fGeometry(numSkPoints, numSkVerbs, numConicWeights)
, fPathInfos(numPaths)
, fScissorSubBatches(numPaths)
, fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
@@ -100,8 +102,9 @@
int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
- fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
- clippedDevIBounds, &currPathPrimitiveCounts);
+ fPathInfos.back().tessellateFan(
+ fAlgorithm, path, fGeometry, currPathVerbsIdx, currPathPointsIdx, clippedDevIBounds,
+ &currPathPrimitiveCounts);
}
fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
@@ -113,9 +116,10 @@
}
}
-void GrCCFiller::PathInfo::tessellateFan(const GrCCFillGeometry& geometry, int verbsIdx,
- int ptsIdx, const SkIRect& clippedDevIBounds,
- PrimitiveTallies* newTriangleCounts) {
+void GrCCFiller::PathInfo::tessellateFan(
+ Algorithm algorithm, const SkPath& originalPath, const GrCCFillGeometry& geometry,
+ int verbsIdx, int ptsIdx, const SkIRect& clippedDevIBounds,
+ PrimitiveTallies* newTriangleCounts) {
using Verb = GrCCFillGeometry::Verb;
SkASSERT(-1 == fFanTessellationCount);
SkASSERT(!fFanTessellation);
@@ -126,11 +130,18 @@
newTriangleCounts->fTriangles =
newTriangleCounts->fWeightedTriangles = 0;
- // Build an SkPath of the Redbook fan. We use "winding" fill type right now because we are
- // producing a coverage count, and must fill in every region that has non-zero wind. The
- // path processor will convert coverage count to the appropriate fill type later.
+ // Build an SkPath of the Redbook fan.
SkPath fan;
- fan.setFillType(SkPath::kWinding_FillType);
+ if (Algorithm::kCoverageCount == algorithm) {
+ // We use "winding" fill type right now because we are producing a coverage count, and must
+ // fill in every region that has non-zero wind. The path processor will convert coverage
+ // count to the appropriate fill type later.
+ fan.setFillType(SkPath::kWinding_FillType);
+ } else {
+ // When counting winding numbers in the stencil buffer, it works to just tessellate the
+ // Redbook fan with the same fill type as the path.
+ fan.setFillType(originalPath.getFillType());
+ }
SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
switch (verbs[i]) {
@@ -192,10 +203,12 @@
std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
}
- if (1 == abs(tessWinding)) {
- ++newTriangleCounts->fTriangles;
- } else {
+ int weight = abs(tessWinding);
+ SkASSERT(SkPath::kEvenOdd_FillType != fan.getFillType() || weight == 1);
+ if (weight > 1 && Algorithm::kCoverageCount == algorithm) {
++newTriangleCounts->fWeightedTriangles;
+ } else {
+ newTriangleCounts->fTriangles += weight;
}
}
@@ -238,10 +251,10 @@
// elements past the end for this method to use as scratch space.
//
// Returns the next triangle instance after the final one emitted.
-static TriPointInstance* emit_recursive_fan(const SkTArray<SkPoint, true>& pts,
- SkTArray<int32_t, true>& indices, int firstIndex,
- int indexCount, const Sk2f& devToAtlasOffset,
- TriPointInstance out[]) {
+static TriPointInstance* emit_recursive_fan(
+ const SkTArray<SkPoint, true>& pts, SkTArray<int32_t, true>& indices, int firstIndex,
+ int indexCount, const Sk2f& devToAtlasOffset, TriPointInstance::Ordering ordering,
+ TriPointInstance out[]) {
if (indexCount < 3) {
return out;
}
@@ -249,35 +262,42 @@
int32_t oneThirdCount = indexCount / 3;
int32_t twoThirdsCount = (2 * indexCount) / 3;
out++->set(pts[indices[firstIndex]], pts[indices[firstIndex + oneThirdCount]],
- pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset);
+ pts[indices[firstIndex + twoThirdsCount]], devToAtlasOffset, ordering);
- out = emit_recursive_fan(pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, out);
- out = emit_recursive_fan(pts, indices, firstIndex + oneThirdCount,
- twoThirdsCount - oneThirdCount + 1, devToAtlasOffset, out);
+ out = emit_recursive_fan(
+ pts, indices, firstIndex, oneThirdCount + 1, devToAtlasOffset, ordering, out);
+ out = emit_recursive_fan(
+ pts, indices, firstIndex + oneThirdCount, twoThirdsCount - oneThirdCount + 1,
+ devToAtlasOffset, ordering, out);
int endIndex = firstIndex + indexCount;
int32_t oldValue = indices[endIndex];
indices[endIndex] = indices[firstIndex];
- out = emit_recursive_fan(pts, indices, firstIndex + twoThirdsCount,
- indexCount - twoThirdsCount + 1, devToAtlasOffset, out);
+ out = emit_recursive_fan(
+ pts, indices, firstIndex + twoThirdsCount, indexCount - twoThirdsCount + 1,
+ devToAtlasOffset, ordering, out);
indices[endIndex] = oldValue;
return out;
}
-static void emit_tessellated_fan(const GrTessellator::WindingVertex* vertices, int numVertices,
- const Sk2f& devToAtlasOffset,
- TriPointInstance* triPointInstanceData,
- QuadPointInstance* quadPointInstanceData,
- GrCCFillGeometry::PrimitiveTallies* indices) {
+void GrCCFiller::emitTessellatedFan(
+ const GrTessellator::WindingVertex* vertices, int numVertices, const Sk2f& devToAtlasOffset,
+ TriPointInstance::Ordering ordering, TriPointInstance* triPointInstanceData,
+ QuadPointInstance* quadPointInstanceData, GrCCFillGeometry::PrimitiveTallies* indices) {
for (int i = 0; i < numVertices; i += 3) {
- if (1 == abs(vertices[i].fWinding)) {
- triPointInstanceData[indices->fTriangles++].set(vertices[i].fPos, vertices[i + 1].fPos,
- vertices[i + 2].fPos, devToAtlasOffset);
- } else {
+ int weight = abs(vertices[i].fWinding);
+ SkASSERT(weight >= 1);
+ if (weight > 1 && Algorithm::kStencilWindingCount != fAlgorithm) {
quadPointInstanceData[indices->fWeightedTriangles++].setW(
vertices[i].fPos, vertices[i+1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
static_cast<float>(abs(vertices[i].fWinding)));
+ } else for (int j = 0; j < weight; ++j) {
+ // Unfortunately, there is not a way to increment stencil values by an amount larger
+ // than 1. Instead we draw the triangle 'weight' times.
+ triPointInstanceData[indices->fTriangles++].set(
+ vertices[i].fPos, vertices[i + 1].fPos, vertices[i + 2].fPos, devToAtlasOffset,
+ ordering);
}
}
}
@@ -289,6 +309,10 @@
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
+ auto triangleOrdering = (Algorithm::kCoverageCount == fAlgorithm)
+ ? TriPointInstance::Ordering::kXYTransposed
+ : TriPointInstance::Ordering::kXYInterleaved;
+
// Here we build a single instance buffer to share with every internal batch.
//
// CCPR processs 3 different types of primitives: triangles, quadratics, cubics. Each primitive
@@ -359,9 +383,10 @@
static_cast<float>(nextPathInfo->devToAtlasOffset().fY));
currFanIsTessellated = nextPathInfo->hasFanTessellation();
if (currFanIsTessellated) {
- emit_tessellated_fan(nextPathInfo->fanTessellation(),
- nextPathInfo->fanTessellationCount(), devToAtlasOffset,
- triPointInstanceData, quadPointInstanceData, currIndices);
+ this->emitTessellatedFan(
+ nextPathInfo->fanTessellation(), nextPathInfo->fanTessellationCount(),
+ devToAtlasOffset, triangleOrdering, triPointInstanceData,
+ quadPointInstanceData, currIndices);
}
++nextPathInfo;
continue;
@@ -383,8 +408,8 @@
continue;
case Verb::kMonotonicQuadraticTo:
- triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
- devToAtlasOffset);
+ triPointInstanceData[currIndices->fQuadratics++].set(
+ &pts[ptsIdx], devToAtlasOffset, TriPointInstance::Ordering::kXYTransposed);
ptsIdx += 2;
if (!currFanIsTessellated) {
SkASSERT(!currFan.empty());
@@ -393,8 +418,8 @@
continue;
case Verb::kMonotonicCubicTo:
- quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
- devToAtlasOffset[1]);
+ quadPointInstanceData[currIndices->fCubics++].set(
+ &pts[ptsIdx], devToAtlasOffset[0], devToAtlasOffset[1]);
ptsIdx += 3;
if (!currFanIsTessellated) {
SkASSERT(!currFan.empty());
@@ -427,9 +452,9 @@
// Reserve space for emit_recursive_fan. Technically this can grow to
// fanSize + log3(fanSize), but we approximate with log2.
currFan.push_back_n(SkNextLog2(fanSize));
- SkDEBUGCODE(TriPointInstance* end =)
- emit_recursive_fan(pts, currFan, 0, fanSize, devToAtlasOffset,
- triPointInstanceData + currIndices->fTriangles);
+ SkDEBUGCODE(TriPointInstance* end =) emit_recursive_fan(
+ pts, currFan, 0, fanSize, devToAtlasOffset, triangleOrdering,
+ triPointInstanceData + currIndices->fTriangles);
currIndices->fTriangles += fanSize - 2;
SkASSERT(triPointInstanceData + currIndices->fTriangles == end);
}
@@ -459,8 +484,9 @@
return true;
}
-void GrCCFiller::drawFills(GrOpFlushState* flushState, GrCCCoverageProcessor* proc,
- BatchID batchID, const SkIRect& drawBounds) const {
+void GrCCFiller::drawFills(
+ GrOpFlushState* flushState, GrCCCoverageProcessor* proc, const GrPipeline& pipeline,
+ BatchID batchID, const SkIRect& drawBounds) const {
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
SkASSERT(fInstanceBuffer);
@@ -468,9 +494,6 @@
GrResourceProvider* rp = flushState->resourceProvider();
const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
- GrPipeline pipeline(GrScissorTest::kEnabled, SkBlendMode::kPlus,
- flushState->drawOpArgs().fOutputSwizzle);
-
if (batchTotalCounts.fTriangles) {
proc->reset(PrimitiveType::kTriangles, rp);
this->drawPrimitives(
@@ -478,6 +501,7 @@
}
if (batchTotalCounts.fWeightedTriangles) {
+ SkASSERT(Algorithm::kStencilWindingCount != fAlgorithm);
proc->reset(PrimitiveType::kWeightedTriangles, rp);
this->drawPrimitives(
flushState, *proc, pipeline, batchID, &PrimitiveTallies::fWeightedTriangles,