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 attributes()157 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 */ uniforms()170 SkSpan<const Uniform> uniforms() const { return SkSpan(fUniforms); } 171 172 /** Provides basic info about individual children: names, indices and runtime effect type. */ children()173 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 stride()187 size_t stride() const { return fStride; } 188 colorSpace()189 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 refSpec()347 sk_sp<SkMeshSpecification> refSpec() const { return fSpec; } spec()348 SkMeshSpecification* spec() const { return fSpec.get(); } 349 mode()350 Mode mode() const { return fMode; } 351 refVertexBuffer()352 sk_sp<VertexBuffer> refVertexBuffer() const { return fVB; } vertexBuffer()353 VertexBuffer* vertexBuffer() const { return fVB.get(); } 354 vertexOffset()355 size_t vertexOffset() const { return fVOffset; } vertexCount()356 size_t vertexCount() const { return fVCount; } 357 refIndexBuffer()358 sk_sp<IndexBuffer> refIndexBuffer() const { return fIB; } indexBuffer()359 IndexBuffer* indexBuffer() const { return fIB.get(); } 360 indexOffset()361 size_t indexOffset() const { return fIOffset; } indexCount()362 size_t indexCount() const { return fICount; } 363 refUniforms()364 sk_sp<const SkData> refUniforms() const { return fUniforms; } uniforms()365 const SkData* uniforms() const { return fUniforms.get(); } 366 children()367 SkSpan<const ChildPtr> children() const { return SkSpan(fChildren); } 368 bounds()369 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 430