summaryrefslogtreecommitdiff
path: root/servers/rendering/shader_language.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'servers/rendering/shader_language.cpp')
-rw-r--r--servers/rendering/shader_language.cpp239
1 files changed, 174 insertions, 65 deletions
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index e6415c0258..0d6d3f5e13 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -558,13 +558,13 @@ ShaderLanguage::Token ShaderLanguage::_get_token() {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
hexa_found = true;
- } else if (GETCHAR(i) == 'e') {
- if (hexa_found || exponent_found || float_suffix_found) {
+ } else if (GETCHAR(i) == 'e' && !hexa_found) {
+ if (exponent_found || float_suffix_found) {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
exponent_found = true;
- } else if (GETCHAR(i) == 'f') {
- if (hexa_found || exponent_found) {
+ } else if (GETCHAR(i) == 'f' && !hexa_found) {
+ if (exponent_found) {
return _make_token(TK_ERROR, "Invalid numeric constant");
}
float_suffix_found = true;
@@ -3102,6 +3102,102 @@ bool ShaderLanguage::_is_operator_assign(Operator p_op) const {
return false;
}
+bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message) {
+ if (current_function == String("light")) {
+ *r_message = RTR("Varying may not be assigned in the 'light' function.");
+ return false;
+ }
+ switch (p_varying.stage) {
+ case ShaderNode::Varying::STAGE_UNKNOWN: // first assign
+ if (current_function == varying_function_names.vertex) {
+ p_varying.stage = ShaderNode::Varying::STAGE_VERTEX;
+ } else if (current_function == varying_function_names.fragment) {
+ p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_VERTEX:
+ if (current_function == varying_function_names.fragment) {
+ *r_message = RTR("Varyings which assigned in 'vertex' function may not be reassigned in 'fragment' or 'light'.");
+ return false;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_FRAGMENT:
+ if (current_function == varying_function_names.vertex) {
+ *r_message = RTR("Varyings which assigned in 'fragment' function may not be reassigned in 'vertex' or 'light'.");
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, String *r_message) {
+ switch (p_varying.stage) {
+ case ShaderNode::Varying::STAGE_UNKNOWN:
+ *r_message = RTR("Varying must be assigned before using!");
+ return false;
+ case ShaderNode::Varying::STAGE_VERTEX:
+ if (current_function == varying_function_names.fragment) {
+ p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT;
+ } else if (current_function == varying_function_names.light) {
+ p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_FRAGMENT:
+ if (current_function == varying_function_names.light) {
+ p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT_TO_LIGHT;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT:
+ if (current_function == varying_function_names.light) {
+ *r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");
+ return false;
+ }
+ break;
+ case ShaderNode::Varying::STAGE_VERTEX_TO_LIGHT:
+ if (current_function == varying_function_names.fragment) {
+ *r_message = RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'");
+ return false;
+ }
+ break;
+ default:
+ break;
+ }
+ return true;
+}
+
+bool ShaderLanguage::_check_node_constness(const Node *p_node) const {
+ switch (p_node->type) {
+ case Node::TYPE_OPERATOR: {
+ OperatorNode *op_node = (OperatorNode *)p_node;
+ for (int i = (1 ? op_node->op == OP_CALL : 0); i < op_node->arguments.size(); i++) {
+ if (!_check_node_constness(op_node->arguments[i])) {
+ return false;
+ }
+ }
+ } break;
+ case Node::TYPE_CONSTANT:
+ break;
+ case Node::TYPE_VARIABLE: {
+ VariableNode *varn = (VariableNode *)p_node;
+ if (!varn->is_const) {
+ return false;
+ }
+ } break;
+ case Node::TYPE_ARRAY: {
+ ArrayNode *arrn = (ArrayNode *)p_node;
+ if (!arrn->is_const) {
+ return false;
+ }
+ } break;
+ default:
+ return false;
+ }
+ return true;
+}
+
bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message) {
if (p_node->type == Node::TYPE_OPERATOR) {
OperatorNode *op = static_cast<OperatorNode *>(p_node);
@@ -3142,13 +3238,6 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
return false;
}
- if (shader->varyings.has(var->name) && current_function != String("vertex")) {
- if (r_message) {
- *r_message = RTR("Varyings can only be assigned in vertex function.");
- }
- return false;
- }
-
if (shader->constants.has(var->name) || var->is_const) {
if (r_message) {
*r_message = RTR("Constants cannot be modified.");
@@ -3169,13 +3258,6 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi
return false;
}
- if (shader->varyings.has(arr->name) && current_function != String("vertex")) {
- if (r_message) {
- *r_message = RTR("Varyings can only be assigned in vertex function.");
- }
- return false;
- }
-
return true;
}
@@ -3761,6 +3843,23 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Unknown identifier in expression: " + String(identifier));
return nullptr;
}
+ if (ident_type == IDENTIFIER_VARYING) {
+ TkPos prev_pos = _get_tkpos();
+ Token next_token = _get_token();
+ _set_tkpos(prev_pos);
+ String error;
+ if (next_token.type == TK_OP_ASSIGN) {
+ if (!_validate_varying_assign(shader->varyings[identifier], &error)) {
+ _set_error(error);
+ return nullptr;
+ }
+ } else {
+ if (!_validate_varying_using(shader->varyings[identifier], &error)) {
+ _set_error(error);
+ return nullptr;
+ }
+ }
+ }
last_const = is_const;
if (ident_type == IDENTIFIER_FUNCTION) {
@@ -3786,10 +3885,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
_set_error("Constants cannot be modified.");
return nullptr;
}
- if (shader->varyings.has(identifier) && current_function != String("vertex")) {
- _set_error("Varyings can only be assigned in vertex function.");
- return nullptr;
- }
assign_expression = _parse_array_constructor(p_block, p_function_info, data_type, struct_name, array_size);
if (!assign_expression) {
return nullptr;
@@ -3891,8 +3986,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ERR_FAIL_COND_V(!expr, nullptr);
/* OK now see what's NEXT to the operator.. */
- /* OK now see what's NEXT to the operator.. */
- /* OK now see what's NEXT to the operator.. */
while (true) {
TkPos pos2 = _get_tkpos();
@@ -4670,7 +4763,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ERR_FAIL_COND_V(next_op == -1, nullptr);
// OK! create operator..
- // OK! create operator..
if (is_unary) {
int expr_pos = next_op;
while (expression[expr_pos].is_op) {
@@ -5322,8 +5414,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
if (node->is_const && n->type == Node::TYPE_OPERATOR && ((OperatorNode *)n)->op == OP_CALL) {
- _set_error("Expected constant expression after '='");
- return ERR_PARSE_ERROR;
+ OperatorNode *op = ((OperatorNode *)n);
+ for (int i = 1; i < op->arguments.size(); i++) {
+ if (!_check_node_constness(op->arguments[i])) {
+ _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='");
+ return ERR_PARSE_ERROR;
+ }
+ }
}
decl.initializer = n;
@@ -5782,7 +5879,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
//check return type
BlockNode *b = p_block;
- if (b && b->parent_function && (b->parent_function->name == "vertex" || b->parent_function->name == "fragment" || b->parent_function->name == "light")) {
+ if (b && b->parent_function && p_function_info.main_function) {
_set_error(vformat("Using 'return' in '%s' processor function results in undefined behavior!", b->parent_function->name));
return ERR_PARSE_ERROR;
}
@@ -5796,6 +5893,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_BUG;
}
+ String return_struct_name = String(b->parent_function->return_struct_name);
+
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
flow->flow_op = FLOW_OP_RETURN;
@@ -5804,7 +5903,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type == TK_SEMICOLON) {
//all is good
if (b->parent_function->return_type != TYPE_VOID) {
- _set_error("Expected return with expression of type '" + get_datatype_name(b->parent_function->return_type) + "'");
+ _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'");
return ERR_PARSE_ERROR;
}
} else {
@@ -5814,8 +5913,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
return ERR_PARSE_ERROR;
}
- if (b->parent_function->return_type != expr->get_datatype()) {
- _set_error("Expected return expression of type '" + get_datatype_name(b->parent_function->return_type) + "'");
+ if (b->parent_function->return_type != expr->get_datatype() || return_struct_name != expr->get_datatype_name()) {
+ _set_error("Expected return with an expression of type '" + (return_struct_name != "" ? return_struct_name : get_datatype_name(b->parent_function->return_type)) + "'");
return ERR_PARSE_ERROR;
}
@@ -5859,15 +5958,15 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- //all is good
_set_error("Expected ';' after discard");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
} else if (tk.type == TK_CF_BREAK) {
if (!p_can_break) {
- //all is good
- _set_error("Breaking is not allowed here");
+ _set_error("'break' is not allowed outside of a loop or 'switch' statement");
+ return ERR_PARSE_ERROR;
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
@@ -5876,8 +5975,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
pos = _get_tkpos();
tk = _get_token();
if (tk.type != TK_SEMICOLON) {
- //all is good
_set_error("Expected ';' after break");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
@@ -5892,8 +5991,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
} else if (tk.type == TK_CF_CONTINUE) {
if (!p_can_continue) {
- //all is good
- _set_error("Continuing is not allowed here");
+ _set_error("'continue' is not allowed outside of a loop");
+ return ERR_PARSE_ERROR;
}
ControlFlowNode *flow = alloc_node<ControlFlowNode>();
@@ -5904,6 +6003,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (tk.type != TK_SEMICOLON) {
//all is good
_set_error("Expected ';' after continue");
+ return ERR_PARSE_ERROR;
}
p_block->statements.push_back(flow);
@@ -6068,6 +6168,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
if (tk.type == TK_IDENTIFIER) {
st.name = tk.text;
+ if (shader->structs.has(st.name)) {
+ _set_error("Redefinition of '" + String(st.name) + "'");
+ return ERR_PARSE_ERROR;
+ }
tk = _get_token();
if (tk.type != TK_CURLY_BRACKET_OPEN) {
_set_error("Expected '{'");
@@ -6260,6 +6364,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
return ERR_PARSE_ERROR;
}
+ TkPos name_pos = _get_tkpos();
name = tk.text;
if (_find_identifier(nullptr, false, FunctionInfo(), name)) {
@@ -6541,11 +6646,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
_set_error("Expected ';'");
return ERR_PARSE_ERROR;
}
- } else {
+ } else { // varying
ShaderNode::Varying varying;
varying.type = type;
varying.precision = precision;
varying.interpolation = interpolation;
+ varying.tkpos = name_pos;
tk = _get_token();
if (tk.type != TK_SEMICOLON && tk.type != TK_BRACKET_OPEN) {
@@ -6751,7 +6857,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
} else {
_set_tkpos(pos2);
- Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
+ Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());
if (!n || n->type != Node::TYPE_CONSTANT || n->get_datatype() != TYPE_INT) {
_set_error("Expected single integer constant > 0");
return ERR_PARSE_ERROR;
@@ -6832,7 +6938,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (tk.type == TK_PARENTHESIS_OPEN || curly) { // initialization
while (true) {
- Node *n = _parse_and_reduce_expression(NULL, FunctionInfo());
+ Node *n = _parse_and_reduce_expression(nullptr, FunctionInfo());
if (!n) {
return ERR_PARSE_ERROR;
}
@@ -6858,10 +6964,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
decl.initializer.push_back(n);
break;
} else {
- if (curly)
+ if (curly) {
_set_error("Expected '}' or ','");
- else
+ } else {
_set_error("Expected ')' or ','");
+ }
return ERR_PARSE_ERROR;
}
}
@@ -6887,12 +6994,18 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
} else {
//variable created with assignment! must parse an expression
- Node *expr = _parse_and_reduce_expression(NULL, FunctionInfo());
- if (!expr)
+ Node *expr = _parse_and_reduce_expression(nullptr, FunctionInfo());
+ if (!expr) {
return ERR_PARSE_ERROR;
+ }
if (expr->type == Node::TYPE_OPERATOR && ((OperatorNode *)expr)->op == OP_CALL) {
- _set_error("Expected constant expression after '='");
- return ERR_PARSE_ERROR;
+ OperatorNode *op = ((OperatorNode *)expr);
+ for (int i = 1; i < op->arguments.size(); i++) {
+ if (!_check_node_constness(op->arguments[i])) {
+ _set_error("Expected constant expression for argument '" + itos(i - 1) + "' of function call after '='");
+ return ERR_PARSE_ERROR;
+ }
+ }
}
constant.initializer = static_cast<ConstantNode *>(expr);
@@ -7158,30 +7271,24 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}
+ for (Map<StringName, ShaderNode::Varying>::Element *E = shader->varyings.front(); E; E = E->next()) {
+ if (E->get().stage == ShaderNode::Varying::STAGE_VERTEX || E->get().stage == ShaderNode::Varying::STAGE_FRAGMENT) {
+ _set_tkpos(E->get().tkpos);
+ _set_error(RTR("Varying must only be used in two different stages, which can be 'vertex' 'fragment' and 'light'"));
+ return ERR_PARSE_ERROR;
+ }
+ }
+
return OK;
}
bool ShaderLanguage::has_builtin(const Map<StringName, ShaderLanguage::FunctionInfo> &p_functions, const StringName &p_name) {
- if (p_functions.has("vertex")) {
- if (p_functions["vertex"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("fragment")) {
- if (p_functions["fragment"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("light")) {
- if (p_functions["light"].built_ins.has(p_name)) {
- return true;
- }
- }
- if (p_functions.has("compute")) {
- if (p_functions["compute"].built_ins.has(p_name)) {
+ for (Map<StringName, ShaderLanguage::FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) {
+ if (E->get().built_ins.has(p_name)) {
return true;
}
}
+
return false;
}
@@ -7315,11 +7422,12 @@ String ShaderLanguage::get_shader_type(const String &p_code) {
return String();
}
-Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {
+Error ShaderLanguage::compile(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func) {
clear();
code = p_code;
global_var_get_type_func = p_global_variable_type_func;
+ varying_function_names = p_varying_function_names;
nodes = nullptr;
@@ -7332,10 +7440,11 @@ Error ShaderLanguage::compile(const String &p_code, const Map<StringName, Functi
return OK;
}
-Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) {
+Error ShaderLanguage::complete(const String &p_code, const Map<StringName, FunctionInfo> &p_functions, const Vector<StringName> &p_render_modes, const VaryingFunctionNames &p_varying_function_names, const Set<String> &p_shader_types, GlobalVariableGetTypeFunc p_global_variable_type_func, List<ScriptCodeCompletionOption> *r_options, String &r_call_hint) {
clear();
code = p_code;
+ varying_function_names = p_varying_function_names;
nodes = nullptr;
global_var_get_type_func = p_global_variable_type_func;