/* * Copyright 2021 Google LLC * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file. */ #ifndef SkMesh_DEFINED #define SkMesh_DEFINED #include "include/core/SkData.h" #include "include/core/SkRect.h" #include "include/core/SkRefCnt.h" #include "include/core/SkSpan.h" #include "include/core/SkString.h" #include "include/effects/SkRuntimeEffect.h" #include "include/private/base/SkAPI.h" #include "include/private/base/SkTArray.h" #include #include #include #include #include #include class GrDirectContext; class SkColorSpace; enum SkAlphaType : int; namespace SkSL { struct Program; } /** * A specification for custom meshes. Specifies the vertex buffer attributes and stride, the * vertex program that produces a user-defined set of varyings, and a fragment program that ingests * the interpolated varyings and produces local coordinates for shading and optionally a color. * * The varyings must include a float2 named "position". If the passed varyings does not * contain such a varying then one is implicitly added to the final specification and the SkSL * Varyings struct described below. It is an error to have a varying named "position" that has a * type other than float2. * * The provided attributes and varyings are used to create Attributes and Varyings structs in SkSL * that are used by the shaders. Each attribute from the Attribute span becomes a member of the * SkSL Attributes struct and likewise for the varyings. * * The signature of the vertex program must be: * Varyings main(const Attributes). * * The signature of the fragment program must be either: * float2 main(const Varyings) * or * float2 main(const Varyings, out (half4|float4) color) * * where the return value is the local coordinates that will be used to access SkShader. If the * color variant is used, the returned color will be blended with SkPaint's SkShader (or SkPaint * color in absence of a SkShader) using the SkBlender passed to SkCanvas drawMesh(). To use * interpolated local space positions as the shader coordinates, equivalent to how SkPaths are * shaded, return the position field from the Varying struct as the coordinates. * * The vertex and fragment programs may both contain uniforms. Uniforms with the same name are * assumed to be shared between stages. It is an error to specify uniforms in the vertex and * fragment program with the same name but different types, dimensionality, or layouts. */ class SK_API SkMeshSpecification : public SkNVRefCnt { public: /** These values are enforced when creating a specification. */ static constexpr size_t kMaxStride = 1024; static constexpr size_t kMaxAttributes = 8; static constexpr size_t kStrideAlignment = 4; static constexpr size_t kOffsetAlignment = 4; static constexpr size_t kMaxVaryings = 6; struct Attribute { enum class Type : uint32_t { // CPU representation Shader Type kFloat, // float float kFloat2, // two floats float2 kFloat3, // three floats float3 kFloat4, // four floats float4 kUByte4_unorm, // four bytes half4 kLast = kUByte4_unorm }; Type type; size_t offset; SkString name; }; struct Varying { enum class Type : uint32_t { kFloat, // "float" kFloat2, // "float2" kFloat3, // "float3" kFloat4, // "float4" kHalf, // "half" kHalf2, // "half2" kHalf3, // "half3" kHalf4, // "half4" kLast = kHalf4 }; Type type; SkString name; }; using Uniform = SkRuntimeEffect::Uniform; using Child = SkRuntimeEffect::Child; ~SkMeshSpecification(); struct Result { sk_sp specification; SkString error; }; /** * If successful the return is a specification and an empty error string. Otherwise, it is a * null specification a non-empty error string. * * @param attributes The vertex attributes that will be consumed by 'vs'. Attributes need * not be tightly packed but attribute offsets must be aligned to * kOffsetAlignment and offset + size may not be greater than * 'vertexStride'. At least one attribute is required. * @param vertexStride The offset between successive attribute values. This must be aligned to * kStrideAlignment. * @param varyings The varyings that will be written by 'vs' and read by 'fs'. This may * be empty. * @param vs The vertex shader code that computes a vertex position and the varyings * from the attributes. * @param fs The fragment code that computes a local coordinate and optionally a * color from the varyings. The local coordinate is used to sample * SkShader. * @param cs The colorspace of the color produced by 'fs'. Ignored if 'fs's main() * function does not have a color out param. * @param at The alpha type of the color produced by 'fs'. Ignored if 'fs's main() * function does not have a color out param. Cannot be kUnknown. */ static Result Make(SkSpan attributes, size_t vertexStride, SkSpan varyings, const SkString& vs, const SkString& fs); static Result Make(SkSpan attributes, size_t vertexStride, SkSpan varyings, const SkString& vs, const SkString& fs, sk_sp cs); static Result Make(SkSpan attributes, size_t vertexStride, SkSpan varyings, const SkString& vs, const SkString& fs, sk_sp cs, SkAlphaType at); SkSpan attributes() const { return SkSpan(fAttributes); } /** * Combined size of all 'uniform' variables. When creating a SkMesh with this specification * provide an SkData of this size, containing values for all of those variables. Use uniforms() * to get the offset of each uniform within the SkData. */ size_t uniformSize() const; /** * Provides info about individual uniforms including the offset into an SkData where each * uniform value should be placed. */ SkSpan uniforms() const { return SkSpan(fUniforms); } /** Provides basic info about individual children: names, indices and runtime effect type. */ SkSpan children() const { return SkSpan(fChildren); } /** Returns a pointer to the named child's description, or nullptr if not found. */ const Child* findChild(std::string_view name) const; /** Returns a pointer to the named uniform variable's description, or nullptr if not found. */ const Uniform* findUniform(std::string_view name) const; /** Returns a pointer to the named attribute, or nullptr if not found. */ const Attribute* findAttribute(std::string_view name) const; /** Returns a pointer to the named varying, or nullptr if not found. */ const Varying* findVarying(std::string_view name) const; size_t stride() const { return fStride; } SkColorSpace* colorSpace() const { return fColorSpace.get(); } private: friend struct SkMeshSpecificationPriv; enum class ColorType { kNone, kHalf4, kFloat4, }; static Result MakeFromSourceWithStructs(SkSpan attributes, size_t stride, SkSpan varyings, const SkString& vs, const SkString& fs, sk_sp cs, SkAlphaType at); SkMeshSpecification(SkSpan, size_t, SkSpan, int passthroughLocalCoordsVaryingIndex, uint32_t deadVaryingMask, std::vector uniforms, std::vector children, std::unique_ptr, std::unique_ptr, ColorType, sk_sp, SkAlphaType); SkMeshSpecification(const SkMeshSpecification&) = delete; SkMeshSpecification(SkMeshSpecification&&) = delete; SkMeshSpecification& operator=(const SkMeshSpecification&) = delete; SkMeshSpecification& operator=(SkMeshSpecification&&) = delete; const std::vector fAttributes; const std::vector fVaryings; const std::vector fUniforms; const std::vector fChildren; const std::unique_ptr fVS; const std::unique_ptr fFS; const size_t fStride; uint32_t fHash; const int fPassthroughLocalCoordsVaryingIndex; const uint32_t fDeadVaryingMask; const ColorType fColorType; const sk_sp fColorSpace; const SkAlphaType fAlphaType; }; /** * A vertex buffer, a topology, optionally an index buffer, and a compatible SkMeshSpecification. * * The data in the vertex buffer is expected to contain the attributes described by the spec * for vertexCount vertices, beginning at vertexOffset. vertexOffset must be aligned to the * SkMeshSpecification's vertex stride. The size of the buffer must be at least vertexOffset + * spec->stride()*vertexCount (even if vertex attributes contains pad at the end of the stride). If * the specified bounds do not contain all the points output by the spec's vertex program when * applied to the vertices in the custom mesh, then the result is undefined. * * MakeIndexed may be used to create an indexed mesh. indexCount indices are read from the index * buffer at the specified offset, which must be aligned to 2. The indices are always unsigned * 16-bit integers. The index count must be at least 3. * * If Make() is used, the implicit index sequence is 0, 1, 2, 3, ... and vertexCount must be at * least 3. * * Both Make() and MakeIndexed() take a SkData with the uniform values. See * SkMeshSpecification::uniformSize() and SkMeshSpecification::uniforms() for sizing and packing * uniforms into the SkData. */ class SK_API SkMesh { public: class IndexBuffer : public SkRefCnt { public: virtual size_t size() const = 0; /** * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer * at offset. Fails if offset + size > this->size() or if either offset or size is not * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We * take it as a parameter to emphasize that the context must be used to update the data and * thus the context must be valid for the current thread. */ bool update(GrDirectContext*, const void* data, size_t offset, size_t size); private: virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; }; class VertexBuffer : public SkRefCnt { public: virtual size_t size() const = 0; /** * Modifies the data in the IndexBuffer by copying size bytes from data into the buffer * at offset. Fails if offset + size > this->size() or if either offset or size is not * aligned to 4 bytes. The GrDirectContext* must match that used to create the buffer. We * take it as a parameter to emphasize that the context must be used to update the data and * thus the context must be valid for the current thread. */ bool update(GrDirectContext*, const void* data, size_t offset, size_t size); private: virtual bool onUpdate(GrDirectContext*, const void* data, size_t offset, size_t size) = 0; }; SkMesh(); ~SkMesh(); SkMesh(const SkMesh&); SkMesh(SkMesh&&); SkMesh& operator=(const SkMesh&); SkMesh& operator=(SkMesh&&); enum class Mode { kTriangles, kTriangleStrip }; struct Result; using ChildPtr = SkRuntimeEffect::ChildPtr; /** * Creates a non-indexed SkMesh. The returned SkMesh can be tested for validity using * SkMesh::isValid(). An invalid mesh simply fails to draws if passed to SkCanvas::drawMesh(). * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the * vertex buffer was null or uniform data too small). */ static Result Make(sk_sp, Mode, sk_sp, size_t vertexCount, size_t vertexOffset, sk_sp uniforms, SkSpan children, const SkRect& bounds); /** * Creates an indexed SkMesh. The returned SkMesh can be tested for validity using * SkMesh::isValid(). A invalid mesh simply fails to draw if passed to SkCanvas::drawMesh(). * If the mesh is invalid the returned string give contain the reason for the failure (e.g. the * index buffer was null or uniform data too small). */ static Result MakeIndexed(sk_sp, Mode, sk_sp, size_t vertexCount, size_t vertexOffset, sk_sp, size_t indexCount, size_t indexOffset, sk_sp uniforms, SkSpan children, const SkRect& bounds); sk_sp refSpec() const { return fSpec; } SkMeshSpecification* spec() const { return fSpec.get(); } Mode mode() const { return fMode; } sk_sp refVertexBuffer() const { return fVB; } VertexBuffer* vertexBuffer() const { return fVB.get(); } size_t vertexOffset() const { return fVOffset; } size_t vertexCount() const { return fVCount; } sk_sp refIndexBuffer() const { return fIB; } IndexBuffer* indexBuffer() const { return fIB.get(); } size_t indexOffset() const { return fIOffset; } size_t indexCount() const { return fICount; } sk_sp refUniforms() const { return fUniforms; } const SkData* uniforms() const { return fUniforms.get(); } SkSpan children() const { return SkSpan(fChildren); } SkRect bounds() const { return fBounds; } bool isValid() const; private: std::tuple validate() const; sk_sp fSpec; sk_sp fVB; sk_sp fIB; sk_sp fUniforms; skia_private::STArray<2, ChildPtr> fChildren; size_t fVOffset = 0; // Must be a multiple of spec->stride() size_t fVCount = 0; size_t fIOffset = 0; // Must be a multiple of sizeof(uint16_t) size_t fICount = 0; Mode fMode = Mode::kTriangles; SkRect fBounds = SkRect::MakeEmpty(); }; struct SkMesh::Result { SkMesh mesh; SkString error; }; namespace SkMeshes { /** * Makes a CPU-backed index buffer to be used with SkMeshes. * * @param data The data used to populate the buffer, or nullptr to create a zero- * initialized buffer. * @param size Both the size of the data in 'data' and the size of the resulting * buffer, in bytes. */ SK_API sk_sp MakeIndexBuffer(const void* data, size_t size); /** * Makes a copy of an index buffer. The copy will be CPU-backed. */ SK_API sk_sp CopyIndexBuffer(const sk_sp&); /** * Makes a CPU-backed vertex buffer to be used with SkMeshes. * * @param data The data used to populate the buffer, or nullptr to create a zero- * initialized buffer. * @param size Both the size of the data in 'data' and the size of the resulting * buffer, in bytes. */ SK_API sk_sp MakeVertexBuffer(const void*, size_t size); /** * Makes a copy of a vertex buffer. The copy will be CPU-backed. */ SK_API sk_sp CopyVertexBuffer(const sk_sp&); } // namespace SkMeshes #endif