Files
test/source/blender/gpu/tests/shader_preprocess_test.cc

Ignoring revisions in .git-blame-ignore-revs. Click here to bypass and see the normal blame view.

901 lines
19 KiB
C++
Raw Normal View History

/* SPDX-FileCopyrightText: 2023 Blender Authors
*
* SPDX-License-Identifier: Apache-2.0 */
#include "glsl_preprocess/glsl_preprocess.hh"
#include "gpu_testing.hh"
namespace blender::gpu::tests {
static void test_preprocess_utilities()
{
using namespace shader;
using namespace std;
string input = "test (u, u(s,(s,s)), u) {t{{}},t,{};(,)} {u{}} end";
EXPECT_EQ(Preprocessor::get_content_between_balanced_pair(input, '{', '}'), "t{{}},t,{};(,)");
EXPECT_EQ(Preprocessor::get_content_between_balanced_pair(input, '{', '}', true), "u{}");
EXPECT_EQ(Preprocessor::replace_char_between_balanced_pair(input, '(', ')', ',', '!'),
"test (u! u(s!(s!s))! u) {t{{}},t,{};(!)} {u{}} end");
vector<string> split_expect{"test (u, u(s,(s,s", "", ", u", " {t{{}},t,{};(,", "} {u{}} end"};
vector<string> split_result = Preprocessor::split_string(input, ')');
EXPECT_EQ_VECTOR(split_expect, split_result);
string input2 = "u, u(s,(s,s)), u";
vector<string> split_expect2{"u", " u(s,(s,s))", " u"};
vector<string> split_result2 = Preprocessor::split_string_not_between_balanced_pair(
input2, ',', '(', ')');
EXPECT_EQ_VECTOR(split_expect2, split_result2);
string input_reference = "void func(int &a, int (&c)[2]) {{ int &b = a; }} int &b = a;";
int fn_ref_count = 0, arg_ref_count = 0, global_ref_count = 0;
Preprocessor::reference_search(
input_reference, [&](int parenthesis_depth, int bracket_depth, char & /*c*/) {
if ((parenthesis_depth == 1 || parenthesis_depth == 2) && bracket_depth == 0) {
arg_ref_count += 1;
}
else if (bracket_depth > 0) {
fn_ref_count += 1;
}
else if (bracket_depth == 0 && parenthesis_depth == 0) {
global_ref_count += 1;
}
});
EXPECT_EQ(arg_ref_count, 2);
EXPECT_EQ(fn_ref_count, 1);
EXPECT_EQ(global_ref_count, 1);
}
GPU_TEST(preprocess_utilities);
GPU: Shader: Add support for basic loop unrolling through preprocessor This adds basic unrolling support for 2 syntax: - `[[gpu::unroll]]` which does full loop unrolling - `[[gpu::unroll(x)]]` which unrolls `x` iteration Nesting is supported. This change is motivated by the added cost in compilation and execution time that some loops have even if they have compile time defined iteration counts. The syntax is inspired by `GL_EXT_control_flow_attributes`. However, we might want to have our own prefix to show it is a blender specific feature and that it differs from the standard. I propose `[[gpu::unroll]]`. In the future, we could extend this to support more directives that can be expanded to backend specific extension / syntax. This would avoid readability issue an error prone copy paste of large amount of preprocessor directives. Currently, given that GL's GLSL flavor doesn't support any of these attributes, the preprocessor does some copy-pasting that does the unrolling at the source level. Note that the added `#line` allow for correct error logging. For the `[[gpu::unroll]]` syntax, the `for` declaration needs to follow a specific syntax to deduce the number of loop iteration. This variant removes the continue condition between iteration, so all iterations are evaluated. This could be modified using a special keyword. For the `[[gpu::unroll(n)]]` syntax, the usercode needs to make sure that `n` is large enough to cover all iterations as the loop is completely removed. We could add shader `assert` to make sure that there is never a remaining iteration. This behavior is usually different from what you see in other implementation as we do not keep a loop at all. Usually, compilers still keep the loop if it is not unrolled fully. But given we don't have IR, this is the best we can do. `break` and `continue` statement are forbidden at the unrolled loop scope level. Nested loop and switch can contain these keywords. This is accounted for by checks in the pre-processor. Only `for` loops are supported for now. There are no real incentive to add support for `while` given how rare it is in the shader codebase. Rel #137446 Pull Request: https://projects.blender.org/blender/blender/pulls/137444
2025-05-05 13:37:51 +02:00
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)
GPU: Shader: Add support for basic loop unrolling through preprocessor This adds basic unrolling support for 2 syntax: - `[[gpu::unroll]]` which does full loop unrolling - `[[gpu::unroll(x)]]` which unrolls `x` iteration Nesting is supported. This change is motivated by the added cost in compilation and execution time that some loops have even if they have compile time defined iteration counts. The syntax is inspired by `GL_EXT_control_flow_attributes`. However, we might want to have our own prefix to show it is a blender specific feature and that it differs from the standard. I propose `[[gpu::unroll]]`. In the future, we could extend this to support more directives that can be expanded to backend specific extension / syntax. This would avoid readability issue an error prone copy paste of large amount of preprocessor directives. Currently, given that GL's GLSL flavor doesn't support any of these attributes, the preprocessor does some copy-pasting that does the unrolling at the source level. Note that the added `#line` allow for correct error logging. For the `[[gpu::unroll]]` syntax, the `for` declaration needs to follow a specific syntax to deduce the number of loop iteration. This variant removes the continue condition between iteration, so all iterations are evaluated. This could be modified using a special keyword. For the `[[gpu::unroll(n)]]` syntax, the usercode needs to make sure that `n` is large enough to cover all iterations as the loop is completely removed. We could add shader `assert` to make sure that there is never a remaining iteration. This behavior is usually different from what you see in other implementation as we do not keep a loop at all. Usually, compilers still keep the loop if it is not unrolled fully. But given we don't have IR, this is the best we can do. `break` and `continue` statement are forbidden at the unrolled loop scope level. Nested loop and switch can contain these keywords. This is accounted for by checks in the pre-processor. Only `for` loops are supported for now. There are no real incentive to add support for `while` given how rare it is in the shader codebase. Rel #137446 Pull Request: https://projects.blender.org/blender/blender/pulls/137444
2025-05-05 13:37:51 +02:00
{
using namespace shader;
Preprocessor preprocessor;
shader::metadata::Source metadata;
std::string result = preprocessor.process(
language,
GPU: Shader: Add support for basic loop unrolling through preprocessor This adds basic unrolling support for 2 syntax: - `[[gpu::unroll]]` which does full loop unrolling - `[[gpu::unroll(x)]]` which unrolls `x` iteration Nesting is supported. This change is motivated by the added cost in compilation and execution time that some loops have even if they have compile time defined iteration counts. The syntax is inspired by `GL_EXT_control_flow_attributes`. However, we might want to have our own prefix to show it is a blender specific feature and that it differs from the standard. I propose `[[gpu::unroll]]`. In the future, we could extend this to support more directives that can be expanded to backend specific extension / syntax. This would avoid readability issue an error prone copy paste of large amount of preprocessor directives. Currently, given that GL's GLSL flavor doesn't support any of these attributes, the preprocessor does some copy-pasting that does the unrolling at the source level. Note that the added `#line` allow for correct error logging. For the `[[gpu::unroll]]` syntax, the `for` declaration needs to follow a specific syntax to deduce the number of loop iteration. This variant removes the continue condition between iteration, so all iterations are evaluated. This could be modified using a special keyword. For the `[[gpu::unroll(n)]]` syntax, the usercode needs to make sure that `n` is large enough to cover all iterations as the loop is completely removed. We could add shader `assert` to make sure that there is never a remaining iteration. This behavior is usually different from what you see in other implementation as we do not keep a loop at all. Usually, compilers still keep the loop if it is not unrolled fully. But given we don't have IR, this is the best we can do. `break` and `continue` statement are forbidden at the unrolled loop scope level. Nested loop and switch can contain these keywords. This is accounted for by checks in the pre-processor. Only `for` loops are supported for now. There are no real incentive to add support for `while` given how rare it is in the shader codebase. Rel #137446 Pull Request: https://projects.blender.org/blender/blender/pulls/137444
2025-05-05 13:37:51 +02:00
str,
"test.glsl",
true,
true,
[&](const std::smatch & /*match*/, const char *err_msg) {
if (first_error.empty()) {
first_error = err_msg;
}
},
metadata);
if (r_metadata != nullptr) {
*r_metadata = metadata;
}
/* Strip first line directive as they are platform dependent. */
size_t newline = result.find('\n');
return result.substr(newline + 1);
}
static void test_preprocess_unroll()
{
using namespace shader;
using namespace std;
{
string input = R"([[gpu::unroll]] for (int i = 2; i < 4; i++, y++) { content += i; })";
string expect = R"({ int i = 2;
#line 1
{ content += i; }
#line 1
i++, y++;
#line 1
{ content += i; }
#line 1
i++, y++;
#line 1
})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"([[gpu::unroll]] for (int i = 2; i < 4 && i < y; i++, y++) { cont += i; })";
string expect = R"({ int i = 2;
#line 1
if (i < y) { cont += i; }
#line 1
i++, y++;
#line 1
if (i < y) { cont += i; }
#line 1
i++, y++;
#line 1
})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"([[gpu::unroll(2)]] for (; i < j;) { content += i; })";
string expect = R"({ ;
#line 1
if (i < j) { content += i; }
#line 1
;
#line 1
if (i < j) { content += i; }
#line 1
;
#line 1
})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"([[gpu::unroll(2)]] for (; i < j;) { [[gpu::unroll(2)]] for (; j < k;) {} })";
string expect = R"({ ;
#line 1
if (i < j) { { ;
#line 1
if (j < k) {}
#line 1
;
#line 1
if (j < k) {}
#line 1
;
#line 1
} }
#line 1
;
#line 1
if (i < j) { { ;
#line 1
if (j < k) {}
#line 1
;
#line 1
if (j < k) {}
#line 1
;
#line 1
} }
#line 1
;
#line 1
})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"([[gpu::unroll(2)]] for (; i < j;) { break; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Error: Unrolled loop cannot contain \"break\" statement.");
}
{
string input = R"([[gpu::unroll(2)]] for (; i < j;) { continue; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Error: Unrolled loop cannot contain \"continue\" statement.");
}
{
string input = R"([[gpu::unroll(2)]] for (; i < j;) { for (; j < k;) {break;continue;} })";
string expect = R"({ ;
#line 1
if (i < j) { for (; j < k;) {break;continue;} }
#line 1
;
#line 1
if (i < j) { for (; j < k;) {break;continue;} }
#line 1
;
#line 1
})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"([[gpu::unroll]] for (int i = 3; i > 2; i++) {})";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Error: Unsupported condition in unrolled loop.");
}
}
GPU_TEST(preprocess_unroll);
static void test_preprocess_template()
{
using namespace shader;
using namespace std;
{
string input = R"(
template<typename T>
void func(T a) {a;}
template void func<float>(float a);)";
string expect = R"(
#define func_TEMPLATE(T) \
void func(T a) {a;}
func_TEMPLATE(float)/*float a*/)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
template<typename T, int i>
void func(T a) {
a;
}
template void func<float, 1>(float a);)";
string expect = R"(
#define func_TEMPLATE(T, i) \
void func_##T##_##i##_(T a) { \
a; \
}
func_TEMPLATE(float, 1)/*float a*/)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(template<typename T, int i = 0> void func(T a) {a;)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Template declaration unsupported syntax");
}
{
string input = R"(template void func(float a);)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Template instantiation unsupported syntax");
}
{
string input = R"(func<float, 1>(a);)";
string expect = R"(TEMPLATE_GLUE2(func, float, 1)(a);)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_template);
GPU: Shader: Add support for references Implementation of #137341 This adds support for using references to any variable in a local scope inside the shader codebase. Example: ```cpp int a = 0; int &b = a; b++; /* a == 1 */ ``` Using `auto` is supported for reference definition as the type is not preserved by the copy paste procedure. Type checking is done by the C++ shader compilation or after the copy paste procedure during shader compilation. `auto` is still unsupported for other variable declarations. Reference to opaque types (`image`, `sampler`) are supported since they are never really assigned to a temp variable. This implements all safety feature related to the implementation being copy pasting the definition string. That is: - No `--`, `++` operators. - No function calls. - Array subscript index needs to be int constants or constant variable. The copy pasting does not replace member access: `auto &a = b; a.a = c;` becomes `b.a = c;` The copy pasting does not replace function calls: `auto &a = b; a = a();` becomes `b = a();` While limited, this already allows for nicer syntax (aliasing) for accessing SSBOs and the potential overhead of a copy semantic: ```cpp ViewMatrices matrices = drw_view_buf[0]; matrices.viewmat = float4x4(1); drw_view_buf[0] = matrices; ``` Can now be written as; ```cpp ViewMatrices &matrices = drw_view_buf[0]; matrices.viewmat = float4x4(1); ``` Which expands to; ```cpp drw_view_buf[0].viewmat = float4x4(1); ``` Note that the reference semantic is not carried through function call because arguments are transformed to `inout` in GLSL. `inout` has copy semantic but it is often implemented as reference by some implementations. Another important note is that this copy-pasting doesn't check if a symbol is a variable. It can match a typename. But given that our typenames have different capitalizations style this is unlikely to be an issue. If that issue arise, we can add a check for it. Rel #137446 Pull Request: https://projects.blender.org/blender/blender/pulls/138412
2025-05-06 13:36:59 +02:00
static void test_preprocess_reference()
{
using namespace shader;
using namespace std;
{
string input = R"(void func() { auto &a = b; a.a = 0; c = a(a); a_c_a = a; })";
string expect = R"(void func() { b.a = 0; c = a(b); a_c_a = b; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(void func() { const int &a = b; a.a = 0; c = a(a); })";
string expect = R"(void func() { b.a = 0; c = a(b); })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(void func() { const int i = 0; auto &a = b[i]; a.a = 0; })";
string expect = R"(void func() { const int i = 0; b[i].a = 0; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(void func() { auto &a = b(0); })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Reference definitions cannot contain function calls.");
}
{
string input = R"(void func() { int i = 0; auto &a = b[i++]; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Reference definitions cannot have side effects.");
}
{
string input = R"(void func() { auto &a = b[0 + 1]; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error,
"Array subscript inside reference declaration must be a single variable or a "
"constant, not an expression.");
}
{
string input = R"(void func() { auto &a = b[c]; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error,
"Cannot locate array subscript variable declaration. "
"If it is a global variable, assign it to a temporary const variable for "
"indexing inside the reference.");
}
{
string input = R"(void func() { int c = 0; auto &a = b[c]; })";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Array subscript variable must be declared as const qualified.");
}
{
string input = R"(auto &a = b;)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Reference is defined inside a global or unterminated scope.");
}
}
GPU_TEST(preprocess_reference);
static void test_preprocess_default_arguments()
{
using namespace shader;
using namespace std;
{
string input = R"(
int func(int a, int b = 0)
{
return a + b;
}
)";
string expect = R"(
int func(int a, int b)
{
return a + b;
}
#line 2
int func(int a)
{
#line 2
return func(a, 0);
}
#line 6
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
int func(int a = 0, const int b = 0)
{
return a + b;
}
)";
string expect = R"(
int func(int a, const int b)
{
return a + b;
}
#line 2
int func(int a)
{
#line 2
return func(a, 0);
}
#line 2
int func()
{
#line 2
return func(0);
}
#line 6
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
int2 func(int2 a = int2(0, 0)) {
return a;
}
)";
string expect = R"(
int2 func(int2 a) {
return a;
}
#line 2
int2 func()
{
#line 2
return func(int2(0, 0));
}
#line 6
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
void func(int a = 0) {
a;
}
)";
string expect = R"(
void func(int a) {
a;
}
#line 2
void func()
{
#line 2
func(0);
}
#line 6
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_default_arguments);
static void test_preprocess_namespace()
{
using namespace shader;
using namespace std;
{
string input = R"(
namespace A {
struct S {};
int func(int a)
{
S s;
return B::func(int a);
}
int func2(int a)
{
T s;
s.S;
s.func;
return func(int a);
}
}
)";
string expect = R"(
struct A_S {};
int A_func(int a)
{
A_S s;
return B_func(int a);
}
int A_func2(int a)
{
T s;
s.S;
s.func;
return A_func(int a);
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
namespace A::B {
int func(int a)
{
return a;
}
int func2(int a)
{
return func(int a);
}
}
)";
string expect = R"(
int A_B_func(int a)
{
return a;
}
int A_B_func2(int a)
{
return A_B_func(int a);
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
namespace A {
namespace B {
int func(int a)
{
return a;
}
}
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "Nested namespaces are unsupported.");
}
{
string input = R"(
namespace A {
int test(int a) {}
int func(int a)
{
using B::test;
return test(a);
}
}
)";
string expect = R"(
int A_test(int a) {}
int A_func(int a)
{
return B_test(a);
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
int func(int a)
{
using B = A::S;
B b;
using C = A::F;
C f = A::B();
f = B();
B d;
}
)";
string expect = R"(
int func(int a)
{
A_S b;
A_F f = A_B();
f = B();
A_S d;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
namespace A::B {
void func() {}
struct S {};
}
namespace A::B {
using A::B::func;
using S = A::B::S;
void test() {
S s;
func();
}
}
)";
string expect = R"(
void A_B_func() {}
struct A_B_S {};
void A_B_test() {
A_B_S s;
A_B_func();
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
using B = A::T;
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error, "The `using` keyword is not allowed in global scope.");
}
{
string input = R"(
namespace A {
using namespace B;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error,
"Unsupported `using namespace`. "
"Add individual `using` directives for each needed symbol.");
}
{
string input = R"(
namespace A {
using B::func;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error,
"The `using` keyword is only allowed in namespace scope to make visible symbols "
"from the same namespace declared in another scope, potentially from another "
"file.");
}
{
string input = R"(
namespace A {
using C = B::func;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(error,
"The `using` keyword is only allowed in namespace scope to make visible symbols "
"from the same namespace declared in another scope, potentially from another "
"file.");
}
{
/* Template on the same line as function signature inside a namespace.
* Template instantiation with other functions. */
string input = R"(
namespace NS {
template<typename T> T read(T a)
{
return a;
}
template float read<float>(float);
float write(float a){ return a; }
}
)";
string expect = R"(
#define NS_read_TEMPLATE(T) T NS_read(T a) \
{ \
return a; \
}
NS_read_TEMPLATE(float)/*float*/
float NS_write(float a){ return a; }
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_namespace);
static void test_preprocess_swizzle()
{
using namespace shader;
using namespace std;
{
string input = R"(a.xyzw().aaa().xxx().grba().yzww; aaaa();)";
string expect = R"(a.xyzw .aaa .xxx .grba .yzww; aaaa();)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_swizzle);
#ifdef __APPLE__ /* This processing is only done for metal compatibility. */
static void test_preprocess_matrix_constructors()
{
using namespace shader;
using namespace std;
{
string input = R"(mat3(a); mat3 a; my_mat4x4(a); mat2x2(a); mat3x2(a);)";
string expect = R"(__mat3x3(a); mat3 a; my_mat4x4(a); __mat2x2(a); mat3x2(a);)";
string error;
string output = process_test_string(input, error, nullptr, Preprocessor::SourceLanguage::GLSL);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_matrix_constructors);
#endif
static void test_preprocess_stage_attribute()
{
using namespace shader;
using namespace std;
{
string input = R"(
[[gpu::vertex_function]] void my_func() {
return;
}
)";
string expect = R"(
#if defined(GPU_VERTEX_SHADER)
#line 2
void my_func() {
return;
}
#endif
#line 5
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_stage_attribute);
static void test_preprocess_resource_guard()
{
using namespace shader;
using namespace std;
{
string input = R"(
void my_func() {
interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
}
)";
string expect = R"(
void my_func() {
#if defined(CREATE_INFO_draw_resource_id_varying)
#line 3
interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
#endif
#line 4
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
uint my_func() {
uint i = 0;
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
return i;
}
)";
string expect = R"(
uint my_func() {
#if defined(CREATE_INFO_draw_resource_id_varying)
#line 3
uint i = 0;
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
return i;
#else
#line 3
return uint(0);
#endif
#line 6
}
)";
string error;
string output = process_test_string(input, error);
// EXPECT_EQ(output, expect); /* TODO: Add support. */
EXPECT_EQ(error,
"Return statement with values are not supported inside the same scope as "
"resource access function.");
}
{
string input = R"(
uint my_func() {
uint i = 0;
{
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
}
return i;
}
)";
string expect = R"(
uint my_func() {
uint i = 0;
{
#if defined(CREATE_INFO_draw_resource_id_varying)
#line 5
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
#endif
#line 6
}
return i;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
{
string input = R"(
uint my_func() {
uint i = 0;
{
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
i += buffer_get(draw_resource_id, resource_id_buf)[0];
}
return i;
}
)";
string expect = R"(
uint my_func() {
uint i = 0;
{
#if defined(CREATE_INFO_draw_resource_id_varying)
#line 5
#if defined(CREATE_INFO_draw_resource_id)
#line 5
i += interface_get(draw_resource_id_varying, drw_ResourceID_iface).resource_index;
i += buffer_get(draw_resource_id, resource_id_buf)[0];
#endif
#line 7
#endif
#line 7
}
return i;
}
)";
string error;
string output = process_test_string(input, error);
EXPECT_EQ(output, expect);
EXPECT_EQ(error, "");
}
}
GPU_TEST(preprocess_resource_guard);
} // namespace blender::gpu::tests