From 6f162395ffb5f54a8530172621c2978b6f0cb4eb Mon Sep 17 00:00:00 2001 From: Yuri Roubinsky Date: Fri, 17 Jan 2020 22:35:22 +0300 Subject: Implementation of 'struct' for shaders --- .../visual/rasterizer_rd/shader_compiler_rd.cpp | 85 ++- servers/visual/shader_language.cpp | 632 ++++++++++++++++----- servers/visual/shader_language.h | 46 +- 3 files changed, 604 insertions(+), 159 deletions(-) (limited to 'servers') 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); @@ -560,6 +604,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: { @@ -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 &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 &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 Mapvariables[p_identifier].struct_name; + } return true; } @@ -946,7 +952,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Maparguments[i].type_str; + } return true; } } @@ -982,6 +990,9 @@ bool ShaderLanguage::_find_identifier(const BlockNode *p_block, const Mapconstants[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 args; + Vector 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(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 &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 Vectorreturn_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(); - func->op = OP_CALL; - VariableNode *funcname = alloc_node(); - 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(); + func->op = OP_STRUCT; + func->struct_name = name; + func->return_cache = TYPE_STRUCT; + VariableNode *funcname = alloc_node(); + 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(); + func->op = OP_CALL; + VariableNode *funcname = alloc_node(); + 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(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(); + } + } + + 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(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(); + } + 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(); 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::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(); 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 Mapstructs.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 Mapstructs.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(); - 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 Mapstructs.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 Mapget_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(); - 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 Mapget_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(); - 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 &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(); + 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(); + 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 &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 &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 &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 &p_funct Node *expr = _parse_and_reduce_expression(NULL, Map()); 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 &p_funct constant.initializer = static_cast(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 &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 &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 &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 Mapstructs.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::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 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 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 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 constants; Map varyings; Map uniforms; + Map structs; Vector render_modes; Vector functions; + Vector 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 &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 &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 &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 &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); -- cgit v1.2.3