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