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