summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--drivers/gles2/rasterizer_storage_gles2.cpp3
-rw-r--r--drivers/gles2/shader_compiler_gles2.cpp82
-rw-r--r--main/tests/test_shader_lang.cpp3
-rw-r--r--servers/visual/rasterizer_rd/shader_compiler_rd.cpp85
-rw-r--r--servers/visual/shader_language.cpp632
-rw-r--r--servers/visual/shader_language.h46
6 files changed, 673 insertions, 178 deletions
diff --git a/drivers/gles2/rasterizer_storage_gles2.cpp b/drivers/gles2/rasterizer_storage_gles2.cpp
index 23fc280d91..245531a935 100644
--- a/drivers/gles2/rasterizer_storage_gles2.cpp
+++ b/drivers/gles2/rasterizer_storage_gles2.cpp
@@ -1636,6 +1636,9 @@ void RasterizerStorageGLES2::shader_get_param_list(RID p_shader, List<PropertyIn
pi.name = E->get();
switch (u.type) {
+ case ShaderLanguage::TYPE_STRUCT: {
+ pi.type = Variant::ARRAY;
+ } break;
case ShaderLanguage::TYPE_VOID: {
pi.type = Variant::NIL;
} break;
diff --git a/drivers/gles2/shader_compiler_gles2.cpp b/drivers/gles2/shader_compiler_gles2.cpp
index b4b9b70abc..08047935bd 100644
--- a/drivers/gles2/shader_compiler_gles2.cpp
+++ b/drivers/gles2/shader_compiler_gles2.cpp
@@ -240,21 +240,20 @@ void ShaderCompilerGLES2::_dump_function_deps(SL::ShaderNode *p_node, const Stri
r_to_add += "\n";
StringBuffer<128> header;
-
- header += _typestr(fnode->return_type);
- header += " ";
- header += _mkid(fnode->name);
- header += "(";
+ if (fnode->return_type == SL::TYPE_STRUCT) {
+ header += _mkid(fnode->return_struct_name) + " " + _mkid(fnode->name) + "(";
+ } else {
+ header += _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "(";
+ }
for (int i = 0; i < fnode->arguments.size(); i++) {
if (i > 0)
header += ", ";
-
- header += _qualstr(fnode->arguments[i].qualifier);
- header += _prestr(fnode->arguments[i].precision);
- header += _typestr(fnode->arguments[i].type);
- header += " ";
- header += _mkid(fnode->arguments[i].name);
+ if (fnode->arguments[i].type == SL::TYPE_STRUCT) {
+ header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].type_str) + " " + _mkid(fnode->arguments[i].name);
+ } else {
+ header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name);
+ }
}
header += ")\n";
@@ -312,6 +311,36 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
StringBuilder vertex_global;
StringBuilder fragment_global;
+ // structs
+
+ for (int i = 0; i < snode->vstructs.size(); i++) {
+
+ SL::StructNode *st = snode->vstructs[i].shader_struct;
+ String struct_code;
+
+ struct_code += "struct ";
+ struct_code += _mkid(snode->vstructs[i].name);
+ struct_code += " ";
+ struct_code += "{\n";
+ for (int j = 0; j < st->members.size(); j++) {
+ SL::MemberNode *m = st->members[j];
+ if (m->datatype == SL::TYPE_STRUCT) {
+ struct_code += _mkid(m->struct_name);
+ } else {
+ struct_code += _prestr(m->precision);
+ struct_code += _typestr(m->datatype);
+ }
+ struct_code += " ";
+ struct_code += m->name;
+ struct_code += ";\n";
+ }
+ struct_code += "}";
+ struct_code += ";\n";
+
+ vertex_global += struct_code;
+ fragment_global += struct_code;
+ }
+
// uniforms
for (Map<StringName, SL::ShaderNode::Uniform>::Element *E = snode->uniforms.front(); E; E = E->next()) {
@@ -374,7 +403,11 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
String gcode;
gcode += "const ";
gcode += _prestr(E->get().precision);
- gcode += _typestr(E->get().type);
+ if (E->get().type == SL::TYPE_STRUCT) {
+ gcode += _mkid(E->get().type_str);
+ } else {
+ gcode += _typestr(E->get().type);
+ }
gcode += " " + _mkid(E->key());
gcode += "=";
gcode += _dump_node_code(E->get().initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
@@ -420,7 +453,9 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
r_gen_code.fragment_global = fragment_global.as_string();
} break;
+ case SL::Node::TYPE_STRUCT: {
+ } break;
case SL::Node::TYPE_FUNCTION: {
} break;
@@ -459,8 +494,12 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
if (var_dec_node->is_const) {
declaration += "const ";
}
- declaration += _prestr(var_dec_node->precision);
- declaration += _typestr(var_dec_node->datatype);
+ if (var_dec_node->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(var_dec_node->struct_name);
+ } else {
+ declaration += _prestr(var_dec_node->precision);
+ declaration += _typestr(var_dec_node->datatype);
+ }
for (int i = 0; i < var_dec_node->declarations.size(); i++) {
@@ -524,9 +563,12 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
SL::ArrayDeclarationNode *arr_dec_node = (SL::ArrayDeclarationNode *)p_node;
StringBuffer<> declaration;
- declaration += _prestr(arr_dec_node->precision);
- declaration += _typestr(arr_dec_node->datatype);
-
+ if (arr_dec_node->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(arr_dec_node->struct_name);
+ } else {
+ declaration += _prestr(arr_dec_node->precision);
+ declaration += _typestr(arr_dec_node->datatype);
+ }
for (int i = 0; i < arr_dec_node->declarations.size(); i++) {
if (i > 0) {
@@ -646,12 +688,14 @@ String ShaderCompilerGLES2::_dump_node_code(SL::Node *p_node, int p_level, Gener
} break;
case SL::OP_CALL:
+ case SL::OP_STRUCT:
case SL::OP_CONSTRUCT: {
ERR_FAIL_COND_V(op_node->arguments[0]->type != SL::Node::TYPE_VARIABLE, String());
SL::VariableNode *var_node = (SL::VariableNode *)op_node->arguments[0];
-
- if (op_node->op == SL::OP_CONSTRUCT) {
+ if (op_node->op == SL::OP_STRUCT) {
+ code += _mkid(var_node->name);
+ } else if (op_node->op == SL::OP_CONSTRUCT) {
code += var_node->name;
} else {
diff --git a/main/tests/test_shader_lang.cpp b/main/tests/test_shader_lang.cpp
index cac5b95635..1e0f3c6425 100644
--- a/main/tests/test_shader_lang.cpp
+++ b/main/tests/test_shader_lang.cpp
@@ -163,6 +163,9 @@ static String dump_node_code(SL::Node *p_node, int p_level) {
//code+=dump_node_code(pnode->body,p_level);
} break;
+ case SL::Node::TYPE_STRUCT: {
+
+ } break;
case SL::Node::TYPE_FUNCTION: {
} break;
diff --git a/servers/visual/rasterizer_rd/shader_compiler_rd.cpp b/servers/visual/rasterizer_rd/shader_compiler_rd.cpp
index 4d89ae14d1..851deb367f 100644
--- a/servers/visual/rasterizer_rd/shader_compiler_rd.cpp
+++ b/servers/visual/rasterizer_rd/shader_compiler_rd.cpp
@@ -90,6 +90,7 @@ static int _get_datatype_size(SL::DataType p_type) {
case SL::TYPE_ISAMPLER3D: return 16;
case SL::TYPE_USAMPLER3D: return 16;
case SL::TYPE_SAMPLERCUBE: return 16;
+ case SL::TYPE_STRUCT: return 0;
}
ERR_FAIL_V(0);
@@ -129,6 +130,7 @@ static int _get_datatype_alignment(SL::DataType p_type) {
case SL::TYPE_ISAMPLER3D: return 16;
case SL::TYPE_USAMPLER3D: return 16;
case SL::TYPE_SAMPLERCUBE: return 16;
+ case SL::TYPE_STRUCT: return 0;
}
ERR_FAIL_V(0);
@@ -315,12 +317,20 @@ void ShaderCompilerRD::_dump_function_deps(const SL::ShaderNode *p_node, const S
r_to_add += "\n";
String header;
- header = _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "(";
+ if (fnode->return_type == SL::TYPE_STRUCT) {
+ header = _mkid(fnode->return_struct_name) + " " + _mkid(fnode->name) + "(";
+ } else {
+ header = _typestr(fnode->return_type) + " " + _mkid(fnode->name) + "(";
+ }
for (int i = 0; i < fnode->arguments.size(); i++) {
if (i > 0)
header += ", ";
- header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name);
+ if (fnode->arguments[i].type == SL::TYPE_STRUCT) {
+ header += _qualstr(fnode->arguments[i].qualifier) + _mkid(fnode->arguments[i].type_str) + " " + _mkid(fnode->arguments[i].name);
+ } else {
+ header += _qualstr(fnode->arguments[i].qualifier) + _prestr(fnode->arguments[i].precision) + _typestr(fnode->arguments[i].type) + " " + _mkid(fnode->arguments[i].name);
+ }
}
header += ")\n";
@@ -359,6 +369,36 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
}
}
+ // structs
+
+ for (int i = 0; i < pnode->vstructs.size(); i++) {
+
+ SL::StructNode *st = pnode->vstructs[i].shader_struct;
+ String struct_code;
+
+ struct_code += "struct ";
+ struct_code += _mkid(pnode->vstructs[i].name);
+ struct_code += " ";
+ struct_code += "{\n";
+ for (int j = 0; j < st->members.size(); j++) {
+ SL::MemberNode *m = st->members[j];
+ if (m->datatype == SL::TYPE_STRUCT) {
+ struct_code += _mkid(m->struct_name);
+ } else {
+ struct_code += _prestr(m->precision);
+ struct_code += _typestr(m->datatype);
+ }
+ struct_code += " ";
+ struct_code += m->name;
+ struct_code += ";\n";
+ }
+ struct_code += "}";
+ struct_code += ";\n";
+
+ r_gen_code.vertex_global += struct_code;
+ r_gen_code.fragment_global += struct_code;
+ }
+
int max_texture_uniforms = 0;
int max_uniforms = 0;
@@ -506,7 +546,11 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
String gcode;
gcode += "const ";
gcode += _prestr(E->get().precision);
- gcode += _typestr(E->get().type);
+ if (E->get().type == SL::TYPE_STRUCT) {
+ gcode += _mkid(E->get().type_str);
+ } else {
+ gcode += _typestr(E->get().type);
+ }
gcode += " " + _mkid(E->key());
gcode += "=";
gcode += _dump_node_code(E->get().initializer, p_level, r_gen_code, p_actions, p_default_actions, p_assigning);
@@ -561,6 +605,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
//code+=dump_node_code(pnode->body,p_level);
} break;
+ case SL::Node::TYPE_STRUCT: {
+
+ } break;
case SL::Node::TYPE_FUNCTION: {
} break;
@@ -590,7 +637,15 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
case SL::Node::TYPE_VARIABLE_DECLARATION: {
SL::VariableDeclarationNode *vdnode = (SL::VariableDeclarationNode *)p_node;
- String declaration = _prestr(vdnode->precision) + _typestr(vdnode->datatype);
+ String declaration;
+ if (vdnode->is_const) {
+ declaration += "const ";
+ }
+ if (vdnode->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(vdnode->struct_name);
+ } else {
+ declaration += _prestr(vdnode->precision) + _typestr(vdnode->datatype);
+ }
for (int i = 0; i < vdnode->declarations.size(); i++) {
if (i > 0) {
declaration += ",";
@@ -649,8 +704,15 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
case SL::Node::TYPE_ARRAY_DECLARATION: {
SL::ArrayDeclarationNode *adnode = (SL::ArrayDeclarationNode *)p_node;
-
- String declaration = _prestr(adnode->precision) + _typestr(adnode->datatype);
+ String declaration;
+ if (adnode->is_const) {
+ declaration += "const ";
+ }
+ if (adnode->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(adnode->struct_name);
+ } else {
+ declaration = _prestr(adnode->precision) + _typestr(adnode->datatype);
+ }
for (int i = 0; i < adnode->declarations.size(); i++) {
if (i > 0) {
declaration += ",";
@@ -664,7 +726,11 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
int sz = adnode->declarations[i].initializer.size();
if (sz > 0) {
declaration += "=";
- declaration += _typestr(adnode->datatype);
+ if (adnode->datatype == SL::TYPE_STRUCT) {
+ declaration += _mkid(adnode->struct_name);
+ } else {
+ declaration += _typestr(adnode->datatype);
+ }
declaration += "[";
declaration += itos(sz);
declaration += "]";
@@ -763,6 +829,7 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
code = _dump_node_code(onode->arguments[0], p_level, r_gen_code, p_actions, p_default_actions, p_assigning) + _opstr(onode->op);
break;
case SL::OP_CALL:
+ case SL::OP_STRUCT:
case SL::OP_CONSTRUCT: {
ERR_FAIL_COND_V(onode->arguments[0]->type != SL::Node::TYPE_VARIABLE, String());
@@ -770,7 +837,9 @@ String ShaderCompilerRD::_dump_node_code(const SL::Node *p_node, int p_level, Ge
SL::VariableNode *vnode = (SL::VariableNode *)onode->arguments[0];
bool is_texture_func = false;
- if (onode->op == SL::OP_CONSTRUCT) {
+ if (onode->op == SL::OP_STRUCT) {
+ code += _mkid(vnode->name);
+ } else if (onode->op == SL::OP_CONSTRUCT) {
code += String(vnode->name);
} else {
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp
index d405dade6f..c2bdc6d7e4 100644
--- a/servers/visual/shader_language.cpp
+++ b/servers/visual/shader_language.cpp
@@ -283,6 +283,7 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = {
{ TK_INTERPOLATION_FLAT, "flat" },
{ TK_INTERPOLATION_SMOOTH, "smooth" },
{ TK_CONST, "const" },
+ { TK_STRUCT, "struct" },
{ TK_PRECISION_LOW, "lowp" },
{ TK_PRECISION_MID, "mediump" },
{ TK_PRECISION_HIGH, "highp" },
@@ -862,6 +863,7 @@ String ShaderLanguage::get_datatype_name(DataType p_type) {
case TYPE_ISAMPLER3D: return "isampler3D";
case TYPE_USAMPLER3D: return "usampler3D";
case TYPE_SAMPLERCUBE: return "samplerCube";
+ case TYPE_STRUCT: return "struct";
}
return "";
@@ -880,6 +882,7 @@ void ShaderLanguage::clear() {
completion_block = NULL;
completion_function = StringName();
completion_class = SubClassTag::TAG_GLOBAL;
+ completion_struct = StringName();
error_line = 0;
tk_line = 1;
@@ -893,7 +896,7 @@ void ShaderLanguage::clear() {
}
}
-bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size) {
+bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type, IdentifierType *r_type, bool *r_is_const, int *r_array_size, StringName *r_struct_name) {
if (p_builtin_types.has(p_identifier)) {
@@ -924,6 +927,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String
if (r_type) {
*r_type = IDENTIFIER_LOCAL_VAR;
}
+ if (r_struct_name) {
+ *r_struct_name = p_block->variables[p_identifier].struct_name;
+ }
return true;
}
@@ -946,7 +952,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String
if (r_type) {
*r_type = IDENTIFIER_FUNCTION_ARGUMENT;
}
-
+ if (r_struct_name) {
+ *r_struct_name = function->arguments[i].type_str;
+ }
return true;
}
}
@@ -982,6 +990,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Map<String
if (r_type) {
*r_type = IDENTIFIER_CONSTANT;
}
+ if (r_struct_name) {
+ *r_struct_name = shader->constants[p_identifier].type_str;
+ }
return true;
}
@@ -1228,7 +1239,11 @@ bool ShaderLanguage::_validate_operator(OperatorNode *p_op, DataType *r_ret_type
case OP_ASSIGN: {
DataType na = p_op->arguments[0]->get_datatype();
DataType nb = p_op->arguments[1]->get_datatype();
- valid = na == nb;
+ if (na == TYPE_STRUCT || nb == TYPE_STRUCT) {
+ valid = p_op->arguments[0]->get_datatype_name() == p_op->arguments[1]->get_datatype_name();
+ } else {
+ valid = na == nb;
+ }
ret_type = na;
} break;
case OP_ASSIGN_ADD:
@@ -2096,11 +2111,12 @@ const ShaderLanguage::BuiltinFuncOutArgs ShaderLanguage::builtin_func_out_args[]
{ NULL, 0 }
};
-bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type) {
+bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str) {
ERR_FAIL_COND_V(p_func->op != OP_CALL && p_func->op != OP_CONSTRUCT, false);
Vector<DataType> args;
+ Vector<StringName> args2;
ERR_FAIL_COND_V(p_func->arguments[0]->type != Node::TYPE_VARIABLE, false);
@@ -2108,6 +2124,7 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p
for (int i = 1; i < p_func->arguments.size(); i++) {
args.push_back(p_func->arguments[i]->get_datatype());
+ args2.push_back(p_func->arguments[i]->get_datatype_name());
}
int argcount = args.size();
@@ -2287,7 +2304,10 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p
bool fail = false;
for (int j = 0; j < args.size(); j++) {
-
+ if (args[j] == TYPE_STRUCT && args2[j] != pfunc->arguments[j].type_str) {
+ fail = true;
+ break;
+ }
if (get_scalar_type(args[j]) == args[j] && p_func->arguments[j + 1]->type == Node::TYPE_CONSTANT && convert_constant(static_cast<ConstantNode *>(p_func->arguments[j + 1]), pfunc->arguments[j].type)) {
//all good, but it needs implicit conversion later
} else if (args[j] != pfunc->arguments[j].type) {
@@ -2317,8 +2337,13 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p
p_func->arguments.write[k + 1] = conversion;
}
- if (r_ret_type)
+ if (r_ret_type) {
*r_ret_type = pfunc->return_type;
+ if (pfunc->return_type == TYPE_STRUCT) {
+ *r_ret_type_str = pfunc->return_struct_name;
+ }
+ }
+
return true;
}
}
@@ -2326,6 +2351,18 @@ bool ShaderLanguage::_validate_function_call(BlockNode *p_block, OperatorNode *p
return false;
}
+bool ShaderLanguage::_compare_datatypes_in_nodes(Node *a, Node *b) const {
+ if (a->get_datatype() != b->get_datatype()) {
+ return false;
+ }
+ if (a->get_datatype() == TYPE_STRUCT || b->get_datatype() == TYPE_STRUCT) {
+ if (a->get_datatype_name() != b->get_datatype_name()) {
+ return false;
+ }
+ }
+ return true;
+}
+
bool ShaderLanguage::_parse_function_arguments(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, OperatorNode *p_func, int *r_complete_arg) {
TkPos pos = _get_tkpos();
@@ -2571,6 +2608,8 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C
// Texture types, likely not relevant here.
break;
}
+ case ShaderLanguage::TYPE_STRUCT:
+ break;
case ShaderLanguage::TYPE_VOID:
break;
}
@@ -3122,7 +3161,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!ok)
return NULL;
- if (!_validate_function_call(p_block, func, &func->return_cache)) {
+ if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) {
_set_error("No matching constructor found for: '" + String(funcname->name) + "'");
return NULL;
}
@@ -3135,135 +3174,187 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
StringName identifier;
+ StructNode *pstruct = NULL;
+ bool struct_init = false;
+
_get_completable_identifier(p_block, COMPLETION_IDENTIFIER, identifier);
+ if (shader->structs.has(identifier)) {
+ pstruct = shader->structs[identifier].shader_struct;
+ struct_init = true;
+ }
+
tk = _get_token();
if (tk.type == TK_PARENTHESIS_OPEN) {
- //a function
- const StringName &name = identifier;
- OperatorNode *func = alloc_node<OperatorNode>();
- func->op = OP_CALL;
- VariableNode *funcname = alloc_node<VariableNode>();
- funcname->name = name;
- func->arguments.push_back(funcname);
+ if (struct_init) { //a struct constructor
- int carg = -1;
+ const StringName &name = identifier;
- bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
+ OperatorNode *func = alloc_node<OperatorNode>();
+ func->op = OP_STRUCT;
+ func->struct_name = name;
+ func->return_cache = TYPE_STRUCT;
+ VariableNode *funcname = alloc_node<VariableNode>();
+ funcname->name = name;
+ func->arguments.push_back(funcname);
- // Check if block has a variable with the same name as function to prevent shader crash.
- ShaderLanguage::BlockNode *bnode = p_block;
- while (bnode) {
- if (bnode->variables.has(name)) {
- _set_error("Expected function name");
- return NULL;
- }
- bnode = bnode->parent_block;
- }
+ for (int i = 0; i < pstruct->members.size(); i++) {
+ Node *nexpr = _parse_and_reduce_expression(p_block, p_builtin_types);
+ if (!nexpr) {
+ return NULL;
+ }
+ Node *node = pstruct->members[i];
- //test if function was parsed first
- int function_index = -1;
- for (int i = 0; i < shader->functions.size(); i++) {
- if (shader->functions[i].name == name) {
- //add to current function as dependency
- for (int j = 0; j < shader->functions.size(); j++) {
- if (shader->functions[j].name == current_function) {
- shader->functions.write[j].uses_function.insert(name);
- break;
- }
+ if (!_compare_datatypes_in_nodes(pstruct->members[i], nexpr)) {
+ String type_name = nexpr->get_datatype() == TYPE_STRUCT ? nexpr->get_datatype_name() : get_datatype_name(nexpr->get_datatype());
+ String type_name2 = node->get_datatype() == TYPE_STRUCT ? node->get_datatype_name() : get_datatype_name(node->get_datatype());
+ _set_error("Invalid assignment of '" + type_name + "' to '" + type_name2 + "'");
+ return NULL;
}
- //see if texture arguments must connect
- function_index = i;
- break;
+ if (i + 1 < pstruct->members.size()) {
+ tk = _get_token();
+ if (tk.type != TK_COMMA) {
+ _set_error("Expected ','");
+ return NULL;
+ }
+ }
+ func->arguments.push_back(nexpr);
+ }
+ tk = _get_token();
+ if (tk.type != TK_PARENTHESIS_CLOSE) {
+ _set_error("Expected ')'");
+ return NULL;
}
- }
- if (carg >= 0) {
- completion_type = COMPLETION_CALL_ARGUMENTS;
- completion_line = tk_line;
- completion_block = p_block;
- completion_function = funcname->name;
- completion_argument = carg;
- }
+ expr = func;
- if (!ok)
- return NULL;
+ } else { //a function
- if (!_validate_function_call(p_block, func, &func->return_cache)) {
- _set_error("No matching function found for: '" + String(funcname->name) + "'");
- return NULL;
- }
- completion_class = TAG_GLOBAL; // reset sub-class
+ const StringName &name = identifier;
- if (function_index >= 0) {
- //connect texture arguments, so we can cache in the
- //argument what type of filter and repeat to use
+ OperatorNode *func = alloc_node<OperatorNode>();
+ func->op = OP_CALL;
+ VariableNode *funcname = alloc_node<VariableNode>();
+ funcname->name = name;
+ func->arguments.push_back(funcname);
- FunctionNode *call_function = shader->functions[function_index].function;
- if (call_function) {
+ int carg = -1;
- //get current base function
- FunctionNode *base_function = NULL;
- {
- BlockNode *b = p_block;
+ bool ok = _parse_function_arguments(p_block, p_builtin_types, func, &carg);
- while (b) {
+ // Check if block has a variable with the same name as function to prevent shader crash.
+ ShaderLanguage::BlockNode *bnode = p_block;
+ while (bnode) {
+ if (bnode->variables.has(name)) {
+ _set_error("Expected function name");
+ return NULL;
+ }
+ bnode = bnode->parent_block;
+ }
- if (b->parent_function) {
- base_function = b->parent_function;
+ //test if function was parsed first
+ int function_index = -1;
+ for (int i = 0; i < shader->functions.size(); i++) {
+ if (shader->functions[i].name == name) {
+ //add to current function as dependency
+ for (int j = 0; j < shader->functions.size(); j++) {
+ if (shader->functions[j].name == current_function) {
+ shader->functions.write[j].uses_function.insert(name);
break;
- } else {
- b = b->parent_block;
}
}
+
+ //see if texture arguments must connect
+ function_index = i;
+ break;
}
+ }
- ERR_FAIL_COND_V(!base_function, NULL); //bug, wtf
-
- for (int i = 0; i < call_function->arguments.size(); i++) {
- int argidx = i + 1;
- if (argidx < func->arguments.size() && is_sampler_type(call_function->arguments[i].type)) {
- //let's see where our argument comes from
- Node *n = func->arguments[argidx];
- ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
- VariableNode *vn = static_cast<VariableNode *>(n);
- StringName varname = vn->name;
- if (shader->uniforms.has(varname)) {
- //being sampler, this either comes from a uniform
- ShaderNode::Uniform *u = &shader->uniforms[varname];
- ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously
- //propagate
- if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
- return NULL;
- }
- } else if (p_builtin_types.has(varname)) {
- //a built-in
- if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
- return NULL;
+ if (carg >= 0) {
+ completion_type = COMPLETION_CALL_ARGUMENTS;
+ completion_line = tk_line;
+ completion_block = p_block;
+ completion_function = funcname->name;
+ completion_argument = carg;
+ }
+
+ if (!ok)
+ return NULL;
+
+ if (!_validate_function_call(p_block, func, &func->return_cache, &func->struct_name)) {
+ _set_error("No matching function found for: '" + String(funcname->name) + "'");
+ return NULL;
+ }
+ completion_class = TAG_GLOBAL; // reset sub-class
+ if (function_index >= 0) {
+ //connect texture arguments, so we can cache in the
+ //argument what type of filter and repeat to use
+
+ FunctionNode *call_function = shader->functions[function_index].function;
+ if (call_function) {
+
+ //get current base function
+ FunctionNode *base_function = NULL;
+ {
+ BlockNode *b = p_block;
+
+ while (b) {
+
+ if (b->parent_function) {
+ base_function = b->parent_function;
+ break;
+ } else {
+ b = b->parent_block;
}
- } else {
- //or this comes from an argument, but nothing else can be a sampler
- bool found = false;
- for (int j = 0; j < base_function->arguments.size(); j++) {
- if (base_function->arguments[j].name == varname) {
- if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) {
- base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>();
+ }
+ }
+
+ ERR_FAIL_COND_V(!base_function, NULL); //bug, wtf
+
+ for (int i = 0; i < call_function->arguments.size(); i++) {
+ int argidx = i + 1;
+ if (argidx < func->arguments.size() && is_sampler_type(call_function->arguments[i].type)) {
+ //let's see where our argument comes from
+ Node *n = func->arguments[argidx];
+ ERR_CONTINUE(n->type != Node::TYPE_VARIABLE); //bug? this should always be a variable
+ VariableNode *vn = static_cast<VariableNode *>(n);
+ StringName varname = vn->name;
+ if (shader->uniforms.has(varname)) {
+ //being sampler, this either comes from a uniform
+ ShaderNode::Uniform *u = &shader->uniforms[varname];
+ ERR_CONTINUE(u->type != call_function->arguments[i].type); //this should have been validated previously
+ //propagate
+ if (!_propagate_function_call_sampler_uniform_settings(name, i, u->filter, u->repeat)) {
+ return NULL;
+ }
+ } else if (p_builtin_types.has(varname)) {
+ //a built-in
+ if (!_propagate_function_call_sampler_builtin_reference(name, i, varname)) {
+ return NULL;
+ }
+ } else {
+ //or this comes from an argument, but nothing else can be a sampler
+ bool found = false;
+ for (int j = 0; j < base_function->arguments.size(); j++) {
+ if (base_function->arguments[j].name == varname) {
+ if (!base_function->arguments[j].tex_argument_connect.has(call_function->name)) {
+ base_function->arguments.write[j].tex_argument_connect[call_function->name] = Set<int>();
+ }
+ base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i);
+ found = true;
+ break;
}
- base_function->arguments.write[j].tex_argument_connect[call_function->name].insert(i);
- found = true;
- break;
}
+ ERR_CONTINUE(!found);
}
- ERR_CONTINUE(!found);
}
}
}
}
+ expr = func;
}
- expr = func;
-
} else {
//an identifier
@@ -3273,6 +3364,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
IdentifierType ident_type;
bool is_const = false;
int array_size = 0;
+ StringName struct_name;
if (p_block && p_block->block_tag != SubClassTag::TAG_GLOBAL) {
int idx = 0;
@@ -3291,7 +3383,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
}
} else {
- if (!_find_identifier(p_block, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size)) {
+ if (!_find_identifier(p_block, p_builtin_types, identifier, &data_type, &ident_type, &is_const, &array_size, &struct_name)) {
_set_error("Unknown identifier in expression: " + String(identifier));
return NULL;
}
@@ -3355,6 +3447,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
ArrayNode *arrname = alloc_node<ArrayNode>();
arrname->name = identifier;
arrname->datatype_cache = data_type;
+ arrname->struct_name = struct_name;
arrname->index_expression = index_expression;
arrname->call_expression = call_expression;
arrname->is_const = is_const;
@@ -3366,6 +3459,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
varname->name = identifier;
varname->datatype_cache = data_type;
varname->is_const = is_const;
+ varname->struct_name = struct_name;
expr = varname;
}
}
@@ -3407,23 +3501,50 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (tk.type == TK_CURSOR) {
//do nothing
+ } else if (tk.type == TK_IDENTIFIER) {
+
} else if (tk.type == TK_PERIOD) {
+ DataType dt = expr->get_datatype();
+ String st = expr->get_datatype_name();
+
StringName identifier;
- if (_get_completable_identifier(p_block, COMPLETION_INDEX, identifier)) {
- completion_base = expr->get_datatype();
+ if (_get_completable_identifier(p_block, dt == TYPE_STRUCT ? COMPLETION_STRUCT : COMPLETION_INDEX, identifier)) {
+ if (dt == TYPE_STRUCT) {
+ completion_struct = st;
+ } else {
+ completion_base = dt;
+ }
}
if (identifier == StringName()) {
_set_error("Expected identifier as member");
return NULL;
}
- DataType dt = expr->get_datatype();
String ident = identifier;
bool ok = true;
DataType member_type = TYPE_VOID;
+ StringName member_struct_name = "";
switch (dt) {
+ case TYPE_STRUCT: {
+ ok = false;
+ String member_name = String(ident.ptr());
+ if (shader->structs.has(st)) {
+ StructNode *n = shader->structs[st].shader_struct;
+ for (List<MemberNode *>::Element *E = n->members.front(); E; E = E->next()) {
+ if (String(E->get()->name) == member_name) {
+ member_type = E->get()->datatype;
+ if (member_type == TYPE_STRUCT) {
+ member_struct_name = E->get()->struct_name;
+ }
+ ok = true;
+ break;
+ }
+ }
+ }
+
+ } break;
case TYPE_BVEC2:
case TYPE_IVEC2:
case TYPE_UVEC2:
@@ -3543,13 +3664,15 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
if (!ok) {
- _set_error("Invalid member for " + get_datatype_name(dt) + " expression: ." + ident);
+ _set_error("Invalid member for " + (dt == TYPE_STRUCT ? st : get_datatype_name(dt)) + " expression: ." + ident);
return NULL;
}
MemberNode *mn = alloc_node<MemberNode>();
mn->basetype = dt;
mn->datatype = member_type;
+ mn->base_struct_name = st;
+ mn->struct_name = member_struct_name;
mn->name = ident;
mn->owner = expr;
expr = mn;
@@ -3969,7 +4092,11 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
for (int i = 0; i < op->arguments.size(); i++) {
if (i > 0)
at += " and ";
- at += get_datatype_name(op->arguments[i]->get_datatype());
+ if (op->arguments[i]->get_datatype() == TYPE_STRUCT) {
+ at += op->arguments[i]->get_datatype_name();
+ } else {
+ at += get_datatype_name(op->arguments[i]->get_datatype());
+ }
}
_set_error("Invalid arguments to operator '" + get_operator_text(op->op) + "' :" + at);
return NULL;
@@ -4124,6 +4251,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
}
+ bool is_struct = shader->structs.has(tk.text);
+
if (tk.type == TK_CURLY_BRACKET_CLOSE) { //end of block
if (p_just_one) {
_set_error("Unexpected '}'");
@@ -4132,31 +4261,50 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
return OK;
- } else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type)) {
+ } else if (tk.type == TK_CONST || is_token_precision(tk.type) || is_token_nonvoid_datatype(tk.type) || is_struct) {
+ String struct_name = "";
+ if (is_struct) {
+ struct_name = tk.text;
+ }
bool is_const = false;
if (tk.type == TK_CONST) {
is_const = true;
tk = _get_token();
+
+ if (!is_struct) {
+ is_struct = shader->structs.has(tk.text); // check again.
+ struct_name = tk.text;
+ }
}
DataPrecision precision = PRECISION_DEFAULT;
if (is_token_precision(tk.type)) {
precision = get_token_precision(tk.type);
tk = _get_token();
+
+ if (!is_struct) {
+ is_struct = shader->structs.has(tk.text); // check again.
+ }
+ if (is_struct && precision != PRECISION_DEFAULT) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
if (!is_token_nonvoid_datatype(tk.type)) {
_set_error("Expected datatype after precision");
return ERR_PARSE_ERROR;
}
}
- if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for variable (samplers not allowed)");
- return ERR_PARSE_ERROR;
+ if (!is_struct) {
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for variable (samplers not allowed)");
+ return ERR_PARSE_ERROR;
+ }
}
- DataType type = get_token_datatype(tk.type);
+ DataType type = is_struct ? TYPE_STRUCT : get_token_datatype(tk.type);
if (_validate_datatype(type) != OK) {
return ERR_PARSE_ERROR;
@@ -4188,6 +4336,7 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
var.line = tk_line;
var.array_size = 0;
var.is_const = is_const;
+ var.struct_name = struct_name;
tk = _get_token();
@@ -4200,7 +4349,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
ArrayDeclarationNode *node = alloc_node<ArrayDeclarationNode>();
- node->datatype = type;
+ if (is_struct) {
+ node->struct_name = struct_name;
+ node->datatype = TYPE_STRUCT;
+ } else {
+ node->datatype = type;
+ }
node->precision = precision;
node->is_const = is_const;
vardecl = (Node *)node;
@@ -4255,16 +4409,29 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
if (is_token_precision(tk.type)) {
precision2 = get_token_precision(tk.type);
tk = _get_token();
+ if (shader->structs.has(tk.text)) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
if (!is_token_nonvoid_datatype(tk.type)) {
_set_error("Expected datatype after precision");
return ERR_PARSE_ERROR;
}
}
- if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for array");
- return ERR_PARSE_ERROR;
+
+ DataType type2;
+ String struct_name2 = "";
+
+ if (shader->structs.has(tk.text)) {
+ type2 = TYPE_STRUCT;
+ struct_name2 = tk.text;
+ } else {
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for array");
+ return ERR_PARSE_ERROR;
+ }
+ type2 = get_token_datatype(tk.type);
}
- DataType type2 = get_token_datatype(tk.type);
int array_size2 = 0;
@@ -4309,13 +4476,17 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
return ERR_PARSE_ERROR;
}
- if (precision != precision2 || type != type2 || var.array_size != array_size2) {
+ if (precision != precision2 || type != type2 || struct_name != struct_name2 || var.array_size != array_size2) {
String error_str = "Cannot convert from '";
if (precision2 != PRECISION_DEFAULT) {
error_str += get_precision_name(precision2);
error_str += " ";
}
- error_str += get_datatype_name(type2);
+ if (type2 == TYPE_STRUCT) {
+ error_str += struct_name2;
+ } else {
+ error_str += get_datatype_name(type2);
+ }
error_str += "[";
error_str += itos(array_size2);
error_str += "]'";
@@ -4324,7 +4495,11 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
error_str += get_precision_name(precision);
error_str += " ";
}
- error_str += get_datatype_name(type);
+ if (type == TYPE_STRUCT) {
+ error_str += struct_name;
+ } else {
+ error_str += get_datatype_name(type);
+ }
error_str += "[";
error_str += itos(var.array_size);
error_str += "]'";
@@ -4362,8 +4537,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
return ERR_PARSE_ERROR;
}
- if (var.type != n->get_datatype()) {
- _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'");
+ if (var.type != n->get_datatype() || struct_name != n->get_datatype_name()) {
+ _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? struct_name : get_datatype_name(var.type)) + "'");
return ERR_PARSE_ERROR;
}
@@ -4409,7 +4584,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
} else if (tk.type == TK_OP_ASSIGN) {
VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>();
- node->datatype = type;
+ if (is_struct) {
+ node->struct_name = struct_name;
+ node->datatype = TYPE_STRUCT;
+ } else {
+ node->datatype = type;
+ }
node->precision = precision;
node->is_const = is_const;
vardecl = (Node *)node;
@@ -4428,8 +4608,8 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
decl.initializer = n;
- if (var.type != n->get_datatype()) {
- _set_error("Invalid assignment of '" + get_datatype_name(n->get_datatype()) + "' to '" + get_datatype_name(var.type) + "'");
+ if (var.type == TYPE_STRUCT ? (var.struct_name != n->get_datatype_name()) : (var.type != n->get_datatype())) {
+ _set_error("Invalid assignment of '" + (n->get_datatype() == TYPE_STRUCT ? n->get_datatype_name() : get_datatype_name(n->get_datatype())) + "' to '" + (var.type == TYPE_STRUCT ? String(var.struct_name) : get_datatype_name(var.type)) + "'");
return ERR_PARSE_ERROR;
}
tk = _get_token();
@@ -4441,7 +4621,12 @@ Error ShaderLanguage::_parse_block(BlockNode *p_block, const Map<StringName, Bui
}
VariableDeclarationNode *node = alloc_node<VariableDeclarationNode>();
- node->datatype = type;
+ if (is_struct) {
+ node->struct_name = struct_name;
+ node->datatype = TYPE_STRUCT;
+ } else {
+ node->datatype = type;
+ }
node->precision = precision;
vardecl = (Node *)node;
@@ -5091,6 +5276,107 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
}
}
} break;
+ case TK_STRUCT: {
+ ShaderNode::Struct st;
+ DataType type;
+
+ tk = _get_token();
+ if (tk.type == TK_IDENTIFIER) {
+ st.name = tk.text;
+ tk = _get_token();
+ if (tk.type != TK_CURLY_BRACKET_OPEN) {
+ _set_error("Expected '{'");
+ return ERR_PARSE_ERROR;
+ }
+ } else {
+ _set_error("Expected struct identifier!");
+ return ERR_PARSE_ERROR;
+ }
+
+ StructNode *st_node = alloc_node<StructNode>();
+ st.shader_struct = st_node;
+
+ int member_count = 0;
+
+ while (true) { // variables list
+ tk = _get_token();
+ if (tk.type == TK_CURLY_BRACKET_CLOSE) {
+ break;
+ }
+ StringName struct_name = "";
+ bool struct_dt = false;
+ bool use_precision = false;
+ DataPrecision precision = DataPrecision::PRECISION_DEFAULT;
+
+ if (tk.type == TK_STRUCT) {
+ _set_error("nested structs are not allowed!");
+ return ERR_PARSE_ERROR;
+ }
+
+ if (is_token_precision(tk.type)) {
+ precision = get_token_precision(tk.type);
+ use_precision = true;
+ tk = _get_token();
+ }
+
+ if (shader->structs.has(tk.text)) {
+ struct_name = tk.text;
+ struct_dt = true;
+ if (use_precision) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
+ }
+
+ if (!is_token_datatype(tk.type) && !struct_dt) {
+ _set_error("Expected datatype.");
+ return ERR_PARSE_ERROR;
+ } else {
+ type = struct_dt ? TYPE_STRUCT : get_token_datatype(tk.type);
+
+ if (is_sampler_type(type)) {
+ _set_error("sampler datatype not allowed here");
+ return ERR_PARSE_ERROR;
+ } else if (type == TYPE_VOID) {
+ _set_error("void datatype not allowed here");
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = _get_token();
+ if (tk.type != TK_IDENTIFIER) {
+ _set_error("Expected identifier!");
+ return ERR_PARSE_ERROR;
+ }
+
+ MemberNode *member = alloc_node<MemberNode>();
+ member->precision = precision;
+ member->datatype = type;
+ member->struct_name = struct_name;
+ member->name = tk.text;
+ st_node->members.push_back(member);
+
+ tk = _get_token();
+ if (tk.type != TK_SEMICOLON) {
+ _set_error("Expected ';'");
+ return ERR_PARSE_ERROR;
+ }
+ member_count++;
+ }
+ }
+ if (member_count == 0) {
+ _set_error("Empty structs are not allowed!");
+ return ERR_PARSE_ERROR;
+ }
+
+ tk = _get_token();
+ if (tk.type != TK_SEMICOLON) {
+ _set_error("Expected ';'");
+ return ERR_PARSE_ERROR;
+ }
+ shader->structs[st.name] = st;
+ shader->vstructs.push_back(st); // struct's order is important!
+
+ } break;
case TK_UNIFORM:
case TK_VARYING: {
@@ -5380,6 +5666,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
//function or constant variable
bool is_constant = false;
+ bool is_struct = false;
+ StringName struct_name;
DataPrecision precision = PRECISION_DEFAULT;
DataType type;
StringName name;
@@ -5394,18 +5682,31 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
tk = _get_token();
}
- if (!is_token_datatype(tk.type)) {
- _set_error("Expected constant, function, uniform or varying ");
- return ERR_PARSE_ERROR;
- }
+ if (shader->structs.has(tk.text)) {
+ if (precision != PRECISION_DEFAULT) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
+ is_struct = true;
+ struct_name = tk.text;
+ } else {
- if (!is_token_variable_datatype(tk.type)) {
- _set_error("Invalid data type for constants or function return (samplers not allowed)");
- return ERR_PARSE_ERROR;
- }
+ if (!is_token_datatype(tk.type)) {
+ _set_error("Expected constant, function, uniform or varying");
+ return ERR_PARSE_ERROR;
+ }
- type = get_token_datatype(tk.type);
+ if (!is_token_variable_datatype(tk.type)) {
+ _set_error("Invalid data type for constants or function return (samplers not allowed)");
+ return ERR_PARSE_ERROR;
+ }
+ }
+ if (is_struct) {
+ type = TYPE_STRUCT;
+ } else {
+ type = get_token_datatype(tk.type);
+ }
TkPos prev_pos = _get_tkpos();
tk = _get_token();
if (tk.type == TK_BRACKET_OPEN) {
@@ -5442,7 +5743,8 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
while (true) {
ShaderNode::Constant constant;
- constant.type = type;
+ constant.type = is_struct ? TYPE_STRUCT : type;
+ constant.type_str = struct_name;
constant.precision = precision;
constant.initializer = NULL;
@@ -5457,7 +5759,6 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
Node *expr = _parse_and_reduce_expression(NULL, Map<StringName, BuiltInInfo>());
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;
@@ -5465,7 +5766,12 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
constant.initializer = static_cast<ConstantNode *>(expr);
- if (type != expr->get_datatype()) {
+ if (is_struct) {
+ if (expr->get_datatype_name() != struct_name) {
+ _set_error("Invalid assignment of '" + (expr->get_datatype() == TYPE_STRUCT ? expr->get_datatype_name() : get_datatype_name(expr->get_datatype())) + "' to '" + struct_name + "'");
+ return ERR_PARSE_ERROR;
+ }
+ } else if (type != expr->get_datatype()) {
_set_error("Invalid assignment of '" + get_datatype_name(expr->get_datatype()) + "' to '" + get_datatype_name(type) + "'");
return ERR_PARSE_ERROR;
}
@@ -5525,6 +5831,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
func_node->name = name;
func_node->return_type = type;
+ func_node->return_struct_name = struct_name;
func_node->return_precision = precision;
if (p_functions.has(name)) {
@@ -5556,27 +5863,43 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
DataType ptype;
StringName pname;
+ StringName param_struct_name;
DataPrecision pprecision = PRECISION_DEFAULT;
+ bool use_precision = false;
if (is_token_precision(tk.type)) {
pprecision = get_token_precision(tk.type);
tk = _get_token();
+ use_precision = true;
}
- if (!is_token_datatype(tk.type)) {
- _set_error("Expected a valid datatype for argument");
- return ERR_PARSE_ERROR;
- }
+ is_struct = false;
- ptype = get_token_datatype(tk.type);
+ if (shader->structs.has(tk.text)) {
+ is_struct = true;
+ param_struct_name = tk.text;
+ if (use_precision) {
+ _set_error("Precision modifier cannot be used on structs.");
+ return ERR_PARSE_ERROR;
+ }
+ }
- if (_validate_datatype(ptype) != OK) {
+ if (!is_struct && !is_token_datatype(tk.type)) {
+ _set_error("Expected a valid datatype for argument");
return ERR_PARSE_ERROR;
}
- if (ptype == TYPE_VOID) {
- _set_error("void not allowed in argument");
- return ERR_PARSE_ERROR;
+ if (is_struct) {
+ ptype = TYPE_STRUCT;
+ } else {
+ ptype = get_token_datatype(tk.type);
+ if (_validate_datatype(ptype) != OK) {
+ return ERR_PARSE_ERROR;
+ }
+ if (ptype == TYPE_VOID) {
+ _set_error("void not allowed in argument");
+ return ERR_PARSE_ERROR;
+ }
}
tk = _get_token();
@@ -5608,6 +5931,7 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct
FunctionNode::Argument arg;
arg.type = ptype;
arg.name = pname;
+ arg.type_str = param_struct_name;
arg.precision = pprecision;
arg.qualifier = qualifier;
arg.tex_argument_check = false;
@@ -5868,6 +6192,18 @@ Error ShaderLanguage::complete(const String &p_code, const Map<StringName, Funct
return OK;
} break;
+ case COMPLETION_STRUCT: {
+
+ if (shader->structs.has(completion_struct)) {
+ StructNode *node = shader->structs[completion_struct].shader_struct;
+ for (int i = 0; i < node->members.size(); i++) {
+ ScriptCodeCompletionOption option(node->members[i]->name, ScriptCodeCompletionOption::KIND_MEMBER);
+ r_options->push_back(option);
+ }
+ }
+
+ return OK;
+ } break;
case COMPLETION_MAIN_FUNCTION: {
for (const Map<StringName, FunctionInfo>::Element *E = p_functions.front(); E; E = E->next()) {
diff --git a/servers/visual/shader_language.h b/servers/visual/shader_language.h
index da5b0600ff..4d474086a6 100644
--- a/servers/visual/shader_language.h
+++ b/servers/visual/shader_language.h
@@ -82,6 +82,7 @@ public:
TK_INTERPOLATION_FLAT,
TK_INTERPOLATION_SMOOTH,
TK_CONST,
+ TK_STRUCT,
TK_PRECISION_LOW,
TK_PRECISION_MID,
TK_PRECISION_HIGH,
@@ -214,6 +215,7 @@ public:
TYPE_ISAMPLER3D,
TYPE_USAMPLER3D,
TYPE_SAMPLERCUBE,
+ TYPE_STRUCT,
};
enum DataPrecision {
@@ -269,6 +271,7 @@ public:
OP_POST_DECREMENT,
OP_CALL,
OP_CONSTRUCT,
+ OP_STRUCT,
OP_INDEX,
OP_MAX
};
@@ -329,11 +332,14 @@ public:
TYPE_MEMBER,
TYPE_ARRAY,
TYPE_ARRAY_DECLARATION,
+ TYPE_STRUCT,
};
Type type;
virtual DataType get_datatype() const { return TYPE_VOID; }
+ virtual String get_datatype_name() const { return ""; }
+
Node(Type t) :
next(NULL),
type(t) {}
@@ -354,20 +360,25 @@ public:
DataType return_cache;
DataPrecision return_precision_cache;
Operator op;
+ StringName struct_name;
Vector<Node *> arguments;
virtual DataType get_datatype() const { return return_cache; }
+ virtual String get_datatype_name() const { return String(struct_name); }
OperatorNode() :
Node(TYPE_OPERATOR),
return_cache(TYPE_VOID),
return_precision_cache(PRECISION_DEFAULT),
- op(OP_EQUAL) {}
+ op(OP_EQUAL),
+ struct_name("") {}
};
struct VariableNode : public Node {
DataType datatype_cache;
StringName name;
+ StringName struct_name;
virtual DataType get_datatype() const { return datatype_cache; }
+ virtual String get_datatype_name() const { return String(struct_name); }
bool is_const;
VariableNode() :
@@ -379,6 +390,7 @@ public:
struct VariableDeclarationNode : public Node {
DataPrecision precision;
DataType datatype;
+ String struct_name;
bool is_const;
struct Declaration {
@@ -398,12 +410,14 @@ public:
struct ArrayNode : public Node {
DataType datatype_cache;
+ StringName struct_name;
StringName name;
Node *index_expression;
Node *call_expression;
bool is_const;
virtual DataType get_datatype() const { return datatype_cache; }
+ virtual String get_datatype_name() const { return String(struct_name); }
ArrayNode() :
Node(TYPE_ARRAY),
@@ -416,6 +430,7 @@ public:
struct ArrayDeclarationNode : public Node {
DataPrecision precision;
DataType datatype;
+ String struct_name;
bool is_const;
struct Declaration {
@@ -470,6 +485,7 @@ public:
struct Variable {
DataType type;
+ StringName struct_name;
DataPrecision precision;
int line; //for completion
int array_size;
@@ -501,11 +517,15 @@ public:
struct MemberNode : public Node {
DataType basetype;
+ StringName base_struct_name;
+ DataPrecision precision;
DataType datatype;
+ StringName struct_name;
StringName name;
Node *owner;
virtual DataType get_datatype() const { return datatype; }
+ virtual String get_datatype_name() const { return String(struct_name); }
MemberNode() :
Node(TYPE_MEMBER),
@@ -514,12 +534,20 @@ public:
owner(NULL) {}
};
+ struct StructNode : public Node {
+
+ List<MemberNode *> members;
+ StructNode() :
+ Node(TYPE_STRUCT) {}
+ };
+
struct FunctionNode : public Node {
struct Argument {
ArgumentQualifier qualifier;
StringName name;
DataType type;
+ StringName type_str;
DataPrecision precision;
//for passing textures as arguments
bool tex_argument_check;
@@ -533,6 +561,7 @@ public:
StringName name;
DataType return_type;
+ StringName return_struct_name;
DataPrecision return_precision;
Vector<Argument> arguments;
BlockNode *body;
@@ -550,6 +579,7 @@ public:
struct Constant {
DataType type;
+ StringName type_str;
DataPrecision precision;
ConstantNode *initializer;
};
@@ -561,6 +591,11 @@ public:
bool callable;
};
+ struct Struct {
+ StringName name;
+ StructNode *shader_struct;
+ };
+
struct Varying {
DataType type;
DataInterpolation interpolation;
@@ -621,9 +656,11 @@ public:
Map<StringName, Constant> constants;
Map<StringName, Varying> varyings;
Map<StringName, Uniform> uniforms;
+ Map<StringName, Struct> structs;
Vector<StringName> render_modes;
Vector<Function> functions;
+ Vector<Struct> vstructs;
ShaderNode() :
Node(TYPE_SHADER) {}
@@ -650,6 +687,7 @@ public:
COMPLETION_FUNCTION_CALL,
COMPLETION_CALL_ARGUMENTS,
COMPLETION_INDEX,
+ COMPLETION_STRUCT,
};
struct Token {
@@ -766,7 +804,7 @@ private:
IDENTIFIER_CONSTANT,
};
- bool _find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL, bool *r_is_const = NULL, int *r_array_size = NULL);
+ bool _find_identifier(const BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types, const StringName &p_identifier, DataType *r_data_type = NULL, IdentifierType *r_type = NULL, bool *r_is_const = NULL, int *r_array_size = NULL, StringName *r_struct_name = NULL);
bool _is_operator_assign(Operator p_op) const;
bool _validate_assign(Node *p_node, const Map<StringName, BuiltInInfo> &p_builtin_types, String *r_message = NULL);
bool _validate_operator(OperatorNode *p_op, DataType *r_ret_type = NULL);
@@ -791,6 +829,7 @@ private:
DataType completion_base;
SubClassTag completion_class;
StringName completion_function;
+ StringName completion_struct;
int completion_argument;
bool _get_completable_identifier(BlockNode *p_block, CompletionType p_type, StringName &identifier);
@@ -798,8 +837,9 @@ private:
static const BuiltinFuncOutArgs builtin_func_out_args[];
Error _validate_datatype(DataType p_type);
+ bool _compare_datatypes_in_nodes(Node *a, Node *b) const;
- bool _validate_function_call(BlockNode *p_block, OperatorNode *p_func, DataType *r_ret_type);
+ 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 = NULL);
bool _propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat);
bool _propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin);