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