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