• 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 "src/base/SkVx.h"
12 #include "src/core/SkGeometry.h"
13 #include "src/sksl/SkSLString.h"
14 
15 #include "src/gpu/graphite/DrawParams.h"
16 #include "src/gpu/graphite/DrawTypes.h"
17 #include "src/gpu/graphite/DrawWriter.h"
18 #include "src/gpu/graphite/PipelineData.h"
19 #include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
20 #include "src/gpu/graphite/render/DynamicInstancesPatchAllocator.h"
21 
22 #include "src/gpu/tessellate/FixedCountBufferUtils.h"
23 #include "src/gpu/tessellate/PatchWriter.h"
24 #include "src/gpu/tessellate/StrokeIterator.h"
25 
26 
27 namespace skgpu::graphite {
28 
29 namespace {
30 
31 using 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.
39 static constexpr PatchAttribs kAttribs = PatchAttribs::kJoinControlPoint |
40                                          PatchAttribs::kStrokeParams |
41                                          PatchAttribs::kPaintDepth |
42                                          PatchAttribs::kSsboIndex;
43 static constexpr PatchAttribs kAttribsWithCurveType = kAttribs | PatchAttribs::kExplicitCurveType;
44 using Writer = PatchWriter<DynamicInstancesPatchAllocator<FixedCountStrokes>,
45                            Required<PatchAttribs::kJoinControlPoint>,
46                            Required<PatchAttribs::kStrokeParams>,
47                            Required<PatchAttribs::kPaintDepth>,
48                            Required<PatchAttribs::kSsboIndex>,
49                            Optional<PatchAttribs::kExplicitCurveType>,
50                            ReplicateLineEndPoints,
51                            TrackJoinControlPoints>;
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
56 static constexpr Attribute kBaseAttributes[] = {
57         {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
58         {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
59         {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
60         {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
61         {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
62         {"ssboIndices", VertexAttribType::kUShort2, SkSLType::kUShort2}};
63 
64 static constexpr Attribute kAttributesWithCurveType[] = {
65         {"p01", VertexAttribType::kFloat4, SkSLType::kFloat4},
66         {"p23", VertexAttribType::kFloat4, SkSLType::kFloat4},
67         {"prevPoint", VertexAttribType::kFloat2, SkSLType::kFloat2},
68         {"stroke", VertexAttribType::kFloat2, SkSLType::kFloat2},
69         {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
70         {"curveType", VertexAttribType::kFloat, SkSLType::kFloat},
71         {"ssboIndices", VertexAttribType::kUShort2, SkSLType::kUShort2}};
72 
73 static constexpr SkSpan<const Attribute> kAttributes[2] = {kAttributesWithCurveType,
74                                                            kBaseAttributes};
75 
76 }  // namespace
77 
TessellateStrokesRenderStep(bool infinitySupport)78 TessellateStrokesRenderStep::TessellateStrokesRenderStep(bool infinitySupport)
79         : RenderStep("TessellateStrokeRenderStep",
80                      "",
81                      Flags::kRequiresMSAA | Flags::kPerformsShading,
82                      /*uniforms=*/{{"affineMatrix", SkSLType::kFloat4},
83                                    {"translate", SkSLType::kFloat2},
84                                    {"maxScale", SkSLType::kFloat}},
85                      PrimitiveType::kTriangleStrip,
86                      kDirectDepthGreaterPass,
87                      /*vertexAttrs=*/  {},
88                      /*instanceAttrs=*/kAttributes[infinitySupport])
89         , fInfinitySupport(infinitySupport) {}
90 
~TessellateStrokesRenderStep()91 TessellateStrokesRenderStep::~TessellateStrokesRenderStep() {}
92 
vertexSkSL() const93 std::string TessellateStrokesRenderStep::vertexSkSL() const {
94     // TODO: Assumes vertex ID support for now, max edges must equal
95     // skgpu::tess::FixedCountStrokes::kMaxEdges -> (2^14 - 1) -> 16383
96     return SkSL::String::printf(
97             R"(
98                 float edgeID = float(sk_VertexID >> 1);
99                 if ((sk_VertexID & 1) != 0) {
100                     edgeID = -edgeID;
101                 }
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 
writeVertices(DrawWriter * dw,const DrawParams & params,skvx::ushort2 ssboIndices) const112 void TessellateStrokesRenderStep::writeVertices(DrawWriter* dw,
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();
147     SkStrokeRec stroke{SkStrokeRec::kHairline_InitStyle};
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.
180                     SkPoint cusp = SkEvalQuadAt(p, SkFindQuadMidTangent(p));
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 
writeUniformsAndTextures(const DrawParams & params,PipelineDataGatherer * gatherer) const241 void TessellateStrokesRenderStep::writeUniformsAndTextures(const DrawParams& params,
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
261