• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
8 #include "src/gpu/graphite/render/TessellateStrokesRenderStep.h"
9 
10 #include "include/core/SkM44.h"
11 #include "include/core/SkMatrix.h"
12 #include "include/core/SkPath.h"
13 #include "include/core/SkStrokeRec.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkDebug.h"
16 #include "include/private/base/SkPoint_impl.h"
17 #include "include/private/base/SkSpan_impl.h"
18 #include "src/base/SkVx.h"
19 #include "src/core/SkGeometry.h"
20 #include "src/core/SkSLTypeShared.h"
21 #include "src/gpu/graphite/Attribute.h"
22 #include "src/gpu/graphite/DrawOrder.h"
23 #include "src/gpu/graphite/DrawParams.h"
24 #include "src/gpu/graphite/DrawTypes.h"
25 #include "src/gpu/graphite/PipelineData.h"
26 #include "src/gpu/graphite/ResourceTypes.h"
27 #include "src/gpu/graphite/geom/Geometry.h"
28 #include "src/gpu/graphite/geom/Shape.h"
29 #include "src/gpu/graphite/geom/Transform.h"
30 #include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
31 #include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h"
32 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
33 #include "src/gpu/tessellate/PatchWriter.h"
34 #include "src/gpu/tessellate/StrokeIterator.h"
35 #include "src/gpu/tessellate/Tessellation.h"
36 #include "src/gpu/tessellate/WangsFormula.h"
37 #include "src/sksl/SkSLString.h"
38 
39 namespace skgpu::graphite {
40 
41 namespace {
42 
43 using namespace skgpu::tess;
44 
45 // Always use dynamic stroke params and join control points, track the join control point in
46 // PatchWriter and replicate line end points (match Ganesh's shader behavior).
47 //
48 // No explicit curve type on platforms that support infinity.
49 // No color or wide color attribs, since it might always be part of the PaintParams
50 // or we'll add a color-only fast path to RenderStep later.
51 static constexpr PatchAttribs kAttribs = PatchAttribs::kJoinControlPoint |
52                                          PatchAttribs::kStrokeParams |
53                                          PatchAttribs::kPaintDepth |
54                                          PatchAttribs::kSsboIndex;
55 static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType;
56 using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountStrokes>,
57                            Required<PatchAttribs::kJoinControlPoint>,
58                            Required<PatchAttribs::kStrokeParams>,
59                            Required<PatchAttribs::kPaintDepth>,
60                            Required<PatchAttribs::kSsboIndex>,
61                            Optional<PatchAttribs::kExplicitCurveType>,
62                            ReplicateLineEndPoints,
63                            TrackJoinControlPoints>;
64 
65 // The order of the attribute declarations must match the order used by
66 // PatchWriter::emitPatchAttribs, i.e.:
67 //     join << fanPoint << stroke << color << depth << curveType << ssboIndices
68 static constexpr Attribute kBaseAttributes[] = {
69         {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
70         {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
71         {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
72         {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
73         {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
74         {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2}};
75 
76 static constexpr Attribute kAttributesWithCurveType[] = {
77         {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
78         {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
79         {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
80         {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
81         {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
82         {"curveType", VertexAttribType::kFloat, SkSLType::kFloat},
83         {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2}};
84 
85 static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType,
86                                                            kBaseAttributes};
87 
88 }  // namespace
89 
TessellateStrokesRenderStep(bool infinitySupport)90 TessellateStrokesRenderStep::TessellateStrokesRenderStep(bool infinitySupport)
91         : RenderStep(RenderStepID::kTessellateStrokes,
92                      Flags::kRequiresMSAA | Flags::kPerformsShading,
93                      /*uniforms=*/{{"affineMatrix", SkSLType::kFloat4},
94                                    {"translate", SkSLType::kFloat2},
95                                    {"maxScale", SkSLType::kFloat}},
96                      PrimitiveType::kTriangleStrip,
97                      kDirectDepthGreaterPass,
98                      /*vertexAttrs=*/  {},
99                      /*instanceAttrs=*/kAttributes[infinitySupport])
100         , fInfinitySupport(infinitySupport) {}
101 
~TessellateStrokesRenderStep()102 TessellateStrokesRenderStep::~TessellateStrokesRenderStep() {}
103 
vertexSkSL() const104 std::string TessellateStrokesRenderStep::vertexSkSL() const {
105     // TODO: Assumes vertex ID support for now, max edges must equal
106     // skgpu::tess::FixedCountStrokes::kMaxEdges -> (2^14 - 1) -> 16383
107     return SkSL::String::printf(
108             "float edgeID = float(sk_VertexID >> 1);\n"
109             "if ((sk_VertexID & 1) != 0) {"
110                 "edgeID = -edgeID;"
111             "}\n"
112             "float2x2 affine = float2x2(affineMatrix.xy, affineMatrix.zw);\n"
113             "float4 devAndLocalCoords = tessellate_stroked_curve("
114                     "edgeID, 16383, affine, translate, maxScale, p01, p23, prevPoint,"
115                     "stroke, %s);\n"
116             "float4 devPosition = float4(devAndLocalCoords.xy, depth, 1.0);\n"
117             "stepLocalCoords = devAndLocalCoords.zw;\n",
118             fInfinitySupport ? "curve_type_using_inf_support(p23)" : "curveType");
119 }
120 
writeVertices(DrawWriter * dw,const DrawParams & params,skvx::uint2 ssboIndices) const121 void TessellateStrokesRenderStep::writeVertices(DrawWriter* dw,
122                                                 const DrawParams& params,
123                                                 skvx::uint2 ssboIndices) const {
124     SkPath path = params.geometry().shape().asPath(); // TODO: Iterate the Shape directly
125 
126     int patchReserveCount = FixedCountStrokes::PreallocCount(path.countVerbs());
127     // Stroke tessellation does not use fixed indices or vertex data, and only needs the vertex ID
128     static const BindBufferInfo kNullBinding = {};
129     // TODO: All HW that Graphite will run on should support instancing ith sk_VertexID, but when
130     // we support Vulkan+Swiftshader, we will need the vertex buffer ID fallback unless Swiftshader
131     // has figured out how to support vertex IDs before then.
132     Writer writer{fInfinitySupport ? kAttribs : kAttribsWithCurveType,
133                   *dw,
134                   kNullBinding,
135                   kNullBinding,
136                   patchReserveCount};
137     writer.updatePaintDepthAttrib(params.order().depthAsFloat());
138     writer.updateSsboIndexAttrib(ssboIndices);
139 
140     // The vector xform approximates how the control points are transformed by the shader to
141     // more accurately compute how many *parametric* segments are needed.
142     // getMaxScale() returns -1 if it can't compute a scale factor (e.g. perspective), taking the
143     // absolute value automatically converts that to an identity scale factor for our purposes.
144     writer.setShaderTransform(wangs_formula::VectorXform{params.transform().matrix()},
145                               params.transform().maxScaleFactor());
146 
147     SkASSERT(params.isStroke());
148     writer.updateStrokeParamsAttrib({params.strokeStyle().halfWidth(),
149                                      params.strokeStyle().joinLimit()});
150 
151     // TODO: If PatchWriter can handle adding caps to its deferred patches, and we can convert
152     // hairlines to use round caps instead of square, then StrokeIterator can be deleted entirely.
153     // Besides being simpler, PatchWriter already has what it needs from the shader matrix and
154     // stroke params, so we don't have to re-extract them here.
155     SkMatrix shaderMatrix = params.transform();
156     SkStrokeRec stroke{SkStrokeRec::kHairline_InitStyle};
157     stroke.setStrokeStyle(params.strokeStyle().width());
158     stroke.setStrokeParams(params.strokeStyle().cap(),
159                            params.strokeStyle().join(),
160                            params.strokeStyle().miterLimit());
161     StrokeIterator strokeIter(path, &stroke, &shaderMatrix);
162     while (strokeIter.next()) {
163         using Verb = StrokeIterator::Verb;
164         const SkPoint* p = strokeIter.pts();
165         int numChops;
166 
167         // TODO: The cusp detection logic should be moved into PatchWriter and shared between
168         // this and StrokeTessellator.cpp, but that will require updating a lot of SkGeometry to
169         // operate on float2 (skvx) instead of the legacy SkNx or SkPoint.
170         switch (strokeIter.verb()) {
171             case Verb::kContourFinished:
172                 writer.writeDeferredStrokePatch();
173                 break;
174             case Verb::kCircle:
175                 // Round cap or else an empty stroke that is specified to be drawn as a circle.
176                 writer.writeCircle(p[0]);
177                 [[fallthrough]];
178             case Verb::kMoveWithinContour:
179                 // A regular kMove invalidates the previous control point; the stroke iterator
180                 // tells us a new value to use.
181                 writer.updateJoinControlPointAttrib(p[0]);
182                 break;
183             case Verb::kLine:
184                 writer.writeLine(p[0], p[1]);
185                 break;
186             case Verb::kQuad:
187                 if (ConicHasCusp(p)) {
188                     // The cusp is always at the midtandent.
189                     SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
190                     writer.writeCircle(cusp);
191                     // A quad can only have a cusp if it's flat with a 180-degree turnaround.
192                     writer.writeLine(p[0], cusp);
193                     writer.writeLine(cusp, p[2]);
194                 } else {
195                     writer.writeQuadratic(p);
196                 }
197                 break;
198             case Verb::kConic:
199                 if (ConicHasCusp(p)) {
200                     // The cusp is always at the midtandent.
201                     SkConic conic(p, strokeIter.w());
202                     SkPoint cusp = conic.evalAt(conic.findMidTangent());
203                     writer.writeCircle(cusp);
204                     // A conic can only have a cusp if it's flat with a 180-degree turnaround.
205                     writer.writeLine(p[0], cusp);
206                     writer.writeLine(cusp, p[2]);
207                 } else {
208                     writer.writeConic(p, strokeIter.w());
209                 }
210                 break;
211             case Verb::kCubic:
212                 SkPoint chops[10];
213                 float T[2];
214                 bool areCusps;
215                 numChops = FindCubicConvex180Chops(p, T, &areCusps);
216                 if (numChops == 0) {
217                     writer.writeCubic(p);
218                 } else if (numChops == 1) {
219                     SkChopCubicAt(p, chops, T[0]);
220                     if (areCusps) {
221                         writer.writeCircle(chops[3]);
222                         // In a perfect world, these 3 points would be be equal after chopping
223                         // on a cusp.
224                         chops[2] = chops[4] = chops[3];
225                     }
226                     writer.writeCubic(chops);
227                     writer.writeCubic(chops + 3);
228                 } else {
229                     SkASSERT(numChops == 2);
230                     SkChopCubicAt(p, chops, T[0], T[1]);
231                     if (areCusps) {
232                         writer.writeCircle(chops[3]);
233                         writer.writeCircle(chops[6]);
234                         // Two cusps are only possible if it's a flat line with two 180-degree
235                         // turnarounds.
236                         writer.writeLine(chops[0], chops[3]);
237                         writer.writeLine(chops[3], chops[6]);
238                         writer.writeLine(chops[6], chops[9]);
239                     } else {
240                         writer.writeCubic(chops);
241                         writer.writeCubic(chops + 3);
242                         writer.writeCubic(chops + 6);
243                     }
244                 }
245                 break;
246         }
247     }
248 }
249 
writeUniformsAndTextures(const DrawParams & params,PipelineDataGatherer * gatherer) const250 void TessellateStrokesRenderStep::writeUniformsAndTextures(const DrawParams& params,
251                                                            PipelineDataGatherer* gatherer) const {
252     // TODO: Implement perspective
253     SkASSERT(params.transform().type() < Transform::Type::kPerspective);
254 
255     SkDEBUGCODE(UniformExpectationsValidator uev(gatherer, this->uniforms());)
256 
257     // affineMatrix = float4 (2x2 of transform), translate = float2, maxScale = float
258     // Column-major 2x2 of the transform.
259     SkV4 upper = {params.transform().matrix().rc(0, 0), params.transform().matrix().rc(1, 0),
260                   params.transform().matrix().rc(0, 1), params.transform().matrix().rc(1, 1)};
261     gatherer->write(upper);
262 
263     gatherer->write(SkPoint{params.transform().matrix().rc(0, 3),
264                             params.transform().matrix().rc(1, 3)});
265 
266     gatherer->write(params.transform().maxScaleFactor());
267 }
268 
269 }  // namespace skgpu::graphite
270