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