• 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/AnalyticRRectRenderStep.h"
9 
10 #include "include/core/SkM44.h"
11 #include "include/core/SkPaint.h"
12 #include "include/core/SkRRect.h"
13 #include "include/core/SkScalar.h"
14 #include "include/private/base/SkAssert.h"
15 #include "include/private/base/SkFloatingPoint.h"
16 #include "include/private/base/SkPoint_impl.h"
17 #include "src/base/SkEnumBitMask.h"
18 #include "src/base/SkVx.h"
19 #include "src/core/SkRRectPriv.h"
20 #include "src/core/SkSLTypeShared.h"
21 #include "src/gpu/BufferWriter.h"
22 #include "src/gpu/graphite/Attribute.h"
23 #include "src/gpu/graphite/BufferManager.h"
24 #include "src/gpu/graphite/DrawOrder.h"
25 #include "src/gpu/graphite/DrawParams.h"
26 #include "src/gpu/graphite/DrawTypes.h"
27 #include "src/gpu/graphite/DrawWriter.h"
28 #include "src/gpu/graphite/geom/EdgeAAQuad.h"
29 #include "src/gpu/graphite/geom/Geometry.h"
30 #include "src/gpu/graphite/geom/Rect.h"
31 #include "src/gpu/graphite/geom/Shape.h"
32 #include "src/gpu/graphite/geom/Transform.h"
33 #include "src/gpu/graphite/render/CommonDepthStencilSettings.h"
34 
35 #include <cstdint>
36 
37 // This RenderStep is flexible and can draw filled rectangles, filled quadrilaterals with per-edge
38 // AA, filled rounded rectangles with arbitrary corner radii, stroked rectangles with any join,
39 // stroked lines with any cap, stroked rounded rectangles with circular corners (each corner can be
40 // different or square), hairline rectangles, hairline lines, and hairline rounded rectangles with
41 // arbitrary corners.
42 //
43 // We combine all of these together to maximize batching across simple geometric draws and reduce
44 // the number pipeline specializations. Additionally, these primitives are the most common
45 // operations and help us avoid triggering MSAA.
46 //
47 // Each of these "primitives" is represented by a single instance. The instance attributes are
48 // flexible enough to describe any of the above shapes without relying on uniforms to define its
49 // operation. The attributes encode shape as follows:
50 //
51 // float4 xRadiiOrFlags - if any components is > 0, the instance represents a filled round rect
52 //    with elliptical corners and these values specify the X radii in top-left CW order.
53 //    Otherwise, if .x < -1, the instance represents a stroked or hairline [round] rect or line,
54 //    where .y differentiates hairline vs. stroke. If .y is negative, then it is a hairline [round]
55 //    rect and xRadiiOrFlags stores (-2 - X radii); if .y is zero, it is a regular stroked [round]
56 //    rect; if .y is positive, then it is a stroked *or* hairline line. For .y >= 0, .z holds the
57 //    stroke radius and .w stores the join limit (matching StrokeStyle's conventions).
58 //    Lastly, if -1 <= .x <= 0, it's a filled quadrilateral with per-edge AA defined by each by the
59 //    component: aa != 0.
60 // float4 radiiOrQuadXs - if in filled round rect or hairline [round] rect mode, these values
61 //    provide the Y radii in top-left CW order. If in stroked [round] rect mode, these values
62 //    provide the circular corner radii (same order). Otherwise, when in per-edge quad mode, these
63 //    values provide the X coordinates of the quadrilateral (same order).
64 // float4 ltrbOrQuadYs - if in filled round rect mode or stroked [round] rect mode, these values
65 //    define the LTRB edge coordinates of the rectangle surrounding the round rect (or the
66 //    rect itself when the radii are 0s). In stroked line mode, LTRB is treated as (x0,y0) and
67 //    (x1,y1) that defines the line. Otherwise, in per-edge quad mode, these values provide
68 //    the Y coordinates of the quadrilateral.
69 //
70 // From the other direction, shapes produce instance values like:
71 //  - filled rect:    [-1 -1 -1 -1]            [L R R L]             [T T B B]
72 //  - stroked rect:   [-2 0 stroke join]       [0 0 0 0]             [L T R B]
73 //  - hairline rect:  [-2 -2 -2 -2]            [0 0 0 0]             [L T R B]
74 //  - filled rrect:   [xRadii(tl,tr,br,bl)]    [yRadii(tl,tr,br,bl)] [L T R B]
75 //  - stroked rrect:  [-2 0 stroke join]       [radii(tl,tr,br,bl)]  [L T R B]
76 //  - hairline rrect: [-2-xRadii(tl,tr,br,bl)] [radii(tl,tr,br,bl)]  [L T R B]
77 //  - filled line:    N/A, discarded higher in the stack
78 //  - stroked line:   [-2 1 stroke cap]        [0 0 0 0]             [x0,y0,x1,y1]
79 //  - hairline line:  [-2 1 0 1]               [0 0 0 0]             [x0,y0,x1,y1]
80 //  - per-edge quad:  [aa(t,r,b,l) ? -1 : 0]   [xs(tl,tr,br,bl)]     [ys(tl,tr,br,bl)]
81 //
82 // This encoding relies on the fact that a valid SkRRect with all x radii equal to 0 must have
83 // y radii equal to 0 (so it's a rectangle and we can treat it as a quadrilateral with
84 // all edges AA'ed). This avoids other encodings' inability to represent a quad with all edges
85 // anti-aliased (e.g. checking for negatives in xRadiiOrFlags to turn on per-edge mode).
86 //
87 // From this encoding, data can be unpacked for each corner, which are equivalent under
88 // rotational symmetry. A corner can have an outer curve, be mitered, or be beveled. It can
89 // have an inner curve, an inner miter, or fill the interior. Per-edge quads are always mitered
90 // and fill the interior, but the vertices are placed such that the edge coverage ramps can
91 // collapse to 0 area on non-AA edges.
92 //
93 // The vertices that describe each corner are placed so that edges, miters, and bevels calculate
94 // coverage by interpolating a varying and then clamping in the fragment shader. Triangles that
95 // cover the inner and outer curves calculate distance to the curve within the fragment shader.
96 //
97 // See https://docs.google.com/presentation/d/1MCPstNsSlDBhR8CrsJo0r-cZNbu-sEJEvU9W94GOJoY/edit?usp=sharing
98 // for diagrams and explanation of how the geometry is defined.
99 //
100 // AnalyticRRectRenderStep uses the common technique of approximating distance to the level set by
101 // one expansion of the Taylor's series for the level set's equation. Given a level set function
102 // C(x,y), this amounts to calculating C(px,py)/|∇C(px,py)|. For the straight edges the level set
103 // is linear and calculated in the vertex shader and then interpolated exactly over the rectangle.
104 // This provides distances to all four exterior edges within the fragment shader and allows it to
105 // reconstruct a relative position per elliptical corner. Unfortunately this requires the fragment
106 // shader to calculate the length of the gradient for straight edges instead of interpolating
107 // exact device-space distance.
108 //
109 // All four corner radii are potentially evaluated by the fragment shader although each corner's
110 // coverage is only calculated when the pixel is within the bounding box of its quadrant. For fills
111 // and simple strokes it's theoretically valid to have each pixel calculate a single corner's
112 // coverage that was controlled via the vertex shader. However, testing all four corners is
113 // necessary in order to correctly handle self-intersecting stroke interiors. Similarly, all four
114 // edges must be evaluated in order to handle extremely thin shapes; whereas often you could get
115 // away with tracking a single edge distance per pixel.
116 //
117 // Analytic derivatives are used so that a single pipeline can be used regardless of HW derivative
118 // support or for geometry that would prove difficult for forward differencing. The device-space
119 // gradient for ellipses is calculated per-pixel by transforming a per-pixel local gradient vector
120 // with the Jacobian of the inverse local-to-device transform:
121 //
122 // (px,py) is the projected point of (u,v) transformed by a 3x3 matrix, M:
123 //                [x(u,v) / w(u,v)]       [x]   [m00 m01 m02] [u]
124 //      (px,py) = [y(u,v) / w(u,v)] where [y] = [m10 m11 m12]X[v] = M*(u,v,1)
125 //                                        [w]   [m20 m21 m22] [1]
126 //
127 // 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 =
128 //
129 //               [x'(px,py) / w'(px,py)]       [x']   [m00' m01' * m02'] [px]
130 //      (u,v) =  [y'(px,py) / w'(px,py)] where [y'] = [m10' m11' * m12']X[py] = M^-1*(px,py,0,1)
131 //                                             [w']   [m20' m21' * m22'] [ 1]
132 //
133 // Note that if the 3x3 M was arrived by dropping the 3rd row and column from a 4x4 since we assume
134 // a local 3rd coordinate of 0, M^-1 is not equal to the 4x4 inverse with dropped rows and columns.
135 //
136 // Using the chain rule, then ∇C(px,py)
137 //   =  ∇Cl(u,v)X[1/w'(px,py)     0       -x'(px,py)/w'(px,py)^2] [m00' m01']
138 //               [    0       1/w'(px,py) -y'(px,py)/w'(px,py)^2]X[m10' m11']
139 //                                                                [m20' m21']
140 //
141 //   = 1/w'(px,py)*∇Cl(u,v)X[1 0 -x'(px,py)/w'(px,py)] [m00' m01']
142 //                          [0 1 -y'(px,py)/w'(px,py)]X[m10' m11']
143 //                                                     [m20' m21']
144 //
145 //   = w(u,v)*∇Cl(u,v)X[1 0 0 -u] [m00' m01']
146 //                     [0 1 0 -v]X[m10' m11']
147 //                                [m20' m21']
148 //
149 //   = w(u,v)*∇Cl(u,v)X[m00'-m20'u m01'-m21'u]
150 //                     [m10'-m20'v m11'-m21'v]
151 //
152 // The vertex shader calculates the rightmost 2x2 matrix and interpolates it across the shape since
153 // each component is linear in (u,v). ∇Cl(u,v) is evaluated per pixel in the fragment shader and
154 // depends on which corner and edge being evaluated. w(u,v) is the device-space W coordinate, so
155 // its reciprocal is provided in sk_FragCoord.w.
156 namespace skgpu::graphite {
157 
158 using AAFlags = EdgeAAQuad::Flags;
159 
load_x_radii(const SkRRect & rrect)160 static skvx::float4 load_x_radii(const SkRRect& rrect) {
161     return skvx::float4{rrect.radii(SkRRect::kUpperLeft_Corner).fX,
162                         rrect.radii(SkRRect::kUpperRight_Corner).fX,
163                         rrect.radii(SkRRect::kLowerRight_Corner).fX,
164                         rrect.radii(SkRRect::kLowerLeft_Corner).fX};
165 }
load_y_radii(const SkRRect & rrect)166 static skvx::float4 load_y_radii(const SkRRect& rrect) {
167     return skvx::float4{rrect.radii(SkRRect::kUpperLeft_Corner).fY,
168                         rrect.radii(SkRRect::kUpperRight_Corner).fY,
169                         rrect.radii(SkRRect::kLowerRight_Corner).fY,
170                         rrect.radii(SkRRect::kLowerLeft_Corner).fY};
171 }
172 
opposite_insets_intersect(const SkRRect & rrect,float strokeRadius,float aaRadius)173 static bool opposite_insets_intersect(const SkRRect& rrect, float strokeRadius, float aaRadius) {
174     // One AA inset per side
175     const float maxInset = strokeRadius + 2.f * aaRadius;
176     return // Horizontal insets would intersect opposite corner's curve
177            maxInset >= rrect.width() - rrect.radii(SkRRect::kLowerLeft_Corner).fX   ||
178            maxInset >= rrect.width() - rrect.radii(SkRRect::kLowerRight_Corner).fX  ||
179            maxInset >= rrect.width() - rrect.radii(SkRRect::kUpperLeft_Corner).fX   ||
180            maxInset >= rrect.width() - rrect.radii(SkRRect::kUpperRight_Corner).fX  ||
181            // Vertical insets would intersect opposite corner's curve
182            maxInset >= rrect.height() - rrect.radii(SkRRect::kLowerLeft_Corner).fY  ||
183            maxInset >= rrect.height() - rrect.radii(SkRRect::kLowerRight_Corner).fY ||
184            maxInset >= rrect.height() - rrect.radii(SkRRect::kUpperLeft_Corner).fY  ||
185            maxInset >= rrect.height() - rrect.radii(SkRRect::kUpperRight_Corner).fY;
186 }
187 
opposite_insets_intersect(const Rect & rect,float strokeRadius,float aaRadius)188 static bool opposite_insets_intersect(const Rect& rect, float strokeRadius, float aaRadius) {
189     return any(rect.size() <= 2.f * (strokeRadius + aaRadius));
190 }
191 
opposite_insets_intersect(const Geometry & geometry,float strokeRadius,float aaRadius)192 static bool opposite_insets_intersect(const Geometry& geometry,
193                                       float strokeRadius,
194                                       float aaRadius) {
195     if (geometry.isEdgeAAQuad()) {
196         SkASSERT(strokeRadius == 0.f);
197         const EdgeAAQuad& quad = geometry.edgeAAQuad();
198         if (quad.edgeFlags() == AAFlags::kNone) {
199             // If all edges are non-AA, there won't be any insetting. This allows completely non-AA
200             // quads to use the fill triangles for simpler fragment shader work.
201             return false;
202         } else if (quad.isRect() && quad.edgeFlags() == AAFlags::kAll) {
203             return opposite_insets_intersect(quad.bounds(), 0.f, aaRadius);
204         } else {
205             // Quads with mixed AA edges are tiles where non-AA edges must seam perfectly together.
206             // If we were to inset along just the axis with AA at a corner, two adjacent quads could
207             // arrive at slightly different inset coordinates and then we wouldn't have a perfect
208             // mesh. Forcing insets to snap to the center means all non-AA edges are formed solely
209             // by the original quad coordinates and should seam perfectly assuming perfect input.
210             // The only downside to this is the fill triangles cannot be used since they would
211             // partially extend into the coverage ramp from adjacent AA edges.
212             return true;
213         }
214     } else {
215         const Shape& shape = geometry.shape();
216         if (shape.isLine()) {
217             return strokeRadius <= aaRadius;
218         } else if (shape.isRect()) {
219             return opposite_insets_intersect(shape.rect(), strokeRadius, aaRadius);
220         } else {
221             SkASSERT(shape.isRRect());
222             return opposite_insets_intersect(shape.rrect(), strokeRadius, aaRadius);
223         }
224     }
225 }
226 
is_clockwise(const EdgeAAQuad & quad)227 static bool is_clockwise(const EdgeAAQuad& quad) {
228     if (quad.isRect()) {
229         return true; // by construction, these are always locally clockwise
230     }
231 
232     // This assumes that each corner has a consistent winding, which is the case for convex inputs,
233     // which is an assumption of the per-edge AA API. Check the sign of cross product between the
234     // first two edges.
235     const skvx::float4& xs = quad.xs();
236     const skvx::float4& ys = quad.ys();
237 
238     float winding = (xs[0] - xs[3])*(ys[1] - ys[0]) - (ys[0] - ys[3])*(xs[1] - xs[0]);
239     if (winding == 0.f) {
240         // The input possibly forms a triangle with duplicate vertices, so check the opposite corner
241         winding = (xs[2] - xs[1])*(ys[3] - ys[2]) - (ys[2] - ys[1])*(xs[3] - xs[2]);
242     }
243 
244     // At this point if winding is < 0, the quad's vertices are CCW. If it's still 0, the vertices
245     // form a line, in which case the vertex shader constructs a correct CW winding. Otherwise,
246     // the quad or triangle vertices produce a positive winding and are CW.
247     return winding >= 0.f;
248 }
249 
quad_center(const EdgeAAQuad & quad)250 static skvx::float2 quad_center(const EdgeAAQuad& quad) {
251     // The center of the bounding box is *not* a good center to use. Take the average of the
252     // four points instead (which is slightly biased if they form a triangle, but still okay).
253     return skvx::float2(dot(quad.xs(), skvx::float4(0.25f)),
254                         dot(quad.ys(), skvx::float4(0.25f)));
255 }
256 
257 // Represents the per-vertex attributes used in each instance.
258 struct Vertex {
259     SkV2 fPosition;
260     SkV2 fNormal;
261     float fNormalScale;
262     float fCenterWeight;
263 };
264 
265 // Allowed values for the center weight instance value (selected at record time based on style
266 // and transform), and are defined such that when (insance-weight > vertex-weight) is true, the
267 // vertex should be snapped to the center instead of its regular calculation.
268 static constexpr float kSolidInterior = 1.f;
269 static constexpr float kStrokeInterior = 0.f;
270 static constexpr float kFilledStrokeInterior = -1.f;
271 
272 // Special value for local AA radius to signal when the self-intersections of a stroke interior
273 // need extra calculations in the vertex shader.
274 static constexpr float kComplexAAInsets = -1.f;
275 
276 static constexpr int kCornerVertexCount = 9; // sk_VertexID is divided by this in SkSL
277 static constexpr int kVertexCount = 4 * kCornerVertexCount;
278 static constexpr int kIndexCount = 69;
279 
write_index_buffer(VertexWriter writer)280 static void write_index_buffer(VertexWriter writer) {
281     static constexpr uint16_t kTL = 0 * kCornerVertexCount;
282     static constexpr uint16_t kTR = 1 * kCornerVertexCount;
283     static constexpr uint16_t kBR = 2 * kCornerVertexCount;
284     static constexpr uint16_t kBL = 3 * kCornerVertexCount;
285 
286     static const uint16_t kIndices[kIndexCount] = {
287         // Exterior AA ramp outset
288         kTL+0,kTL+4,kTL+1,kTL+5,kTL+2,kTL+3,kTL+5,
289         kTR+0,kTR+4,kTR+1,kTR+5,kTR+2,kTR+3,kTR+5,
290         kBR+0,kBR+4,kBR+1,kBR+5,kBR+2,kBR+3,kBR+5,
291         kBL+0,kBL+4,kBL+1,kBL+5,kBL+2,kBL+3,kBL+5,
292         kTL+0,kTL+4, // close and jump to next strip
293         // Outer to inner edges
294         kTL+4,kTL+6,kTL+5,kTL+7,
295         kTR+4,kTR+6,kTR+5,kTR+7,
296         kBR+4,kBR+6,kBR+5,kBR+7,
297         kBL+4,kBL+6,kBL+5,kBL+7,
298         kTL+4,kTL+6, // close and jump to next strip
299         // Fill triangles
300         kTL+6,kTL+8,kTL+7, kTL+7,kTR+8,
301         kTR+6,kTR+8,kTR+7, kTR+7,kBR+8,
302         kBR+6,kBR+8,kBR+7, kBR+7,kBL+8,
303         kBL+6,kBL+8,kBL+7, kBL+7,kTL+8,
304         kTL+6 // close
305     };
306 
307     if (writer) {
308         writer << kIndices;
309     } // otherwise static buffer creation failed, so do nothing; Context initialization will fail.
310 }
311 
write_vertex_buffer(VertexWriter writer)312 static void write_vertex_buffer(VertexWriter writer) {
313     // Allowed values for the normal scale attribute. +1 signals a device-space outset along the
314     // normal away from the outer edge of the stroke. 0 signals no outset, but placed on the outer
315     // edge of the stroke. -1 signals a local inset along the normal from the inner edge.
316     static constexpr float kOutset = 1.0;
317     static constexpr float kInset  = -1.0;
318 
319     static constexpr float kCenter = 1.f; // "true" as a float
320 
321     // Zero, but named this way to help call out non-zero parameters.
322     static constexpr float _______ = 0.f;
323 
324     static constexpr float kHR2 = 0.5f * SK_FloatSqrt2; // "half root 2"
325 
326     // This template is repeated 4 times in the vertex buffer, for each of the four corners.
327     // The vertex ID is used to lookup per-corner instance properties such as corner radii or
328     // positions, but otherwise this vertex data produces a consistent clockwise mesh from
329     // TL -> TR -> BR -> BL.
330     static constexpr Vertex kCornerTemplate[kCornerVertexCount] = {
331         // Device-space AA outsets from outer curve
332         { {1.0f, 0.0f}, {1.0f, 0.0f}, kOutset, _______ },
333         { {1.0f, 0.0f}, {kHR2, kHR2}, kOutset, _______ },
334         { {0.0f, 1.0f}, {kHR2, kHR2}, kOutset, _______ },
335         { {0.0f, 1.0f}, {0.0f, 1.0f}, kOutset, _______ },
336 
337         // Outer anchors (no local or device-space normal outset)
338         { {1.0f, 0.0f}, {kHR2, kHR2}, _______, _______ },
339         { {0.0f, 1.0f}, {kHR2, kHR2}, _______, _______ },
340 
341         // Inner curve (with additional AA inset in the common case)
342         { {1.0f, 0.0f}, {1.0f, 0.0f}, kInset, _______ },
343         { {0.0f, 1.0f}, {0.0f, 1.0f}, kInset, _______ },
344 
345         // Center filling vertices (equal to inner AA insets unless 'center' triggers a fill).
346         // TODO: On backends that support "cull" distances (and with SkSL support), these vertices
347         // and their corresponding triangles can be completely removed. The inset vertices can
348         // set their cull distance value to cause all filling triangles to be discarded or not
349         // depending on the instance's style.
350         { {1.0f, 0.0f}, {1.0f, 0.0f}, kInset,  kCenter },
351     };
352 
353     if (writer) {
354         writer << kCornerTemplate  // TL
355                << kCornerTemplate  // TR
356                << kCornerTemplate  // BR
357                << kCornerTemplate; // BL
358     } // otherwise static buffer creation failed, so do nothing; Context initialization will fail.
359 }
360 
AnalyticRRectRenderStep(StaticBufferManager * bufferManager)361 AnalyticRRectRenderStep::AnalyticRRectRenderStep(StaticBufferManager* bufferManager)
362         : RenderStep(RenderStepID::kAnalyticRRect,
363                      Flags::kPerformsShading | Flags::kEmitsCoverage | Flags::kOutsetBoundsForAA |
364                      Flags::kUseNonAAInnerFill,
365                      /*uniforms=*/{},
366                      PrimitiveType::kTriangleStrip,
367                      kDirectDepthGreaterPass,
368                      /*vertexAttrs=*/{
369                             {"position", VertexAttribType::kFloat2, SkSLType::kFloat2},
370                             {"normal", VertexAttribType::kFloat2, SkSLType::kFloat2},
371                             // TODO: These values are all +1/0/-1, or +1/0, so could be packed
372                             // much more densely than as three floats.
373                             {"normalScale", VertexAttribType::kFloat, SkSLType::kFloat},
374                             {"centerWeight", VertexAttribType::kFloat, SkSLType::kFloat}
375                      },
376                      /*instanceAttrs=*/
377                             {{"xRadiiOrFlags", VertexAttribType::kFloat4, SkSLType::kFloat4},
378                              {"radiiOrQuadXs", VertexAttribType::kFloat4, SkSLType::kFloat4},
379                              {"ltrbOrQuadYs", VertexAttribType::kFloat4, SkSLType::kFloat4},
380                              // XY stores center of rrect in local coords. Z and W store values to
381                              // control interior fill behavior. Z can be -1, 0, or 1:
382                              //   -1: A stroked interior where AA insets overlap, but isn't solid.
383                              //    0: A stroked interior with no complications.
384                              //    1: A solid interior (fill or sufficiently large stroke width).
385                              // W specifies the size of the AA inset if it's >= 0, or signals that
386                              // the inner curves intersect in a complex manner (rare).
387                              {"center", VertexAttribType::kFloat4, SkSLType::kFloat4},
388 
389                              // TODO: pack depth and ssbo index into one 32-bit attribute, if we can
390                              // go without needing both render step and paint ssbo index attributes.
391                              {"depth", VertexAttribType::kFloat, SkSLType::kFloat},
392                              {"ssboIndices", VertexAttribType::kUInt2, SkSLType::kUInt2},
393 
394                              {"mat0", VertexAttribType::kFloat3, SkSLType::kFloat3},
395                              {"mat1", VertexAttribType::kFloat3, SkSLType::kFloat3},
396                              {"mat2", VertexAttribType::kFloat3, SkSLType::kFloat3}},
397                      /*varyings=*/{
398                              // TODO: If the inverse transform is part of the draw's SSBO, we can
399                              // reconstruct the Jacobian in the fragment shader using the existing
400                              // local coordinates varying
401                              {"jacobian", SkSLType::kFloat4}, // float2x2
402                              // Distance to LTRB edges of unstroked shape. Depending on
403                              // 'perPixelControl' these will either be local or device-space values.
404                              {"edgeDistances", SkSLType::kFloat4}, // distance to LTRB edges
405                              // TODO: These are constant for all fragments for a given instance,
406                              // could we store them in the draw's SSBO?
407                              {"xRadii", SkSLType::kFloat4},
408                              {"yRadii", SkSLType::kFloat4},
409                              // Matches the StrokeStyle struct (X is radius, Y < 0 is round join,
410                              // Y = 0 is bevel, Y > 0 is miter join).
411                              // TODO: These could easily be considered part of the draw's uniforms.
412                              {"strokeParams", SkSLType::kFloat2},
413                              // 'perPixelControl' is a tightly packed description of how to
414                              // evaluate the possible edges that influence coverage in a pixel.
415                              // The decision points and encoded values are spread across X and Y
416                              // so that they are consistent regardless of whether or not MSAA is
417                              // used and does not require centroid sampling.
418                              //
419                              // The signs of values are used to determine the type of coverage to
420                              // calculate in the fragment shader and depending on the state, extra
421                              // varying state is encoded in the fields:
422                              //  - A positive X value overrides all per-pixel coverage calculations
423                              //    and sets the pixel to full coverage. Y is ignored in this case.
424                              //  - A zero X value represents a solid interior shape.
425                              //  - X much less than 0 represents bidirectional coverage for a
426                              //    stroke, using a sufficiently negative value to avoid
427                              //    extrapolation from fill triangles. For actual shapes with
428                              //    bidirectional coverage, the fill triangles are zero area.
429                              //
430                              //  - Y much greater than 0 takes precedence over the latter two X
431                              //    rules and signals that 'edgeDistances' holds device-space values
432                              //    and does not require additional per-pixel calculations. The
433                              //    coverage scale is encoded as (1+scale*w) and the bias is
434                              //    reconstructed from that. X is always 0 for non-fill triangles
435                              //    since device-space edge distance is only used for solid interiors
436                              //  - Otherwise, any negative Y value represents an additional
437                              //    reduction in coverage due to a device-space outset. It is clamped
438                              //    below 0 to avoid adding coverage from extrapolation.
439                              {"perPixelControl", SkSLType::kFloat2},
440                      }) {
441     // Initialize the static buffers we'll use when recording draw calls.
442     // NOTE: Each instance of this RenderStep gets its own copy of the data. Since there should only
443     // ever be one AnalyticRRectRenderStep at a time, this shouldn't be an issue.
444     write_vertex_buffer(bufferManager->getVertexWriter(sizeof(Vertex) * kVertexCount,
445                                                        &fVertexBuffer));
446     write_index_buffer(bufferManager->getIndexWriter(sizeof(uint16_t) * kIndexCount,
447                                                      &fIndexBuffer));
448 }
449 
~AnalyticRRectRenderStep()450 AnalyticRRectRenderStep::~AnalyticRRectRenderStep() {}
451 
vertexSkSL() const452 std::string AnalyticRRectRenderStep::vertexSkSL() const {
453     // Returns the body of a vertex function, which must define a float4 devPosition variable and
454     // must write to an already-defined float2 stepLocalCoords variable.
455     return "float4 devPosition = analytic_rrect_vertex_fn("
456                    // Vertex Attributes
457                    "position, normal, normalScale, centerWeight, "
458                    // Instance Attributes
459                    "xRadiiOrFlags, radiiOrQuadXs, ltrbOrQuadYs, center, depth, "
460                    "float3x3(mat0, mat1, mat2), "
461                    // Varyings
462                    "jacobian, edgeDistances, xRadii, yRadii, strokeParams, perPixelControl, "
463                    // Render Step
464                    "stepLocalCoords);\n";
465 }
466 
fragmentCoverageSkSL() const467 const char* AnalyticRRectRenderStep::fragmentCoverageSkSL() const {
468     // The returned SkSL must write its coverage into a 'half4 outputCoverage' variable (defined in
469     // the calling code) with the actual coverage splatted out into all four channels.
470     return "outputCoverage = analytic_rrect_coverage_fn(sk_FragCoord, "
471                                                        "jacobian, "
472                                                        "edgeDistances, "
473                                                        "xRadii, "
474                                                        "yRadii, "
475                                                        "strokeParams, "
476                                                        "perPixelControl);";
477 }
478 
writeVertices(DrawWriter * writer,const DrawParams & params,skvx::uint2 ssboIndices) const479 void AnalyticRRectRenderStep::writeVertices(DrawWriter* writer,
480                                             const DrawParams& params,
481                                             skvx::uint2 ssboIndices) const {
482     SkASSERT(params.geometry().isShape() || params.geometry().isEdgeAAQuad());
483 
484     DrawWriter::Instances instance{*writer, fVertexBuffer, fIndexBuffer, kIndexCount};
485     auto vw = instance.append(1);
486 
487     // The bounds of a rect is the rect, and the bounds of a rrect is tight (== SkRRect::getRect()).
488     Rect bounds = params.geometry().bounds();
489 
490     // aaRadius will be set to a negative value to signal a complex self-intersection that has to
491     // be calculated in the vertex shader.
492     float aaRadius = params.transform().localAARadius(bounds);
493     float strokeInset = 0.f;
494     float centerWeight = kSolidInterior;
495 
496     if (params.isStroke()) {
497          // EdgeAAQuads are not stroked so we know it's a Shape, but we support rects, rrects, and
498          // lines that all need to be converted to the same form.
499         const Shape& shape = params.geometry().shape();
500 
501         SkASSERT(params.strokeStyle().halfWidth() >= 0.f);
502         SkASSERT(shape.isRect() || shape.isLine() || params.strokeStyle().halfWidth() == 0.f ||
503                  (shape.isRRect() && SkRRectPriv::AllCornersCircular(shape.rrect())));
504 
505         float strokeRadius = params.strokeStyle().halfWidth();
506 
507         skvx::float2 size = shape.isLine() ? skvx::float2(length(shape.p1() - shape.p0()), 0.f)
508                                            : bounds.size(); // rect or [r]rect
509 
510         skvx::float2 innerGap = size - 2.f * params.strokeStyle().halfWidth();
511         if (any(innerGap <= 0.f) && strokeRadius > 0.f) {
512             // AA inset intersections are measured from the *outset* and remain marked as "solid"
513             strokeInset = -strokeRadius;
514         } else {
515             // This will be upgraded to kFilledStrokeInterior if insets intersect
516             centerWeight = kStrokeInterior;
517             strokeInset = strokeRadius;
518         }
519 
520         skvx::float4 xRadii = shape.isRRect() ? load_x_radii(shape.rrect()) : skvx::float4(0.f);
521         if (strokeRadius > 0.f || shape.isLine()) {
522             // Regular strokes only need to upload 4 corner radii; hairline lines can be uploaded in
523             // the same manner since it has no real corner radii.
524             float joinStyle = params.strokeStyle().joinLimit();
525             float lineFlag = shape.isLine() ? 1.f : 0.f;
526             auto empty = size == 0.f;
527 
528             // Points and lines produce caps instead of joins. However, the capped geometry is
529             // visually equivalent to a joined, stroked [r]rect of the paired join style.
530             if (shape.isLine() || all(empty)) {
531                 // However, butt-cap points are defined not to produce any geometry, so that combo
532                 // should have been rejected earlier.
533                 SkASSERT(shape.isLine() || params.strokeStyle().cap() != SkPaint::kButt_Cap);
534                 switch(params.strokeStyle().cap()) {
535                     case SkPaint::kRound_Cap:  joinStyle = -1.f; break; // round cap == round join
536                     case SkPaint::kButt_Cap:   joinStyle =  0.f; break; // butt cap == bevel join
537                     case SkPaint::kSquare_Cap: joinStyle =  1.f; break; // square cap == miter join
538                 }
539             } else if (params.strokeStyle().isMiterJoin()) {
540                 // Normal corners are 90-degrees so become beveled if the miter limit is < sqrt(2).
541                 // If the [r]rect has a width or height of 0, the corners are actually 180-degrees,
542                 // so the must always be beveled (or, equivalently, butt-capped).
543                 if (params.strokeStyle().miterLimit() < SK_ScalarSqrt2 || any(empty)) {
544                     joinStyle = 0.f; // == bevel (or butt if width or height are zero)
545                 } else {
546                     // Discard actual miter limit because a 90-degree corner never exceeds it.
547                     joinStyle = 1.f;
548                 }
549             } // else no join style correction needed for non-empty geometry or round joins
550 
551             // Write a negative value outside [-1, 0] to signal a stroked shape, the line flag, then
552             // the style params, followed by corner radii and coords.
553             vw << -2.f << lineFlag << strokeRadius << joinStyle << xRadii
554                << (shape.isLine() ? shape.line() : bounds.ltrb());
555         } else {
556             // Write -2 - cornerRadii to encode the X radii in such a way to trigger stroking but
557             // guarantee the 2nd field is non-zero to signal hairline. Then we upload Y radii as
558             // well to allow for elliptical hairlines.
559             skvx::float4 yRadii = shape.isRRect() ? load_y_radii(shape.rrect()) : skvx::float4(0.f);
560             vw << (-2.f - xRadii) << yRadii << bounds.ltrb();
561         }
562     } else {
563         // Empty fills should not have been recorded at all.
564         SkASSERT(!bounds.isEmptyNegativeOrNaN());
565 
566         if (params.geometry().isEdgeAAQuad()) {
567             // NOTE: If quad.isRect() && quad.edgeFlags() == kAll, the written data is identical to
568             // Shape.isRect() case below.
569             const EdgeAAQuad& quad = params.geometry().edgeAAQuad();
570 
571             // If all edges are non-AA, set localAARadius to 0 so that the fill triangles cover the
572             // entire shape. Otherwise leave it as-is for the full AA rect case; in the event it's
573             // mixed-AA or a quad, it'll be converted to complex insets down below.
574             if (quad.edgeFlags() == EdgeAAQuad::Flags::kNone) {
575                 aaRadius = 0.f;
576             }
577 
578             // -1 for AA on, 0 for AA off
579             auto edgeSigns = skvx::float4{quad.edgeFlags() & AAFlags::kLeft   ? -1.f : 0.f,
580                                           quad.edgeFlags() & AAFlags::kTop    ? -1.f : 0.f,
581                                           quad.edgeFlags() & AAFlags::kRight  ? -1.f : 0.f,
582                                           quad.edgeFlags() & AAFlags::kBottom ? -1.f : 0.f};
583 
584             // The vertex shader expects points to be in clockwise order. EdgeAAQuad is the only
585             // shape that *might* have counter-clockwise input.
586             if (is_clockwise(quad)) {
587                 vw << edgeSigns << quad.xs() << quad.ys();
588             } else {
589                 vw << skvx::shuffle<2,1,0,3>(edgeSigns)  // swap left and right AA bits
590                    << skvx::shuffle<1,0,3,2>(quad.xs())  // swap TL with TR, and BL with BR
591                    << skvx::shuffle<1,0,3,2>(quad.ys()); //   ""
592             }
593         } else {
594             const Shape& shape = params.geometry().shape();
595             // Filled lines are empty by definition, so they shouldn't have been recorded
596             SkASSERT(!shape.isLine());
597 
598             if (shape.isRect() || (shape.isRRect() && shape.rrect().isRect())) {
599                 // Rectangles (or rectangles embedded in an SkRRect) are converted to the
600                 // quadrilateral case, but with all edges anti-aliased (== -1).
601                 skvx::float4 ltrb = bounds.ltrb();
602                 vw << /*edge flags*/ skvx::float4(-1.f)
603                    << /*xs*/ skvx::shuffle<0,2,2,0>(ltrb)
604                    << /*ys*/ skvx::shuffle<1,1,3,3>(ltrb);
605             } else {
606                 // A filled rounded rectangle, so make sure at least one corner radii > 0 or the
607                 // shader won't detect it as a rounded rect.
608                 SkASSERT(any(load_x_radii(shape.rrect()) > 0.f));
609 
610                 vw << load_x_radii(shape.rrect()) << load_y_radii(shape.rrect()) << bounds.ltrb();
611             }
612         }
613     }
614 
615     if (opposite_insets_intersect(params.geometry(), strokeInset, aaRadius)) {
616         aaRadius = kComplexAAInsets;
617         if (centerWeight == kStrokeInterior) {
618             centerWeight = kFilledStrokeInterior;
619         }
620     }
621 
622     // All instance types share the remaining instance attribute definitions
623     const SkM44& m = params.transform().matrix();
624     auto center = params.geometry().isEdgeAAQuad() ? quad_center(params.geometry().edgeAAQuad())
625                                                    : bounds.center();
626     vw << center << centerWeight << aaRadius
627        << params.order().depthAsFloat()
628        << ssboIndices
629        << m.rc(0,0) << m.rc(1,0) << m.rc(3,0)  // mat0
630        << m.rc(0,1) << m.rc(1,1) << m.rc(3,1)  // mat1
631        << m.rc(0,3) << m.rc(1,3) << m.rc(3,3); // mat2
632 }
633 
writeUniformsAndTextures(const DrawParams &,PipelineDataGatherer *) const634 void AnalyticRRectRenderStep::writeUniformsAndTextures(const DrawParams&,
635                                                        PipelineDataGatherer*) const {
636     // All data is uploaded as instance attributes, so no uniforms are needed.
637 }
638 
639 }  // namespace skgpu::graphite
640