From 89d4c01c4f8dc0b36a5d8bb165cd2733630c3177 Mon Sep 17 00:00:00 2001 From: Chaosus Date: Sat, 1 Feb 2025 17:01:44 +0300 Subject: [PATCH] [Shaders] Allow constants and expressions in `hint_range` --- servers/rendering/shader_language.cpp | 107 ++++++++++++++------------ servers/rendering/shader_language.h | 13 ++-- servers/rendering/shader_types.cpp | 19 ++++- 3 files changed, 82 insertions(+), 57 deletions(-) diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index 59adb53b786..e9c78f336ed 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -1394,6 +1394,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea if (r_is_const) { *r_is_const = p_function_info.built_ins[p_identifier].constant; } + if (r_constant_values) { + *r_constant_values = p_function_info.built_ins[p_identifier].values; + } if (r_type) { *r_type = IDENTIFIER_BUILTIN_VAR; } @@ -1546,7 +1549,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea return false; } -bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { +bool ShaderLanguage::_validate_operator(const BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) { bool valid = false; DataType ret_type = TYPE_VOID; int ret_size = 0; @@ -2012,18 +2015,18 @@ bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode * if (valid && (!p_block || p_block->use_op_eval)) { // Need to be placed here and not in the `_reduce_expression` because otherwise expressions like `1 + 2 / 2` will not work correctly. - valid = _eval_operator(p_block, p_op); + valid = _eval_operator(p_block, p_function_info, p_op); } return valid; } -Vector ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) { +Vector ShaderLanguage::_get_node_values(const BlockNode *p_block, const FunctionInfo &p_function_info, Node *p_node) { Vector result; switch (p_node->type) { case Node::NODE_TYPE_VARIABLE: { - _find_identifier(p_block, false, FunctionInfo(), static_cast(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result); + _find_identifier(p_block, false, p_function_info, static_cast(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result); } break; default: { result = p_node->get_values(); @@ -2033,7 +2036,7 @@ Vector ShaderLanguage::_get_node_values(const BlockNode return result; } -bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) { +bool ShaderLanguage::_eval_operator(const BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_op) { bool is_valid = true; switch (p_op->op) { @@ -2075,8 +2078,8 @@ bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op } } - Vector va = _get_node_values(p_block, p_op->arguments[0]); - Vector vb = _get_node_values(p_block, p_op->arguments[1]); + Vector va = _get_node_values(p_block, p_function_info, p_op->arguments[0]); + Vector vb = _get_node_values(p_block, p_function_info, p_op->arguments[1]); if (is_op_vec_transform) { p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype()); @@ -2087,7 +2090,7 @@ bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op case OP_NOT: case OP_NEGATE: case OP_BIT_INVERT: { - p_op->values = _eval_unary_vector(_get_node_values(p_block, p_op->arguments[0]), p_op->get_datatype(), p_op->op); + p_op->values = _eval_unary_vector(_get_node_values(p_block, p_function_info, p_op->arguments[0]), p_op->get_datatype(), p_op->op); } break; default: { } break; @@ -3656,7 +3659,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI int max = builtin_func_const_args[constarg_idx].max; bool error = false; - Vector values = _get_node_values(p_block, p_func->arguments[arg]); + Vector values = _get_node_values(p_block, p_function_info, p_func->arguments[arg]); if (p_func->arguments[arg]->get_datatype() == TYPE_INT && !values.is_empty()) { if (values[0].sint < min || values[0].sint > max) { error = true; @@ -5653,7 +5656,7 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo & Node *expr = _parse_and_reduce_expression(p_block, p_function_info); if (expr) { - Vector values = _get_node_values(p_block, expr); + Vector values = _get_node_values(p_block, p_function_info, expr); if (!values.is_empty()) { switch (expr->get_datatype()) { @@ -7271,7 +7274,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons op->op = tk.type == TK_OP_DECREMENT ? OP_POST_DECREMENT : OP_POST_INCREMENT; op->arguments.push_back(expr); - if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, p_function_info, op, &op->return_cache, &op->return_array_size)) { _set_error(RTR("Invalid base type for increment/decrement operator.")); return nullptr; } @@ -7623,7 +7626,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[i].is_op = false; expression.write[i].node = op; - if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, p_function_info, op, &op->return_cache, &op->return_array_size)) { if (error_set) { return nullptr; } @@ -7665,7 +7668,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons expression.write[next_op - 1].is_op = false; expression.write[next_op - 1].node = op; - if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, p_function_info, op, &op->return_cache, &op->return_array_size)) { if (error_set) { return nullptr; } @@ -7732,7 +7735,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons //replace all 3 nodes by this operator and make it an expression - if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) { + if (!_validate_operator(p_block, p_function_info, op, &op->return_cache, &op->return_array_size)) { if (error_set) { return nullptr; } @@ -9065,6 +9068,40 @@ Error ShaderLanguage::_validate_precision(DataType p_type, DataPrecision p_preci return OK; } +bool ShaderLanguage::_parse_numeric_constant_expression(const FunctionInfo &p_function_info, float &r_constant) { + ShaderLanguage::Node *expr = _parse_and_reduce_expression(nullptr, p_function_info); + if (expr == nullptr) { + return false; + } + + Vector values; + if (expr->type == Node::NODE_TYPE_VARIABLE) { + _find_identifier(nullptr, false, p_function_info, static_cast(expr)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &values); + } else { + values = expr->get_values(); + } + + if (values.is_empty()) { + return false; // To prevent possible crash. + } + + switch (expr->get_datatype()) { + case TYPE_FLOAT: + r_constant = values[0].real; + break; + case TYPE_INT: + r_constant = static_cast(values[0].sint); + break; + case TYPE_UINT: + r_constant = static_cast(values[0].uint); + break; + default: + return false; + } + + return true; +} + Error ShaderLanguage::_parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types) { Token tk; TkPos prev_pos; @@ -9797,58 +9834,30 @@ Error ShaderLanguage::_parse_shader(const HashMap &p_f return ERR_PARSE_ERROR; } - tk = _get_token(); - - float sign = 1.0; - - if (tk.type == TK_OP_SUB) { - sign = -1.0; - tk = _get_token(); - } - - if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant.")); + if (!_parse_numeric_constant_expression(constants, uniform.hint_range[0])) { + _set_error(RTR("Expected a valid numeric expression.")); return ERR_PARSE_ERROR; } - uniform.hint_range[0] = tk.constant; - uniform.hint_range[0] *= sign; - tk = _get_token(); if (tk.type != TK_COMMA) { - _set_error(RTR("Expected ',' after integer constant.")); + _set_expected_error(","); return ERR_PARSE_ERROR; } - tk = _get_token(); - - sign = 1.0; - - if (tk.type == TK_OP_SUB) { - sign = -1.0; - tk = _get_token(); - } - - if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant after ','.")); + if (!_parse_numeric_constant_expression(constants, uniform.hint_range[1])) { + _set_error(RTR("Expected a valid numeric expression after ','.")); return ERR_PARSE_ERROR; } - uniform.hint_range[1] = tk.constant; - uniform.hint_range[1] *= sign; - tk = _get_token(); if (tk.type == TK_COMMA) { - tk = _get_token(); - - if (tk.type != TK_FLOAT_CONSTANT && !tk.is_integer_constant()) { - _set_error(RTR("Expected an integer constant after ','.")); + if (!_parse_numeric_constant_expression(constants, uniform.hint_range[2])) { + _set_error(RTR("Expected a valid numeric expression after ','.")); return ERR_PARSE_ERROR; } - - uniform.hint_range[2] = tk.constant; tk = _get_token(); } else { if (type == TYPE_INT) { diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index a774f8674a5..b06028dd7c1 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -860,12 +860,14 @@ public: struct BuiltInInfo { DataType type = TYPE_VOID; bool constant = false; + Vector values; BuiltInInfo() {} - BuiltInInfo(DataType p_type, bool p_constant = false) : + BuiltInInfo(DataType p_type, bool p_constant = false, const Vector &p_values = {}) : type(p_type), - constant(p_constant) {} + constant(p_constant), + values(p_values) {} }; struct StageFunctionInfo { @@ -1117,10 +1119,10 @@ private: #endif // DEBUG_ENABLED bool _is_operator_assign(Operator p_op) const; bool _validate_assign(Node *p_node, const FunctionInfo &p_function_info, String *r_message = nullptr); - bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); + bool _validate_operator(const BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr); - Vector _get_node_values(const BlockNode *p_block, Node *p_node); - bool _eval_operator(const BlockNode *p_block, OperatorNode *p_op); + Vector _get_node_values(const BlockNode *p_block, const FunctionInfo &p_function_info, Node *p_node); + bool _eval_operator(const BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_op); Scalar _eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type); Scalar _eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid); Vector _eval_unary_vector(const Vector &p_va, DataType p_ret_type, Operator p_op); @@ -1210,6 +1212,7 @@ private: String _get_shader_type_list(const HashSet &p_shader_types) const; String _get_qualifier_str(ArgumentQualifier p_qualifier) const; + bool _parse_numeric_constant_expression(const FunctionInfo &p_function_info, float &r_constant); Error _parse_shader(const HashMap &p_functions, const Vector &p_render_modes, const HashSet &p_shader_types); Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op); diff --git a/servers/rendering/shader_types.cpp b/servers/rendering/shader_types.cpp index 32fc3ffa911..9b9dd4936cb 100644 --- a/servers/rendering/shader_types.cpp +++ b/servers/rendering/shader_types.cpp @@ -52,16 +52,29 @@ static ShaderLanguage::BuiltInInfo constt(ShaderLanguage::DataType p_type) { return ShaderLanguage::BuiltInInfo(p_type, true); } +static ShaderLanguage::BuiltInInfo constvt(ShaderLanguage::DataType p_type, const Vector &p_values) { + return ShaderLanguage::BuiltInInfo(p_type, true, p_values); +} + ShaderTypes::ShaderTypes() { singleton = this; /*************** SPATIAL ***********************/ + ShaderLanguage::Scalar pi_scalar; + pi_scalar.real = Math_PI; + + ShaderLanguage::Scalar tau_scalar; + tau_scalar.real = Math_TAU; + + ShaderLanguage::Scalar e_scalar; + e_scalar.real = Math_E; + shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["TIME"] = constt(ShaderLanguage::TYPE_FLOAT); shader_modes[RS::SHADER_SPATIAL].functions["global"].built_ins["EXPOSURE"] = constt(ShaderLanguage::TYPE_FLOAT); - shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["PI"] = constt(ShaderLanguage::TYPE_FLOAT); - shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["TAU"] = constt(ShaderLanguage::TYPE_FLOAT); - shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["E"] = constt(ShaderLanguage::TYPE_FLOAT); + shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["PI"] = constvt(ShaderLanguage::TYPE_FLOAT, { pi_scalar }); + shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["TAU"] = constvt(ShaderLanguage::TYPE_FLOAT, { tau_scalar }); + shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["E"] = constvt(ShaderLanguage::TYPE_FLOAT, { e_scalar }); shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["OUTPUT_IS_SRGB"] = constt(ShaderLanguage::TYPE_BOOL); shader_modes[RS::SHADER_SPATIAL].functions["constants"].built_ins["CLIP_SPACE_FAR"] = constt(ShaderLanguage::TYPE_FLOAT);