diff options
Diffstat (limited to 'servers/visual/shader_language.cpp')
-rw-r--r-- | servers/visual/shader_language.cpp | 483 |
1 files changed, 402 insertions, 81 deletions
diff --git a/servers/visual/shader_language.cpp b/servers/visual/shader_language.cpp index 98ccfcfc68..d405dade6f 100644 --- a/servers/visual/shader_language.cpp +++ b/servers/visual/shader_language.cpp @@ -207,6 +207,14 @@ const char *ShaderLanguage::token_names[TK_MAX] = { "HINT_BLACK_ALBEDO_TEXTURE", "HINT_COLOR", "HINT_RANGE", + "FILTER_NEAREST", + "FILTER_LINEAR", + "FILTER_NEAREST_MIPMAP", + "FILTER_LINEAR_MIPMAP", + "FILTER_NEAREST_MIPMAP_ANISO", + "FILTER_LINEAR_MIPMAP_ANISO", + "REPEAT_ENABLE", + "REPEAT_DISABLE", "SHADER_TYPE", "CURSOR", "ERROR", @@ -299,13 +307,26 @@ const ShaderLanguage::KeyWord ShaderLanguage::keyword_list[] = { { TK_HINT_WHITE_TEXTURE, "hint_white" }, { TK_HINT_BLACK_TEXTURE, "hint_black" }, { TK_HINT_NORMAL_TEXTURE, "hint_normal" }, + { TK_HINT_ROUGHNESS_NORMAL_TEXTURE, "hint_roughness_normal" }, + { TK_HINT_ROUGHNESS_R, "hint_roughness_r" }, + { TK_HINT_ROUGHNESS_G, "hint_roughness_g" }, + { TK_HINT_ROUGHNESS_B, "hint_roughness_b" }, + { TK_HINT_ROUGHNESS_A, "hint_roughness_a" }, + { TK_HINT_ROUGHNESS_GRAY, "hint_roughness_gray" }, { TK_HINT_ANISO_TEXTURE, "hint_aniso" }, { TK_HINT_ALBEDO_TEXTURE, "hint_albedo" }, { TK_HINT_BLACK_ALBEDO_TEXTURE, "hint_black_albedo" }, { TK_HINT_COLOR, "hint_color" }, { TK_HINT_RANGE, "hint_range" }, + { TK_FILTER_NEAREST, "filter_nearest" }, + { TK_FILTER_LINEAR, "filter_linear" }, + { TK_FILTER_NEAREST_MIPMAP, "filter_nearest_mipmap" }, + { TK_FILTER_LINEAR_MIPMAP, "filter_linear_mipmap" }, + { TK_FILTER_NEAREST_MIPMAP_ANISO, "filter_nearest_mipmap_aniso" }, + { TK_FILTER_LINEAR_MIPMAP_ANISO, "filter_linear_mipmap_aniso" }, + { TK_REPEAT_ENABLE, "repeat_enable" }, + { TK_REPEAT_DISABLE, "repeat_disable" }, { TK_SHADER_TYPE, "shader_type" }, - { TK_ERROR, NULL } }; @@ -2558,6 +2579,132 @@ Variant ShaderLanguage::constant_value_to_variant(const Vector<ShaderLanguage::C return Variant(); } +PropertyInfo ShaderLanguage::uniform_to_property_info(const ShaderNode::Uniform &p_uniform) { + PropertyInfo pi; + switch (p_uniform.type) { + case ShaderLanguage::TYPE_VOID: pi.type = Variant::NIL; break; + case ShaderLanguage::TYPE_BOOL: pi.type = Variant::BOOL; break; + case ShaderLanguage::TYPE_BVEC2: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y"; + break; + case ShaderLanguage::TYPE_BVEC3: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z"; + break; + case ShaderLanguage::TYPE_BVEC4: + pi.type = Variant::INT; + pi.hint = PROPERTY_HINT_FLAGS; + pi.hint_string = "x,y,z,w"; + break; + case ShaderLanguage::TYPE_UINT: + case ShaderLanguage::TYPE_INT: { + pi.type = Variant::INT; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]); + } + + } break; + case ShaderLanguage::TYPE_IVEC2: + case ShaderLanguage::TYPE_IVEC3: + case ShaderLanguage::TYPE_IVEC4: + case ShaderLanguage::TYPE_UVEC2: + case ShaderLanguage::TYPE_UVEC3: + case ShaderLanguage::TYPE_UVEC4: { + + pi.type = Variant::POOL_INT_ARRAY; + } break; + case ShaderLanguage::TYPE_FLOAT: { + pi.type = Variant::REAL; + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_RANGE) { + pi.hint = PROPERTY_HINT_RANGE; + pi.hint_string = rtos(p_uniform.hint_range[0]) + "," + rtos(p_uniform.hint_range[1]) + "," + rtos(p_uniform.hint_range[2]); + } + + } break; + case ShaderLanguage::TYPE_VEC2: pi.type = Variant::VECTOR2; break; + case ShaderLanguage::TYPE_VEC3: pi.type = Variant::VECTOR3; break; + case ShaderLanguage::TYPE_VEC4: { + if (p_uniform.hint == ShaderLanguage::ShaderNode::Uniform::HINT_COLOR) { + pi.type = Variant::COLOR; + } else { + pi.type = Variant::PLANE; + } + } break; + case ShaderLanguage::TYPE_MAT2: pi.type = Variant::TRANSFORM2D; break; + case ShaderLanguage::TYPE_MAT3: pi.type = Variant::BASIS; break; + case ShaderLanguage::TYPE_MAT4: pi.type = Variant::TRANSFORM; break; + case ShaderLanguage::TYPE_SAMPLER2D: + case ShaderLanguage::TYPE_ISAMPLER2D: + case ShaderLanguage::TYPE_USAMPLER2D: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture2D"; + } break; + case ShaderLanguage::TYPE_SAMPLER2DARRAY: + case ShaderLanguage::TYPE_ISAMPLER2DARRAY: + case ShaderLanguage::TYPE_USAMPLER2DARRAY: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "TextureArray"; + } break; + case ShaderLanguage::TYPE_SAMPLER3D: + case ShaderLanguage::TYPE_ISAMPLER3D: + case ShaderLanguage::TYPE_USAMPLER3D: { + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "Texture3D"; + } break; + case ShaderLanguage::TYPE_SAMPLERCUBE: { + + pi.type = Variant::OBJECT; + pi.hint = PROPERTY_HINT_RESOURCE_TYPE; + pi.hint_string = "CubeMap"; + } break; + } + return pi; +} + +uint32_t ShaderLanguage::get_type_size(DataType p_type) { + switch (p_type) { + case TYPE_BOOL: + case TYPE_INT: + case TYPE_UINT: + case TYPE_FLOAT: return 4; + case TYPE_BVEC2: + case TYPE_IVEC2: + case TYPE_UVEC2: + case TYPE_VEC2: return 8; + case TYPE_BVEC3: + case TYPE_IVEC3: + case TYPE_UVEC3: + case TYPE_VEC3: return 12; + case TYPE_BVEC4: + case TYPE_IVEC4: + case TYPE_UVEC4: + case TYPE_VEC4: return 16; + case TYPE_MAT2: return 8; + case TYPE_MAT3: return 12; + case TYPE_MAT4: return 16; + case TYPE_SAMPLER2D: + case TYPE_ISAMPLER2D: + case TYPE_USAMPLER2D: + case TYPE_SAMPLER2DARRAY: + case TYPE_ISAMPLER2DARRAY: + case TYPE_USAMPLER2DARRAY: + case TYPE_SAMPLER3D: + case TYPE_ISAMPLER3D: + case TYPE_USAMPLER3D: + case TYPE_SAMPLERCUBE: return 4; //not really, but useful for indices + } + return 0; +} + void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { Set<String> kws; @@ -2795,6 +2942,78 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const Map<StringName, BuiltI return false; } +bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_builtin_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_argument_check) { + //was checked, verify that filter and repeat are the same + if (arg->tex_argument_filter == p_filter && arg->tex_argument_repeat == p_repeat) { + return true; + } else { + + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using textures that differ in either filter or repeat setting."); + return false; + } + } else { + + arg->tex_argument_check = true; + arg->tex_argument_filter = p_filter; + arg->tex_argument_repeat = p_repeat; + for (Map<StringName, Set<int> >::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_uniform_settings(E->key(), F->get(), p_filter, p_repeat)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} +bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(StringName p_name, int p_argument, const StringName &p_builtin) { + for (int i = 0; shader->functions.size(); i++) { + if (shader->functions[i].name == p_name) { + + ERR_FAIL_INDEX_V(p_argument, shader->functions[i].function->arguments.size(), false); + FunctionNode::Argument *arg = &shader->functions[i].function->arguments.write[p_argument]; + if (arg->tex_argument_check) { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using both built-ins and uniform textures, this is not supported (use either one or the other)."); + return false; + } else if (arg->tex_builtin_check) { + //was checked, verify that the built-in is the same + if (arg->tex_builtin == p_builtin) { + return true; + } else { + _set_error("Sampler argument #" + itos(p_argument) + " of function '" + String(p_name) + "' called more than once using different built-ins. Only calling with the same built-in is supported."); + return false; + } + } else { + + arg->tex_builtin_check = true; + arg->tex_builtin = p_builtin; + + for (Map<StringName, Set<int> >::Element *E = arg->tex_argument_connect.front(); E; E = E->next()) { + for (Set<int>::Element *F = E->get().front(); F; F = F->next()) { + if (!_propagate_function_call_sampler_builtin_reference(E->key(), F->get(), p_builtin)) { + return false; + } + } + } + return true; + } + } + } + ERR_FAIL_V(false); //bug? function not found +} + ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, const Map<StringName, BuiltInInfo> &p_builtin_types) { Vector<Expression> expression; @@ -2944,6 +3163,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } //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 @@ -2953,6 +3173,9 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons break; } } + + //see if texture arguments must connect + function_index = i; break; } } @@ -2974,6 +3197,71 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons } 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; + } + } + } + + 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; + } + } + ERR_CONTINUE(!found); + } + } + } + } + } expr = func; } else { @@ -4881,116 +5169,145 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct if (tk.type == TK_COLON) { //hint - - tk = _get_token(); - if (tk.type == TK_HINT_WHITE_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_WHITE; - } else if (tk.type == TK_HINT_BLACK_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_BLACK; - } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; - } else if (tk.type == TK_HINT_ANISO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ANISO; - } else if (tk.type == TK_HINT_ALBEDO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_ALBEDO; - } else if (tk.type == TK_HINT_BLACK_ALBEDO_TEXTURE) { - uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO; - } else if (tk.type == TK_HINT_COLOR) { - if (type != TYPE_VEC4) { - _set_error("Color hint is for vec4 only"); - return ERR_PARSE_ERROR; - } - uniform2.hint = ShaderNode::Uniform::HINT_COLOR; - } else if (tk.type == TK_HINT_RANGE) { - - uniform2.hint = ShaderNode::Uniform::HINT_RANGE; - if (type != TYPE_FLOAT && type != TYPE_INT) { - _set_error("Range hint is for float and int only"); - return ERR_PARSE_ERROR; - } - - tk = _get_token(); - if (tk.type != TK_PARENTHESIS_OPEN) { - _set_error("Expected '(' after hint_range"); - return ERR_PARSE_ERROR; - } - + do { tk = _get_token(); + if (tk.type == TK_HINT_WHITE_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_WHITE; + } else if (tk.type == TK_HINT_BLACK_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK; + } else if (tk.type == TK_HINT_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_NORMAL_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_NORMAL; + } else if (tk.type == TK_HINT_ROUGHNESS_R) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_R; + } else if (tk.type == TK_HINT_ROUGHNESS_G) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_G; + } else if (tk.type == TK_HINT_ROUGHNESS_B) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_B; + } else if (tk.type == TK_HINT_ROUGHNESS_A) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_A; + } else if (tk.type == TK_HINT_ROUGHNESS_GRAY) { + uniform2.hint = ShaderNode::Uniform::HINT_ROUGHNESS_GRAY; + } else if (tk.type == TK_HINT_ANISO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ANISO; + } else if (tk.type == TK_HINT_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_ALBEDO; + } else if (tk.type == TK_HINT_BLACK_ALBEDO_TEXTURE) { + uniform2.hint = ShaderNode::Uniform::HINT_BLACK_ALBEDO; + } else if (tk.type == TK_HINT_COLOR) { + if (type != TYPE_VEC4) { + _set_error("Color hint is for vec4 only"); + return ERR_PARSE_ERROR; + } + uniform2.hint = ShaderNode::Uniform::HINT_COLOR; + } else if (tk.type == TK_HINT_RANGE) { - float sign = 1.0; + uniform2.hint = ShaderNode::Uniform::HINT_RANGE; + if (type != TYPE_FLOAT && type != TYPE_INT) { + _set_error("Range hint is for float and int only"); + return ERR_PARSE_ERROR; + } - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { - _set_error("Expected integer constant"); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_PARENTHESIS_OPEN) { + _set_error("Expected '(' after hint_range"); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[0] = tk.constant; - uniform2.hint_range[0] *= sign; + tk = _get_token(); - tk = _get_token(); + float sign = 1.0; - if (tk.type != TK_COMMA) { - _set_error("Expected ',' after integer constant"); - return ERR_PARSE_ERROR; - } + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } - tk = _get_token(); + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant"); + return ERR_PARSE_ERROR; + } - sign = 1.0; + uniform2.hint_range[0] = tk.constant; + uniform2.hint_range[0] *= sign; - if (tk.type == TK_OP_SUB) { - sign = -1.0; tk = _get_token(); - } - if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { - _set_error("Expected integer constant after ','"); - return ERR_PARSE_ERROR; - } + if (tk.type != TK_COMMA) { + _set_error("Expected ',' after integer constant"); + return ERR_PARSE_ERROR; + } - uniform2.hint_range[1] = tk.constant; - uniform2.hint_range[1] *= sign; + tk = _get_token(); - tk = _get_token(); + sign = 1.0; - if (tk.type == TK_COMMA) { - tk = _get_token(); + if (tk.type == TK_OP_SUB) { + sign = -1.0; + tk = _get_token(); + } if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { _set_error("Expected integer constant after ','"); return ERR_PARSE_ERROR; } - uniform2.hint_range[2] = tk.constant; + uniform2.hint_range[1] = tk.constant; + uniform2.hint_range[1] *= sign; + tk = _get_token(); - } else { - if (type == TYPE_INT) { - uniform2.hint_range[2] = 1; + + if (tk.type == TK_COMMA) { + tk = _get_token(); + + if (tk.type != TK_REAL_CONSTANT && tk.type != TK_INT_CONSTANT) { + _set_error("Expected integer constant after ','"); + return ERR_PARSE_ERROR; + } + + uniform2.hint_range[2] = tk.constant; + tk = _get_token(); } else { - uniform2.hint_range[2] = 0.001; + if (type == TYPE_INT) { + uniform2.hint_range[2] = 1; + } else { + uniform2.hint_range[2] = 0.001; + } + } + + if (tk.type != TK_PARENTHESIS_CLOSE) { + _set_error("Expected ','"); + return ERR_PARSE_ERROR; } + } else if (tk.type == TK_FILTER_LINEAR) { + uniform2.filter = FILTER_LINEAR; + } else if (tk.type == TK_FILTER_NEAREST) { + uniform2.filter = FILTER_NEAREST; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP) { + uniform2.filter = FILTER_NEAREST_MIPMAP; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP) { + uniform2.filter = FILTER_LINEAR_MIPMAP; + } else if (tk.type == TK_FILTER_NEAREST_MIPMAP_ANISO) { + uniform2.filter = FILTER_NEAREST_MIPMAP_ANISO; + } else if (tk.type == TK_FILTER_LINEAR_MIPMAP_ANISO) { + uniform2.filter = FILTER_LINEAR_MIPMAP_ANISO; + } else if (tk.type == TK_REPEAT_DISABLE) { + uniform2.repeat = REPEAT_DISABLE; + } else if (tk.type == TK_REPEAT_ENABLE) { + uniform2.repeat = REPEAT_ENABLE; + } else { + _set_error("Expected valid type hint after ':'."); } - if (tk.type != TK_PARENTHESIS_CLOSE) { - _set_error("Expected ','"); + if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { + _set_error("This hint is only for sampler types"); return ERR_PARSE_ERROR; } - } else { - _set_error("Expected valid type hint after ':'."); - } - - if (uniform2.hint != ShaderNode::Uniform::HINT_RANGE && uniform2.hint != ShaderNode::Uniform::HINT_NONE && uniform2.hint != ShaderNode::Uniform::HINT_COLOR && type <= TYPE_MAT4) { - _set_error("This hint is only for sampler types"); - return ERR_PARSE_ERROR; - } + tk = _get_token(); - tk = _get_token(); + } while (tk.type == TK_COMMA); } if (tk.type == TK_OP_ASSIGN) { @@ -5293,6 +5610,10 @@ Error ShaderLanguage::_parse_shader(const Map<StringName, FunctionInfo> &p_funct arg.name = pname; arg.precision = pprecision; arg.qualifier = qualifier; + arg.tex_argument_check = false; + arg.tex_builtin_check = false; + arg.tex_argument_filter = FILTER_DEFAULT; + arg.tex_argument_repeat = REPEAT_DEFAULT; func_node->arguments.push_back(arg); |