• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright 2021 Google LLC
3  *
4  * Use of this source code is governed by a BSD-style license that can be
5  * found in the LICENSE file.
6  */
7 
8 #include "include/core/SkMesh.h"
9 #include "include/core/SkRefCnt.h"
10 #include "include/core/SkSpan.h"
11 #include "include/core/SkString.h"
12 #include "include/core/SkTypes.h"
13 #include "src/base/SkZip.h"
14 #include "src/core/SkMeshPriv.h"
15 #include "tests/Test.h"
16 
17 #include <algorithm>
18 #include <cstddef>
19 #include <cstdint>
20 #include <initializer_list>
21 #include <limits>
22 #include <string>
23 #include <string_view>
24 #include <tuple>
25 #include <utility>
26 #include <vector>
27 
28 using Attribute = SkMeshSpecification::Attribute;
29 using Varying   = SkMeshSpecification::Varying;
30 
attr_type_str(const Attribute::Type type)31 static const char* attr_type_str(const Attribute::Type type) {
32     switch (type) {
33         case Attribute::Type::kFloat:        return "float";
34         case Attribute::Type::kFloat2:       return "float2";
35         case Attribute::Type::kFloat3:       return "float3";
36         case Attribute::Type::kFloat4:       return "float4";
37         case Attribute::Type::kUByte4_unorm: return "ubyte4_unorm";
38     }
39     SkUNREACHABLE;
40 }
41 
var_type_str(const Varying::Type type)42 static const char* var_type_str(const Varying::Type type) {
43     switch (type) {
44         case Varying::Type::kFloat:  return "float";
45         case Varying::Type::kFloat2: return "float2";
46         case Varying::Type::kFloat3: return "float3";
47         case Varying::Type::kFloat4: return "float4";
48         case Varying::Type::kHalf:   return "half";
49         case Varying::Type::kHalf2:  return "half2";
50         case Varying::Type::kHalf3:  return "half3";
51         case Varying::Type::kHalf4:  return "half4";
52     }
53     SkUNREACHABLE;
54 }
55 
make_description(SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs)56 static SkString make_description(SkSpan<const Attribute> attributes,
57                                  size_t                  stride,
58                                  SkSpan<const Varying>   varyings,
59                                  const SkString&         vs,
60                                  const SkString&         fs) {
61     static constexpr size_t kMax = 10;
62     SkString result;
63     result.appendf("Attributes (count=%zu, stride=%zu):\n", attributes.size(), stride);
64     for (size_t i = 0; i < std::min(kMax, attributes.size()); ++i) {
65         const auto& a = attributes[i];
66         result.appendf(" {%-10s, %3zu, \"%s\"}\n", attr_type_str(a.type), a.offset, a.name.c_str());
67     }
68     if (kMax < attributes.size()) {
69         result.append(" ...\n");
70     }
71 
72     result.appendf("Varyings (count=%zu):\n", varyings.size());
73     for (size_t i = 0; i < std::min(kMax, varyings.size()); ++i) {
74         const auto& v = varyings[i];
75         result.appendf(" {%5s, \"%s\"}\n", var_type_str(v.type), v.name.c_str());
76     }
77     if (kMax < varyings.size()) {
78         result.append(" ...\n");
79     }
80 
81     result.appendf("\n--VS--\n%s\n------\n", vs.c_str());
82     result.appendf("\n--FS--\n%s\n------\n", fs.c_str());
83     return result;
84 }
85 
check_for_failure(skiatest::Reporter * r,SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs)86 static bool check_for_failure(skiatest::Reporter*     r,
87                               SkSpan<const Attribute> attributes,
88                               size_t                  stride,
89                               SkSpan<const Varying>   varyings,
90                               const SkString&         vs,
91                               const SkString&         fs) {
92     auto [spec, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
93     SkString description;
94     if (!spec) {
95         return true;
96     }
97     ERRORF(r,
98            "Expected to fail but succeeded:\n%s",
99            make_description(attributes, stride, varyings, vs, fs).c_str());
100     return false;
101 }
102 
check_for_success(skiatest::Reporter * r,SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs,sk_sp<SkMeshSpecification> * spec=nullptr)103 static bool check_for_success(skiatest::Reporter*         r,
104                               SkSpan<const Attribute>     attributes,
105                               size_t                      stride,
106                               SkSpan<const Varying>       varyings,
107                               const SkString&             vs,
108                               const SkString&             fs,
109                               sk_sp<SkMeshSpecification>* spec = nullptr) {
110     auto [s, error] = SkMeshSpecification::Make(attributes, stride, varyings, vs, fs);
111     if (s) {
112         REPORTER_ASSERT(r, error.isEmpty());
113         if (spec) {
114             *spec = std::move(s);
115         }
116         return true;
117     }
118     ERRORF(r,
119            "Expected to succeed but failed:\n%sError:\n%s",
120            make_description(attributes, stride, varyings, vs, fs).c_str(),
121            error.c_str());
122     return false;
123 }
124 
125 // Simple valid strings to make specifications
126 static const SkString kValidVS {R"(
127 Varyings main(const Attributes attrs) {
128     Varyings v;
129     return v;
130 })"};
131 
132 // There are multiple valid VS signatures.
133 static const SkString kValidFSes[]{
134         SkString{"float2 main(const Varyings varyings) { return float2(10); }"},
135         SkString{R"(
136             float2 main(const Varyings varyings, out half4 color) {
137                 color = half4(.2);
138                 return float2(10);
139             }
140         )"},
141 };
142 
143 // Simple valid attributes, stride, and varyings to make specifications
144 static const Attribute kValidAttrs[] = {
145         {Attribute::Type::kFloat4, 0, SkString{"pos"}},
146 };
147 static constexpr size_t kValidStride = 4*4;
148 static const Varying kValidVaryings[] = {
149         {Varying::Type::kFloat2, SkString{"uv"}},
150 };
151 
test_good(skiatest::Reporter * r)152 static void test_good(skiatest::Reporter* r) {
153     for (const auto& validFS : kValidFSes) {
154         if (!check_for_success(r,
155                                kValidAttrs,
156                                kValidStride,
157                                kValidVaryings,
158                                kValidVS,
159                                validFS)) {
160             return;
161         }
162     }
163 }
164 
test_bad_sig(skiatest::Reporter * r)165 static void test_bad_sig(skiatest::Reporter* r) {
166     static constexpr const char* kVSBody = "{ return float2(10); }";
167 
168     static constexpr const char* kInvalidVSSigs[] {
169             "float3   main(const Attributes attrs)",   // bad return
170             "Varyings main(Attributes attrs)",         // non-const Attributes
171             "Varyings main(out Attributes attrs)",     // out Varyings
172             "Varyings main()",                         // no Attributes
173             "Varyings main(const Varyings v, float2)"  // extra arg
174     };
175 
176     static constexpr const char* kNoColorFSBody = "{ return float2(10); }";
177 
178     static constexpr const char* kInvalidNoColorFSSigs[] {
179             "half2  main(const Varyings v)",      // bad return
180             "float2 main(const Attributes v)",    // wrong param type
181             "float2 main(inout Varyings attrs)",  // inout Varyings
182             "float2 main(Varyings v)",            // non-const Varyings
183             "float2 main()",                      // no args
184             "float2 main(const Varyings, float)"  // extra arg
185     };
186 
187     static constexpr const char* kColorFSBody = "{ color = half4(.2); return float2(10); }";
188 
189     static constexpr const char* kInvalidColorFSSigs[] {
190             "half2  main(const Varyings v, out half4 color)",        // bad return
191             "float2 main(const Attributes v, out half4 color)",      // wrong first param type
192             "float2 main(const Varyings v, out half3 color)",        // wrong second param type
193             "float2 main(out   Varyings v, out half4 color)",        // out Varyings
194             "float2 main(const Varyings v, half4 color)",            // in color
195             "float2 main(const Varyings v, out half4 color, float)"  // extra arg
196     };
197 
198     for (const char* vsSig : kInvalidVSSigs) {
199         SkString invalidVS;
200         invalidVS.appendf("%s %s", vsSig, kVSBody);
201         for (const auto& validFS : kValidFSes) {
202             if (!check_for_failure(r,
203                                    kValidAttrs,
204                                    kValidStride,
205                                    kValidVaryings,
206                                    invalidVS,
207                                    validFS)) {
208                 return;
209             }
210         }
211     }
212 
213     for (const char* noColorFSSig : kInvalidNoColorFSSigs) {
214         SkString invalidFS;
215         invalidFS.appendf("%s %s", noColorFSSig, kNoColorFSBody);
216         if (!check_for_failure(r,
217                                kValidAttrs,
218                                kValidStride,
219                                kValidVaryings,
220                                kValidVS,
221                                invalidFS)) {
222             return;
223         }
224     }
225 
226     for (const char* colorFSSig : kInvalidColorFSSigs) {
227         SkString invalidFS;
228         invalidFS.appendf("%s %s", colorFSSig, kColorFSBody);
229         if (!check_for_failure(r,
230                                kValidAttrs,
231                                kValidStride,
232                                kValidVaryings,
233                                kValidVS,
234                                invalidFS)) {
235             return;
236         }
237     }
238 }
239 
240 // We allow the optional out color from the FS to either be float4 or half4
test_float4_color(skiatest::Reporter * r)241 static void test_float4_color(skiatest::Reporter* r) {
242     static const SkString kFloat4FS {
243         R"(
244             float2 main(const Varyings varyings, out float4 color) {
245                 color = float4(.2); return float2(10);
246             }
247         )"
248     };
249     check_for_success(r,
250                       kValidAttrs,
251                       kValidStride,
252                       kValidVaryings,
253                       kValidVS,
254                       kFloat4FS);
255 }
256 
257 // We don't allow child effects in custom meshes currently.
test_bad_globals(skiatest::Reporter * r)258 static void test_bad_globals(skiatest::Reporter* r) {
259     static constexpr const char* kBadGlobals[] {
260         "uniform shader myshader;"
261     };
262     for (const auto& global : kBadGlobals) {
263         SkString badVS = kValidVS;
264         badVS.prepend(global);
265         if (!check_for_failure(r,
266                                kValidAttrs,
267                                kValidStride,
268                                kValidVaryings,
269                                badVS,
270                                kValidFSes[0])) {
271             return;
272         }
273     }
274     for (const auto& global : kBadGlobals) {
275         SkString badFS = kValidFSes[0];
276         badFS.prepend(global);
277         if (!check_for_failure(r,
278                                kValidAttrs,
279                                kValidStride,
280                                kValidVaryings,
281                                kValidVS,
282                                badFS)) {
283             return;
284         }
285     }
286 }
287 
test_good_uniforms(skiatest::Reporter * r)288 static void test_good_uniforms(skiatest::Reporter* r) {
289     using Uniform = SkMeshSpecification::Uniform;
290     using Type    = Uniform::Type;
291     using Flags   = Uniform::Flags;
292 
293     constexpr Flags kVS    = Uniform::kVertex_Flag;
294     constexpr Flags kFS    = Uniform::kFragment_Flag;
295     constexpr Flags kColor = Uniform::kColor_Flag;
296     constexpr Flags kHalfP = Uniform::kHalfPrecision_Flag;
297 
298     auto make_uni = [](Type type,
299                        std::string_view name,
300                        size_t offset,
301                        uint32_t flags,
302                        int count = 0) {
303         if (count) {
304             return Uniform{name, offset, type, count, flags | Uniform::kArray_Flag};
305         } else {
306             SkASSERT(!(flags & Uniform::kArray_Flag));
307             return Uniform{name, offset, type, 1, flags};
308         }
309     };
310 
311     // Each test case is a set of VS and FS uniform declarations followed and the expected output
312     // of SkMeshSpecification::uniforms().
313     struct {
314         const std::vector<const char*>                  vsUniformDecls;
315         const std::vector<const char*>                  fsUniformDecls;
316         const std::vector<SkMeshSpecification::Uniform> expectations;
317     } static kTestCases[] {
318             // A single VS uniform.
319             {
320                     {
321                             "uniform float x;"
322                     },
323                     {},
324                     {
325                             make_uni(Type::kFloat, "x", 0, kVS)
326                     }
327             },
328 
329             // A single FS uniform.
330             {
331                     {},
332                     {
333                             "uniform float2 v;"
334                     },
335                     {
336                             make_uni(Type::kFloat2, "v", 0, kFS)
337                     }
338             },
339 
340             // A single uniform in both that uses color layout.
341             {
342                     {
343                             "layout(color) uniform float4 color;",
344                     },
345                     {
346                             "layout(color) uniform float4 color;",
347                     },
348                     {
349                             make_uni(Type::kFloat4, "color", 0, kVS|kFS|kColor)
350                     }
351             },
352 
353             // A shared uniform after an unshared vertex uniform
354             {
355                     {
356                             "layout(color) uniform float4 color;",
357                             "              uniform float x[5];",
358                     },
359                     {
360                             "uniform float x[5];",
361                     },
362                     {
363                              make_uni(Type::kFloat4, "color",  0, kVS|kColor, 0),
364                              make_uni(Type::kFloat , "x"    , 16, kVS|kFS   , 5)
365                     }
366             },
367 
368             // A shared uniform before an unshared vertex uniform
369             {
370                 {
371                         "uniform half x[2];",
372                         "uniform int  y;",
373                 },
374                 {
375                         "uniform half x[2];",
376                 },
377                 {
378                         make_uni(Type::kFloat, "x",  0, kVS|kFS|kHalfP, 2),
379                         make_uni(Type::kInt,   "y",  8, kVS           , 0)
380                 }
381             },
382 
383             // A shared uniform after an unshared fragment uniform
384             {
385                      {
386                             "uniform float3x3 m;",
387                      },
388                      {
389                              "uniform int2     i2;",
390                              "uniform float3x3 m;",
391                      },
392                      {
393                             make_uni(Type::kFloat3x3, "m" ,  0, kVS|kFS),
394                             make_uni(Type::kInt2    , "i2", 36, kFS    )
395                      }
396             },
397 
398             // A shared uniform before an unshared fragment uniform
399             {
400                     {
401                             "uniform half4x4 m[4];",
402                     },
403                     {
404                             "uniform half4x4  m[4];",
405                             "uniform int3    i3[1];",
406                     },
407                     {
408                             make_uni(Type::kFloat4x4, "m",    0, kVS|kFS|kHalfP, 4),
409                             make_uni(Type::kInt3,     "i3", 256, kFS           , 1)
410                     }
411             },
412 
413             // Complex case with 2 shared uniforms that are declared in the opposite order.
414             {
415                     {
416                              "uniform float   x;"
417                              "uniform half4x4 m[4];",  // shared
418                              "uniform int2    i2[2];"
419                              "uniform float3  v[8];"   // shared
420                              "uniform int3    i3;"
421                     },
422                     {
423                              "uniform float   y;"
424                              "uniform float3  v[8];"   // shared
425                              "uniform int4    i4[2];"
426                              "uniform half4x4 m[4];",  // shared
427                              "uniform int     i;"
428                     },
429                     {
430                              make_uni(Type::kFloat,    "x" ,   0, kVS           , 0),
431                              make_uni(Type::kFloat4x4, "m" ,   4, kVS|kFS|kHalfP, 4),
432                              make_uni(Type::kInt2,     "i2", 260, kVS           , 2),
433                              make_uni(Type::kFloat3,   "v" , 276, kVS|kFS       , 8),
434                              make_uni(Type::kInt3,     "i3", 372, kVS           , 0),
435                              make_uni(Type::kFloat,    "y" , 384, kFS           , 0),
436                              make_uni(Type::kInt4,     "i4", 388, kFS           , 2),
437                              make_uni(Type::kInt,      "i" , 420, kFS           , 0),
438                     }
439             },
440     };
441 
442     for (const auto& c : kTestCases) {
443         SkString vs = kValidVS;
444         SkString unis;
445         for (const auto u : c.vsUniformDecls) {
446             unis.append(u);
447         }
448         vs.prepend(unis);
449 
450         SkString fs = kValidFSes[0];
451         unis = {};
452         for (const auto u : c.fsUniformDecls) {
453             unis.append(u);
454         }
455         fs.prepend(unis);
456 
457         auto attrs = SkSpan(kValidAttrs);
458         auto varys = SkSpan(kValidVaryings);
459         sk_sp<SkMeshSpecification> spec;
460         if (!check_for_success(r, attrs, kValidStride, varys, vs, fs, &spec)) {
461             return;
462         }
463         SkString desc = make_description(attrs, kValidStride, varys, vs, fs);
464         SkSpan<const Uniform> uniforms = spec->uniforms();
465         if (uniforms.size() != c.expectations.size()) {
466             ERRORF(r,
467                    "Expected %zu uniforms but actually %zu:\n%s",
468                    c.expectations.size(),
469                    uniforms.size(),
470                    desc.c_str());
471             return;
472         }
473         for (const auto& [actual, expected] : SkMakeZip(uniforms, c.expectations)) {
474             std::string name = std::string(actual.name);
475             if (name != expected.name) {
476                 ERRORF(r,
477                        "Actual uniform name (%s) does not match expected name (%.*s)",
478                        name.c_str(),
479                        (int)expected.name.size(), expected.name.data());
480                 return;
481             }
482             if (actual.type != expected.type) {
483                 ERRORF(r,
484                        "Uniform %s: Actual type (%d) does not match expected type (%d)",
485                        name.c_str(),
486                        static_cast<int>(actual.type),
487                        static_cast<int>(expected.type));
488                 return;
489             }
490             if (actual.count != expected.count) {
491                 ERRORF(r,
492                        "Uniform %s: Actual count (%d) does not match expected count (%d)",
493                        name.c_str(),
494                        actual.count,
495                        expected.count);
496                 return;
497             }
498             if (actual.flags != expected.flags) {
499                 ERRORF(r,
500                        "Uniform %s: Actual flags (0x%04x) do not match expected flags (0x%04x)",
501                        name.c_str(),
502                        actual.flags,
503                        expected.flags);
504                 return;
505             }
506             if (actual.offset != expected.offset) {
507                 ERRORF(r,
508                        "Uniform %s: Actual offset (%zu) does not match expected offset (%zu)",
509                        name.c_str(),
510                        actual.offset,
511                        expected.offset);
512                 return;
513             }
514         }
515     }
516 }
517 
test_bad_uniforms(skiatest::Reporter * r)518 static void test_bad_uniforms(skiatest::Reporter* r) {
519     // We assume general uniform declarations are broadly tested generically in SkSL. Here we are
520     // concerned with agreement between VS and FS declarations, which is a unique aspect of
521     // SkMeshSpecification.
522 
523     // Each test case is a fs and vs uniform declaration with the same name but some other
524     // difference that should make them incompatible.
525     static std::tuple<const char*, const char*> kTestCases[]{
526             // different types
527             {"uniform float x;", "uniform int x;"},
528             // array vs non-array
529             {"uniform float2x2 m[1];", "uniform float2x2 m;"},
530             // array count mismatch
531             {"uniform int3 i[1];", "uniform int3 i[2];"},
532             // layout difference
533             {"layout(color) uniform float4 color;", "uniform float4 color;"},
534     };
535 
536     for (bool reverse : {false, true}) {
537         for (auto [u1, u2] : kTestCases) {
538             if (reverse) {
539                 using std::swap;
540                 swap(u1, u2);
541             }
542             SkString vs = kValidVS;
543             vs.prepend(u1);
544 
545             SkString fs = kValidFSes[0];
546             fs.prepend(u2);
547 
548             auto attrs = SkSpan(kValidAttrs);
549             auto varys = SkSpan(kValidVaryings);
550             if (!check_for_failure(r, attrs, kValidStride, varys, vs, fs)) {
551                 return;
552             }
553         }
554     }
555 }
556 
test_no_main(skiatest::Reporter * r)557 static void test_no_main(skiatest::Reporter* r) {
558     static const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
559 
560     // Empty VS
561     if (!check_for_failure(r,
562                            kValidAttrs,
563                            kValidStride,
564                            kValidVaryings,
565                            SkString{},
566                            kValidFSes[0])) {
567         return;
568     }
569 
570     // VS with helper function but no main
571     if (!check_for_failure(r,
572                            kValidAttrs,
573                            kValidStride,
574                            kValidVaryings,
575                            kHelper,
576                            kValidFSes[0])) {
577         return;
578     }
579 
580     // Empty FS
581     if (!check_for_failure(r,
582                            kValidAttrs,
583                            kValidStride,
584                            kValidVaryings,
585                            kValidVS,
586                            SkString{})) {
587         return;
588     }
589 
590     // VS with helper function but no main
591     if (!check_for_failure(r,
592                            kValidAttrs,
593                            kValidStride,
594                            kValidVaryings,
595                            kValidVS,
596                            kHelper)) {
597         return;
598     }
599 }
600 
test_zero_attrs(skiatest::Reporter * r)601 static void test_zero_attrs(skiatest::Reporter* r) {
602     // We require at least one attribute
603     check_for_failure(r,
604                       SkSpan<Attribute>(),
605                       kValidStride,
606                       kValidVaryings,
607                       kValidVS,
608                       kValidFSes[0]);
609 }
610 
test_zero_varyings(skiatest::Reporter * r)611 static void test_zero_varyings(skiatest::Reporter* r) {
612     // Varyings are not required.
613     check_for_success(r,
614                       kValidAttrs,
615                       kValidStride,
616                       SkSpan<Varying>(),
617                       kValidVS,
618                       kValidFSes[0]);
619 }
620 
test_bad_strides(skiatest::Reporter * r)621 static void test_bad_strides(skiatest::Reporter* r) {
622     // Zero stride
623     if (!check_for_failure(r,
624                            kValidAttrs,
625                            0,
626                            kValidVaryings,
627                            kValidVS,
628                            kValidFSes[0])) {
629         return;
630     }
631 
632     // Unaligned
633     if (!check_for_failure(r,
634                            kValidAttrs,
635                            kValidStride + 1,
636                            kValidVaryings,
637                            kValidVS,
638                            kValidFSes[0])) {
639         return;
640     }
641 
642     // Too large
643     if (!check_for_failure(r,
644                            kValidAttrs,
645                            1 << 20,
646                            kValidVaryings,
647                            kValidVS,
648                            kValidFSes[0])) {
649         return;
650     }
651 }
652 
test_bad_offsets(skiatest::Reporter * r)653 static void test_bad_offsets(skiatest::Reporter* r) {
654     {  // offset isn't aligned
655         static const Attribute kAttributes[] {
656                 {Attribute::Type::kFloat4,  1, SkString{"var"}},
657         };
658         if (!check_for_failure(r,
659                                kAttributes,
660                                32,
661                                kValidVaryings,
662                                kValidVS,
663                                kValidFSes[0])) {
664             return;
665         }
666     }
667     {  // straddles stride boundary
668         static const Attribute kAttributes[] {
669                 {Attribute::Type::kFloat4,   0, SkString{"var"}},
670                 {Attribute::Type::kFloat2,  16, SkString{"var"}},
671         };
672         if (!check_for_failure(r,
673                                kAttributes,
674                                20,
675                                kValidVaryings,
676                                kValidVS,
677                                kValidFSes[0])) {
678             return;
679         }
680     }
681     {  // straddles stride boundary with attempt to overflow
682         static const Attribute kAttributes[] {
683                 {Attribute::Type::kFloat, std::numeric_limits<size_t>::max() - 3, SkString{"var"}},
684         };
685         if (!check_for_failure(r,
686                                kAttributes,
687                                4,
688                                kValidVaryings,
689                                kValidVS,
690                                kValidFSes[0])) {
691             return;
692         }
693     }
694 }
695 
test_too_many_attrs(skiatest::Reporter * r)696 static void test_too_many_attrs(skiatest::Reporter* r) {
697     static constexpr size_t kN = 500;
698     std::vector<Attribute> attrs;
699     attrs.reserve(kN);
700     for (size_t i = 0; i < kN; ++i) {
701         attrs.push_back({Attribute::Type::kFloat4, 0, SkStringPrintf("attr%zu", i)});
702     }
703     check_for_failure(r,
704                       attrs,
705                       4*4,
706                       kValidVaryings,
707                       kValidVS,
708                       kValidFSes[0]);
709 }
710 
test_too_many_varyings(skiatest::Reporter * r)711 static void test_too_many_varyings(skiatest::Reporter* r) {
712     static constexpr size_t kN = 500;
713     std::vector<Varying> varyings;
714     varyings.reserve(kN);
715     for (size_t i = 0; i < kN; ++i) {
716         varyings.push_back({Varying::Type::kFloat4, SkStringPrintf("varying%zu", i)});
717     }
718     check_for_failure(r,
719                       kValidAttrs,
720                       kValidStride,
721                       SkSpan(varyings),
722                       kValidVS,
723                       kValidFSes[0]);
724 }
725 
test_duplicate_attribute_names(skiatest::Reporter * r)726 static void test_duplicate_attribute_names(skiatest::Reporter* r) {
727     static const Attribute kAttributes[] {
728             {Attribute::Type::kFloat4,  0, SkString{"var"}},
729             {Attribute::Type::kFloat2, 16, SkString{"var"}}
730     };
731     check_for_failure(r,
732                       kAttributes,
733                       24,
734                       kValidVaryings,
735                       kValidVS,
736                       kValidFSes[0]);
737 }
738 
test_duplicate_varying_names(skiatest::Reporter * r)739 static void test_duplicate_varying_names(skiatest::Reporter* r) {
740     static const Varying kVaryings[] {
741         {Varying::Type::kFloat4, SkString{"var"}},
742         {Varying::Type::kFloat3, SkString{"var"}}
743     };
744     check_for_failure(r,
745                       kValidAttrs,
746                       kValidStride,
747                       kVaryings,
748                       kValidVS,
749                       kValidFSes[0]);
750 }
751 
752 static constexpr const char* kSneakyName = "name; float3 sneaky";
753 
test_sneaky_attribute_name(skiatest::Reporter * r)754 static void test_sneaky_attribute_name(skiatest::Reporter* r) {
755     static const Attribute kAttributes[] {
756             {Attribute::Type::kFloat4, 0, SkString{kSneakyName}},
757     };
758     check_for_failure(r,
759                       kAttributes,
760                       16,
761                       kValidVaryings,
762                       kValidVS,
763                       kValidFSes[0]);
764 }
765 
test_sneaky_varying_name(skiatest::Reporter * r)766 static void test_sneaky_varying_name(skiatest::Reporter* r) {
767     static const Varying kVaryings[] {
768             {Varying::Type::kFloat4, SkString{kSneakyName}},
769     };
770     check_for_failure(r,
771                       kValidAttrs,
772                       kValidStride,
773                       kVaryings,
774                       kValidVS,
775                       kValidFSes[0]);
776 }
777 
test_good_position_varying(skiatest::Reporter * r)778 static void test_good_position_varying(skiatest::Reporter* r) {
779     // Position varying can be explicit if it is float2
780     static const Varying kVaryings[] {
781             {Varying::Type::kFloat2, SkString{"position"}},
782     };
783     check_for_success(r,
784                       kValidAttrs,
785                       kValidStride,
786                       kVaryings,
787                       kValidVS,
788                       kValidFSes[0]);
789 }
790 
test_bad_position_varying(skiatest::Reporter * r)791 static void test_bad_position_varying(skiatest::Reporter* r) {
792     // Position varying can be explicit but it must be float2
793     static const Varying kVaryings[] {
794             {Varying::Type::kFloat4, SkString{"position"}},
795     };
796     check_for_failure(r,
797                       kValidAttrs,
798                       kValidStride,
799                       kVaryings,
800                       kValidVS,
801                       kValidFSes[0]);
802 }
803 
test_empty_attribute_name(skiatest::Reporter * r)804 static void test_empty_attribute_name(skiatest::Reporter* r) {
805     static const Attribute kAttributes[] {
806             {Attribute::Type::kFloat4, 0, SkString{}},
807     };
808     check_for_failure(r,
809                       kAttributes,
810                       16,
811                       kValidVaryings,
812                       kValidVS,
813                       kValidFSes[0]);
814 }
815 
test_empty_varying_name(skiatest::Reporter * r)816 static void test_empty_varying_name(skiatest::Reporter* r) {
817     static const Varying kVaryings[] {
818             {Varying::Type::kFloat4, SkString{}},
819     };
820     check_for_failure(r,
821                       kValidAttrs,
822                       kValidStride,
823                       kVaryings,
824                       kValidVS,
825                       kValidFSes[0]);
826 }
827 
DEF_TEST(MeshSpec,reporter)828 DEF_TEST(MeshSpec, reporter) {
829     struct X {};
830     test_good(reporter);
831     test_bad_sig(reporter);
832     test_float4_color(reporter);
833     test_bad_globals(reporter);
834     test_good_uniforms(reporter);
835     test_bad_uniforms(reporter);
836     test_no_main(reporter);
837     test_zero_attrs(reporter);
838     test_zero_varyings(reporter);
839     test_bad_strides(reporter);
840     test_bad_offsets(reporter);
841     test_too_many_attrs(reporter);
842     test_too_many_varyings(reporter);
843     test_duplicate_attribute_names(reporter);
844     test_duplicate_varying_names(reporter);
845     test_sneaky_attribute_name(reporter);
846     test_sneaky_varying_name(reporter);
847     test_good_position_varying(reporter);
848     test_bad_position_varying(reporter);
849     test_empty_attribute_name(reporter);
850     test_empty_varying_name(reporter);
851 }
852 
853 
DEF_TEST(MeshSpecVaryingPassthrough,reporter)854 DEF_TEST(MeshSpecVaryingPassthrough, reporter) {
855     static const Attribute kAttributes[]{
856             {Attribute::Type::kFloat2,        0, SkString{"position"}},
857             {Attribute::Type::kFloat2,        8, SkString{"uv"}      },
858             {Attribute::Type::kUByte4_unorm, 16, SkString{"color"}   },
859     };
860     static const Varying kVaryings[]{
861             {Varying::Type::kFloat2, SkString{"position"}},
862             {Varying::Type::kFloat2, SkString{"uv"}      },
863             {Varying::Type::kHalf4,  SkString{"color"}   },
864     };
865 
866     static constexpr char kVS[] = R"(
867             Varyings main(const Attributes a) {
868                 Varyings v;
869                 v.uv       = a.uv;
870                 v.position = a.position;
871                 v.color    = a.color;
872                 return v;
873             }
874     )";
875     auto check = [&] (const char* fs, const char* passthroughAttr) {
876         auto [spec, error] = SkMeshSpecification::Make(kAttributes,
877                                                        /*vertexStride=*/24,
878                                                        kVaryings,
879                                                        SkString(kVS),
880                                                        SkString(fs));
881         if (!spec) {
882             ERRORF(reporter, "%s\n%s", fs, error.c_str());
883             return;
884         }
885         int idx = SkMeshSpecificationPriv::PassthroughLocalCoordsVaryingIndex(*spec);
886         const SkString& actualAttr = idx >= 0 ? spec->attributes()[idx].name : SkString("<none>");
887         if (!passthroughAttr) {
888             if (idx >= 0) {
889                 ERRORF(reporter, "Expected no passthrough coords attribute, found %s.\n%s",
890                        actualAttr.c_str(),
891                        fs);
892             }
893         } else if (!actualAttr.equals(passthroughAttr)) {
894             ERRORF(reporter, "Expected %s as passthrough coords attribute, found %s.\n%s",
895                    passthroughAttr,
896                    actualAttr.c_str(),
897                    fs);
898         }
899     };
900 
901     // Simple
902     check(R"(float2 main(const Varyings v) {
903                   return v.uv;
904               })",
905           "uv");
906 
907     // Simple, using position
908     check(R"(float2 main(const Varyings v) {
909                   return v.position;
910               })",
911           "position");
912 
913     // Simple, with output color
914     check(R"(float2 main(const Varyings v, out half4 color) {
915                   color = v.color;
916                   return v.uv;
917               })",
918           "uv");
919 
920     // Three returns, all the same.
921     check(R"(uniform int selector;
922 
923              float2 main(const Varyings v, out half4 color) {
924                   if (selector == 0) {
925                       color = half4(1, 0, 0, 1);
926                       return v.position;
927                   }
928                   if (selector == 1) {
929                       color = half4(1, 1, 0, 1);
930                       return v.position;
931                   }
932                   color = half4(1, 0, 1, 1);
933                   return v.position;
934              })",
935           "position");
936 
937     // Three returns, one not like the others
938     check(R"(uniform int selector;
939 
940              float2 main(const Varyings v, out half4 color) {
941                   if (selector == 0) {
942                       color = color.bgra;
943                       return v.position;
944                   }
945                   if (selector == 1) {
946                       color = half4(1);
947                       return v.uv;
948                   }
949                   color = color;
950                   return v.position;
951              })",
952           nullptr);
953 
954     // Swizzles aren't handled (yet?).
955     check(R"(float2 main(const Varyings v) {
956                   return v.uv.yx;
957               })",
958           nullptr);
959 
960     // Return from non-main fools us?
961     check(R"(noinline half4 get_color(const Varyings v) { return v.color; }
962 
963              float2 main(const Varyings v, out half4 color) {
964                   color = get_color(v);
965                   return v.position;
966               })",
967           "position");
968 }
969 
DEF_TEST(MeshSpecUnusedVaryings,reporter)970 DEF_TEST(MeshSpecUnusedVaryings, reporter) {
971     static const Attribute kAttributes[]{
972             {Attribute::Type::kFloat2,        0, SkString{"position"}},
973             {Attribute::Type::kFloat2,        8, SkString{"uv"}      },
974             {Attribute::Type::kUByte4_unorm, 16, SkString{"color"}   },
975     };
976     static const Varying kVaryings[]{
977             {Varying::Type::kFloat2, SkString{"position"}},
978             {Varying::Type::kFloat2, SkString{"uv"}      },
979             {Varying::Type::kHalf4,  SkString{"color"}   },
980     };
981 
982     static constexpr char kVS[] = R"(
983             Varyings main(const Attributes a) {
984                 Varyings v;
985                 v.uv       = a.uv;
986                 v.position = a.position;
987                 v.color    = a.color;
988                 return v;
989             }
990     )";
991 
992     auto check = [&](const char* fs, bool positionDead, bool uvDead, bool colorDead) {
993         static_assert(std::size(kVaryings) == 3);
994         auto [spec, error] = SkMeshSpecification::Make(kAttributes,
995                                                        /*vertexStride=*/24,
996                                                        kVaryings,
997                                                        SkString(kVS),
998                                                        SkString(fs));
999         if (!spec) {
1000             ERRORF(reporter, "%s\n%s", fs, error.c_str());
1001             return;
1002         }
1003         bool positionActuallyDead = SkMeshSpecificationPriv::VaryingIsDead(*spec, 0);
1004         bool uvActuallyDead       = SkMeshSpecificationPriv::VaryingIsDead(*spec, 1);
1005         bool colorActuallyDead    = SkMeshSpecificationPriv::VaryingIsDead(*spec, 2);
1006         auto str = [](bool dead) { return dead ? "dead" : "not dead"; };
1007         if (positionActuallyDead != positionDead) {
1008             ERRORF(reporter,
1009                    "Expected position to be detected %s but it is detected %s.\n%s",
1010                    str(positionDead),
1011                    str(positionActuallyDead),
1012                    fs);
1013         }
1014         if (uvActuallyDead != uvDead) {
1015             ERRORF(reporter,
1016                    "Expected uv to be detected %s but it is detected %s.\n%s",
1017                    str(uvDead),
1018                    str(uvActuallyDead),
1019                    fs);
1020         }
1021         if (colorActuallyDead != colorDead) {
1022             ERRORF(reporter,
1023                    "Expected color to be detected %s but it is detected %s.\n%s",
1024                    str(colorDead),
1025                    str(colorActuallyDead),
1026                    fs);
1027         }
1028     };
1029 
1030     // Simple
1031     check(R"(float2 main(const Varyings v) {
1032                  return v.uv;
1033              })",
1034           true,
1035           true,
1036           true);
1037 
1038     // Simple, using position
1039     check(R"(float2 main(const Varyings v) {
1040                  return v.position;
1041              })",
1042           true,
1043           true,
1044           true);
1045 
1046     // Two returns that are both passthrough of the same varying
1047     check(R"(float2 main(const Varyings v, out half4 color) {
1048                  if (v.color.r > 0.5) {
1049                      color = v.color;
1050                      return v.uv;
1051                  } else {
1052                      color = 2*color;
1053                      return v.uv;
1054                  }
1055              })",
1056           true,
1057           true,
1058           false);
1059 
1060     // Two returns that are both passthrough of the different varyings and unused other varying
1061     check(R"(float2 main(const Varyings v, out half4 color) {
1062                  if (v.position.x > 10) {
1063                      color = half4(0);
1064                      return v.uv;
1065                  } else {
1066                      color = half4(1);
1067                      return v.position;
1068                  }
1069              })",
1070           false,
1071           false,
1072           true);
1073 
1074     // Passthrough but we also use the varying elsewhere
1075     check(R"(float2 main(const Varyings v, out half4 color) {
1076                  color = half4(v.uv.x, 0, 0, 1);
1077                  return v.uv;
1078              })",
1079           true,
1080           false,
1081           true);
1082 
1083     // Use two varyings is a return statement
1084     check(R"(float2 main(const Varyings v) {
1085                   return v.uv + v.position;
1086               })",
1087           false,
1088           false,
1089           true);
1090 
1091     // Slightly more complicated varying use.
1092     check(R"(noinline vec2 get_pos(const Varyings v) { return v.position; }
1093 
1094              noinline half4 identity(half4 c) { return c; }
1095 
1096              float2 main(const Varyings v, out half4 color) {
1097                  color = identity(v.color);
1098                  return v.uv + get_pos(v);
1099              })",
1100           false,
1101           false,
1102           false);
1103 
1104     // Go through assignment to another Varyings.
1105     check(R"(float2 main(const Varyings v) {
1106                  Varyings otherVaryings;
1107                  otherVaryings = v;
1108                  return otherVaryings.uv;
1109              })",
1110           true,
1111           false,
1112           true);
1113 
1114     // We're not very smart. We just look for any use of the field in any Varyings value and don't
1115     // do any data flow analysis.
1116     check(R"(float2 main(const Varyings v) {
1117                  Varyings otherVaryings;
1118                  otherVaryings.uv       = half2(5);
1119                  otherVaryings.position = half2(10);
1120                  return otherVaryings.position;
1121              })",
1122           false,
1123           false,
1124           true);
1125 }
1126