Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
SkMesh.cpp
Go to the documentation of this file.
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
9
12#include "include/core/SkData.h"
19#include "src/base/SkSafeMath.h"
20#include "src/core/SkChecksum.h"
21#include "src/core/SkMeshPriv.h"
44
45#include <algorithm>
46#include <locale>
47#include <optional>
48#include <string>
49#include <tuple>
50#include <utility>
51
52using namespace skia_private;
53
56
59
60#define RETURN_FAILURE(...) return Result{nullptr, SkStringPrintf(__VA_ARGS__)}
61
62#define RETURN_ERROR(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__))
63
64#define RETURN_SUCCESS return std::make_tuple(true, SkString{})
65
68
69static std::vector<Uniform>::iterator find_uniform(std::vector<Uniform>& uniforms,
70 std::string_view name) {
71 return std::find_if(uniforms.begin(), uniforms.end(),
72 [name](const SkMeshSpecification::Uniform& u) { return u.name == name; });
73}
74
75static std::tuple<bool, SkString>
77 std::vector<Uniform>* uniforms,
78 std::vector<Child>* children,
80 size_t* offset) {
81 bool foundMain = false;
82 for (const SkSL::ProgramElement* elem : program.elements()) {
83 if (elem->is<SkSL::FunctionDefinition>()) {
85 const SkSL::FunctionDeclaration& decl = defn.declaration();
86 if (decl.isMain()) {
87 foundMain = true;
88 }
89 } else if (elem->is<SkSL::GlobalVarDeclaration>()) {
91 const SkSL::VarDeclaration& varDecl = global.declaration()->as<SkSL::VarDeclaration>();
92 const SkSL::Variable& var = *varDecl.var();
93 if (var.modifierFlags().isUniform()) {
94 if (var.type().isEffectChild()) {
95 // This is a child effect; add it to our list of children.
96 children->push_back(SkRuntimeEffectPriv::VarAsChild(var, children->size()));
97 } else {
98 // This is a uniform variable; make sure it exists in our list of uniforms, and
99 // ensure that the type and layout matches between VS and FS.
100 auto iter = find_uniform(*uniforms, var.name());
101 const auto& context = *program.fContext;
102 if (iter == uniforms->end()) {
103 uniforms->push_back(SkRuntimeEffectPriv::VarAsUniform(var, context,
104 offset));
105 uniforms->back().flags |= stage;
106 } else {
107 // Check that the two declarations are equivalent
108 size_t ignoredOffset = 0;
109 auto uniform = SkRuntimeEffectPriv::VarAsUniform(var, context,
110 &ignoredOffset);
111 if (uniform.isArray() != iter->isArray() ||
112 uniform.type != iter->type ||
113 uniform.count != iter->count) {
114 return {false,
115 SkStringPrintf("Uniform %.*s declared with different types"
116 " in vertex and fragment shaders.",
117 (int)var.name().size(), var.name().data())};
118 }
119 if (uniform.isColor() != iter->isColor()) {
120 return {false,
121 SkStringPrintf("Uniform %.*s declared with different color"
122 " layout in vertex and fragment shaders.",
123 (int)var.name().size(), var.name().data())};
124 }
125 (*iter).flags |= stage;
126 }
127 }
128 }
129 }
130 }
131 if (!foundMain) {
132 return {false, SkString("No main function found.")};
133 }
134 return {true, {}};
135}
136
138
140 for (const SkSL::ProgramElement* elem : fsProgram.elements()) {
141 if (elem->is<SkSL::FunctionDefinition>()) {
143 const SkSL::FunctionDeclaration& decl = defn.declaration();
144 if (decl.isMain()) {
145 SkASSERT(decl.parameters().size() == 1 || decl.parameters().size() == 2);
146 if (decl.parameters().size() == 1) {
147 return ColorType::kNone;
148 }
149 const SkSL::Type& paramType = decl.parameters()[1]->type();
150 SkASSERT(paramType.matches(*fsProgram.fContext->fTypes.fHalf4) ||
151 paramType.matches(*fsProgram.fContext->fTypes.fFloat4));
152 return paramType.matches(*fsProgram.fContext->fTypes.fHalf4) ? ColorType::kHalf4
153 : ColorType::kFloat4;
154 }
155 }
156 }
158}
159
160// This is a non-exhaustive check for the validity of a variable name. The SkSL compiler will
161// actually process the name. We're just guarding against having multiple tokens embedded in the
162// name before we put it into a struct definition.
163static bool check_name(const SkString& name) {
164 if (name.isEmpty()) {
165 return false;
166 }
167 for (size_t i = 0; i < name.size(); ++i) {
168 if (name[i] != '_' && !std::isalnum(name[i], std::locale::classic())) {
169 return false;
170 }
171 }
172 return true;
173}
174
176 switch (type) {
177 case Attribute::Type::kFloat: return 4;
178 case Attribute::Type::kFloat2: return 2*4;
179 case Attribute::Type::kFloat3: return 3*4;
180 case Attribute::Type::kFloat4: return 4*4;
181 case Attribute::Type::kUByte4_unorm: return 4;
182 }
184}
185
187 switch (type) {
188 case Attribute::Type::kFloat: return "float";
189 case Attribute::Type::kFloat2: return "float2";
190 case Attribute::Type::kFloat3: return "float3";
191 case Attribute::Type::kFloat4: return "float4";
192 case Attribute::Type::kUByte4_unorm: return "half4";
193 }
195}
196
198 switch (type) {
199 case Varying::Type::kFloat: return "float";
200 case Varying::Type::kFloat2: return "float2";
201 case Varying::Type::kFloat3: return "float3";
202 case Varying::Type::kFloat4: return "float4";
203 case Varying::Type::kHalf: return "half";
204 case Varying::Type::kHalf2: return "half2";
205 case Varying::Type::kHalf3: return "half3";
206 case Varying::Type::kHalf4: return "half4";
207 }
209}
210
211std::tuple<bool, SkString>
213 size_t stride) {
214 // Vulkan 1.0 has a minimum maximum attribute count of 2048.
215 static_assert(SkMeshSpecification::kMaxStride <= 2048);
216 // ES 2 has a max of 8.
217 static_assert(SkMeshSpecification::kMaxAttributes <= 8);
218 // Four bytes alignment is required by Metal.
219 static_assert(SkMeshSpecification::kStrideAlignment >= 4);
220 static_assert(SkMeshSpecification::kOffsetAlignment >= 4);
221 // ES2 has a minimum maximum of 8. We may need one for a broken gl_FragCoord workaround and
222 // one for local coords.
223 static_assert(SkMeshSpecification::kMaxVaryings <= 6);
224
225 if (attributes.empty()) {
226 RETURN_ERROR("At least 1 attribute is required.");
227 }
228 if (attributes.size() > SkMeshSpecification::kMaxAttributes) {
229 RETURN_ERROR("A maximum of %zu attributes is allowed.",
231 }
233 if (stride == 0 || stride & (SkMeshSpecification::kStrideAlignment - 1)) {
234 RETURN_ERROR("Vertex stride must be a non-zero multiple of %zu.",
236 }
237 if (stride > SkMeshSpecification::kMaxStride) {
238 RETURN_ERROR("Stride cannot exceed %zu.", SkMeshSpecification::kMaxStride);
239 }
240 for (const auto& a : attributes) {
241 if (a.offset & (SkMeshSpecification::kOffsetAlignment - 1)) {
242 RETURN_ERROR("Attribute offset must be a multiple of %zu.",
244 }
245 // This equivalent to vertexAttributeAccessBeyondStride==VK_FALSE in
246 // VK_KHR_portability_subset. First check is to avoid overflow in second check.
247 if (a.offset >= stride || a.offset + attribute_type_size(a.type) > stride) {
248 RETURN_ERROR("Attribute offset plus size cannot exceed stride.");
249 }
250 }
252}
253
255 uint32_t* deadVaryingMask) {
256 SkASSERT(deadVaryingMask);
257
258 using namespace SkSL;
259 static constexpr int kFailed = -2;
260
261 class Visitor final : public SkSL::ProgramVisitor {
262 public:
263 Visitor(const Context& context) : fContext(context) {}
264
265 void visit(const Program& program) { ProgramVisitor::visit(program); }
266
267 int passthroughFieldIndex() const { return fPassthroughFieldIndex; }
268
269 uint32_t fieldUseMask() const { return fFieldUseMask; }
270
271 protected:
272 bool visitProgramElement(const ProgramElement& p) override {
273 if (p.is<StructDefinition>()) {
274 const auto& def = p.as<StructDefinition>();
275 if (def.type().name() == "Varyings") {
276 fVaryingsType = &def.type();
277 }
278 // No reason to keep looking at this type definition.
279 return false;
280 }
281 if (p.is<FunctionDefinition>() && p.as<FunctionDefinition>().declaration().isMain()) {
282 SkASSERT(!fVaryings);
283 fVaryings = p.as<FunctionDefinition>().declaration().parameters()[0];
284
285 SkASSERT(fVaryingsType && fVaryingsType->matches(fVaryings->type()));
286
287 fInMain = true;
289 fInMain = false;
290 return result;
291 }
293 }
294
295 bool visitStatement(const Statement& s) override {
296 if (!fInMain) {
298 }
299 // We should only get here if are in main and therefore found the varyings parameter.
300 SkASSERT(fVaryings);
301 SkASSERT(fVaryingsType);
302
303 if (fPassthroughFieldIndex == kFailed) {
304 // We've already determined there are return statements that aren't passthrough
305 // or return different fields.
307 }
308 if (!s.is<ReturnStatement>()) {
310 }
311
312 // We just detect simple cases like "return varyings.foo;"
313 const auto& rs = s.as<ReturnStatement>();
314 SkASSERT(rs.expression());
315 if (!rs.expression()->is<FieldAccess>()) {
316 this->passthroughFailed();
318 }
319 const auto& fa = rs.expression()->as<FieldAccess>();
320 if (!fa.base()->is<VariableReference>()) {
321 this->passthroughFailed();
323 }
324 const auto& baseRef = fa.base()->as<VariableReference>();
325 if (baseRef.variable() != fVaryings) {
326 this->passthroughFailed();
328 }
329 if (fPassthroughFieldIndex >= 0) {
330 // We already found an OK return statement. Check if this one returns the same
331 // field.
332 if (fa.fieldIndex() != fPassthroughFieldIndex) {
333 this->passthroughFailed();
335 }
336 // We don't call our base class here because we don't want to hit visitExpression
337 // and mark the returned field as used.
338 return false;
339 }
340 const Field& field = fVaryings->type().fields()[fa.fieldIndex()];
341 if (!field.fType->matches(*fContext.fTypes.fFloat2)) {
342 this->passthroughFailed();
344 }
345 fPassthroughFieldIndex = fa.fieldIndex();
346 // We don't call our base class here because we don't want to hit visitExpression and
347 // mark the returned field as used.
348 return false;
349 }
350
351 bool visitExpression(const Expression& e) override {
352 // Anything before the Varyings struct is defined doesn't matter.
353 if (!fVaryingsType) {
354 return false;
355 }
356 if (!e.is<FieldAccess>()) {
358 }
359 const auto& fa = e.as<FieldAccess>();
360 if (!fa.base()->type().matches(*fVaryingsType)) {
362 }
363 fFieldUseMask |= 1 << fa.fieldIndex();
364 return false;
365 }
366
367 private:
368 void passthroughFailed() {
369 if (fPassthroughFieldIndex >= 0) {
370 fFieldUseMask |= 1 << fPassthroughFieldIndex;
371 }
372 fPassthroughFieldIndex = kFailed;
373 }
374
375 const Context& fContext;
376 const Type* fVaryingsType = nullptr;
377 const Variable* fVaryings = nullptr;
378 int fPassthroughFieldIndex = -1;
379 bool fInMain = false;
380 uint32_t fFieldUseMask = 0;
381 };
382
383 Visitor v(*fsProgram.fContext);
384 v.visit(fsProgram);
385 *deadVaryingMask = ~v.fieldUseMask();
386 return v.passthroughFieldIndex();
387}
388
390 size_t vertexStride,
391 SkSpan<const Varying> varyings,
392 const SkString& vs,
393 const SkString& fs) {
394 return Make(attributes,
395 vertexStride,
396 varyings,
397 vs,
398 fs,
401}
402
404 size_t vertexStride,
405 SkSpan<const Varying> varyings,
406 const SkString& vs,
407 const SkString& fs,
409 return Make(attributes, vertexStride, varyings, vs, fs, std::move(cs), kPremul_SkAlphaType);
410}
411
413 size_t vertexStride,
414 SkSpan<const Varying> varyings,
415 const SkString& vs,
416 const SkString& fs,
418 SkAlphaType at) {
419 SkString attributesStruct("struct Attributes {\n");
420 for (const auto& a : attributes) {
421 attributesStruct.appendf(" %s %s;\n", attribute_type_string(a.type), a.name.c_str());
422 }
423 attributesStruct.append("};\n");
424
425 bool userProvidedPositionVarying = false;
426 for (const auto& v : varyings) {
427 if (v.name.equals("position")) {
428 if (v.type != Varying::Type::kFloat2) {
429 return {nullptr, SkString("Varying \"position\" must have type float2.")};
430 }
431 userProvidedPositionVarying = true;
432 }
433 }
434
436 if (!userProvidedPositionVarying) {
437 // Even though we check the # of varyings in MakeFromSourceWithStructs we check here, too,
438 // to avoid overflow with + 1.
439 if (varyings.size() > kMaxVaryings - 1) {
440 RETURN_FAILURE("A maximum of %zu varyings is allowed.", kMaxVaryings);
441 }
442 for (const auto& v : varyings) {
443 tempVaryings.push_back(v);
444 }
445 tempVaryings.push_back(Varying{Varying::Type::kFloat2, SkString("position")});
446 varyings = tempVaryings;
447 }
448
449 SkString varyingStruct("struct Varyings {\n");
450 for (const auto& v : varyings) {
451 varyingStruct.appendf(" %s %s;\n", varying_type_string(v.type), v.name.c_str());
452 }
453 varyingStruct.append("};\n");
454
455 SkString fullVS;
456 fullVS.append(varyingStruct.c_str());
457 fullVS.append(attributesStruct.c_str());
458 fullVS.append(vs.c_str());
459
460 SkString fullFS;
461 fullFS.append(varyingStruct.c_str());
462 fullFS.append(fs.c_str());
463
464 return MakeFromSourceWithStructs(attributes,
465 vertexStride,
466 varyings,
467 fullVS,
468 fullFS,
469 std::move(cs),
470 at);
471}
472
473SkMeshSpecification::Result SkMeshSpecification::MakeFromSourceWithStructs(
474 SkSpan<const Attribute> attributes,
475 size_t stride,
476 SkSpan<const Varying> varyings,
477 const SkString& vs,
478 const SkString& fs,
480 SkAlphaType at) {
482 return {nullptr, error};
483 }
484
485 for (const auto& a : attributes) {
486 if (!check_name(a.name)) {
487 RETURN_FAILURE("\"%s\" is not a valid attribute name.", a.name.c_str());
488 }
489 }
490
491 if (varyings.size() > kMaxVaryings) {
492 RETURN_FAILURE("A maximum of %zu varyings is allowed.", kMaxVaryings);
493 }
494
495 for (const auto& v : varyings) {
496 if (!check_name(v.name)) {
497 return {nullptr, SkStringPrintf("\"%s\" is not a valid varying name.", v.name.c_str())};
498 }
499 }
500
501 std::vector<Uniform> uniforms;
502 std::vector<Child> children;
503 size_t offset = 0;
504
506
507 // Disable memory pooling; this might slow down compilation slightly, but it will ensure that a
508 // long-lived mesh specification doesn't waste memory.
510 settings.fUseMemoryPool = false;
511
512 // TODO(skia:11209): Add SkCapabilities to the API, check against required version.
513 std::unique_ptr<SkSL::Program> vsProgram = compiler.convertProgram(
515 std::string(vs.c_str()),
516 settings);
517 if (!vsProgram) {
518 RETURN_FAILURE("VS: %s", compiler.errorText().c_str());
519 }
520
522 *vsProgram,
523 &uniforms,
524 &children,
526 &offset);
527 !result) {
528 return {nullptr, std::move(error)};
529 }
530
532 RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
533 }
534
535 std::unique_ptr<SkSL::Program> fsProgram = compiler.convertProgram(
537 std::string(fs.c_str()),
538 settings);
539
540 if (!fsProgram) {
541 RETURN_FAILURE("FS: %s", compiler.errorText().c_str());
542 }
543
545 *fsProgram,
546 &uniforms,
547 &children,
549 &offset);
550 !result) {
551 return {nullptr, std::move(error)};
552 }
553
555 RETURN_FAILURE("Color transform intrinsics are not permitted in custom mesh shaders");
556 }
557
558 ColorType ct = get_fs_color_type(*fsProgram);
559
560 if (ct == ColorType::kNone) {
561 cs = nullptr;
563 } else {
564 if (!cs) {
565 return {nullptr, SkString{"Must provide a color space if FS returns a color."}};
566 }
567 if (at == kUnknown_SkAlphaType) {
568 return {nullptr, SkString{"Must provide a valid alpha type if FS returns a color."}};
569 }
570 }
571
572 uint32_t deadVaryingMask;
573 int passthroughLocalCoordsVaryingIndex =
574 check_for_passthrough_local_coords_and_dead_varyings(*fsProgram, &deadVaryingMask);
575
576 if (passthroughLocalCoordsVaryingIndex >= 0) {
577 SkASSERT(varyings[passthroughLocalCoordsVaryingIndex].type == Varying::Type::kFloat2);
578 }
579
581 stride,
582 varyings,
583 passthroughLocalCoordsVaryingIndex,
584 deadVaryingMask,
585 std::move(uniforms),
586 std::move(children),
587 std::move(vsProgram),
588 std::move(fsProgram),
589 ct,
590 std::move(cs),
591 at)),
592 /*error=*/{}};
593}
594
596
597SkMeshSpecification::SkMeshSpecification(
598 SkSpan<const Attribute> attributes,
599 size_t stride,
600 SkSpan<const Varying> varyings,
601 int passthroughLocalCoordsVaryingIndex,
602 uint32_t deadVaryingMask,
603 std::vector<Uniform> uniforms,
604 std::vector<Child> children,
605 std::unique_ptr<const SkSL::Program> vs,
606 std::unique_ptr<const SkSL::Program> fs,
607 ColorType ct,
609 SkAlphaType at)
610 : fAttributes(attributes.begin(), attributes.end())
611 , fVaryings(varyings.begin(), varyings.end())
612 , fUniforms(std::move(uniforms))
613 , fChildren(std::move(children))
614 , fVS(std::move(vs))
615 , fFS(std::move(fs))
616 , fStride(stride)
617 , fPassthroughLocalCoordsVaryingIndex(passthroughLocalCoordsVaryingIndex)
618 , fDeadVaryingMask(deadVaryingMask)
619 , fColorType(ct)
620 , fColorSpace(std::move(cs))
621 , fAlphaType(at) {
622 fHash = SkChecksum::Hash32(fVS->fSource->c_str(), fVS->fSource->size(), 0);
623 fHash = SkChecksum::Hash32(fFS->fSource->c_str(), fFS->fSource->size(), fHash);
624
625 // The attributes and varyings SkSL struct declarations are included in the program source.
626 // However, the attribute offsets and types need to be included, the latter because the SkSL
627 // struct definition has the GPU type but not the CPU data format.
628 for (const auto& a : fAttributes) {
629 fHash = SkChecksum::Hash32(&a.offset, sizeof(a.offset), fHash);
630 fHash = SkChecksum::Hash32(&a.type, sizeof(a.type), fHash);
631 }
632
633 fHash = SkChecksum::Hash32(&stride, sizeof(stride), fHash);
634
635 uint64_t csHash = fColorSpace ? fColorSpace->hash() : 0;
636 fHash = SkChecksum::Hash32(&csHash, sizeof(csHash), fHash);
637
638 auto atInt = static_cast<uint32_t>(fAlphaType);
639 fHash = SkChecksum::Hash32(&atInt, sizeof(atInt), fHash);
640}
641
643 return fUniforms.empty() ? 0
644 : SkAlign4(fUniforms.back().offset + fUniforms.back().sizeInBytes());
645}
646
647const Uniform* SkMeshSpecification::findUniform(std::string_view name) const {
648 for (const Uniform& uniform : fUniforms) {
649 if (uniform.name == name) {
650 return &uniform;
651 }
652 }
653 return nullptr;
654}
655
656const Child* SkMeshSpecification::findChild(std::string_view name) const {
657 for (const Child& child : fChildren) {
658 if (child.name == name) {
659 return &child;
660 }
661 }
662 return nullptr;
663}
664
665const Attribute* SkMeshSpecification::findAttribute(std::string_view name) const {
666 for (const Attribute& attr : fAttributes) {
667 if (name == attr.name.c_str()) {
668 return &attr;
669 }
670 }
671 return nullptr;
672}
673
674const Varying* SkMeshSpecification::findVarying(std::string_view name) const {
675 for (const Varying& varying : fVaryings) {
676 if (name == varying.name.c_str()) {
677 return &varying;
678 }
679 }
680 return nullptr;
681}
682
683//////////////////////////////////////////////////////////////////////////////
684
685SkMesh::SkMesh() = default;
686SkMesh::~SkMesh() = default;
687
688SkMesh::SkMesh(const SkMesh&) = default;
689SkMesh::SkMesh(SkMesh&&) = default;
690
691SkMesh& SkMesh::operator=(const SkMesh&) = default;
692SkMesh& SkMesh::operator=(SkMesh&&) = default;
693
695 Mode mode,
697 size_t vertexCount,
698 size_t vertexOffset,
699 sk_sp<const SkData> uniforms,
700 SkSpan<ChildPtr> children,
701 const SkRect& bounds) {
702 SkMesh mesh;
703 mesh.fSpec = std::move(spec);
704 mesh.fMode = mode;
705 mesh.fVB = std::move(vb);
706 mesh.fUniforms = std::move(uniforms);
707 mesh.fChildren.push_back_n(children.size(), children.data());
708 mesh.fVCount = vertexCount;
709 mesh.fVOffset = vertexOffset;
710 mesh.fBounds = bounds;
711 auto [valid, msg] = mesh.validate();
712 if (!valid) {
713 mesh = {};
714 }
715 return {std::move(mesh), std::move(msg)};
716}
717
719 Mode mode,
721 size_t vertexCount,
722 size_t vertexOffset,
724 size_t indexCount,
725 size_t indexOffset,
726 sk_sp<const SkData> uniforms,
727 SkSpan<ChildPtr> children,
728 const SkRect& bounds) {
729 if (!ib) {
730 // We check this before calling validate to disambiguate from a non-indexed mesh where
731 // IB is expected to be null.
732 return {{}, SkString{"An index buffer is required."}};
733 }
734 SkMesh mesh;
735 mesh.fSpec = std::move(spec);
736 mesh.fMode = mode;
737 mesh.fVB = std::move(vb);
738 mesh.fVCount = vertexCount;
739 mesh.fVOffset = vertexOffset;
740 mesh.fIB = std::move(ib);
741 mesh.fUniforms = std::move(uniforms);
742 mesh.fChildren.push_back_n(children.size(), children.data());
743 mesh.fICount = indexCount;
744 mesh.fIOffset = indexOffset;
745 mesh.fBounds = bounds;
746 auto [valid, msg] = mesh.validate();
747 if (!valid) {
748 mesh = {};
749 }
750 return {std::move(mesh), std::move(msg)};
751}
752
753bool SkMesh::isValid() const {
754 bool valid = SkToBool(fSpec);
755 SkASSERT(valid == std::get<0>(this->validate()));
756 return valid;
757}
758
760 switch (mode) {
761 case SkMesh::Mode::kTriangles: return 3;
762 case SkMesh::Mode::kTriangleStrip: return 3;
763 }
765}
766
767std::tuple<bool, SkString> SkMesh::validate() const {
768#define FAIL_MESH_VALIDATE(...) return std::make_tuple(false, SkStringPrintf(__VA_ARGS__))
769 if (!fSpec) {
770 FAIL_MESH_VALIDATE("SkMeshSpecification is required.");
771 }
772
773 if (!fVB) {
774 FAIL_MESH_VALIDATE("A vertex buffer is required.");
775 }
776
777 if (fSpec->children().size() != SkToSizeT(fChildren.size())) {
778 FAIL_MESH_VALIDATE("The mesh specification declares %zu child effects, "
779 "but the mesh supplies %d.",
780 fSpec->children().size(),
781 fChildren.size());
782 }
783
784 for (int index = 0; index < fChildren.size(); ++index) {
785 const SkRuntimeEffect::Child& meshSpecChild = fSpec->children()[index];
786 if (fChildren[index].type().has_value()) {
787 if (meshSpecChild.type != fChildren[index].type()) {
788 FAIL_MESH_VALIDATE("Child effect '%.*s' was specified as a %s, but passed as a %s.",
789 (int)meshSpecChild.name.size(), meshSpecChild.name.data(),
791 SkRuntimeEffectPriv::ChildTypeToStr(*fChildren[index].type()));
792 }
793 }
794 }
795
796 auto vb = static_cast<SkMeshPriv::VB*>(fVB.get());
797 auto ib = static_cast<SkMeshPriv::IB*>(fIB.get());
798
799 SkSafeMath sm;
800 size_t vsize = sm.mul(fSpec->stride(), fVCount);
801 if (sm.add(vsize, fVOffset) > vb->size()) {
802 FAIL_MESH_VALIDATE("The vertex buffer offset and vertex count reads beyond the end of the"
803 " vertex buffer.");
804 }
805
806 if (fVOffset%fSpec->stride() != 0) {
807 FAIL_MESH_VALIDATE("The vertex offset (%zu) must be a multiple of the vertex stride (%zu).",
808 fVOffset,
809 fSpec->stride());
810 }
811
812 if (size_t uniformSize = fSpec->uniformSize()) {
813 if (!fUniforms || fUniforms->size() < uniformSize) {
814 FAIL_MESH_VALIDATE("The uniform data is %zu bytes but must be at least %zu.",
815 fUniforms->size(),
816 uniformSize);
817 }
818 }
819
820 auto modeToStr = [](Mode m) {
821 switch (m) {
822 case Mode::kTriangles: return "triangles";
823 case Mode::kTriangleStrip: return "triangle-strip";
824 }
826 };
827 if (ib) {
828 if (fICount < min_vcount_for_mode(fMode)) {
829 FAIL_MESH_VALIDATE("%s mode requires at least %zu indices but index count is %zu.",
830 modeToStr(fMode),
831 min_vcount_for_mode(fMode),
832 fICount);
833 }
834 size_t isize = sm.mul(sizeof(uint16_t), fICount);
835 if (sm.add(isize, fIOffset) > ib->size()) {
836 FAIL_MESH_VALIDATE("The index buffer offset and index count reads beyond the end of the"
837 " index buffer.");
838
839 }
840 // If we allow 32 bit indices then this should enforce 4 byte alignment in that case.
841 if (!SkIsAlign2(fIOffset)) {
842 FAIL_MESH_VALIDATE("The index offset must be a multiple of 2.");
843 }
844 } else {
845 if (fVCount < min_vcount_for_mode(fMode)) {
846 FAIL_MESH_VALIDATE("%s mode requires at least %zu vertices but vertex count is %zu.",
847 modeToStr(fMode),
848 min_vcount_for_mode(fMode),
849 fICount);
850 }
851 SkASSERT(!fICount);
852 SkASSERT(!fIOffset);
853 }
854
855 if (!sm.ok()) {
856 FAIL_MESH_VALIDATE("Overflow");
857 }
858#undef FAIL_MESH_VALIDATE
859 return {true, {}};
860}
861
862//////////////////////////////////////////////////////////////////////////////
863
864static inline bool check_update(const void* data, size_t offset, size_t size, size_t bufferSize) {
865 SkSafeMath sm;
866 return data &&
867 size &&
869 SkIsAlign4(size) &&
870 sm.add(offset, size) <= bufferSize &&
871 sm.ok();
872}
873
875 const void* data,
876 size_t offset,
877 size_t size) {
878 return check_update(data, offset, size, this->size()) && this->onUpdate(dc, data, offset, size);
879}
880
882 const void* data,
883 size_t offset,
884 size_t size) {
885 return check_update(data, offset, size, this->size()) && this->onUpdate(dc, data, offset, size);
886}
887
888namespace SkMeshes {
889sk_sp<IndexBuffer> MakeIndexBuffer(const void* data, size_t size) {
890 return SkMeshPriv::CpuIndexBuffer::Make(data, size);
891}
892
894 if (!src) {
895 return nullptr;
896 }
897 auto* ib = static_cast<SkMeshPriv::IB*>(src.get());
898 const void* data = ib->peek();
899 if (!data) {
900 return nullptr;
901 }
902 return MakeIndexBuffer(data, ib->size());
903}
904
905sk_sp<VertexBuffer> MakeVertexBuffer(const void* data, size_t size) {
906 return SkMeshPriv::CpuVertexBuffer::Make(data, size);
907}
908
910 if (!src) {
911 return nullptr;
912 }
913 auto* vb = static_cast<SkMeshPriv::VB*>(src.get());
914 const void* data = vb->peek();
915 if (!data) {
916 return nullptr;
917 }
918 return MakeVertexBuffer(data, vb->size());
919}
920} // namespace SkMeshes
SkColorType fColorType
static constexpr bool SkIsAlign2(T x)
Definition SkAlign.h:19
static constexpr bool SkIsAlign4(T x)
Definition SkAlign.h:20
static constexpr T SkAlign4(T x)
Definition SkAlign.h:16
SkAlphaType
Definition SkAlphaType.h:26
@ kUnknown_SkAlphaType
uninitialized
Definition SkAlphaType.h:27
@ kPremul_SkAlphaType
pixel components are premultiplied by alpha
Definition SkAlphaType.h:29
#define SkUNREACHABLE
Definition SkAssert.h:135
#define SkASSERT(cond)
Definition SkAssert.h:116
static bool ok(int result)
constexpr bool SkIsPow2(T value)
Definition SkMath.h:51
static const char * attribute_type_string(Attribute::Type type)
Definition SkMesh.cpp:186
#define FAIL_MESH_VALIDATE(...)
static size_t min_vcount_for_mode(SkMesh::Mode mode)
Definition SkMesh.cpp:759
std::tuple< bool, SkString > check_vertex_offsets_and_stride(SkSpan< const Attribute > attributes, size_t stride)
Definition SkMesh.cpp:212
static std::tuple< bool, SkString > gather_uniforms_and_check_for_main(const SkSL::Program &program, std::vector< Uniform > *uniforms, std::vector< Child > *children, SkMeshSpecification::Uniform::Flags stage, size_t *offset)
Definition SkMesh.cpp:76
static std::vector< Uniform >::iterator find_uniform(std::vector< Uniform > &uniforms, std::string_view name)
Definition SkMesh.cpp:69
static bool check_update(const void *data, size_t offset, size_t size, size_t bufferSize)
Definition SkMesh.cpp:864
static size_t attribute_type_size(Attribute::Type type)
Definition SkMesh.cpp:175
static bool check_name(const SkString &name)
Definition SkMesh.cpp:163
#define RETURN_FAILURE(...)
Definition SkMesh.cpp:60
#define RETURN_SUCCESS
Definition SkMesh.cpp:64
int check_for_passthrough_local_coords_and_dead_varyings(const SkSL::Program &fsProgram, uint32_t *deadVaryingMask)
Definition SkMesh.cpp:254
#define RETURN_ERROR(...)
Definition SkMesh.cpp:62
static const char * varying_type_string(Varying::Type type)
Definition SkMesh.cpp:197
ColorType get_fs_color_type(const SkSL::Program &fsProgram)
Definition SkMesh.cpp:139
const Context & fContext
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
constexpr size_t SkToSizeT(S x)
Definition SkTo.h:31
static constexpr bool SkToBool(const T &x)
Definition SkTo.h:35
uint64_t hash() const
static sk_sp< SkColorSpace > MakeSRGB()
size_t size() const
Definition SkData.h:30
static sk_sp< Base > Make(const void *data, size_t size)
Definition SkMeshPriv.h:123
SkSpan< const Uniform > uniforms() const
Definition SkMesh.h:170
static constexpr size_t kStrideAlignment
Definition SkMesh.h:70
size_t stride() const
Definition SkMesh.h:187
const Uniform * findUniform(std::string_view name) const
Definition SkMesh.cpp:647
static constexpr size_t kMaxVaryings
Definition SkMesh.h:72
SkSpan< const Child > children() const
Definition SkMesh.h:173
static constexpr size_t kMaxAttributes
Definition SkMesh.h:69
static constexpr size_t kOffsetAlignment
Definition SkMesh.h:71
static constexpr size_t kMaxStride
Definition SkMesh.h:68
static Result Make(SkSpan< const Attribute > attributes, size_t vertexStride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs)
Definition SkMesh.cpp:389
SkSpan< const Attribute > attributes() const
Definition SkMesh.h:157
size_t uniformSize() const
Definition SkMesh.cpp:642
const Varying * findVarying(std::string_view name) const
Definition SkMesh.cpp:674
SkRuntimeEffect::Uniform Uniform
Definition SkMesh.h:106
SkRuntimeEffect::Child Child
Definition SkMesh.h:107
const Attribute * findAttribute(std::string_view name) const
Definition SkMesh.cpp:665
const Child * findChild(std::string_view name) const
Definition SkMesh.cpp:656
bool update(GrDirectContext *, const void *data, size_t offset, size_t size)
Definition SkMesh.cpp:874
virtual size_t size() const =0
virtual bool onUpdate(GrDirectContext *, const void *data, size_t offset, size_t size)=0
bool update(GrDirectContext *, const void *data, size_t offset, size_t size)
Definition SkMesh.cpp:881
SkMesh & operator=(const SkMesh &)
SkSpan< const ChildPtr > children() const
Definition SkMesh.h:367
bool isValid() const
Definition SkMesh.cpp:753
static Result Make(sk_sp< SkMeshSpecification >, Mode, sk_sp< VertexBuffer >, size_t vertexCount, size_t vertexOffset, sk_sp< const SkData > uniforms, SkSpan< ChildPtr > children, const SkRect &bounds)
Definition SkMesh.cpp:694
size_t vertexCount() const
Definition SkMesh.h:356
size_t vertexOffset() const
Definition SkMesh.h:355
Mode mode() const
Definition SkMesh.h:350
size_t indexCount() const
Definition SkMesh.h:362
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, SkSpan< ChildPtr > children, const SkRect &bounds)
Definition SkMesh.cpp:718
SkRect bounds() const
Definition SkMesh.h:369
size_t indexOffset() const
Definition SkMesh.h:361
const SkData * uniforms() const
Definition SkMesh.h:365
SkMeshSpecification * spec() const
Definition SkMesh.h:348
static const char * ChildTypeToStr(SkRuntimeEffect::ChildType type)
static SkRuntimeEffect::Uniform VarAsUniform(const SkSL::Variable &, const SkSL::Context &, size_t *offset)
static SkRuntimeEffect::Child VarAsChild(const SkSL::Variable &var, int index)
const FunctionDeclaration & declaration() const
std::unique_ptr< Statement > & declaration()
const T & as() const
Definition SkSLIRNode.h:133
bool visit(const Program &program)
const Type & type() const
const Type & type() const
Definition SkSLSymbol.h:42
virtual bool visitStatement(typename T::Statement &statement)
virtual bool visitExpression(typename T::Expression &expression)
virtual bool visitProgramElement(typename T::ProgramElement &programElement)
bool matches(const Type &other) const
Definition SkSLType.h:269
size_t add(size_t x, size_t y)
Definition SkSafeMath.h:33
bool ok() const
Definition SkSafeMath.h:26
size_t mul(size_t x, size_t y)
Definition SkSafeMath.h:29
constexpr bool empty() const
Definition SkSpan_impl.h:96
constexpr size_t size() const
Definition SkSpan_impl.h:95
void append(const char text[])
Definition SkString.h:203
const char * c_str() const
Definition SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition SkString.cpp:550
static const char * begin(const StringSlice &s)
Definition editor.cpp:252
struct MyStruct s
struct MyStruct a[10]
glong glong end
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
const char * name
Definition fuchsia.cc:50
uint32_t Hash32(const void *data, size_t bytes, uint32_t seed)
SK_API sk_sp< SkMesh::IndexBuffer > CopyIndexBuffer(const sk_sp< SkMesh::IndexBuffer > &)
Definition SkMesh.cpp:893
SK_API sk_sp< SkMesh::IndexBuffer > MakeIndexBuffer(const void *data, size_t size)
Definition SkMesh.cpp:889
SK_API sk_sp< SkMesh::VertexBuffer > MakeVertexBuffer(const void *, size_t size)
Definition SkMesh.cpp:905
SK_API sk_sp< SkMesh::VertexBuffer > CopyVertexBuffer(const sk_sp< SkMesh::VertexBuffer > &)
Definition SkMesh.cpp:909
bool CallsColorTransformIntrinsics(const Program &program)
Definition ref_ptr.h:256
Point offset
SkMeshSpecification::ColorType ColorType
Definition SkMeshPriv.h:18
const Type * fType
Definition SkSLType.h:88
ElementsCollection elements() const
std::shared_ptr< Context > fContext