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