1 /* 2 * Copyright 2022 Google LLC 3 * 4 * Use of this source code is governed by a BSD-style license that can be 5 * found in the LICENSE file. 6 */ 7 8 #ifndef skgpu_tessellate_FixedCountBufferUtils_DEFINED 9 #define skgpu_tessellate_FixedCountBufferUtils_DEFINED 10 11 #include "src/gpu/tessellate/LinearTolerances.h" 12 #include "src/gpu/tessellate/Tessellation.h" 13 14 #include <algorithm> 15 #include <cstddef> 16 #include <cstdint> 17 18 namespace skgpu { struct VertexWriter; } 19 20 namespace skgpu::tess { 21 22 /** 23 * Fixed-count tessellation operates in three modes, two for filling paths, and one for stroking. 24 * These modes may have additional sub-variations, but in terms of vertex buffer management, these 25 * three categories are sufficient: 26 * 27 * - FixedCountCurves: for filling paths where just the curves are tessellated. Additional measures 28 * to fill space between the inner control points of the paths are needed. 29 * - FixedCountWedges: for filling paths by tessellating the curves and adding an additional inline 30 * triangle with a shared vertex that all verbs connect to. Works with PatchAttribs::kFanPoint. 31 * - FixedCountStrokes: for stroking a path. Likely paired with PatchAttribs::kJoinControlPoint and 32 * PatchAttribs::kStrokeParams. 33 * 34 * The three types defined below for these three modes provide utility functions for heuristics to 35 * choose pre-allocation size when accumulating instance attributes with a PatchWriter, and 36 * functions for creating static/GPU-private vertex and index buffers that are used as the template 37 * for instanced rendering. 38 */ 39 class FixedCountCurves { 40 FixedCountCurves() = delete; 41 public: 42 // A heuristic function for reserving instance attribute space before using a PatchWriter. PreallocCount(int totalCombinedPathVerbCnt)43 static constexpr int PreallocCount(int totalCombinedPathVerbCnt) { 44 // Over-allocate enough curves for 1 in 4 to chop. Every chop introduces 2 new patches: 45 // another curve patch and a triangle patch that glues the two chops together, 46 // i.e. + 2 * ((count + 3) / 4) == (count + 3) / 2 47 return totalCombinedPathVerbCnt + (totalCombinedPathVerbCnt + 3) / 2; 48 } 49 50 // Convert the accumulated worst-case tolerances into an index count passed into an instanced, 51 // indexed draw function that uses FixedCountCurves static vertex and index buffers. VertexCount(const LinearTolerances & tolerances)52 static int VertexCount(const LinearTolerances& tolerances) { 53 // We should already chopped curves to make sure none needed a higher resolveLevel than 54 // kMaxResolveLevel. 55 int resolveLevel = std::min(tolerances.requiredResolveLevel(), kMaxResolveLevel); 56 return NumCurveTrianglesAtResolveLevel(resolveLevel) * 3; 57 } 58 59 // Return the number of bytes to allocate for a buffer filled via WriteVertexBuffer, assuming 60 // the shader and curve instances do require more than kMaxParametricSegments segments. VertexBufferSize()61 static constexpr size_t VertexBufferSize() { 62 return (kMaxParametricSegments + 1) * (2 * sizeof(float)); 63 } 64 65 // As above but for the corresponding index buffer, written via WriteIndexBuffer. IndexBufferSize()66 static constexpr size_t IndexBufferSize() { 67 return NumCurveTrianglesAtResolveLevel(kMaxResolveLevel) * 3 * sizeof(uint16_t); 68 } 69 70 static void WriteVertexBuffer(VertexWriter, size_t bufferSize); 71 72 static void WriteIndexBuffer(VertexWriter, size_t bufferSize); 73 }; 74 75 class FixedCountWedges { 76 FixedCountWedges() = delete; 77 public: 78 // These functions provide equivalent functionality to the matching ones in FixedCountCurves, 79 // but are intended for use with a shader and PatchWriter that has enabled the kFanPoint attrib. 80 PreallocCount(int totalCombinedPathVerbCnt)81 static constexpr int PreallocCount(int totalCombinedPathVerbCnt) { 82 // Over-allocate enough wedges for 1 in 4 to chop, i.e., ceil(maxWedges * 5/4) 83 return (totalCombinedPathVerbCnt * 5 + 3) / 4; 84 } 85 VertexCount(const LinearTolerances & tolerances)86 static int VertexCount(const LinearTolerances& tolerances) { 87 // Emit 3 vertices per curve triangle, plus 3 more for the wedge fan triangle. 88 int resolveLevel = std::min(tolerances.requiredResolveLevel(), kMaxResolveLevel); 89 return (NumCurveTrianglesAtResolveLevel(resolveLevel) + 1) * 3; 90 } 91 VertexBufferSize()92 static constexpr size_t VertexBufferSize() { 93 return ((kMaxParametricSegments + 1) + 1/*fan vertex*/) * (2 * sizeof(float)); 94 } 95 IndexBufferSize()96 static constexpr size_t IndexBufferSize() { 97 return (NumCurveTrianglesAtResolveLevel(kMaxResolveLevel) + 1/*fan triangle*/) * 98 3 * sizeof(uint16_t); 99 } 100 101 static void WriteVertexBuffer(VertexWriter, size_t bufferSize); 102 103 static void WriteIndexBuffer(VertexWriter, size_t bufferSize); 104 }; 105 106 class FixedCountStrokes { 107 FixedCountStrokes() = delete; 108 public: 109 // These functions provide equivalent functionality to the matching ones in FixedCountCurves, 110 // but are intended for a shader that that strokes a path instead of filling, where vertices 111 // are associated with joins, caps, radial segments, or parametric segments. 112 // 113 // NOTE: The fixed-count stroke buffer is only needed when vertex IDs are not available as an 114 // SkSL built-in. And unlike the curve and wedge variants, stroke drawing never relies on an 115 // index buffer so those functions are not provided. 116 117 // Don't draw more vertices than can be indexed by a signed short. We just have to draw the line 118 // somewhere and this seems reasonable enough. (There are two vertices per edge, so 2^14 edges 119 // make 2^15 vertices.) 120 static constexpr int kMaxEdges = (1 << 14) - 1; 121 static constexpr int kMaxEdgesNoVertexIDs = 1024; 122 PreallocCount(int totalCombinedPathVerbCnt)123 static constexpr int PreallocCount(int totalCombinedPathVerbCnt) { 124 // Over-allocate enough patches for each stroke to chop once, and for 8 extra caps. Since 125 // we have to chop at inflections, points of 180 degree rotation, and anywhere a stroke 126 // requires too many parametric segments, many strokes will end up getting choppped. 127 return (totalCombinedPathVerbCnt * 2) + 8/* caps */; 128 } 129 130 // Does not account for falling back to kMaxEdgesNoVertexIDs VertexCount(const LinearTolerances & tolerances)131 static int VertexCount(const LinearTolerances& tolerances) { 132 return std::min(tolerances.requiredStrokeEdges(), kMaxEdges) * 2; 133 } 134 VertexBufferSize()135 static constexpr size_t VertexBufferSize() { 136 // Each vertex is a single float (explicit id) and each edge is composed of two vertices. 137 return 2 * kMaxEdgesNoVertexIDs * sizeof(float); 138 } 139 140 // Initializes the fallback vertex buffer that should be bound when sk_VertexID is not supported 141 static void WriteVertexBuffer(VertexWriter, size_t bufferSize); 142 }; 143 144 } // namespace skgpu::tess 145 146 #endif // skgpu_tessellate_FixedCountBufferUtils 147