Coverage Report

Created: 2024-05-20 07:14

/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