• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 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 tessellate_PathTessellator_DEFINED
9 #define tessellate_PathTessellator_DEFINED
10 
11 #include "src/gpu/tessellate/Tessellation.h"
12 
13 class SkPath;
14 
15 #if SK_GPU_V1
16 
17 #include "src/gpu/GrVertexChunkArray.h"
18 #include "src/gpu/tessellate/PatchWriter.h"
19 
20 class GrGpuBuffer;
21 class GrMeshDrawTarget;
22 class GrOpFlushState;
23 
24 #endif
25 
26 namespace skgpu {
27 
28 class PatchWriter;
29 
30 // Prepares GPU data for, and then draws a path's tessellated geometry. Depending on the subclass,
31 // the caller may or may not be required to draw the path's inner fan separately.
32 class PathTessellator {
33 public:
34     // This is the maximum number of segments contained in our vertex and index buffers for
35     // fixed-count rendering. If rendering in fixed-count mode and a curve requires more segments,
36     // it must be chopped.
37     constexpr static int kMaxFixedResolveLevel = 5;
38 
39     struct PathDrawList {
40         PathDrawList(const SkMatrix& pathMatrix,
41                      const SkPath& path,
42                      const SkPMColor4f& color,
43                      PathDrawList* next = nullptr)
fPathMatrixPathDrawList44                 : fPathMatrix(pathMatrix), fPath(path), fColor(color), fNext(next) {}
45 
46         SkMatrix fPathMatrix;
47         SkPath fPath;
48         SkPMColor4f fColor;
49         PathDrawList* fNext;
50 
51         struct Iter {
52             void operator++() { fHead = fHead->fNext; }
53             bool operator!=(const Iter& b) const { return fHead != b.fHead; }
54             std::tuple<const SkMatrix&, const SkPath&, const SkPMColor4f&> operator*() const {
55                 return {fHead->fPathMatrix, fHead->fPath, fHead->fColor};
56             }
57             const PathDrawList* fHead;
58         };
beginPathDrawList59         Iter begin() const { return {this}; }
endPathDrawList60         Iter end() const { return {nullptr}; }
61     };
62 
~PathTessellator()63     virtual ~PathTessellator() {}
64 
patchAttribs()65     PatchAttribs patchAttribs() const { return fAttribs; }
66 
67     // Gives an approximate initial buffer size for this class to write patches into. Ideally the
68     // whole path will fit into this initial buffer, but if it requires a lot of chopping, the
69     // PatchWriter will allocate more buffer(s).
70     virtual int patchPreallocCount(int totalCombinedPathVerbCnt) const = 0;
71 
72     // Writes out patches to the given PatchWriter, chopping as necessary so the curves all fit in
73     // maxTessellationSegments or fewer.
74     //
75     // Each path's fPathMatrix in the list is applied on the CPU while the geometry is being written
76     // out. This is a tool for batching, and is applied in addition to the shader's on-GPU matrix.
77     virtual void writePatches(PatchWriter&,
78                               int maxTessellationSegments,
79                               const SkMatrix& shaderMatrix,
80                               const PathDrawList&) = 0;
81 
82 #if SK_GPU_V1
83     // Initializes the internal vertex and index buffers required for drawFixedCount().
84     virtual void prepareFixedCountBuffers(GrMeshDrawTarget*) = 0;
85 
86     // Called before draw(). Prepares GPU buffers containing the geometry to tessellate.
prepare(GrMeshDrawTarget * target,int maxTessellationSegments,const SkMatrix & shaderMatrix,const PathDrawList & pathDrawList,int totalCombinedPathVerbCnt,bool willUseTessellationShaders)87     void prepare(GrMeshDrawTarget* target,
88                  int maxTessellationSegments,
89                  const SkMatrix& shaderMatrix,
90                  const PathDrawList& pathDrawList,
91                  int totalCombinedPathVerbCnt,
92                  bool willUseTessellationShaders) {
93         if (int patchPreallocCount = this->patchPreallocCount(totalCombinedPathVerbCnt)) {
94             PatchWriter patchWriter(target, this, patchPreallocCount);
95             this->writePatches(patchWriter, maxTessellationSegments, shaderMatrix, pathDrawList);
96         }
97         if (!willUseTessellationShaders) {
98             this->prepareFixedCountBuffers(target);
99         }
100     }
101 
102     // Issues hardware tessellation draw calls over the patches. The caller is responsible for
103     // binding its desired pipeline ahead of time.
104     virtual void drawTessellated(GrOpFlushState*) const = 0;
105 
106     // Issues fixed-count instanced draw calls over the patches. The caller is responsible for
107     // binding its desired pipeline ahead of time.
108     virtual void drawFixedCount(GrOpFlushState*) const = 0;
109 
draw(GrOpFlushState * flushState,bool willUseTessellationShaders)110     void draw(GrOpFlushState* flushState, bool willUseTessellationShaders) {
111         if (willUseTessellationShaders) {
112             this->drawTessellated(flushState);
113         } else {
114             this->drawFixedCount(flushState);
115         }
116     }
117 #endif
118 
119     // Returns an upper bound on the number of combined edges there might be from all inner fans in
120     // a PathDrawList.
MaxCombinedFanEdgesInPathDrawList(int totalCombinedPathVerbCnt)121     static int MaxCombinedFanEdgesInPathDrawList(int totalCombinedPathVerbCnt) {
122         // Path fans might have an extra edge from an implicit kClose at the end, but they also
123         // always begin with kMove. So the max possible number of edges in a single path is equal to
124         // the number of verbs. Therefore, the max number of combined fan edges in a PathDrawList is
125         // the number of combined path verbs in that PathDrawList.
126         return totalCombinedPathVerbCnt;
127     }
128 
129 protected:
130     // How many triangles are in a curve with 2^resolveLevel line segments?
NumCurveTrianglesAtResolveLevel(int resolveLevel)131     constexpr static int NumCurveTrianglesAtResolveLevel(int resolveLevel) {
132         // resolveLevel=0 -> 0 line segments -> 0 triangles
133         // resolveLevel=1 -> 2 line segments -> 1 triangle
134         // resolveLevel=2 -> 4 line segments -> 3 triangles
135         // resolveLevel=3 -> 8 line segments -> 7 triangles
136         // ...
137         return (1 << resolveLevel) - 1;
138     }
139 
PathTessellator(bool infinitySupport,PatchAttribs attribs)140     PathTessellator(bool infinitySupport, PatchAttribs attribs) : fAttribs(attribs) {
141         if (!infinitySupport) {
142             fAttribs |= PatchAttribs::kExplicitCurveType;
143         }
144     }
145 
146     PatchAttribs fAttribs;
147 
148     // Calculated during prepare(). If using fixed count, this is the resolveLevel to use on our
149     // instanced draws. 2^resolveLevel == numSegments.
150     int fFixedResolveLevel = 0;
151 
152 #if SK_GPU_V1
153     friend class PatchWriter;  // To access fVertexChunkArray.
154 
155     GrVertexChunkArray fVertexChunkArray;
156 
157     // If using fixed-count rendering, these are the vertex and index buffers.
158     sk_sp<const GrGpuBuffer> fFixedVertexBuffer;
159     sk_sp<const GrGpuBuffer> fFixedIndexBuffer;
160 #endif
161 };
162 
163 }  // namespace skgpu
164 
165 #endif  // tessellate_PathTessellator_DEFINED
166