• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2023 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/PerEdgeAAQuadRenderStep.h"
9 
10 #include "include/core/SkM44.h"
11 #include "include/private/base/SkAssert.h"
12 #include "include/private/base/SkDebug.h"
13 #include "include/private/base/SkFloatingPoint.h"
14 #include "src/base/SkEnumBitMask.h"
15 #include "src/base/SkVx.h"
16 #include "src/core/SkSLTypeShared.h"
17 #include "src/gpu/BufferWriter.h"
18 #include "src/gpu/graphite/Attribute.h"
19 #include "src/gpu/graphite/BufferManager.h"
20 #include "src/gpu/graphite/DrawOrder.h"
21 #include "src/gpu/graphite/DrawParams.h"
22 #include "src/gpu/graphite/DrawTypes.h"
23 #include "src/gpu/graphite/DrawWriter.h"
24 #include "src/gpu/graphite/geom/EdgeAAQuad.h"
25 #include "src/gpu/graphite/geom/Geometry.h"
26 #include "src/gpu/graphite/geom/Rect.h"
27 #include "src/gpu/graphite/geom/Transform.h"
28 #include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
29 
30 #include <cstdint>
31 
32 // This RenderStep is specialized to draw filled rectangles with per-edge AA.
33 //
34 // Each of these "primitives" is represented by a single instance. The instance attributes are
35 // flexible enough to describe per-edge AA quads without relying on uniforms to define its
36 // operation. The attributes encode shape as follows:
37 //
38 // float4 edgeFlags - per-edge AA defined by each component: aa != 0.
39 // float4 quadXs - these values provide the X coordinates of the quadrilateral in top-left CW order.
40 // float4 quadYs - these values provide the Y coordinates of the quadrilateral.
41 //
42 // From the other direction, per-edge AA quads produce instance values like:
43 //  - [aa(t,r,b,l) ? 255 : 0]   [xs(tl,tr,br,bl)]     [ys(tl,tr,br,bl)]
44 //
45 // From this encoding, data can be unpacked for each corner, which are equivalent under
46 // rotational symmetry. Per-edge quads are always mitered and fill the interior, but the
47 // vertices are placed such that the edge coverage ramps can collapse to 0 area on non-AA edges.
48 //
49 // The vertices that describe each corner are placed so that edges and miters calculate
50 // coverage by interpolating a varying and then clamping in the fragment shader. Triangles that
51 // cover the inner and outer curves calculate distance to the curve within the fragment shader.
52 //
53 // See https://docs.google.com/presentation/d/1MCPstNsSlDBhR8CrsJo0r-cZNbu-sEJEvU9W94GOJoY/edit?usp=sharing
54 // for diagrams and explanation of how the geometry is defined.
55 //
56 // PerEdgeAAQuadRenderStep uses the common technique of approximating distance to the level set by
57 // one expansion of the Taylor's series for the level set's equation. Given a level set function
58 // C(x,y), this amounts to calculating C(px,py)/|∇C(px,py)|. For the straight edges the level set
59 // is linear and calculated in the vertex shader and then interpolated exactly over the rectangle.
60 // This provides distances to all four exterior edges within the fragment shader and allows it to
61 // reconstruct a relative position per elliptical corner. Unfortunately this requires the fragment
62 // shader to calculate the length of the gradient for straight edges instead of interpolating
63 // exact device-space distance.
64 //
65 // Unlike AnalyticRRectRenderStep, for per-edge AA quads it's valid to have each pixel calculate a
66 // single corner's coverage that's controlled via the vertex shader. Any bias is a constant 1/2,
67 // so this is also added in the vertex shader.
68 //
69 // Analytic derivatives are used so that a single pipeline can be used regardless of HW derivative
70 // support or for geometry that would prove difficult for forward differencing. The device-space
71 // gradient for ellipses is calculated per-pixel by transforming a per-pixel local gradient vector
72 // with the Jacobian of the inverse local-to-device transform:
73 //
74 // (px,py) is the projected point of (u,v) transformed by a 3x3 matrix, M:
75 //                [x(u,v) / w(u,v)]       [x]   [m00 m01 m02] [u]
76 //      (px,py) = [y(u,v) / w(u,v)] where [y] = [m10 m11 m12]X[v] = M*(u,v,1)
77 //                                        [w]   [m20 m21 m22] [1]
78 //
79 // C(px,py) can be defined in terms of a local Cl(u,v) as C(px,py) = Cl(p^-1(px,py)), where p^-1 =
80 //
81 //               [x'(px,py) / w'(px,py)]       [x']   [m00' m01' * m02'] [px]
82 //      (u,v) =  [y'(px,py) / w'(px,py)] where [y'] = [m10' m11' * m12']X[py] = M^-1*(px,py,0,1)
83 //                                             [w']   [m20' m21' * m22'] [ 1]
84 //
85 // Note that if the 3x3 M was arrived by dropping the 3rd row and column from a 4x4 since we assume
86 // a local 3rd coordinate of 0, M^-1 is not equal to the 4x4 inverse with dropped rows and columns.
87 //
88 // Using the chain rule, then ∇C(px,py)
89 //   =  ∇Cl(u,v)X[1/w'(px,py)     0       -x'(px,py)/w'(px,py)^2] [m00' m01']
90 //               [    0       1/w'(px,py) -y'(px,py)/w'(px,py)^2]X[m10' m11']
91 //                                                                [m20' m21']
92 //
93 //   = 1/w'(px,py)*∇Cl(u,v)X[1 0 -x'(px,py)/w'(px,py)] [m00' m01']
94 //                          [0 1 -y'(px,py)/w'(px,py)]X[m10' m11']
95 //                                                     [m20' m21']
96 //
97 //   = w(u,v)*∇Cl(u,v)X[1 0 0 -u] [m00' m01']
98 //                     [0 1 0 -v]X[m10' m11']
99 //                                [m20' m21']
100 //
101 //   = w(u,v)*∇Cl(u,v)X[m00'-m20'u m01'-m21'u]
102 //                     [m10'-m20'v m11'-m21'v]
103 //
104 // The vertex shader calculates the rightmost 2x2 matrix and interpolates it across the shape since
105 // each component is linear in (u,v). ∇Cl(u,v) is evaluated per pixel in the fragment shader and
106 // depends on which corner and edge being evaluated. w(u,v) is the device-space W coordinate, so
107 // its reciprocal is provided in sk_FragCoord.w.
108 namespace skgpu::graphite {
109 
110 using AAFlags = EdgeAAQuad::Flags;
111 
is_clockwise(const EdgeAAQuad & quad)112 static bool is_clockwise(const EdgeAAQuad& quad) {
113     if (quad.isRect()) {
114         return true; // by construction, these are always locally clockwise
115     }
116 
117     // This assumes that each corner has a consistent winding, which is the case for convex inputs,
118     // which is an assumption of the per-edge AA API. Check the sign of cross product between the
119     // first two edges.
120     const skvx::float4& xs = quad.xs();
121     const skvx::float4& ys = quad.ys();
122 
123     float winding = (xs[0] - xs[3])*(ys[1] - ys[0]) - (ys[0] - ys[3])*(xs[1] - xs[0]);
124     if (winding == 0.f) {
125         // The input possibly forms a triangle with duplicate vertices, so check the opposite corner
126         winding = (xs[2] - xs[1])*(ys[3] - ys[2]) - (ys[2] - ys[1])*(xs[3] - xs[2]);
127     }
128 
129     // At this point if winding is < 0, the quad's vertices are CCW. If it's still 0, the vertices
130     // form a line, in which case the vertex shader constructs a correct CW winding. Otherwise,
131     // the quad or triangle vertices produce a positive winding and are CW.
132     return winding >= 0.f;
133 }
134 
135 // Represents the per-vertex attributes used in each instance.
136 struct Vertex {
137     SkV2 fNormal;
138 };
139 
140 // Allowed values for the center weight instance value (selected at record time based on style
141 // and transform), and are defined such that when (insance-weight > vertex-weight) is true, the
142 // vertex should be snapped to the center instead of its regular calculation.
143 static constexpr int kCornerVertexCount = 4; // sk_VertexID is divided by this in SkSL
144 static constexpr int kVertexCount = 4 * kCornerVertexCount;
145 static constexpr int kIndexCount = 29;
146 
write_index_buffer(VertexWriter writer)147 static void write_index_buffer(VertexWriter writer) {
148     static constexpr uint16_t kTL = 0 * kCornerVertexCount;
149     static constexpr uint16_t kTR = 1 * kCornerVertexCount;
150     static constexpr uint16_t kBR = 2 * kCornerVertexCount;
151     static constexpr uint16_t kBL = 3 * kCornerVertexCount;
152 
153     static const uint16_t kIndices[kIndexCount] = {
154         // Exterior AA ramp outset
155         kTL+1,kTL+2,kTL+3,kTR+0,kTR+3,kTR+1,
156         kTR+1,kTR+2,kTR+3,kBR+0,kBR+3,kBR+1,
157         kBR+1,kBR+2,kBR+3,kBL+0,kBL+3,kBL+1,
158         kBL+1,kBL+2,kBL+3,kTL+0,kTL+3,kTL+1,
159         kTL+3,
160         // Fill triangles
161         kTL+3,kTR+3,kBL+3,kBR+3
162     };
163 
164     if (writer) {
165         writer << kIndices;
166     } // otherwise static buffer creation failed, so do nothing; Context initialization will fail.
167 }
168 
write_vertex_buffer(VertexWriter writer)169 static void write_vertex_buffer(VertexWriter writer) {
170     static constexpr float kHR2 = 0.5f * SK_FloatSqrt2; // "half root 2"
171 
172     // This template is repeated 4 times in the vertex buffer, for each of the four corners.
173     // The vertex ID is used to lookup per-corner instance properties such as positions,
174     // but otherwise this vertex data produces a consistent clockwise mesh from
175     // TL -> TR -> BR -> BL.
176     static constexpr Vertex kCornerTemplate[kCornerVertexCount] = {
177         // Normals for device-space AA outsets from outer curve
178         { {1.0f, 0.0f} },
179         { {kHR2, kHR2} },
180         { {0.0f, 1.0f} },
181 
182         // Normal for outer anchor (zero length to signal no local or device-space normal outset)
183         { {0.0f, 0.0f} },
184     };
185 
186     if (writer) {
187         writer << kCornerTemplate  // TL
188                << kCornerTemplate  // TR
189                << kCornerTemplate  // BR
190                << kCornerTemplate; // BL
191     } // otherwise static buffer creation failed, so do nothing; Context initialization will fail.
192 }
193 
PerEdgeAAQuadRenderStep(StaticBufferManager * bufferManager)194 PerEdgeAAQuadRenderStep::PerEdgeAAQuadRenderStep(StaticBufferManager* bufferManager)
195         : RenderStep(RenderStepID::kPerEdgeAAQuad,
196                      Flags::kPerformsShading | Flags::kEmitsCoverage | Flags::kOutsetBoundsForAA |
197                      Flags::kUseNonAAInnerFill,
198                      /*uniforms=*/{},
199                      PrimitiveType::kTriangleStrip,
200                      kDirectDepthGreaterPass,
201                      /*vertexAttrs=*/{
202                             {"normal", VertexAttribType::kFloat2, SkSLType::kFloat2},
203                      },
204                      /*instanceAttrs=*/
205                             {{"edgeFlags", VertexAttribType::kUByte4_norm, SkSLType::kFloat4},
206                              {"quadXs", VertexAttribType::kFloat4, SkSLType::kFloat4},
207                              {"quadYs", VertexAttribType::kFloat4, SkSLType::kFloat4},
208 
209                              // TODO: pack depth and ssbo index into one 32-bit attribute, if we can
210                              // go without needing both render step and paint ssbo index attributes.
211                              {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
212                              {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2},
213 
214                              {"mat0", VertexAttribType::kFloat3, SkSLType::kFloat3},
215                              {"mat1", VertexAttribType::kFloat3, SkSLType::kFloat3},
216                              {"mat2", VertexAttribType::kFloat3, SkSLType::kFloat3}},
217                      /*varyings=*/{
218                              // Device-space distance to LTRB edges of quad.
219                              {"edgeDistances", SkSLType::kFloat4}, // distance to LTRB edges
220                      }) {
221     // Initialize the static buffers we'll use when recording draw calls.
222     // NOTE: Each instance of this RenderStep gets its own copy of the data. Since there should only
223     // ever be one PerEdgeAAQuadRenderStep at a time, this shouldn't be an issue.
224     write_vertex_buffer(bufferManager->getVertexWriter(sizeof(Vertex) * kVertexCount,
225                                                        &fVertexBuffer));
226     write_index_buffer(bufferManager->getIndexWriter(sizeof(uint16_t) * kIndexCount,
227                                                      &fIndexBuffer));
228 }
229 
~PerEdgeAAQuadRenderStep()230 PerEdgeAAQuadRenderStep::~PerEdgeAAQuadRenderStep() {}
231 
vertexSkSL() const232 std::string PerEdgeAAQuadRenderStep::vertexSkSL() const {
233     // Returns the body of a vertex function, which must define a float4 devPosition variable and
234     // must write to an already-defined float2 stepLocalCoords variable.
235     return "float4 devPosition = per_edge_aa_quad_vertex_fn("
236                    // Vertex Attributes
237                    "normal, "
238                    // Instance Attributes
239                    "edgeFlags, quadXs, quadYs, depth, "
240                    "float3x3(mat0, mat1, mat2), "
241                    // Varyings
242                    "edgeDistances, "
243                    // Render Step
244                    "stepLocalCoords);\n";
245 }
246 
fragmentCoverageSkSL() const247 const char* PerEdgeAAQuadRenderStep::fragmentCoverageSkSL() const {
248     // The returned SkSL must write its coverage into a 'half4 outputCoverage' variable (defined in
249     // the calling code) with the actual coverage splatted out into all four channels.
250     return "outputCoverage = per_edge_aa_quad_coverage_fn(sk_FragCoord, edgeDistances);";
251 }
252 
writeVertices(DrawWriter * writer,const DrawParams & params,skvx::uint2 ssboIndices) const253 void PerEdgeAAQuadRenderStep::writeVertices(DrawWriter* writer,
254                                            const DrawParams& params,
255                                            skvx::uint2 ssboIndices) const {
256     SkASSERT(params.geometry().isEdgeAAQuad());
257     const EdgeAAQuad& quad = params.geometry().edgeAAQuad();
258 
259     DrawWriter::Instances instance{*writer, fVertexBuffer, fIndexBuffer, kIndexCount};
260     auto vw = instance.append(1);
261 
262     // Empty fills should not have been recorded at all.
263     SkDEBUGCODE(Rect bounds = params.geometry().bounds());
264     SkASSERT(!bounds.isEmptyNegativeOrNaN());
265 
266     constexpr uint8_t kAAOn = 255;
267     constexpr uint8_t kAAOff = 0;
268     auto edgeSigns = skvx::byte4{quad.edgeFlags() & AAFlags::kLeft   ? kAAOn : kAAOff,
269                                  quad.edgeFlags() & AAFlags::kTop    ? kAAOn : kAAOff,
270                                  quad.edgeFlags() & AAFlags::kRight  ? kAAOn : kAAOff,
271                                  quad.edgeFlags() & AAFlags::kBottom ? kAAOn : kAAOff};
272 
273     // The vertex shader expects points to be in clockwise order. EdgeAAQuad is the only
274     // shape that *might* have counter-clockwise input.
275     if (is_clockwise(quad)) {
276         vw << edgeSigns << quad.xs() << quad.ys();
277     } else {
278         vw << skvx::shuffle<2,1,0,3>(edgeSigns)  // swap left and right AA bits
279            << skvx::shuffle<1,0,3,2>(quad.xs())  // swap TL with TR, and BL with BR
280            << skvx::shuffle<1,0,3,2>(quad.ys()); //   ""
281     }
282 
283     // All instance types share the remaining instance attribute definitions
284     const SkM44& m = params.transform().matrix();
285 
286     vw << params.order().depthAsFloat()
287        << ssboIndices
288        << m.rc(0,0) << m.rc(1,0) << m.rc(3,0)  // mat0
289        << m.rc(0,1) << m.rc(1,1) << m.rc(3,1)  // mat1
290        << m.rc(0,3) << m.rc(1,3) << m.rc(3,3); // mat2
291 }
292 
writeUniformsAndTextures(const DrawParams &,PipelineDataGatherer *) const293 void PerEdgeAAQuadRenderStep::writeUniformsAndTextures(const DrawParams&,
294                                                        PipelineDataGatherer*) const {
295     // All data is uploaded as instance attributes, so no uniforms are needed.
296 }
297 
298 }  // namespace skgpu::graphite
299