Flutter Engine
The Flutter Engine
MeshTest.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
12#include "include/core/SkData.h"
13#include "include/core/SkMesh.h"
14#include "include/core/SkRect.h"
17#include "include/core/SkSpan.h"
21#include "src/base/SkZip.h"
22#include "src/core/SkMeshPriv.h"
23#include "tests/Test.h"
24
25#include <algorithm>
26#include <cstddef>
27#include <cstdint>
28#include <initializer_list>
29#include <limits>
30#include <string>
31#include <string_view>
32#include <tuple>
33#include <utility>
34#include <vector>
35
38
39static const char* attr_type_str(const Attribute::Type type) {
40 switch (type) {
41 case Attribute::Type::kFloat: return "float";
42 case Attribute::Type::kFloat2: return "float2";
43 case Attribute::Type::kFloat3: return "float3";
44 case Attribute::Type::kFloat4: return "float4";
45 case Attribute::Type::kUByte4_unorm: return "ubyte4_unorm";
46 }
48}
49
50static const char* var_type_str(const Varying::Type type) {
51 switch (type) {
52 case Varying::Type::kFloat: return "float";
53 case Varying::Type::kFloat2: return "float2";
54 case Varying::Type::kFloat3: return "float3";
55 case Varying::Type::kFloat4: return "float4";
56 case Varying::Type::kHalf: return "half";
57 case Varying::Type::kHalf2: return "half2";
58 case Varying::Type::kHalf3: return "half3";
59 case Varying::Type::kHalf4: return "half4";
60 }
62}
63
65 size_t stride,
66 SkSpan<const Varying> varyings,
67 const SkString& vs,
68 const SkString& fs) {
69 static constexpr size_t kMax = 10;
71 result.appendf("Attributes (count=%zu, stride=%zu):\n", attributes.size(), stride);
72 for (size_t i = 0; i < std::min(kMax, attributes.size()); ++i) {
73 const auto& a = attributes[i];
74 result.appendf(" {%-10s, %3zu, \"%s\"}\n", attr_type_str(a.type), a.offset, a.name.c_str());
75 }
76 if (kMax < attributes.size()) {
77 result.append(" ...\n");
78 }
79
80 result.appendf("Varyings (count=%zu):\n", varyings.size());
81 for (size_t i = 0; i < std::min(kMax, varyings.size()); ++i) {
82 const auto& v = varyings[i];
83 result.appendf(" {%5s, \"%s\"}\n", var_type_str(v.type), v.name.c_str());
84 }
85 if (kMax < varyings.size()) {
86 result.append(" ...\n");
87 }
88
89 result.appendf("\n--VS--\n%s\n------\n", vs.c_str());
90 result.appendf("\n--FS--\n%s\n------\n", fs.c_str());
91 return result;
92}
93
95 SkSpan<const Attribute> attributes,
96 size_t stride,
97 SkSpan<const Varying> varyings,
98 const SkString& vs,
99 const SkString& fs,
100 const char* expectedErrorSubstring = nullptr) {
101 auto [spec, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
102 if (spec) {
104 "Expected to fail but succeeded:\n%s",
105 make_description(attributes, stride, varyings, vs, fs).c_str());
106 return false;
107 }
108 if (expectedErrorSubstring && !error.contains(expectedErrorSubstring)) {
110 " Expected: %s\n"
111 "Actual error: %s\n",
112 expectedErrorSubstring, error.c_str());
113 return false;
114 }
115 return true;
116}
117
119 SkSpan<const Attribute> attributes,
120 size_t stride,
121 SkSpan<const Varying> varyings,
122 const SkString& vs,
123 const SkString& fs,
124 sk_sp<SkMeshSpecification>* spec = nullptr) {
125 auto [s, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
126 if (s) {
127 REPORTER_ASSERT(reporter, error.isEmpty());
128 if (spec) {
129 *spec = std::move(s);
130 }
131 return true;
132 }
134 "Expected to succeed but failed:\n%sError:\n%s",
135 make_description(attributes, stride, varyings, vs, fs).c_str(),
136 error.c_str());
137 return false;
138}
139
140// Simple valid strings to make specifications
141static const SkString kValidVS {R"(
142Varyings main(const Attributes attrs) {
143 Varyings v;
144 return v;
145})"};
146
147// There are multiple valid VS signatures.
148static const SkString kValidFSes[]{
149 SkString{"float2 main(const Varyings varyings) { return float2(10); }"},
150 SkString{R"(
151 float2 main(const Varyings varyings, out half4 color) {
152 color = half4(.2);
153 return float2(10);
154 }
155 )"},
156};
157
158// Simple valid attributes, stride, and varyings to make specifications
159static const Attribute kValidAttrs[] = {
161};
162static constexpr size_t kValidStride = 4*4;
163static const Varying kValidVaryings[] = {
165};
166
167DEF_TEST(MeshSpec_Valid, reporter) {
168 for (const auto& validFS : kValidFSes) {
173 kValidVS,
174 validFS)) {
175 return;
176 }
177 }
178}
179
180DEF_TEST(MeshSpec_InvalidSignature, reporter) {
181 static constexpr const char* kVSBody = "{ return float2(10); }";
182
183 static constexpr const char* kInvalidVSSigs[] {
184 "float3 main(const Attributes attrs)", // bad return
185 "Varyings main(Attributes attrs)", // non-const Attributes
186 "Varyings main(out Attributes attrs)", // out Varyings
187 "Varyings main()", // no Attributes
188 "Varyings main(const Varyings v, float2)" // extra arg
189 };
190
191 static constexpr const char* kNoColorFSBody = "{ return float2(10); }";
192
193 static constexpr const char* kInvalidNoColorFSSigs[] {
194 "half2 main(const Varyings v)", // bad return
195 "float2 main(const Attributes v)", // wrong param type
196 "float2 main(inout Varyings attrs)", // inout Varyings
197 "float2 main(Varyings v)", // non-const Varyings
198 "float2 main()", // no args
199 "float2 main(const Varyings, float)" // extra arg
200 };
201
202 static constexpr const char* kColorFSBody = "{ color = half4(.2); return float2(10); }";
203
204 static constexpr const char* kInvalidColorFSSigs[] {
205 "half2 main(const Varyings v, out half4 color)", // bad return
206 "float2 main(const Attributes v, out half4 color)", // wrong first param type
207 "float2 main(const Varyings v, out half3 color)", // wrong second param type
208 "float2 main(out Varyings v, out half4 color)", // out Varyings
209 "float2 main(const Varyings v, half4 color)", // in color
210 "float2 main(const Varyings v, out half4 color, float)" // extra arg
211 };
212
213 for (const char* vsSig : kInvalidVSSigs) {
214 SkString invalidVS;
215 invalidVS.appendf("%s %s", vsSig, kVSBody);
216 for (const auto& validFS : kValidFSes) {
221 invalidVS,
222 validFS)) {
223 return;
224 }
225 }
226 }
227
228 for (const char* noColorFSSig : kInvalidNoColorFSSigs) {
229 SkString invalidFS;
230 invalidFS.appendf("%s %s", noColorFSSig, kNoColorFSBody);
235 kValidVS,
236 invalidFS)) {
237 return;
238 }
239 }
240
241 for (const char* colorFSSig : kInvalidColorFSSigs) {
242 SkString invalidFS;
243 invalidFS.appendf("%s %s", colorFSSig, kColorFSBody);
248 kValidVS,
249 invalidFS)) {
250 return;
251 }
252 }
253}
254
255// We allow the optional out color from the FS to either be float4 or half4
256DEF_TEST(MeshSpec_Float4Color, reporter) {
257 static const SkString kFloat4FS {
258 R"(
259 float2 main(const Varyings varyings, out float4 color) {
260 color = float4(.2); return float2(10);
261 }
262 )"
263 };
269 kFloat4FS);
270}
271
272DEF_TEST(MeshSpec_DisallowsChildEffectInVertex, reporter) {
273 static constexpr const char* kChildEffects[] {
274 "uniform shader myshader;",
275 "uniform colorFilter mycolorfilter;",
276 "uniform blender myblender;"
277 };
278
279 for (const auto& global : kChildEffects) {
280 SkString vsWithChild{global};
281 vsWithChild.append(kValidVS);
282
283 SkString fsWithChild{global};
284 fsWithChild.append(kValidFSes[0]);
285
290 vsWithChild,
291 kValidFSes[0],
292 "effects are not permitted in mesh vertex shaders")) {
293 return;
294 }
295
300 vsWithChild,
301 fsWithChild,
302 "effects are not permitted in mesh vertex shaders")) {
303 return;
305 }
306}
307
308DEF_TEST(MeshSpec_AllowsChildEffectInFragment, reporter) {
309 static constexpr const char* kChildEffects[] {
310 "uniform shader myshader;",
311 "uniform colorFilter mycolorfilter; uniform shader myshader;",
312 "uniform shader myshader; uniform blender myblender; uniform colorFilter mycolorfilter;"
313 };
314
315 for (const auto& global : kChildEffects) {
316 SkString fsWithChild{global};
317 fsWithChild.append(kValidFSes[0]);
318
323 kValidVS,
324 fsWithChild)) {
325 return;
327 }
328}
329
330DEF_TEST(MeshSpec_FindChild, reporter) {
331 SkString fsWithChild{"uniform shader myshader;"
332 "uniform blender myblender;"
333 "uniform colorFilter mycolorfilter;"};
334 fsWithChild.append(kValidFSes[0]);
335
341 kValidVS,
342 fsWithChild,
343 &meshSpec)) {
344 return;
345 }
346
347 REPORTER_ASSERT(reporter, meshSpec->findChild("myshader")->index == 0);
348 REPORTER_ASSERT(reporter, meshSpec->findChild("myblender")->index == 1);
349 REPORTER_ASSERT(reporter, meshSpec->findChild("mycolorfilter")->index == 2);
350 REPORTER_ASSERT(reporter, !meshSpec->findChild("missing"));
351}
352
353DEF_TEST(Mesh_ChildEffectsMatchSpec, reporter) {
354 auto test = [&](const char* prefix,
356 const char* expectedError = nullptr) {
357 SkString fsWithChild{prefix};
358 fsWithChild.append(kValidFSes[0]);
359
365 kValidVS,
366 fsWithChild,
367 &meshSpec)) {
368 return;
369 }
370
371 constexpr float kVertexCount = 4;
372 sk_sp<SkMesh::VertexBuffer> vertexBuffer =
376 vertexBuffer,
378 /*vertexOffset=*/0,
379 /*uniforms=*/nullptr,
380 children,
382
383 if (expectedError) {
384 REPORTER_ASSERT(reporter, !result.mesh.isValid());
386 result.error.contains(expectedError),
387 "Expected: '%s'\n"
388 " Actual: '%s'\n", expectedError, result.error.c_str());
389 } else {
390 REPORTER_ASSERT(reporter, result.mesh.isValid());
392 result.error.isEmpty(),
393 "Expected: no errors\n"
394 " Actual: '%s'\n", result.error.c_str());
395 }
396 };
397
401 SkRuntimeEffect::ChildPtr childNull[1] = {};
402
403 // These are expected to report a count mismatch.
404 test("uniform shader myshader;", {},
405 "The mesh specification declares 1 child effects, but the mesh supplies 0.");
406 test("", childShader,
407 "The mesh specification declares 0 child effects, but the mesh supplies 1.");
408
409 // These are expected to report a type mismatch.
410 test("uniform shader myshader;", childFilter,
411 "Child effect 'myshader' was specified as a shader, but passed as a color filter.");
412 test("uniform shader myshader;", childBlender,
413 "Child effect 'myshader' was specified as a shader, but passed as a blender.");
414 test("uniform colorFilter myfilter;", childShader,
415 "Child effect 'myfilter' was specified as a color filter, but passed as a shader.");
416 test("uniform colorFilter myfilter;", childBlender,
417 "Child effect 'myfilter' was specified as a color filter, but passed as a blender.");
418 test("uniform blender myblender;", childShader,
419 "Child effect 'myblender' was specified as a blender, but passed as a shader.");
420 test("uniform blender myblender;", childFilter,
421 "Child effect 'myblender' was specified as a blender, but passed as a color filter.");
422
423 // Null children are supported.
424 test("uniform shader myshader;", childNull);
425 test("uniform shader myfilter;", childNull);
426 test("uniform shader myblender;", childNull);
427
428 // Properly-typed child effects are supported.
429 test("uniform shader myshader;", childShader);
430 test("uniform colorFilter myfilter;", childFilter);
431 test("uniform blender myblender;", childBlender);
432
433}
434
435DEF_TEST(MeshSpec_ValidUniforms, reporter) {
437 using Type = Uniform::Type;
438 using Flags = Uniform::Flags;
439
440 constexpr Flags kVS = Uniform::kVertex_Flag;
441 constexpr Flags kFS = Uniform::kFragment_Flag;
443 constexpr Flags kHalfP = Uniform::kHalfPrecision_Flag;
444
445 auto make_uni = [](Type type,
446 std::string_view name,
447 size_t offset,
448 uint32_t flags,
449 int count = 0) {
450 if (count) {
452 } else {
454 return Uniform{name, offset, type, 1, flags};
455 }
456 };
457
458 // Each test case is a set of VS and FS uniform declarations followed and the expected output
459 // of SkMeshSpecification::uniforms().
460 struct {
461 const std::vector<const char*> vsUniformDecls;
462 const std::vector<const char*> fsUniformDecls;
463 const std::vector<SkMeshSpecification::Uniform> expectations;
464 } static kTestCases[] {
465 // A single VS uniform.
466 {
467 {
468 "uniform float x;"
469 },
470 {},
471 {
472 make_uni(Type::kFloat, "x", 0, kVS)
473 }
474 },
475
476 // A single FS uniform.
477 {
478 {},
479 {
480 "uniform float2 v;"
481 },
482 {
483 make_uni(Type::kFloat2, "v", 0, kFS)
484 }
485 },
486
487 // A single uniform in both that uses color layout.
488 {
489 {
490 "layout(color) uniform float4 color;",
491 },
492 {
493 "layout(color) uniform float4 color;",
494 },
495 {
496 make_uni(Type::kFloat4, "color", 0, kVS|kFS|kColor)
497 }
498 },
499
500 // A shared uniform after an unshared vertex uniform
501 {
502 {
503 "layout(color) uniform float4 color;",
504 " uniform float x[5];",
505 },
506 {
507 "uniform float x[5];",
508 },
509 {
510 make_uni(Type::kFloat4, "color", 0, kVS|kColor, 0),
511 make_uni(Type::kFloat , "x" , 16, kVS|kFS , 5)
512 }
513 },
514
515 // A shared uniform before an unshared vertex uniform
516 {
517 {
518 "uniform half x[2];",
519 "uniform int y;",
520 },
521 {
522 "uniform half x[2];",
523 },
524 {
525 make_uni(Type::kFloat, "x", 0, kVS|kFS|kHalfP, 2),
526 make_uni(Type::kInt, "y", 8, kVS , 0)
527 }
528 },
529
530 // A shared uniform after an unshared fragment uniform
531 {
532 {
533 "uniform float3x3 m;",
534 },
535 {
536 "uniform int2 i2;",
537 "uniform float3x3 m;",
538 },
539 {
540 make_uni(Type::kFloat3x3, "m" , 0, kVS|kFS),
541 make_uni(Type::kInt2 , "i2", 36, kFS )
542 }
543 },
544
545 // A shared uniform before an unshared fragment uniform
546 {
547 {
548 "uniform half4x4 m[4];",
549 },
550 {
551 "uniform half4x4 m[4];",
552 "uniform int3 i3[1];",
553 },
554 {
555 make_uni(Type::kFloat4x4, "m", 0, kVS|kFS|kHalfP, 4),
556 make_uni(Type::kInt3, "i3", 256, kFS , 1)
557 }
558 },
559
560 // Complex case with 2 shared uniforms that are declared in the opposite order.
561 {
562 {
563 "uniform float x;"
564 "uniform half4x4 m[4];", // shared
565 "uniform int2 i2[2];"
566 "uniform float3 v[8];" // shared
567 "uniform int3 i3;"
568 },
569 {
570 "uniform float y;"
571 "uniform float3 v[8];" // shared
572 "uniform int4 i4[2];"
573 "uniform half4x4 m[4];", // shared
574 "uniform int i;"
575 },
576 {
577 make_uni(Type::kFloat, "x" , 0, kVS , 0),
578 make_uni(Type::kFloat4x4, "m" , 4, kVS|kFS|kHalfP, 4),
579 make_uni(Type::kInt2, "i2", 260, kVS , 2),
580 make_uni(Type::kFloat3, "v" , 276, kVS|kFS , 8),
581 make_uni(Type::kInt3, "i3", 372, kVS , 0),
582 make_uni(Type::kFloat, "y" , 384, kFS , 0),
583 make_uni(Type::kInt4, "i4", 388, kFS , 2),
584 make_uni(Type::kInt, "i" , 420, kFS , 0),
585 }
586 },
587 };
588
589 for (const auto& c : kTestCases) {
590 SkString vs = kValidVS;
591 SkString unis;
592 for (const auto u : c.vsUniformDecls) {
593 unis.append(u);
594 }
595 vs.prepend(unis);
596
597 SkString fs = kValidFSes[0];
598 unis = {};
599 for (const auto u : c.fsUniformDecls) {
600 unis.append(u);
601 }
602 fs.prepend(unis);
603
604 auto attrs = SkSpan(kValidAttrs);
605 auto varys = SkSpan(kValidVaryings);
607 if (!check_for_success(reporter, attrs, kValidStride, varys, vs, fs, &spec)) {
608 return;
609 }
610 SkString desc = make_description(attrs, kValidStride, varys, vs, fs);
611 SkSpan<const Uniform> uniforms = spec->uniforms();
612 if (uniforms.size() != c.expectations.size()) {
614 "Expected %zu uniforms but actually %zu:\n%s",
615 c.expectations.size(),
616 uniforms.size(),
617 desc.c_str());
618 return;
619 }
620 for (const auto& [actual, expected] : SkMakeZip(uniforms, c.expectations)) {
621 std::string name = std::string(actual.name);
622 if (name != expected.name) {
624 "Actual uniform name (%s) does not match expected name (%.*s)",
625 name.c_str(),
626 (int)expected.name.size(), expected.name.data());
627 return;
628 }
629 if (actual.type != expected.type) {
631 "Uniform %s: Actual type (%d) does not match expected type (%d)",
632 name.c_str(),
633 static_cast<int>(actual.type),
634 static_cast<int>(expected.type));
635 return;
636 }
637 if (actual.count != expected.count) {
639 "Uniform %s: Actual count (%d) does not match expected count (%d)",
640 name.c_str(),
641 actual.count,
642 expected.count);
643 return;
644 }
645 if (actual.flags != expected.flags) {
647 "Uniform %s: Actual flags (0x%04x) do not match expected flags (0x%04x)",
648 name.c_str(),
649 actual.flags,
650 expected.flags);
651 return;
652 }
653 if (actual.offset != expected.offset) {
655 "Uniform %s: Actual offset (%zu) does not match expected offset (%zu)",
656 name.c_str(),
657 actual.offset,
658 expected.offset);
659 return;
660 }
662 }
663}
664
665DEF_TEST(MeshSpec_InvalidUniforms, reporter) {
666 // We assume general uniform declarations are broadly tested generically in SkSL. Here we are
667 // concerned with agreement between VS and FS declarations, which is a unique aspect of
668 // SkMeshSpecification.
669
670 // Each test case is a fs and vs uniform declaration with the same name but some other
671 // difference that should make them incompatible.
672 static std::tuple<const char*, const char*> kTestCases[]{
673 // different types
674 {"uniform float x;", "uniform int x;"},
675 // array vs non-array
676 {"uniform float2x2 m[1];", "uniform float2x2 m;"},
677 // array count mismatch
678 {"uniform int3 i[1];", "uniform int3 i[2];"},
679 // layout difference
680 {"layout(color) uniform float4 color;", "uniform float4 color;"},
681 };
682
683 for (bool reverse : {false, true}) {
684 for (auto [u1, u2] : kTestCases) {
685 if (reverse) {
686 using std::swap;
687 swap(u1, u2);
688 }
689 SkString vs = kValidVS;
690 vs.prepend(u1);
691
692 SkString fs = kValidFSes[0];
693 fs.prepend(u2);
694
695 auto attrs = SkSpan(kValidAttrs);
696 auto varys = SkSpan(kValidVaryings);
697 if (!check_for_failure(reporter, attrs, kValidStride, varys, vs, fs)) {
698 return;
699 }
701 }
702}
703
704DEF_TEST(MeshSpec_MissingMain, reporter) {
705 static const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
706
707 // Empty VS
712 SkString{},
713 kValidFSes[0])) {
714 return;
715 }
716
717 // VS with helper function but no main
722 kHelper,
723 kValidFSes[0])) {
724 return;
725 }
726
727 // Empty FS
732 kValidVS,
733 SkString{})) {
734 return;
735 }
736
737 // VS with helper function but no main
742 kValidVS,
743 kHelper)) {
744 return;
745 }
746}
747
748DEF_TEST(MeshSpec_ZeroAttributes, reporter) {
749 // We require at least one attribute
755 kValidFSes[0]);
756}
757
758DEF_TEST(MeshSpec_ZeroVaryings, reporter) {
759 // Varyings are not required.
765 kValidFSes[0]);
766}
767
768DEF_TEST(MeshSpec_InvalidStride, reporter) {
769 // Zero stride
772 0,
774 kValidVS,
775 kValidFSes[0])) {
776 return;
777 }
778
779 // Unaligned
782 kValidStride + 1,
784 kValidVS,
785 kValidFSes[0])) {
786 return;
787 }
788
789 // Too large
792 1 << 20,
794 kValidVS,
795 kValidFSes[0])) {
796 return;
797 }
798}
799
800DEF_TEST(MeshSpec_InvalidOffset, reporter) {
801 { // offset isn't aligned
802 static const Attribute kAttributes[] {
804 };
807 32,
809 kValidVS,
810 kValidFSes[0])) {
811 return;
812 }
813 }
814 { // straddles stride boundary
815 static const Attribute kAttributes[] {
818 };
821 20,
823 kValidVS,
824 kValidFSes[0])) {
825 return;
826 }
827 }
828 { // straddles stride boundary with attempt to overflow
829 static const Attribute kAttributes[] {
831 };
834 4,
836 kValidVS,
837 kValidFSes[0])) {
838 return;
840 }
841}
842
843DEF_TEST(MeshSpec_TooManyAttributes, reporter) {
844 static constexpr size_t kN = 500;
845 std::vector<Attribute> attrs;
846 attrs.reserve(kN);
847 for (size_t i = 0; i < kN; ++i) {
848 attrs.push_back({Attribute::Type::kFloat4, 0, SkStringPrintf("attr%zu", i)});
849 }
851 attrs,
852 4*4,
855 kValidFSes[0]);
856}
857
858DEF_TEST(MeshSpec_TooManyVaryings, reporter) {
859 static constexpr size_t kN = 500;
860 std::vector<Varying> varyings;
861 varyings.reserve(kN);
862 for (size_t i = 0; i < kN; ++i) {
863 varyings.push_back({Varying::Type::kFloat4, SkStringPrintf("varying%zu", i)});
864 }
868 SkSpan(varyings),
870 kValidFSes[0]);
871}
872
873DEF_TEST(MeshSpec_DuplicateAttributeNames, reporter) {
874 static const Attribute kAttributes[] {
877 };
880 24,
883 kValidFSes[0]);
884}
885
886DEF_TEST(MeshSpec_DuplicateVaryingNames, reporter) {
887 static const Varying kVaryings[] {
890 };
894 kVaryings,
896 kValidFSes[0]);
898
899static constexpr const char* kSneakyName = "name; float3 sneaky";
900
901DEF_TEST(MeshSpec_SneakyExtraAttribute, reporter) {
902 static const Attribute kAttributes[] {
904 };
907 16,
910 kValidFSes[0]);
911}
912
913DEF_TEST(MeshSpec_SneakyExtraVarying, reporter) {
914 static const Varying kVaryings[] {
916 };
920 kVaryings,
922 kValidFSes[0]);
923}
924
925DEF_TEST(MeshSpec_AllowsFloat2PositionVarying, reporter) {
926 // Position varying can be explicit if it is float2
927 static const Varying kVaryings[] {
928 {Varying::Type::kFloat2, SkString{"position"}},
929 };
933 kVaryings,
935 kValidFSes[0]);
936}
937
938DEF_TEST(MeshSpec_InvalidPositionType, reporter) {
939 // Position varying can be explicit but it must be float2
940 static const Varying kVaryings[] {
941 {Varying::Type::kFloat4, SkString{"position"}},
942 };
946 kVaryings,
948 kValidFSes[0]);
949}
950
951DEF_TEST(MeshSpec_EmptyAttributeName, reporter) {
952 static const Attribute kAttributes[] {
954 };
957 16,
960 kValidFSes[0]);
961}
962
963DEF_TEST(MeshSpec_EmptyVaryingName, reporter) {
964 static const Varying kVaryings[] {
966 };
970 kVaryings,
972 kValidFSes[0]);
973}
974
975DEF_TEST(MeshSpecVaryingPassthrough, reporter) {
976 static const Attribute kAttributes[]{
977 {Attribute::Type::kFloat2, 0, SkString{"position"}},
980 };
981 static const Varying kVaryings[]{
982 {Varying::Type::kFloat2, SkString{"position"}},
984 {Varying::Type::kHalf4, SkString{"color"} },
985 };
986
987 static constexpr char kVS[] = R"(
988 Varyings main(const Attributes a) {
989 Varyings v;
990 v.uv = a.uv;
991 v.position = a.position;
992 v.color = a.color;
993 return v;
994 }
995 )";
996 auto check = [&] (const char* fs, const char* passthroughAttr) {
998 /*vertexStride=*/24,
999 kVaryings,
1000 SkString(kVS),
1001 SkString(fs));
1002 if (!spec) {
1003 ERRORF(reporter, "%s\n%s", fs, error.c_str());
1004 return;
1005 }
1007 const SkString& actualAttr = idx >= 0 ? spec->attributes()[idx].name : SkString("<none>");
1008 if (!passthroughAttr) {
1009 if (idx >= 0) {
1010 ERRORF(reporter, "Expected no passthrough coords attribute, found %s.\n%s",
1011 actualAttr.c_str(),
1012 fs);
1013 }
1014 } else if (!actualAttr.equals(passthroughAttr)) {
1015 ERRORF(reporter, "Expected %s as passthrough coords attribute, found %s.\n%s",
1016 passthroughAttr,
1017 actualAttr.c_str(),
1018 fs);
1019 }
1020 };
1021
1022 // Simple
1023 check(R"(float2 main(const Varyings v) {
1024 return v.uv;
1025 })",
1026 "uv");
1027
1028 // Simple, using position
1029 check(R"(float2 main(const Varyings v) {
1030 return v.position;
1031 })",
1032 "position");
1033
1034 // Simple, with output color
1035 check(R"(float2 main(const Varyings v, out half4 color) {
1036 color = v.color;
1037 return v.uv;
1038 })",
1039 "uv");
1040
1041 // Three returns, all the same.
1042 check(R"(uniform int selector;
1043
1044 float2 main(const Varyings v, out half4 color) {
1045 if (selector == 0) {
1046 color = half4(1, 0, 0, 1);
1047 return v.position;
1048 }
1049 if (selector == 1) {
1050 color = half4(1, 1, 0, 1);
1051 return v.position;
1052 }
1053 color = half4(1, 0, 1, 1);
1054 return v.position;
1055 })",
1056 "position");
1057
1058 // Three returns, one not like the others
1059 check(R"(uniform int selector;
1060
1061 float2 main(const Varyings v, out half4 color) {
1062 if (selector == 0) {
1063 color = color.bgra;
1064 return v.position;
1065 }
1066 if (selector == 1) {
1067 color = half4(1);
1068 return v.uv;
1069 }
1070 color = color;
1071 return v.position;
1072 })",
1073 nullptr);
1074
1075 // Swizzles aren't handled (yet?).
1076 check(R"(float2 main(const Varyings v) {
1077 return v.uv.yx;
1078 })",
1079 nullptr);
1080
1081 // Return from non-main fools us?
1082 check(R"(noinline half4 get_color(const Varyings v) { return v.color; }
1083
1084 float2 main(const Varyings v, out half4 color) {
1085 color = get_color(v);
1086 return v.position;
1087 })",
1088 "position");
1089}
1090
1091DEF_TEST(MeshSpecUnusedVaryings, reporter) {
1092 static const Attribute kAttributes[]{
1093 {Attribute::Type::kFloat2, 0, SkString{"position"}},
1096 };
1097 static const Varying kVaryings[]{
1098 {Varying::Type::kFloat2, SkString{"position"}},
1100 {Varying::Type::kHalf4, SkString{"color"} },
1101 };
1102
1103 static constexpr char kVS[] = R"(
1104 Varyings main(const Attributes a) {
1105 Varyings v;
1106 v.uv = a.uv;
1107 v.position = a.position;
1108 v.color = a.color;
1109 return v;
1110 }
1111 )";
1112
1113 auto check = [&](const char* fs, bool positionDead, bool uvDead, bool colorDead) {
1114 static_assert(std::size(kVaryings) == 3);
1116 /*vertexStride=*/24,
1117 kVaryings,
1118 SkString(kVS),
1119 SkString(fs));
1120 if (!spec) {
1121 ERRORF(reporter, "%s\n%s", fs, error.c_str());
1122 return;
1123 }
1124 bool positionActuallyDead = SkMeshSpecificationPriv::VaryingIsDead(*spec, 0);
1125 bool uvActuallyDead = SkMeshSpecificationPriv::VaryingIsDead(*spec, 1);
1126 bool colorActuallyDead = SkMeshSpecificationPriv::VaryingIsDead(*spec, 2);
1127 auto str = [](bool dead) { return dead ? "dead" : "not dead"; };
1128 if (positionActuallyDead != positionDead) {
1130 "Expected position to be detected %s but it is detected %s.\n%s",
1131 str(positionDead),
1132 str(positionActuallyDead),
1133 fs);
1134 }
1135 if (uvActuallyDead != uvDead) {
1137 "Expected uv to be detected %s but it is detected %s.\n%s",
1138 str(uvDead),
1139 str(uvActuallyDead),
1140 fs);
1141 }
1142 if (colorActuallyDead != colorDead) {
1144 "Expected color to be detected %s but it is detected %s.\n%s",
1145 str(colorDead),
1146 str(colorActuallyDead),
1147 fs);
1148 }
1149 };
1150
1151 // Simple
1152 check(R"(float2 main(const Varyings v) {
1153 return v.uv;
1154 })",
1155 true,
1156 true,
1157 true);
1158
1159 // Simple, using position
1160 check(R"(float2 main(const Varyings v) {
1161 return v.position;
1162 })",
1163 true,
1164 true,
1165 true);
1166
1167 // Two returns that are both passthrough of the same varying
1168 check(R"(float2 main(const Varyings v, out half4 color) {
1169 if (v.color.r > 0.5) {
1170 color = v.color;
1171 return v.uv;
1172 } else {
1173 color = 2*color;
1174 return v.uv;
1175 }
1176 })",
1177 true,
1178 true,
1179 false);
1180
1181 // Two returns that are both passthrough of the different varyings and unused other varying
1182 check(R"(float2 main(const Varyings v, out half4 color) {
1183 if (v.position.x > 10) {
1184 color = half4(0);
1185 return v.uv;
1186 } else {
1187 color = half4(1);
1188 return v.position;
1189 }
1190 })",
1191 false,
1192 false,
1193 true);
1194
1195 // Passthrough but we also use the varying elsewhere
1196 check(R"(float2 main(const Varyings v, out half4 color) {
1197 color = half4(v.uv.x, 0, 0, 1);
1198 return v.uv;
1199 })",
1200 true,
1201 false,
1202 true);
1203
1204 // Use two varyings is a return statement
1205 check(R"(float2 main(const Varyings v) {
1206 return v.uv + v.position;
1207 })",
1208 false,
1209 false,
1210 true);
1211
1212 // Slightly more complicated varying use.
1213 check(R"(noinline vec2 get_pos(const Varyings v) { return v.position; }
1214
1215 noinline half4 identity(half4 c) { return c; }
1216
1217 float2 main(const Varyings v, out half4 color) {
1218 color = identity(v.color);
1219 return v.uv + get_pos(v);
1220 })",
1221 false,
1222 false,
1223 false);
1224
1225 // Go through assignment to another Varyings.
1226 check(R"(float2 main(const Varyings v) {
1227 Varyings otherVaryings;
1228 otherVaryings = v;
1229 return otherVaryings.uv;
1230 })",
1231 true,
1232 false,
1233 true);
1234
1235 // We're not very smart. We just look for any use of the field in any Varyings value and don't
1236 // do any data flow analysis.
1237 check(R"(float2 main(const Varyings v) {
1238 Varyings otherVaryings;
1239 otherVaryings.uv = half2(5);
1240 otherVaryings.position = half2(10);
1241 return otherVaryings.position;
1242 })",
1243 false,
1244 false,
1245 true);
1246}
static constexpr SkColor kColor
Definition: CanvasTest.cpp:265
#define test(name)
reporter
Definition: FontMgrTest.cpp:39
int count
Definition: FontMgrTest.cpp:50
static const size_t kVertexCount
DEF_TEST(MeshSpec_Valid, reporter)
Definition: MeshTest.cpp:167
SkMeshSpecification::Attribute Attribute
Definition: MeshTest.cpp:36
static const char * attr_type_str(const Attribute::Type type)
Definition: MeshTest.cpp:39
static const Attribute kValidAttrs[]
Definition: MeshTest.cpp:159
static const SkString kValidFSes[]
Definition: MeshTest.cpp:148
static bool check_for_success(skiatest::Reporter *reporter, SkSpan< const Attribute > attributes, size_t stride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs, sk_sp< SkMeshSpecification > *spec=nullptr)
Definition: MeshTest.cpp:118
static const SkString kValidVS
Definition: MeshTest.cpp:141
SkMeshSpecification::Varying Varying
Definition: MeshTest.cpp:37
static constexpr const char * kSneakyName
Definition: MeshTest.cpp:895
static bool check_for_failure(skiatest::Reporter *reporter, SkSpan< const Attribute > attributes, size_t stride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs, const char *expectedErrorSubstring=nullptr)
Definition: MeshTest.cpp:94
static SkString make_description(SkSpan< const Attribute > attributes, size_t stride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs)
Definition: MeshTest.cpp:64
static const char * var_type_str(const Varying::Type type)
Definition: MeshTest.cpp:50
static const Varying kValidVaryings[]
Definition: MeshTest.cpp:163
static constexpr size_t kValidStride
Definition: MeshTest.cpp:162
#define check(reporter, ref, unref, make, kill)
Definition: RefCntTest.cpp:85
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
@ kSrcOver
r = s + (1-sa)*d
constexpr SkColor SK_ColorBLACK
Definition: SkColor.h:103
void swap(sk_sp< T > &a, sk_sp< T > &b)
Definition: SkRefCnt.h:341
SkSpan(Container &&) -> SkSpan< std::remove_pointer_t< decltype(std::data(std::declval< Container >()))> >
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 auto SkMakeZip(Ts &&... ts)
Definition: SkZip.h:212
#define REPORTER_ASSERT(r, cond,...)
Definition: Test.h:286
#define ERRORF(r,...)
Definition: Test.h:293
GLenum type
static sk_sp< SkBlender > Mode(SkBlendMode mode)
static sk_sp< SkColorFilter > LinearToSRGBGamma()
SkSpan< const Uniform > uniforms() const
Definition: SkMesh.h:170
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
SkRuntimeEffect::Uniform Uniform
Definition: SkMesh.h:106
const Child * findChild(std::string_view name) const
Definition: SkMesh.cpp:656
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
constexpr size_t size() const
Definition: SkSpan_impl.h:95
bool equals(const SkString &) const
Definition: SkString.cpp:324
void append(const char text[])
Definition: SkString.h:203
void prepend(const char text[])
Definition: SkString.h:215
const char * c_str() const
Definition: SkString.h:133
void void void appendf(const char format[],...) SK_PRINTF_LIKE(2
Definition: SkString.cpp:550
struct MyStruct s
struct MyStruct a[10]
FlutterSemanticsFlag flags
const uint8_t uint32_t uint32_t GError ** error
GAsyncResult * result
static float max(float r, float g, float b)
Definition: hsl.cpp:49
static float min(float r, float g, float b)
Definition: hsl.cpp:48
SK_API sk_sp< SkMesh::VertexBuffer > MakeVertexBuffer(const void *, size_t size)
Definition: SkMesh.cpp:905
SK_API sk_sp< SkShader > Color(SkColor)
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 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
static const std::map< std::string, VerticesBuilder::AttributeType > kAttributes
SeparatedVector2 offset
static int PassthroughLocalCoordsVaryingIndex(const SkMeshSpecification &spec)
Definition: SkMeshPriv.h:65
static bool VaryingIsDead(const SkMeshSpecification &spec, int v)
Definition: SkMeshPriv.h:75
static constexpr SkRect MakeEmpty()
Definition: SkRect.h:595