ccpr: Rename GrCCPathParser to GrCCFiller
Various renames and other refactorings that will allow us to add new
stroking classes alongside the existing code for fills.
Bug: skia:
Change-Id: Ib477f9e1d87f9d4c1604719f9af0695a53614081
Reviewed-on: https://skia-review.googlesource.com/147503
Commit-Queue: Chris Dalton <csmartdalton@google.com>
Reviewed-by: Robert Phillips <robertphillips@google.com>
diff --git a/bench/GrCCGeometryBench.cpp b/bench/GrCCFillGeometryBench.cpp
similarity index 96%
rename from bench/GrCCGeometryBench.cpp
rename to bench/GrCCFillGeometryBench.cpp
index 5406cea..84a9e1e 100644
--- a/bench/GrCCGeometryBench.cpp
+++ b/bench/GrCCFillGeometryBench.cpp
@@ -7,7 +7,7 @@
#include "Benchmark.h"
-#include "ccpr/GrCCGeometry.h"
+#include "ccpr/GrCCFillGeometry.h"
#include "SkGeometry.h"
static int kNumBaseLoops = 50000;
@@ -61,7 +61,7 @@
private:
SkPoint fPoints[5];
SkString fName;
- GrCCGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
+ GrCCFillGeometry fGeometry{4*100*kNumBaseLoops, 2*100*kNumBaseLoops};
typedef Benchmark INHERITED;
};
diff --git a/gn/bench.gni b/gn/bench.gni
index 07a7e57..60410ad 100644
--- a/gn/bench.gni
+++ b/gn/bench.gni
@@ -51,7 +51,7 @@
"$_bench/GeometryBench.cpp",
"$_bench/GMBench.cpp",
"$_bench/GradientBench.cpp",
- "$_bench/GrCCGeometryBench.cpp",
+ "$_bench/GrCCFillGeometryBench.cpp",
"$_bench/GrMemoryPoolBench.cpp",
"$_bench/GrMipMapBench.cpp",
"$_bench/GrResourceCacheBench.cpp",
diff --git a/gn/gpu.gni b/gn/gpu.gni
index fc115fb..b92b6cd 100644
--- a/gn/gpu.gni
+++ b/gn/gpu.gni
@@ -313,12 +313,12 @@
"$_src/gpu/ccpr/GrCCCubicShader.h",
"$_src/gpu/ccpr/GrCCDrawPathsOp.cpp",
"$_src/gpu/ccpr/GrCCDrawPathsOp.h",
- "$_src/gpu/ccpr/GrCCGeometry.cpp",
- "$_src/gpu/ccpr/GrCCGeometry.h",
+ "$_src/gpu/ccpr/GrCCFiller.cpp",
+ "$_src/gpu/ccpr/GrCCFiller.h",
+ "$_src/gpu/ccpr/GrCCFillGeometry.cpp",
+ "$_src/gpu/ccpr/GrCCFillGeometry.h",
"$_src/gpu/ccpr/GrCCPathCache.cpp",
"$_src/gpu/ccpr/GrCCPathCache.h",
- "$_src/gpu/ccpr/GrCCPathParser.cpp",
- "$_src/gpu/ccpr/GrCCPathParser.h",
"$_src/gpu/ccpr/GrCCPathProcessor.cpp",
"$_src/gpu/ccpr/GrCCPathProcessor.h",
"$_src/gpu/ccpr/GrCCPerFlushResources.cpp",
diff --git a/samplecode/SampleCCPRGeometry.cpp b/samplecode/SampleCCPRGeometry.cpp
index f883dbd..8d34520 100644
--- a/samplecode/SampleCCPRGeometry.cpp
+++ b/samplecode/SampleCCPRGeometry.cpp
@@ -21,7 +21,7 @@
#include "SkPath.h"
#include "SkRectPriv.h"
#include "ccpr/GrCCCoverageProcessor.h"
-#include "ccpr/GrCCGeometry.h"
+#include "ccpr/GrCCFillGeometry.h"
#include "gl/GrGLGpu.cpp"
#include "glsl/GrGLSLFragmentProcessor.h"
#include "ops/GrDrawOp.h"
@@ -76,7 +76,8 @@
public:
DrawCoverageCountOp(CCPRGeometryView* view) : INHERITED(ClassID()), fView(view) {
- this->setBounds(SkRectPriv::MakeLargest(), GrOp::HasAABloat::kNo, GrOp::IsZeroArea::kNo);
+ this->setBounds(SkRect::MakeIWH(fView->width(), fView->height()), GrOp::HasAABloat::kNo,
+ GrOp::IsZeroArea::kNo);
}
const char* name() const override {
@@ -191,9 +192,10 @@
if (GrRenderTargetContext* rtc = canvas->internal_private_accessTopLayerRenderTargetContext()) {
// Render coverage count.
GrContext* ctx = canvas->getGrContext();
+ SkASSERT(ctx);
+
GrOpMemoryPool* pool = ctx->contextPriv().opMemoryPool();
- SkASSERT(ctx);
sk_sp<GrRenderTargetContext> ccbuff =
ctx->contextPriv().makeDeferredRenderTargetContext(SkBackingFit::kApprox,
this->width(), this->height(),
@@ -249,26 +251,27 @@
}
void CCPRGeometryView::updateGpuData() {
+ using Verb = GrCCFillGeometry::Verb;
fTriPointInstances.reset();
fQuadPointInstances.reset();
if (PrimitiveType::kCubics == fPrimitiveType) {
double t[2], s[2];
fCubicType = GrPathUtils::getCubicKLM(fPoints, &fCubicKLM, t, s);
- GrCCGeometry geometry;
+ GrCCFillGeometry geometry;
geometry.beginContour(fPoints[0]);
geometry.cubicTo(fPoints, kDebugBloat / 2, kDebugBloat / 2);
geometry.endContour();
int ptsIdx = 0;
- for (GrCCGeometry::Verb verb : geometry.verbs()) {
+ for (Verb verb : geometry.verbs()) {
switch (verb) {
- case GrCCGeometry::Verb::kLineTo:
+ case Verb::kLineTo:
++ptsIdx;
continue;
- case GrCCGeometry::Verb::kMonotonicQuadraticTo:
+ case Verb::kMonotonicQuadraticTo:
ptsIdx += 2;
continue;
- case GrCCGeometry::Verb::kMonotonicCubicTo:
+ case Verb::kMonotonicCubicTo:
fQuadPointInstances.push_back().set(&geometry.points()[ptsIdx], 0, 0);
ptsIdx += 3;
continue;
@@ -278,7 +281,7 @@
}
} else if (PrimitiveType::kTriangles != fPrimitiveType) {
SkPoint P3[3] = {fPoints[0], fPoints[1], fPoints[3]};
- GrCCGeometry geometry;
+ GrCCFillGeometry geometry;
geometry.beginContour(P3[0]);
if (PrimitiveType::kQuadratics == fPrimitiveType) {
geometry.quadraticTo(P3);
@@ -288,23 +291,22 @@
}
geometry.endContour();
int ptsIdx = 0, conicWeightIdx = 0;
- for (GrCCGeometry::Verb verb : geometry.verbs()) {
- if (GrCCGeometry::Verb::kBeginContour == verb ||
- GrCCGeometry::Verb::kEndOpenContour == verb ||
- GrCCGeometry::Verb::kEndClosedContour == verb) {
+ for (Verb verb : geometry.verbs()) {
+ if (Verb::kBeginContour == verb ||
+ Verb::kEndOpenContour == verb ||
+ Verb::kEndClosedContour == verb) {
continue;
}
- if (GrCCGeometry::Verb::kLineTo == verb) {
+ if (Verb::kLineTo == verb) {
++ptsIdx;
continue;
}
- SkASSERT(GrCCGeometry::Verb::kMonotonicQuadraticTo == verb ||
- GrCCGeometry::Verb::kMonotonicConicTo == verb);
+ SkASSERT(Verb::kMonotonicQuadraticTo == verb || Verb::kMonotonicConicTo == verb);
if (PrimitiveType::kQuadratics == fPrimitiveType &&
- GrCCGeometry::Verb::kMonotonicQuadraticTo == verb) {
+ Verb::kMonotonicQuadraticTo == verb) {
fTriPointInstances.push_back().set(&geometry.points()[ptsIdx], Sk2f(0, 0));
} else if (PrimitiveType::kConics == fPrimitiveType &&
- GrCCGeometry::Verb::kMonotonicConicTo == verb) {
+ Verb::kMonotonicConicTo == verb) {
fQuadPointInstances.push_back().setW(&geometry.points()[ptsIdx], Sk2f(0, 0),
geometry.getConicWeight(conicWeightIdx++));
}
diff --git a/src/gpu/ccpr/GrCCGeometry.cpp b/src/gpu/ccpr/GrCCFillGeometry.cpp
similarity index 94%
rename from src/gpu/ccpr/GrCCGeometry.cpp
rename to src/gpu/ccpr/GrCCFillGeometry.cpp
index 17a54af..81692cf 100644
--- a/src/gpu/ccpr/GrCCGeometry.cpp
+++ b/src/gpu/ccpr/GrCCFillGeometry.cpp
@@ -5,7 +5,7 @@
* found in the LICENSE file.
*/
-#include "GrCCGeometry.h"
+#include "GrCCFillGeometry.h"
#include "GrTypes.h"
#include "SkGeometry.h"
@@ -13,19 +13,14 @@
#include <cmath>
#include <cstdlib>
-// We convert between SkPoint and Sk2f freely throughout this file.
-GR_STATIC_ASSERT(SK_SCALAR_IS_FLOAT);
-GR_STATIC_ASSERT(2 * sizeof(float) == sizeof(SkPoint));
-GR_STATIC_ASSERT(0 == offsetof(SkPoint, fX));
-
static constexpr float kFlatnessThreshold = 1/16.f; // 1/16 of a pixel.
-void GrCCGeometry::beginPath() {
+void GrCCFillGeometry::beginPath() {
SkASSERT(!fBuildingContour);
fVerbs.push_back(Verb::kBeginPath);
}
-void GrCCGeometry::beginContour(const SkPoint& pt) {
+void GrCCFillGeometry::beginContour(const SkPoint& pt) {
SkASSERT(!fBuildingContour);
// Store the current verb count in the fTriangles field for now. When we close the contour we
// will use this value to calculate the actual number of triangles in its fan.
@@ -38,7 +33,7 @@
SkDEBUGCODE(fBuildingContour = true);
}
-void GrCCGeometry::lineTo(const SkPoint P[2]) {
+void GrCCFillGeometry::lineTo(const SkPoint P[2]) {
SkASSERT(fBuildingContour);
SkASSERT(P[0] == fPoints.back());
Sk2f p0 = Sk2f::Load(P);
@@ -46,7 +41,7 @@
this->appendLine(p0, p1);
}
-inline void GrCCGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
+inline void GrCCFillGeometry::appendLine(const Sk2f& p0, const Sk2f& p1) {
SkASSERT(fPoints.back() == SkPoint::Make(p0[0], p0[1]));
if ((p0 == p1).allTrue()) {
return;
@@ -138,7 +133,7 @@
return SkNx_fma(t, b - a, a);
}
-void GrCCGeometry::quadraticTo(const SkPoint P[3]) {
+void GrCCFillGeometry::quadraticTo(const SkPoint P[3]) {
SkASSERT(fBuildingContour);
SkASSERT(P[0] == fPoints.back());
Sk2f p0 = Sk2f::Load(P);
@@ -155,7 +150,7 @@
this->appendQuadratics(p0, p1, p2);
}
-inline void GrCCGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
+inline void GrCCFillGeometry::appendQuadratics(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
Sk2f tan0 = p1 - p0;
Sk2f tan1 = p2 - p1;
@@ -193,7 +188,8 @@
this->appendMonotonicQuadratic(p012, p12, p2);
}
-inline void GrCCGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2) {
+inline void GrCCFillGeometry::appendMonotonicQuadratic(const Sk2f& p0, const Sk2f& p1,
+ const Sk2f& p2) {
// Don't send curves to the GPU if we know they are nearly flat (or just very small).
if (are_collinear(p0, p1, p2)) {
this->appendLine(p0, p2);
@@ -465,7 +461,7 @@
}
}
-void GrCCGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
+void GrCCFillGeometry::cubicTo(const SkPoint P[4], float inflectPad, float loopIntersectPad) {
SkASSERT(fBuildingContour);
SkASSERT(P[0] == fPoints.back());
@@ -541,9 +537,9 @@
*abcd = lerp(*abc, *bcd, TT);
}
-void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2, const Sk2f& p3, const float chops[], int numChops,
- float localT0, float localT1) {
+void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
+ const Sk2f& p2, const Sk2f& p3, const float chops[],
+ int numChops, float localT0, float localT1) {
if (numChops) {
SkASSERT(numChops > 0);
int midChopIdx = numChops/2;
@@ -576,8 +572,8 @@
this->appendCubics(mode, p0, p1, p2, p3);
}
-void GrCCGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
- const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
+void GrCCFillGeometry::appendCubics(AppendCubicMode mode, const Sk2f& p0, const Sk2f& p1,
+ const Sk2f& p2, const Sk2f& p3, int maxSubdivisions) {
if (SkCubicType::kLoop != fCurrCubicType) {
// Serpentines and cusps are always monotonic after chopping around inflection points.
SkASSERT(!SkCubicIsDegenerate(fCurrCubicType));
@@ -672,11 +668,11 @@
return std::abs(q*q - r) < std::abs(a*c - r) ? q/a : c/q;
}
-inline void GrCCGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
- const Sk2f& p1, const Sk2f& p2,
- const Sk2f& p3, const Sk2f& tan0,
- const Sk2f& tan1,
- int maxFutureSubdivisions) {
+inline void GrCCFillGeometry::chopAndAppendCubicAtMidTangent(AppendCubicMode mode, const Sk2f& p0,
+ const Sk2f& p1, const Sk2f& p2,
+ const Sk2f& p3, const Sk2f& tan0,
+ const Sk2f& tan1,
+ int maxFutureSubdivisions) {
float midT = find_midtangent(tan0, tan1, p3 + (p1 - p2)*3 - p0,
(p0 - p1*2 + p2)*2,
p1 - p0);
@@ -694,7 +690,7 @@
this->appendCubics(mode, pT, p11, p12, p3, maxFutureSubdivisions);
}
-void GrCCGeometry::conicTo(const SkPoint P[3], float w) {
+void GrCCFillGeometry::conicTo(const SkPoint P[3], float w) {
SkASSERT(fBuildingContour);
SkASSERT(P[0] == fPoints.back());
Sk2f p0 = Sk2f::Load(P);
@@ -743,7 +739,8 @@
this->appendMonotonicConic(p0, p1, p2, w);
}
-void GrCCGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2, float w) {
+void GrCCFillGeometry::appendMonotonicConic(const Sk2f& p0, const Sk2f& p1, const Sk2f& p2,
+ float w) {
SkASSERT(w >= 0);
Sk2f base = p2 - p0;
@@ -784,7 +781,7 @@
++fCurrContourTallies.fConics;
}
-GrCCGeometry::PrimitiveTallies GrCCGeometry::endContour() {
+GrCCFillGeometry::PrimitiveTallies GrCCFillGeometry::endContour() {
SkASSERT(fBuildingContour);
SkASSERT(fVerbs.count() >= fCurrContourTallies.fTriangles);
diff --git a/src/gpu/ccpr/GrCCGeometry.h b/src/gpu/ccpr/GrCCFillGeometry.h
similarity index 83%
rename from src/gpu/ccpr/GrCCGeometry.h
rename to src/gpu/ccpr/GrCCFillGeometry.h
index 5c5d1d2..df817db 100644
--- a/src/gpu/ccpr/GrCCGeometry.h
+++ b/src/gpu/ccpr/GrCCFillGeometry.h
@@ -5,8 +5,8 @@
* found in the LICENSE file.
*/
-#ifndef GrGrCCGeometry_DEFINED
-#define GrGrCCGeometry_DEFINED
+#ifndef GrGrCCFillGeometry_DEFINED
+#define GrGrCCFillGeometry_DEFINED
#include "SkGeometry.h"
#include "SkNx.h"
@@ -15,14 +15,14 @@
/**
* This class chops device-space contours up into a series of segments that CCPR knows how to
- * render. (See GrCCGeometry::Verb.)
+ * fill. (See GrCCFillGeometry::Verb.)
*
* NOTE: This must be done in device space, since an affine transformation can change whether a
* curve is monotonic.
*/
-class GrCCGeometry {
+class GrCCFillGeometry {
public:
- // These are the verbs that CCPR knows how to draw. If a path has any segments that don't map to
+ // These are the verbs that CCPR knows how to fill. If a path has any segments that don't map to
// this list, then they are chopped into smaller ones that do. A list of these comprise a
// compact representation of what can later be expanded into GPU instance data.
enum class Verb : uint8_t {
@@ -49,7 +49,7 @@
bool operator==(const PrimitiveTallies&);
};
- GrCCGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
+ GrCCFillGeometry(int numSkPoints = 0, int numSkVerbs = 0, int numConicWeights = 0)
: fPoints(numSkPoints * 3) // Reserve for a 3x expansion in points and verbs.
, fVerbs(numSkVerbs * 3)
, fConicWeights(numConicWeights * 3/2) {}
@@ -64,17 +64,6 @@
fVerbs.reset();
}
- // This is included in case the caller needs to discard previously added contours. It is up to
- // the caller to track counts and ensure we don't pop back into the middle of a different
- // contour.
- void resize_back(int numPoints, int numVerbs) {
- SkASSERT(!fBuildingContour);
- fPoints.resize_back(numPoints);
- fVerbs.resize_back(numVerbs);
- SkASSERT(fVerbs.empty() || fVerbs.back() == Verb::kEndOpenContour ||
- fVerbs.back() == Verb::kEndClosedContour);
- }
-
void beginPath();
void beginContour(const SkPoint&);
void lineTo(const SkPoint P[2]);
@@ -129,7 +118,7 @@
SkSTArray<32, float, true> fConicWeights;
};
-inline void GrCCGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
+inline void GrCCFillGeometry::PrimitiveTallies::operator+=(const PrimitiveTallies& b) {
fTriangles += b.fTriangles;
fWeightedTriangles += b.fWeightedTriangles;
fQuadratics += b.fQuadratics;
@@ -137,8 +126,8 @@
fConics += b.fConics;
}
-GrCCGeometry::PrimitiveTallies
-inline GrCCGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
+GrCCFillGeometry::PrimitiveTallies
+inline GrCCFillGeometry::PrimitiveTallies::operator-(const PrimitiveTallies& b) const {
return {fTriangles - b.fTriangles,
fWeightedTriangles - b.fWeightedTriangles,
fQuadratics - b.fQuadratics,
@@ -146,7 +135,7 @@
fConics - b.fConics};
}
-inline bool GrCCGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
+inline bool GrCCFillGeometry::PrimitiveTallies::operator==(const PrimitiveTallies& b) {
return fTriangles == b.fTriangles && fWeightedTriangles == b.fWeightedTriangles &&
fQuadratics == b.fQuadratics && fCubics == b.fCubics && fConics == b.fConics;
}
diff --git a/src/gpu/ccpr/GrCCPathParser.cpp b/src/gpu/ccpr/GrCCFiller.cpp
similarity index 64%
rename from src/gpu/ccpr/GrCCPathParser.cpp
rename to src/gpu/ccpr/GrCCFiller.cpp
index 190f162..cdace98 100644
--- a/src/gpu/ccpr/GrCCPathParser.cpp
+++ b/src/gpu/ccpr/GrCCFiller.cpp
@@ -5,7 +5,7 @@
* found in the LICENSE file.
*/
-#include "GrCCPathParser.h"
+#include "GrCCFiller.h"
#include "GrCaps.h"
#include "GrGpuCommandBuffer.h"
@@ -15,105 +15,36 @@
#include "SkPath.h"
#include "SkPathPriv.h"
#include "SkPoint.h"
-#include "ccpr/GrCCGeometry.h"
#include <stdlib.h>
using TriPointInstance = GrCCCoverageProcessor::TriPointInstance;
using QuadPointInstance = GrCCCoverageProcessor::QuadPointInstance;
-GrCCPathParser::GrCCPathParser(int numPaths, const PathStats& pathStats)
- // Overallocate by one point to accomodate for overflow with Sk4f. (See parsePath.)
- : fLocalDevPtsBuffer(pathStats.fMaxPointsPerPath + 1)
- , fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
+GrCCFiller::GrCCFiller(int numPaths, const PathStats& pathStats)
+ : fGeometry(pathStats.fNumTotalSkPoints, pathStats.fNumTotalSkVerbs,
pathStats.fNumTotalConicWeights)
- , fPathsInfo(numPaths)
+ , fPathInfos(numPaths)
, fScissorSubBatches(numPaths)
, fTotalPrimitiveCounts{PrimitiveTallies(), PrimitiveTallies()} {
// Batches decide what to draw by looking where the previous one ended. Define initial batches
// that "end" at the beginning of the data. These will not be drawn, but will only be be read by
// the first actual batch.
fScissorSubBatches.push_back() = {PrimitiveTallies(), SkIRect::MakeEmpty()};
- fCoverageCountBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(),
- PrimitiveTallies()};
+ fBatches.push_back() = {PrimitiveTallies(), fScissorSubBatches.count(), PrimitiveTallies()};
}
-void GrCCPathParser::parsePath(const SkMatrix& m, const SkPath& path, SkRect* devBounds,
- SkRect* devBounds45) {
- const SkPoint* pts = SkPathPriv::PointData(path);
- int numPts = path.countPoints();
- SkASSERT(numPts + 1 <= fLocalDevPtsBuffer.count());
+void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
+ GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
+ const SkIVector& devToAtlasOffset) {
+ SkASSERT(!fInstanceBuffer); // Can't call after prepareToDraw().
+ SkASSERT(!path.isEmpty());
- if (!numPts) {
- devBounds->setEmpty();
- devBounds45->setEmpty();
- this->parsePath(path, nullptr);
- return;
- }
-
- // m45 transforms path points into "45 degree" device space. A bounding box in this space gives
- // the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal
- // transform is not necessary as long as the shader uses the correct inverse.
- SkMatrix m45;
- m45.setSinCos(1, 1);
- m45.preConcat(m);
-
- // X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points:
- // device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords).
- // | 1 1 |
- Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY());
- Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY());
- Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY());
-
- // Map the path's points to device space and accumulate bounding boxes.
- Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T);
- devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt);
- Sk4f topLeft = devPt;
- Sk4f bottomRight = devPt;
-
- // Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two,
- // and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must
- // be at least one larger than the number of points.
- devPt.store(&fLocalDevPtsBuffer[0]);
-
- for (int i = 1; i < numPts; ++i) {
- devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T);
- devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt);
- topLeft = Sk4f::Min(topLeft, devPt);
- bottomRight = Sk4f::Max(bottomRight, devPt);
- devPt.store(&fLocalDevPtsBuffer[i]);
- }
-
- SkPoint topLeftPts[2], bottomRightPts[2];
- topLeft.store(topLeftPts);
- bottomRight.store(bottomRightPts);
- devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(),
- bottomRightPts[0].y());
- devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(),
- bottomRightPts[1].y());
-
- this->parsePath(path, fLocalDevPtsBuffer.get());
-}
-
-void GrCCPathParser::parseDeviceSpacePath(const SkPath& deviceSpacePath) {
- this->parsePath(deviceSpacePath, SkPathPriv::PointData(deviceSpacePath));
-}
-
-void GrCCPathParser::parsePath(const SkPath& path, const SkPoint* deviceSpacePts) {
- SkASSERT(!fInstanceBuffer); // Can't call after finalize().
- SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath() for the last one first.
- SkDEBUGCODE(fParsingPath = true);
- SkASSERT(path.isEmpty() || deviceSpacePts);
-
- fCurrPathPointsIdx = fGeometry.points().count();
- fCurrPathVerbsIdx = fGeometry.verbs().count();
- fCurrPathPrimitiveCounts = PrimitiveTallies();
+ int currPathPointsIdx = fGeometry.points().count();
+ int currPathVerbsIdx = fGeometry.verbs().count();
+ PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();
fGeometry.beginPath();
- if (path.isEmpty()) {
- return;
- }
-
const float* conicWeights = SkPathPriv::ConicWeightData(path);
int ptsIdx = 0;
int conicWeightsIdx = 0;
@@ -122,13 +53,17 @@
for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
switch (verb) {
case SkPath::kMove_Verb:
- this->endContourIfNeeded(insideContour);
+ if (insideContour) {
+ currPathPrimitiveCounts += fGeometry.endContour();
+ }
fGeometry.beginContour(deviceSpacePts[ptsIdx]);
++ptsIdx;
insideContour = true;
continue;
case SkPath::kClose_Verb:
- this->endContourIfNeeded(insideContour);
+ if (insideContour) {
+ currPathPrimitiveCounts += fGeometry.endContour();
+ }
insideContour = false;
continue;
case SkPath::kLine_Verb:
@@ -155,123 +90,124 @@
SkASSERT(ptsIdx == path.countPoints());
SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));
- this->endContourIfNeeded(insideContour);
-}
-
-void GrCCPathParser::endContourIfNeeded(bool insideContour) {
if (insideContour) {
- fCurrPathPrimitiveCounts += fGeometry.endContour();
+ currPathPrimitiveCounts += fGeometry.endContour();
}
-}
-void GrCCPathParser::saveParsedPath(GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
- const SkIVector& devToAtlasOffset) {
- SkASSERT(fParsingPath);
-
- fPathsInfo.emplace_back(scissorTest, devToAtlasOffset);
+ fPathInfos.emplace_back(scissorTest, devToAtlasOffset);
// Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
- int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1;
+ int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
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.
- fCurrPathPrimitiveCounts.fTriangles =
- fCurrPathPrimitiveCounts.fWeightedTriangles = 0;
-
- const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
- const SkTArray<SkPoint, true>& pts = fGeometry.points();
- int ptsIdx = fCurrPathPointsIdx;
-
- // 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.
- SkPath fan;
- fan.setFillType(SkPath::kWinding_FillType);
- SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
- for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
- switch (verbs[i]) {
- case GrCCGeometry::Verb::kBeginPath:
- SK_ABORT("Invalid GrCCGeometry");
- continue;
-
- case GrCCGeometry::Verb::kBeginContour:
- fan.moveTo(pts[ptsIdx++]);
- continue;
-
- case GrCCGeometry::Verb::kLineTo:
- fan.lineTo(pts[ptsIdx++]);
- continue;
-
- case GrCCGeometry::Verb::kMonotonicQuadraticTo:
- case GrCCGeometry::Verb::kMonotonicConicTo:
- fan.lineTo(pts[ptsIdx + 1]);
- ptsIdx += 2;
- continue;
-
- case GrCCGeometry::Verb::kMonotonicCubicTo:
- fan.lineTo(pts[ptsIdx + 2]);
- ptsIdx += 3;
- continue;
-
- case GrCCGeometry::Verb::kEndClosedContour:
- case GrCCGeometry::Verb::kEndOpenContour:
- fan.close();
- continue;
- }
- }
- GrTessellator::WindingVertex* vertices = nullptr;
- int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
- SkRect::Make(clippedDevIBounds), &vertices);
- SkASSERT(0 == count % 3);
- for (int i = 0; i < count; i += 3) {
- int tessWinding = vertices[i].fWinding;
- SkASSERT(tessWinding == vertices[i + 1].fWinding);
- SkASSERT(tessWinding == vertices[i + 2].fWinding);
-
- // Ensure this triangle's points actually wind in the same direction as tessWinding.
- // CCPR shaders use the sign of wind to determine which direction to bloat, so even for
- // "wound" triangles the winding sign and point ordering need to agree.
- float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
- float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
- float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
- float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
- float wind = ax*by - ay*bx;
- if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
- std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
- }
-
- if (1 == abs(tessWinding)) {
- ++fCurrPathPrimitiveCounts.fTriangles;
- } else {
- ++fCurrPathPrimitiveCounts.fWeightedTriangles;
- }
- }
-
- fPathsInfo.back().adoptFanTessellation(vertices, count);
+ fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
+ clippedDevIBounds, &currPathPrimitiveCounts);
}
- fTotalPrimitiveCounts[(int)scissorTest] += fCurrPathPrimitiveCounts;
+ fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;
if (GrScissorTest::kEnabled == scissorTest) {
fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
devToAtlasOffset.fY)};
}
-
- SkDEBUGCODE(fParsingPath = false);
}
-void GrCCPathParser::discardParsedPath() {
- SkASSERT(fParsingPath);
- fGeometry.resize_back(fCurrPathPointsIdx, fCurrPathVerbsIdx);
- SkDEBUGCODE(fParsingPath = false);
+void GrCCFiller::PathInfo::tessellateFan(const GrCCFillGeometry& geometry, int verbsIdx,
+ int ptsIdx, const SkIRect& clippedDevIBounds,
+ PrimitiveTallies* newTriangleCounts) {
+ using Verb = GrCCFillGeometry::Verb;
+ SkASSERT(-1 == fFanTessellationCount);
+ SkASSERT(!fFanTessellation);
+
+ const SkTArray<Verb, true>& verbs = geometry.verbs();
+ const SkTArray<SkPoint, true>& pts = geometry.points();
+
+ 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.
+ SkPath fan;
+ fan.setFillType(SkPath::kWinding_FillType);
+ SkASSERT(Verb::kBeginPath == verbs[verbsIdx]);
+ for (int i = verbsIdx + 1; i < verbs.count(); ++i) {
+ switch (verbs[i]) {
+ case Verb::kBeginPath:
+ SK_ABORT("Invalid GrCCFillGeometry");
+ continue;
+
+ case Verb::kBeginContour:
+ fan.moveTo(pts[ptsIdx++]);
+ continue;
+
+ case Verb::kLineTo:
+ fan.lineTo(pts[ptsIdx++]);
+ continue;
+
+ case Verb::kMonotonicQuadraticTo:
+ case Verb::kMonotonicConicTo:
+ fan.lineTo(pts[ptsIdx + 1]);
+ ptsIdx += 2;
+ continue;
+
+ case Verb::kMonotonicCubicTo:
+ fan.lineTo(pts[ptsIdx + 2]);
+ ptsIdx += 3;
+ continue;
+
+ case Verb::kEndClosedContour:
+ case Verb::kEndOpenContour:
+ fan.close();
+ continue;
+ }
+ }
+
+ GrTessellator::WindingVertex* vertices = nullptr;
+ fFanTessellationCount =
+ GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
+ SkRect::Make(clippedDevIBounds), &vertices);
+ if (fFanTessellationCount <= 0) {
+ SkASSERT(0 == fFanTessellationCount);
+ SkASSERT(nullptr == vertices);
+ return;
+ }
+
+ SkASSERT(0 == fFanTessellationCount % 3);
+ for (int i = 0; i < fFanTessellationCount; i += 3) {
+ int tessWinding = vertices[i].fWinding;
+ SkASSERT(tessWinding == vertices[i + 1].fWinding);
+ SkASSERT(tessWinding == vertices[i + 2].fWinding);
+
+ // Ensure this triangle's points actually wind in the same direction as tessWinding.
+ // CCPR shaders use the sign of wind to determine which direction to bloat, so even for
+ // "wound" triangles the winding sign and point ordering need to agree.
+ float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
+ float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
+ float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
+ float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
+ float wind = ax*by - ay*bx;
+ if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
+ std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
+ }
+
+ if (1 == abs(tessWinding)) {
+ ++newTriangleCounts->fTriangles;
+ } else {
+ ++newTriangleCounts->fWeightedTriangles;
+ }
+ }
+
+ fFanTessellation.reset(vertices);
}
-GrCCPathParser::CoverageCountBatchID GrCCPathParser::closeCurrentBatch() {
+GrCCFiller::BatchID GrCCFiller::closeCurrentBatch() {
SkASSERT(!fInstanceBuffer);
- SkASSERT(!fCoverageCountBatches.empty());
+ SkASSERT(!fBatches.empty());
- const auto& lastBatch = fCoverageCountBatches.back();
+ const auto& lastBatch = fBatches.back();
int maxMeshes = 1 + fScissorSubBatches.count() - lastBatch.fEndScissorSubBatchIdx;
fMaxMeshesPerDraw = SkTMax(fMaxMeshesPerDraw, maxMeshes);
@@ -282,12 +218,12 @@
lastScissorSubBatch.fEndPrimitiveIndices;
// This will invalidate lastBatch.
- fCoverageCountBatches.push_back() = {
+ fBatches.push_back() = {
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled],
fScissorSubBatches.count(),
batchTotalCounts
};
- return fCoverageCountBatches.count() - 1;
+ return fBatches.count() - 1;
}
// Emits a contour's triangle fan.
@@ -334,7 +270,7 @@
const Sk2f& devToAtlasOffset,
TriPointInstance* triPointInstanceData,
QuadPointInstance* quadPointInstanceData,
- GrCCGeometry::PrimitiveTallies* indices) {
+ 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,
@@ -347,11 +283,12 @@
}
}
-bool GrCCPathParser::finalize(GrOnFlushResourceProvider* onFlushRP) {
- SkASSERT(!fParsingPath); // Call saveParsedPath() or discardParsedPath().
- SkASSERT(fCoverageCountBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
+bool GrCCFiller::prepareToDraw(GrOnFlushResourceProvider* onFlushRP) {
+ using Verb = GrCCFillGeometry::Verb;
+ SkASSERT(!fInstanceBuffer);
+ SkASSERT(fBatches.back().fEndNonScissorIndices == // Call closeCurrentBatch().
fTotalPrimitiveCounts[(int)GrScissorTest::kDisabled]);
- SkASSERT(fCoverageCountBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
+ SkASSERT(fBatches.back().fEndScissorSubBatchIdx == fScissorSubBatches.count());
// Here we build a single instance buffer to share with every internal batch.
//
@@ -393,6 +330,7 @@
fInstanceBuffer = onFlushRP->makeBuffer(kVertex_GrBufferType,
quadEndIdx * sizeof(QuadPointInstance));
if (!fInstanceBuffer) {
+ SkDebugf("WARNING: failed to allocate CCPR fill instance buffer.\n");
return false;
}
@@ -401,7 +339,7 @@
reinterpret_cast<QuadPointInstance*>(triPointInstanceData);
SkASSERT(quadPointInstanceData);
- PathInfo* nextPathInfo = fPathsInfo.begin();
+ PathInfo* nextPathInfo = fPathInfos.begin();
Sk2f devToAtlasOffset;
PrimitiveTallies instanceIndices[2] = {fBaseInstances[0], fBaseInstances[1]};
PrimitiveTallies* currIndices = nullptr;
@@ -413,9 +351,9 @@
int nextConicWeightIdx = 0;
// Expand the ccpr verbs into GPU instance buffers.
- for (GrCCGeometry::Verb verb : fGeometry.verbs()) {
+ for (Verb verb : fGeometry.verbs()) {
switch (verb) {
- case GrCCGeometry::Verb::kBeginPath:
+ case Verb::kBeginPath:
SkASSERT(currFan.empty());
currIndices = &instanceIndices[(int)nextPathInfo->scissorTest()];
devToAtlasOffset = Sk2f(static_cast<float>(nextPathInfo->devToAtlasOffset().fX),
@@ -429,7 +367,7 @@
++nextPathInfo;
continue;
- case GrCCGeometry::Verb::kBeginContour:
+ case Verb::kBeginContour:
SkASSERT(currFan.empty());
++ptsIdx;
if (!currFanIsTessellated) {
@@ -437,7 +375,7 @@
}
continue;
- case GrCCGeometry::Verb::kLineTo:
+ case Verb::kLineTo:
++ptsIdx;
if (!currFanIsTessellated) {
SkASSERT(!currFan.empty());
@@ -445,7 +383,7 @@
}
continue;
- case GrCCGeometry::Verb::kMonotonicQuadraticTo:
+ case Verb::kMonotonicQuadraticTo:
triPointInstanceData[currIndices->fQuadratics++].set(&pts[ptsIdx],
devToAtlasOffset);
ptsIdx += 2;
@@ -455,7 +393,7 @@
}
continue;
- case GrCCGeometry::Verb::kMonotonicCubicTo:
+ case Verb::kMonotonicCubicTo:
quadPointInstanceData[currIndices->fCubics++].set(&pts[ptsIdx], devToAtlasOffset[0],
devToAtlasOffset[1]);
ptsIdx += 3;
@@ -465,7 +403,7 @@
}
continue;
- case GrCCGeometry::Verb::kMonotonicConicTo:
+ case Verb::kMonotonicConicTo:
quadPointInstanceData[currIndices->fConics++].setW(
&pts[ptsIdx], devToAtlasOffset,
fGeometry.getConicWeight(nextConicWeightIdx));
@@ -477,13 +415,13 @@
}
continue;
- case GrCCGeometry::Verb::kEndClosedContour: // endPt == startPt.
+ case Verb::kEndClosedContour: // endPt == startPt.
if (!currFanIsTessellated) {
SkASSERT(!currFan.empty());
currFan.pop_back();
}
// fallthru.
- case GrCCGeometry::Verb::kEndOpenContour: // endPt != startPt.
+ case Verb::kEndOpenContour: // endPt != startPt.
SkASSERT(!currFanIsTessellated || currFan.empty());
if (!currFanIsTessellated && currFan.count() >= 3) {
int fanSize = currFan.count();
@@ -503,7 +441,7 @@
fInstanceBuffer->unmap();
- SkASSERT(nextPathInfo == fPathsInfo.end());
+ SkASSERT(nextPathInfo == fPathInfos.end());
SkASSERT(ptsIdx == pts.count() - 1);
SkASSERT(instanceIndices[0].fTriangles == fBaseInstances[1].fTriangles);
SkASSERT(instanceIndices[1].fTriangles == fBaseInstances[0].fQuadratics);
@@ -522,13 +460,13 @@
return true;
}
-void GrCCPathParser::drawCoverageCount(GrOpFlushState* flushState, CoverageCountBatchID batchID,
- const SkIRect& drawBounds) const {
+void GrCCFiller::drawFills(GrOpFlushState* flushState, BatchID batchID,
+ const SkIRect& drawBounds) const {
using PrimitiveType = GrCCCoverageProcessor::PrimitiveType;
SkASSERT(fInstanceBuffer);
- const PrimitiveTallies& batchTotalCounts = fCoverageCountBatches[batchID].fTotalPrimitiveCounts;
+ const PrimitiveTallies& batchTotalCounts = fBatches[batchID].fTotalPrimitiveCounts;
GrPipeline pipeline(flushState->drawOpArgs().fProxy, GrScissorTest::kEnabled,
SkBlendMode::kPlus);
@@ -559,11 +497,10 @@
}
}
-void GrCCPathParser::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
- CoverageCountBatchID batchID,
- GrCCCoverageProcessor::PrimitiveType primitiveType,
- int PrimitiveTallies::*instanceType,
- const SkIRect& drawBounds) const {
+void GrCCFiller::drawPrimitives(GrOpFlushState* flushState, const GrPipeline& pipeline,
+ BatchID batchID, GrCCCoverageProcessor::PrimitiveType primitiveType,
+ int PrimitiveTallies::*instanceType,
+ const SkIRect& drawBounds) const {
SkASSERT(pipeline.isScissorEnabled());
// Don't call reset(), as that also resets the reserve count.
@@ -573,9 +510,9 @@
GrCCCoverageProcessor proc(flushState->resourceProvider(), primitiveType);
SkASSERT(batchID > 0);
- SkASSERT(batchID < fCoverageCountBatches.count());
- const CoverageCountBatch& previousBatch = fCoverageCountBatches[batchID - 1];
- const CoverageCountBatch& batch = fCoverageCountBatches[batchID];
+ SkASSERT(batchID < fBatches.count());
+ const Batch& previousBatch = fBatches[batchID - 1];
+ const Batch& batch = fBatches[batchID];
SkDEBUGCODE(int totalInstanceCount = 0);
if (int instanceCount = batch.fEndNonScissorIndices.*instanceType -
diff --git a/src/gpu/ccpr/GrCCFiller.h b/src/gpu/ccpr/GrCCFiller.h
new file mode 100644
index 0000000..40dc657
--- /dev/null
+++ b/src/gpu/ccpr/GrCCFiller.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright 2017 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrCCPathParser_DEFINED
+#define GrCCPathParser_DEFINED
+
+#include "GrMesh.h"
+#include "SkPath.h"
+#include "SkPathPriv.h"
+#include "SkRect.h"
+#include "SkRefCnt.h"
+#include "GrTessellator.h"
+#include "ccpr/GrCCCoverageProcessor.h"
+#include "ccpr/GrCCFillGeometry.h"
+#include "ops/GrDrawOp.h"
+
+class GrOnFlushResourceProvider;
+class SkMatrix;
+class SkPath;
+
+/**
+ * This class parses SkPaths into CCPR primitives in GPU buffers, then issues calls to draw their
+ * coverage counts.
+ */
+class GrCCFiller {
+public:
+ struct PathStats {
+ int fMaxPointsPerPath = 0;
+ int fNumTotalSkPoints = 0;
+ int fNumTotalSkVerbs = 0;
+ int fNumTotalConicWeights = 0;
+
+ void statPath(const SkPath&);
+ };
+
+ GrCCFiller(int numPaths, const PathStats&);
+
+ // Parses a device-space SkPath into the current batch, using the SkPath's original verbs and
+ // 'deviceSpacePts'. Accepts an optional post-device-space translate for placement in an atlas.
+ void parseDeviceSpaceFill(const SkPath&, const SkPoint* deviceSpacePts, GrScissorTest,
+ const SkIRect& clippedDevIBounds, const SkIVector& devToAtlasOffset);
+
+ using BatchID = int;
+
+ // Compiles the outstanding parsed paths into a batch, and returns an ID that can be used to
+ // draw their fills in the future.
+ BatchID closeCurrentBatch();
+
+ // Builds internal GPU buffers and prepares for calls to drawFills(). Caller must close the
+ // current batch before calling this method, and cannot parse new paths afer.
+ bool prepareToDraw(GrOnFlushResourceProvider*);
+
+ // Called after prepareToDraw(). Draws the given batch of path fills.
+ void drawFills(GrOpFlushState*, BatchID, const SkIRect& drawBounds) const;
+
+private:
+ static constexpr int kNumScissorModes = 2;
+ using PrimitiveTallies = GrCCFillGeometry::PrimitiveTallies;
+
+ // Every kBeginPath verb has a corresponding PathInfo entry.
+ class PathInfo {
+ public:
+ PathInfo(GrScissorTest scissorTest, const SkIVector& devToAtlasOffset)
+ : fScissorTest(scissorTest), fDevToAtlasOffset(devToAtlasOffset) {}
+
+ GrScissorTest scissorTest() const { return fScissorTest; }
+ const SkIVector& devToAtlasOffset() const { return fDevToAtlasOffset; }
+
+ // An empty tessellation fan is also valid; we use negative count to denote not tessellated.
+ bool hasFanTessellation() const { return fFanTessellationCount >= 0; }
+ int fanTessellationCount() const {
+ SkASSERT(this->hasFanTessellation());
+ return fFanTessellationCount;
+ }
+ const GrTessellator::WindingVertex* fanTessellation() const {
+ SkASSERT(this->hasFanTessellation());
+ return fFanTessellation.get();
+ }
+ void tessellateFan(const GrCCFillGeometry&, int verbsIdx, int ptsIdx,
+ const SkIRect& clippedDevIBounds, PrimitiveTallies* newTriangleCounts);
+
+ private:
+ GrScissorTest fScissorTest;
+ SkIVector fDevToAtlasOffset; // Translation from device space to location in atlas.
+ int fFanTessellationCount = -1;
+ std::unique_ptr<const GrTessellator::WindingVertex[]> fFanTessellation;
+ };
+
+ // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
+ // Batch in the list.
+ struct Batch {
+ PrimitiveTallies fEndNonScissorIndices;
+ int fEndScissorSubBatchIdx;
+ PrimitiveTallies fTotalPrimitiveCounts;
+ };
+
+ // Defines a sub-batch that will be drawn with the given scissor rect. Start indices are deduced
+ // by looking at the previous ScissorSubBatch in the list.
+ struct ScissorSubBatch {
+ PrimitiveTallies fEndPrimitiveIndices;
+ SkIRect fScissor;
+ };
+
+ void drawPrimitives(GrOpFlushState*, const GrPipeline&, BatchID,
+ GrCCCoverageProcessor::PrimitiveType, int PrimitiveTallies::*instanceType,
+ const SkIRect& drawBounds) const;
+
+ GrCCFillGeometry fGeometry;
+ SkSTArray<32, PathInfo, true> fPathInfos;
+ SkSTArray<32, Batch, true> fBatches;
+ SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
+ PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
+ int fMaxMeshesPerDraw = 0;
+
+ sk_sp<GrBuffer> fInstanceBuffer;
+ PrimitiveTallies fBaseInstances[kNumScissorModes];
+ mutable SkSTArray<32, GrMesh> fMeshesScratchBuffer;
+ mutable SkSTArray<32, SkIRect> fScissorRectScratchBuffer;
+};
+
+inline void GrCCFiller::PathStats::statPath(const SkPath& path) {
+ fMaxPointsPerPath = SkTMax(fMaxPointsPerPath, path.countPoints());
+ fNumTotalSkPoints += path.countPoints();
+ fNumTotalSkVerbs += path.countVerbs();
+ fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
+}
+
+#endif
diff --git a/src/gpu/ccpr/GrCCPathCache.cpp b/src/gpu/ccpr/GrCCPathCache.cpp
index 7a00d35..01781b0 100644
--- a/src/gpu/ccpr/GrCCPathCache.cpp
+++ b/src/gpu/ccpr/GrCCPathCache.cpp
@@ -9,7 +9,6 @@
#include "GrShape.h"
#include "SkNx.h"
-#include "ccpr/GrCCPathParser.h"
// The maximum number of cache entries we allow in our own cache.
static constexpr int kMaxCacheCount = 1 << 16;
diff --git a/src/gpu/ccpr/GrCCPathParser.h b/src/gpu/ccpr/GrCCPathParser.h
deleted file mode 100644
index 9ec2a3c..0000000
--- a/src/gpu/ccpr/GrCCPathParser.h
+++ /dev/null
@@ -1,166 +0,0 @@
-/*
- * Copyright 2017 Google Inc.
- *
- * Use of this source code is governed by a BSD-style license that can be
- * found in the LICENSE file.
- */
-
-#ifndef GrCCPathParser_DEFINED
-#define GrCCPathParser_DEFINED
-
-#include "GrMesh.h"
-#include "SkPath.h"
-#include "SkPathPriv.h"
-#include "SkRect.h"
-#include "SkRefCnt.h"
-#include "GrTessellator.h"
-#include "ccpr/GrCCCoverageProcessor.h"
-#include "ccpr/GrCCGeometry.h"
-#include "ops/GrDrawOp.h"
-
-class GrOnFlushResourceProvider;
-class SkMatrix;
-class SkPath;
-
-/**
- * This class parses SkPaths into CCPR primitives in GPU buffers, then issues calls to draw their
- * coverage counts.
- */
-class GrCCPathParser {
-public:
- struct PathStats {
- int fMaxPointsPerPath = 0;
- int fNumTotalSkPoints = 0;
- int fNumTotalSkVerbs = 0;
- int fNumTotalConicWeights = 0;
-
- void statPath(const SkPath&);
- };
-
- GrCCPathParser(int numPaths, const PathStats&);
-
- ~GrCCPathParser() {
- // Enforce the contract that the client always calls saveParsedPath or discardParsedPath.
- SkASSERT(!fParsingPath);
- }
-
- using CoverageCountBatchID = int;
-
- // Parses an SkPath into a temporary staging area. The path will not be included in the current
- // batch until there is a matching call to saveParsedPath. The user must complement this with a
- // following call to either saveParsedPath or discardParsedPath.
- //
- // Returns two tight bounding boxes: device space and "45 degree" (| 1 -1 | * devCoords) space.
- // | 1 1 |
- void parsePath(const SkMatrix&, const SkPath&, SkRect* devBounds, SkRect* devBounds45);
-
- // Parses a device-space SkPath into a temporary staging area. The path will not be included in
- // the current batch until there is a matching call to saveParsedPath. The user must complement
- // this with a following call to either saveParsedPath or discardParsedPath.
- void parseDeviceSpacePath(const SkPath&);
-
- // Commits the currently-parsed path from staging to the current batch, and specifies whether
- // the mask should be rendered with a scissor in effect. Accepts an optional post-device-space
- // translate for placement in an atlas.
- void saveParsedPath(GrScissorTest, const SkIRect& clippedDevIBounds,
- const SkIVector& devToAtlasOffset);
- void discardParsedPath();
-
- // Compiles the outstanding saved paths into a batch, and returns an ID that can be used to draw
- // their coverage counts in the future.
- CoverageCountBatchID closeCurrentBatch();
-
- // Builds internal GPU buffers and prepares for calls to drawCoverageCount. Caller must close
- // the current batch before calling this method, and cannot parse new paths afer.
- bool finalize(GrOnFlushResourceProvider*);
-
- // Called after finalize. Draws the given batch of parsed paths.
- void drawCoverageCount(GrOpFlushState*, CoverageCountBatchID, const SkIRect& drawBounds) const;
-
-private:
- static constexpr int kNumScissorModes = 2;
- using PrimitiveTallies = GrCCGeometry::PrimitiveTallies;
-
- // Every kBeginPath verb has a corresponding PathInfo entry.
- class PathInfo {
- public:
- PathInfo(GrScissorTest scissorTest, const SkIVector& devToAtlasOffset)
- : fScissorTest(scissorTest), fDevToAtlasOffset(devToAtlasOffset) {}
-
- GrScissorTest scissorTest() const { return fScissorTest; }
- const SkIVector& devToAtlasOffset() const { return fDevToAtlasOffset; }
-
- // An empty tessellation fan is also valid; we use negative count to denote not tessellated.
- bool hasFanTessellation() const { return fFanTessellationCount >= 0; }
- int fanTessellationCount() const {
- SkASSERT(this->hasFanTessellation());
- return fFanTessellationCount;
- }
- const GrTessellator::WindingVertex* fanTessellation() const {
- SkASSERT(this->hasFanTessellation());
- return fFanTessellation.get();
- }
-
- void adoptFanTessellation(const GrTessellator::WindingVertex* vertices, int count) {
- SkASSERT(count >= 0);
- fFanTessellation.reset(vertices);
- fFanTessellationCount = count;
- }
-
- private:
- GrScissorTest fScissorTest;
- SkIVector fDevToAtlasOffset; // Translation from device space to location in atlas.
- int fFanTessellationCount = -1;
- std::unique_ptr<const GrTessellator::WindingVertex[]> fFanTessellation;
- };
-
- // Defines a batch of CCPR primitives. Start indices are deduced by looking at the previous
- // CoverageCountBatch in the list.
- struct CoverageCountBatch {
- PrimitiveTallies fEndNonScissorIndices;
- int fEndScissorSubBatchIdx;
- PrimitiveTallies fTotalPrimitiveCounts;
- };
-
- // Defines a sub-batch from CoverageCountBatch that will be drawn with the given scissor rect.
- // Start indices are deduced by looking at the previous ScissorSubBatch in the list.
- struct ScissorSubBatch {
- PrimitiveTallies fEndPrimitiveIndices;
- SkIRect fScissor;
- };
-
- void parsePath(const SkPath&, const SkPoint* deviceSpacePts);
- void endContourIfNeeded(bool insideContour);
-
- void drawPrimitives(GrOpFlushState*, const GrPipeline&, CoverageCountBatchID,
- GrCCCoverageProcessor::PrimitiveType, int PrimitiveTallies::*instanceType,
- const SkIRect& drawBounds) const;
-
- // Staging area for the path being parsed.
- SkDEBUGCODE(int fParsingPath = false);
- const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
- int fCurrPathPointsIdx;
- int fCurrPathVerbsIdx;
- PrimitiveTallies fCurrPathPrimitiveCounts;
-
- GrCCGeometry fGeometry;
- SkSTArray<32, PathInfo, true> fPathsInfo;
- SkSTArray<32, CoverageCountBatch, true> fCoverageCountBatches;
- SkSTArray<32, ScissorSubBatch, true> fScissorSubBatches;
- PrimitiveTallies fTotalPrimitiveCounts[kNumScissorModes];
- int fMaxMeshesPerDraw = 0;
-
- sk_sp<GrBuffer> fInstanceBuffer;
- PrimitiveTallies fBaseInstances[kNumScissorModes];
- mutable SkSTArray<32, GrMesh> fMeshesScratchBuffer;
- mutable SkSTArray<32, SkIRect> fScissorRectScratchBuffer;
-};
-
-inline void GrCCPathParser::PathStats::statPath(const SkPath& path) {
- fMaxPointsPerPath = SkTMax(fMaxPointsPerPath, path.countPoints());
- fNumTotalSkPoints += path.countPoints();
- fNumTotalSkVerbs += path.countVerbs();
- fNumTotalConicWeights += SkPathPriv::ConicWeightCnt(path);
-}
-
-#endif
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.cpp b/src/gpu/ccpr/GrCCPerFlushResources.cpp
index 886d679..0ab4e5d 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.cpp
+++ b/src/gpu/ccpr/GrCCPerFlushResources.cpp
@@ -15,7 +15,7 @@
#include "SkMakeUnique.h"
#include "ccpr/GrCCPathCache.h"
-using CoverageCountBatchID = GrCCPathParser::CoverageCountBatchID;
+using FillBatchID = GrCCFiller::BatchID;
using PathInstance = GrCCPathProcessor::Instance;
namespace {
@@ -101,7 +101,7 @@
static std::unique_ptr<GrDrawOp> Make(GrContext* context,
sk_sp<const GrCCPerFlushResources> resources,
- CoverageCountBatchID batchID, const SkISize& drawBounds) {
+ FillBatchID batchID, const SkISize& drawBounds) {
GrOpMemoryPool* pool = context->contextPriv().opMemoryPool();
return pool->allocate<RenderAtlasOp>(std::move(resources), batchID, drawBounds);
@@ -111,20 +111,20 @@
const char* name() const override { return "RenderAtlasOp (CCPR)"; }
void onExecute(GrOpFlushState* flushState) override {
- fResources->pathParser().drawCoverageCount(flushState, fBatchID, fDrawBounds);
+ fResources->filler().drawFills(flushState, fBatchID, fDrawBounds);
}
private:
friend class ::GrOpMemoryPool; // for ctor
- RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, CoverageCountBatchID batchID,
+ RenderAtlasOp(sk_sp<const GrCCPerFlushResources> resources, FillBatchID batchID,
const SkISize& drawBounds)
: AtlasOp(ClassID(), std::move(resources), drawBounds)
, fBatchID(batchID)
, fDrawBounds(SkIRect::MakeWH(drawBounds.width(), drawBounds.height())) {
}
- const CoverageCountBatchID fBatchID;
+ const FillBatchID fBatchID;
const SkIRect fDrawBounds;
};
@@ -138,7 +138,11 @@
GrCCPerFlushResources::GrCCPerFlushResources(GrOnFlushResourceProvider* onFlushRP,
const GrCCPerFlushResourceSpecs& specs)
- : fPathParser(specs.fNumRenderedPaths + specs.fNumClipPaths, specs.fRenderedPathStats)
+ // Overallocate by one point so we can call Sk4f::Store at the final SkPoint in the array.
+ // (See transform_path_pts below.)
+ // FIXME: instead use built-in instructions to write only the first two lanes of an Sk4f.
+ : fLocalDevPtsBuffer(specs.fRenderedPathStats.fMaxPointsPerPath + 1)
+ , fFiller(specs.fNumRenderedPaths + specs.fNumClipPaths, specs.fRenderedPathStats)
, fCopyAtlasStack(kAlpha_8_GrPixelConfig, specs.fCopyAtlasSpecs, onFlushRP->caps())
, fRenderedAtlasStack(kAlpha_half_GrPixelConfig, specs.fRenderedAtlasSpecs,
onFlushRP->caps())
@@ -183,6 +187,56 @@
return &fCopyAtlasStack.current();
}
+static void transform_path_pts(const SkMatrix& m, const SkPath& path,
+ const SkAutoSTArray<32, SkPoint>& outDevPts, SkRect* devBounds,
+ SkRect* devBounds45) {
+ const SkPoint* pts = SkPathPriv::PointData(path);
+ int numPts = path.countPoints();
+ SkASSERT(numPts + 1 <= outDevPts.count());
+ SkASSERT(numPts);
+
+ // m45 transforms path points into "45 degree" device space. A bounding box in this space gives
+ // the circumscribing octagon's diagonals. We could use SK_ScalarRoot2Over2, but an orthonormal
+ // transform is not necessary as long as the shader uses the correct inverse.
+ SkMatrix m45;
+ m45.setSinCos(1, 1);
+ m45.preConcat(m);
+
+ // X,Y,T are two parallel view matrices that accumulate two bounding boxes as they map points:
+ // device-space bounds and "45 degree" device-space bounds (| 1 -1 | * devCoords).
+ // | 1 1 |
+ Sk4f X = Sk4f(m.getScaleX(), m.getSkewY(), m45.getScaleX(), m45.getSkewY());
+ Sk4f Y = Sk4f(m.getSkewX(), m.getScaleY(), m45.getSkewX(), m45.getScaleY());
+ Sk4f T = Sk4f(m.getTranslateX(), m.getTranslateY(), m45.getTranslateX(), m45.getTranslateY());
+
+ // Map the path's points to device space and accumulate bounding boxes.
+ Sk4f devPt = SkNx_fma(Y, Sk4f(pts[0].y()), T);
+ devPt = SkNx_fma(X, Sk4f(pts[0].x()), devPt);
+ Sk4f topLeft = devPt;
+ Sk4f bottomRight = devPt;
+
+ // Store all 4 values [dev.x, dev.y, dev45.x, dev45.y]. We are only interested in the first two,
+ // and will overwrite [dev45.x, dev45.y] with the next point. This is why the dst buffer must
+ // be at least one larger than the number of points.
+ devPt.store(&outDevPts[0]);
+
+ for (int i = 1; i < numPts; ++i) {
+ devPt = SkNx_fma(Y, Sk4f(pts[i].y()), T);
+ devPt = SkNx_fma(X, Sk4f(pts[i].x()), devPt);
+ topLeft = Sk4f::Min(topLeft, devPt);
+ bottomRight = Sk4f::Max(bottomRight, devPt);
+ devPt.store(&outDevPts[i]);
+ }
+
+ SkPoint topLeftPts[2], bottomRightPts[2];
+ topLeft.store(topLeftPts);
+ bottomRight.store(bottomRightPts);
+ devBounds->setLTRB(topLeftPts[0].x(), topLeftPts[0].y(), bottomRightPts[0].x(),
+ bottomRightPts[0].y());
+ devBounds45->setLTRB(topLeftPts[1].x(), topLeftPts[1].y(), bottomRightPts[1].x(),
+ bottomRightPts[1].y());
+}
+
const GrCCAtlas* GrCCPerFlushResources::renderPathInAtlas(const SkIRect& clipIBounds,
const SkMatrix& m, const SkPath& path,
SkRect* devBounds, SkRect* devBounds45,
@@ -191,13 +245,24 @@
SkASSERT(this->isMapped());
SkASSERT(fNextPathInstanceIdx < fEndPathInstance);
- fPathParser.parsePath(m, path, devBounds, devBounds45);
+ if (path.isEmpty()) {
+ SkDEBUGCODE(--fEndPathInstance);
+ return nullptr;
+ }
+
+ transform_path_pts(m, path, fLocalDevPtsBuffer, devBounds, devBounds45);
devBounds->roundOut(devIBounds);
- if (!this->placeParsedPathInAtlas(clipIBounds, *devIBounds, devToAtlasOffset)) {
+ GrScissorTest scissorTest;
+ SkIRect clippedPathIBounds;
+ if (!this->placeRenderedPathInAtlas(clipIBounds, *devIBounds, &scissorTest, &clippedPathIBounds,
+ devToAtlasOffset)) {
SkDEBUGCODE(--fEndPathInstance);
return nullptr; // Path was degenerate or clipped away.
}
+
+ fFiller.parseDeviceSpaceFill(path, fLocalDevPtsBuffer.begin(), scissorTest, clippedPathIBounds,
+ *devToAtlasOffset);
return &fRenderedAtlasStack.current();
}
@@ -205,37 +270,45 @@
const SkIRect& clipIBounds, const SkPath& devPath, const SkIRect& devPathIBounds,
SkIVector* devToAtlasOffset) {
SkASSERT(this->isMapped());
- fPathParser.parseDeviceSpacePath(devPath);
- if (!this->placeParsedPathInAtlas(clipIBounds, devPathIBounds, devToAtlasOffset)) {
+
+ if (devPath.isEmpty()) {
return nullptr;
}
+
+ GrScissorTest scissorTest;
+ SkIRect clippedPathIBounds;
+ if (!this->placeRenderedPathInAtlas(clipIBounds, devPathIBounds, &scissorTest,
+ &clippedPathIBounds, devToAtlasOffset)) {
+ return nullptr;
+ }
+
+ fFiller.parseDeviceSpaceFill(devPath, SkPathPriv::PointData(devPath), scissorTest,
+ clippedPathIBounds, *devToAtlasOffset);
return &fRenderedAtlasStack.current();
}
-bool GrCCPerFlushResources::placeParsedPathInAtlas(const SkIRect& clipIBounds,
- const SkIRect& pathIBounds,
- SkIVector* devToAtlasOffset) {
- GrScissorTest scissorTest;
- SkIRect clippedPathIBounds;
+bool GrCCPerFlushResources::placeRenderedPathInAtlas(const SkIRect& clipIBounds,
+ const SkIRect& pathIBounds,
+ GrScissorTest* scissorTest,
+ SkIRect* clippedPathIBounds,
+ SkIVector* devToAtlasOffset) {
if (clipIBounds.contains(pathIBounds)) {
- clippedPathIBounds = pathIBounds;
- scissorTest = GrScissorTest::kDisabled;
- } else if (clippedPathIBounds.intersect(clipIBounds, pathIBounds)) {
- scissorTest = GrScissorTest::kEnabled;
+ *clippedPathIBounds = pathIBounds;
+ *scissorTest = GrScissorTest::kDisabled;
+ } else if (clippedPathIBounds->intersect(clipIBounds, pathIBounds)) {
+ *scissorTest = GrScissorTest::kEnabled;
} else {
- fPathParser.discardParsedPath();
return false;
}
if (GrCCAtlas* retiredAtlas =
- fRenderedAtlasStack.addRect(clippedPathIBounds, devToAtlasOffset)) {
+ fRenderedAtlasStack.addRect(*clippedPathIBounds, devToAtlasOffset)) {
// We did not fit in the previous coverage count atlas and it was retired. Close the path
// parser's current batch (which does not yet include the path we just parsed). We will
// render this batch into the retired atlas during finalize().
- CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
+ FillBatchID batchID = fFiller.closeCurrentBatch();
retiredAtlas->setUserBatchID(batchID);
}
- fPathParser.saveParsedPath(scissorTest, clippedPathIBounds, *devToAtlasOffset);
return true;
}
@@ -253,14 +326,13 @@
fCopyAtlasStack.current().setUserBatchID(fNextCopyInstanceIdx);
}
if (!fRenderedAtlasStack.empty()) {
- CoverageCountBatchID batchID = fPathParser.closeCurrentBatch();
+ FillBatchID batchID = fFiller.closeCurrentBatch();
fRenderedAtlasStack.current().setUserBatchID(batchID);
}
// Build the GPU buffers to render path coverage counts. (This must not happen until after the
// final call to fPathParser.closeCurrentBatch().)
- if (!fPathParser.finalize(onFlushRP)) {
- SkDebugf("WARNING: failed to allocate GPU buffers for CCPR. No paths will be drawn.\n");
+ if (!fFiller.prepareToDraw(onFlushRP)) {
return false;
}
@@ -320,5 +392,5 @@
fRenderedPathStats.fNumTotalSkPoints += fCopyPathStats.fNumTotalSkPoints;
fRenderedPathStats.fNumTotalSkVerbs += fCopyPathStats.fNumTotalSkVerbs;
fRenderedPathStats.fNumTotalConicWeights += fCopyPathStats.fNumTotalConicWeights;
- fCopyPathStats = GrCCPathParser::PathStats();
+ fCopyPathStats = GrCCFiller::PathStats();
}
diff --git a/src/gpu/ccpr/GrCCPerFlushResources.h b/src/gpu/ccpr/GrCCPerFlushResources.h
index cdc89f8..3fa392e 100644
--- a/src/gpu/ccpr/GrCCPerFlushResources.h
+++ b/src/gpu/ccpr/GrCCPerFlushResources.h
@@ -10,7 +10,7 @@
#include "GrNonAtomicRef.h"
#include "ccpr/GrCCAtlas.h"
-#include "ccpr/GrCCPathParser.h"
+#include "ccpr/GrCCFiller.h"
#include "ccpr/GrCCPathProcessor.h"
class GrCCPathCacheEntry;
@@ -24,12 +24,12 @@
int fNumCachedPaths = 0;
int fNumCopiedPaths = 0;
- GrCCPathParser::PathStats fCopyPathStats;
+ GrCCFiller::PathStats fCopyPathStats;
GrCCAtlas::Specs fCopyAtlasSpecs;
int fNumRenderedPaths = 0;
int fNumClipPaths = 0;
- GrCCPathParser::PathStats fRenderedPathStats;
+ GrCCFiller::PathStats fRenderedPathStats;
GrCCAtlas::Specs fRenderedAtlasSpecs;
bool isEmpty() const {
@@ -85,7 +85,7 @@
SkTArray<sk_sp<GrRenderTargetContext>>* out);
// Accessors used by draw calls, once the resources have been finalized.
- const GrCCPathParser& pathParser() const { SkASSERT(!this->isMapped()); return fPathParser; }
+ const GrCCFiller& filler() const { SkASSERT(!this->isMapped()); return fFiller; }
const GrBuffer* indexBuffer() const { SkASSERT(!this->isMapped()); return fIndexBuffer.get(); }
const GrBuffer* vertexBuffer() const { SkASSERT(!this->isMapped()); return fVertexBuffer.get();}
GrBuffer* instanceBuffer() const { SkASSERT(!this->isMapped()); return fInstanceBuffer.get(); }
@@ -107,10 +107,12 @@
}
private:
- bool placeParsedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
- SkIVector* devToAtlasOffset);
+ bool placeRenderedPathInAtlas(const SkIRect& clipIBounds, const SkIRect& pathIBounds,
+ GrScissorTest*, SkIRect* clippedPathIBounds,
+ SkIVector* devToAtlasOffset);
- GrCCPathParser fPathParser;
+ const SkAutoSTArray<32, SkPoint> fLocalDevPtsBuffer;
+ GrCCFiller fFiller;
GrCCAtlasStack fCopyAtlasStack;
GrCCAtlasStack fRenderedAtlasStack;
diff --git a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
index d68d4c2..f783259 100644
--- a/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
+++ b/src/gpu/ccpr/GrCoverageCountingPathRenderer.cpp
@@ -15,7 +15,6 @@
#include "ccpr/GrCCClipProcessor.h"
#include "ccpr/GrCCDrawPathsOp.h"
#include "ccpr/GrCCPathCache.h"
-#include "ccpr/GrCCPathParser.h"
using PathInstance = GrCCPathProcessor::Instance;