Blender V4.5
shader_preprocess_test.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: Apache-2.0 */
4
6
7#include "gpu_testing.hh"
8
9namespace blender::gpu::tests {
10
12{
13 using namespace shader;
14 using namespace std;
15
16 string input = "test (u, u(s,(s,s)), u) {t{{}},t,{};(,)} {u{}} end";
19
21 "test (u! u(s!(s!s))! u) {t{{}},t,{};(!)} {u{}} end");
22
23 vector<string> split_expect{"test (u, u(s,(s,s", "", ", u", " {t{{}},t,{};(,", "} {u{}} end"};
25 EXPECT_EQ_VECTOR(split_expect, split_result);
26
27 string input2 = "u, u(s,(s,s)), u";
28 vector<string> split_expect2{"u", " u(s,(s,s))", " u"};
30 input2, ',', '(', ')');
31 EXPECT_EQ_VECTOR(split_expect2, split_result2);
32
33 string input_reference = "void func(int &a, int (&c)[2]) {{ int &b = a; }} int &b = a;";
34 int fn_ref_count = 0, arg_ref_count = 0, global_ref_count = 0;
36 input_reference, [&](int parenthesis_depth, int bracket_depth, char & /*c*/) {
37 if ((parenthesis_depth == 1 || parenthesis_depth == 2) && bracket_depth == 0) {
38 arg_ref_count += 1;
39 }
40 else if (bracket_depth > 0) {
41 fn_ref_count += 1;
42 }
43 else if (bracket_depth == 0 && parenthesis_depth == 0) {
44 global_ref_count += 1;
45 }
46 });
47 EXPECT_EQ(arg_ref_count, 2);
48 EXPECT_EQ(fn_ref_count, 1);
49 EXPECT_EQ(global_ref_count, 1);
50}
51GPU_TEST(preprocess_utilities);
52
53static std::string process_test_string(std::string str,
54 std::string &first_error,
55 shader::metadata::Source *r_metadata = nullptr,
58{
59 using namespace shader;
60 Preprocessor preprocessor;
62 std::string result = preprocessor.process(
63 language,
64 str,
65 "test.glsl",
66 true,
67 true,
68 [&](const std::smatch & /*match*/, const char *err_msg) {
69 if (first_error.empty()) {
70 first_error = err_msg;
71 }
72 },
73 metadata);
74
75 if (r_metadata != nullptr) {
76 *r_metadata = metadata;
77 }
78
79 /* Strip first line directive as they are platform dependent. */
80 size_t newline = result.find('\n');
81 return result.substr(newline + 1);
82}
83
85{
86 using namespace shader;
87 using namespace std;
88
89 {
90 string input = R"([[gpu::unroll]] for (int i = 2; i < 4; i++, y++) { content += i; })";
91 string expect = R"({ int i = 2;
92#line 1
93{ content += i; }
94#line 1
95i++, y++;
96#line 1
97{ content += i; }
98#line 1
99i++, y++;
100#line 1
101})";
102 string error;
104 EXPECT_EQ(output, expect);
105 EXPECT_EQ(error, "");
106 }
107 {
108 string input = R"([[gpu::unroll]] for (int i = 2; i < 4 && i < y; i++, y++) { cont += i; })";
109 string expect = R"({ int i = 2;
110#line 1
111if (i < y) { cont += i; }
112#line 1
113i++, y++;
114#line 1
115if (i < y) { cont += i; }
116#line 1
117i++, y++;
118#line 1
119})";
120 string error;
122 EXPECT_EQ(output, expect);
123 EXPECT_EQ(error, "");
124 }
125 {
126 string input = R"([[gpu::unroll(2)]] for (; i < j;) { content += i; })";
127 string expect = R"({ ;
128#line 1
129if (i < j) { content += i; }
130#line 1
131;
132#line 1
133if (i < j) { content += i; }
134#line 1
135;
136#line 1
137})";
138 string error;
140 EXPECT_EQ(output, expect);
141 EXPECT_EQ(error, "");
142 }
143 {
144 string input = R"([[gpu::unroll(2)]] for (; i < j;) { [[gpu::unroll(2)]] for (; j < k;) {} })";
145 string expect = R"({ ;
146#line 1
147if (i < j) { { ;
148#line 1
149 if (j < k) {}
150#line 1
151 ;
152#line 1
153 if (j < k) {}
154#line 1
155 ;
156#line 1
157 } }
158#line 1
159;
160#line 1
161if (i < j) { { ;
162#line 1
163 if (j < k) {}
164#line 1
165 ;
166#line 1
167 if (j < k) {}
168#line 1
169 ;
170#line 1
171 } }
172#line 1
173;
174#line 1
175})";
176 string error;
178 EXPECT_EQ(output, expect);
179 EXPECT_EQ(error, "");
180 }
181 {
182 string input = R"([[gpu::unroll(2)]] for (; i < j;) { break; })";
183 string error;
185 EXPECT_EQ(error, "Error: Unrolled loop cannot contain \"break\" statement.");
186 }
187 {
188 string input = R"([[gpu::unroll(2)]] for (; i < j;) { continue; })";
189 string error;
191 EXPECT_EQ(error, "Error: Unrolled loop cannot contain \"continue\" statement.");
193 {
194 string input = R"([[gpu::unroll(2)]] for (; i < j;) { for (; j < k;) {break;continue;} })";
195 string expect = R"({ ;
196#line 1
197if (i < j) { for (; j < k;) {break;continue;} }
198#line 1
199;
200#line 1
201if (i < j) { for (; j < k;) {break;continue;} }
202#line 1
203;
204#line 1
205})";
206 string error;
208 EXPECT_EQ(output, expect);
209 EXPECT_EQ(error, "");
210 }
211 {
212 string input = R"([[gpu::unroll]] for (int i = 3; i > 2; i++) {})";
213 string error;
215 EXPECT_EQ(error, "Error: Unsupported condition in unrolled loop.");
216 }
217}
218GPU_TEST(preprocess_unroll);
219
220static void test_preprocess_template()
221{
222 using namespace shader;
223 using namespace std;
224
225 {
226 string input = R"(
227template<typename T>
228void func(T a) {a;}
229template void func<float>(float a);)";
230 string expect = R"(
231#define func_TEMPLATE(T) \
232void func(T a) {a;}
233func_TEMPLATE(float)/*float a*/)";
234 string error;
236 EXPECT_EQ(output, expect);
237 EXPECT_EQ(error, "");
238 }
239 {
240 string input = R"(
241template<typename T, int i>
242void func(T a) {
243 a;
244}
245template void func<float, 1>(float a);)";
246 string expect = R"(
247#define func_TEMPLATE(T, i) \
248void func_##T##_##i##_(T a) { \
249 a; \
250}
251func_TEMPLATE(float, 1)/*float a*/)";
252 string error;
254 EXPECT_EQ(output, expect);
255 EXPECT_EQ(error, "");
256 }
257 {
258 string input = R"(template<typename T, int i = 0> void func(T a) {a;)";
259 string error;
261 EXPECT_EQ(error, "Template declaration unsupported syntax");
262 }
263 {
264 string input = R"(template void func(float a);)";
265 string error;
267 EXPECT_EQ(error, "Template instantiation unsupported syntax");
268 }
269 {
270 string input = R"(func<float, 1>(a);)";
271 string expect = R"(TEMPLATE_GLUE2(func, float, 1)(a);)";
272 string error;
274 EXPECT_EQ(output, expect);
275 EXPECT_EQ(error, "");
276 }
277}
278GPU_TEST(preprocess_template);
279
280static void test_preprocess_reference()
281{
282 using namespace shader;
283 using namespace std;
284
285 {
286 string input = R"(void func() { auto &a = b; a.a = 0; c = a(a); a_c_a = a; })";
287 string expect = R"(void func() { b.a = 0; c = a(b); a_c_a = b; })";
288 string error;
290 EXPECT_EQ(output, expect);
291 EXPECT_EQ(error, "");
292 }
293 {
294 string input = R"(void func() { const int &a = b; a.a = 0; c = a(a); })";
295 string expect = R"(void func() { b.a = 0; c = a(b); })";
296 string error;
298 EXPECT_EQ(output, expect);
299 EXPECT_EQ(error, "");
300 }
301 {
302 string input = R"(void func() { const int i = 0; auto &a = b[i]; a.a = 0; })";
303 string expect = R"(void func() { const int i = 0; b[i].a = 0; })";
304 string error;
306 EXPECT_EQ(output, expect);
308 }
309 {
310 string input = R"(void func() { auto &a = b(0); })";
311 string error;
313 EXPECT_EQ(error, "Reference definitions cannot contain function calls.");
314 }
315 {
316 string input = R"(void func() { int i = 0; auto &a = b[i++]; })";
317 string error;
319 EXPECT_EQ(error, "Reference definitions cannot have side effects.");
320 }
321 {
322 string input = R"(void func() { auto &a = b[0 + 1]; })";
323 string error;
326 "Array subscript inside reference declaration must be a single variable or a "
327 "constant, not an expression.");
328 }
329 {
330 string input = R"(void func() { auto &a = b[c]; })";
331 string error;
334 "Cannot locate array subscript variable declaration. "
335 "If it is a global variable, assign it to a temporary const variable for "
336 "indexing inside the reference.");
337 }
338 {
339 string input = R"(void func() { int c = 0; auto &a = b[c]; })";
340 string error;
342 EXPECT_EQ(error, "Array subscript variable must be declared as const qualified.");
343 }
344 {
345 string input = R"(auto &a = b;)";
346 string error;
348 EXPECT_EQ(error, "Reference is defined inside a global or unterminated scope.");
349 }
350}
351GPU_TEST(preprocess_reference);
352
354{
355 using namespace shader;
356 using namespace std;
357
358 {
359 string input = R"(
360int func(int a, int b = 0)
361{
362 return a + b;
363}
364)";
365 string expect = R"(
366int func(int a, int b)
367{
368 return a + b;
369}
370#line 2
371int func(int a)
372{
373#line 2
374 return func(a, 0);
375}
376#line 6
377)";
378 string error;
380 EXPECT_EQ(output, expect);
381 EXPECT_EQ(error, "");
382 }
383 {
384 string input = R"(
385int func(int a = 0, const int b = 0)
386{
387 return a + b;
388}
389)";
390 string expect = R"(
391int func(int a, const int b)
392{
393 return a + b;
394}
395#line 2
396int func(int a)
397{
398#line 2
399 return func(a, 0);
400}
401#line 2
402int func()
403{
404#line 2
405 return func(0);
406}
407#line 6
408)";
409 string error;
411 EXPECT_EQ(output, expect);
412 EXPECT_EQ(error, "");
413 }
414 {
415 string input = R"(
416int2 func(int2 a = int2(0, 0)) {
417 return a;
419)";
420 string expect = R"(
421int2 func(int2 a) {
422 return a;
423}
424#line 2
425int2 func()
426{
427#line 2
428 return func(int2(0, 0));
429}
430#line 6
431)";
432 string error;
434 EXPECT_EQ(output, expect);
435 EXPECT_EQ(error, "");
436 }
437 {
438 string input = R"(
439void func(int a = 0) {
440 a;
441}
442)";
443 string expect = R"(
444void func(int a) {
445 a;
446}
447#line 2
448void func()
449{
450#line 2
451 func(0);
452}
453#line 6
454)";
455 string error;
457 EXPECT_EQ(output, expect);
458 EXPECT_EQ(error, "");
459 }
460}
461GPU_TEST(preprocess_default_arguments);
462
463static void test_preprocess_namespace()
464{
465 using namespace shader;
466 using namespace std;
467
468 {
469 string input = R"(
470namespace A {
471struct S {};
472int func(int a)
473{
474 S s;
475 return B::func(int a);
476}
477int func2(int a)
478{
479 T s;
480 s.S;
481 s.func;
482 return func(int a);
483}
484}
485)";
486 string expect = R"(
487
488struct A_S {};
489int A_func(int a)
490{
491 A_S s;
492 return B_func(int a);
493}
494int A_func2(int a)
495{
496 T s;
497 s.S;
498 s.func;
499 return A_func(int a);
500}
501
502)";
503 string error;
505 EXPECT_EQ(output, expect);
506 EXPECT_EQ(error, "");
507 }
508 {
509 string input = R"(
510namespace A::B {
511int func(int a)
512{
513 return a;
514}
515int func2(int a)
516{
517 return func(int a);
518}
519}
520)";
521 string expect = R"(
522
523int A_B_func(int a)
524{
525 return a;
526}
527int A_B_func2(int a)
528{
529 return A_B_func(int a);
530}
531
532)";
533 string error;
535 EXPECT_EQ(output, expect);
536 EXPECT_EQ(error, "");
537 }
538 {
539 string input = R"(
540namespace A {
541namespace B {
542int func(int a)
543{
544 return a;
545}
546}
547}
548)";
549 string error;
551 EXPECT_EQ(error, "Nested namespaces are unsupported.");
552 }
553 {
554 string input = R"(
555namespace A {
556int test(int a) {}
557int func(int a)
558{
559 using B::test;
560 return test(a);
561}
562}
563)";
564 string expect = R"(
565
566int A_test(int a) {}
567int A_func(int a)
568{
569
570 return B_test(a);
571}
572
573)";
574 string error;
576 EXPECT_EQ(output, expect);
577 EXPECT_EQ(error, "");
578 }
579 {
580 string input = R"(
581int func(int a)
582{
583 using B = A::S;
584 B b;
585 using C = A::F;
586 C f = A::B();
587 f = B();
588 B d;
589}
590)";
591 string expect = R"(
592int func(int a)
593{
594
595 A_S b;
596
597 A_F f = A_B();
598 f = B();
599 A_S d;
600}
601)";
602 string error;
604 EXPECT_EQ(output, expect);
605 EXPECT_EQ(error, "");
606 }
607 {
608 string input = R"(
609namespace A::B {
610void func() {}
611struct S {};
612}
613namespace A::B {
614using A::B::func;
615using S = A::B::S;
616void test() {
617 S s;
618 func();
619}
620}
621)";
622 string expect = R"(
623
624void A_B_func() {}
625struct A_B_S {};
626
627
628
629
630void A_B_test() {
631 A_B_S s;
632 A_B_func();
633}
634
635)";
636 string error;
638 EXPECT_EQ(output, expect);
639 EXPECT_EQ(error, "");
640 }
641 {
642 string input = R"(
643using B = A::T;
644)";
645 string error;
647 EXPECT_EQ(error, "The `using` keyword is not allowed in global scope.");
648 }
649 {
650 string input = R"(
651namespace A {
652using namespace B;
653}
654)";
655 string error;
658 "Unsupported `using namespace`. "
659 "Add individual `using` directives for each needed symbol.");
660 }
661 {
662 string input = R"(
663namespace A {
664using B::func;
665}
666)";
667 string error;
670 "The `using` keyword is only allowed in namespace scope to make visible symbols "
671 "from the same namespace declared in another scope, potentially from another "
672 "file.");
673 }
674 {
675 string input = R"(
676namespace A {
677using C = B::func;
678}
679)";
680 string error;
683 "The `using` keyword is only allowed in namespace scope to make visible symbols "
684 "from the same namespace declared in another scope, potentially from another "
685 "file.");
686 }
687 {
688 /* Template on the same line as function signature inside a namespace.
689 * Template instantiation with other functions. */
690 string input = R"(
691namespace NS {
692template<typename T> T read(T a)
693{
694 return a;
695}
696template float read<float>(float);
697float write(float a){ return a; }
698}
699)";
700
701 string expect = R"(
702
703#define NS_read_TEMPLATE(T) T NS_read(T a) \
704{ \
705 return a; \
706}
707NS_read_TEMPLATE(float)/*float*/
708float NS_write(float a){ return a; }
709
710)";
711 string error;
713 EXPECT_EQ(output, expect);
714 EXPECT_EQ(error, "");
715 }
716}
717GPU_TEST(preprocess_namespace);
718
719static void test_preprocess_swizzle()
720{
721 using namespace shader;
722 using namespace std;
723
724 {
725 string input = R"(a.xyzw().aaa().xxx().grba().yzww; aaaa();)";
726 string expect = R"(a.xyzw .aaa .xxx .grba .yzww; aaaa();)";
727 string error;
729 EXPECT_EQ(output, expect);
730 EXPECT_EQ(error, "");
731 }
732}
733GPU_TEST(preprocess_swizzle);
734
735#ifdef __APPLE__ /* This processing is only done for metal compatibility. */
736static void test_preprocess_matrix_constructors()
737{
738 using namespace shader;
739 using namespace std;
740
741 {
742 string input = R"(mat3(a); mat3 a; my_mat4x4(a); mat2x2(a); mat3x2(a);)";
743 string expect = R"(__mat3x3(a); mat3 a; my_mat4x4(a); __mat2x2(a); mat3x2(a);)";
744 string error;
745 string output = process_test_string(input, error, nullptr, Preprocessor::SourceLanguage::GLSL);
746 EXPECT_EQ(output, expect);
747 EXPECT_EQ(error, "");
748 }
749}
750GPU_TEST(preprocess_matrix_constructors);
751#endif
752
753} // namespace blender::gpu::tests
EXPECT_EQ(BLI_expr_pylike_eval(expr, nullptr, 0, &result), EXPR_PYLIKE_INVALID)
std::string process(SourceLanguage language, std::string str, const std::string &filename, bool do_parse_function, bool do_small_type_linting, report_callback report_error, metadata::Source &r_metadata)
static std::string get_content_between_balanced_pair(const std::string &input, char start_delimiter, char end_delimiter, const bool backwards=false)
static void reference_search(std::string &str, std::function< void(int, int, char &)> callback)
static std::string replace_char_between_balanced_pair(const std::string &input, const char start_delimiter, const char end_delimiter, const char from, const char to)
static std::vector< std::string > split_string_not_between_balanced_pair(const std::string &str, const char delimiter, const char pair_start, const char pair_end)
static std::vector< std::string > split_string(const std::string &str, const char delimiter)
#define str(s)
#define input
#define output
#define GPU_TEST(test_name)
static void error(const char *str)
static void test_preprocess_namespace()
static void test_preprocess_reference()
static void test_preprocess_default_arguments()
static std::string process_test_string(std::string str, std::string &first_error, shader::metadata::Source *r_metadata=nullptr, shader::Preprocessor::SourceLanguage language=shader::Preprocessor::SourceLanguage::BLENDER_GLSL)
static void test_preprocess_utilities()