423 lines
18 KiB
C++
423 lines
18 KiB
C++
/*
|
|
* 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/SkTypes.h"
|
|
|
|
#ifdef SK_ENABLE_SKSL
|
|
#include "include/core/SkAlphaType.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 <memory>
|
|
#include <vector>
|
|
|
|
class GrDirectContext;
|
|
class SkColorSpace;
|
|
class SkData;
|
|
|
|
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 SkMeshSpecification : public SkNVRefCnt<SkMeshSpecification> {
|
|
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;
|
|
|
|
~SkMeshSpecification();
|
|
|
|
struct Result {
|
|
sk_sp<SkMeshSpecification> 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<const Attribute> attributes,
|
|
size_t vertexStride,
|
|
SkSpan<const Varying> varyings,
|
|
const SkString& vs,
|
|
const SkString& fs);
|
|
static Result Make(SkSpan<const Attribute> attributes,
|
|
size_t vertexStride,
|
|
SkSpan<const Varying> varyings,
|
|
const SkString& vs,
|
|
const SkString& fs,
|
|
sk_sp<SkColorSpace> cs);
|
|
static Result Make(SkSpan<const Attribute> attributes,
|
|
size_t vertexStride,
|
|
SkSpan<const Varying> varyings,
|
|
const SkString& vs,
|
|
const SkString& fs,
|
|
sk_sp<SkColorSpace> cs,
|
|
SkAlphaType at);
|
|
|
|
SkSpan<const Attribute> 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<const Uniform> uniforms() const { return SkSpan(fUniforms); }
|
|
|
|
/** Returns pointer to the named uniform variable's description, or nullptr if not found. */
|
|
const Uniform* findUniform(std::string_view name) const;
|
|
|
|
/** Returns pointer to the named attribute, or nullptr if not found. */
|
|
const Attribute* findAttribute(std::string_view name) const;
|
|
|
|
/** Returns pointer to the named varying, or nullptr if not found. */
|
|
const Varying* findVarying(std::string_view name) const;
|
|
|
|
size_t stride() const { return fStride; }
|
|
|
|
private:
|
|
friend struct SkMeshSpecificationPriv;
|
|
|
|
enum class ColorType {
|
|
kNone,
|
|
kHalf4,
|
|
kFloat4,
|
|
};
|
|
|
|
static Result MakeFromSourceWithStructs(SkSpan<const Attribute> attributes,
|
|
size_t stride,
|
|
SkSpan<const Varying> varyings,
|
|
const SkString& vs,
|
|
const SkString& fs,
|
|
sk_sp<SkColorSpace> cs,
|
|
SkAlphaType at);
|
|
|
|
SkMeshSpecification(SkSpan<const Attribute>,
|
|
size_t,
|
|
SkSpan<const Varying>,
|
|
int passthroughLocalCoordsVaryingIndex,
|
|
uint32_t deadVaryingMask,
|
|
std::vector<Uniform> uniforms,
|
|
std::unique_ptr<const SkSL::Program>,
|
|
std::unique_ptr<const SkSL::Program>,
|
|
ColorType,
|
|
sk_sp<SkColorSpace>,
|
|
SkAlphaType);
|
|
|
|
SkMeshSpecification(const SkMeshSpecification&) = delete;
|
|
SkMeshSpecification(SkMeshSpecification&&) = delete;
|
|
|
|
SkMeshSpecification& operator=(const SkMeshSpecification&) = delete;
|
|
SkMeshSpecification& operator=(SkMeshSpecification&&) = delete;
|
|
|
|
const std::vector<Attribute> fAttributes;
|
|
const std::vector<Varying> fVaryings;
|
|
const std::vector<Uniform> fUniforms;
|
|
const std::unique_ptr<const SkSL::Program> fVS;
|
|
const std::unique_ptr<const SkSL::Program> fFS;
|
|
const size_t fStride;
|
|
uint32_t fHash;
|
|
const int fPassthroughLocalCoordsVaryingIndex;
|
|
const uint32_t fDeadVaryingMask;
|
|
const ColorType fColorType;
|
|
const sk_sp<SkColorSpace> 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 does 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 16bit
|
|
* 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 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&&);
|
|
|
|
/**
|
|
* Makes an index buffer to be used with SkMeshes. The buffer may be CPU- or GPU-backed
|
|
* depending on whether GrDirectContext* is nullptr.
|
|
*
|
|
* @param GrDirectContext* If nullptr a CPU-backed object is returned. Otherwise, the data is
|
|
* uploaded to the GPU and a GPU-backed buffer is returned. It may
|
|
* only be used to draw into SkSurfaces that are backed by the passed
|
|
* GrDirectContext.
|
|
* @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.
|
|
*/
|
|
static sk_sp<IndexBuffer> MakeIndexBuffer(GrDirectContext*, const void* data, size_t size);
|
|
|
|
/**
|
|
* Makes a copy of an index buffer. The implementation currently only supports a CPU-backed
|
|
* source buffer.
|
|
*/
|
|
static sk_sp<IndexBuffer> CopyIndexBuffer(GrDirectContext*, sk_sp<IndexBuffer>);
|
|
|
|
/**
|
|
* Makes a vertex buffer to be used with SkMeshes. The buffer may be CPU- or GPU-backed
|
|
* depending on whether GrDirectContext* is nullptr.
|
|
*
|
|
* @param GrDirectContext* If nullptr a CPU-backed object is returned. Otherwise, the data is
|
|
* uploaded to the GPU and a GPU-backed buffer is returned. It may
|
|
* only be used to draw into SkSurfaces that are backed by the passed
|
|
* GrDirectContext.
|
|
* @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.
|
|
*/
|
|
static sk_sp<VertexBuffer> MakeVertexBuffer(GrDirectContext*, const void*, size_t size);
|
|
|
|
/**
|
|
* Makes a copy of a vertex buffer. The implementation currently only supports a CPU-backed
|
|
* source buffer.
|
|
*/
|
|
static sk_sp<VertexBuffer> CopyVertexBuffer(GrDirectContext*, sk_sp<VertexBuffer>);
|
|
|
|
enum class Mode { kTriangles, kTriangleStrip };
|
|
|
|
struct Result;
|
|
|
|
/**
|
|
* 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<SkMeshSpecification>,
|
|
Mode,
|
|
sk_sp<VertexBuffer>,
|
|
size_t vertexCount,
|
|
size_t vertexOffset,
|
|
sk_sp<const SkData> uniforms,
|
|
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<SkMeshSpecification>,
|
|
Mode,
|
|
sk_sp<VertexBuffer>,
|
|
size_t vertexCount,
|
|
size_t vertexOffset,
|
|
sk_sp<IndexBuffer>,
|
|
size_t indexCount,
|
|
size_t indexOffset,
|
|
sk_sp<const SkData> uniforms,
|
|
const SkRect& bounds);
|
|
|
|
sk_sp<SkMeshSpecification> refSpec() const { return fSpec; }
|
|
SkMeshSpecification* spec() const { return fSpec.get(); }
|
|
|
|
Mode mode() const { return fMode; }
|
|
|
|
sk_sp<VertexBuffer> refVertexBuffer() const { return fVB; }
|
|
VertexBuffer* vertexBuffer() const { return fVB.get(); }
|
|
|
|
size_t vertexOffset() const { return fVOffset; }
|
|
size_t vertexCount() const { return fVCount; }
|
|
|
|
sk_sp<IndexBuffer> refIndexBuffer() const { return fIB; }
|
|
IndexBuffer* indexBuffer() const { return fIB.get(); }
|
|
|
|
size_t indexOffset() const { return fIOffset; }
|
|
size_t indexCount() const { return fICount; }
|
|
|
|
sk_sp<const SkData> refUniforms() const { return fUniforms; }
|
|
const SkData* uniforms() const { return fUniforms.get(); }
|
|
|
|
SkRect bounds() const { return fBounds; }
|
|
|
|
bool isValid() const;
|
|
|
|
private:
|
|
friend struct SkMeshPriv;
|
|
|
|
std::tuple<bool, SkString> validate() const;
|
|
|
|
sk_sp<SkMeshSpecification> fSpec;
|
|
|
|
sk_sp<VertexBuffer> fVB;
|
|
sk_sp<IndexBuffer> fIB;
|
|
|
|
sk_sp<const SkData> fUniforms;
|
|
|
|
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; };
|
|
|
|
#endif // SK_ENABLE_SKSL
|
|
|
|
#endif
|