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