Flutter Engine
The Flutter Engine
TessellateStrokesRenderStep.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2022 Google LLC
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
9
10#include "include/core/SkM44.h"
11#include "src/base/SkVx.h"
12#include "src/core/SkGeometry.h"
13#include "src/sksl/SkSLString.h"
14
21
25
26
27namespace skgpu::graphite {
28
29namespace {
30
31using namespace skgpu::tess;
32
33// Always use dynamic stroke params and join control points, track the join control point in
34// PatchWriter and replicate line end points (match Ganesh's shader behavior).
35//
36// No explicit curve type on platforms that support infinity.
37// No color or wide color attribs, since it might always be part of the PaintParams
38// or we'll add a color-only fast path to RenderStep later.
39static constexpr PatchAttribs kAttribs = PatchAttribs::kJoinControlPoint |
40 PatchAttribs::kStrokeParams |
41 PatchAttribs::kPaintDepth |
42 PatchAttribs::kSsboIndex;
43static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType;
52
53// The order of the attribute declarations must match the order used by
54// PatchWriter::emitPatchAttribs, i.e.:
55// join << fanPoint << stroke << color << depth << curveType << ssboIndices
56static constexpr Attribute kBaseAttributes[] = {
63
64static constexpr Attribute kAttributesWithCurveType[] = {
72
73static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType,
74 kBaseAttributes};
75
76} // namespace
77
79 : RenderStep("TessellateStrokeRenderStep",
80 "",
81 Flags::kRequiresMSAA | Flags::kPerformsShading,
82 /*uniforms=*/{{"affineMatrix", SkSLType::kFloat4},
83 {"translate", SkSLType::kFloat2},
84 {"maxScale", SkSLType::kFloat}},
87 /*vertexAttrs=*/ {},
88 /*instanceAttrs=*/kAttributes[infinitySupport])
89 , fInfinitySupport(infinitySupport) {}
90
92
94 // TODO: Assumes vertex ID support for now, max edges must equal
95 // skgpu::tess::FixedCountStrokes::kMaxEdges -> (2^14 - 1) -> 16383
97 R"(
98 float edgeID = float(sk_VertexID >> 1);
99 if ((sk_VertexID & 1) != 0) {
100 edgeID = -edgeID;
102 float2x2 affine = float2x2(affineMatrix.xy, affineMatrix.zw);
103 float4 devAndLocalCoords = tessellate_stroked_curve(
104 edgeID, 16383, affine, translate, maxScale, p01, p23, prevPoint,
105 stroke, %s);
106 float4 devPosition = float4(devAndLocalCoords.xy, depth, 1.0);
107 stepLocalCoords = devAndLocalCoords.zw;
108 )",
109 fInfinitySupport ? "curve_type_using_inf_support(p23)" : "curveType");
110}
111
113 const DrawParams& params,
114 skvx::ushort2 ssboIndices) const {
115 SkPath path = params.geometry().shape().asPath(); // TODO: Iterate the Shape directly
116
117 int patchReserveCount = FixedCountStrokes::PreallocCount(path.countVerbs());
118 // Stroke tessellation does not use fixed indices or vertex data, and only needs the vertex ID
119 static const BindBufferInfo kNullBinding = {};
120 // TODO: All HW that Graphite will run on should support instancing ith sk_VertexID, but when
121 // we support Vulkan+Swiftshader, we will need the vertex buffer ID fallback unless Swiftshader
122 // has figured out how to support vertex IDs before then.
123 Writer writer{fInfinitySupport ? kAttribs : kAttribsWithCurveType,
124 *dw,
125 kNullBinding,
126 kNullBinding,
127 patchReserveCount};
128 writer.updatePaintDepthAttrib(params.order().depthAsFloat());
129 writer.updateSsboIndexAttrib(ssboIndices);
130
131 // The vector xform approximates how the control points are transformed by the shader to
132 // more accurately compute how many *parametric* segments are needed.
133 // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
134 // absolute value automatically converts that to an identity scale factor for our purposes.
135 writer.setShaderTransform(wangs_formula::VectorXform{params.transform().matrix()},
136 params.transform().maxScaleFactor());
137
138 SkASSERT(params.isStroke());
139 writer.updateStrokeParamsAttrib({params.strokeStyle().halfWidth(),
140 params.strokeStyle().joinLimit()});
141
142 // TODO: If PatchWriter can handle adding caps to its deferred patches, and we can convert
143 // hairlines to use round caps instead of square, then StrokeIterator can be deleted entirely.
144 // Besides being simpler, PatchWriter already has what it needs from the shader matrix and
145 // stroke params, so we don't have to re-extract them here.
146 SkMatrix shaderMatrix = params.transform();
148 stroke.setStrokeStyle(params.strokeStyle().width());
149 stroke.setStrokeParams(params.strokeStyle().cap(),
150 params.strokeStyle().join(),
151 params.strokeStyle().miterLimit());
152 StrokeIterator strokeIter(path, &stroke, &shaderMatrix);
153 while (strokeIter.next()) {
154 using Verb = StrokeIterator::Verb;
155 const SkPoint* p = strokeIter.pts();
156 int numChops;
157
158 // TODO: The cusp detection logic should be moved into PatchWriter and shared between
159 // this and StrokeTessellator.cpp, but that will require updating a lot of SkGeometry to
160 // operate on float2 (skvx) instead of the legacy SkNx or SkPoint.
161 switch (strokeIter.verb()) {
162 case Verb::kContourFinished:
163 writer.writeDeferredStrokePatch();
164 break;
165 case Verb::kCircle:
166 // Round cap or else an empty stroke that is specified to be drawn as a circle.
167 writer.writeCircle(p[0]);
168 [[fallthrough]];
169 case Verb::kMoveWithinContour:
170 // A regular kMove invalidates the previous control point; the stroke iterator
171 // tells us a new value to use.
172 writer.updateJoinControlPointAttrib(p[0]);
173 break;
174 case Verb::kLine:
175 writer.writeLine(p[0], p[1]);
176 break;
177 case Verb::kQuad:
178 if (ConicHasCusp(p)) {
179 // The cusp is always at the midtandent.
181 writer.writeCircle(cusp);
182 // A quad can only have a cusp if it's flat with a 180-degree turnaround.
183 writer.writeLine(p[0], cusp);
184 writer.writeLine(cusp, p[2]);
185 } else {
186 writer.writeQuadratic(p);
187 }
188 break;
189 case Verb::kConic:
190 if (ConicHasCusp(p)) {
191 // The cusp is always at the midtandent.
192 SkConic conic(p, strokeIter.w());
193 SkPoint cusp = conic.evalAt(conic.findMidTangent());
194 writer.writeCircle(cusp);
195 // A conic can only have a cusp if it's flat with a 180-degree turnaround.
196 writer.writeLine(p[0], cusp);
197 writer.writeLine(cusp, p[2]);
198 } else {
199 writer.writeConic(p, strokeIter.w());
200 }
201 break;
202 case Verb::kCubic:
203 SkPoint chops[10];
204 float T[2];
205 bool areCusps;
206 numChops = FindCubicConvex180Chops(p, T, &areCusps);
207 if (numChops == 0) {
208 writer.writeCubic(p);
209 } else if (numChops == 1) {
210 SkChopCubicAt(p, chops, T[0]);
211 if (areCusps) {
212 writer.writeCircle(chops[3]);
213 // In a perfect world, these 3 points would be be equal after chopping
214 // on a cusp.
215 chops[2] = chops[4] = chops[3];
216 }
217 writer.writeCubic(chops);
218 writer.writeCubic(chops + 3);
219 } else {
220 SkASSERT(numChops == 2);
221 SkChopCubicAt(p, chops, T[0], T[1]);
222 if (areCusps) {
223 writer.writeCircle(chops[3]);
224 writer.writeCircle(chops[6]);
225 // Two cusps are only possible if it's a flat line with two 180-degree
226 // turnarounds.
227 writer.writeLine(chops[0], chops[3]);
228 writer.writeLine(chops[3], chops[6]);
229 writer.writeLine(chops[6], chops[9]);
230 } else {
231 writer.writeCubic(chops);
232 writer.writeCubic(chops + 3);
233 writer.writeCubic(chops + 6);
234 }
235 }
236 break;
237 }
238 }
239}
240
242 PipelineDataGatherer* gatherer) const {
243 // TODO: Implement perspective
244 SkASSERT(params.transform().type() < Transform::Type::kPerspective);
245
246 SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());)
247
248 // affineMatrix = float4 (2x2 of transform), translate = float2, maxScale = float
249 // Column-major 2x2 of the transform.
250 SkV4 upper = {params.transform().matrix().rc(0, 0), params.transform().matrix().rc(1, 0),
251 params.transform().matrix().rc(0, 1), params.transform().matrix().rc(1, 1)};
252 gatherer->write(upper);
253
254 gatherer->write(SkPoint{params.transform().matrix().rc(0, 3),
255 params.transform().matrix().rc(1, 3)});
256
257 gatherer->write(params.transform().maxScaleFactor());
258}
259
260} // namespace skgpu::graphite
#define SkASSERT(cond)
Definition: SkAssert.h:116
float SkFindQuadMidTangent(const SkPoint src[3])
Definition: SkGeometry.cpp:231
void SkChopCubicAt(const SkPoint src[4], SkPoint dst[7], SkScalar t)
Definition: SkGeometry.cpp:473
void SkEvalQuadAt(const SkPoint src[3], SkScalar t, SkPoint *pt, SkVector *tangent)
Definition: SkGeometry.cpp:132
SkDEBUGCODE(SK_SPI) SkThreadID SkGetThreadID()
Definition: SkPath.h:59
@ kHairline_InitStyle
Definition: SkStrokeRec.h:25
SkSpan< const Uniform > uniforms() const
Definition: Renderer.h:143
void writeUniformsAndTextures(const DrawParams &, PipelineDataGatherer *) const override
void writeVertices(DrawWriter *, const DrawParams &, skvx::ushort2 ssboIndices) const override
static constexpr int PreallocCount(int totalCombinedPathVerbCnt)
const EmbeddedViewParams * params
unsigned useCenter Optional< SkMatrix > matrix
Definition: SkRecords.h:258
std::string printf(const char *fmt,...) SK_PRINTF_LIKE(1
Definition: SkSLString.cpp:83
DEF_SWITCHES_START aot vmservice shared library Name of the *so containing AOT compiled Dart assets for launching the service isolate vm snapshot The VM snapshot data that will be memory mapped as read only SnapshotAssetPath must be present isolate snapshot The isolate snapshot data that will be memory mapped as read only SnapshotAssetPath must be present cache dir path
Definition: switches.h:57
static const std::map< std::string, VerticesBuilder::AttributeType > kAttributes
static constexpr DepthStencilSettings kDirectDepthGreaterPass
const SkPoint kQuad[4]
bool ConicHasCusp(const SkPoint p[3])
Definition: Tessellation.h:131
int FindCubicConvex180Chops(const SkPoint pts[], float T[2], bool *areCusps)
AI float conic(float tolerance, const SkPoint pts[], float w, const VectorXform &vectorXform=VectorXform())
Definition: WangsFormula.h:287
#define T
Definition: precompiler.cc:65
Definition: SkM44.h:98
Definition: SkVx.h:83