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

source code of flutter_engine/third_party/skia/include/core/SkMesh.h