Skip to content

Commit f9aea7f

Browse files
csmartdalton86Skia Commit-Bot
authored andcommitted
Add a tessellation mode that triangulates the inner polygon separately
Wedges fanning out from the center work fine for relatively simple paths, but for paths made up of thousands of verbs, a fan is an inefficient triangulation to give the rasterizer. This CL adds a tessellation mode that draws the inner polygon and standalone cubics separately, and triangulates the inner polygon by recursive subdivision. This reduces the stencil time from 7.4ms -> 3.0ms on desk_ynevsvg.skp. Change-Id: Ie56e760d98e6c69e9a97752fe851726f36a7f574 Reviewed-on: https://skia-review.googlesource.com/c/skia/+/265522 Reviewed-by: Michael Ludwig <michaelludwig@google.com> Commit-Queue: Chris Dalton <csmartdalton@google.com>
1 parent dbc9f64 commit f9aea7f

12 files changed

+556
-221
lines changed

gn/gpu.gni

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -426,10 +426,10 @@ skia_gpu_sources = [
426426
"$_src/gpu/tessellate/GrGpuTessellationPathRenderer.h",
427427
"$_src/gpu/tessellate/GrPathParser.cpp",
428428
"$_src/gpu/tessellate/GrPathParser.h",
429+
"$_src/gpu/tessellate/GrStencilPathShader.cpp",
430+
"$_src/gpu/tessellate/GrStencilPathShader.h",
429431
"$_src/gpu/tessellate/GrTessellatePathOp.cpp",
430432
"$_src/gpu/tessellate/GrTessellatePathOp.h",
431-
"$_src/gpu/tessellate/GrTessellateWedgeShader.cpp",
432-
"$_src/gpu/tessellate/GrTessellateWedgeShader.h",
433433

434434
# text
435435
"$_src/gpu/text/GrAtlasManager.cpp",

samplecode/SampleTessellatedWedge.cpp

Lines changed: 19 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -25,9 +25,20 @@
2525
class TessellatedWedgeView : public Sample {
2626
public:
2727
TessellatedWedgeView() {
28+
#if 0
29+
fPath.moveTo(1, 0);
30+
int numSides = 32 * 3;
31+
for (int i = 1; i < numSides; ++i) {
32+
float theta = 2*3.1415926535897932384626433832785 * i / numSides;
33+
fPath.lineTo(std::cos(theta), std::sin(theta));
34+
}
35+
fPath.transform(SkMatrix::MakeScale(200, 200));
36+
fPath.transform(SkMatrix::MakeTrans(300, 300));
37+
#else
2838
fPath.moveTo(100, 200);
2939
fPath.cubicTo(100, 100, 400, 100, 400, 200);
3040
fPath.lineTo(250, 500);
41+
#endif
3142
}
3243

3344
private:
@@ -87,8 +98,14 @@ void TessellatedWedgeView::onDrawContent(SkCanvas* canvas) {
8798
SkPaint pointsPaint;
8899
pointsPaint.setColor(SK_ColorBLUE);
89100
pointsPaint.setStrokeWidth(8);
90-
canvas->drawPoints(SkCanvas::kPoints_PointMode, fPath.countPoints(),
91-
SkPathPriv::PointData(fPath), pointsPaint);
101+
SkPath devPath = fPath;
102+
devPath.transform(canvas->getTotalMatrix());
103+
{
104+
SkAutoCanvasRestore acr(canvas, true);
105+
canvas->setMatrix(SkMatrix::I());
106+
canvas->drawPoints(SkCanvas::kPoints_PointMode, devPath.countPoints(),
107+
SkPathPriv::PointData(devPath), pointsPaint);
108+
}
92109

93110
fLastViewMatrix = canvas->getTotalMatrix();
94111
}

src/gpu/GrProcessor.h

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -139,7 +139,6 @@ class GrProcessor {
139139
kGrSRGBEffect_ClassID,
140140
kGrSampleMaskProcessor_ClassID,
141141
kGrSweepGradientLayout_ClassID,
142-
kGrTessellateWedgeShader_ClassID,
143142
kGrTextureEffect_ClassID,
144143
kGrTextureGradientColorizer_ClassID,
145144
kGrTiledGradientEffect_ClassID,
@@ -162,6 +161,9 @@ class GrProcessor {
162161
kSwizzleFragmentProcessor_ClassID,
163162
kTessellationTestTriShader_ClassID,
164163
kTessellationTestRectShader_ClassID,
164+
kTessellate_GrStencilCubicShader_ClassID,
165+
kTessellate_GrStencilTriangleShader_ClassID,
166+
kTessellate_GrStencilWedgeShader_ClassID,
165167
kTestFP_ClassID,
166168
kTestRectOp_ClassID,
167169
kFlatNormalsFP_ClassID,

src/gpu/tessellate/GrGpuTessellationPathRenderer.cpp

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -41,9 +41,9 @@ bool GrGpuTessellationPathRenderer::onDrawPath(const DrawPathArgs& args) {
4141
SkPath path;
4242
args.fShape->asPath(&path);
4343

44-
GrOpMemoryPool* pool = args.fContext->priv().opMemoryPool();
45-
args.fRenderTargetContext->addDrawOp(*args.fClip, pool->allocate<GrTessellatePathOp>(
46-
*args.fViewMatrix, path, std::move(args.fPaint), args.fAAType));
44+
auto op = args.fContext->priv().opMemoryPool()->allocate<GrTessellatePathOp>(
45+
*args.fViewMatrix, path, std::move(args.fPaint), args.fAAType);
46+
args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
4747

4848
return true;
4949
}
@@ -54,7 +54,7 @@ void GrGpuTessellationPathRenderer::onStencilPath(const StencilPathArgs& args) {
5454

5555
GrAAType aaType = (GrAA::kYes == args.fDoStencilMSAA) ? GrAAType::kMSAA : GrAAType::kNone;
5656

57-
GrOpMemoryPool* pool = args.fContext->priv().opMemoryPool();
58-
args.fRenderTargetContext->addDrawOp(*args.fClip, pool->allocate<GrTessellatePathOp>(
59-
*args.fViewMatrix, path, GrPaint(), aaType, GrTessellatePathOp::Flags::kStencilOnly));
57+
auto op = args.fContext->priv().opMemoryPool()->allocate<GrTessellatePathOp>(
58+
*args.fViewMatrix, path, GrPaint(), aaType, GrTessellatePathOp::Flags::kStencilOnly);
59+
args.fRenderTargetContext->addDrawOp(*args.fClip, std::move(op));
6060
}

src/gpu/tessellate/GrPathParser.cpp

Lines changed: 109 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,10 +7,9 @@
77

88
#include "src/gpu/tessellate/GrPathParser.h"
99

10+
#include "include/private/SkTArray.h"
1011
#include "src/core/SkPathPriv.h"
1112

12-
namespace GrPathParser {
13-
1413
static SkPoint lerp(const SkPoint& a, const SkPoint& b, float T) {
1514
SkASSERT(1 != T); // The below does not guarantee lerp(a, b, 1) === b.
1615
return (b - a) * T + a;
@@ -25,7 +24,7 @@ static SkPoint write_line_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoi
2524
}
2625

2726
static SkPoint write_quadratic_as_cubic(SkPoint* data, const SkPoint& p0, const SkPoint& p1,
28-
const SkPoint& p2) {
27+
const SkPoint& p2) {
2928
data[0] = p0;
3029
data[1] = lerp(p0, p1, 2/3.f);
3130
data[2] = lerp(p1, p2, 1/3.f);
@@ -77,7 +76,7 @@ class MidpointContourParser : public SkTPathContourParser<MidpointContourParser>
7776
int fMidpointWeight;
7877
};
7978

80-
int EmitCenterWedges(const SkPath& path, SkPoint* patchData) {
79+
int GrPathParser::EmitCenterWedgePatches(const SkPath& path, SkPoint* patchData) {
8180
int vertexCount = 0;
8281
MidpointContourParser parser(path);
8382
while (parser.parseNextContour()) {
@@ -122,8 +121,112 @@ int EmitCenterWedges(const SkPath& path, SkPoint* patchData) {
122121
}
123122
}
124123

125-
SkASSERT(vertexCount <= MaxPossibleWedgeVertices(path));
124+
SkASSERT(vertexCount <= MaxWedgeVertices(path));
125+
return vertexCount;
126+
}
127+
128+
// Triangulates the polygon defined by the points in the range [first..last] inclusive.
129+
// Called by InnerPolygonContourParser::emitInnerPolygon() (and recursively).
130+
static int emit_subpolygon(const SkPoint* points, int first, int last, SkPoint* vertexData) {
131+
if (last - first < 2) {
132+
return 0;
133+
}
134+
135+
// For sub-polygons we subdivide the points in two and connect the endpoints.
136+
int mid = (first + last) / 2;
137+
vertexData[0] = points[first];
138+
vertexData[1] = points[mid];
139+
vertexData[2] = points[last];
140+
141+
// Emit the sub-polygon at each outer-edge of our new triangle.
142+
int vertexCount = 3;
143+
vertexCount += emit_subpolygon(points, first, mid, vertexData + vertexCount);
144+
vertexCount += emit_subpolygon(points, mid, last, vertexData + vertexCount);
145+
return vertexCount;
146+
}
147+
148+
class InnerPolygonContourParser : public SkTPathContourParser<InnerPolygonContourParser> {
149+
public:
150+
InnerPolygonContourParser(const SkPath& path) : SkTPathContourParser(path) {
151+
fPolyPoints.reserve(GrPathParser::MaxInnerPolygonVertices(path));
152+
}
153+
154+
int emitInnerPolygon(SkPoint* vertexData) {
155+
if (fPolyPoints.size() < 3) {
156+
return 0;
157+
}
158+
159+
// For the first triangle in the polygon, subdivide our points into thirds.
160+
int i1 = fPolyPoints.size() / 3;
161+
int i2 = (2 * fPolyPoints.size()) / 3;
162+
vertexData[0] = fPolyPoints[0];
163+
vertexData[1] = fPolyPoints[i1];
164+
vertexData[2] = fPolyPoints[i2];
165+
166+
// Emit the sub-polygons at all three edges of our first triangle.
167+
int vertexCount = 3;
168+
vertexCount += emit_subpolygon(fPolyPoints.begin(), 0, i1, vertexData + vertexCount);
169+
vertexCount += emit_subpolygon(fPolyPoints.begin(), i1, i2, vertexData + vertexCount);
170+
int i3 = fPolyPoints.size();
171+
fPolyPoints.push_back(fPolyPoints.front());
172+
vertexCount += emit_subpolygon(fPolyPoints.begin(), i2, i3, vertexData + vertexCount);
173+
fPolyPoints.pop_back();
174+
175+
return vertexCount;
176+
}
177+
178+
int numCurves() const { return fNumCurves; }
179+
180+
private:
181+
friend class SkTPathContourParser<InnerPolygonContourParser>;
182+
183+
void resetGeometry(const SkPoint& startPoint) {
184+
fPolyPoints.pop_back_n(fPolyPoints.count());
185+
fPolyPoints.push_back(startPoint);
186+
fNumCurves = 0;
187+
}
188+
189+
void geometryTo(SkPathVerb verb, const SkPoint& endpoint) {
190+
fPolyPoints.push_back(endpoint);
191+
if (SkPathVerb::kLine != verb) {
192+
++fNumCurves;
193+
}
194+
}
195+
196+
SkSTArray<128, SkPoint> fPolyPoints;
197+
int fNumCurves;
198+
};
199+
200+
int GrPathParser::EmitInnerPolygonTriangles(const SkPath& path, SkPoint* vertexData,
201+
int* numCurves) {
202+
*numCurves = 0;
203+
int vertexCount = 0;
204+
InnerPolygonContourParser parser(path);
205+
while (parser.parseNextContour()) {
206+
vertexCount += parser.emitInnerPolygon(vertexData + vertexCount);
207+
*numCurves += parser.numCurves();
208+
}
209+
210+
SkASSERT(vertexCount <= MaxInnerPolygonVertices(path));
126211
return vertexCount;
127212
}
128213

129-
} // namespace
214+
int GrPathParser::EmitCubicInstances(const SkPath& path, SkPoint* vertexData) {
215+
int instanceCount = 0;
216+
SkPath::Iter iter(path, false);
217+
SkPath::Verb verb;
218+
SkPoint pts[4];
219+
while ((verb = iter.next(pts)) != SkPath::kDone_Verb) {
220+
if (SkPath::kQuad_Verb == verb) {
221+
write_quadratic_as_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2]);
222+
++instanceCount;
223+
continue;
224+
}
225+
if (SkPath::kCubic_Verb == verb) {
226+
write_cubic(vertexData + (instanceCount * 4), pts[0], pts[1], pts[2], pts[3]);
227+
++instanceCount;
228+
continue;
229+
}
230+
}
231+
return instanceCount;
232+
}

src/gpu/tessellate/GrPathParser.h

Lines changed: 26 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,9 @@
1212

1313
namespace GrPathParser {
1414

15-
// Returns the maximum possible number of vertices that can be written by EmitCenterWedges() for the
16-
// given path.
17-
inline int MaxPossibleWedgeVertices(const SkPath& path) {
15+
// Returns the maximum number of vertices that can be written by EmitCenterWedges() for the given
16+
// path.
17+
inline int MaxWedgeVertices(const SkPath& path) {
1818
// No initial moveTo, one wedge per verb, plus an implicit close at the end.
1919
// Each wedge has 5 vertices.
2020
return (path.countVerbs() + 1) * 5;
@@ -29,8 +29,29 @@ inline int MaxPossibleWedgeVertices(const SkPath& path) {
2929
//
3030
// Returns the number of vertices written to the array.
3131
//
32-
// NOTE: The incoming patchData must have allocated at least MaxWedgeVertices() vertices.
33-
int EmitCenterWedges(const SkPath&, SkPoint* patchData);
32+
// The incoming patchData array must have at least MaxWedgeVertices() elements.
33+
int EmitCenterWedgePatches(const SkPath&, SkPoint* patchData);
34+
35+
// Returns the maximum number of vertices required to triangulate the given path's inner polygon(s).
36+
inline int MaxInnerPolygonVertices(const SkPath& path) {
37+
// No initial moveTo, plus an implicit close at the end; n-2 trianles fill an n-gon.
38+
// Each triangle has 3 vertices.
39+
return (path.countVerbs() - 1) * 3;
40+
}
41+
42+
// Triangulates the path's inner polygon(s) and writes the result to "vertexData". The inner
43+
// polygons connect the endpoints of each verb. (i.e., they are the path that would result from
44+
// collapsing all curves to single lines.)
45+
//
46+
// This method works by recursively subdividing the path rather than emitting a linear triangle fan
47+
// or strip. This can reduce the load on the rasterizer by a great deal on complex paths.
48+
//
49+
// Returns the number of vertices written to the array.
50+
//
51+
// The incoming vertexData array must have at least MaxInnerPolygonVertices() elements.
52+
int EmitInnerPolygonTriangles(const SkPath&, SkPoint* vertexData, int* numCurves);
53+
54+
int EmitCubicInstances(const SkPath&, SkPoint* vertexData);
3455

3556
} // namespace
3657

0 commit comments

Comments
 (0)