xref: /aosp_15_r20/external/skia/src/gpu/tessellate/FixedCountBufferUtils.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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