xref: /aosp_15_r20/external/skia/src/gpu/tessellate/PatchWriter.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1*c8dee2aaSAndroid Build Coastguard Worker /*
2*c8dee2aaSAndroid Build Coastguard Worker  * Copyright 2021 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 #ifndef skgpu_tessellate_PatchWriter_DEFINED
9*c8dee2aaSAndroid Build Coastguard Worker #define skgpu_tessellate_PatchWriter_DEFINED
10*c8dee2aaSAndroid Build Coastguard Worker 
11*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkAlphaType.h"
12*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkScalar.h"
13*c8dee2aaSAndroid Build Coastguard Worker #include "include/core/SkTypes.h"
14*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/SkColorData.h"
15*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkDebug.h"
16*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkFloatingPoint.h"
17*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkPoint_impl.h"
18*c8dee2aaSAndroid Build Coastguard Worker #include "include/private/base/SkTemplates.h"
19*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkUtils.h"
20*c8dee2aaSAndroid Build Coastguard Worker #include "src/base/SkVx.h"
21*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/BufferWriter.h"
22*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/LinearTolerances.h"
23*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h"
24*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/Tessellation.h"
25*c8dee2aaSAndroid Build Coastguard Worker #include "src/gpu/tessellate/WangsFormula.h"
26*c8dee2aaSAndroid Build Coastguard Worker 
27*c8dee2aaSAndroid Build Coastguard Worker #include <algorithm>
28*c8dee2aaSAndroid Build Coastguard Worker #include <cstdint>
29*c8dee2aaSAndroid Build Coastguard Worker #include <cstring>
30*c8dee2aaSAndroid Build Coastguard Worker #include <math.h>
31*c8dee2aaSAndroid Build Coastguard Worker #include <tuple>
32*c8dee2aaSAndroid Build Coastguard Worker #include <type_traits>
33*c8dee2aaSAndroid Build Coastguard Worker #include <utility>
34*c8dee2aaSAndroid Build Coastguard Worker #include <variant>
35*c8dee2aaSAndroid Build Coastguard Worker 
36*c8dee2aaSAndroid Build Coastguard Worker namespace skgpu::tess {
37*c8dee2aaSAndroid Build Coastguard Worker 
38*c8dee2aaSAndroid Build Coastguard Worker /**
39*c8dee2aaSAndroid Build Coastguard Worker  * PatchWriter writes out tessellation patches, formatted with their specific attribs, to a GPU
40*c8dee2aaSAndroid Build Coastguard Worker  * buffer.
41*c8dee2aaSAndroid Build Coastguard Worker  *
42*c8dee2aaSAndroid Build Coastguard Worker  * PatchWriter is a template class that takes traits to configure both its compile-time and runtime
43*c8dee2aaSAndroid Build Coastguard Worker  * behavior for the different tessellation rendering algorithms and GPU backends. The complexity of
44*c8dee2aaSAndroid Build Coastguard Worker  * this system is worthwhile because the attribute writing operations and math already require
45*c8dee2aaSAndroid Build Coastguard Worker  * heavy inlining for performance, and the algorithmic variations tend to only differ slightly, but
46*c8dee2aaSAndroid Build Coastguard Worker  * do so in the inner most loops. Additionally, Graphite and Ganesh use the same fundamental
47*c8dee2aaSAndroid Build Coastguard Worker  * algorithms, but Graphite's architecture and higher required hardware level mean that its
48*c8dee2aaSAndroid Build Coastguard Worker  * attribute configurations can be determined entirely at compile time.
49*c8dee2aaSAndroid Build Coastguard Worker  *
50*c8dee2aaSAndroid Build Coastguard Worker  * Traits are specified in PatchWriter's single var-args template pack. Traits come in two main
51*c8dee2aaSAndroid Build Coastguard Worker  * categories: PatchAttribs configuration and feature/processing configuration. A given PatchAttrib
52*c8dee2aaSAndroid Build Coastguard Worker  * can be always enabled, enabled at runtime, or always disabled. A feature can be either enabled
53*c8dee2aaSAndroid Build Coastguard Worker  * or disabled and are coupled more closely with the control points of the curve. Across the two
54*c8dee2aaSAndroid Build Coastguard Worker  * GPU backends and different path rendering strategies, a "patch" has the following structure:
55*c8dee2aaSAndroid Build Coastguard Worker  *
56*c8dee2aaSAndroid Build Coastguard Worker  *   - 4 control points (8 floats total) defining the curve's geometry
57*c8dee2aaSAndroid Build Coastguard Worker  *      - quadratic curves are converted to equivalent cubics on the CPU during writing
58*c8dee2aaSAndroid Build Coastguard Worker  *      - conic curves store {w, inf} in their last control point
59*c8dee2aaSAndroid Build Coastguard Worker  *      - triangles store {inf, inf} in their last control point
60*c8dee2aaSAndroid Build Coastguard Worker  *      - everything else is presumed to be a cubic defined by all 4 control points
61*c8dee2aaSAndroid Build Coastguard Worker  *   - Enabled PatchAttrib values, constant for the entire instance
62*c8dee2aaSAndroid Build Coastguard Worker  *      - layout is identical to PatchAttrib's definition, skipping disabled attribs
63*c8dee2aaSAndroid Build Coastguard Worker  *      - attribs can be enabled/disabled at runtime by building a mask of attrib values
64*c8dee2aaSAndroid Build Coastguard Worker  *
65*c8dee2aaSAndroid Build Coastguard Worker  * Currently PatchWriter supports the following traits:
66*c8dee2aaSAndroid Build Coastguard Worker  *   - Required<PatchAttrib>
67*c8dee2aaSAndroid Build Coastguard Worker  *   - Optional<PatchAttrib>
68*c8dee2aaSAndroid Build Coastguard Worker  *   - TrackJoinControlPoints
69*c8dee2aaSAndroid Build Coastguard Worker  *   - AddTrianglesWhenChopping
70*c8dee2aaSAndroid Build Coastguard Worker  *   - DiscardFlatCurves
71*c8dee2aaSAndroid Build Coastguard Worker  *
72*c8dee2aaSAndroid Build Coastguard Worker  * In addition to variable traits, PatchWriter's first template argument defines the type used for
73*c8dee2aaSAndroid Build Coastguard Worker  * allocating the GPU instance data. The templated "PatchAllocator" can be any type that provides:
74*c8dee2aaSAndroid Build Coastguard Worker  *    // A GPU-backed vertex writer for a single instance worth of data. The provided
75*c8dee2aaSAndroid Build Coastguard Worker  *    // LinearTolerances value represents the tolerances for the curve that will be written to the
76*c8dee2aaSAndroid Build Coastguard Worker  *    // returned vertex space.
77*c8dee2aaSAndroid Build Coastguard Worker  *    skgpu::VertexWriter append(const LinearTolerances&);
78*c8dee2aaSAndroid Build Coastguard Worker  *
79*c8dee2aaSAndroid Build Coastguard Worker  * Additionally, it must have a constructor that takes the stride as its first argument.
80*c8dee2aaSAndroid Build Coastguard Worker  * PatchWriter forwards any additional constructor args from its ctor to the allocator after
81*c8dee2aaSAndroid Build Coastguard Worker  * computing the necessary stride for its PatchAttribs configuration.
82*c8dee2aaSAndroid Build Coastguard Worker  */
83*c8dee2aaSAndroid Build Coastguard Worker 
84*c8dee2aaSAndroid Build Coastguard Worker // *** TRAITS ***
85*c8dee2aaSAndroid Build Coastguard Worker 
86*c8dee2aaSAndroid Build Coastguard Worker // Marks a PatchAttrib is enabled at compile time, i.e. it must always be set and will always be
87*c8dee2aaSAndroid Build Coastguard Worker // written to each patch's instance data. If present, will assert if the runtime attribs do not fit.
88*c8dee2aaSAndroid Build Coastguard Worker template <PatchAttribs A> struct Required {};
89*c8dee2aaSAndroid Build Coastguard Worker // Marks a PatchAttrib as supported, i.e. it can be enabled or disabled at runtime. Optional<A> is
90*c8dee2aaSAndroid Build Coastguard Worker // overridden by Required<A>. If neither Required<A> nor Optional<A> are in a PatchWriter's trait
91*c8dee2aaSAndroid Build Coastguard Worker // list, then the attrib is disabled at compile time and it will assert if the runtime attribs
92*c8dee2aaSAndroid Build Coastguard Worker // attempt to enable it.
93*c8dee2aaSAndroid Build Coastguard Worker template <PatchAttribs A> struct Optional {};
94*c8dee2aaSAndroid Build Coastguard Worker 
95*c8dee2aaSAndroid Build Coastguard Worker // Enables tracking of the kJoinControlPointAttrib based on control points of the previously
96*c8dee2aaSAndroid Build Coastguard Worker // written patch (automatically taking into account curve chopping). When a patch is first written
97*c8dee2aaSAndroid Build Coastguard Worker // (and there is no prior patch to define the join control point), the PatchWriter automatically
98*c8dee2aaSAndroid Build Coastguard Worker // records the patch to a temporary buffer--sans join--until writeDeferredStrokePatch() is called,
99*c8dee2aaSAndroid Build Coastguard Worker // filling in the now-defined join control point.
100*c8dee2aaSAndroid Build Coastguard Worker //
101*c8dee2aaSAndroid Build Coastguard Worker // This feature must be paired with Required<PatchAttribs::kJoinControlPoint>
102*c8dee2aaSAndroid Build Coastguard Worker struct TrackJoinControlPoints {};
103*c8dee2aaSAndroid Build Coastguard Worker 
104*c8dee2aaSAndroid Build Coastguard Worker // Write additional triangular patches to fill the resulting empty area when a curve is chopped.
105*c8dee2aaSAndroid Build Coastguard Worker // Normally, the patch geometry covers the curve defined by its control points, up to the implicitly
106*c8dee2aaSAndroid Build Coastguard Worker // closing edge between its first and last control points. When a curve is chopped to fit within
107*c8dee2aaSAndroid Build Coastguard Worker // the maximum segment count, the resulting space between the original closing edge and new closing
108*c8dee2aaSAndroid Build Coastguard Worker // edges is not filled, unless some mechanism of the shader makes it so (e.g. a fan point or
109*c8dee2aaSAndroid Build Coastguard Worker // stroking).
110*c8dee2aaSAndroid Build Coastguard Worker //
111*c8dee2aaSAndroid Build Coastguard Worker // This feature enables automatically writing triangular patches to fill this empty space when a
112*c8dee2aaSAndroid Build Coastguard Worker // curve is chopped.
113*c8dee2aaSAndroid Build Coastguard Worker struct AddTrianglesWhenChopping {};
114*c8dee2aaSAndroid Build Coastguard Worker 
115*c8dee2aaSAndroid Build Coastguard Worker // If a curve requires at most 1 segment to render accurately, it's effectively a straight line.
116*c8dee2aaSAndroid Build Coastguard Worker // This feature turns on automatically ignoring those curves, with the assumption that some other
117*c8dee2aaSAndroid Build Coastguard Worker // render pass will produce equivalent geometry (e.g. middle-out or inner triangulations).
118*c8dee2aaSAndroid Build Coastguard Worker struct DiscardFlatCurves {};
119*c8dee2aaSAndroid Build Coastguard Worker 
120*c8dee2aaSAndroid Build Coastguard Worker // Upload lines as a cubic with {a, a, b, b} for control points, instead of the truly linear cubic
121*c8dee2aaSAndroid Build Coastguard Worker // of {a, 2/3a + 1/3b, 1/3a + 2/3b, b}. Wang's formula will not return an tight lower bound on the
122*c8dee2aaSAndroid Build Coastguard Worker // number of segments in this case, but it's convenient to detect in the vertex shader and assume
123*c8dee2aaSAndroid Build Coastguard Worker // only a single segment is required. This bypasses numerical stability issues in Wang's formula
124*c8dee2aaSAndroid Build Coastguard Worker // when evaluated on the ideal linear cubic for very large control point coordinates. Other curve
125*c8dee2aaSAndroid Build Coastguard Worker // types with large coordinates do not need this treatment since they would be pre-chopped and
126*c8dee2aaSAndroid Build Coastguard Worker // culled to lines.
127*c8dee2aaSAndroid Build Coastguard Worker struct ReplicateLineEndPoints {};
128*c8dee2aaSAndroid Build Coastguard Worker 
129*c8dee2aaSAndroid Build Coastguard Worker // *** PatchWriter internals ***
130*c8dee2aaSAndroid Build Coastguard Worker 
131*c8dee2aaSAndroid Build Coastguard Worker // AttribValue exposes a consistent store and write interface for a PatchAttrib's value while
132*c8dee2aaSAndroid Build Coastguard Worker // abstracting over compile-time enabled, conditionally-enabled, or compile-time disabled attribs.
133*c8dee2aaSAndroid Build Coastguard Worker template <PatchAttribs A, typename T, bool Required, bool Optional>
134*c8dee2aaSAndroid Build Coastguard Worker struct AttribValue {
135*c8dee2aaSAndroid Build Coastguard Worker     using DataType = std::conditional_t<Required, T,
136*c8dee2aaSAndroid Build Coastguard Worker                      std::conditional_t<Optional, std::pair<T, bool>,
137*c8dee2aaSAndroid Build Coastguard Worker                                        /* else */ std::monostate>>;
138*c8dee2aaSAndroid Build Coastguard Worker 
139*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kEnabled = Required || Optional;
140*c8dee2aaSAndroid Build Coastguard Worker 
AttribValueAttribValue141*c8dee2aaSAndroid Build Coastguard Worker     explicit AttribValue(PatchAttribs attribs) : AttribValue(attribs, {}) {}
AttribValueAttribValue142*c8dee2aaSAndroid Build Coastguard Worker     AttribValue(PatchAttribs attribs, const T& t) {
143*c8dee2aaSAndroid Build Coastguard Worker         (void) attribs; // may be unused on release builds
144*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (Required) {
145*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(attribs & A);
146*c8dee2aaSAndroid Build Coastguard Worker         } else if constexpr (Optional) {
147*c8dee2aaSAndroid Build Coastguard Worker             std::get<1>(fV) = attribs & A;
148*c8dee2aaSAndroid Build Coastguard Worker         } else {
149*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!(attribs & A));
150*c8dee2aaSAndroid Build Coastguard Worker         }
151*c8dee2aaSAndroid Build Coastguard Worker         *this = t;
152*c8dee2aaSAndroid Build Coastguard Worker     }
153*c8dee2aaSAndroid Build Coastguard Worker 
154*c8dee2aaSAndroid Build Coastguard Worker     AttribValue& operator=(const T& v) {
155*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (Required) {
156*c8dee2aaSAndroid Build Coastguard Worker             fV = v;
157*c8dee2aaSAndroid Build Coastguard Worker         } else if constexpr (Optional) {
158*c8dee2aaSAndroid Build Coastguard Worker             // for simplicity, store even if disabled and won't be written out to VertexWriter
159*c8dee2aaSAndroid Build Coastguard Worker             std::get<0>(fV) = v;
160*c8dee2aaSAndroid Build Coastguard Worker         } // else ignore for disabled values
161*c8dee2aaSAndroid Build Coastguard Worker         return *this;
162*c8dee2aaSAndroid Build Coastguard Worker     }
163*c8dee2aaSAndroid Build Coastguard Worker 
164*c8dee2aaSAndroid Build Coastguard Worker     DataType fV;
165*c8dee2aaSAndroid Build Coastguard Worker };
166*c8dee2aaSAndroid Build Coastguard Worker 
167*c8dee2aaSAndroid Build Coastguard Worker template <PatchAttribs A, typename T, bool Required, bool Optional>
168*c8dee2aaSAndroid Build Coastguard Worker VertexWriter& operator<<(VertexWriter& w, const AttribValue<A, T, Required, Optional>& v) {
169*c8dee2aaSAndroid Build Coastguard Worker     if constexpr (Required) {
170*c8dee2aaSAndroid Build Coastguard Worker         w << v.fV; // always write
171*c8dee2aaSAndroid Build Coastguard Worker     } else if constexpr (Optional) {
172*c8dee2aaSAndroid Build Coastguard Worker         if (std::get<1>(v.fV)) {
173*c8dee2aaSAndroid Build Coastguard Worker             w << std::get<0>(v.fV); // write if enabled
174*c8dee2aaSAndroid Build Coastguard Worker         }
175*c8dee2aaSAndroid Build Coastguard Worker     } // else never write
176*c8dee2aaSAndroid Build Coastguard Worker     return w;
177*c8dee2aaSAndroid Build Coastguard Worker }
178*c8dee2aaSAndroid Build Coastguard Worker 
179*c8dee2aaSAndroid Build Coastguard Worker // Stores state and deferred patch data when TrackJoinControlPoints is used for a PatchWriter.
180*c8dee2aaSAndroid Build Coastguard Worker template <size_t Stride>
181*c8dee2aaSAndroid Build Coastguard Worker struct PatchStorage {
182*c8dee2aaSAndroid Build Coastguard Worker     float fN_p4    = -1.f; // The parametric segment value to restore on LinearTolerances
183*c8dee2aaSAndroid Build Coastguard Worker     bool  fMustDefer = true;  // True means next patch must be deferred
184*c8dee2aaSAndroid Build Coastguard Worker 
185*c8dee2aaSAndroid Build Coastguard Worker     // Holds an entire patch, except with an undefined join control point.
186*c8dee2aaSAndroid Build Coastguard Worker     char fData[Stride];
187*c8dee2aaSAndroid Build Coastguard Worker 
hasPendingPatchStorage188*c8dee2aaSAndroid Build Coastguard Worker     bool hasPending() const {
189*c8dee2aaSAndroid Build Coastguard Worker         return fN_p4 >= 0.f;
190*c8dee2aaSAndroid Build Coastguard Worker     }
resetPatchStorage191*c8dee2aaSAndroid Build Coastguard Worker     void reset() {
192*c8dee2aaSAndroid Build Coastguard Worker         fN_p4 = -1.f;
193*c8dee2aaSAndroid Build Coastguard Worker         fMustDefer = true;
194*c8dee2aaSAndroid Build Coastguard Worker     }
195*c8dee2aaSAndroid Build Coastguard Worker };
196*c8dee2aaSAndroid Build Coastguard Worker 
197*c8dee2aaSAndroid Build Coastguard Worker // An empty object that has the same constructor signature as MiddleOutPolygonTriangulator, used
198*c8dee2aaSAndroid Build Coastguard Worker // as a stand-in when AddTrianglesWhenChopping is not a defined trait.
199*c8dee2aaSAndroid Build Coastguard Worker struct NullTriangulator {
NullTriangulatorNullTriangulator200*c8dee2aaSAndroid Build Coastguard Worker     NullTriangulator(int, SkPoint) {}
201*c8dee2aaSAndroid Build Coastguard Worker };
202*c8dee2aaSAndroid Build Coastguard Worker 
203*c8dee2aaSAndroid Build Coastguard Worker #define AI SK_ALWAYS_INLINE
204*c8dee2aaSAndroid Build Coastguard Worker #define ENABLE_IF(cond) template <typename Void=void> std::enable_if_t<cond, Void>
205*c8dee2aaSAndroid Build Coastguard Worker 
206*c8dee2aaSAndroid Build Coastguard Worker // *** PatchWriter ***
207*c8dee2aaSAndroid Build Coastguard Worker template <typename PatchAllocator, typename... Traits>
208*c8dee2aaSAndroid Build Coastguard Worker class PatchWriter {
209*c8dee2aaSAndroid Build Coastguard Worker     // Helpers to extract specifics from the template traits pack.
210*c8dee2aaSAndroid Build Coastguard Worker     template <typename F>     struct has_trait  : std::disjunction<std::is_same<F, Traits>...> {};
211*c8dee2aaSAndroid Build Coastguard Worker     template <PatchAttribs A> using  req_attrib = has_trait<Required<A>>;
212*c8dee2aaSAndroid Build Coastguard Worker     template <PatchAttribs A> using  opt_attrib = has_trait<Optional<A>>;
213*c8dee2aaSAndroid Build Coastguard Worker 
214*c8dee2aaSAndroid Build Coastguard Worker     // Enabled features and attribute configuration
215*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kTrackJoinControlPoints   = has_trait<TrackJoinControlPoints>::value;
216*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kAddTrianglesWhenChopping = has_trait<AddTrianglesWhenChopping>::value;
217*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kDiscardFlatCurves        = has_trait<DiscardFlatCurves>::value;
218*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kReplicateLineEndPoints   = has_trait<ReplicateLineEndPoints>::value;
219*c8dee2aaSAndroid Build Coastguard Worker 
220*c8dee2aaSAndroid Build Coastguard Worker     // NOTE: MSVC 19.24 cannot compile constexpr fold expressions referenced in templates, so
221*c8dee2aaSAndroid Build Coastguard Worker     // extract everything into constexpr bool's instead of using `req_attrib` directly, etc. :(
222*c8dee2aaSAndroid Build Coastguard Worker     template <PatchAttribs A, typename T, bool Req/*=req_attrib<A>*/, bool Opt/*=opt_attrib<A>*/>
223*c8dee2aaSAndroid Build Coastguard Worker     using attrib_t = AttribValue<A, T, Req, Opt>;
224*c8dee2aaSAndroid Build Coastguard Worker 
225*c8dee2aaSAndroid Build Coastguard Worker     // TODO: Remove when MSVC compiler is fixed, in favor of `using Name = attrib_t<>` directly.
226*c8dee2aaSAndroid Build Coastguard Worker #define DEF_ATTRIB_TYPE(name, A, T) \
227*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kRequire##name = req_attrib<A>::value; \
228*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kOptional##name = opt_attrib<A>::value; \
229*c8dee2aaSAndroid Build Coastguard Worker     using name = attrib_t<A, T, kRequire##name, kOptional##name>
230*c8dee2aaSAndroid Build Coastguard Worker 
231*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(JoinAttrib,      PatchAttribs::kJoinControlPoint,  SkPoint);
232*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(FanPointAttrib,  PatchAttribs::kFanPoint,          SkPoint);
233*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(StrokeAttrib,    PatchAttribs::kStrokeParams,      StrokeParams);
234*c8dee2aaSAndroid Build Coastguard Worker 
235*c8dee2aaSAndroid Build Coastguard Worker     // kWideColorIfEnabled does not define an attribute, but changes the type of the kColor attrib.
236*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kRequireWideColor  = req_attrib<PatchAttribs::kWideColorIfEnabled>::value;
237*c8dee2aaSAndroid Build Coastguard Worker     static constexpr bool kOptionalWideColor = opt_attrib<PatchAttribs::kWideColorIfEnabled>::value;
238*c8dee2aaSAndroid Build Coastguard Worker     using Color = std::conditional_t<kRequireWideColor,  SkPMColor4f,
239*c8dee2aaSAndroid Build Coastguard Worker                   std::conditional_t<kOptionalWideColor, VertexColor,
240*c8dee2aaSAndroid Build Coastguard Worker                                               /* else */ uint32_t>>;
241*c8dee2aaSAndroid Build Coastguard Worker 
242*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(ColorAttrib,     PatchAttribs::kColor,             Color);
243*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(DepthAttrib,     PatchAttribs::kPaintDepth,        float);
244*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(CurveTypeAttrib, PatchAttribs::kExplicitCurveType, float);
245*c8dee2aaSAndroid Build Coastguard Worker     DEF_ATTRIB_TYPE(SsboIndexAttrib, PatchAttribs::kSsboIndex,         skvx::uint2);
246*c8dee2aaSAndroid Build Coastguard Worker #undef DEF_ATTRIB_TYPE
247*c8dee2aaSAndroid Build Coastguard Worker 
248*c8dee2aaSAndroid Build Coastguard Worker     static constexpr size_t kMaxStride = 4 * sizeof(SkPoint) + // control points
249*c8dee2aaSAndroid Build Coastguard Worker             (JoinAttrib::kEnabled      ? sizeof(SkPoint)                              : 0) +
250*c8dee2aaSAndroid Build Coastguard Worker             (FanPointAttrib::kEnabled  ? sizeof(SkPoint)                              : 0) +
251*c8dee2aaSAndroid Build Coastguard Worker             (StrokeAttrib::kEnabled    ? sizeof(StrokeParams)                         : 0) +
252*c8dee2aaSAndroid Build Coastguard Worker             (ColorAttrib::kEnabled     ? std::min(sizeof(Color), sizeof(SkPMColor4f)) : 0) +
253*c8dee2aaSAndroid Build Coastguard Worker             (DepthAttrib::kEnabled     ? sizeof(float)                                : 0) +
254*c8dee2aaSAndroid Build Coastguard Worker             (CurveTypeAttrib::kEnabled ? sizeof(float)                                : 0) +
255*c8dee2aaSAndroid Build Coastguard Worker             (SsboIndexAttrib::kEnabled ? 2 * sizeof(uint32_t)                         : 0);
256*c8dee2aaSAndroid Build Coastguard Worker 
257*c8dee2aaSAndroid Build Coastguard Worker     // Types that vary depending on the activated features, but do not define the patch data.
258*c8dee2aaSAndroid Build Coastguard Worker     using DeferredPatch = std::conditional_t<kTrackJoinControlPoints,
259*c8dee2aaSAndroid Build Coastguard Worker             PatchStorage<kMaxStride>, std::monostate>;
260*c8dee2aaSAndroid Build Coastguard Worker     using InnerTriangulator = std::conditional_t<kAddTrianglesWhenChopping,
261*c8dee2aaSAndroid Build Coastguard Worker             MiddleOutPolygonTriangulator, NullTriangulator>;
262*c8dee2aaSAndroid Build Coastguard Worker 
263*c8dee2aaSAndroid Build Coastguard Worker     using float2 = skvx::float2;
264*c8dee2aaSAndroid Build Coastguard Worker     using float4 = skvx::float4;
265*c8dee2aaSAndroid Build Coastguard Worker 
266*c8dee2aaSAndroid Build Coastguard Worker     static_assert(!kTrackJoinControlPoints || req_attrib<PatchAttribs::kJoinControlPoint>::value,
267*c8dee2aaSAndroid Build Coastguard Worker                   "Deferred patches and auto-updating joins requires kJoinControlPoint attrib");
268*c8dee2aaSAndroid Build Coastguard Worker public:
269*c8dee2aaSAndroid Build Coastguard Worker     template <typename... Args> // forwarded to PatchAllocator
PatchWriter(PatchAttribs attribs,Args &&...allocArgs)270*c8dee2aaSAndroid Build Coastguard Worker     PatchWriter(PatchAttribs attribs,
271*c8dee2aaSAndroid Build Coastguard Worker                 Args&&... allocArgs)
272*c8dee2aaSAndroid Build Coastguard Worker             : fAttribs(attribs)
273*c8dee2aaSAndroid Build Coastguard Worker             , fPatchAllocator(PatchStride(attribs), std::forward<Args>(allocArgs)...)
274*c8dee2aaSAndroid Build Coastguard Worker             , fJoin(attribs)
275*c8dee2aaSAndroid Build Coastguard Worker             , fFanPoint(attribs)
276*c8dee2aaSAndroid Build Coastguard Worker             , fStrokeParams(attribs)
277*c8dee2aaSAndroid Build Coastguard Worker             , fColor(attribs)
278*c8dee2aaSAndroid Build Coastguard Worker             , fDepth(attribs)
279*c8dee2aaSAndroid Build Coastguard Worker             , fSsboIndex(attribs) {
280*c8dee2aaSAndroid Build Coastguard Worker         // Explicit curve types are provided on the writePatch signature, and not a field of
281*c8dee2aaSAndroid Build Coastguard Worker         // PatchWriter, so initialize one in the ctor to validate the provided runtime attribs.
282*c8dee2aaSAndroid Build Coastguard Worker         SkDEBUGCODE((void) CurveTypeAttrib(attribs);)
283*c8dee2aaSAndroid Build Coastguard Worker         // Validate the kWideColorIfEnabled attribute variant flag as well
284*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
285*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(attribs & PatchAttribs::kWideColorIfEnabled);    // required
286*c8dee2aaSAndroid Build Coastguard Worker         } else if constexpr (!opt_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
287*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!(attribs & PatchAttribs::kWideColorIfEnabled)); // disabled
288*c8dee2aaSAndroid Build Coastguard Worker         }
289*c8dee2aaSAndroid Build Coastguard Worker     }
290*c8dee2aaSAndroid Build Coastguard Worker 
~PatchWriter()291*c8dee2aaSAndroid Build Coastguard Worker     ~PatchWriter() {
292*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kTrackJoinControlPoints) {
293*c8dee2aaSAndroid Build Coastguard Worker             // flush any pending patch
294*c8dee2aaSAndroid Build Coastguard Worker             this->writeDeferredStrokePatch();
295*c8dee2aaSAndroid Build Coastguard Worker         }
296*c8dee2aaSAndroid Build Coastguard Worker     }
297*c8dee2aaSAndroid Build Coastguard Worker 
attribs()298*c8dee2aaSAndroid Build Coastguard Worker     PatchAttribs attribs() const { return fAttribs; }
299*c8dee2aaSAndroid Build Coastguard Worker 
300*c8dee2aaSAndroid Build Coastguard Worker     // The max scale factor should be derived from the same matrix that 'xform' was. It's only used
301*c8dee2aaSAndroid Build Coastguard Worker     // in stroking calculations, so can be ignored for path filling.
302*c8dee2aaSAndroid Build Coastguard Worker     void setShaderTransform(const wangs_formula::VectorXform& xform,
303*c8dee2aaSAndroid Build Coastguard Worker                             float maxScale = 1.f) {
304*c8dee2aaSAndroid Build Coastguard Worker         fApproxTransform = xform;
305*c8dee2aaSAndroid Build Coastguard Worker         fMaxScale = maxScale;
306*c8dee2aaSAndroid Build Coastguard Worker     }
307*c8dee2aaSAndroid Build Coastguard Worker 
308*c8dee2aaSAndroid Build Coastguard Worker     // Completes a closed contour of a stroke by rewriting a deferred patch with now-available
309*c8dee2aaSAndroid Build Coastguard Worker     // join control point information. Automatically resets the join control point attribute.
writeDeferredStrokePatch()310*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(kTrackJoinControlPoints) writeDeferredStrokePatch() {
311*c8dee2aaSAndroid Build Coastguard Worker         if (fDeferredPatch.hasPending()) {
312*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(!fDeferredPatch.fMustDefer);
313*c8dee2aaSAndroid Build Coastguard Worker             // Overwrite join control point with updated value, which is the first attribute
314*c8dee2aaSAndroid Build Coastguard Worker             // after the 4 control points.
315*c8dee2aaSAndroid Build Coastguard Worker             memcpy(SkTAddOffset<void>(fDeferredPatch.fData, 4 * sizeof(SkPoint)),
316*c8dee2aaSAndroid Build Coastguard Worker                    &fJoin, sizeof(SkPoint));
317*c8dee2aaSAndroid Build Coastguard Worker             // Assuming that the stroke parameters aren't changing within a contour, we only have
318*c8dee2aaSAndroid Build Coastguard Worker             // to set the parametric segments in order to recover the LinearTolerances state at the
319*c8dee2aaSAndroid Build Coastguard Worker             // time the deferred patch was recorded.
320*c8dee2aaSAndroid Build Coastguard Worker             fTolerances.setParametricSegments(fDeferredPatch.fN_p4);
321*c8dee2aaSAndroid Build Coastguard Worker             if (VertexWriter vw = fPatchAllocator.append(fTolerances)) {
322*c8dee2aaSAndroid Build Coastguard Worker                 vw << VertexWriter::Array<char>(fDeferredPatch.fData, PatchStride(fAttribs));
323*c8dee2aaSAndroid Build Coastguard Worker             }
324*c8dee2aaSAndroid Build Coastguard Worker         }
325*c8dee2aaSAndroid Build Coastguard Worker 
326*c8dee2aaSAndroid Build Coastguard Worker         fDeferredPatch.reset();
327*c8dee2aaSAndroid Build Coastguard Worker     }
328*c8dee2aaSAndroid Build Coastguard Worker 
329*c8dee2aaSAndroid Build Coastguard Worker     // Updates the stroke's join control point that will be written out with each patch. This is
330*c8dee2aaSAndroid Build Coastguard Worker     // automatically adjusted when appending various geometries (e.g. Conic/Cubic), but sometimes
331*c8dee2aaSAndroid Build Coastguard Worker     // must be set explicitly.
updateJoinControlPointAttrib(SkPoint lastControlPoint)332*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(JoinAttrib::kEnabled) updateJoinControlPointAttrib(SkPoint lastControlPoint) {
333*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kJoinControlPoint); // must be runtime enabled as well
334*c8dee2aaSAndroid Build Coastguard Worker         fJoin = lastControlPoint;
335*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kTrackJoinControlPoints) {
336*c8dee2aaSAndroid Build Coastguard Worker             fDeferredPatch.fMustDefer = false;
337*c8dee2aaSAndroid Build Coastguard Worker         }
338*c8dee2aaSAndroid Build Coastguard Worker     }
339*c8dee2aaSAndroid Build Coastguard Worker 
340*c8dee2aaSAndroid Build Coastguard Worker     // Updates the fan point that will be written out with each patch (i.e., the point that wedges
341*c8dee2aaSAndroid Build Coastguard Worker     // fan around).
updateFanPointAttrib(SkPoint fanPoint)342*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(FanPointAttrib::kEnabled) updateFanPointAttrib(SkPoint fanPoint) {
343*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kFanPoint);
344*c8dee2aaSAndroid Build Coastguard Worker         fFanPoint = fanPoint;
345*c8dee2aaSAndroid Build Coastguard Worker     }
346*c8dee2aaSAndroid Build Coastguard Worker 
347*c8dee2aaSAndroid Build Coastguard Worker     // Updates the stroke params that are written out with each patch.
updateStrokeParamsAttrib(StrokeParams strokeParams)348*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(StrokeAttrib::kEnabled) updateStrokeParamsAttrib(StrokeParams strokeParams) {
349*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kStrokeParams);
350*c8dee2aaSAndroid Build Coastguard Worker         fStrokeParams = strokeParams;
351*c8dee2aaSAndroid Build Coastguard Worker         fTolerances.setStroke(strokeParams, fMaxScale);
352*c8dee2aaSAndroid Build Coastguard Worker     }
353*c8dee2aaSAndroid Build Coastguard Worker     // Updates tolerances to account for stroke params that are stored as uniforms instead of
354*c8dee2aaSAndroid Build Coastguard Worker     // dynamic instance attributes.
updateUniformStrokeParams(StrokeParams strokeParams)355*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(StrokeAttrib::kEnabled) updateUniformStrokeParams(StrokeParams strokeParams) {
356*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(!(fAttribs & PatchAttribs::kStrokeParams));
357*c8dee2aaSAndroid Build Coastguard Worker         fTolerances.setStroke(strokeParams, fMaxScale);
358*c8dee2aaSAndroid Build Coastguard Worker     }
359*c8dee2aaSAndroid Build Coastguard Worker 
360*c8dee2aaSAndroid Build Coastguard Worker     // Updates the color that will be written out with each patch.
updateColorAttrib(const SkPMColor4f & color)361*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(ColorAttrib::kEnabled) updateColorAttrib(const SkPMColor4f& color) {
362*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kColor);
363*c8dee2aaSAndroid Build Coastguard Worker         // Converts SkPMColor4f to the selected 'Color' attrib type. The always-wide and never-wide
364*c8dee2aaSAndroid Build Coastguard Worker         // branches match what VertexColor does based on the runtime check.
365*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (req_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
366*c8dee2aaSAndroid Build Coastguard Worker             fColor = color;
367*c8dee2aaSAndroid Build Coastguard Worker         } else if constexpr (opt_attrib<PatchAttribs::kWideColorIfEnabled>::value) {
368*c8dee2aaSAndroid Build Coastguard Worker             fColor = VertexColor(color, fAttribs & PatchAttribs::kWideColorIfEnabled);
369*c8dee2aaSAndroid Build Coastguard Worker         } else {
370*c8dee2aaSAndroid Build Coastguard Worker             fColor = color.toBytes_RGBA();
371*c8dee2aaSAndroid Build Coastguard Worker         }
372*c8dee2aaSAndroid Build Coastguard Worker     }
373*c8dee2aaSAndroid Build Coastguard Worker 
374*c8dee2aaSAndroid Build Coastguard Worker     // Updates the paint depth written out with each patch.
updatePaintDepthAttrib(float depth)375*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(DepthAttrib::kEnabled) updatePaintDepthAttrib(float depth) {
376*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kPaintDepth);
377*c8dee2aaSAndroid Build Coastguard Worker         fDepth = depth;
378*c8dee2aaSAndroid Build Coastguard Worker     }
379*c8dee2aaSAndroid Build Coastguard Worker 
380*c8dee2aaSAndroid Build Coastguard Worker     // Updates the storage buffer index used to access uniforms.
381*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(SsboIndexAttrib::kEnabled)
updateSsboIndexAttrib(skvx::uint2 ssboIndex)382*c8dee2aaSAndroid Build Coastguard Worker     updateSsboIndexAttrib(skvx::uint2 ssboIndex) {
383*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(fAttribs & PatchAttribs::kSsboIndex);
384*c8dee2aaSAndroid Build Coastguard Worker         fSsboIndex = ssboIndex;
385*c8dee2aaSAndroid Build Coastguard Worker     }
386*c8dee2aaSAndroid Build Coastguard Worker 
387*c8dee2aaSAndroid Build Coastguard Worker     /**
388*c8dee2aaSAndroid Build Coastguard Worker      * writeX functions for supported patch geometry types. Every geometric type is converted to an
389*c8dee2aaSAndroid Build Coastguard Worker      * equivalent cubic or conic, so this will always write at minimum 8 floats for the four control
390*c8dee2aaSAndroid Build Coastguard Worker      * points (cubic) or three control points and {w, inf} (conics). The PatchWriter additionally
391*c8dee2aaSAndroid Build Coastguard Worker      * writes the current values of all attributes enabled in its PatchAttribs flags.
392*c8dee2aaSAndroid Build Coastguard Worker      */
393*c8dee2aaSAndroid Build Coastguard Worker 
394*c8dee2aaSAndroid Build Coastguard Worker     // Write a cubic curve with its four control points.
writeCubic(float2 p0,float2 p1,float2 p2,float2 p3)395*c8dee2aaSAndroid Build Coastguard Worker     AI void writeCubic(float2 p0, float2 p1, float2 p2, float2 p3) {
396*c8dee2aaSAndroid Build Coastguard Worker         float n4 = wangs_formula::cubic_p4(kPrecision, p0, p1, p2, p3, fApproxTransform);
397*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kDiscardFlatCurves) {
398*c8dee2aaSAndroid Build Coastguard Worker             if (n4 <= 1.f) {
399*c8dee2aaSAndroid Build Coastguard Worker                 // This cubic only needs one segment (e.g. a line) but we're not filling space with
400*c8dee2aaSAndroid Build Coastguard Worker                 // fans or stroking, so nothing actually needs to be drawn.
401*c8dee2aaSAndroid Build Coastguard Worker                 return;
402*c8dee2aaSAndroid Build Coastguard Worker             }
403*c8dee2aaSAndroid Build Coastguard Worker         }
404*c8dee2aaSAndroid Build Coastguard Worker         if (int numPatches = this->accountForCurve(n4)) {
405*c8dee2aaSAndroid Build Coastguard Worker             this->chopAndWriteCubics(p0, p1, p2, p3, numPatches);
406*c8dee2aaSAndroid Build Coastguard Worker         } else {
407*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0, p1, p2, p3);
408*c8dee2aaSAndroid Build Coastguard Worker         }
409*c8dee2aaSAndroid Build Coastguard Worker     }
writeCubic(const SkPoint pts[4])410*c8dee2aaSAndroid Build Coastguard Worker     AI void writeCubic(const SkPoint pts[4]) {
411*c8dee2aaSAndroid Build Coastguard Worker         float4 p0p1 = float4::Load(pts);
412*c8dee2aaSAndroid Build Coastguard Worker         float4 p2p3 = float4::Load(pts + 2);
413*c8dee2aaSAndroid Build Coastguard Worker         this->writeCubic(p0p1.lo, p0p1.hi, p2p3.lo, p2p3.hi);
414*c8dee2aaSAndroid Build Coastguard Worker     }
415*c8dee2aaSAndroid Build Coastguard Worker 
416*c8dee2aaSAndroid Build Coastguard Worker     // Write a conic curve with three control points and 'w', with the last coord of the last
417*c8dee2aaSAndroid Build Coastguard Worker     // control point signaling a conic by being set to infinity.
writeConic(float2 p0,float2 p1,float2 p2,float w)418*c8dee2aaSAndroid Build Coastguard Worker     AI void writeConic(float2 p0, float2 p1, float2 p2, float w) {
419*c8dee2aaSAndroid Build Coastguard Worker         float n2 = wangs_formula::conic_p2(kPrecision, p0, p1, p2, w, fApproxTransform);
420*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kDiscardFlatCurves) {
421*c8dee2aaSAndroid Build Coastguard Worker             if (n2 <= 1.f) {
422*c8dee2aaSAndroid Build Coastguard Worker                 // This conic only needs one segment (e.g. a line) but we're not filling space with
423*c8dee2aaSAndroid Build Coastguard Worker                 // fans or stroking, so nothing actually needs to be drawn.
424*c8dee2aaSAndroid Build Coastguard Worker                 return;
425*c8dee2aaSAndroid Build Coastguard Worker             }
426*c8dee2aaSAndroid Build Coastguard Worker         }
427*c8dee2aaSAndroid Build Coastguard Worker         if (int numPatches = this->accountForCurve(n2 * n2)) {
428*c8dee2aaSAndroid Build Coastguard Worker             this->chopAndWriteConics(p0, p1, p2, w, numPatches);
429*c8dee2aaSAndroid Build Coastguard Worker         } else {
430*c8dee2aaSAndroid Build Coastguard Worker             this->writeConicPatch(p0, p1, p2, w);
431*c8dee2aaSAndroid Build Coastguard Worker         }
432*c8dee2aaSAndroid Build Coastguard Worker     }
writeConic(const SkPoint pts[3],float w)433*c8dee2aaSAndroid Build Coastguard Worker     AI void writeConic(const SkPoint pts[3], float w) {
434*c8dee2aaSAndroid Build Coastguard Worker         this->writeConic(sk_bit_cast<float2>(pts[0]),
435*c8dee2aaSAndroid Build Coastguard Worker                          sk_bit_cast<float2>(pts[1]),
436*c8dee2aaSAndroid Build Coastguard Worker                          sk_bit_cast<float2>(pts[2]),
437*c8dee2aaSAndroid Build Coastguard Worker                          w);
438*c8dee2aaSAndroid Build Coastguard Worker     }
439*c8dee2aaSAndroid Build Coastguard Worker 
440*c8dee2aaSAndroid Build Coastguard Worker     // Write a quadratic curve that automatically converts its three control points into an
441*c8dee2aaSAndroid Build Coastguard Worker     // equivalent cubic.
writeQuadratic(float2 p0,float2 p1,float2 p2)442*c8dee2aaSAndroid Build Coastguard Worker     AI void writeQuadratic(float2 p0, float2 p1, float2 p2) {
443*c8dee2aaSAndroid Build Coastguard Worker         float n4 = wangs_formula::quadratic_p4(kPrecision, p0, p1, p2, fApproxTransform);
444*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kDiscardFlatCurves) {
445*c8dee2aaSAndroid Build Coastguard Worker             if (n4 <= 1.f) {
446*c8dee2aaSAndroid Build Coastguard Worker                 // This quad only needs one segment (e.g. a line) but we're not filling space with
447*c8dee2aaSAndroid Build Coastguard Worker                 // fans or stroking, so nothing actually needs to be drawn.
448*c8dee2aaSAndroid Build Coastguard Worker                 return;
449*c8dee2aaSAndroid Build Coastguard Worker             }
450*c8dee2aaSAndroid Build Coastguard Worker         }
451*c8dee2aaSAndroid Build Coastguard Worker         if (int numPatches = this->accountForCurve(n4)) {
452*c8dee2aaSAndroid Build Coastguard Worker             this->chopAndWriteQuads(p0, p1, p2, numPatches);
453*c8dee2aaSAndroid Build Coastguard Worker         } else {
454*c8dee2aaSAndroid Build Coastguard Worker             this->writeQuadPatch(p0, p1, p2);
455*c8dee2aaSAndroid Build Coastguard Worker         }
456*c8dee2aaSAndroid Build Coastguard Worker     }
writeQuadratic(const SkPoint pts[3])457*c8dee2aaSAndroid Build Coastguard Worker     AI void writeQuadratic(const SkPoint pts[3]) {
458*c8dee2aaSAndroid Build Coastguard Worker         this->writeQuadratic(sk_bit_cast<float2>(pts[0]),
459*c8dee2aaSAndroid Build Coastguard Worker                              sk_bit_cast<float2>(pts[1]),
460*c8dee2aaSAndroid Build Coastguard Worker                              sk_bit_cast<float2>(pts[2]));
461*c8dee2aaSAndroid Build Coastguard Worker     }
462*c8dee2aaSAndroid Build Coastguard Worker 
463*c8dee2aaSAndroid Build Coastguard Worker     // Write a line that is automatically converted into an equivalent cubic.
writeLine(float4 p0p1)464*c8dee2aaSAndroid Build Coastguard Worker     AI void writeLine(float4 p0p1) {
465*c8dee2aaSAndroid Build Coastguard Worker         // No chopping needed, a line only ever requires one segment (the minimum required already).
466*c8dee2aaSAndroid Build Coastguard Worker         fTolerances.setParametricSegments(1.f);
467*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kReplicateLineEndPoints) {
468*c8dee2aaSAndroid Build Coastguard Worker             // Visually this cubic is still a line, but 't' does not move linearly over the line,
469*c8dee2aaSAndroid Build Coastguard Worker             // so Wang's formula is more pessimistic. Shaders should avoid evaluating Wang's
470*c8dee2aaSAndroid Build Coastguard Worker             // formula when a patch has control points in this arrangement.
471*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0p1.lo, p0p1.lo, p0p1.hi, p0p1.hi);
472*c8dee2aaSAndroid Build Coastguard Worker         } else {
473*c8dee2aaSAndroid Build Coastguard Worker             // In exact math, this cubic structure should have Wang's formula return 0. Due to
474*c8dee2aaSAndroid Build Coastguard Worker             // floating point math, this isn't always the case, so shaders need some way to restrict
475*c8dee2aaSAndroid Build Coastguard Worker             // the number of parametric segments if Wang's formula numerically blows up.
476*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0p1.lo, (p0p1.zwxy() - p0p1) * (1/3.f) + p0p1, p0p1.hi);
477*c8dee2aaSAndroid Build Coastguard Worker         }
478*c8dee2aaSAndroid Build Coastguard Worker     }
writeLine(float2 p0,float2 p1)479*c8dee2aaSAndroid Build Coastguard Worker     AI void writeLine(float2 p0, float2 p1) { this->writeLine({p0, p1}); }
writeLine(SkPoint p0,SkPoint p1)480*c8dee2aaSAndroid Build Coastguard Worker     AI void writeLine(SkPoint p0, SkPoint p1) {
481*c8dee2aaSAndroid Build Coastguard Worker         this->writeLine(sk_bit_cast<float2>(p0), sk_bit_cast<float2>(p1));
482*c8dee2aaSAndroid Build Coastguard Worker     }
483*c8dee2aaSAndroid Build Coastguard Worker 
484*c8dee2aaSAndroid Build Coastguard Worker     // Write a triangle by setting it to a conic with w=Inf, and using a distinct
485*c8dee2aaSAndroid Build Coastguard Worker     // explicit curve type for when inf isn't supported in shaders.
writeTriangle(float2 p0,float2 p1,float2 p2)486*c8dee2aaSAndroid Build Coastguard Worker     AI void writeTriangle(float2 p0, float2 p1, float2 p2) {
487*c8dee2aaSAndroid Build Coastguard Worker         // No chopping needed, the max supported segment count should always support 2 lines
488*c8dee2aaSAndroid Build Coastguard Worker         // (which form a triangle when implicitly closed).
489*c8dee2aaSAndroid Build Coastguard Worker         static constexpr float kTriangleSegments_p4 = 2.f * 2.f * 2.f * 2.f;
490*c8dee2aaSAndroid Build Coastguard Worker         fTolerances.setParametricSegments(kTriangleSegments_p4);
491*c8dee2aaSAndroid Build Coastguard Worker         this->writePatch(p0, p1, p2, {SK_FloatInfinity, SK_FloatInfinity},
492*c8dee2aaSAndroid Build Coastguard Worker                          kTriangularConicCurveType);
493*c8dee2aaSAndroid Build Coastguard Worker     }
writeTriangle(SkPoint p0,SkPoint p1,SkPoint p2)494*c8dee2aaSAndroid Build Coastguard Worker     AI void writeTriangle(SkPoint p0, SkPoint p1, SkPoint p2) {
495*c8dee2aaSAndroid Build Coastguard Worker         this->writeTriangle(sk_bit_cast<float2>(p0),
496*c8dee2aaSAndroid Build Coastguard Worker                             sk_bit_cast<float2>(p1),
497*c8dee2aaSAndroid Build Coastguard Worker                             sk_bit_cast<float2>(p2));
498*c8dee2aaSAndroid Build Coastguard Worker     }
499*c8dee2aaSAndroid Build Coastguard Worker 
500*c8dee2aaSAndroid Build Coastguard Worker     // Writes a circle used for round caps and joins in stroking, encoded as a cubic with
501*c8dee2aaSAndroid Build Coastguard Worker     // identical control points and an empty join.
writeCircle(SkPoint p)502*c8dee2aaSAndroid Build Coastguard Worker     AI void writeCircle(SkPoint p) {
503*c8dee2aaSAndroid Build Coastguard Worker         // This does not use writePatch() because it uses its own location as the join attribute
504*c8dee2aaSAndroid Build Coastguard Worker         // value instead of fJoin and never defers.
505*c8dee2aaSAndroid Build Coastguard Worker         fTolerances.setParametricSegments(0.f);
506*c8dee2aaSAndroid Build Coastguard Worker         if (VertexWriter vw = fPatchAllocator.append(fTolerances)) {
507*c8dee2aaSAndroid Build Coastguard Worker             vw << VertexWriter::Repeat<4>(p); // p0,p1,p2,p3 = p -> 4 copies
508*c8dee2aaSAndroid Build Coastguard Worker             this->emitPatchAttribs(std::move(vw), {fAttribs, p}, kCubicCurveType);
509*c8dee2aaSAndroid Build Coastguard Worker         }
510*c8dee2aaSAndroid Build Coastguard Worker     }
511*c8dee2aaSAndroid Build Coastguard Worker 
512*c8dee2aaSAndroid Build Coastguard Worker private:
emitPatchAttribs(VertexWriter vertexWriter,const JoinAttrib & join,float explicitCurveType)513*c8dee2aaSAndroid Build Coastguard Worker     AI void emitPatchAttribs(VertexWriter vertexWriter,
514*c8dee2aaSAndroid Build Coastguard Worker                              const JoinAttrib& join,
515*c8dee2aaSAndroid Build Coastguard Worker                              float explicitCurveType) {
516*c8dee2aaSAndroid Build Coastguard Worker         // NOTE: operator<< overrides automatically handle optional and disabled attribs.
517*c8dee2aaSAndroid Build Coastguard Worker         vertexWriter << join << fFanPoint << fStrokeParams << fColor << fDepth
518*c8dee2aaSAndroid Build Coastguard Worker                      << CurveTypeAttrib{fAttribs, explicitCurveType} << fSsboIndex;
519*c8dee2aaSAndroid Build Coastguard Worker     }
520*c8dee2aaSAndroid Build Coastguard Worker 
appendPatch()521*c8dee2aaSAndroid Build Coastguard Worker     AI VertexWriter appendPatch() {
522*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kTrackJoinControlPoints) {
523*c8dee2aaSAndroid Build Coastguard Worker             if (fDeferredPatch.fMustDefer) {
524*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(!fDeferredPatch.hasPending());
525*c8dee2aaSAndroid Build Coastguard Worker                 SkASSERT(PatchStride(fAttribs) <= kMaxStride);
526*c8dee2aaSAndroid Build Coastguard Worker                 // Save the computed parametric segment tolerance value so that we can pass that to
527*c8dee2aaSAndroid Build Coastguard Worker                 // the PatchAllocator when flushing the deferred patch.
528*c8dee2aaSAndroid Build Coastguard Worker                 fDeferredPatch.fN_p4 = fTolerances.numParametricSegments_p4();
529*c8dee2aaSAndroid Build Coastguard Worker                 return {fDeferredPatch.fData, PatchStride(fAttribs)};
530*c8dee2aaSAndroid Build Coastguard Worker             }
531*c8dee2aaSAndroid Build Coastguard Worker         }
532*c8dee2aaSAndroid Build Coastguard Worker         return fPatchAllocator.append(fTolerances);
533*c8dee2aaSAndroid Build Coastguard Worker     }
534*c8dee2aaSAndroid Build Coastguard Worker 
writePatch(float2 p0,float2 p1,float2 p2,float2 p3,float explicitCurveType)535*c8dee2aaSAndroid Build Coastguard Worker     AI void writePatch(float2 p0, float2 p1, float2 p2, float2 p3, float explicitCurveType) {
536*c8dee2aaSAndroid Build Coastguard Worker         if (VertexWriter vw = this->appendPatch()) {
537*c8dee2aaSAndroid Build Coastguard Worker             // NOTE: fJoin will be undefined if we're writing to a deferred patch. If that's the
538*c8dee2aaSAndroid Build Coastguard Worker             // case, correct data will overwrite it when the contour is closed (this is fine since a
539*c8dee2aaSAndroid Build Coastguard Worker             // deferred patch writes to CPU memory instead of directly to the GPU buffer).
540*c8dee2aaSAndroid Build Coastguard Worker             vw << p0 << p1 << p2 << p3;
541*c8dee2aaSAndroid Build Coastguard Worker             this->emitPatchAttribs(std::move(vw), fJoin, explicitCurveType);
542*c8dee2aaSAndroid Build Coastguard Worker 
543*c8dee2aaSAndroid Build Coastguard Worker             // Automatically update join control point for next patch.
544*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kTrackJoinControlPoints) {
545*c8dee2aaSAndroid Build Coastguard Worker                 if (explicitCurveType == kCubicCurveType && any(p3 != p2)) {
546*c8dee2aaSAndroid Build Coastguard Worker                     // p2 is control point defining the tangent vector into the next patch.
547*c8dee2aaSAndroid Build Coastguard Worker                     p2.store(&fJoin);
548*c8dee2aaSAndroid Build Coastguard Worker                 } else if (any(p2 != p1)) {
549*c8dee2aaSAndroid Build Coastguard Worker                     // p1 is the control point defining the tangent vector.
550*c8dee2aaSAndroid Build Coastguard Worker                     p1.store(&fJoin);
551*c8dee2aaSAndroid Build Coastguard Worker                 } else {
552*c8dee2aaSAndroid Build Coastguard Worker                     // p0 is the control point defining the tangent vector.
553*c8dee2aaSAndroid Build Coastguard Worker                     p0.store(&fJoin);
554*c8dee2aaSAndroid Build Coastguard Worker                 }
555*c8dee2aaSAndroid Build Coastguard Worker                 fDeferredPatch.fMustDefer = false;
556*c8dee2aaSAndroid Build Coastguard Worker             }
557*c8dee2aaSAndroid Build Coastguard Worker         }
558*c8dee2aaSAndroid Build Coastguard Worker     }
559*c8dee2aaSAndroid Build Coastguard Worker 
560*c8dee2aaSAndroid Build Coastguard Worker     // Helpers that normalize curves to a generic patch, but do no other work.
writeCubicPatch(float2 p0,float2 p1,float2 p2,float2 p3)561*c8dee2aaSAndroid Build Coastguard Worker     AI void writeCubicPatch(float2 p0, float2 p1, float2 p2, float2 p3) {
562*c8dee2aaSAndroid Build Coastguard Worker         this->writePatch(p0, p1, p2, p3, kCubicCurveType);
563*c8dee2aaSAndroid Build Coastguard Worker     }
writeCubicPatch(float2 p0,float4 p1p2,float2 p3)564*c8dee2aaSAndroid Build Coastguard Worker     AI void writeCubicPatch(float2 p0, float4 p1p2, float2 p3) {
565*c8dee2aaSAndroid Build Coastguard Worker         this->writeCubicPatch(p0, p1p2.lo, p1p2.hi, p3);
566*c8dee2aaSAndroid Build Coastguard Worker     }
writeQuadPatch(float2 p0,float2 p1,float2 p2)567*c8dee2aaSAndroid Build Coastguard Worker     AI void writeQuadPatch(float2 p0, float2 p1, float2 p2) {
568*c8dee2aaSAndroid Build Coastguard Worker         this->writeCubicPatch(p0, mix(float4(p0, p2), p1.xyxy(), 2/3.f), p2);
569*c8dee2aaSAndroid Build Coastguard Worker     }
writeConicPatch(float2 p0,float2 p1,float2 p2,float w)570*c8dee2aaSAndroid Build Coastguard Worker     AI void writeConicPatch(float2 p0, float2 p1, float2 p2, float w) {
571*c8dee2aaSAndroid Build Coastguard Worker         this->writePatch(p0, p1, p2, {w, SK_FloatInfinity}, kConicCurveType);
572*c8dee2aaSAndroid Build Coastguard Worker     }
573*c8dee2aaSAndroid Build Coastguard Worker 
accountForCurve(float n4)574*c8dee2aaSAndroid Build Coastguard Worker     int accountForCurve(float n4) {
575*c8dee2aaSAndroid Build Coastguard Worker         if (n4 <= kMaxParametricSegments_p4) {
576*c8dee2aaSAndroid Build Coastguard Worker             // Record n^4 and return 0 to signal no chopping
577*c8dee2aaSAndroid Build Coastguard Worker             fTolerances.setParametricSegments(n4);
578*c8dee2aaSAndroid Build Coastguard Worker             return 0;
579*c8dee2aaSAndroid Build Coastguard Worker         } else {
580*c8dee2aaSAndroid Build Coastguard Worker             // Clamp to max allowed segmentation for a patch and return required number of chops
581*c8dee2aaSAndroid Build Coastguard Worker             // to achieve visual correctness.
582*c8dee2aaSAndroid Build Coastguard Worker             fTolerances.setParametricSegments(kMaxParametricSegments_p4);
583*c8dee2aaSAndroid Build Coastguard Worker             return SkScalarCeilToInt(wangs_formula::root4(std::min(n4, kMaxSegmentsPerCurve_p4) /
584*c8dee2aaSAndroid Build Coastguard Worker                                                           kMaxParametricSegments_p4));
585*c8dee2aaSAndroid Build Coastguard Worker         }
586*c8dee2aaSAndroid Build Coastguard Worker     }
587*c8dee2aaSAndroid Build Coastguard Worker 
588*c8dee2aaSAndroid Build Coastguard Worker     // This does not return b when t==1, but it otherwise seems to get better precision than
589*c8dee2aaSAndroid Build Coastguard Worker     // "a*(1 - t) + b*t" for things like chopping cubics on exact cusp points.
590*c8dee2aaSAndroid Build Coastguard Worker     // The responsibility falls on the caller to check that t != 1 before calling.
mix(float4 a,float4 b,float4 T)591*c8dee2aaSAndroid Build Coastguard Worker     static AI float4 mix(float4 a, float4 b, float4 T) {
592*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(all((0 <= T) & (T < 1)));
593*c8dee2aaSAndroid Build Coastguard Worker         return (b - a)*T + a;
594*c8dee2aaSAndroid Build Coastguard Worker     }
595*c8dee2aaSAndroid Build Coastguard Worker 
596*c8dee2aaSAndroid Build Coastguard Worker     // Helpers that chop the curve type into 'numPatches' parametrically uniform curves. It is
597*c8dee2aaSAndroid Build Coastguard Worker     // assumed that 'numPatches' is calculated such that the resulting curves require the maximum
598*c8dee2aaSAndroid Build Coastguard Worker     // number of segments to draw appropriately (since the original presumably needed even more).
chopAndWriteQuads(float2 p0,float2 p1,float2 p2,int numPatches)599*c8dee2aaSAndroid Build Coastguard Worker     void chopAndWriteQuads(float2 p0, float2 p1, float2 p2, int numPatches) {
600*c8dee2aaSAndroid Build Coastguard Worker         InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
601*c8dee2aaSAndroid Build Coastguard Worker         for (; numPatches >= 3; numPatches -= 2) {
602*c8dee2aaSAndroid Build Coastguard Worker             // Chop into 3 quads.
603*c8dee2aaSAndroid Build Coastguard Worker             float4 T = float4(1,1,2,2) / numPatches;
604*c8dee2aaSAndroid Build Coastguard Worker             float4 ab = mix(p0.xyxy(), p1.xyxy(), T);
605*c8dee2aaSAndroid Build Coastguard Worker             float4 bc = mix(p1.xyxy(), p2.xyxy(), T);
606*c8dee2aaSAndroid Build Coastguard Worker             float4 abc = mix(ab, bc, T);
607*c8dee2aaSAndroid Build Coastguard Worker             // p1 & p2 of the cubic representation of the middle quad.
608*c8dee2aaSAndroid Build Coastguard Worker             float4 middle = mix(ab, bc, mix(T, T.zwxy(), 2/3.f));
609*c8dee2aaSAndroid Build Coastguard Worker 
610*c8dee2aaSAndroid Build Coastguard Worker             this->writeQuadPatch(p0, ab.lo, abc.lo);  // Write the 1st quad.
611*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
612*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangle(p0, abc.lo, abc.hi);
613*c8dee2aaSAndroid Build Coastguard Worker             }
614*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(abc.lo, middle, abc.hi);  // Write the 2nd quad (already a cubic)
615*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
616*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(abc.hi)));
617*c8dee2aaSAndroid Build Coastguard Worker             }
618*c8dee2aaSAndroid Build Coastguard Worker             std::tie(p0, p1) = {abc.hi, bc.hi};  // Save the 3rd quad.
619*c8dee2aaSAndroid Build Coastguard Worker         }
620*c8dee2aaSAndroid Build Coastguard Worker         if (numPatches == 2) {
621*c8dee2aaSAndroid Build Coastguard Worker             // Chop into 2 quads.
622*c8dee2aaSAndroid Build Coastguard Worker             float2 ab = (p0 + p1) * .5f;
623*c8dee2aaSAndroid Build Coastguard Worker             float2 bc = (p1 + p2) * .5f;
624*c8dee2aaSAndroid Build Coastguard Worker             float2 abc = (ab + bc) * .5f;
625*c8dee2aaSAndroid Build Coastguard Worker 
626*c8dee2aaSAndroid Build Coastguard Worker             this->writeQuadPatch(p0, ab, abc);  // Write the 1st quad.
627*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
628*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangle(p0, abc, p2);
629*c8dee2aaSAndroid Build Coastguard Worker             }
630*c8dee2aaSAndroid Build Coastguard Worker             this->writeQuadPatch(abc, bc, p2);  // Write the 2nd quad.
631*c8dee2aaSAndroid Build Coastguard Worker         } else {
632*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(numPatches == 1);
633*c8dee2aaSAndroid Build Coastguard Worker             this->writeQuadPatch(p0, p1, p2);  // Write the single remaining quad.
634*c8dee2aaSAndroid Build Coastguard Worker         }
635*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kAddTrianglesWhenChopping) {
636*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p2)));
637*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.close());
638*c8dee2aaSAndroid Build Coastguard Worker         }
639*c8dee2aaSAndroid Build Coastguard Worker     }
640*c8dee2aaSAndroid Build Coastguard Worker 
chopAndWriteConics(float2 p0,float2 p1,float2 p2,float w,int numPatches)641*c8dee2aaSAndroid Build Coastguard Worker     void chopAndWriteConics(float2 p0, float2 p1, float2 p2, float w, int numPatches) {
642*c8dee2aaSAndroid Build Coastguard Worker         InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
643*c8dee2aaSAndroid Build Coastguard Worker         // Load the conic in 3d homogeneous (unprojected) space.
644*c8dee2aaSAndroid Build Coastguard Worker         float4 h0 = float4(p0,1,1);
645*c8dee2aaSAndroid Build Coastguard Worker         float4 h1 = float4(p1,1,1) * w;
646*c8dee2aaSAndroid Build Coastguard Worker         float4 h2 = float4(p2,1,1);
647*c8dee2aaSAndroid Build Coastguard Worker         for (; numPatches >= 2; --numPatches) {
648*c8dee2aaSAndroid Build Coastguard Worker             // Chop in homogeneous space.
649*c8dee2aaSAndroid Build Coastguard Worker             float T = 1.f/numPatches;
650*c8dee2aaSAndroid Build Coastguard Worker             float4 ab = mix(h0, h1, T);
651*c8dee2aaSAndroid Build Coastguard Worker             float4 bc = mix(h1, h2, T);
652*c8dee2aaSAndroid Build Coastguard Worker             float4 abc = mix(ab, bc, T);
653*c8dee2aaSAndroid Build Coastguard Worker 
654*c8dee2aaSAndroid Build Coastguard Worker             // Project and write the 1st conic.
655*c8dee2aaSAndroid Build Coastguard Worker             float2 midpoint = abc.xy() / abc.w();
656*c8dee2aaSAndroid Build Coastguard Worker             this->writeConicPatch(h0.xy() / h0.w(),
657*c8dee2aaSAndroid Build Coastguard Worker                                   ab.xy() / ab.w(),
658*c8dee2aaSAndroid Build Coastguard Worker                                   midpoint,
659*c8dee2aaSAndroid Build Coastguard Worker                                   ab.w() / sqrtf(h0.w() * abc.w()));
660*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
661*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(midpoint)));
662*c8dee2aaSAndroid Build Coastguard Worker             }
663*c8dee2aaSAndroid Build Coastguard Worker             std::tie(h0, h1) = {abc, bc};  // Save the 2nd conic (in homogeneous space).
664*c8dee2aaSAndroid Build Coastguard Worker         }
665*c8dee2aaSAndroid Build Coastguard Worker         // Project and write the remaining conic.
666*c8dee2aaSAndroid Build Coastguard Worker         SkASSERT(numPatches == 1);
667*c8dee2aaSAndroid Build Coastguard Worker         this->writeConicPatch(h0.xy() / h0.w(),
668*c8dee2aaSAndroid Build Coastguard Worker                               h1.xy() / h1.w(),
669*c8dee2aaSAndroid Build Coastguard Worker                               h2.xy(), // h2.w == 1
670*c8dee2aaSAndroid Build Coastguard Worker                               h1.w() / sqrtf(h0.w()));
671*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kAddTrianglesWhenChopping) {
672*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(h2.xy())));
673*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.close());
674*c8dee2aaSAndroid Build Coastguard Worker         }
675*c8dee2aaSAndroid Build Coastguard Worker     }
676*c8dee2aaSAndroid Build Coastguard Worker 
chopAndWriteCubics(float2 p0,float2 p1,float2 p2,float2 p3,int numPatches)677*c8dee2aaSAndroid Build Coastguard Worker     void chopAndWriteCubics(float2 p0, float2 p1, float2 p2, float2 p3, int numPatches) {
678*c8dee2aaSAndroid Build Coastguard Worker         InnerTriangulator triangulator(numPatches, sk_bit_cast<SkPoint>(p0));
679*c8dee2aaSAndroid Build Coastguard Worker         for (; numPatches >= 3; numPatches -= 2) {
680*c8dee2aaSAndroid Build Coastguard Worker             // Chop into 3 cubics.
681*c8dee2aaSAndroid Build Coastguard Worker             float4 T = float4(1,1,2,2) / numPatches;
682*c8dee2aaSAndroid Build Coastguard Worker             float4 ab = mix(p0.xyxy(), p1.xyxy(), T);
683*c8dee2aaSAndroid Build Coastguard Worker             float4 bc = mix(p1.xyxy(), p2.xyxy(), T);
684*c8dee2aaSAndroid Build Coastguard Worker             float4 cd = mix(p2.xyxy(), p3.xyxy(), T);
685*c8dee2aaSAndroid Build Coastguard Worker             float4 abc = mix(ab, bc, T);
686*c8dee2aaSAndroid Build Coastguard Worker             float4 bcd = mix(bc, cd, T);
687*c8dee2aaSAndroid Build Coastguard Worker             float4 abcd = mix(abc, bcd, T);
688*c8dee2aaSAndroid Build Coastguard Worker             float4 middle = mix(abc, bcd, T.zwxy());  // p1 & p2 of the middle cubic.
689*c8dee2aaSAndroid Build Coastguard Worker 
690*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0, ab.lo, abc.lo, abcd.lo);  // Write the 1st cubic.
691*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
692*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangle(p0, abcd.lo, abcd.hi);
693*c8dee2aaSAndroid Build Coastguard Worker             }
694*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(abcd.lo, middle, abcd.hi);  // Write the 2nd cubic.
695*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
696*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(abcd.hi)));
697*c8dee2aaSAndroid Build Coastguard Worker             }
698*c8dee2aaSAndroid Build Coastguard Worker             std::tie(p0, p1, p2) = {abcd.hi, bcd.hi, cd.hi};  // Save the 3rd cubic.
699*c8dee2aaSAndroid Build Coastguard Worker         }
700*c8dee2aaSAndroid Build Coastguard Worker         if (numPatches == 2) {
701*c8dee2aaSAndroid Build Coastguard Worker             // Chop into 2 cubics.
702*c8dee2aaSAndroid Build Coastguard Worker             float2 ab = (p0 + p1) * .5f;
703*c8dee2aaSAndroid Build Coastguard Worker             float2 bc = (p1 + p2) * .5f;
704*c8dee2aaSAndroid Build Coastguard Worker             float2 cd = (p2 + p3) * .5f;
705*c8dee2aaSAndroid Build Coastguard Worker             float2 abc = (ab + bc) * .5f;
706*c8dee2aaSAndroid Build Coastguard Worker             float2 bcd = (bc + cd) * .5f;
707*c8dee2aaSAndroid Build Coastguard Worker             float2 abcd = (abc + bcd) * .5f;
708*c8dee2aaSAndroid Build Coastguard Worker 
709*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0, ab, abc, abcd);  // Write the 1st cubic.
710*c8dee2aaSAndroid Build Coastguard Worker             if constexpr (kAddTrianglesWhenChopping) {
711*c8dee2aaSAndroid Build Coastguard Worker                 this->writeTriangle(p0, abcd, p3);
712*c8dee2aaSAndroid Build Coastguard Worker             }
713*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(abcd, bcd, cd, p3);  // Write the 2nd cubic.
714*c8dee2aaSAndroid Build Coastguard Worker         } else {
715*c8dee2aaSAndroid Build Coastguard Worker             SkASSERT(numPatches == 1);
716*c8dee2aaSAndroid Build Coastguard Worker             this->writeCubicPatch(p0, p1, p2, p3);  // Write the single remaining cubic.
717*c8dee2aaSAndroid Build Coastguard Worker         }
718*c8dee2aaSAndroid Build Coastguard Worker         if constexpr (kAddTrianglesWhenChopping) {
719*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.pushVertex(sk_bit_cast<SkPoint>(p3)));
720*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangleStack(triangulator.close());
721*c8dee2aaSAndroid Build Coastguard Worker         }
722*c8dee2aaSAndroid Build Coastguard Worker     }
723*c8dee2aaSAndroid Build Coastguard Worker 
724*c8dee2aaSAndroid Build Coastguard Worker     ENABLE_IF(kAddTrianglesWhenChopping)
writeTriangleStack(MiddleOutPolygonTriangulator::PoppedTriangleStack && stack)725*c8dee2aaSAndroid Build Coastguard Worker     writeTriangleStack(MiddleOutPolygonTriangulator::PoppedTriangleStack&& stack) {
726*c8dee2aaSAndroid Build Coastguard Worker         for (auto [p0, p1, p2] : stack) {
727*c8dee2aaSAndroid Build Coastguard Worker             this->writeTriangle(p0, p1, p2);
728*c8dee2aaSAndroid Build Coastguard Worker         }
729*c8dee2aaSAndroid Build Coastguard Worker     }
730*c8dee2aaSAndroid Build Coastguard Worker 
731*c8dee2aaSAndroid Build Coastguard Worker     // Runtime configuration, will always contain required attribs but may not have all optional
732*c8dee2aaSAndroid Build Coastguard Worker     // attribs enabled (e.g. depending on caps or batching).
733*c8dee2aaSAndroid Build Coastguard Worker     const PatchAttribs fAttribs;
734*c8dee2aaSAndroid Build Coastguard Worker 
735*c8dee2aaSAndroid Build Coastguard Worker     // The 2x2 approximation of the local-to-device transform that will affect subsequently
736*c8dee2aaSAndroid Build Coastguard Worker     // recorded curves (when fully transformed in the vertex shader).
737*c8dee2aaSAndroid Build Coastguard Worker     wangs_formula::VectorXform fApproxTransform = {};
738*c8dee2aaSAndroid Build Coastguard Worker     // A maximum scale factor extracted from the current approximate transform.
739*c8dee2aaSAndroid Build Coastguard Worker     float fMaxScale = 1.0f;
740*c8dee2aaSAndroid Build Coastguard Worker     // Tracks the linear tolerances for the most recently written patches.
741*c8dee2aaSAndroid Build Coastguard Worker     LinearTolerances fTolerances;
742*c8dee2aaSAndroid Build Coastguard Worker 
743*c8dee2aaSAndroid Build Coastguard Worker     PatchAllocator fPatchAllocator;
744*c8dee2aaSAndroid Build Coastguard Worker     DeferredPatch  fDeferredPatch; // only usable if kTrackJoinControlPoints is true
745*c8dee2aaSAndroid Build Coastguard Worker 
746*c8dee2aaSAndroid Build Coastguard Worker     // Instance attribute state written after the 4 control points of a patch
747*c8dee2aaSAndroid Build Coastguard Worker     JoinAttrib     fJoin;
748*c8dee2aaSAndroid Build Coastguard Worker     FanPointAttrib fFanPoint;
749*c8dee2aaSAndroid Build Coastguard Worker     StrokeAttrib   fStrokeParams;
750*c8dee2aaSAndroid Build Coastguard Worker     ColorAttrib    fColor;
751*c8dee2aaSAndroid Build Coastguard Worker     DepthAttrib    fDepth;
752*c8dee2aaSAndroid Build Coastguard Worker 
753*c8dee2aaSAndroid Build Coastguard Worker     // Index into a shared storage buffer containing this PatchWriter's patches' corresponding
754*c8dee2aaSAndroid Build Coastguard Worker     // uniforms. Written out as an attribute with every patch, to read the appropriate uniform
755*c8dee2aaSAndroid Build Coastguard Worker     // values from the storage buffer on draw.
756*c8dee2aaSAndroid Build Coastguard Worker     SsboIndexAttrib fSsboIndex;
757*c8dee2aaSAndroid Build Coastguard Worker };
758*c8dee2aaSAndroid Build Coastguard Worker 
759*c8dee2aaSAndroid Build Coastguard Worker }  // namespace skgpu::tess
760*c8dee2aaSAndroid Build Coastguard Worker 
761*c8dee2aaSAndroid Build Coastguard Worker #undef ENABLE_IF
762*c8dee2aaSAndroid Build Coastguard Worker #undef AI
763*c8dee2aaSAndroid Build Coastguard Worker 
764*c8dee2aaSAndroid Build Coastguard Worker #endif  // skgpu_tessellate_PatchWriter_DEFINED
765