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