/src/skia/include/core/SkMesh.h
Line | Count | Source (jump to first uncovered line) |
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 SkMesh_DEFINED |
9 | | #define SkMesh_DEFINED |
10 | | |
11 | | #include "include/core/SkData.h" |
12 | | #include "include/core/SkRect.h" |
13 | | #include "include/core/SkRefCnt.h" |
14 | | #include "include/core/SkSpan.h" |
15 | | #include "include/core/SkString.h" |
16 | | #include "include/effects/SkRuntimeEffect.h" |
17 | | #include "include/private/base/SkAPI.h" |
18 | | #include "include/private/base/SkTArray.h" |
19 | | |
20 | | #include <cstddef> |
21 | | #include <cstdint> |
22 | | #include <memory> |
23 | | #include <string_view> |
24 | | #include <tuple> |
25 | | #include <vector> |
26 | | |
27 | | class GrDirectContext; |
28 | | class SkColorSpace; |
29 | | enum SkAlphaType : int; |
30 | | |
31 | | namespace SkSL { struct Program; } |
32 | | |
33 | | /** |
34 | | * A specification for custom meshes. Specifies the vertex buffer attributes and stride, the |
35 | | * vertex program that produces a user-defined set of varyings, and a fragment program that ingests |
36 | | * the interpolated varyings and produces local coordinates for shading and optionally a color. |
37 | | * |
38 | | * The varyings must include a float2 named "position". If the passed varyings does not |
39 | | * contain such a varying then one is implicitly added to the final specification and the SkSL |
40 | | * Varyings struct described below. It is an error to have a varying named "position" that has a |
41 | | * type other than float2. |
42 | | * |
43 | | * The provided attributes and varyings are used to create Attributes and Varyings structs in SkSL |
44 | | * that are used by the shaders. Each attribute from the Attribute span becomes a member of the |
45 | | * SkSL Attributes struct and likewise for the varyings. |
46 | | * |
47 | | * The signature of the vertex program must be: |
48 | | * Varyings main(const Attributes). |
49 | | * |
50 | | * The signature of the fragment program must be either: |
51 | | * float2 main(const Varyings) |
52 | | * or |
53 | | * float2 main(const Varyings, out (half4|float4) color) |
54 | | * |
55 | | * where the return value is the local coordinates that will be used to access SkShader. If the |
56 | | * color variant is used, the returned color will be blended with SkPaint's SkShader (or SkPaint |
57 | | * color in absence of a SkShader) using the SkBlender passed to SkCanvas drawMesh(). To use |
58 | | * interpolated local space positions as the shader coordinates, equivalent to how SkPaths are |
59 | | * shaded, return the position field from the Varying struct as the coordinates. |
60 | | * |
61 | | * The vertex and fragment programs may both contain uniforms. Uniforms with the same name are |
62 | | * assumed to be shared between stages. It is an error to specify uniforms in the vertex and |
63 | | * fragment program with the same name but different types, dimensionality, or layouts. |
64 | | */ |
65 | | class SK_API SkMeshSpecification : public SkNVRefCnt<SkMeshSpecification> { |
66 | | public: |
67 | | /** These values are enforced when creating a specification. */ |
68 | | static constexpr size_t kMaxStride = 1024; |
69 | | static constexpr size_t kMaxAttributes = 8; |
70 | | static constexpr size_t kStrideAlignment = 4; |
71 | | static constexpr size_t kOffsetAlignment = 4; |
72 | | static constexpr size_t kMaxVaryings = 6; |
73 | | |
74 | | struct Attribute { |
75 | | enum class Type : uint32_t { // CPU representation Shader Type |
76 | | kFloat, // float float |
77 | | kFloat2, // two floats float2 |
78 | | kFloat3, // three floats float3 |
79 | | kFloat4, // four floats float4 |
80 | | kUByte4_unorm, // four bytes half4 |
81 | | |
82 | | kLast = kUByte4_unorm |
83 | | }; |
84 | | Type type; |
85 | | size_t offset; |
86 | | SkString name; |
87 | | }; |
88 | | |
89 | | struct Varying { |
90 | | enum class Type : uint32_t { |
91 | | kFloat, // "float" |
92 | | kFloat2, // "float2" |
93 | | kFloat3, // "float3" |
94 | | kFloat4, // "float4" |
95 | | kHalf, // "half" |
96 | | kHalf2, // "half2" |
97 | | kHalf3, // "half3" |
98 | | kHalf4, // "half4" |
99 | | |
100 | | kLast = kHalf4 |
101 | | }; |
102 | | Type type; |
103 | | SkString name; |
104 | | }; |
105 | | |
106 | | using Uniform = SkRuntimeEffect::Uniform; |
107 | | using Child = SkRuntimeEffect::Child; |
108 | | |
109 | | ~SkMeshSpecification(); |
110 | | |
111 | | struct Result { |
112 | | sk_sp<SkMeshSpecification> specification; |
113 | | SkString error; |
114 | | }; |
115 | | |
116 | | /** |
117 | | * If successful the return is a specification and an empty error string. Otherwise, it is a |
118 | | * null specification a non-empty error string. |
119 | | * |
120 | | * @param attributes The vertex attributes that will be consumed by 'vs'. Attributes need |
121 | | * not be tightly packed but attribute offsets must be aligned to |
122 | | * kOffsetAlignment and offset + size may not be greater than |
123 | | * 'vertexStride'. At least one attribute is required. |
124 | | * @param vertexStride The offset between successive attribute values. This must be aligned to |
125 | | * kStrideAlignment. |
126 | | * @param varyings The varyings that will be written by 'vs' and read by 'fs'. This may |
127 | | * be empty. |
128 | | * @param vs The vertex shader code that computes a vertex position and the varyings |
129 | | * from the attributes. |
130 | | * @param fs The fragment code that computes a local coordinate and optionally a |
131 | | * color from the varyings. The local coordinate is used to sample |
132 | | * SkShader. |
133 | | * @param cs The colorspace of the color produced by 'fs'. Ignored if 'fs's main() |
134 | | * function does not have a color out param. |
135 | | * @param at The alpha type of the color produced by 'fs'. Ignored if 'fs's main() |
136 | | * function does not have a color out param. Cannot be kUnknown. |
137 | | */ |
138 | | static Result Make(SkSpan<const Attribute> attributes, |
139 | | size_t vertexStride, |
140 | | SkSpan<const Varying> varyings, |
141 | | const SkString& vs, |
142 | | const SkString& fs); |
143 | | static Result Make(SkSpan<const Attribute> attributes, |
144 | | size_t vertexStride, |
145 | | SkSpan<const Varying> varyings, |
146 | | const SkString& vs, |
147 | | const SkString& fs, |
148 | | sk_sp<SkColorSpace> cs); |
149 | | static Result Make(SkSpan<const Attribute> attributes, |
150 | | size_t vertexStride, |
151 | | SkSpan<const Varying> varyings, |
152 | | const SkString& vs, |
153 | | const SkString& fs, |
154 | | sk_sp<SkColorSpace> cs, |
155 | | SkAlphaType at); |
156 | | |
157 | 2.95k | SkSpan<const Attribute> attributes() const { return SkSpan(fAttributes); } |
158 | | |
159 | | /** |
160 | | * Combined size of all 'uniform' variables. When creating a SkMesh with this specification |
161 | | * provide an SkData of this size, containing values for all of those variables. Use uniforms() |
162 | | * to get the offset of each uniform within the SkData. |
163 | | */ |
164 | | size_t uniformSize() const; |
165 | | |
166 | | /** |
167 | | * Provides info about individual uniforms including the offset into an SkData where each |
168 | | * uniform value should be placed. |
169 | | */ |
170 | 0 | SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); } |
171 | | |
172 | | /** Provides basic info about individual children: names, indices and runtime effect type. */ |
173 | 0 | SkSpan<const Child> children() const { return SkSpan(fChildren); } |
174 | | |
175 | | /** Returns a pointer to the named child's description, or nullptr if not found. */ |
176 | | const Child* findChild(std::string_view name) const; |
177 | | |
178 | | /** Returns a pointer to the named uniform variable's description, or nullptr if not found. */ |
179 | | const Uniform* findUniform(std::string_view name) const; |
180 | | |
181 | | /** Returns a pointer to the named attribute, or nullptr if not found. */ |
182 | | const Attribute* findAttribute(std::string_view name) const; |
183 | | |
184 | | /** Returns a pointer to the named varying, or nullptr if not found. */ |
185 | | const Varying* findVarying(std::string_view name) const; |
186 | | |
187 | 5.91k | size_t stride() const { return fStride; } |
188 | | |
189 | 0 | SkColorSpace* colorSpace() const { return fColorSpace.get(); } |
190 | | |
191 | | private: |
192 | | friend struct SkMeshSpecificationPriv; |
193 | | |
194 | | enum class ColorType { |
195 | | kNone, |
196 | | kHalf4, |
197 | | kFloat4, |
198 | | }; |
199 | | |
200 | | static Result MakeFromSourceWithStructs(SkSpan<const Attribute> attributes, |
201 | | size_t stride, |
202 | | SkSpan<const Varying> varyings, |
203 | | const SkString& vs, |
204 | | const SkString& fs, |
205 | | sk_sp<SkColorSpace> cs, |
206 | | SkAlphaType at); |
207 | | |
208 | | SkMeshSpecification(SkSpan<const Attribute>, |
209 | | size_t, |
210 | | SkSpan<const Varying>, |
211 | | int passthroughLocalCoordsVaryingIndex, |
212 | | uint32_t deadVaryingMask, |
213 | | std::vector<Uniform> uniforms, |
214 | | std::vector<Child> children, |
215 | | std::unique_ptr<const SkSL::Program>, |
216 | | std::unique_ptr<const SkSL::Program>, |
217 | | ColorType, |
218 | | sk_sp<SkColorSpace>, |
219 | | SkAlphaType); |
220 | | |
221 | | SkMeshSpecification(const SkMeshSpecification&) = delete; |
222 | | SkMeshSpecification(SkMeshSpecification&&) = delete; |
223 | | |
224 | | SkMeshSpecification& operator=(const SkMeshSpecification&) = delete; |
225 | | SkMeshSpecification& operator=(SkMeshSpecification&&) = delete; |
226 | | |
227 | | const std::vector<Attribute> fAttributes; |
228 | | const std::vector<Varying> fVaryings; |
229 | | const std::vector<Uniform> fUniforms; |
230 | | const std::vector<Child> fChildren; |
231 | | const std::unique_ptr<const SkSL::Program> fVS; |
232 | | const std::unique_ptr<const SkSL::Program> fFS; |
233 | | const size_t fStride; |
234 | | uint32_t fHash; |
235 | | const int fPassthroughLocalCoordsVaryingIndex; |
236 | | const uint32_t fDeadVaryingMask; |
237 | | const ColorType fColorType; |
238 | | const sk_sp<SkColorSpace> fColorSpace; |
239 | | const SkAlphaType fAlphaType; |
240 | | }; |
241 | | |
242 | | /** |
243 | | * A vertex buffer, a topology, optionally an index buffer, and a compatible SkMeshSpecification. |
244 | | * |
245 | | * The data in the vertex buffer is expected to contain the attributes described by the spec |
246 | | * for vertexCount vertices, beginning at vertexOffset. vertexOffset must be aligned to the |
247 | | * SkMeshSpecification's vertex stride. The size of the buffer must be at least vertexOffset + |
248 | | * spec->stride()*vertexCount (even if vertex attributes contains pad at the end of the stride). If |
249 | | * the specified bounds do not contain all the points output by the spec's vertex program when |
250 | | * applied to the vertices in the custom mesh, then the result is undefined. |
251 | | * |
252 | | * MakeIndexed may be used to create an indexed mesh. indexCount indices are read from the index |
253 | | * buffer at the specified offset, which must be aligned to 2. The indices are always unsigned |
254 | | * 16-bit integers. The index count must be at least 3. |
255 | | * |
256 | | * If Make() is used, the implicit index sequence is 0, 1, 2, 3, ... and vertexCount must be at |
257 | | * least 3. |
258 | | * |
259 | | * Both Make() and MakeIndexed() take a SkData with the uniform values. See |
260 | | * SkMeshSpecification::uniformSize() and SkMeshSpecification::uniforms() for sizing and packing |
261 | | * uniforms into the SkData. |
262 | | */ |
263 | | class SK_API SkMesh { |
264 | | public: |
265 | | class IndexBuffer : public SkRefCnt { |
266 | | public: |
267 | | virtual size_t size() const = 0; |
268 | | |
269 | | /** |
270 | | * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer |
271 | | * at offset. Fails if offset + size > this->size() or if either offset or size is not |
272 | | * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We |
273 | | * take it as a parameter to emphasize that the context must be used to update the data and |
274 | | * thus the context must be valid for the current thread. |
275 | | */ |
276 | | bool update(GrDirectContext*, const void* data, size_t offset, size_t size); |
277 | | |
278 | | private: |
279 | | virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; |
280 | | }; |
281 | | |
282 | | class VertexBuffer : public SkRefCnt { |
283 | | public: |
284 | | virtual size_t size() const = 0; |
285 | | |
286 | | /** |
287 | | * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer |
288 | | * at offset. Fails if offset + size > this->size() or if either offset or size is not |
289 | | * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We |
290 | | * take it as a parameter to emphasize that the context must be used to update the data and |
291 | | * thus the context must be valid for the current thread. |
292 | | */ |
293 | | bool update(GrDirectContext*, const void* data, size_t offset, size_t size); |
294 | | |
295 | | private: |
296 | | virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; |
297 | | }; |
298 | | |
299 | | SkMesh(); |
300 | | ~SkMesh(); |
301 | | |
302 | | SkMesh(const SkMesh&); |
303 | | SkMesh(SkMesh&&); |
304 | | |
305 | | SkMesh& operator=(const SkMesh&); |
306 | | SkMesh& operator=(SkMesh&&); |
307 | | |
308 | | enum class Mode { kTriangles, kTriangleStrip }; |
309 | | |
310 | | struct Result; |
311 | | |
312 | | using ChildPtr = SkRuntimeEffect::ChildPtr; |
313 | | |
314 | | /** |
315 | | * Creates a non-indexed SkMesh. The returned SkMesh can be tested for validity using |
316 | | * SkMesh::isValid(). An invalid mesh simply fails to draws if passed to SkCanvas::drawMesh(). |
317 | | * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the |
318 | | * vertex buffer was null or uniform data too small). |
319 | | */ |
320 | | static Result Make(sk_sp<SkMeshSpecification>, |
321 | | Mode, |
322 | | sk_sp<VertexBuffer>, |
323 | | size_t vertexCount, |
324 | | size_t vertexOffset, |
325 | | sk_sp<const SkData> uniforms, |
326 | | SkSpan<ChildPtr> children, |
327 | | const SkRect& bounds); |
328 | | |
329 | | /** |
330 | | * Creates an indexed SkMesh. The returned SkMesh can be tested for validity using |
331 | | * SkMesh::isValid(). A invalid mesh simply fails to draw if passed to SkCanvas::drawMesh(). |
332 | | * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the |
333 | | * index buffer was null or uniform data too small). |
334 | | */ |
335 | | static Result MakeIndexed(sk_sp<SkMeshSpecification>, |
336 | | Mode, |
337 | | sk_sp<VertexBuffer>, |
338 | | size_t vertexCount, |
339 | | size_t vertexOffset, |
340 | | sk_sp<IndexBuffer>, |
341 | | size_t indexCount, |
342 | | size_t indexOffset, |
343 | | sk_sp<const SkData> uniforms, |
344 | | SkSpan<ChildPtr> children, |
345 | | const SkRect& bounds); |
346 | | |
347 | 0 | sk_sp<SkMeshSpecification> refSpec() const { return fSpec; } |
348 | 0 | SkMeshSpecification* spec() const { return fSpec.get(); } |
349 | | |
350 | 0 | Mode mode() const { return fMode; } |
351 | | |
352 | 0 | sk_sp<VertexBuffer> refVertexBuffer() const { return fVB; } |
353 | 0 | VertexBuffer* vertexBuffer() const { return fVB.get(); } |
354 | | |
355 | 0 | size_t vertexOffset() const { return fVOffset; } |
356 | 0 | size_t vertexCount() const { return fVCount; } |
357 | | |
358 | 0 | sk_sp<IndexBuffer> refIndexBuffer() const { return fIB; } |
359 | 0 | IndexBuffer* indexBuffer() const { return fIB.get(); } |
360 | | |
361 | 0 | size_t indexOffset() const { return fIOffset; } |
362 | 0 | size_t indexCount() const { return fICount; } |
363 | | |
364 | 0 | sk_sp<const SkData> refUniforms() const { return fUniforms; } |
365 | 0 | const SkData* uniforms() const { return fUniforms.get(); } |
366 | | |
367 | 0 | SkSpan<const ChildPtr> children() const { return SkSpan(fChildren); } |
368 | | |
369 | 0 | SkRect bounds() const { return fBounds; } |
370 | | |
371 | | bool isValid() const; |
372 | | |
373 | | private: |
374 | | std::tuple<bool, SkString> validate() const; |
375 | | |
376 | | sk_sp<SkMeshSpecification> fSpec; |
377 | | |
378 | | sk_sp<VertexBuffer> fVB; |
379 | | sk_sp<IndexBuffer> fIB; |
380 | | |
381 | | sk_sp<const SkData> fUniforms; |
382 | | skia_private::STArray<2, ChildPtr> fChildren; |
383 | | |
384 | | size_t fVOffset = 0; // Must be a multiple of spec->stride() |
385 | | size_t fVCount = 0; |
386 | | |
387 | | size_t fIOffset = 0; // Must be a multiple of sizeof(uint16_t) |
388 | | size_t fICount = 0; |
389 | | |
390 | | Mode fMode = Mode::kTriangles; |
391 | | |
392 | | SkRect fBounds = SkRect::MakeEmpty(); |
393 | | }; |
394 | | |
395 | | struct SkMesh::Result { SkMesh mesh; SkString error; }; |
396 | | |
397 | | namespace SkMeshes { |
398 | | /** |
399 | | * Makes a CPU-backed index buffer to be used with SkMeshes. |
400 | | * |
401 | | * @param data The data used to populate the buffer, or nullptr to create a zero- |
402 | | * initialized buffer. |
403 | | * @param size Both the size of the data in 'data' and the size of the resulting |
404 | | * buffer, in bytes. |
405 | | */ |
406 | | SK_API sk_sp<SkMesh::IndexBuffer> MakeIndexBuffer(const void* data, size_t size); |
407 | | |
408 | | /** |
409 | | * Makes a copy of an index buffer. The copy will be CPU-backed. |
410 | | */ |
411 | | SK_API sk_sp<SkMesh::IndexBuffer> CopyIndexBuffer(const sk_sp<SkMesh::IndexBuffer>&); |
412 | | |
413 | | /** |
414 | | * Makes a CPU-backed vertex buffer to be used with SkMeshes. |
415 | | * |
416 | | * @param data The data used to populate the buffer, or nullptr to create a zero- |
417 | | * initialized buffer. |
418 | | * @param size Both the size of the data in 'data' and the size of the resulting |
419 | | * buffer, in bytes. |
420 | | */ |
421 | | SK_API sk_sp<SkMesh::VertexBuffer> MakeVertexBuffer(const void*, size_t size); |
422 | | |
423 | | /** |
424 | | * Makes a copy of a vertex buffer. The copy will be CPU-backed. |
425 | | */ |
426 | | SK_API sk_sp<SkMesh::VertexBuffer> CopyVertexBuffer(const sk_sp<SkMesh::VertexBuffer>&); |
427 | | } // namespace SkMeshes |
428 | | |
429 | | #endif |