1 /*
2 * Copyright 2021 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 "experimental/graphite/src/Renderer.h"
9
10 #include "experimental/graphite/src/DrawWriter.h"
11 #include "experimental/graphite/src/UniformManager.h"
12 #include "experimental/graphite/src/geom/Shape.h"
13 #include "experimental/graphite/src/geom/Transform_graphite.h"
14 #include "include/core/SkPathTypes.h"
15 #include "include/core/SkRect.h"
16 #include "src/core/SkUniformData.h"
17 #include "src/gpu/BufferWriter.h"
18 #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
19
20 namespace skgpu {
21
22 namespace {
23
24 // TODO: These settings are actually shared by tessellating path renderers, so will be exposed later
25
26 // Returns the stencil settings to use for a standard Redbook "stencil" pass.
fillrule_settings(bool evenOdd)27 constexpr DepthStencilSettings fillrule_settings(bool evenOdd) {
28 // Increments clockwise triangles and decrements counterclockwise. Used for "winding" fill.
29 constexpr DepthStencilSettings::Face kIncCW = {
30 /*stencilFail=*/ StencilOp::kKeep,
31 /*depthFail=*/ StencilOp::kKeep,
32 /*dsPass=*/ StencilOp::kIncWrap,
33 /*stencilCompare=*/CompareOp::kAlways,
34 /*readMask=*/ 0xffffffff,
35 /*writeMask=*/ 0xffffffff
36 };
37 constexpr DepthStencilSettings::Face kDecCCW = {
38 /*stencilFail=*/ StencilOp::kKeep,
39 /*depthFail=*/ StencilOp::kKeep,
40 /*dsPass=*/ StencilOp::kDecWrap,
41 /*stencilCompare=*/CompareOp::kAlways,
42 /*readMask=*/ 0xffffffff,
43 /*writeMask=*/ 0xffffffff
44 };
45
46 // Toggles the bottom stencil bit. Used for "even-odd" fill.
47 constexpr DepthStencilSettings::Face kToggle = {
48 /*stencilFail=*/ StencilOp::kKeep,
49 /*depthFail=*/ StencilOp::kKeep,
50 /*dsPass=*/ StencilOp::kInvert,
51 /*stencilCompare=*/CompareOp::kAlways,
52 /*readMask=*/ 0xffffffff,
53 /*writeMask=*/ 0x00000001
54 };
55
56 // Always use ref = 0, disable depths, but still use greater depth test.
57 constexpr DepthStencilSettings kWindingFill = {
58 /*frontStencil=*/kIncCW,
59 /*backStencil=*/ kDecCCW,
60 /*refValue=*/ 0,
61 /*stencilTest=*/ true,
62 /*depthCompare=*/CompareOp::kAlways, // kGreater once steps know the right depth value
63 /*depthTest=*/ true,
64 /*depthWrite=*/ false
65 };
66 constexpr DepthStencilSettings kEvenOddFill = {
67 /*frontStencil=*/kToggle,
68 /*backStencil=*/ kToggle,
69 /*refValue=*/ 0,
70 /*stencilTest=*/ true,
71 /*depthCompare=*/CompareOp::kAlways, // kGreater once steps know the right depth value
72 /*depthTest=*/ true,
73 /*depthWrite=*/ false
74 };
75
76 return evenOdd ? kEvenOddFill : kWindingFill;
77 }
78
79 // Returns the stencil settings to use for a standard Redbook "fill" pass. Allows non-zero
80 // stencil values to pass and write a color, and resets the stencil value back to zero; discards
81 // immediately on stencil values of zero (or does the inverse of these operations when the path
82 // requires filling everything else).
cover_settings(bool inverse)83 constexpr DepthStencilSettings cover_settings(bool inverse) {
84 // Resets non-zero bits to 0, passes when not zero. We set depthFail to kZero because if we
85 // encounter that case, the kNotEqual=0 stencil test passed, so it does need to be set back to 0
86 // and the dsPass op won't be run. In practice, since the stencil steps will fail the same depth
87 // test, the stencil value will likely not be non-zero, but best to be explicit.
88 constexpr DepthStencilSettings::Face kNormal = {
89 /*stencilFail=*/ StencilOp::kKeep,
90 /*depthFail=*/ StencilOp::kZero,
91 /*dsPass=*/ StencilOp::kZero,
92 /*stencilCompare=*/CompareOp::kNotEqual,
93 /*readMask=*/ 0xffffffff,
94 /*writeMask=*/ 0xffffffff
95 };
96
97 // Resets non-zero bits to 0, passes when zero
98 constexpr DepthStencilSettings::Face kInverted = {
99 /*stencilFail=*/ StencilOp::kZero,
100 /*depthFail=*/ StencilOp::kKeep,
101 /*dsPass=*/ StencilOp::kKeep,
102 /*stencilCompare=*/CompareOp::kEqual,
103 /*readMask=*/ 0xffffffff,
104 /*writeMask=*/ 0xffffffff
105 };
106
107 // Always use ref = 0, enabled depth writes, and greater depth test, both
108 // front and back use the same stencil settings.
109 constexpr DepthStencilSettings kNormalDSS = {
110 /*frontStencil=*/kNormal,
111 /*frontStencil=*/kNormal,
112 /*refValue=*/ 0,
113 /*stencilTest=*/ true,
114 /*depthCompare=*/CompareOp::kAlways, // kGreater once steps know the right depth value
115 /*depthTest=*/ true,
116 /*depthWrite=*/ true
117 };
118 constexpr DepthStencilSettings kInvertedDSS = {
119 /*frontStencil=*/kInverted,
120 /*backStencil=*/ kInverted,
121 /*refValue=*/ 0,
122 /*stencilTest=*/ true,
123 /*depthCompare=*/CompareOp::kAlways, // kGreater once steps know the right depth value
124 /*depthTest=*/ true,
125 /*depthWrite=*/ true
126 };
127 return inverse ? kInvertedDSS : kNormalDSS;
128 }
129
130 class StencilFanRenderStep final : public RenderStep {
131 public:
StencilFanRenderStep(bool evenOdd)132 StencilFanRenderStep(bool evenOdd)
133 : RenderStep(Flags::kRequiresMSAA,
134 /*uniforms=*/{},
135 PrimitiveType::kTriangles,
136 fillrule_settings(evenOdd),
137 /*vertexAttrs=*/{{"position",
138 VertexAttribType::kFloat3,
139 SkSLType::kFloat3}},
140 /*instanceAttrs=*/{}) {}
141
~StencilFanRenderStep()142 ~StencilFanRenderStep() override {}
143
name() const144 const char* name() const override { return "stencil-fan"; }
145
vertexSkSL() const146 const char* vertexSkSL() const override {
147 return " float4 devPosition = float4(position.xy, 0.0, position.z);\n";
148 }
149
writeVertices(DrawWriter * writer,const SkIRect & bounds,const Transform & localToDevice,const Shape & shape) const150 void writeVertices(DrawWriter* writer,
151 const SkIRect& bounds,
152 const Transform& localToDevice,
153 const Shape& shape) const override {
154 // TODO: Have Shape provide a path-like iterator so we don't actually have to convert non
155 // paths to SkPath just to iterate their pts/verbs
156 SkPath path = shape.asPath();
157 DrawWriter::Vertices verts{*writer};
158 for (PathMiddleOutFanIter it(path); !it.done();) {
159 for (auto [p0, p1, p2] : it.nextStack()) {
160 // TODO: PathMiddleOutFanIter should use SkV2 instead of SkPoint?
161 SkV2 p[3] = {{p0.fX, p0.fY}, {p1.fX, p1.fY}, {p2.fX, p2.fY}};
162 SkV4 devPoints[3];
163 localToDevice.mapPoints(p, devPoints, 3);
164
165 // TODO: Support reserving maxTrianglesInFans*3 vertices outside the loop, with
166 // automatic returns of unused verts.
167 verts.append(3) << devPoints[0].x << devPoints[0].y << devPoints[0].w // p0
168 << devPoints[1].x << devPoints[1].y << devPoints[1].w // p1
169 << devPoints[2].x << devPoints[2].y << devPoints[2].w; // p2
170 }
171 }
172 }
173
writeUniforms(Layout layout,const SkIRect &,const Transform &,const Shape &) const174 sk_sp<SkUniformData> writeUniforms(Layout layout,
175 const SkIRect&,
176 const Transform&,
177 const Shape&) const override {
178 // Control points are pre-transformed to device space on the CPU, so no uniforms needed.
179 return nullptr;
180 }
181 };
182
183 // TODO: Hand off to csmartdalton, this should roughly correspond to the fStencilPathProgram stage
184 // of skgpu::v1::PathStencilCoverOp using the PathCurveTessellator
185 /*
186 class StencilCurvesRenderStep : public RenderStep {
187 public:
188 StencilCurvesRenderStep() {}
189
190 ~StencilCurvesRenderStep() override {}
191
192 const char* name() const override { return "stencil-curves"; }
193 bool requiresStencil() const override { return true; }
194 bool requiresMSAA() const override { return true; }
195 bool performsShading() const override { return false; }
196
197 private:
198 };
199 */
200
201 // TODO: Hand off to csmartdalton, this should roughly correspond to the fCoverBBoxProgram stage
202 // of skgpu::v1::PathStencilCoverOp.
203 class FillBoundsRenderStep final : public RenderStep {
204 public:
205 // TODO: Will need to add kRequiresStencil when we support specifying stencil settings and
206 // the Renderer includes the stenciling step first.
FillBoundsRenderStep(bool inverseFill)207 FillBoundsRenderStep(bool inverseFill)
208 : RenderStep(Flags::kPerformsShading,
209 /*uniforms=*/{},
210 PrimitiveType::kTriangles,
211 cover_settings(inverseFill),
212 /*vertexAttrs=*/{{"position",
213 VertexAttribType::kFloat3,
214 SkSLType::kFloat3}},
215 /*instanceAttrs=*/{})
216 , fInverseFill(inverseFill) {}
217
~FillBoundsRenderStep()218 ~FillBoundsRenderStep() override {}
219
name() const220 const char* name() const override { return "fill-bounds"; }
221
vertexSkSL() const222 const char* vertexSkSL() const override {
223 return " float4 devPosition = float4(position.xy, 0.0, position.z);\n";
224 }
225
writeVertices(DrawWriter * writer,const SkIRect & bounds,const Transform & localToDevice,const Shape & shape) const226 void writeVertices(DrawWriter* writer,
227 const SkIRect& bounds,
228 const Transform& localToDevice,
229 const Shape& shape) const override {
230 SkV4 devPoints[4]; // ordered TL, TR, BR, BL
231
232 if (fInverseFill) {
233 // TODO: When we handle local coords, we'd need to map these corners by the inverse.
234 devPoints[0] = {(float) bounds.fLeft, (float) bounds.fTop, 0.f, 1.f};
235 devPoints[1] = {(float) bounds.fRight, (float) bounds.fTop, 0.f, 1.f};
236 devPoints[2] = {(float) bounds.fRight, (float) bounds.fBottom, 0.f, 1.f};
237 devPoints[3] = {(float) bounds.fLeft, (float) bounds.fBottom, 0.f, 1.f};
238 } else {
239 localToDevice.mapPoints(shape.bounds(), devPoints);
240 }
241
242 DrawWriter::Vertices verts{*writer};
243 verts.append(6) << devPoints[0].x << devPoints[0].y << devPoints[0].w // TL
244 << devPoints[3].x << devPoints[3].y << devPoints[3].w // BL
245 << devPoints[1].x << devPoints[1].y << devPoints[1].w // TR
246 << devPoints[1].x << devPoints[1].y << devPoints[1].w // TR
247 << devPoints[3].x << devPoints[3].y << devPoints[3].w // BL
248 << devPoints[2].x << devPoints[2].y << devPoints[2].w;// BR
249 }
250
writeUniforms(Layout layout,const SkIRect &,const Transform & localToDevice,const Shape &) const251 sk_sp<SkUniformData> writeUniforms(Layout layout,
252 const SkIRect&,
253 const Transform& localToDevice,
254 const Shape&) const override {
255 // Positions are pre-transformed on the CPU so no uniforms needed
256 return nullptr;
257 }
258
259 private:
260 const bool fInverseFill;
261 };
262
263 } // anonymous namespace
264
StencilAndFillPath(SkPathFillType fillType)265 const Renderer& Renderer::StencilAndFillPath(SkPathFillType fillType) {
266 // Because each fill type uses a different stencil settings, there is one Renderer per type.
267 // However, at each stage (stencil vs. cover), there are only two RenderSteps to branch on.
268 static const StencilFanRenderStep kWindingStencilFan{false};
269 static const StencilFanRenderStep kEvenOddStencilFan{true};
270 static const FillBoundsRenderStep kFill{false};
271 static const FillBoundsRenderStep kInverseFill{true};
272
273 // TODO: Uncomment and include the curve stenciling steps to draw curved paths
274 static const Renderer kWindingRenderer{"stencil-and-fill[winding]",
275 &kWindingStencilFan,
276 /*&kWindingStencilCurves,*/
277 &kFill};
278 static const Renderer kInverseWindingRenderer{"stencil-and-fill[inverse-winding]",
279 &kWindingStencilFan,
280 /*&kWindingStencilCurves,*/
281 &kInverseFill};
282 static const Renderer kEvenOddRenderer{"stencil-and-fill[evenodd]",
283 &kEvenOddStencilFan,
284 /*&kEvenOddStencilCurves,*/
285 &kFill};
286 static const Renderer kInverseEvenOddRenderer{"stencil-and-fill[inverse-evenodd]",
287 &kEvenOddStencilFan,
288 /*&kEvenOddStencilCurves,*/
289 &kInverseFill};
290
291 switch(fillType) {
292 case SkPathFillType::kWinding: return kWindingRenderer;
293 case SkPathFillType::kEvenOdd: return kEvenOddRenderer;
294 case SkPathFillType::kInverseWinding: return kInverseWindingRenderer;
295 case SkPathFillType::kInverseEvenOdd: return kInverseEvenOddRenderer;
296 }
297 SkUNREACHABLE;
298 }
299
300 } // namespace skgpu
301