Flutter Engine
The Flutter Engine
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,
525 SkMeshSpecification::Uniform::Flags::kVertex_Flag,
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,
548 SkMeshSpecification::Uniform::Flags::kFragment_Flag,
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 {
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
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
SkAlphaType fAlphaType
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
SkMesh::VertexBuffer VertexBuffer
Definition: SkMesh.cpp:58
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
SkMeshSpecification::Attribute Attribute
Definition: SkMesh.cpp:54
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
SkMeshSpecification::Varying Varying
Definition: SkMesh.cpp:55
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
SkMesh::IndexBuffer IndexBuffer
Definition: SkMesh.cpp:57
#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 SkStringPrintf(const char *format,...) SK_PRINTF_LIKE(1
Creates a new string and writes into it using a printf()-style format.
constexpr size_t SkToSizeT(S x)
Definition: SkTo.h:31
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
GLenum type
uint64_t hash() const
Definition: SkColorSpace.h:222
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
Definition: SkMesh.h:263
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
Mode
Definition: SkMesh.h:308
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
uint32_t Hash32(const void *data, size_t bytes, uint32_t seed)
Definition: SkChecksum.cpp:113
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
Optional< SkRect > bounds
Definition: SkRecords.h:189
SkMesh mesh
Definition: SkRecords.h:345
bool CallsColorTransformIntrinsics(const Program &program)
Visitor(Ts...) -> Visitor< Ts... >
@ kNone
Definition: layer.h:53
DEF_SWITCHES_START aot vmservice shared library name
Definition: switches.h:32
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive mode
Definition: switches.h:228
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
compiler
Definition: malisc.py:17
const myers::Point & get< 0 >(const myers::Segment &s)
Definition: Myers.h:80
Definition: ref_ptr.h:256
SeparatedVector2 offset
SkMeshSpecification::ColorType ColorType
Definition: SkMeshPriv.h:18
std::string_view name
const Type * fType
Definition: SkSLType.h:88
ElementsCollection elements() const
Definition: SkSLProgram.h:140
std::shared_ptr< Context > fContext
Definition: SkSLProgram.h:154
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63