summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorYuri Rubinsky <chaosus89@gmail.com>2024-07-01 16:39:49 +0300
committerChaosus <chaosus89@gmail.com>2024-09-09 15:12:11 +0300
commitaf92fdb0ac1764bb58815d2fe7eb46bc4a1c50de (patch)
treedbf8059b97e7cdffa45d6e9335ad5b9ed7e79374
parentd0dc3896ad6fe039e4ad0607d901c81197cb3a3b (diff)
downloadredot-engine-af92fdb0ac1764bb58815d2fe7eb46bc4a1c50de.tar.gz
Add basic support to evaluate operator value in shader language
-rw-r--r--drivers/gles3/storage/material_storage.cpp4
-rw-r--r--servers/rendering/renderer_rd/storage_rd/material_storage.cpp4
-rw-r--r--servers/rendering/shader_compiler.cpp2
-rw-r--r--servers/rendering/shader_language.cpp607
-rw-r--r--servers/rendering/shader_language.h44
5 files changed, 540 insertions, 121 deletions
diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp
index a37eba3b15..7d5af48384 100644
--- a/drivers/gles3/storage/material_storage.cpp
+++ b/drivers/gles3/storage/material_storage.cpp
@@ -348,7 +348,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
}
}
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@@ -572,7 +572,7 @@ void ShaderData::set_default_texture_parameter(const StringName &p_name, RID p_t
Variant ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
- Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
index 63dc54e24c..9f390c99f9 100644
--- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp
@@ -342,7 +342,7 @@ static void _fill_std140_variant_ubo_value(ShaderLanguage::DataType type, int p_
}
}
-_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::ConstantNode::Value> &value, uint8_t *data) {
+_FORCE_INLINE_ static void _fill_std140_ubo_value(ShaderLanguage::DataType type, const Vector<ShaderLanguage::Scalar> &value, uint8_t *data) {
switch (type) {
case ShaderLanguage::TYPE_BOOL: {
uint32_t *gui = (uint32_t *)data;
@@ -566,7 +566,7 @@ void MaterialStorage::ShaderData::set_default_texture_parameter(const StringName
Variant MaterialStorage::ShaderData::get_default_parameter(const StringName &p_parameter) const {
if (uniforms.has(p_parameter)) {
ShaderLanguage::ShaderNode::Uniform uniform = uniforms[p_parameter];
- Vector<ShaderLanguage::ConstantNode::Value> default_value = uniform.default_value;
+ Vector<ShaderLanguage::Scalar> default_value = uniform.default_value;
return ShaderLanguage::constant_value_to_variant(default_value, uniform.type, uniform.array_size, uniform.hint);
}
return Variant();
diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp
index 49e005ca96..43703f8656 100644
--- a/servers/rendering/shader_compiler.cpp
+++ b/servers/rendering/shader_compiler.cpp
@@ -185,7 +185,7 @@ static String f2sp0(float p_float) {
return num;
}
-static String get_constant_text(SL::DataType p_type, const Vector<SL::ConstantNode::Value> &p_values) {
+static String get_constant_text(SL::DataType p_type, const Vector<SL::Scalar> &p_values) {
switch (p_type) {
case SL::TYPE_BOOL:
return p_values[0].boolean ? "true" : "false";
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 4eaf7fcb55..2249cd2010 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1362,7 +1362,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden
}
#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) {
+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, Vector<Scalar> *r_constant_values) {
if (is_shader_inc) {
for (int i = 0; i < RenderingServer::SHADER_MAX; i++) {
for (const KeyValue<StringName, FunctionInfo> &E : ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(i))) {
@@ -1424,8 +1424,8 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_struct_name) {
*r_struct_name = p_block->variables[p_identifier].struct_name;
}
- if (r_constant_value) {
- *r_constant_value = p_block->variables[p_identifier].value;
+ if (r_constant_values && !p_block->variables[p_identifier].values.is_empty()) {
+ *r_constant_values = p_block->variables[p_identifier].values;
}
if (r_type) {
*r_type = IDENTIFIER_LOCAL_VAR;
@@ -1507,13 +1507,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
if (r_struct_name) {
*r_struct_name = shader->constants[p_identifier].struct_name;
}
- if (r_constant_value) {
- if (shader->constants[p_identifier].initializer && shader->constants[p_identifier].initializer->type == Node::NODE_TYPE_CONSTANT) {
- ConstantNode *cnode = static_cast<ConstantNode *>(shader->constants[p_identifier].initializer);
-
- if (cnode->values.size() == 1) {
- *r_constant_value = cnode->values[0];
- }
+ if (r_constant_values) {
+ if (shader->constants[p_identifier].initializer && !shader->constants[p_identifier].initializer->get_values().is_empty()) {
+ *r_constant_values = shader->constants[p_identifier].initializer->get_values();
}
}
if (r_type) {
@@ -1544,7 +1540,7 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, bool p_allow_rea
return false;
}
-bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
+bool ShaderLanguage::_validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type, int *r_ret_size) {
bool valid = false;
DataType ret_type = TYPE_VOID;
int ret_size = 0;
@@ -2007,9 +2003,384 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
if (r_ret_size) {
*r_ret_size = ret_size;
}
+
+ 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);
+ }
+
return valid;
}
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_get_node_values(const BlockNode *p_block, Node *p_node) {
+ Vector<Scalar> result;
+
+ switch (p_node->type) {
+ case Node::NODE_TYPE_VARIABLE: {
+ _find_identifier(p_block, false, FunctionInfo(), static_cast<VariableNode *>(p_node)->name, nullptr, nullptr, nullptr, nullptr, nullptr, &result);
+ } break;
+ default: {
+ result = p_node->get_values();
+ } break;
+ }
+
+ return result;
+}
+
+bool ShaderLanguage::_eval_operator(const BlockNode *p_block, OperatorNode *p_op) {
+ bool is_valid = true;
+
+ switch (p_op->op) {
+ case OP_EQUAL:
+ case OP_NOT_EQUAL:
+ case OP_LESS:
+ case OP_LESS_EQUAL:
+ case OP_GREATER:
+ case OP_GREATER_EQUAL:
+ case OP_AND:
+ case OP_OR:
+ case OP_ADD:
+ case OP_SUB:
+ case OP_MUL:
+ case OP_DIV:
+ case OP_MOD:
+ case OP_SHIFT_LEFT:
+ case OP_SHIFT_RIGHT:
+ case OP_BIT_AND:
+ case OP_BIT_OR:
+ case OP_BIT_XOR: {
+ DataType a = p_op->arguments[0]->get_datatype();
+ DataType b = p_op->arguments[1]->get_datatype();
+
+ bool is_op_vec_transform = false;
+ if (p_op->op == OP_MUL) {
+ DataType ta = a;
+ DataType tb = b;
+
+ if (ta > tb) {
+ SWAP(ta, tb);
+ }
+ if (ta == TYPE_VEC2 && tb == TYPE_MAT2) {
+ is_op_vec_transform = true;
+ } else if (ta == TYPE_VEC3 && tb == TYPE_MAT3) {
+ is_op_vec_transform = true;
+ } else if (ta == TYPE_VEC4 && tb == TYPE_MAT4) {
+ is_op_vec_transform = true;
+ }
+ }
+
+ Vector<Scalar> va = _get_node_values(p_block, p_op->arguments[0]);
+ Vector<Scalar> vb = _get_node_values(p_block, p_op->arguments[1]);
+
+ if (is_op_vec_transform) {
+ p_op->values = _eval_vector_transform(va, vb, a, b, p_op->get_datatype());
+ } else {
+ p_op->values = _eval_vector(va, vb, a, b, p_op->get_datatype(), p_op->op, is_valid);
+ }
+ } break;
+ 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);
+ } break;
+ default: {
+ } break;
+ }
+
+ return is_valid;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_unary_scalar(const Scalar &p_a, Operator p_op, DataType p_ret_type) {
+ Scalar scalar;
+
+ switch (p_op) {
+ case OP_NOT: {
+ scalar.boolean = !p_a.boolean;
+ } break;
+ case OP_NEGATE: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = -p_a.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ // Intentionally wrap the unsigned int value, because GLSL does.
+ scalar.uint = 0 - p_a.uint;
+ } else { // float types
+ scalar.real = -scalar.real;
+ }
+ } break;
+ case OP_BIT_INVERT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = ~p_a.sint;
+ } else { // uint types
+ scalar.uint = ~p_a.uint;
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return scalar;
+}
+
+ShaderLanguage::Scalar ShaderLanguage::_eval_scalar(const Scalar &p_a, const Scalar &p_b, Operator p_op, DataType p_ret_type, bool &r_is_valid) {
+ Scalar scalar;
+
+ switch (p_op) {
+ case OP_EQUAL: {
+ scalar.boolean = p_a.boolean == p_b.boolean;
+ } break;
+ case OP_NOT_EQUAL: {
+ scalar.boolean = p_a.boolean != p_b.boolean;
+ } break;
+ case OP_LESS: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint < p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint < p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real < p_b.real;
+ }
+ } break;
+ case OP_LESS_EQUAL: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint <= p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint <= p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real <= p_b.real;
+ }
+ } break;
+ case OP_GREATER: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint > p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint > p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real > p_b.real;
+ }
+ } break;
+ case OP_GREATER_EQUAL: {
+ if (p_ret_type == TYPE_INT) {
+ scalar.boolean = p_a.sint >= p_b.sint;
+ } else if (p_ret_type == TYPE_UINT) {
+ scalar.boolean = p_a.uint >= p_b.uint;
+ } else { // float type
+ scalar.boolean = p_a.real >= p_b.real;
+ }
+ } break;
+ case OP_AND: {
+ scalar.boolean = p_a.boolean && p_b.boolean;
+ } break;
+ case OP_OR: {
+ scalar.boolean = p_a.boolean || p_b.boolean;
+ } break;
+ case OP_ADD: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint + p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint + p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real + p_b.real;
+ }
+ } break;
+ case OP_SUB: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint - p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint - p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real - p_b.real;
+ }
+ } break;
+ case OP_MUL: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint * p_b.sint;
+ } else if (p_ret_type >= TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ scalar.uint = p_a.uint * p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real * p_b.real;
+ }
+ } break;
+ case OP_DIV: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ if (p_b.sint == 0) {
+ _set_error(RTR("Division by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.sint = p_a.sint / p_b.sint;
+ } else if (p_ret_type == TYPE_UINT && p_ret_type <= TYPE_UVEC4) {
+ if (p_b.uint == 0U) {
+ _set_error(RTR("Division by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.uint = p_a.uint / p_b.uint;
+ } else { // float + matrix types
+ scalar.real = p_a.real / p_b.real;
+ }
+ } break;
+ case OP_MOD: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ if (p_b.sint == 0) {
+ _set_error(RTR("Modulo by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.sint = p_a.sint % p_b.sint;
+ } else { // uint types
+ if (p_b.uint == 0U) {
+ _set_error(RTR("Modulo by zero error."));
+ r_is_valid = false;
+ break;
+ }
+ scalar.uint = p_a.uint % p_b.uint;
+ }
+ } break;
+ case OP_SHIFT_LEFT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint << p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint << p_b.uint;
+ }
+ } break;
+ case OP_SHIFT_RIGHT: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint >> p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint >> p_b.uint;
+ }
+ } break;
+ case OP_BIT_AND: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint & p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint & p_b.uint;
+ }
+ } break;
+ case OP_BIT_OR: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint | p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint | p_b.uint;
+ }
+ } break;
+ case OP_BIT_XOR: {
+ if (p_ret_type >= TYPE_INT && p_ret_type <= TYPE_IVEC4) {
+ scalar.sint = p_a.sint ^ p_b.sint;
+ } else { // uint types
+ scalar.uint = p_a.uint ^ p_b.uint;
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return scalar;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op) {
+ uint32_t size = get_datatype_component_count(p_ret_type);
+ if (p_va.size() != p_ret_type) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+ Vector<Scalar> value;
+ value.resize(size);
+
+ Scalar *w = value.ptrw();
+ for (uint32_t i = 0U; i < size; i++) {
+ w[i] = _eval_unary_scalar(p_va[i], p_op, p_ret_type);
+ }
+ return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid) {
+ uint32_t left_size = get_datatype_component_count(p_left_type);
+ uint32_t right_size = get_datatype_component_count(p_right_type);
+
+ if (p_va.size() != left_size || p_vb.size() != right_size) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+
+ uint32_t ret_size = get_datatype_component_count(p_ret_type);
+ Vector<Scalar> value;
+ value.resize(ret_size);
+
+ Scalar *w = value.ptrw();
+ for (uint32_t i = 0U; i < ret_size; i++) {
+ w[i] = _eval_scalar(p_va[MIN(i, left_size - 1)], p_vb[MIN(i, right_size - 1)], p_op, p_ret_type, r_is_valid);
+ if (!r_is_valid) {
+ return value;
+ }
+ }
+ return value;
+}
+
+Vector<ShaderLanguage::Scalar> ShaderLanguage::_eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type) {
+ uint32_t left_size = get_datatype_component_count(p_left_type);
+ uint32_t right_size = get_datatype_component_count(p_right_type);
+
+ if (p_va.size() != left_size || p_vb.size() != right_size) {
+ return Vector<Scalar>(); // Non-evaluable values should not be parsed further.
+ }
+
+ uint32_t ret_size = get_datatype_component_count(p_ret_type);
+ Vector<Scalar> value;
+ value.resize_zeroed(ret_size);
+
+ Scalar *w = value.ptrw();
+ switch (p_ret_type) {
+ case TYPE_VEC2: {
+ if (left_size == 2) { // v * m
+ Vector2 v = Vector2(p_va[0].real, p_va[1].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y);
+ w[1].real = (p_vb[2].real * v.x + p_vb[3].real * v.y);
+ } else { // m * v
+ Vector2 v = Vector2(p_vb[0].real, p_vb[1].real);
+
+ w[0].real = (p_va[0].real * v.x + p_va[2].real * v.y);
+ w[1].real = (p_va[1].real * v.x + p_va[3].real * v.y);
+ }
+ } break;
+ case TYPE_VEC3: {
+ if (left_size == 3) { // v * m
+ Vector3 v = Vector3(p_va[0].real, p_va[1].real, p_va[2].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z);
+ w[1].real = (p_vb[3].real * v.x + p_vb[4].real * v.y + p_vb[5].real * v.z);
+ w[2].real = (p_vb[6].real * v.x + p_vb[7].real * v.y + p_vb[8].real * v.z);
+ } else { // m * v
+ Vector3 v = Vector3(p_vb[0].real, p_vb[1].real, p_vb[2].real);
+
+ w[0].real = (p_va[0].real * v.x + p_va[3].real * v.y + p_va[6].real * v.z);
+ w[1].real = (p_va[1].real * v.x + p_va[4].real * v.y + p_va[7].real * v.z);
+ w[2].real = (p_va[2].real * v.x + p_va[5].real * v.y + p_va[8].real * v.z);
+ }
+ } break;
+ case TYPE_VEC4: {
+ if (left_size == 4) { // v * m
+ Vector4 v = Vector4(p_va[0].real, p_va[1].real, p_va[2].real, p_va[3].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[1].real * v.y + p_vb[2].real * v.z + p_vb[3].real * v.w);
+ w[1].real = (p_vb[4].real * v.x + p_vb[5].real * v.y + p_vb[6].real * v.z + p_vb[7].real * v.w);
+ w[2].real = (p_vb[8].real * v.x + p_vb[9].real * v.y + p_vb[10].real * v.z + p_vb[11].real * v.w);
+ w[3].real = (p_vb[12].real * v.x + p_vb[13].real * v.y + p_vb[14].real * v.z + p_vb[15].real * v.w);
+ } else { // m * v
+ Vector4 v = Vector4(p_vb[0].real, p_vb[1].real, p_vb[2].real, p_vb[3].real);
+
+ w[0].real = (p_vb[0].real * v.x + p_vb[4].real * v.y + p_vb[8].real * v.z + p_vb[12].real * v.w);
+ w[1].real = (p_vb[1].real * v.x + p_vb[5].real * v.y + p_vb[9].real * v.z + p_vb[13].real * v.w);
+ w[2].real = (p_vb[2].real * v.x + p_vb[6].real * v.y + p_vb[10].real * v.z + p_vb[14].real * v.w);
+ w[3].real = (p_vb[3].real * v.x + p_vb[7].real * v.y + p_vb[11].real * v.z + p_vb[15].real * v.w);
+ }
+ } break;
+ default: {
+ } break;
+ }
+
+ return value;
+}
+
const ShaderLanguage::BuiltinFuncDef ShaderLanguage::builtin_func_defs[] = {
// Constructors.
@@ -3271,34 +3642,15 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, const FunctionI
int max = builtin_func_const_args[constarg_idx].max;
bool error = false;
- if (p_func->arguments[arg]->type == Node::NODE_TYPE_VARIABLE) {
- const VariableNode *vn = static_cast<VariableNode *>(p_func->arguments[arg]);
-
- bool is_const = false;
- ConstantNode::Value value;
- value.sint = -1;
-
- _find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, &is_const, nullptr, nullptr, &value);
- if (!is_const || value.sint < min || value.sint > max) {
+ Vector<Scalar> values = _get_node_values(p_block, 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;
}
} else {
- if (p_func->arguments[arg]->type == Node::NODE_TYPE_CONSTANT) {
- const ConstantNode *cn = static_cast<ConstantNode *>(p_func->arguments[arg]);
-
- if (cn->get_datatype() == TYPE_INT && cn->values.size() == 1) {
- int value = cn->values[0].sint;
-
- if (value < min || value > max) {
- error = true;
- }
- } else {
- error = true;
- }
- } else {
- error = true;
- }
+ error = true;
}
+
if (error) {
_set_error(vformat(RTR("Expected integer constant within [%d..%d] range."), min, max));
return false;
@@ -3760,7 +4112,7 @@ bool ShaderLanguage::is_token_hint(TokenType p_type) {
return int(p_type) > int(TK_RENDER_MODE) && int(p_type) < int(TK_SHADER_TYPE);
}
-bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value) {
+bool ShaderLanguage::convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value) {
if (p_constant->datatype == p_to_type) {
if (p_value) {
for (int i = 0; i < p_constant->values.size(); i++) {
@@ -3828,7 +4180,7 @@ bool ShaderLanguage::is_sampler_type(DataType p_type) {
return p_type > TYPE_MAT4 && p_type < TYPE_STRUCT;
}
-Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
+Variant ShaderLanguage::constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint) {
int array_size = p_array_size;
if (p_value.size() > 0) {
@@ -4437,6 +4789,52 @@ uint32_t ShaderLanguage::get_datatype_size(ShaderLanguage::DataType p_type) {
ERR_FAIL_V(0);
}
+uint32_t ShaderLanguage::get_datatype_component_count(ShaderLanguage::DataType p_type) {
+ switch (p_type) {
+ case TYPE_BOOL:
+ return 1U;
+ case TYPE_BVEC2:
+ return 2U;
+ case TYPE_BVEC3:
+ return 3U;
+ case TYPE_BVEC4:
+ return 4U;
+ case TYPE_INT:
+ return 1U;
+ case TYPE_IVEC2:
+ return 2U;
+ case TYPE_IVEC3:
+ return 3U;
+ case TYPE_IVEC4:
+ return 4U;
+ case TYPE_UINT:
+ return 1U;
+ case TYPE_UVEC2:
+ return 2U;
+ case TYPE_UVEC3:
+ return 3U;
+ case TYPE_UVEC4:
+ return 4U;
+ case TYPE_FLOAT:
+ return 1U;
+ case TYPE_VEC2:
+ return 2U;
+ case TYPE_VEC3:
+ return 3U;
+ case TYPE_VEC4:
+ return 4U;
+ case TYPE_MAT2:
+ return 4U;
+ case TYPE_MAT3:
+ return 9U;
+ case TYPE_MAT4:
+ return 16U;
+ default:
+ break;
+ }
+ return 0U;
+}
+
void ShaderLanguage::get_keyword_list(List<String> *r_keywords) {
HashSet<String> kws;
@@ -4929,45 +5327,30 @@ Error ShaderLanguage::_parse_array_size(BlockNode *p_block, const FunctionInfo &
*r_unknown_size = true;
}
} else {
- int array_size = 0;
+ _set_tkpos(pos);
- if (!tk.is_integer_constant() || ((int)tk.constant) <= 0) {
- _set_tkpos(pos);
- Node *n = _parse_and_reduce_expression(p_block, p_function_info);
- if (n) {
- if (n->type == Node::NODE_TYPE_VARIABLE) {
- VariableNode *vn = static_cast<VariableNode *>(n);
- if (vn) {
- ConstantNode::Value v;
- DataType data_type;
- bool is_const = false;
+ int array_size = 0;
+ Node *expr = _parse_and_reduce_expression(p_block, p_function_info);
- _find_identifier(p_block, false, p_function_info, vn->name, &data_type, nullptr, &is_const, nullptr, nullptr, &v);
+ if (expr) {
+ Vector<Scalar> values = _get_node_values(p_block, expr);
- if (is_const) {
- if (data_type == TYPE_INT) {
- int32_t value = v.sint;
- if (value > 0) {
- array_size = value;
- }
- } else if (data_type == TYPE_UINT) {
- uint32_t value = v.uint;
- if (value > 0U) {
- array_size = value;
- }
- }
- }
- }
- } else if (n->type == Node::NODE_TYPE_OPERATOR) {
- _set_error(vformat(RTR("Array size expressions are not supported.")));
- return ERR_PARSE_ERROR;
- }
- if (r_size_expression != nullptr) {
- *r_size_expression = n;
+ if (!values.is_empty()) {
+ switch (expr->get_datatype()) {
+ case TYPE_INT: {
+ array_size = values[0].sint;
+ } break;
+ case TYPE_UINT: {
+ array_size = (int)values[0].uint;
+ } break;
+ default: {
+ } break;
}
}
- } else if (((int)tk.constant) > 0) {
- array_size = (uint32_t)tk.constant;
+
+ if (r_size_expression != nullptr) {
+ *r_size_expression = expr;
+ }
}
if (array_size <= 0) {
@@ -5064,7 +5447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
idx++;
}
if (!auto_size && !undefined_size && an->initializer.size() != array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), array_size, an->initializer.size()));
return nullptr;
}
} else {
@@ -5185,7 +5568,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_array_constructor(BlockNode *p_bloc
}
}
if (an->initializer.size() != p_array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), p_array_size, an->initializer.size()));
return nullptr;
}
} else {
@@ -5230,7 +5613,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_FLOAT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.real = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_FLOAT;
@@ -5238,7 +5621,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_INT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.sint = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_INT;
@@ -5246,7 +5629,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_UINT_CONSTANT) {
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.uint = tk.constant;
constant->values.push_back(v);
constant->datatype = TYPE_UINT;
@@ -5255,7 +5638,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_TRUE) {
//handle true constant
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.boolean = true;
constant->values.push_back(v);
constant->datatype = TYPE_BOOL;
@@ -5264,7 +5647,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
} else if (tk.type == TK_FALSE) {
//handle false constant
ConstantNode *constant = alloc_node<ConstantNode>();
- ConstantNode::Value v;
+ Scalar v;
v.boolean = false;
constant->values.push_back(v);
constant->datatype = TYPE_BOOL;
@@ -6527,7 +6910,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(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
_set_error(RTR("Invalid base type for increment/decrement operator."));
return nullptr;
}
@@ -6876,7 +7259,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
expression.write[i].is_op = false;
expression.write[i].node = op;
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int j = 0; j < op->arguments.size(); j++) {
if (j > 0) {
@@ -6914,7 +7301,11 @@ 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(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
@@ -6950,6 +7341,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
if (_is_operator_assign(op->op)) {
+ if (p_block && expression[next_op - 1].node->type == Node::NODE_TYPE_VARIABLE) {
+ VariableNode *vn = static_cast<VariableNode *>(expression[next_op - 1].node);
+ p_block->use_op_eval = vn->is_const;
+ }
+
String assign_message;
if (!_validate_assign(expression[next_op - 1].node, p_function_info, &assign_message)) {
_set_error(assign_message);
@@ -6972,7 +7368,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
//replace all 3 nodes by this operator and make it an expression
- if (!_validate_operator(op, &op->return_cache, &op->return_array_size)) {
+ if (!_validate_operator(p_block, op, &op->return_cache, &op->return_array_size)) {
+ if (error_set) {
+ return nullptr;
+ }
+
String at;
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0) {
@@ -6998,6 +7398,10 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
}
+ if (p_block) {
+ p_block->use_op_eval = true;
+ }
+
if (p_previous_expression_info != nullptr) {
p_previous_expression_info->expression->push_back(expression[0]);
}
@@ -7020,7 +7424,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
DataType base = get_scalar_type(type);
int cardinality = get_cardinality(type);
- Vector<ConstantNode::Value> values;
+ Vector<Scalar> values;
for (int i = 1; i < op->arguments.size(); i++) {
op->arguments.write[i] = _reduce_expression(p_block, op->arguments[i]);
@@ -7032,7 +7436,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
values.push_back(cn->values[j]);
}
} else if (get_scalar_type(cn->datatype) == cn->datatype) {
- ConstantNode::Value v;
+ Scalar v;
if (!convert_constant(cn, base, &v)) {
return p_node;
}
@@ -7048,8 +7452,8 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
if (values.size() == 1) {
if (type >= TYPE_MAT2 && type <= TYPE_MAT4) {
- ConstantNode::Value value = values[0];
- ConstantNode::Value zero;
+ Scalar value = values[0];
+ Scalar zero;
zero.real = 0.0f;
int size = 2 + (type - TYPE_MAT2);
@@ -7060,7 +7464,7 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
}
}
} else {
- ConstantNode::Value value = values[0];
+ Scalar value = values[0];
for (int i = 1; i < cardinality; i++) {
values.push_back(value);
}
@@ -7081,10 +7485,10 @@ ShaderLanguage::Node *ShaderLanguage::_reduce_expression(BlockNode *p_block, Sha
DataType base = get_scalar_type(cn->datatype);
- Vector<ConstantNode::Value> values;
+ Vector<Scalar> values;
for (int i = 0; i < cn->values.size(); i++) {
- ConstantNode::Value nv;
+ Scalar nv;
switch (base) {
case TYPE_BOOL: {
nv.boolean = !cn->values[i].boolean;
@@ -7515,7 +7919,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
decl.size = decl.initializer.size();
var.array_size = decl.initializer.size();
} else if (decl.initializer.size() != var.array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), var.array_size, decl.initializer.size()));
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -7537,7 +7941,9 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
array_size = var.array_size;
} else if (tk.type == TK_OP_ASSIGN) {
- //variable created with assignment! must parse an expression
+ p_block->use_op_eval = is_const;
+
+ // Variable created with assignment! Must parse an expression.
Node *n = _parse_and_reduce_expression(p_block, p_function_info);
if (!n) {
return ERR_PARSE_ERROR;
@@ -7552,11 +7958,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
}
}
- if (n->type == Node::NODE_TYPE_CONSTANT) {
- ConstantNode *const_node = static_cast<ConstantNode *>(n);
- if (const_node && const_node->values.size() == 1) {
- var.value = const_node->values[0];
- }
+ if (is_const) {
+ var.values = n->get_values();
}
if (!_compare_datatypes(var.type, var.struct_name, var.array_size, n->get_datatype(), n->get_datatype_name(), n->get_array_size())) {
@@ -7734,13 +8137,13 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
if (!vn) {
return ERR_PARSE_ERROR;
}
- ConstantNode::Value v;
+ Vector<Scalar> v = { Scalar() };
_find_identifier(p_block, false, p_function_info, vn->name, nullptr, nullptr, nullptr, nullptr, nullptr, &v);
- if (constants.has(v.sint)) {
- _set_error(vformat(RTR("Duplicated case label: %d."), v.sint));
+ if (constants.has(v[0].sint)) {
+ _set_error(vformat(RTR("Duplicated case label: %d."), v[0].sint));
return ERR_PARSE_ERROR;
}
- constants.insert(v.sint);
+ constants.insert(v[0].sint);
}
} else if (flow->flow_op == FLOW_OP_DEFAULT) {
continue;
@@ -7801,7 +8204,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const FunctionInfo &p_fun
vn->name = tk.text;
n = vn;
} else {
- ConstantNode::Value v;
+ Scalar v;
if (tk.type == TK_UINT_CONSTANT) {
v.uint = (uint32_t)tk.constant;
} else {
@@ -9678,7 +10081,7 @@ Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_f
decl.size = decl.initializer.size();
constant.array_size = decl.initializer.size();
} else if (decl.initializer.size() != constant.array_size) {
- _set_error(RTR("Array size mismatch."));
+ _set_error(vformat(RTR("Array size mismatch. Expected %d elements (found %d)."), constant.array_size, decl.initializer.size()));
return ERR_PARSE_ERROR;
}
} else {
diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h
index 63dca99654..ba02e181b9 100644
--- a/servers/rendering/shader_language.h
+++ b/servers/rendering/shader_language.h
@@ -355,6 +355,13 @@ public:
}
};
+ union Scalar {
+ bool boolean = false;
+ float real;
+ int32_t sint;
+ uint32_t uint;
+ };
+
struct Node {
Node *next = nullptr;
@@ -379,6 +386,7 @@ public:
virtual String get_datatype_name() const { return ""; }
virtual int get_array_size() const { return 0; }
virtual bool is_indexed() const { return false; }
+ virtual Vector<Scalar> get_values() const { return Vector<Scalar>(); }
Node(Type t) :
type(t) {}
@@ -402,11 +410,13 @@ public:
Operator op = OP_EQUAL;
StringName struct_name;
Vector<Node *> arguments;
+ Vector<Scalar> values;
virtual DataType get_datatype() const override { return return_cache; }
virtual String get_datatype_name() const override { return String(struct_name); }
virtual int get_array_size() const override { return return_array_size; }
virtual bool is_indexed() const override { return op == OP_INDEX; }
+ virtual Vector<Scalar> get_values() const override { return values; }
OperatorNode() :
Node(NODE_TYPE_OPERATOR) {}
@@ -485,19 +495,15 @@ public:
String struct_name = "";
int array_size = 0;
- union Value {
- bool boolean = false;
- float real;
- int32_t sint;
- uint32_t uint;
- };
-
- Vector<Value> values;
+ Vector<Scalar> values;
Vector<VariableDeclarationNode::Declaration> array_declarations;
virtual DataType get_datatype() const override { return datatype; }
virtual String get_datatype_name() const override { return struct_name; }
virtual int get_array_size() const override { return array_size; }
+ virtual Vector<Scalar> get_values() const override {
+ return values;
+ }
ConstantNode() :
Node(NODE_TYPE_CONSTANT) {}
@@ -529,13 +535,14 @@ public:
int line; //for completion
int array_size;
bool is_const;
- ConstantNode::Value value;
+ Vector<Scalar> values;
};
HashMap<StringName, Variable> variables;
List<Node *> statements;
bool single_statement = false;
bool use_comma_between_statements = false;
+ bool use_op_eval = true;
BlockNode() :
Node(NODE_TYPE_BLOCK) {}
@@ -657,7 +664,7 @@ public:
DataType type = TYPE_VOID;
DataPrecision precision = PRECISION_DEFAULT;
int array_size = 0;
- Vector<ConstantNode::Value> default_value;
+ Vector<Scalar> default_value;
Scope scope = SCOPE_LOCAL;
Hint hint = HINT_NONE;
bool use_color = false;
@@ -803,15 +810,16 @@ public:
static bool is_token_operator_assign(TokenType p_type);
static bool is_token_hint(TokenType p_type);
- static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, ConstantNode::Value *p_value = nullptr);
+ static bool convert_constant(ConstantNode *p_constant, DataType p_to_type, Scalar *p_value = nullptr);
static DataType get_scalar_type(DataType p_type);
static int get_cardinality(DataType p_type);
static bool is_scalar_type(DataType p_type);
static bool is_float_type(DataType p_type);
static bool is_sampler_type(DataType p_type);
- static Variant constant_value_to_variant(const Vector<ShaderLanguage::ConstantNode::Value> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
+ static Variant constant_value_to_variant(const Vector<Scalar> &p_value, DataType p_type, int p_array_size, ShaderLanguage::ShaderNode::Uniform::Hint p_hint = ShaderLanguage::ShaderNode::Uniform::HINT_NONE);
static PropertyInfo uniform_to_property_info(const ShaderNode::Uniform &p_uniform);
static uint32_t get_datatype_size(DataType p_type);
+ static uint32_t get_datatype_component_count(DataType p_type);
static void get_keyword_list(List<String> *r_keywords);
static bool is_control_flow_keyword(String p_keyword);
@@ -1070,13 +1078,21 @@ private:
IdentifierType last_type = IDENTIFIER_MAX;
- bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, ConstantNode::Value *r_constant_value = nullptr);
+ bool _find_identifier(const BlockNode *p_block, bool p_allow_reassign, const FunctionInfo &p_function_info, const StringName &p_identifier, DataType *r_data_type = nullptr, IdentifierType *r_type = nullptr, bool *r_is_const = nullptr, int *r_array_size = nullptr, StringName *r_struct_name = nullptr, Vector<Scalar> *r_constant_values = nullptr);
#ifdef DEBUG_ENABLED
void _parse_used_identifier(const StringName &p_identifier, IdentifierType p_type, const StringName &p_function);
#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(OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+ bool _validate_operator(const BlockNode *p_block, OperatorNode *p_op, DataType *r_ret_type = nullptr, int *r_ret_size = nullptr);
+
+ Vector<Scalar> _get_node_values(const BlockNode *p_block, Node *p_node);
+ bool _eval_operator(const BlockNode *p_block, 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<Scalar> _eval_unary_vector(const Vector<Scalar> &p_va, DataType p_ret_type, Operator p_op);
+ Vector<Scalar> _eval_vector(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type, Operator p_op, bool &r_is_valid);
+ Vector<Scalar> _eval_vector_transform(const Vector<Scalar> &p_va, const Vector<Scalar> &p_vb, DataType p_left_type, DataType p_right_type, DataType p_ret_type);
struct BuiltinEntry {
const char *name;