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.cpp272
1 files changed, 225 insertions, 47 deletions
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 4ae0eda232..2ce7707257 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -33,6 +33,8 @@
#include "core/string/print_string.h"
#include "servers/rendering_server.h"
+#define HAS_WARNING(flag) (warning_flags & flag)
+
static bool _is_text_char(char32_t c) {
return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '_';
}
@@ -901,6 +903,8 @@ bool ShaderLanguage::is_token_nonvoid_datatype(TokenType p_type) {
void ShaderLanguage::clear() {
current_function = StringName();
+ last_name = StringName();
+ last_type = IDENTIFIER_MAX;
completion_type = COMPLETION_NONE;
completion_block = nullptr;
@@ -908,12 +912,20 @@ void ShaderLanguage::clear() {
completion_class = SubClassTag::TAG_GLOBAL;
completion_struct = StringName();
+#ifdef DEBUG_ENABLED
+ used_constants.clear();
+ used_varyings.clear();
+ used_uniforms.clear();
+ used_functions.clear();
+ used_structs.clear();
+ warnings.clear();
+#endif // DEBUG_ENABLED
+
error_line = 0;
tk_line = 1;
char_idx = 0;
error_set = false;
error_str = "";
- last_const = false;
while (nodes) {
Node *n = nodes;
nodes = nodes->next;
@@ -921,6 +933,35 @@ void ShaderLanguage::clear() {
}
}
+#ifdef DEBUG_ENABLED
+void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, IdentifierType p_type) {
+ switch (p_type) {
+ case IdentifierType::IDENTIFIER_CONSTANT:
+ if (HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG) && used_constants.has(p_identifier)) {
+ used_constants[p_identifier].used = true;
+ }
+ break;
+ case IdentifierType::IDENTIFIER_VARYING:
+ if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) {
+ used_varyings[p_identifier].used = true;
+ }
+ break;
+ case IdentifierType::IDENTIFIER_UNIFORM:
+ if (HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG) && used_uniforms.has(p_identifier)) {
+ used_uniforms[p_identifier].used = true;
+ }
+ break;
+ case IdentifierType::IDENTIFIER_FUNCTION:
+ if (HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG) && used_functions.has(p_identifier)) {
+ used_functions[p_identifier].used = true;
+ }
+ break;
+ default:
+ break;
+ }
+}
+#endif // DEBUG_ENABLED
+
bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name, ConstantNode::Value *r_constant_value) {
if (p_function_info.built_ins.has(p_identifier)) {
if (r_data_type) {
@@ -2969,6 +3010,20 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
}
}
+bool ShaderLanguage::is_control_flow_keyword(String p_keyword) {
+ return p_keyword == "break" ||
+ p_keyword == "case" ||
+ p_keyword == "continue" ||
+ p_keyword == "default" ||
+ p_keyword == "do" ||
+ p_keyword == "else" ||
+ p_keyword == "for" ||
+ p_keyword == "if" ||
+ p_keyword == "return" ||
+ p_keyword == "switch" ||
+ p_keyword == "while";
+}
+
void ShaderLanguage::get_builtin_funcs(List<String> *r_keywords) {
Set<String> kws;
@@ -3109,20 +3164,20 @@ bool ShaderLanguage::_validate_varying_assign(ShaderNode::Varying &p_varying, St
}
switch (p_varying.stage) {
case ShaderNode::Varying::STAGE_UNKNOWN: // first assign
- if (current_function == String("vertex")) {
+ if (current_function == varying_function_names.vertex) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX;
- } else if (current_function == String("fragment")) {
+ } else if (current_function == varying_function_names.fragment) {
p_varying.stage = ShaderNode::Varying::STAGE_FRAGMENT;
}
break;
case ShaderNode::Varying::STAGE_VERTEX:
- if (current_function == String("fragment")) {
+ 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 == String("vertex")) {
+ 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;
}
@@ -3139,25 +3194,25 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str
*r_message = RTR("Varying must be assigned before using!");
return false;
case ShaderNode::Varying::STAGE_VERTEX:
- if (current_function == String("fragment")) {
+ if (current_function == varying_function_names.fragment) {
p_varying.stage = ShaderNode::Varying::STAGE_VERTEX_TO_FRAGMENT;
- } else if (current_function == String("light")) {
+ } 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 == String("light")) {
+ 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 == String("light")) {
+ 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 == String("fragment")) {
+ 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;
}
@@ -3168,6 +3223,36 @@ bool ShaderLanguage::_validate_varying_using(ShaderNode::Varying &p_varying, Str
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 = int(op_node->op == OP_CALL); 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);
@@ -3558,6 +3643,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (shader->structs.has(identifier)) {
pstruct = shader->structs[identifier].shader_struct;
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG) && used_structs.has(identifier)) {
+ used_structs[identifier].used = true;
+ }
+#endif // DEBUG_ENABLED
struct_init = true;
}
@@ -3781,11 +3871,17 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
expr = func;
+#ifdef DEBUG_ENABLED
+ if (check_warnings) {
+ _parse_used_identifier(name, IdentifierType::IDENTIFIER_FUNCTION);
+ }
+#endif // DEBUG_ENABLED
}
} else {
//an identifier
- last_const = false;
+ last_name = identifier;
+ last_type = IDENTIFIER_MAX;
_set_tkpos(pos);
DataType data_type;
@@ -3830,12 +3926,16 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
}
- last_const = is_const;
if (ident_type == IDENTIFIER_FUNCTION) {
_set_error("Can't use function as identifier: " + String(identifier));
return nullptr;
}
+ if (is_const) {
+ last_type = IDENTIFIER_CONSTANT;
+ } else {
+ last_type = ident_type;
+ }
}
Node *index_expression = nullptr;
@@ -3909,7 +4009,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
arrname->assign_expression = assign_expression;
arrname->is_const = is_const;
expr = arrname;
-
} else {
VariableNode *varname = alloc_node<VariableNode>();
varname->name = identifier;
@@ -3918,6 +4017,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
varname->struct_name = struct_name;
expr = varname;
}
+#ifdef DEBUG_ENABLED
+ if (check_warnings) {
+ _parse_used_identifier(identifier, ident_type);
+ }
+#endif // DEBUG_ENABLED
}
} else if (tk.type == TK_OP_ADD) {
continue; //this one does nothing
@@ -3956,8 +4060,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();
@@ -4248,8 +4350,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (array_size > 0) {
tk = _get_token();
if (tk.type == TK_OP_ASSIGN) {
- if (last_const) {
- last_const = false;
+ if (last_type == IDENTIFIER_CONSTANT) {
_set_error("Constants cannot be modified.");
return nullptr;
}
@@ -4259,8 +4360,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
mn->assign_expression = assign_expression;
} else if (tk.type == TK_PERIOD) {
- _set_error("Nested array length() is not yet implemented");
- return nullptr;
+ completion_class = TAG_ARRAY;
+ p_block->block_tag = SubClassTag::TAG_ARRAY;
+ Node *call_expression = _parse_and_reduce_expression(p_block, p_function_info);
+ p_block->block_tag = SubClassTag::TAG_GLOBAL;
+ if (!call_expression) {
+ return nullptr;
+ }
+ mn->datatype = call_expression->get_datatype();
+ mn->call_expression = call_expression;
} else if (tk.type == TK_BRACKET_OPEN) {
Node *index_expression = _parse_and_reduce_expression(p_block, p_function_info);
if (!index_expression) {
@@ -4599,9 +4707,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
bool unary = false;
bool ternary = false;
+ Operator op = expression[i].op;
int priority;
- switch (expression[i].op) {
+ switch (op) {
case OP_EQUAL:
priority = 8;
break;
@@ -4722,6 +4831,12 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ERR_FAIL_V(nullptr); //unexpected operator
}
+#if DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::FLOAT_COMPARISON_FLAG) && (op == OP_EQUAL || op == OP_NOT_EQUAL) && expression[i - 1].node->get_datatype() == TYPE_FLOAT && expression[i + 1].node->get_datatype() == TYPE_FLOAT) {
+ _add_line_warning(ShaderWarning::FLOAT_COMPARISON);
+ }
+#endif // DEBUG_ENABLED
+
if (priority < min_priority) {
// < is used for left to right (default)
// <= is used for right to left
@@ -4735,7 +4850,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) {
@@ -5387,8 +5501,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;
@@ -5430,7 +5549,6 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
p_block->statements.push_back(vardecl);
p_block->variables[name] = var;
-
if (tk.type == TK_COMMA) {
if (p_block->block_type == BlockNode::BLOCK_TYPE_FOR) {
_set_error("Multiple declarations in 'for' loop are not implemented yet.");
@@ -5847,7 +5965,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;
}
@@ -6260,7 +6378,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
shader->structs[st.name] = st;
shader->vstructs.push_back(st); // struct's order is important!
-
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_STRUCT_FLAG)) {
+ used_structs.insert(st.name, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
} break;
case TK_GLOBAL: {
tk = _get_token();
@@ -6607,6 +6729,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
shader->uniforms[name] = uniform2;
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_UNIFORM_FLAG)) {
+ used_uniforms.insert(name, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
+
//reset scope for next uniform
uniform_scope = ShaderNode::Uniform::SCOPE_LOCAL;
@@ -6650,6 +6778,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
shader->varyings[name] = varying;
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG)) {
+ used_varyings.insert(name, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
}
} break;
@@ -6967,8 +7100,13 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
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);
@@ -6991,6 +7129,11 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
shader->constants[name] = constant;
shader->vconstants.push_back(constant);
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_CONSTANT_FLAG)) {
+ used_constants.insert(name, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
if (tk.type == TK_COMMA) {
tk = _get_token();
@@ -7052,6 +7195,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
if (p_functions.has(name)) {
func_node->can_discard = p_functions[name].can_discard;
+ } else {
+#ifdef DEBUG_ENABLED
+ if (check_warnings && HAS_WARNING(ShaderWarning::UNUSED_FUNCTION_FLAG)) {
+ used_functions.insert(name, Usage(tk_line));
+ }
+#endif // DEBUG_ENABLED
}
func_node->body = alloc_node<BlockNode>();
@@ -7246,26 +7395,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
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;
}
@@ -7399,27 +7534,62 @@ 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) {
+#ifdef DEBUG_ENABLED
+void ShaderLanguage::_check_warning_accums() {
+ for (Map<ShaderWarning::Code, Map<StringName, Usage> *>::Element *E = warnings_check_map.front(); E; E = E->next()) {
+ for (const Map<StringName, Usage>::Element *U = (*E->get()).front(); U; U = U->next()) {
+ if (!U->get().used) {
+ _add_warning(E->key(), U->get().decl_line, U->key());
+ }
+ }
+ }
+}
+List<ShaderWarning>::Element *ShaderLanguage::get_warnings_ptr() {
+ return warnings.front();
+}
+void ShaderLanguage::enable_warning_checking(bool p_enabled) {
+ check_warnings = p_enabled;
+}
+bool ShaderLanguage::is_warning_checking_enabled() const {
+ return check_warnings;
+}
+void ShaderLanguage::set_warning_flags(uint32_t p_flags) {
+ warning_flags = p_flags;
+}
+uint32_t ShaderLanguage::get_warning_flags() const {
+ return warning_flags;
+}
+#endif // DEBUG_ENABLED
+
+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;
shader = alloc_node<ShaderNode>();
Error err = _parse_shader(p_functions, p_render_modes, p_shader_types);
+#ifdef DEBUG_ENABLED
+ if (check_warnings) {
+ _check_warning_accums();
+ }
+#endif // DEBUG_ENABLED
+
if (err != OK) {
return err;
}
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;
@@ -7818,6 +7988,14 @@ ShaderLanguage::ShaderNode *ShaderLanguage::get_shader() {
ShaderLanguage::ShaderLanguage() {
nodes = nullptr;
completion_class = TAG_GLOBAL;
+
+#if DEBUG_ENABLED
+ warnings_check_map.insert(ShaderWarning::UNUSED_CONSTANT, &used_constants);
+ warnings_check_map.insert(ShaderWarning::UNUSED_FUNCTION, &used_functions);
+ warnings_check_map.insert(ShaderWarning::UNUSED_STRUCT, &used_structs);
+ warnings_check_map.insert(ShaderWarning::UNUSED_UNIFORM, &used_uniforms);
+ warnings_check_map.insert(ShaderWarning::UNUSED_VARYING, &used_varyings);
+#endif // DEBUG_ENABLED
}
ShaderLanguage::~ShaderLanguage() {