Flutter Engine
The Flutter Engine
Loading...
Searching...
No Matches
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
398 SkRuntimeEffect::ChildPtr childShader[] = {SkShaders::Color(SK_ColorBLACK)};
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 };
806 kAttributes,
807 32,
809 kValidVS,
810 kValidFSes[0])) {
811 return;
812 }
813 }
814 { // straddles stride boundary
815 static const Attribute kAttributes[] {
818 };
820 kAttributes,
821 20,
823 kValidVS,
824 kValidFSes[0])) {
825 return;
826 }
827 }
828 { // straddles stride boundary with attempt to overflow
829 static const Attribute kAttributes[] {
830 {Attribute::Type::kFloat, std::numeric_limits<size_t>::max() - 3, SkString{"var"}},
831 };
833 kAttributes,
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 };
879 kAttributes,
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 };
906 kAttributes,
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 };
956 kAttributes,
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) {
997 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
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);
1115 auto [spec, error] = SkMeshSpecification::Make(kAttributes,
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
#define test(name)
reporter
int count
static const size_t kVertexCount
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
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)
#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
SK_API SkString static SkString SkStringPrintf()
Definition SkString.h:287
constexpr auto SkMakeZip(Ts &&... ts)
Definition SkZip.h:212
#define DEF_TEST(name, reporter)
Definition Test.h:312
#define REPORTER_ASSERT(r, cond,...)
Definition Test.h:286
#define ERRORF(r,...)
Definition Test.h:293
static sk_sp< SkBlender > Mode(SkBlendMode mode)
static sk_sp< SkColorFilter > LinearToSRGBGamma()
static Result Make(SkSpan< const Attribute > attributes, size_t vertexStride, SkSpan< const Varying > varyings, const SkString &vs, const SkString &fs)
Definition SkMesh.cpp:389
SkRuntimeEffect::Uniform Uniform
Definition SkMesh.h:106
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
const char * name
Definition fuchsia.cc:50
SK_API sk_sp< SkMesh::VertexBuffer > MakeVertexBuffer(const void *, size_t size)
Definition SkMesh.cpp:905
Point 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