diff options
32 files changed, 548 insertions, 291 deletions
diff --git a/doc/classes/RenderingServer.xml b/doc/classes/RenderingServer.xml index 6199c7b4e6..c2752d9f7b 100644 --- a/doc/classes/RenderingServer.xml +++ b/doc/classes/RenderingServer.xml @@ -2748,6 +2748,13 @@ Returns the parameters of a shader. </description> </method> + <method name="shader_set_code"> + <return type="void" /> + <argument index="0" name="shader" type="RID" /> + <argument index="1" name="code" type="String" /> + <description> + </description> + </method> <method name="shader_set_default_texture_param"> <return type="void" /> <argument index="0" name="shader" type="RID" /> @@ -2759,6 +2766,13 @@ [b]Note:[/b] If the sampler array is used use [code]index[/code] to access the specified texture. </description> </method> + <method name="shader_set_path_hint"> + <return type="void" /> + <argument index="0" name="shader" type="RID" /> + <argument index="1" name="path" type="String" /> + <description> + </description> + </method> <method name="skeleton_allocate_data"> <return type="void" /> <argument index="0" name="skeleton" type="RID" /> diff --git a/drivers/gles3/storage/material_storage.cpp b/drivers/gles3/storage/material_storage.cpp index 8a8d79b3f7..ab92fe0d1a 100644 --- a/drivers/gles3/storage/material_storage.cpp +++ b/drivers/gles3/storage/material_storage.cpp @@ -2462,6 +2462,13 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { } } +void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) { + GLES3::Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + shader->path_hint = p_path; +} + String MaterialStorage::shader_get_code(RID p_shader) const { const GLES3::Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, String()); diff --git a/drivers/gles3/storage/material_storage.h b/drivers/gles3/storage/material_storage.h index 6ad277c2b9..0e96541250 100644 --- a/drivers/gles3/storage/material_storage.h +++ b/drivers/gles3/storage/material_storage.h @@ -73,6 +73,7 @@ struct Material; struct Shader { ShaderData *data = nullptr; String code; + String path_hint; RS::ShaderMode mode; HashMap<StringName, HashMap<int, RID>> default_texture_parameter; HashSet<Material *> owners; @@ -542,6 +543,7 @@ public: virtual void shader_free(RID p_rid) override; virtual void shader_set_code(RID p_shader, const String &p_code) override; + virtual void shader_set_path_hint(RID p_shader, const String &p_path) override; virtual String shader_get_code(RID p_shader) const override; virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; diff --git a/editor/plugins/shader_editor_plugin.cpp b/editor/plugins/shader_editor_plugin.cpp index 9a20b0087e..e7e7751f3b 100644 --- a/editor/plugins/shader_editor_plugin.cpp +++ b/editor/plugins/shader_editor_plugin.cpp @@ -45,6 +45,7 @@ #include "editor/shader_create_dialog.h" #include "scene/gui/split_container.h" #include "servers/display_server.h" +#include "servers/rendering/shader_preprocessor.h" #include "servers/rendering/shader_types.h" /*** SHADER SCRIPT EDITOR ****/ @@ -84,24 +85,47 @@ void ShaderTextEditor::set_edited_shader(const Ref<Shader> &p_shader, const Stri if (shader == p_shader) { return; } + if (shader.is_valid()) { + shader->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } shader = p_shader; shader_inc = Ref<ShaderInclude>(); set_edited_code(p_code); + + if (shader.is_valid()) { + shader->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } } void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc) { set_edited_shader_include(p_shader_inc, p_shader_inc->get_code()); } +void ShaderTextEditor::_shader_changed() { + // This function is used for dependencies (include changing changes main shader and forces it to revalidate) + if (block_shader_changed) { + return; + } + dependencies_version++; + _validate_script(); +} + void ShaderTextEditor::set_edited_shader_include(const Ref<ShaderInclude> &p_shader_inc, const String &p_code) { if (shader_inc == p_shader_inc) { return; } + if (shader_inc.is_valid()) { + shader_inc->disconnect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } shader_inc = p_shader_inc; shader = Ref<Shader>(); set_edited_code(p_code); + + if (shader_inc.is_valid()) { + shader_inc->connect(SNAME("changed"), callable_mp(this, &ShaderTextEditor::_shader_changed)); + } } void ShaderTextEditor::set_edited_code(const String &p_code) { @@ -174,7 +198,7 @@ void ShaderTextEditor::_load_theme_settings() { } List<String> pp_keywords; - ShaderLanguage::get_preprocessor_keyword_list(&pp_keywords, false); + ShaderPreprocessor::get_keyword_list(&pp_keywords, false); for (const String &E : pp_keywords) { syntax_highlighter->add_keyword_color(E, keyword_color); @@ -255,7 +279,9 @@ void ShaderTextEditor::_check_shader_mode() { } if (shader->get_mode() != mode) { + set_block_shader_changed(true); shader->set_code(get_text_editor()->get_text()); + set_block_shader_changed(false); _load_theme_settings(); } } @@ -265,7 +291,47 @@ static ShaderLanguage::DataType _get_global_variable_type(const StringName &p_va return (ShaderLanguage::DataType)RS::global_variable_type_get_shader_datatype(gvt); } +static String complete_from_path; + +static void _complete_include_paths_search(EditorFileSystemDirectory *p_efsd, List<ScriptLanguage::CodeCompletionOption> *r_options) { + if (!p_efsd) { + return; + } + for (int i = 0; i < p_efsd->get_file_count(); i++) { + if (p_efsd->get_file_type(i) == SNAME("ShaderInclude")) { + String path = p_efsd->get_file_path(i); + if (path.begins_with(complete_from_path)) { + path = path.replace_first(complete_from_path, ""); + } + r_options->push_back(ScriptLanguage::CodeCompletionOption(path, ScriptLanguage::CODE_COMPLETION_KIND_FILE_PATH)); + } + } + for (int j = 0; j < p_efsd->get_subdir_count(); j++) { + _complete_include_paths_search(p_efsd->get_subdir(j), r_options); + } +} + +static void _complete_include_paths(List<ScriptLanguage::CodeCompletionOption> *r_options) { + _complete_include_paths_search(EditorFileSystem::get_singleton()->get_filesystem(), r_options); +} + void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) { + List<ScriptLanguage::CodeCompletionOption> pp_options; + ShaderPreprocessor preprocessor; + String code; + complete_from_path = (shader.is_valid() ? shader->get_path() : shader_inc->get_path()).get_base_dir(); + if (!complete_from_path.ends_with("/")) { + complete_from_path += "/"; + } + preprocessor.preprocess(p_code, code, nullptr, nullptr, nullptr, &pp_options, _complete_include_paths); + complete_from_path = String(); + if (pp_options.size()) { + for (const ScriptLanguage::CodeCompletionOption &E : pp_options) { + r_options->push_back(E); + } + return; + } + ShaderLanguage sl; String calltip; ShaderLanguage::ShaderCompileInfo info; @@ -289,6 +355,8 @@ void ShaderTextEditor::_code_complete_script(const String &p_code, List<ScriptLa } void ShaderTextEditor::_validate_script() { + emit_signal(SNAME("script_changed")); // Ensure to notify that it changed, so it is applied + String code; if (shader.is_valid()) { @@ -298,72 +366,117 @@ void ShaderTextEditor::_validate_script() { code = shader_inc->get_code(); } - ShaderLanguage sl; + ShaderPreprocessor preprocessor; + String code_pp; + String error_pp; + List<ShaderPreprocessor::FilePosition> err_positions; + last_compile_result = preprocessor.preprocess(code, code_pp, &error_pp, &err_positions); - sl.enable_warning_checking(saved_warnings_enabled); - uint32_t flags = saved_warning_flags; - if (shader.is_null()) { - if (flags & ShaderWarning::UNUSED_CONSTANT) { - flags &= ~(ShaderWarning::UNUSED_CONSTANT); - } - if (flags & ShaderWarning::UNUSED_FUNCTION) { - flags &= ~(ShaderWarning::UNUSED_FUNCTION); - } - if (flags & ShaderWarning::UNUSED_STRUCT) { - flags &= ~(ShaderWarning::UNUSED_STRUCT); - } - if (flags & ShaderWarning::UNUSED_UNIFORM) { - flags &= ~(ShaderWarning::UNUSED_UNIFORM); - } - if (flags & ShaderWarning::UNUSED_VARYING) { - flags &= ~(ShaderWarning::UNUSED_VARYING); - } - } - sl.set_warning_flags(flags); - - ShaderLanguage::ShaderCompileInfo info; - info.global_variable_type_func = _get_global_variable_type; - - if (shader.is_null()) { - info.is_include = true; - } else { - Shader::Mode mode = shader->get_mode(); - info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); - info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); - info.shader_types = ShaderTypes::get_singleton()->get_types(); + for (int i = 0; i < get_text_editor()->get_line_count(); i++) { + get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); } - - last_compile_result = sl.compile(code, info); + set_error(""); if (last_compile_result != OK) { - String error_text = "error(" + itos(sl.get_error_line()) + "): " + sl.get_error_text(); + //preprocessor error + ERR_FAIL_COND(err_positions.size() == 0); + + String error_text; + int error_line = err_positions.front()->get().line; + if (err_positions.size() == 1) { + // Error in main file + error_text = "error(" + itos(error_line) + "): " + error_text; + } else { + error_text = "error(" + itos(error_line) + ") in include " + err_positions.back()->get().file.get_file() + ":" + itos(err_positions.back()->get().line) + ": " + error_text; + set_error_count(err_positions.size() - 1); + } + set_error(error_text); - set_error_pos(sl.get_error_line() - 1, 0); + set_error_pos(error_line - 1, 0); for (int i = 0; i < get_text_editor()->get_line_count(); i++) { get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); } - get_text_editor()->set_line_background_color(sl.get_error_line() - 1, marked_line_color); + get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); + + set_warning_count(0); + } else { - for (int i = 0; i < get_text_editor()->get_line_count(); i++) { - get_text_editor()->set_line_background_color(i, Color(0, 0, 0, 0)); + ShaderLanguage sl; + + sl.enable_warning_checking(saved_warnings_enabled); + uint32_t flags = saved_warning_flags; + if (shader.is_null()) { + if (flags & ShaderWarning::UNUSED_CONSTANT) { + flags &= ~(ShaderWarning::UNUSED_CONSTANT); + } + if (flags & ShaderWarning::UNUSED_FUNCTION) { + flags &= ~(ShaderWarning::UNUSED_FUNCTION); + } + if (flags & ShaderWarning::UNUSED_STRUCT) { + flags &= ~(ShaderWarning::UNUSED_STRUCT); + } + if (flags & ShaderWarning::UNUSED_UNIFORM) { + flags &= ~(ShaderWarning::UNUSED_UNIFORM); + } + if (flags & ShaderWarning::UNUSED_VARYING) { + flags &= ~(ShaderWarning::UNUSED_VARYING); + } } - set_error(""); - } + sl.set_warning_flags(flags); - if (warnings.size() > 0 || last_compile_result != OK) { - warnings_panel->clear(); - } - warnings.clear(); - for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) { - warnings.push_back(E->get()); - } - if (warnings.size() > 0 && last_compile_result == OK) { - warnings.sort_custom<WarningsComparator>(); - _update_warning_panel(); - } else { - set_warning_count(0); + ShaderLanguage::ShaderCompileInfo info; + info.global_variable_type_func = _get_global_variable_type; + + if (shader.is_null()) { + info.is_include = true; + } else { + Shader::Mode mode = shader->get_mode(); + info.functions = ShaderTypes::get_singleton()->get_functions(RenderingServer::ShaderMode(mode)); + info.render_modes = ShaderTypes::get_singleton()->get_modes(RenderingServer::ShaderMode(mode)); + info.shader_types = ShaderTypes::get_singleton()->get_types(); + } + + code = code_pp; + //compiler error + last_compile_result = sl.compile(code, info); + + if (last_compile_result != OK) { + String error_text; + int error_line; + Vector<ShaderLanguage::FilePosition> include_positions = sl.get_include_positions(); + if (include_positions.size() > 1) { + //error is in an include + error_line = include_positions[0].line; + error_text = "error(" + itos(error_line) + ") in include " + include_positions[include_positions.size() - 1].file + ":" + itos(include_positions[include_positions.size() - 1].line) + ": " + sl.get_error_text(); + set_error_count(include_positions.size() - 1); + } else { + error_line = sl.get_error_line(); + error_text = "error(" + itos(error_line) + "): " + sl.get_error_text(); + set_error_count(0); + } + set_error(error_text); + set_error_pos(error_line - 1, 0); + get_text_editor()->set_line_background_color(error_line - 1, marked_line_color); + } else { + set_error(""); + } + + if (warnings.size() > 0 || last_compile_result != OK) { + warnings_panel->clear(); + } + warnings.clear(); + for (List<ShaderWarning>::Element *E = sl.get_warnings_ptr(); E; E = E->next()) { + warnings.push_back(E->get()); + } + if (warnings.size() > 0 && last_compile_result == OK) { + warnings.sort_custom<WarningsComparator>(); + _update_warning_panel(); + } else { + set_warning_count(0); + } } - emit_signal(SNAME("script_changed")); + + emit_signal(SNAME("script_validated"), last_compile_result == OK); // Notify that validation finished, to update the list of scripts } void ShaderTextEditor::_update_warning_panel() { @@ -410,6 +523,7 @@ void ShaderTextEditor::_update_warning_panel() { } void ShaderTextEditor::_bind_methods() { + ADD_SIGNAL(MethodInfo("script_validated", PropertyInfo(Variant::BOOL, "valid"))); } ShaderTextEditor::ShaderTextEditor() { @@ -545,6 +659,8 @@ void ShaderEditor::_warning_clicked(Variant p_line) { void ShaderEditor::_bind_methods() { ClassDB::bind_method("_show_warnings_panel", &ShaderEditor::_show_warnings_panel); ClassDB::bind_method("_warning_clicked", &ShaderEditor::_warning_clicked); + + ADD_SIGNAL(MethodInfo("validation_changed")); } void ShaderEditor::ensure_select_current() { @@ -626,7 +742,9 @@ void ShaderEditor::_reload_shader_from_disk() { Ref<Shader> rel_shader = ResourceLoader::load(shader->get_path(), shader->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); ERR_FAIL_COND(!rel_shader.is_valid()); + shader_editor->set_block_shader_changed(true); shader->set_code(rel_shader->get_code()); + shader_editor->set_block_shader_changed(false); shader->set_last_modified_time(rel_shader->get_last_modified_time()); shader_editor->reload_text(); } @@ -635,7 +753,9 @@ void ShaderEditor::_reload_shader_include_from_disk() { Ref<ShaderInclude> rel_shader_include = ResourceLoader::load(shader_inc->get_path(), shader_inc->get_class(), ResourceFormatLoader::CACHE_MODE_IGNORE); ERR_FAIL_COND(!rel_shader_include.is_valid()); + shader_editor->set_block_shader_changed(true); shader_inc->set_code(rel_shader_include->get_code()); + shader_editor->set_block_shader_changed(false); shader_inc->set_last_modified_time(rel_shader_include->get_last_modified_time()); shader_editor->reload_text(); } @@ -713,18 +833,24 @@ void ShaderEditor::apply_shaders() { String editor_code = shader_editor->get_text_editor()->get_text(); if (shader.is_valid()) { String shader_code = shader->get_code(); - if (shader_code != editor_code) { + if (shader_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { + shader_editor->set_block_shader_changed(true); shader->set_code(editor_code); + shader_editor->set_block_shader_changed(false); shader->set_edited(true); } } if (shader_inc.is_valid()) { String shader_inc_code = shader_inc->get_code(); - if (shader_inc_code != editor_code) { + if (shader_inc_code != editor_code || dependencies_version != shader_editor->get_dependencies_version()) { + shader_editor->set_block_shader_changed(true); shader_inc->set_code(editor_code); + shader_editor->set_block_shader_changed(false); shader_inc->set_edited(true); } } + + dependencies_version = shader_editor->get_dependencies_version(); } void ShaderEditor::_text_edit_gui_input(const Ref<InputEvent> &ev) { @@ -838,6 +964,8 @@ ShaderEditor::ShaderEditor() { shader_editor = memnew(ShaderTextEditor); + shader_editor->connect("script_validated", callable_mp(this, &ShaderEditor::_script_validated)); + shader_editor->set_v_size_flags(SIZE_EXPAND_FILL); shader_editor->add_theme_constant_override("separation", 0); shader_editor->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); @@ -1023,6 +1151,21 @@ void ShaderEditorPlugin::_update_shader_list() { for (int i = 1; i < FILE_MAX; i++) { file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), edited_shaders.size() == 0); } + + _update_shader_list_status(); +} + +void ShaderEditorPlugin::_update_shader_list_status() { + for (int i = 0; i < shader_list->get_item_count(); i++) { + ShaderEditor *se = Object::cast_to<ShaderEditor>(shader_tabs->get_tab_control(i)); + if (se) { + if (se->was_compilation_successful()) { + shader_list->set_item_tag_icon(i, Ref<Texture2D>()); + } else { + shader_list->set_item_tag_icon(i, shader_list->get_theme_icon(SNAME("Error"), SNAME("EditorIcons"))); + } + } + } } void ShaderEditorPlugin::edit(Object *p_object) { @@ -1041,6 +1184,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader_editor = memnew(ShaderEditor); es.shader_editor->edit(si); shader_tabs->add_child(es.shader_editor); + es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list_status)); } else { Shader *s = Object::cast_to<Shader>(p_object); for (uint32_t i = 0; i < edited_shaders.size(); i++) { @@ -1060,6 +1204,7 @@ void ShaderEditorPlugin::edit(Object *p_object) { es.shader_editor = memnew(ShaderEditor); es.shader_editor->edit(s); shader_tabs->add_child(es.shader_editor); + es.shader_editor->connect("validation_changed", callable_mp(this, &ShaderEditorPlugin::_update_shader_list_status)); } } @@ -1233,7 +1378,7 @@ ShaderEditorPlugin::ShaderEditorPlugin() { file_menu->get_popup()->connect("id_pressed", callable_mp(this, &ShaderEditorPlugin::_menu_item_pressed)); file_hb->add_child(file_menu); - for (int i = 1; i < FILE_MAX; i++) { + for (int i = 2; i < FILE_MAX; i++) { file_menu->get_popup()->set_item_disabled(file_menu->get_popup()->get_item_index(i), true); } diff --git a/editor/plugins/shader_editor_plugin.h b/editor/plugins/shader_editor_plugin.h index c9cd75e451..221822e8f2 100644 --- a/editor/plugins/shader_editor_plugin.h +++ b/editor/plugins/shader_editor_plugin.h @@ -67,6 +67,11 @@ class ShaderTextEditor : public CodeTextEditor { void _check_shader_mode(); void _update_warning_panel(); + bool block_shader_changed = false; + void _shader_changed(); + + uint32_t dependencies_version = 0; // Incremented if deps changed + protected: void _notification(int p_what); static void _bind_methods(); @@ -75,6 +80,9 @@ protected: virtual void _code_complete_script(const String &p_code, List<ScriptLanguage::CodeCompletionOption> *r_options) override; public: + void set_block_shader_changed(bool p_block) { block_shader_changed = p_block; } + uint32_t get_dependencies_version() const { return dependencies_version; } + virtual void _validate_script() override; void reload_text(); @@ -135,6 +143,7 @@ class ShaderEditor : public PanelContainer { ConfirmationDialog *disk_changed = nullptr; ShaderTextEditor *shader_editor = nullptr; + bool compilation_success = true; void _menu_option(int p_option); mutable Ref<Shader> shader; @@ -151,6 +160,13 @@ class ShaderEditor : public PanelContainer { void _warning_clicked(Variant p_line); void _update_warnings(bool p_validate); + void _script_validated(bool p_valid) { + compilation_success = p_valid; + emit_signal(SNAME("validation_changed")); + } + + uint32_t dependencies_version = 0xFFFFFFFF; + protected: void _notification(int p_what); static void _bind_methods(); @@ -161,6 +177,7 @@ protected: void _bookmark_item_pressed(int p_idx); public: + bool was_compilation_successful() const { return compilation_success; } void apply_shaders(); void ensure_select_current(); void edit(const Ref<Shader> &p_shader); @@ -215,6 +232,7 @@ class ShaderEditorPlugin : public EditorPlugin { void _shader_created(Ref<Shader> p_shader); void _shader_include_created(Ref<ShaderInclude> p_shader_inc); + void _update_shader_list_status(); public: virtual String get_name() const override { return "Shader"; } diff --git a/scene/resources/shader.cpp b/scene/resources/shader.cpp index e7f8e6ae11..16117986fe 100644 --- a/scene/resources/shader.cpp +++ b/scene/resources/shader.cpp @@ -33,6 +33,7 @@ #include "core/io/file_access.h" #include "scene/scene_string_names.h" #include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" #include "servers/rendering_server.h" #include "texture.h" @@ -47,21 +48,17 @@ void Shader::_dependency_changed() { emit_changed(); } -void Shader::set_code(const String &p_code) { - HashSet<Ref<ShaderInclude>> new_include_dependencies; +void Shader::set_path(const String &p_path, bool p_take_over) { + Resource::set_path(p_path, p_take_over); + RS::get_singleton()->shader_set_path_hint(shader, p_path); +} +void Shader::set_code(const String &p_code) { for (Ref<ShaderInclude> E : include_dependencies) { E->disconnect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); } - String type = ShaderLanguage::get_shader_type_and_dependencies(p_code, &new_include_dependencies); - - // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) - include_dependencies = new_include_dependencies; - - for (Ref<ShaderInclude> E : include_dependencies) { - E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); - } + String type = ShaderLanguage::get_shader_type(p_code); if (type == "canvas_item") { mode = MODE_CANVAS_ITEM; @@ -75,7 +72,27 @@ void Shader::set_code(const String &p_code) { mode = MODE_SPATIAL; } - RenderingServer::get_singleton()->shader_set_code(shader, p_code); + code = p_code; + String pp_code = p_code; + + HashSet<Ref<ShaderInclude>> new_include_dependencies; + + { + // Preprocessor must run here and not in the server because: + // 1) Need to keep track of include dependencies at resource level + // 2) Server does not do interaction with Resource filetypes, this is a scene level feature. + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_include_dependencies); + } + + // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) + include_dependencies = new_include_dependencies; + + for (Ref<ShaderInclude> E : include_dependencies) { + E->connect(SNAME("changed"), callable_mp(this, &Shader::_dependency_changed)); + } + + RenderingServer::get_singleton()->shader_set_code(shader, pp_code); params_cache_dirty = true; emit_changed(); @@ -83,7 +100,7 @@ void Shader::set_code(const String &p_code) { String Shader::get_code() const { _update_shader(); - return RenderingServer::get_singleton()->shader_get_code(shader); + return code; } void Shader::get_param_list(List<PropertyInfo> *p_params) const { diff --git a/scene/resources/shader.h b/scene/resources/shader.h index dc3fe8e619..5de8ad5518 100644 --- a/scene/resources/shader.h +++ b/scene/resources/shader.h @@ -55,6 +55,7 @@ private: RID shader; Mode mode = MODE_SPATIAL; HashSet<Ref<ShaderInclude>> include_dependencies; + String code; // hack the name of performance // shaders keep a list of ShaderMaterial -> RenderingServer name translations, to make @@ -72,6 +73,8 @@ public: //void set_mode(Mode p_mode); virtual Mode get_mode() const; + virtual void set_path(const String &p_path, bool p_take_over = false) override; + void set_code(const String &p_code); String get_code() const; diff --git a/scene/resources/shader_include.cpp b/scene/resources/shader_include.cpp index 2d4f18534d..b819128af3 100644 --- a/scene/resources/shader_include.cpp +++ b/scene/resources/shader_include.cpp @@ -30,6 +30,7 @@ #include "shader_include.h" #include "servers/rendering/shader_language.h" +#include "servers/rendering/shader_preprocessor.h" void ShaderInclude::_dependency_changed() { emit_changed(); @@ -43,7 +44,11 @@ void ShaderInclude::set_code(const String &p_code) { E->disconnect(SNAME("changed"), callable_mp(this, &ShaderInclude::_dependency_changed)); } - ShaderLanguage::get_shader_dependencies(p_code, &new_dependencies); + { + String pp_code; + ShaderPreprocessor preprocessor; + preprocessor.preprocess(p_code, pp_code, nullptr, nullptr, &new_dependencies); + } // This ensures previous include resources are not freed and then re-loaded during parse (which would make compiling slower) dependencies = new_dependencies; diff --git a/servers/rendering/dummy/storage/material_storage.h b/servers/rendering/dummy/storage/material_storage.h index d4809f81e3..5d1f64991b 100644 --- a/servers/rendering/dummy/storage/material_storage.h +++ b/servers/rendering/dummy/storage/material_storage.h @@ -63,6 +63,8 @@ public: virtual void shader_free(RID p_rid) override{}; virtual void shader_set_code(RID p_shader, const String &p_code) override {} + virtual void shader_set_path_hint(RID p_shader, const String &p_code) override {} + virtual String shader_get_code(RID p_shader) const override { return ""; } virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override {} diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp index 1951bfe915..a559241846 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.cpp @@ -37,6 +37,10 @@ using namespace RendererSceneRenderImplementation; +void SceneShaderForwardClustered::ShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void SceneShaderForwardClustered::ShaderData::set_code(const String &p_code) { //compile diff --git a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h index 1cfe723174..05dabc1c56 100644 --- a/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/scene_shader_forward_clustered.h @@ -180,6 +180,7 @@ public: uint32_t index = 0; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_path); virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp index dd00dc2bf9..afe4eac0b3 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.cpp @@ -39,6 +39,10 @@ using namespace RendererSceneRenderImplementation; /* ShaderData */ +void SceneShaderForwardMobile::ShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void SceneShaderForwardMobile::ShaderData::set_code(const String &p_code) { //compile diff --git a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h index 88c2143b09..651155932a 100644 --- a/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/scene_shader_forward_mobile.h @@ -139,6 +139,8 @@ public: uint32_t index = 0; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_path); + virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 7d55be1216..b8ad4e4511 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1968,6 +1968,10 @@ void RendererCanvasRenderRD::occluder_polygon_set_cull_mode(RID p_occluder, RS:: oc->cull_mode = p_mode; } +void RendererCanvasRenderRD::CanvasShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void RendererCanvasRenderRD::CanvasShaderData::set_code(const String &p_code) { //compile diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h index 2ab5a7c831..c0e6b27587 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.h @@ -178,6 +178,7 @@ class RendererCanvasRenderRD : public RendererCanvasRender { bool uses_time = false; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_path); virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 5ede2e761d..6d680be253 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -3978,6 +3978,10 @@ void RendererSceneRenderRD::_setup_decals(const PagedArray<RID> &p_decals, const //////////////////////////////////////////////////////////////////////////////// // FOG SHADER +void RendererSceneRenderRD::FogShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void RendererSceneRenderRD::FogShaderData::set_code(const String &p_code) { //compile diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index d11bbd183e..6acad3f0b7 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -947,6 +947,7 @@ private: bool uses_time = false; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp index 73175d3cf3..689fbba885 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.cpp @@ -42,6 +42,10 @@ //////////////////////////////////////////////////////////////////////////////// // SKY SHADER +void RendererSceneSkyRD::SkyShaderData::set_path_hint(const String &p_path) { + path = p_path; +} + void RendererSceneSkyRD::SkyShaderData::set_code(const String &p_code) { //compile diff --git a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h index a8ee406abc..4376fe4c5b 100644 --- a/servers/rendering/renderer_rd/renderer_scene_sky_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_sky_rd.h @@ -128,6 +128,7 @@ private: bool uses_light = false; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index fcd25852eb..af2f5aafed 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -2341,6 +2341,7 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { } if (shader->data) { + shader->data->set_path_hint(shader->path_hint); shader->data->set_code(p_code); } @@ -2351,6 +2352,16 @@ void MaterialStorage::shader_set_code(RID p_shader, const String &p_code) { } } +void MaterialStorage::shader_set_path_hint(RID p_shader, const String &p_path) { + Shader *shader = shader_owner.get_or_null(p_shader); + ERR_FAIL_COND(!shader); + + shader->path_hint = p_path; + if (shader->data) { + shader->data->set_path_hint(p_path); + } +} + String MaterialStorage::shader_get_code(RID p_shader) const { Shader *shader = shader_owner.get_or_null(p_shader); ERR_FAIL_COND_V(!shader, String()); diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index e35d5e7669..b083968e5f 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -57,6 +57,7 @@ enum ShaderType { struct ShaderData { virtual void set_code(const String &p_Code) = 0; + virtual void set_path_hint(const String &p_hint) = 0; virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index) = 0; virtual void get_param_list(List<PropertyInfo> *p_param_list) const = 0; @@ -77,6 +78,7 @@ struct Material; struct Shader { ShaderData *data = nullptr; String code; + String path_hint; ShaderType type; HashMap<StringName, HashMap<int, RID>> default_texture_parameter; HashSet<Material *> owners; @@ -364,6 +366,7 @@ public: virtual void shader_free(RID p_rid) override; virtual void shader_set_code(RID p_shader, const String &p_code) override; + virtual void shader_set_path_hint(RID p_shader, const String &p_path) override; virtual String shader_get_code(RID p_shader) const override; virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const override; diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 5200e0d318..ea4bd6ab0b 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -1512,6 +1512,9 @@ bool ParticlesStorage::particles_is_inactive(RID p_particles) const { /* Particles SHADER */ +void ParticlesStorage::ParticlesShaderData::set_path_hint(const String &p_path) { + path = p_path; +} void ParticlesStorage::ParticlesShaderData::set_code(const String &p_code) { ParticlesStorage *particles_storage = ParticlesStorage::get_singleton(); //compile diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index 70ac6f0349..f3a4e97fa7 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -363,6 +363,7 @@ private: uint32_t userdata_count = 0; virtual void set_code(const String &p_Code); + virtual void set_path_hint(const String &p_hint); virtual void set_default_texture_param(const StringName &p_name, RID p_texture, int p_index); virtual void get_param_list(List<PropertyInfo> *p_param_list) const; virtual void get_instance_param_list(List<RendererMaterialStorage::InstanceShaderParam> *p_param_list) const; diff --git a/servers/rendering/rendering_server_default.h b/servers/rendering/rendering_server_default.h index 429b8a06e2..4e41a87a2c 100644 --- a/servers/rendering/rendering_server_default.h +++ b/servers/rendering/rendering_server_default.h @@ -224,6 +224,7 @@ public: FUNCRIDSPLIT(shader) FUNC2(shader_set_code, RID, const String &) + FUNC2(shader_set_path_hint, RID, const String &) FUNC1RC(String, shader_get_code, RID) FUNC2SC(shader_get_param_list, RID, List<PropertyInfo> *) diff --git a/servers/rendering/shader_compiler.cpp b/servers/rendering/shader_compiler.cpp index 463b67033d..e291246d01 100644 --- a/servers/rendering/shader_compiler.cpp +++ b/servers/rendering/shader_compiler.cpp @@ -1323,18 +1323,76 @@ Error ShaderCompiler::compile(RS::ShaderMode p_mode, const String &p_code, Ident Error err = parser.compile(p_code, info); if (err != OK) { + Vector<ShaderLanguage::FilePosition> include_positions = parser.get_include_positions(); + + String current; + HashMap<String, Vector<String>> includes; + includes[""] = Vector<String>(); + Vector<String> include_stack; Vector<String> shader = p_code.split("\n"); + + // Reconstruct the files. for (int i = 0; i < shader.size(); i++) { - if (i + 1 == parser.get_error_line()) { - // Mark the error line to be visible without having to look at - // the trace at the end. - print_line(vformat("E%4d-> %s", i + 1, shader[i])); + String l = shader[i]; + if (l.begins_with("@@>")) { + String inc_path = l.replace_first("@@>", ""); + + l = "#include \"" + inc_path + "\""; + includes[current].append("#include \"" + inc_path + "\""); // Restore the include directive + include_stack.push_back(current); + current = inc_path; + includes[inc_path] = Vector<String>(); + + } else if (l.begins_with("@@<")) { + if (include_stack.size()) { + current = include_stack[include_stack.size() - 1]; + include_stack.resize(include_stack.size() - 1); + } + } else { + includes[current].push_back(l); + } + } + + // Print the files. + for (const KeyValue<String, Vector<String>> &E : includes) { + if (E.key.is_empty()) { + if (p_path == "") { + print_line("--Main Shader--"); + } else { + print_line("--" + p_path + "--"); + } } else { - print_line(vformat("%5d | %s", i + 1, shader[i])); + print_line("--" + E.key + "--"); } + int err_line = -1; + for (int i = 0; i < include_positions.size(); i++) { + if (include_positions[i].file == E.key) { + err_line = include_positions[i].line; + } + } + const Vector<String> &V = E.value; + for (int i = 0; i < V.size(); i++) { + if (i == err_line - 1) { + // Mark the error line to be visible without having to look at + // the trace at the end. + print_line(vformat("E%4d-> %s", i + 1, V[i])); + } else { + print_line(vformat("%5d | %s", i + 1, V[i])); + } + } + } + + String file; + int line; + if (include_positions.size() > 1) { + file = include_positions[include_positions.size() - 1].file; + line = include_positions[include_positions.size() - 1].line; + } else { + file = p_path; + line = parser.get_error_line(); } - _err_print_error(nullptr, p_path.utf8().get_data(), parser.get_error_line(), parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER); + _err_print_error(nullptr, file.utf8().get_data(), line, parser.get_error_text().utf8().get_data(), false, ERR_HANDLER_SHADER); return err; } diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index f4de32d14b..d2b80fb277 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -32,8 +32,8 @@ #include "core/os/os.h" #include "core/string/print_string.h" +#include "core/templates/local_vector.h" #include "servers/rendering_server.h" -#include "shader_preprocessor.h" #define HAS_WARNING(flag) (warning_flags & flag) @@ -575,6 +575,37 @@ ShaderLanguage::Token ShaderLanguage::_get_token() { return _make_token(TK_OP_MOD); } break; + case '@': { + if (GETCHAR(0) == '@' && GETCHAR(1) == '>') { + char_idx += 2; + + LocalVector<char32_t> incp; + while (GETCHAR(0) != '\n') { + incp.push_back(GETCHAR(0)); + char_idx++; + } + incp.push_back(0); // Zero end it. + String include_path(incp.ptr()); + include_positions.write[include_positions.size() - 1].line = tk_line; + FilePosition fp; + fp.file = include_path; + fp.line = 0; + tk_line = 0; + include_positions.push_back(fp); + + } else if (GETCHAR(0) == '@' && GETCHAR(1) == '<') { + if (include_positions.size() == 1) { + return _make_token(TK_ERROR, "Invalid include exit hint @@< without matching enter hint."); + } + char_idx += 2; + + include_positions.resize(include_positions.size() - 1); // Pop back. + tk_line = include_positions[include_positions.size() - 1].line; // Restore line. + + } else { + return _make_token(TK_ERROR, "Invalid include enter/exit hint token (@@> and @@<)"); + } + } break; default: { char_idx--; //go back one, since we have no idea what this is @@ -1123,6 +1154,9 @@ void ShaderLanguage::clear() { completion_base = TYPE_VOID; completion_base_array = false; + include_positions.clear(); + include_positions.push_back(FilePosition()); + #ifdef DEBUG_ENABLED keyword_completion_context = CF_GLOBAL_SPACE; used_constants.clear(); @@ -4119,10 +4153,6 @@ void ShaderLanguage::get_keyword_list(List<String> *r_keywords) { } } -void ShaderLanguage::get_preprocessor_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords) { - ShaderPreprocessor::get_keyword_list(r_keywords, p_include_shader_keywords); -} - bool ShaderLanguage::is_control_flow_keyword(String p_keyword) { return p_keyword == "break" || p_keyword == "case" || @@ -7682,27 +7712,6 @@ Error ShaderLanguage::_validate_precision(DataType p_type, DataPrecision p_preci return OK; } -Error ShaderLanguage::_preprocess_shader(const String &p_code, String &r_result, int *r_completion_type) { - Error error = OK; - - ShaderPreprocessor processor(p_code); - processor.preprocess(r_result); - - ShaderPreprocessor::State *state = processor.get_state(); - if (!state->error.is_empty()) { - error_line = state->error_line; - error_set = true; - error_str = state->error; - error = FAILED; - } - - if (r_completion_type != nullptr) { - *r_completion_type = (int)state->completion_type; - } - - return error; -} - Error ShaderLanguage::_parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types, bool p_is_include) { Token tk; TkPos prev_pos; @@ -9500,97 +9509,6 @@ String ShaderLanguage::get_shader_type(const String &p_code) { return String(); } -void ShaderLanguage::get_shader_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies) { - bool reading_inc = false; - String cur_identifier; - - for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) { - if (p_code[i] == ';') { - continue; - - } else if (p_code[i] <= 32) { - if (cur_identifier == "#include") { - reading_inc = true; - cur_identifier = String(); - } else { - if (reading_inc) { - String path = cur_identifier; - if (path.begins_with("\"") && path.ends_with("\"")) { - path = path.substr(1, path.length() - 2); - if (!path.begins_with("res://")) { - path = path.insert(0, "res://"); - } - Ref<ShaderInclude> inc = ResourceLoader::load(path); - if (inc.is_valid()) { - r_dependencies->insert(inc); - } - } - reading_inc = false; - } - } - } else { - cur_identifier += String::chr(p_code[i]); - } - } -} - -String ShaderLanguage::get_shader_type_and_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies) { - bool read_type = true; - bool reading_type = false; - bool reading_inc = false; - String type; - - String cur_identifier; - - for (int i = _get_first_ident_pos(p_code); i < p_code.length(); i++) { - if (p_code[i] == ';') { - continue; - - } else if (p_code[i] <= 32) { - if (!cur_identifier.is_empty()) { - if (read_type) { - if (!reading_type) { - if (cur_identifier == "shader_type") { - reading_type = true; - cur_identifier = String(); - } - } else { - type = cur_identifier; - read_type = false; - cur_identifier = String(); - } - } else if (cur_identifier == "#include") { - reading_inc = true; - cur_identifier = String(); - } else { - if (reading_inc) { - String path = cur_identifier; - if (path.begins_with("\"") && path.ends_with("\"")) { - path = path.substr(1, path.length() - 2); - if (!path.begins_with("res://")) { - path = path.insert(0, "res://"); - } - Ref<ShaderInclude> inc = ResourceLoader::load(path); - if (inc.is_valid()) { - r_dependencies->insert(inc); - } - } - reading_inc = false; - } - } - } - } else { - cur_identifier += String::chr(p_code[i]); - } - } - - if (reading_type) { - return type; - } - - return String(); -} - #ifdef DEBUG_ENABLED void ShaderLanguage::_check_warning_accums() { for (const KeyValue<ShaderWarning::Code, HashMap<StringName, HashMap<StringName, Usage>> *> &E : warnings_check_map2) { @@ -9630,21 +9548,15 @@ uint32_t ShaderLanguage::get_warning_flags() const { Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_info) { clear(); - Error err = _preprocess_shader(p_code, code); - if (err != OK) { - return err; - } - - // Clear after preprocessing. Because preprocess uses the resource loader, it means if this instance is held in a singleton, it can have a changed state after. - clear(); - + code = p_code; global_var_get_type_func = p_info.global_variable_type_func; + varying_function_names = p_info.varying_function_names; nodes = nullptr; shader = alloc_node<ShaderNode>(); - err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types, p_info.is_include); + Error err = _parse_shader(p_info.functions, p_info.render_modes, p_info.shader_types, p_info.is_include); #ifdef DEBUG_ENABLED if (check_warnings) { @@ -9661,44 +9573,7 @@ Error ShaderLanguage::compile(const String &p_code, const ShaderCompileInfo &p_i Error ShaderLanguage::complete(const String &p_code, const ShaderCompileInfo &p_info, List<ScriptLanguage::CodeCompletionOption> *r_options, String &r_call_hint) { clear(); - int preprocessor_completion_type; - Error error = _preprocess_shader(p_code, code, &preprocessor_completion_type); - - switch (preprocessor_completion_type) { - case ShaderPreprocessor::COMPLETION_TYPE_DIRECTIVE: { - static List<String> options; - - if (options.is_empty()) { - ShaderPreprocessor::get_keyword_list(&options, true); - } - - for (const String &E : options) { - ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); - r_options->push_back(option); - } - - return OK; - } break; - case ShaderPreprocessor::COMPLETION_TYPE_PRAGMA: { - static List<String> options; - - if (options.is_empty()) { - ShaderPreprocessor::get_pragma_list(&options); - } - - for (const String &E : options) { - ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); - r_options->push_back(option); - } - - return OK; - } break; - } - - if (error != OK) { - return error; - } - + code = p_code; varying_function_names = p_info.varying_function_names; nodes = nullptr; @@ -10231,6 +10106,10 @@ String ShaderLanguage::get_error_text() { return error_str; } +Vector<ShaderLanguage::FilePosition> ShaderLanguage::get_include_positions() { + return include_positions; +} + int ShaderLanguage::get_error_line() { return error_line; } diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index 59d679fd98..4e7283a714 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -777,7 +777,6 @@ public: static uint32_t get_datatype_size(DataType p_type); static void get_keyword_list(List<String> *r_keywords); - static void get_preprocessor_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords); static bool is_control_flow_keyword(String p_keyword); static void get_builtin_funcs(List<String> *r_keywords); @@ -869,6 +868,11 @@ public: typedef DataType (*GlobalVariableGetTypeFunc)(const StringName &p_name); + struct FilePosition { + String file; + int line = 0; + }; + private: struct KeyWord { TokenType token; @@ -886,6 +890,8 @@ private: String error_str; int error_line = 0; + Vector<FilePosition> include_positions; + #ifdef DEBUG_ENABLED struct Usage { int decl_line; @@ -953,6 +959,7 @@ private: error_line = tk_line; error_set = true; error_str = p_str; + include_positions.write[include_positions.size() - 1].line = tk_line; } void _set_expected_error(const String &p_what) { @@ -1072,7 +1079,6 @@ private: String _get_shader_type_list(const HashSet<String> &p_shader_types) const; String _get_qualifier_str(ArgumentQualifier p_qualifier) const; - Error _preprocess_shader(const String &p_code, String &r_result, int *r_completion_type = nullptr); Error _parse_shader(const HashMap<StringName, FunctionInfo> &p_functions, const Vector<ModeInfo> &p_render_modes, const HashSet<String> &p_shader_types, bool p_is_include); Error _find_last_flow_op_in_block(BlockNode *p_block, FlowOperation p_op); @@ -1094,8 +1100,6 @@ public: void clear(); static String get_shader_type(const String &p_code); - static void get_shader_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies); - static String get_shader_type_and_dependencies(const String &p_code, HashSet<Ref<ShaderInclude>> *r_dependencies); struct ShaderCompileInfo { HashMap<StringName, FunctionInfo> functions; @@ -1110,6 +1114,7 @@ public: Error complete(const String &p_code, const ShaderCompileInfo &p_info, List<ScriptLanguage::CodeCompletionOption> *r_options, String &r_call_hint); String get_error_text(); + Vector<FilePosition> get_include_positions(); int get_error_line(); ShaderNode *get_shader(); diff --git a/servers/rendering/shader_preprocessor.cpp b/servers/rendering/shader_preprocessor.cpp index 3890c63e9f..9623d11010 100644 --- a/servers/rendering/shader_preprocessor.cpp +++ b/servers/rendering/shader_preprocessor.cpp @@ -555,6 +555,15 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { p_tokenizer->advance('"'); String path = tokens_to_string(p_tokenizer->advance('"')); + for (int i = 0; i < path.length(); i++) { + if (path[i] == '\n') { + break; //stop parsing + } + if (path[i] == CURSOR) { + state->completion_type = COMPLETION_TYPE_INCLUDE_PATH; + break; + } + } path = path.substr(0, path.length() - 1); p_tokenizer->skip_whitespace(); @@ -569,7 +578,7 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { return; } - Ref<ShaderInclude> shader_inc = Object::cast_to<ShaderInclude>(*res); + Ref<ShaderInclude> shader_inc = res; if (shader_inc.is_null()) { set_error(RTR("Shader include resource type is wrong."), line); return; @@ -584,6 +593,8 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { } } + state->shader_includes.insert(shader_inc); + const String real_path = shader_inc->get_path(); if (state->includes.has(real_path)) { // Already included, skip. @@ -603,18 +614,26 @@ void ShaderPreprocessor::process_include(Tokenizer *p_tokenizer) { String old_include = state->current_include; state->current_include = real_path; - ShaderPreprocessor processor(included); + ShaderPreprocessor processor; int prev_condition_depth = state->condition_depth; state->condition_depth = 0; + FilePosition fp; + fp.file = state->current_include; + fp.line = line; + state->include_positions.push_back(fp); + String result; - processor.preprocess(state, result); + processor.preprocess(state, included, result); + add_to_output("@@>" + real_path + "\n"); // Add token for enter include path add_to_output(result); + add_to_output("\n@@<\n"); // Add token for exit include path // Reset to last include if there are no errors. We want to use this as context. if (state->error.is_empty()) { state->current_include = old_include; + state->include_positions.pop_back(); } else { return; } @@ -855,7 +874,9 @@ void ShaderPreprocessor::add_to_output(const String &p_str) { void ShaderPreprocessor::set_error(const String &p_error, int p_line) { if (state->error.is_empty()) { state->error = p_error; - state->error_line = p_line + 1; + FilePosition fp; + fp.line = p_line + 1; + state->include_positions.push_back(fp); } } @@ -867,18 +888,6 @@ bool ShaderPreprocessor::check_directive_before_type(Tokenizer *p_tokenizer, con return true; } -ShaderPreprocessor::State *ShaderPreprocessor::create_state() { - State *new_state = memnew(State); - - String platform = OS::get_singleton()->get_name().replace(" ", "_").to_upper(); - new_state->defines[platform] = create_define("true"); - - Engine *engine = Engine::get_singleton(); - new_state->defines["EDITOR"] = create_define(engine->is_editor_hint() ? "true" : "false"); - - return new_state; -} - ShaderPreprocessor::Define *ShaderPreprocessor::create_define(const String &p_body) { ShaderPreprocessor::Define *define = memnew(Define); define->body = p_body; @@ -903,18 +912,14 @@ void ShaderPreprocessor::clear() { state = nullptr; } -Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) { +Error ShaderPreprocessor::preprocess(State *p_state, const String &p_code, String &r_result) { clear(); output.clear(); state = p_state; - if (state == nullptr) { - state = create_state(); - state_owner = true; - } - CommentRemover remover(code); + CommentRemover remover(p_code); String stripped = remover.strip(); String error = remover.get_error(); if (!error.is_empty()) { @@ -923,7 +928,7 @@ Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) { } // Track code hashes to prevent cyclic include. - uint64_t code_hash = code.hash64(); + uint64_t code_hash = p_code.hash64(); state->cyclic_include_hashes.push_back(code_hash); Tokenizer p_tokenizer(stripped); @@ -990,12 +995,55 @@ Error ShaderPreprocessor::preprocess(State *p_state, String &r_result) { return OK; } -Error ShaderPreprocessor::preprocess(String &r_result) { - return preprocess(nullptr, r_result); -} +Error ShaderPreprocessor::preprocess(const String &p_code, String &r_result, String *r_error_text, List<FilePosition> *r_error_position, HashSet<Ref<ShaderInclude>> *r_includes, List<ScriptLanguage::CodeCompletionOption> *r_completion_options, IncludeCompletionFunction p_include_completion_func) { + State pp_state; + Error err = preprocess(&pp_state, p_code, r_result); + if (err != OK) { + if (r_error_text) { + *r_error_text = pp_state.error; + } + if (r_error_position) { + *r_error_position = pp_state.include_positions; + } + } + if (r_includes) { + *r_includes = pp_state.shader_includes; + } + + if (r_completion_options) { + switch (pp_state.completion_type) { + case COMPLETION_TYPE_DIRECTIVE: { + List<String> options; + get_keyword_list(&options, true); + + for (const String &E : options) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_completion_options->push_back(option); + } + + } break; + case COMPLETION_TYPE_PRAGMA: { + List<String> options; + + ShaderPreprocessor::get_pragma_list(&options); -ShaderPreprocessor::State *ShaderPreprocessor::get_state() { - return state; + for (const String &E : options) { + ScriptLanguage::CodeCompletionOption option(E, ScriptLanguage::CODE_COMPLETION_KIND_PLAIN_TEXT); + r_completion_options->push_back(option); + } + + } break; + case COMPLETION_TYPE_INCLUDE_PATH: { + if (p_include_completion_func && r_completion_options) { + p_include_completion_func(r_completion_options); + } + + } break; + default: { + } + } + } + return err; } void ShaderPreprocessor::get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords) { @@ -1018,8 +1066,7 @@ void ShaderPreprocessor::get_pragma_list(List<String> *r_pragmas) { r_pragmas->push_back("disable_preprocessor"); } -ShaderPreprocessor::ShaderPreprocessor(const String &p_code) : - code(p_code) { +ShaderPreprocessor::ShaderPreprocessor() { } ShaderPreprocessor::~ShaderPreprocessor() { diff --git a/servers/rendering/shader_preprocessor.h b/servers/rendering/shader_preprocessor.h index 1cbab8e36f..9bac706049 100644 --- a/servers/rendering/shader_preprocessor.h +++ b/servers/rendering/shader_preprocessor.h @@ -44,6 +44,20 @@ #include "scene/resources/shader_include.h" class ShaderPreprocessor { +public: + enum CompletionType { + COMPLETION_TYPE_NONE, + COMPLETION_TYPE_DIRECTIVE, + COMPLETION_TYPE_PRAGMA_DIRECTIVE, + COMPLETION_TYPE_PRAGMA, + COMPLETION_TYPE_INCLUDE_PATH, + }; + + struct FilePosition { + String file; + int line = 0; + }; + private: struct Token { char32_t text; @@ -113,14 +127,6 @@ private: int end_line = -1; }; -public: - enum CompletionType { - COMPLETION_TYPE_NONE, - COMPLETION_TYPE_DIRECTIVE, - COMPLETION_TYPE_PRAGMA_DIRECTIVE, - COMPLETION_TYPE_PRAGMA, - }; - struct State { RBMap<String, Define *> defines; Vector<bool> skip_stack_else; @@ -132,14 +138,14 @@ public: String current_shader_type; int shader_type_pos = -1; String error; - int error_line = -1; + List<FilePosition> include_positions; RBMap<String, Vector<SkippedCondition *>> skipped_conditions; bool disabled = false; CompletionType completion_type = COMPLETION_TYPE_NONE; + HashSet<Ref<ShaderInclude>> shader_includes; }; private: - String code; LocalVector<char32_t> output; State *state = nullptr; bool state_owner = false; @@ -175,21 +181,21 @@ private: void set_error(const String &p_error, int p_line); bool check_directive_before_type(Tokenizer *p_tokenizer, const String &p_directive); - static State *create_state(); static Define *create_define(const String &p_body); void clear(); + Error preprocess(State *p_state, const String &p_code, String &r_result); + public: - Error preprocess(State *p_state, String &r_result); - Error preprocess(String &r_result); + typedef void (*IncludeCompletionFunction)(List<ScriptLanguage::CodeCompletionOption> *); - State *get_state(); + Error preprocess(const String &p_code, String &r_result, String *r_error_text = nullptr, List<FilePosition> *r_error_position = nullptr, HashSet<Ref<ShaderInclude>> *r_includes = nullptr, List<ScriptLanguage::CodeCompletionOption> *r_completion_options = nullptr, IncludeCompletionFunction p_include_completion_func = nullptr); - static void get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords = false); + static void get_keyword_list(List<String> *r_keywords, bool p_include_shader_keywords); static void get_pragma_list(List<String> *r_pragmas); - ShaderPreprocessor(const String &p_code); + ShaderPreprocessor(); ~ShaderPreprocessor(); }; diff --git a/servers/rendering/storage/material_storage.h b/servers/rendering/storage/material_storage.h index 00790106af..4189e792db 100644 --- a/servers/rendering/storage/material_storage.h +++ b/servers/rendering/storage/material_storage.h @@ -61,6 +61,7 @@ public: virtual void shader_free(RID p_rid) = 0; virtual void shader_set_code(RID p_shader, const String &p_code) = 0; + virtual void shader_set_path_hint(RID p_shader, const String &p_path) = 0; virtual String shader_get_code(RID p_shader) const = 0; virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const = 0; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 5ee12d04d9..da02bdc66a 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -1709,6 +1709,8 @@ void RenderingServer::_bind_methods() { /* SHADER */ ClassDB::bind_method(D_METHOD("shader_create"), &RenderingServer::shader_create); + ClassDB::bind_method(D_METHOD("shader_set_code", "shader", "code"), &RenderingServer::shader_set_code); + ClassDB::bind_method(D_METHOD("shader_set_path_hint", "shader", "path"), &RenderingServer::shader_set_path_hint); ClassDB::bind_method(D_METHOD("shader_get_code", "shader"), &RenderingServer::shader_get_code); ClassDB::bind_method(D_METHOD("shader_get_param_list", "shader"), &RenderingServer::_shader_get_param_list); ClassDB::bind_method(D_METHOD("shader_get_param_default", "shader", "param"), &RenderingServer::shader_get_param_default); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 8d224f2832..39490a0346 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -170,6 +170,7 @@ public: virtual RID shader_create() = 0; virtual void shader_set_code(RID p_shader, const String &p_code) = 0; + virtual void shader_set_path_hint(RID p_shader, const String &p_path) = 0; virtual String shader_get_code(RID p_shader) const = 0; virtual void shader_get_param_list(RID p_shader, List<PropertyInfo> *p_param_list) const = 0; virtual Variant shader_get_param_default(RID p_shader, const StringName &p_param) const = 0; |