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

[3.x] Allow pass varyings as out param to the function, when it's possible #56478

Merged
merged 1 commit into from
Jan 4, 2022
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
168 changes: 95 additions & 73 deletions servers/visual/shader_language.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,8 +883,6 @@ void ShaderLanguage::clear() {
completion_class = SubClassTag::TAG_GLOBAL;
completion_struct = StringName();

unknown_varying_usages.clear();

error_line = 0;
tk_line = 1;
char_idx = 0;
Expand Down Expand Up @@ -2839,43 +2837,6 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St
return true;
}

bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, String *r_message) {
switch (p_varying.stage) {
case ShaderNode::Varying::STAGE_UNKNOWN:
VaryingUsage usage;
usage.var = &p_varying;
usage.line = tk_line;
unknown_varying_usages.push_back(usage);
break;
case ShaderNode::Varying::STAGE_VERTEX:
if (current_function == String("fragment") || current_function == String("light")) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT;
}
break;
case ShaderNode::Varying::STAGE_FRAGMENT:
if (current_function == String("light")) {
p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
}
break;
default:
break;
}
return true;
}

bool ShaderLanguage::_check_varying_usages(int *r_error_line, String *r_error_message) const {
for (const List<ShaderLanguage::VaryingUsage>::Element *E = unknown_varying_usages.front(); E; E = E->next()) {
ShaderNode::Varying::Stage stage = E->get().var->stage;
if (stage != ShaderNode::Varying::STAGE_UNKNOWN && stage != ShaderNode::Varying::STAGE_VERTEX && stage != ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT) {
*r_error_line = E->get().line;
*r_error_message = RTR("Fragment-stage varying could not been accessed in custom function!");
return false;
}
}

return true;
}

bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message) {
if (p_node->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
Expand Down Expand Up @@ -3443,44 +3404,100 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
for (int i = 0; i < call_function->arguments.size(); i++) {
int argidx = i + 1;
if (argidx < func->arguments.size()) {
if (call_function->arguments[i].is_const || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_OUT || call_function->arguments[i].qualifier == ArgumentQualifier::ARGUMENT_QUALIFIER_INOUT) {
bool error = false;
Node *n = func->arguments[argidx];
bool error = false;
Node *n = func->arguments[argidx];
ArgumentQualifier arg_qual = call_function->arguments[i].qualifier;
bool is_out_arg = arg_qual != ArgumentQualifier::ARGUMENT_QUALIFIER_IN;

if (n->type == Node::TYPE_VARIABLE || n->type == Node::TYPE_ARRAY) {
StringName varname;

if (n->type == Node::TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
varname = vn->name;
} else { // TYPE_ARRAY
ArrayNode *an = static_cast<ArrayNode *>(n);
varname = an->name;
}

if (shader->varyings.has(varname)) {
switch (shader->varyings[varname].stage) {
case ShaderNode::Varying::STAGE_UNKNOWN: {
_set_error(vformat("Varying '%s' must be assigned in the vertex or fragment function first!", varname));
return nullptr;
}
case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT:
FALLTHROUGH;
case ShaderNode::Varying::STAGE_VERTEX:
if (is_out_arg && current_function != varying_function_names.vertex) { // inout/out
error = true;
}
break;
case ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT:
FALLTHROUGH;
case ShaderNode::Varying::STAGE_FRAGMENT:
if (!is_out_arg) {
if (current_function != varying_function_names.fragment && current_function != varying_function_names.light) {
error = true;
}
} else if (current_function != varying_function_names.fragment) { // inout/out
error = true;
}
break;
default:
break;
}

if (error) {
_set_error(vformat("Varying '%s' cannot be passed for the '%s' parameter in that context!", varname, _get_qualifier_str(arg_qual)));
return nullptr;
}
}
}

bool is_const_arg = call_function->arguments[i].is_const;

if (is_const_arg || is_out_arg) {
StringName varname;

if (n->type == Node::TYPE_CONSTANT || n->type == Node::TYPE_OPERATOR) {
if (!call_function->arguments[i].is_const) {
if (!is_const_arg) {
error = true;
}
} else if (n->type == Node::TYPE_ARRAY) {
ArrayNode *an = static_cast<ArrayNode *>(n);
if (an->call_expression != nullptr || an->is_const) {
if (!is_const_arg && (an->call_expression != nullptr || an->is_const)) {
error = true;
}
varname = an->name;
} else if (n->type == Node::TYPE_VARIABLE) {
VariableNode *vn = static_cast<VariableNode *>(n);
if (vn->is_const) {
if (vn->is_const && !is_const_arg) {
error = true;
} else {
StringName varname = vn->name;
if (shader->constants.has(varname)) {
error = true;
} else if (shader->uniforms.has(varname)) {
}
varname = vn->name;
} else if (n->type == Node::TYPE_MEMBER) {
MemberNode *mn = static_cast<MemberNode *>(n);
if (mn->basetype_const && is_out_arg) {
error = true;
}
}

if (!error && varname != StringName()) {
if (shader->constants.has(varname)) {
error = true;
} else if (shader->uniforms.has(varname)) {
error = true;
} else if (p_builtin_types.has(varname)) {
BuiltInInfo info = p_builtin_types[varname];
if (info.constant) {
error = true;
} else {
if (shader->varyings.has(varname)) {
_set_error(vformat("Varyings cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier)));
return nullptr;
}
if (p_builtin_types.has(varname)) {
BuiltInInfo info = p_builtin_types[varname];
if (info.constant) {
error = true;
}
}
}
}
}

if (error) {
_set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(call_function->arguments[i].qualifier)));
_set_error(vformat("Constant value cannot be passed for '%s' parameter!", _get_qualifier_str(arg_qual)));
return nullptr;
}
}
Expand Down Expand Up @@ -3546,9 +3563,21 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
return nullptr;
}
} else {
if (!_validate_varying_using(shader->varyings[identifier], &error)) {
_set_error(error);
return nullptr;
ShaderNode::Varying &var = shader->varyings[identifier];

switch (var.stage) {
case ShaderNode::Varying::STAGE_VERTEX:
if (current_function == varying_function_names.fragment || current_function == varying_function_names.light) {
var.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT_LIGHT;
}
break;
case ShaderNode::Varying::STAGE_FRAGMENT:
if (current_function == varying_function_names.light) {
var.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
}
break;
default:
break;
}
}
}
Expand Down Expand Up @@ -3854,6 +3883,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons

MemberNode *mn = alloc_node<MemberNode>();
mn->basetype = dt;
mn->basetype_const = last_const;
mn->datatype = member_type;
mn->base_struct_name = st;
mn->struct_name = member_struct_name;
Expand Down Expand Up @@ -6652,14 +6682,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}

int error_line;
String error_message;
if (!_check_varying_usages(&error_line, &error_message)) {
_set_tkpos({ 0, error_line });
_set_error(error_message);
return ERR_PARSE_ERROR;
}

return OK;
}

Expand Down
22 changes: 14 additions & 8 deletions servers/visual/shader_language.h
Original file line number Diff line number Diff line change
Expand Up @@ -294,6 +294,17 @@ class ShaderLanguage {
TAG_ARRAY,
};

struct VaryingFunctionNames {
StringName fragment;
StringName vertex;
StringName light;
VaryingFunctionNames() {
fragment = "fragment";
vertex = "vertex";
light = "light";
}
};

struct Node {
Node *next;

Expand Down Expand Up @@ -516,6 +527,7 @@ class ShaderLanguage {

struct MemberNode : public Node {
DataType basetype;
bool basetype_const;
StringName base_struct_name;
DataPrecision precision;
DataType datatype;
Expand All @@ -533,6 +545,7 @@ class ShaderLanguage {
MemberNode() :
Node(TYPE_MEMBER),
basetype(TYPE_VOID),
basetype_const(false),
datatype(TYPE_VOID),
array_size(0),
owner(nullptr),
Expand Down Expand Up @@ -763,13 +776,7 @@ class ShaderLanguage {
StringName current_function;
bool last_const = false;

struct VaryingUsage {
ShaderNode::Varying *var;
int line;
};
List<VaryingUsage> unknown_varying_usages;

bool _check_varying_usages(int *r_error_line, String *r_error_message) const;
VaryingFunctionNames varying_function_names;

TkPos _get_tkpos() {
TkPos tkp;
Expand Down Expand Up @@ -848,7 +855,6 @@ class ShaderLanguage {
bool _validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str);
bool _parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg = nullptr);
bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message);
bool _validate_varying_using(ShaderNode::Varying &p_varying, String *r_message);

Node *_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types);
Node *_parse_array_constructor(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, DataType p_type, const StringName &p_struct_name, int p_array_size);
Expand Down