Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support shader preprocessor concatenation symbol #74737

Merged
merged 1 commit into from
Aug 8, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
65 changes: 55 additions & 10 deletions servers/rendering/shader_preprocessor.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -420,11 +420,11 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}

Vector<String> args;
if (p_tokenizer->peek() == '(') {
// Macro has arguments.
p_tokenizer->get_token();

Vector<String> args;
while (true) {
String name = p_tokenizer->get_identifier();
if (name.is_empty()) {
Expand All @@ -442,17 +442,24 @@ void ShaderPreprocessor::process_define(Tokenizer *p_tokenizer) {
return;
}
}
}

Define *define = memnew(Define);
String body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
if (body.begins_with("##")) {
set_error(RTR("'##' must not appear at beginning of macro expansion."), line);
return;
}
if (body.ends_with("##")) {
set_error(RTR("'##' must not appear at end of macro expansion."), line);
return;
}

Define *define = memnew(Define);
if (!args.is_empty()) {
define->arguments = args;
define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
state->defines[label] = define;
} else {
// Simple substitution macro.
Define *define = memnew(Define);
define->body = tokens_to_string(p_tokenizer->advance('\n')).strip_edges();
state->defines[label] = define;
}
define->body = body;
state->defines[label] = define;
}

void ShaderPreprocessor::process_elif(Tokenizer *p_tokenizer) {
Expand Down Expand Up @@ -1074,9 +1081,13 @@ bool ShaderPreprocessor::expand_macros_once(const String &p_line, int p_line_num
}
}

concatenate_macro_body(body);

result = result.substr(0, index) + " " + body + " " + result.substr(args_end + 1, result.length());
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I removed the padding " " because they made unit testing much more complicated. Have not found any case where this caused problems.

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should be fine, like I mentioned in RocketChat adding those spaces might have been there "just in case".

Copy link
Contributor Author

@JohanAR JohanAR Mar 14, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found one case where it changes the behaviour:

#define MOD +
float a MOD= 1.0;

Previously this would've been expanded to invalid code, while now it works. I did some tests with a C++ compiler and it doesn't work there.

Should I revert this line and update the tests to require the surrounding spaces? I think it would be difficult to relax the test to allow them without adding a full blown glsl parser/tokenizer.

On the other hand, this is probably an edge case, and I would say that (with my change) it works like I would expect it to.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Was about to revert this padding change, but then I realised that the example above would not be affected since the padding was only done for macros with arguments.

E.g. before my PR the following defines:

#define A -10
#define B(x) -x
int n = 1-A;
int m = 1-B(3);

Would be expanded to:

int n = 1--10;
int m = 1- -3 ;

I.e. the behaviour changes depending on if you have args or not, and leading to one case where the code becomes invalid.

Kind of leaning towards adding the padding for argument-less macros too now, even if it'll make my unit tests uglier.

} else {
result = result.substr(0, index) + body + result.substr(index + key.length(), result.length() - (index + key.length()));
concatenate_macro_body(body);

result = result.substr(0, index) + " " + body + " " + result.substr(index + key.length(), result.length() - (index + key.length()));
}

r_expanded = result;
Expand Down Expand Up @@ -1115,6 +1126,40 @@ bool ShaderPreprocessor::find_match(const String &p_string, const String &p_valu
return false;
}

void ShaderPreprocessor::concatenate_macro_body(String &r_body) {
int index_start = r_body.find("##");
while (index_start > -1) {
int index_end = index_start + 2; // First character after ##.
// The macro was checked during creation so this should never happen.
ERR_FAIL_INDEX(index_end, r_body.size());

// If there more than two # in a row, then it's not a concatenation.
bool is_concat = true;
while (index_end <= r_body.length() && r_body[index_end] == '#') {
index_end++;
is_concat = false;
}
if (!is_concat) {
index_start = r_body.find("##", index_end);
continue;
}

// Skip whitespace after ##.
while (index_end < r_body.length() && is_char_space(r_body[index_end])) {
index_end++;
}

// Skip whitespace before ##.
while (index_start >= 1 && is_char_space(r_body[index_start - 1])) {
index_start--;
}

r_body = r_body.substr(0, index_start) + r_body.substr(index_end, r_body.length() - index_end);

index_start = r_body.find("##", index_start);
}
}

String ShaderPreprocessor::next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives) {
const int line = p_tokenizer->get_line();
int nesting = 0;
Expand Down
1 change: 1 addition & 0 deletions servers/rendering/shader_preprocessor.h
Original file line number Diff line number Diff line change
Expand Up @@ -205,6 +205,7 @@ class ShaderPreprocessor {
Error expand_macros(const String &p_string, int p_line, String &r_result);
bool expand_macros_once(const String &p_line, int p_line_number, const RBMap<String, Define *>::Element *p_define_pair, String &r_expanded);
bool find_match(const String &p_string, const String &p_value, int &r_index, int &r_index_start);
void concatenate_macro_body(String &r_body);

String next_directive(Tokenizer *p_tokenizer, const Vector<String> &p_directives);
void add_to_output(const String &p_str);
Expand Down
Loading