/* * Copyright 2021 Google LLC. * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef skgpu_tessellate_PatchWriter_DEFINED #define skgpu_tessellate_PatchWriter_DEFINED #include "include/core/SkAlphaType.h" #include "include/core/SkScalar.h" #include "include/core/SkTypes.h" #include "include/private/SkColorData.h" #include "include/private/base/SkDebug.h" #include "include/private/base/SkFloatingPoint.h" #include "include/private/base/SkPoint_impl.h" #include "include/private/base/SkTemplates.h" #include "src/base/SkUtils.h" #include "src/base/SkVx.h" #include "src/gpu/BufferWriter.h" #include "src/gpu/tessellate/LinearTolerances.h" #include "src/gpu/tessellate/MiddleOutPolygonTriangulator.h" #include "src/gpu/tessellate/Tessellation.h" #include "src/gpu/tessellate/WangsFormula.h" #include <algorithm> #include <cstdint> #include <cstring> #include <math.h> #include <tuple> #include <type_traits> #include <utility> #include <variant> namespace skgpu::tess { /** * PatchWriter writes out tessellation patches, formatted with their specific attribs, to a GPU * buffer. * * PatchWriter is a template class that takes traits to configure both its compile-time and runtime * behavior for the different tessellation rendering algorithms and GPU backends. The complexity of * this system is worthwhile because the attribute writing operations and math already require * heavy inlining for performance, and the algorithmic variations tend to only differ slightly, but * do so in the inner most loops. Additionally, Graphite and Ganesh use the same fundamental * algorithms, but Graphite's architecture and higher required hardware level mean that its * attribute configurations can be determined entirely at compile time. * * Traits are specified in PatchWriter's single var-args template pack. Traits come in two main * categories: PatchAttribs configuration and feature/processing configuration. A given PatchAttrib * can be always enabled, enabled at runtime, or always disabled. A feature can be either enabled * or disabled and are coupled more closely with the control points of the curve. Across the two * GPU backends and different path rendering strategies, a "patch" has the following structure: * * - 4 control points (8 floats total) defining the curve's geometry * - quadratic curves are converted to equivalent cubics on the CPU during writing * - conic curves store {w, inf} in their last control point * - triangles store {inf, inf} in their last control point * - everything else is presumed to be a cubic defined by all 4 control points * - Enabled PatchAttrib values, constant for the entire instance * - layout is identical to PatchAttrib's definition, skipping disabled attribs * - attribs can be enabled/disabled at runtime by building a mask of attrib values * * Currently PatchWriter supports the following traits: * - Required<PatchAttrib> * - Optional<PatchAttrib> * - TrackJoinControlPoints * - AddTrianglesWhenChopping * - DiscardFlatCurves * * In addition to variable traits, PatchWriter's first template argument defines the type used for * allocating the GPU instance data. The templated "PatchAllocator" can be any type that provides: * // A GPU-backed vertex writer for a single instance worth of data. The provided * // LinearTolerances value represents the tolerances for the curve that will be written to the * // returned vertex space. * skgpu::VertexWriter append(const LinearTolerances&); * * Additionally, it must have a constructor that takes the stride as its first argument. * PatchWriter forwards any additional constructor args from its ctor to the allocator after * computing the necessary stride for its PatchAttribs configuration. */ // *** TRAITS *** // Marks a PatchAttrib is enabled at compile time, i.e. it must always be set and will always be // written to each patch's instance data. If present, will assert if the runtime attribs do not fit. template <PatchAttribs A> struct Required { … }; // Marks a PatchAttrib as supported, i.e. it can be enabled or disabled at runtime. Optional<A> is // overridden by Required<A>. If neither Required<A> nor Optional<A> are in a PatchWriter's trait // list, then the attrib is disabled at compile time and it will assert if the runtime attribs // attempt to enable it. template <PatchAttribs A> struct Optional { … }; // Enables tracking of the kJoinControlPointAttrib based on control points of the previously // written patch (automatically taking into account curve chopping). When a patch is first written // (and there is no prior patch to define the join control point), the PatchWriter automatically // records the patch to a temporary buffer--sans join--until writeDeferredStrokePatch() is called, // filling in the now-defined join control point. // // This feature must be paired with Required<PatchAttribs::kJoinControlPoint> struct TrackJoinControlPoints { … }; // Write additional triangular patches to fill the resulting empty area when a curve is chopped. // Normally, the patch geometry covers the curve defined by its control points, up to the implicitly // closing edge between its first and last control points. When a curve is chopped to fit within // the maximum segment count, the resulting space between the original closing edge and new closing // edges is not filled, unless some mechanism of the shader makes it so (e.g. a fan point or // stroking). // // This feature enables automatically writing triangular patches to fill this empty space when a // curve is chopped. struct AddTrianglesWhenChopping { … }; // If a curve requires at most 1 segment to render accurately, it's effectively a straight line. // This feature turns on automatically ignoring those curves, with the assumption that some other // render pass will produce equivalent geometry (e.g. middle-out or inner triangulations). struct DiscardFlatCurves { … }; // Upload lines as a cubic with {a, a, b, b} for control points, instead of the truly linear cubic // of {a, 2/3a + 1/3b, 1/3a + 2/3b, b}. Wang's formula will not return an tight lower bound on the // number of segments in this case, but it's convenient to detect in the vertex shader and assume // only a single segment is required. This bypasses numerical stability issues in Wang's formula // when evaluated on the ideal linear cubic for very large control point coordinates. Other curve // types with large coordinates do not need this treatment since they would be pre-chopped and // culled to lines. struct ReplicateLineEndPoints { … }; // *** PatchWriter internals *** // AttribValue exposes a consistent store and write interface for a PatchAttrib's value while // abstracting over compile-time enabled, conditionally-enabled, or compile-time disabled attribs. template <PatchAttribs A, typename T, bool Required, bool Optional> struct AttribValue { … }; template <PatchAttribs A, typename T, bool Required, bool Optional> VertexWriter& operator<<(VertexWriter& w, const AttribValue<A, T, Required, Optional>& v) { … } // Stores state and deferred patch data when TrackJoinControlPoints is used for a PatchWriter. template <size_t Stride> struct PatchStorage { … }; // An empty object that has the same constructor signature as MiddleOutPolygonTriangulator, used // as a stand-in when AddTrianglesWhenChopping is not a defined trait. struct NullTriangulator { … }; #define AI … #define ENABLE_IF … // *** PatchWriter *** template <typename PatchAllocator, typename... Traits> class PatchWriter { … }; } // namespace skgpu::tess #undef ENABLE_IF #undef AI #endif // skgpu_tessellate_PatchWriter_DEFINED