xref: /aosp_15_r20/external/skia/tests/MeshTest.cpp (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
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/SkBlendMode.h"
9 #include "include/core/SkBlender.h"
10 #include "include/core/SkColor.h"
11 #include "include/core/SkColorFilter.h"
12 #include "include/core/SkData.h"
13 #include "include/core/SkMesh.h"
14 #include "include/core/SkRect.h"
15 #include "include/core/SkRefCnt.h"
16 #include "include/core/SkShader.h"
17 #include "include/core/SkSpan.h"
18 #include "include/core/SkString.h"
19 #include "include/core/SkTypes.h"
20 #include "include/effects/SkRuntimeEffect.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 
36 using Attribute = SkMeshSpecification::Attribute;
37 using Varying   = SkMeshSpecification::Varying;
38 
attr_type_str(const Attribute::Type type)39 static 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     }
47     SkUNREACHABLE;
48 }
49 
var_type_str(const Varying::Type type)50 static 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     }
61     SkUNREACHABLE;
62 }
63 
make_description(SkSpan<const Attribute> attributes,size_t stride,SkSpan<const Varying> varyings,const SkString & vs,const SkString & fs)64 static SkString make_description(SkSpan<const Attribute> attributes,
65                                  size_t                  stride,
66                                  SkSpan<const Varying>   varyings,
67                                  const SkString&         vs,
68                                  const SkString&         fs) {
69     static constexpr size_t kMax = 10;
70     SkString result;
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 
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)94 static bool check_for_failure(skiatest::Reporter*     reporter,
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) {
103         ERRORF(reporter,
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)) {
109         ERRORF(reporter,
110                "    Expected: %s\n"
111                "Actual error: %s\n",
112                expectedErrorSubstring, error.c_str());
113         return false;
114     }
115     return true;
116 }
117 
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)118 static bool check_for_success(skiatest::Reporter*         reporter,
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     }
133     ERRORF(reporter,
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
141 static const SkString kValidVS {R"(
142 Varyings main(const Attributes attrs) {
143     Varyings v;
144     return v;
145 })"};
146 
147 // There are multiple valid VS signatures.
148 static 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
159 static const Attribute kValidAttrs[] = {
160         {Attribute::Type::kFloat4, 0, SkString{"pos"}},
161 };
162 static constexpr size_t kValidStride = 4*4;
163 static const Varying kValidVaryings[] = {
164         {Varying::Type::kFloat2, SkString{"uv"}},
165 };
166 
DEF_TEST(MeshSpec_Valid,reporter)167 DEF_TEST(MeshSpec_Valid, reporter) {
168     for (const auto& validFS : kValidFSes) {
169         if (!check_for_success(reporter,
170                                kValidAttrs,
171                                kValidStride,
172                                kValidVaryings,
173                                kValidVS,
174                                validFS)) {
175             return;
176         }
177     }
178 }
179 
DEF_TEST(MeshSpec_InvalidSignature,reporter)180 DEF_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) {
217             if (!check_for_failure(reporter,
218                                    kValidAttrs,
219                                    kValidStride,
220                                    kValidVaryings,
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);
231         if (!check_for_failure(reporter,
232                                kValidAttrs,
233                                kValidStride,
234                                kValidVaryings,
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);
244         if (!check_for_failure(reporter,
245                                kValidAttrs,
246                                kValidStride,
247                                kValidVaryings,
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
DEF_TEST(MeshSpec_Float4Color,reporter)256 DEF_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     };
264     check_for_success(reporter,
265                       kValidAttrs,
266                       kValidStride,
267                       kValidVaryings,
268                       kValidVS,
269                       kFloat4FS);
270 }
271 
DEF_TEST(MeshSpec_DisallowsChildEffectInVertex,reporter)272 DEF_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 
286         if (!check_for_failure(reporter,
287                                kValidAttrs,
288                                kValidStride,
289                                kValidVaryings,
290                                vsWithChild,
291                                kValidFSes[0],
292                                "effects are not permitted in mesh vertex shaders")) {
293             return;
294         }
295 
296         if (!check_for_failure(reporter,
297                                kValidAttrs,
298                                kValidStride,
299                                kValidVaryings,
300                                vsWithChild,
301                                fsWithChild,
302                                "effects are not permitted in mesh vertex shaders")) {
303             return;
304         }
305     }
306 }
307 
DEF_TEST(MeshSpec_AllowsChildEffectInFragment,reporter)308 DEF_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 
319         if (!check_for_success(reporter,
320                                kValidAttrs,
321                                kValidStride,
322                                kValidVaryings,
323                                kValidVS,
324                                fsWithChild)) {
325             return;
326         }
327     }
328 }
329 
DEF_TEST(MeshSpec_FindChild,reporter)330 DEF_TEST(MeshSpec_FindChild, reporter) {
331     SkString fsWithChild{"uniform shader myshader;"
332                          "uniform blender myblender;"
333                          "uniform colorFilter mycolorfilter;"};
334     fsWithChild.append(kValidFSes[0]);
335 
336     sk_sp<SkMeshSpecification> meshSpec;
337     if (!check_for_success(reporter,
338                            kValidAttrs,
339                            kValidStride,
340                            kValidVaryings,
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 
DEF_TEST(Mesh_ChildEffectsMatchSpec,reporter)353 DEF_TEST(Mesh_ChildEffectsMatchSpec, reporter) {
354     auto test = [&](const char* prefix,
355                     SkSpan<SkRuntimeEffect::ChildPtr> children,
356                     const char* expectedError = nullptr) {
357         SkString fsWithChild{prefix};
358         fsWithChild.append(kValidFSes[0]);
359 
360         sk_sp<SkMeshSpecification> meshSpec;
361         if (!check_for_success(reporter,
362                                kValidAttrs,
363                                kValidStride,
364                                kValidVaryings,
365                                kValidVS,
366                                fsWithChild,
367                                &meshSpec)) {
368             return;
369         }
370 
371         constexpr float kVertexCount = 4;
372         sk_sp<SkMesh::VertexBuffer> vertexBuffer =
373                 SkMeshes::MakeVertexBuffer(nullptr, kValidStride * kVertexCount);
374         SkMesh::Result result = SkMesh::Make(meshSpec,
375                                              SkMesh::Mode::kTriangleStrip,
376                                              vertexBuffer,
377                                              kVertexCount,
378                                              /*vertexOffset=*/0,
379                                              /*uniforms=*/nullptr,
380                                              children,
381                                              SkRect::MakeEmpty());
382 
383         if (expectedError) {
384             REPORTER_ASSERT(reporter, !result.mesh.isValid());
385             REPORTER_ASSERT(reporter,
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());
391             REPORTER_ASSERT(reporter,
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)};
399     SkRuntimeEffect::ChildPtr childFilter[]  = {SkColorFilters::LinearToSRGBGamma()};
400     SkRuntimeEffect::ChildPtr childBlender[] = {SkBlender::Mode(SkBlendMode::kSrcOver)};
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 
DEF_TEST(MeshSpec_ValidUniforms,reporter)435 DEF_TEST(MeshSpec_ValidUniforms, reporter) {
436     using Uniform = SkMeshSpecification::Uniform;
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;
442     constexpr Flags kColor = Uniform::kColor_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) {
451             return Uniform{name, offset, type, count, flags | Uniform::kArray_Flag};
452         } else {
453             SkASSERT(!(flags & Uniform::kArray_Flag));
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);
606         sk_sp<SkMeshSpecification> spec;
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()) {
613             ERRORF(reporter,
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) {
623                 ERRORF(reporter,
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) {
630                 ERRORF(reporter,
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) {
638                 ERRORF(reporter,
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) {
646                 ERRORF(reporter,
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) {
654                 ERRORF(reporter,
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             }
661         }
662     }
663 }
664 
DEF_TEST(MeshSpec_InvalidUniforms,reporter)665 DEF_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             }
700         }
701     }
702 }
703 
DEF_TEST(MeshSpec_MissingMain,reporter)704 DEF_TEST(MeshSpec_MissingMain, reporter) {
705     static const SkString kHelper{"float2 swiz(float2 x) { return z.yx; }"};
706 
707     // Empty VS
708     if (!check_for_failure(reporter,
709                            kValidAttrs,
710                            kValidStride,
711                            kValidVaryings,
712                            SkString{},
713                            kValidFSes[0])) {
714         return;
715     }
716 
717     // VS with helper function but no main
718     if (!check_for_failure(reporter,
719                            kValidAttrs,
720                            kValidStride,
721                            kValidVaryings,
722                            kHelper,
723                            kValidFSes[0])) {
724         return;
725     }
726 
727     // Empty FS
728     if (!check_for_failure(reporter,
729                            kValidAttrs,
730                            kValidStride,
731                            kValidVaryings,
732                            kValidVS,
733                            SkString{})) {
734         return;
735     }
736 
737     // VS with helper function but no main
738     if (!check_for_failure(reporter,
739                            kValidAttrs,
740                            kValidStride,
741                            kValidVaryings,
742                            kValidVS,
743                            kHelper)) {
744         return;
745     }
746 }
747 
DEF_TEST(MeshSpec_ZeroAttributes,reporter)748 DEF_TEST(MeshSpec_ZeroAttributes, reporter) {
749     // We require at least one attribute
750     check_for_failure(reporter,
751                       SkSpan<Attribute>(),
752                       kValidStride,
753                       kValidVaryings,
754                       kValidVS,
755                       kValidFSes[0]);
756 }
757 
DEF_TEST(MeshSpec_ZeroVaryings,reporter)758 DEF_TEST(MeshSpec_ZeroVaryings, reporter) {
759     // Varyings are not required.
760     check_for_success(reporter,
761                       kValidAttrs,
762                       kValidStride,
763                       SkSpan<Varying>(),
764                       kValidVS,
765                       kValidFSes[0]);
766 }
767 
DEF_TEST(MeshSpec_InvalidStride,reporter)768 DEF_TEST(MeshSpec_InvalidStride, reporter) {
769     // Zero stride
770     if (!check_for_failure(reporter,
771                            kValidAttrs,
772                            0,
773                            kValidVaryings,
774                            kValidVS,
775                            kValidFSes[0])) {
776         return;
777     }
778 
779     // Unaligned
780     if (!check_for_failure(reporter,
781                            kValidAttrs,
782                            kValidStride + 1,
783                            kValidVaryings,
784                            kValidVS,
785                            kValidFSes[0])) {
786         return;
787     }
788 
789     // Too large
790     if (!check_for_failure(reporter,
791                            kValidAttrs,
792                            1 << 20,
793                            kValidVaryings,
794                            kValidVS,
795                            kValidFSes[0])) {
796         return;
797     }
798 }
799 
DEF_TEST(MeshSpec_InvalidOffset,reporter)800 DEF_TEST(MeshSpec_InvalidOffset, reporter) {
801     {  // offset isn't aligned
802         static const Attribute kAttributes[] {
803                 {Attribute::Type::kFloat4,  1, SkString{"var"}},
804         };
805         if (!check_for_failure(reporter,
806                                kAttributes,
807                                32,
808                                kValidVaryings,
809                                kValidVS,
810                                kValidFSes[0])) {
811             return;
812         }
813     }
814     {  // straddles stride boundary
815         static const Attribute kAttributes[] {
816                 {Attribute::Type::kFloat4,   0, SkString{"var"}},
817                 {Attribute::Type::kFloat2,  16, SkString{"var"}},
818         };
819         if (!check_for_failure(reporter,
820                                kAttributes,
821                                20,
822                                kValidVaryings,
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         };
832         if (!check_for_failure(reporter,
833                                kAttributes,
834                                4,
835                                kValidVaryings,
836                                kValidVS,
837                                kValidFSes[0])) {
838             return;
839         }
840     }
841 }
842 
DEF_TEST(MeshSpec_TooManyAttributes,reporter)843 DEF_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     }
850     check_for_failure(reporter,
851                       attrs,
852                       4*4,
853                       kValidVaryings,
854                       kValidVS,
855                       kValidFSes[0]);
856 }
857 
DEF_TEST(MeshSpec_TooManyVaryings,reporter)858 DEF_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     }
865     check_for_failure(reporter,
866                       kValidAttrs,
867                       kValidStride,
868                       SkSpan(varyings),
869                       kValidVS,
870                       kValidFSes[0]);
871 }
872 
DEF_TEST(MeshSpec_DuplicateAttributeNames,reporter)873 DEF_TEST(MeshSpec_DuplicateAttributeNames, reporter) {
874     static const Attribute kAttributes[] {
875             {Attribute::Type::kFloat4,  0, SkString{"var"}},
876             {Attribute::Type::kFloat2, 16, SkString{"var"}}
877     };
878     check_for_failure(reporter,
879                       kAttributes,
880                       24,
881                       kValidVaryings,
882                       kValidVS,
883                       kValidFSes[0]);
884 }
885 
DEF_TEST(MeshSpec_DuplicateVaryingNames,reporter)886 DEF_TEST(MeshSpec_DuplicateVaryingNames, reporter) {
887     static const Varying kVaryings[] {
888         {Varying::Type::kFloat4, SkString{"var"}},
889         {Varying::Type::kFloat3, SkString{"var"}}
890     };
891     check_for_failure(reporter,
892                       kValidAttrs,
893                       kValidStride,
894                       kVaryings,
895                       kValidVS,
896                       kValidFSes[0]);
897 }
898 
899 static constexpr const char* kSneakyName = "name; float3 sneaky";
900 
DEF_TEST(MeshSpec_SneakyExtraAttribute,reporter)901 DEF_TEST(MeshSpec_SneakyExtraAttribute, reporter) {
902     static const Attribute kAttributes[] {
903             {Attribute::Type::kFloat4, 0, SkString{kSneakyName}},
904     };
905     check_for_failure(reporter,
906                       kAttributes,
907                       16,
908                       kValidVaryings,
909                       kValidVS,
910                       kValidFSes[0]);
911 }
912 
DEF_TEST(MeshSpec_SneakyExtraVarying,reporter)913 DEF_TEST(MeshSpec_SneakyExtraVarying, reporter) {
914     static const Varying kVaryings[] {
915             {Varying::Type::kFloat4, SkString{kSneakyName}},
916     };
917     check_for_failure(reporter,
918                       kValidAttrs,
919                       kValidStride,
920                       kVaryings,
921                       kValidVS,
922                       kValidFSes[0]);
923 }
924 
DEF_TEST(MeshSpec_AllowsFloat2PositionVarying,reporter)925 DEF_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     };
930     check_for_success(reporter,
931                       kValidAttrs,
932                       kValidStride,
933                       kVaryings,
934                       kValidVS,
935                       kValidFSes[0]);
936 }
937 
DEF_TEST(MeshSpec_InvalidPositionType,reporter)938 DEF_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     };
943     check_for_failure(reporter,
944                       kValidAttrs,
945                       kValidStride,
946                       kVaryings,
947                       kValidVS,
948                       kValidFSes[0]);
949 }
950 
DEF_TEST(MeshSpec_EmptyAttributeName,reporter)951 DEF_TEST(MeshSpec_EmptyAttributeName, reporter) {
952     static const Attribute kAttributes[] {
953             {Attribute::Type::kFloat4, 0, SkString{}},
954     };
955     check_for_failure(reporter,
956                       kAttributes,
957                       16,
958                       kValidVaryings,
959                       kValidVS,
960                       kValidFSes[0]);
961 }
962 
DEF_TEST(MeshSpec_EmptyVaryingName,reporter)963 DEF_TEST(MeshSpec_EmptyVaryingName, reporter) {
964     static const Varying kVaryings[] {
965             {Varying::Type::kFloat4, SkString{}},
966     };
967     check_for_failure(reporter,
968                       kValidAttrs,
969                       kValidStride,
970                       kVaryings,
971                       kValidVS,
972                       kValidFSes[0]);
973 }
974 
DEF_TEST(MeshSpecVaryingPassthrough,reporter)975 DEF_TEST(MeshSpecVaryingPassthrough, reporter) {
976     static const Attribute kAttributes[]{
977             {Attribute::Type::kFloat2,        0, SkString{"position"}},
978             {Attribute::Type::kFloat2,        8, SkString{"uv"}      },
979             {Attribute::Type::kUByte4_unorm, 16, SkString{"color"}   },
980     };
981     static const Varying kVaryings[]{
982             {Varying::Type::kFloat2, SkString{"position"}},
983             {Varying::Type::kFloat2, SkString{"uv"}      },
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         }
1006         int idx = SkMeshSpecificationPriv::PassthroughLocalCoordsVaryingIndex(*spec);
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 
DEF_TEST(MeshSpecUnusedVaryings,reporter)1091 DEF_TEST(MeshSpecUnusedVaryings, reporter) {
1092     static const Attribute kAttributes[]{
1093             {Attribute::Type::kFloat2,        0, SkString{"position"}},
1094             {Attribute::Type::kFloat2,        8, SkString{"uv"}      },
1095             {Attribute::Type::kUByte4_unorm, 16, SkString{"color"}   },
1096     };
1097     static const Varying kVaryings[]{
1098             {Varying::Type::kFloat2, SkString{"position"}},
1099             {Varying::Type::kFloat2, SkString{"uv"}      },
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) {
1129             ERRORF(reporter,
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) {
1136             ERRORF(reporter,
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) {
1143             ERRORF(reporter,
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 }
1247