diff options
456 files changed, 12888 insertions, 7883 deletions
diff --git a/.github/actions/godot-build/action.yml b/.github/actions/godot-build/action.yml index 377480b123..0a0899db78 100644 --- a/.github/actions/godot-build/action.yml +++ b/.github/actions/godot-build/action.yml @@ -31,6 +31,18 @@ runs: SCONS_CACHE_LIMIT: ${{ inputs.scons-cache-limit }} run: | echo "Building with flags:" platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} - if [ "${{ inputs.target }}" != "editor" ]; then rm -rf editor; fi # Ensure we don't include editor code. + + if [ "${{ inputs.target }}" != "editor" ]; then + # Ensure we don't include editor code in export template builds. + rm -rf editor + fi + + if [ "${{ github.event.number }}" != "" ]; then + # Set build identifier with pull request number if available. This is displayed throughout the editor. + export BUILD_NAME="gh-${{ github.event.number }}" + else + export BUILD_NAME="gh" + fi + scons platform=${{ inputs.platform }} target=${{ inputs.target }} tests=${{ inputs.tests }} ${{ env.SCONSFLAGS }} ls -l bin/ diff --git a/COPYRIGHT.txt b/COPYRIGHT.txt index 4b8a246df0..c549761beb 100644 --- a/COPYRIGHT.txt +++ b/COPYRIGHT.txt @@ -483,7 +483,7 @@ License: Expat Files: ./thirdparty/thorvg/ Comment: ThorVG -Copyright: 2020-2023, The ThorVG Project +Copyright: 2020-2024, The ThorVG Project License: Expat Files: ./thirdparty/tinyexr/ diff --git a/core/core_bind.cpp b/core/core_bind.cpp index e3dad09091..e5363f9acc 100644 --- a/core/core_bind.cpp +++ b/core/core_bind.cpp @@ -1375,11 +1375,11 @@ Variant ClassDB::instantiate(const StringName &p_class) const { } } -bool ClassDB::class_has_signal(StringName p_class, StringName p_signal) const { +bool ClassDB::class_has_signal(const StringName &p_class, const StringName &p_signal) const { return ::ClassDB::has_signal(p_class, p_signal); } -Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) const { +Dictionary ClassDB::class_get_signal(const StringName &p_class, const StringName &p_signal) const { MethodInfo signal; if (::ClassDB::get_signal(p_class, p_signal, &signal)) { return signal.operator Dictionary(); @@ -1388,7 +1388,7 @@ Dictionary ClassDB::class_get_signal(StringName p_class, StringName p_signal) co } } -TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_signal_list(const StringName &p_class, bool p_no_inheritance) const { List<MethodInfo> signals; ::ClassDB::get_signal_list(p_class, &signals, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1400,7 +1400,7 @@ TypedArray<Dictionary> ClassDB::class_get_signal_list(StringName p_class, bool p return ret; } -TypedArray<Dictionary> ClassDB::class_get_property_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_property_list(const StringName &p_class, bool p_no_inheritance) const { List<PropertyInfo> plist; ::ClassDB::get_property_list(p_class, &plist, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1428,11 +1428,11 @@ Error ClassDB::class_set_property(Object *p_object, const StringName &p_property return OK; } -bool ClassDB::class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance) const { +bool ClassDB::class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance) const { return ::ClassDB::has_method(p_class, p_method, p_no_inheritance); } -TypedArray<Dictionary> ClassDB::class_get_method_list(StringName p_class, bool p_no_inheritance) const { +TypedArray<Dictionary> ClassDB::class_get_method_list(const StringName &p_class, bool p_no_inheritance) const { List<MethodInfo> methods; ::ClassDB::get_method_list(p_class, &methods, p_no_inheritance); TypedArray<Dictionary> ret; @@ -1513,7 +1513,7 @@ StringName ClassDB::class_get_integer_constant_enum(const StringName &p_class, c return ::ClassDB::get_integer_constant_enum(p_class, p_name, p_no_inheritance); } -bool ClassDB::is_class_enabled(StringName p_class) const { +bool ClassDB::is_class_enabled(const StringName &p_class) const { return ::ClassDB::is_class_enabled(p_class); } diff --git a/core/core_bind.h b/core/core_bind.h index a8244cf7c5..94d95f2ce9 100644 --- a/core/core_bind.h +++ b/core/core_bind.h @@ -435,17 +435,17 @@ public: bool can_instantiate(const StringName &p_class) const; Variant instantiate(const StringName &p_class) const; - bool class_has_signal(StringName p_class, StringName p_signal) const; - Dictionary class_get_signal(StringName p_class, StringName p_signal) const; - TypedArray<Dictionary> class_get_signal_list(StringName p_class, bool p_no_inheritance = false) const; + bool class_has_signal(const StringName &p_class, const StringName &p_signal) const; + Dictionary class_get_signal(const StringName &p_class, const StringName &p_signal) const; + TypedArray<Dictionary> class_get_signal_list(const StringName &p_class, bool p_no_inheritance = false) const; - TypedArray<Dictionary> class_get_property_list(StringName p_class, bool p_no_inheritance = false) const; + TypedArray<Dictionary> class_get_property_list(const StringName &p_class, bool p_no_inheritance = false) const; Variant class_get_property(Object *p_object, const StringName &p_property) const; Error class_set_property(Object *p_object, const StringName &p_property, const Variant &p_value) const; - bool class_has_method(StringName p_class, StringName p_method, bool p_no_inheritance = false) const; + bool class_has_method(const StringName &p_class, const StringName &p_method, bool p_no_inheritance = false) const; - TypedArray<Dictionary> class_get_method_list(StringName p_class, bool p_no_inheritance = false) const; + TypedArray<Dictionary> class_get_method_list(const StringName &p_class, bool p_no_inheritance = false) const; PackedStringArray class_get_integer_constant_list(const StringName &p_class, bool p_no_inheritance = false) const; bool class_has_integer_constant(const StringName &p_class, const StringName &p_name) const; @@ -456,7 +456,7 @@ public: PackedStringArray class_get_enum_constants(const StringName &p_class, const StringName &p_enum, bool p_no_inheritance = false) const; StringName class_get_integer_constant_enum(const StringName &p_class, const StringName &p_name, bool p_no_inheritance = false) const; - bool is_class_enabled(StringName p_class) const; + bool is_class_enabled(const StringName &p_class) const; ClassDB() {} ~ClassDB() {} diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 2f70fdf219..3b96fc20c6 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -845,7 +845,7 @@ bool CoreConstants::is_global_enum(const StringName &p_enum) { return _global_enums.has(p_enum); } -void CoreConstants::get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values) { +void CoreConstants::get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values) { ERR_FAIL_NULL_MSG(p_values, "Trying to get enum values with null map."); ERR_FAIL_COND_MSG(!_global_enums.has(p_enum), "Trying to get values of non-existing enum."); for (const _CoreConstant &constant : _global_enums[p_enum]) { diff --git a/core/core_constants.h b/core/core_constants.h index 51842490c8..82d626c749 100644 --- a/core/core_constants.h +++ b/core/core_constants.h @@ -45,7 +45,7 @@ public: static bool is_global_constant(const StringName &p_name); static int get_global_constant_index(const StringName &p_name); static bool is_global_enum(const StringName &p_enum); - static void get_enum_values(StringName p_enum, HashMap<StringName, int64_t> *p_values); + static void get_enum_values(const StringName &p_enum, HashMap<StringName, int64_t> *p_values); }; #endif // CORE_CONSTANTS_H diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 19ffe96a09..ce01531b5c 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -666,12 +666,12 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions; -void GDExtension::register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) { +void GDExtension::register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer) { ERR_FAIL_COND_MSG(gdextension_interface_functions.has(p_function_name), "Attempt to register interface function '" + p_function_name + "', which appears to be already registered."); gdextension_interface_functions.insert(p_function_name, p_function_pointer); } -GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(StringName p_function_name) { +GDExtensionInterfaceFunctionPtr GDExtension::get_interface_function(const StringName &p_function_name) { GDExtensionInterfaceFunctionPtr *function = gdextension_interface_functions.getptr(p_function_name); ERR_FAIL_NULL_V_MSG(function, nullptr, "Attempt to get non-existent interface function: " + String(p_function_name) + "."); return *function; diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index 0d20b8e50c..0b39581751 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -154,8 +154,8 @@ public: void initialize_library(InitializationLevel p_level); void deinitialize_library(InitializationLevel p_level); - static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); - static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name); + static void register_interface_function(const StringName &p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); + static GDExtensionInterfaceFunctionPtr get_interface_function(const StringName &p_function_name); static void initialize_gdextensions(); static void finalize_gdextensions(); diff --git a/core/io/image.cpp b/core/io/image.cpp index c72064e4f7..9aa7c9794a 100644 --- a/core/io/image.cpp +++ b/core/io/image.cpp @@ -3013,6 +3013,7 @@ void Image::fill_rect(const Rect2i &p_rect, const Color &p_color) { } ImageMemLoadFunc Image::_png_mem_loader_func = nullptr; +ImageMemLoadFunc Image::_png_mem_unpacker_func = nullptr; ImageMemLoadFunc Image::_jpg_mem_loader_func = nullptr; ImageMemLoadFunc Image::_webp_mem_loader_func = nullptr; ImageMemLoadFunc Image::_tga_mem_loader_func = nullptr; diff --git a/core/io/image.h b/core/io/image.h index a21d05187b..a5025ee8a1 100644 --- a/core/io/image.h +++ b/core/io/image.h @@ -145,6 +145,7 @@ public: }; static ImageMemLoadFunc _png_mem_loader_func; + static ImageMemLoadFunc _png_mem_unpacker_func; static ImageMemLoadFunc _jpg_mem_loader_func; static ImageMemLoadFunc _webp_mem_loader_func; static ImageMemLoadFunc _tga_mem_loader_func; diff --git a/core/io/resource_loader.cpp b/core/io/resource_loader.cpp index 0c7764392a..1fbb6ff2ed 100644 --- a/core/io/resource_loader.cpp +++ b/core/io/resource_loader.cpp @@ -340,7 +340,7 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (load_task.resource.is_valid()) { if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - load_task.resource->set_path(load_task.local_path, load_task.cache_mode == ResourceFormatLoader::CACHE_MODE_REPLACE); + load_task.resource->set_path(load_task.local_path); } else if (!load_task.local_path.is_resource_file()) { load_task.resource->set_path_cache(load_task.local_path); } @@ -361,17 +361,6 @@ void ResourceLoader::_thread_load_function(void *p_userdata) { if (_loaded_callback) { _loaded_callback(load_task.resource, load_task.local_path); } - } else if (load_task.cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { - Ref<Resource> existing = ResourceCache::get_ref(load_task.local_path); - if (existing.is_valid()) { - load_task.resource = existing; - load_task.status = THREAD_LOAD_LOADED; - load_task.progress = 1.0; - - if (_loaded_callback) { - _loaded_callback(load_task.resource, load_task.local_path); - } - } } thread_load_mutex.unlock(); @@ -474,7 +463,7 @@ Ref<ResourceLoader::LoadToken> ResourceLoader::_load_start(const String &p_path, load_task.type_hint = p_type_hint; load_task.cache_mode = p_cache_mode; load_task.use_sub_threads = p_thread_mode == LOAD_THREAD_DISTRIBUTE; - if (p_cache_mode == ResourceFormatLoader::CACHE_MODE_REUSE) { + if (p_cache_mode != ResourceFormatLoader::CACHE_MODE_IGNORE) { Ref<Resource> existing = ResourceCache::get_ref(local_path); if (existing.is_valid()) { //referencing is fine diff --git a/core/object/script_language.h b/core/object/script_language.h index 69da50f074..66106bf139 100644 --- a/core/object/script_language.h +++ b/core/object/script_language.h @@ -243,7 +243,7 @@ public: virtual void get_doc_comment_delimiters(List<String> *p_delimiters) const = 0; virtual void get_string_delimiters(List<String> *p_delimiters) const = 0; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const { return Ref<Script>(); } - virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) { return Vector<ScriptTemplate>(); } + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) { return Vector<ScriptTemplate>(); } virtual bool is_using_templates() { return false; } virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptError> *r_errors = nullptr, List<Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const = 0; virtual String validate_path(const String &p_path) const { return ""; } diff --git a/core/object/script_language_extension.h b/core/object/script_language_extension.h index 852b2aebd8..8b01667519 100644 --- a/core/object/script_language_extension.h +++ b/core/object/script_language_extension.h @@ -265,7 +265,7 @@ public: GDVIRTUAL1RC(TypedArray<Dictionary>, _get_built_in_templates, StringName) - virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override { + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override { TypedArray<Dictionary> ret; GDVIRTUAL_REQUIRED_CALL(_get_built_in_templates, p_object, ret); Vector<ScriptTemplate> stret; diff --git a/core/string/translation.cpp b/core/string/translation.cpp index 8fcf2b24b5..829c9bf777 100644 --- a/core/string/translation.cpp +++ b/core/string/translation.cpp @@ -881,7 +881,7 @@ StringName TranslationServer::tool_pseudolocalize(const StringName &p_message) c String TranslationServer::get_override_string(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -895,7 +895,7 @@ String TranslationServer::get_override_string(String &p_message) const { String TranslationServer::double_vowels(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -913,7 +913,7 @@ String TranslationServer::double_vowels(String &p_message) const { String TranslationServer::replace_with_accented_string(String &p_message) const { String res; - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (pseudolocalization_skip_placeholders_enabled && is_placeholder(p_message, i)) { res += p_message[i]; res += p_message[i + 1]; @@ -936,7 +936,7 @@ String TranslationServer::wrap_with_fakebidi_characters(String &p_message) const char32_t fakebidisuffix = U'\u202c'; res += fakebidiprefix; // The fake bidi unicode gets popped at every newline so pushing it back at every newline. - for (int i = 0; i < p_message.size(); i++) { + for (int i = 0; i < p_message.length(); i++) { if (p_message[i] == '\n') { res += fakebidisuffix; res += p_message[i]; @@ -978,7 +978,7 @@ const char32_t *TranslationServer::get_accented_version(char32_t p_character) co } bool TranslationServer::is_placeholder(String &p_message, int p_index) const { - return p_index < p_message.size() - 1 && p_message[p_index] == '%' && + return p_index < p_message.length() - 1 && p_message[p_index] == '%' && (p_message[p_index + 1] == 's' || p_message[p_index + 1] == 'c' || p_message[p_index + 1] == 'd' || p_message[p_index + 1] == 'o' || p_message[p_index + 1] == 'x' || p_message[p_index + 1] == 'X' || p_message[p_index + 1] == 'f'); } diff --git a/core/templates/self_list.h b/core/templates/self_list.h index fdf91beacc..afa0501c75 100644 --- a/core/templates/self_list.h +++ b/core/templates/self_list.h @@ -159,6 +159,9 @@ public: _FORCE_INLINE_ SelfList<T> *first() { return _first; } _FORCE_INLINE_ const SelfList<T> *first() const { return _first; } + // Forbid copying, which has broken behavior. + void operator=(const List &) = delete; + _FORCE_INLINE_ List() {} _FORCE_INLINE_ ~List() { // A self list must be empty on destruction. @@ -185,6 +188,9 @@ public: _FORCE_INLINE_ const SelfList<T> *prev() const { return _prev; } _FORCE_INLINE_ T *self() const { return _self; } + // Forbid copying, which has broken behavior. + void operator=(const SelfList<T> &) = delete; + _FORCE_INLINE_ SelfList(T *p_self) { _self = p_self; } diff --git a/core/variant/variant.h b/core/variant/variant.h index 296319d408..602d287f22 100644 --- a/core/variant/variant.h +++ b/core/variant/variant.h @@ -781,8 +781,8 @@ public: static Variant get_constant_value(Variant::Type p_type, const StringName &p_value, bool *r_valid = nullptr); static void get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums); - static void get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations); - static int get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid = nullptr); + static void get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations); + static int get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid = nullptr); typedef String (*ObjectDeConstruct)(const Variant &p_object, void *ud); typedef void (*ObjectConstruct)(const String &p_text, void *ud, Variant &r_value); diff --git a/core/variant/variant_call.cpp b/core/variant/variant_call.cpp index 1daad2058e..03836985f3 100644 --- a/core/variant/variant_call.cpp +++ b/core/variant/variant_call.cpp @@ -1071,14 +1071,14 @@ struct _VariantCall { static ConstantData *constant_data; - static void add_constant(int p_type, StringName p_constant_name, int64_t p_constant_value) { + static void add_constant(int p_type, const StringName &p_constant_name, int64_t p_constant_value) { constant_data[p_type].value[p_constant_name] = p_constant_value; #ifdef DEBUG_ENABLED constant_data[p_type].value_ordered.push_back(p_constant_name); #endif } - static void add_variant_constant(int p_type, StringName p_constant_name, const Variant &p_constant_value) { + static void add_variant_constant(int p_type, const StringName &p_constant_name, const Variant &p_constant_value) { constant_data[p_type].variant_value[p_constant_name] = p_constant_value; #ifdef DEBUG_ENABLED constant_data[p_type].variant_value_ordered.push_back(p_constant_name); @@ -1091,7 +1091,7 @@ struct _VariantCall { static EnumData *enum_data; - static void add_enum_constant(int p_type, StringName p_enum_type_name, StringName p_enumeration_name, int p_enum_value) { + static void add_enum_constant(int p_type, const StringName &p_enum_type_name, const StringName &p_enumeration_name, int p_enum_value) { enum_data[p_type].value[p_enum_type_name][p_enumeration_name] = p_enum_value; } }; @@ -1504,7 +1504,7 @@ void Variant::get_enums_for_type(Variant::Type p_type, List<StringName> *p_enums } } -void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_name, List<StringName> *p_enumerations) { +void Variant::get_enumerations_for_enum(Variant::Type p_type, const StringName &p_enum_name, List<StringName> *p_enumerations) { ERR_FAIL_INDEX(p_type, Variant::VARIANT_MAX); _VariantCall::EnumData &enum_data = _VariantCall::enum_data[p_type]; @@ -1516,7 +1516,7 @@ void Variant::get_enumerations_for_enum(Variant::Type p_type, StringName p_enum_ } } -int Variant::get_enum_value(Variant::Type p_type, StringName p_enum_name, StringName p_enumeration, bool *r_valid) { +int Variant::get_enum_value(Variant::Type p_type, const StringName &p_enum_name, const StringName &p_enumeration, bool *r_valid) { if (r_valid) { *r_valid = false; } diff --git a/doc/classes/AnimationMixer.xml b/doc/classes/AnimationMixer.xml index 4a489a3ef2..31308c4e20 100644 --- a/doc/classes/AnimationMixer.xml +++ b/doc/classes/AnimationMixer.xml @@ -15,10 +15,10 @@ <param index="0" name="animation" type="Animation" /> <param index="1" name="track" type="int" /> <param index="2" name="value" type="Variant" /> - <param index="3" name="object" type="Object" /> - <param index="4" name="object_idx" type="int" /> + <param index="3" name="object_id" type="int" /> + <param index="4" name="object_sub_idx" type="int" /> <description> - A virtual function for processing after key getting during playback. + A virtual function for processing after getting a key during playback. </description> </method> <method name="add_animation_library"> diff --git a/doc/classes/AudioEffectCapture.xml b/doc/classes/AudioEffectCapture.xml index 275e5ab0b6..e17c510907 100644 --- a/doc/classes/AudioEffectCapture.xml +++ b/doc/classes/AudioEffectCapture.xml @@ -6,11 +6,10 @@ <description> AudioEffectCapture is an AudioEffect which copies all audio frames from the attached audio effect bus into its internal ring buffer. Application code should consume these audio frames from this ring buffer using [method get_buffer] and process it as needed, for example to capture data from an [AudioStreamMicrophone], implement application-defined effects, or to transmit audio over the network. When capturing audio data from a microphone, the format of the samples will be stereo 32-bit floating point PCM. - [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. + Unlike [AudioEffectRecord], this effect only returns the raw audio samples instead of encoding them into an [AudioStream]. </description> <tutorials> <link title="Audio buses">$DOCS_URL/tutorials/audio/audio_buses.html</link> - <link title="Audio Mic Record Demo">https://github.com/godotengine/godot-demo-projects/tree/master/audio/mic_record</link> </tutorials> <methods> <method name="can_get_buffer" qualifiers="const"> diff --git a/doc/classes/AudioEffectRecord.xml b/doc/classes/AudioEffectRecord.xml index e07d66fa7b..5b43c5ebaa 100644 --- a/doc/classes/AudioEffectRecord.xml +++ b/doc/classes/AudioEffectRecord.xml @@ -4,9 +4,10 @@ Audio effect used for recording the sound from an audio bus. </brief_description> <description> - Allows the user to record the sound from an audio bus. This can include all audio output by Godot when used on the "Master" audio bus. + Allows the user to record the sound from an audio bus into an [AudioStreamWAV]. When used on the "Master" audio bus, this includes all audio output by Godot. + Unlike [AudioEffectCapture], this effect encodes the recording with the given format (8-bit, 16-bit, or compressed) instead of giving access to the raw audio samples. Can be used (with an [AudioStreamMicrophone]) to record from a microphone. - It sets and gets the format in which the audio file will be recorded (8-bit, 16-bit, or compressed). It checks whether or not the recording is active, and if it is, records the sound. It then returns the recorded sample. + [b]Note:[/b] [member ProjectSettings.audio/driver/enable_input] must be [code]true[/code] for audio input to work. See also that setting's description for caveats related to permissions and operating system privacy settings. </description> <tutorials> <link title="Recording with microphone">$DOCS_URL/tutorials/audio/recording_with_microphone.html</link> diff --git a/doc/classes/CanvasTexture.xml b/doc/classes/CanvasTexture.xml index 1b22adb723..a14f71cc46 100644 --- a/doc/classes/CanvasTexture.xml +++ b/doc/classes/CanvasTexture.xml @@ -5,7 +5,7 @@ </brief_description> <description> [CanvasTexture] is an alternative to [ImageTexture] for 2D rendering. It allows using normal maps and specular maps in any node that inherits from [CanvasItem]. [CanvasTexture] also allows overriding the texture's filter and repeat mode independently of the node's properties (or the project settings). - [b]Note:[/b] [CanvasTexture] cannot be used in 3D rendering. For physically-based materials in 3D, use [BaseMaterial3D] instead. + [b]Note:[/b] [CanvasTexture] cannot be used in 3D. It will not display correctly when applied to any [VisualInstance3D], such as [Sprite3D] or [Decal]. For physically-based materials in 3D, use [BaseMaterial3D] instead. </description> <tutorials> <link title="2D Lights and Shadows">$DOCS_URL/tutorials/2d/2d_lights_and_shadows.html</link> diff --git a/doc/classes/GraphNode.xml b/doc/classes/GraphNode.xml index 9e1392567a..5bb3a25158 100644 --- a/doc/classes/GraphNode.xml +++ b/doc/classes/GraphNode.xml @@ -116,6 +116,20 @@ Returns the right (output) [Color] of the slot with the given [param slot_index]. </description> </method> + <method name="get_slot_custom_icon_left" qualifiers="const"> + <return type="Texture2D" /> + <param index="0" name="slot_index" type="int" /> + <description> + Returns the left (input) custom [Texture2D] of the slot with the given [param slot_index]. + </description> + </method> + <method name="get_slot_custom_icon_right" qualifiers="const"> + <return type="Texture2D" /> + <param index="0" name="slot_index" type="int" /> + <description> + Returns the right (output) custom [Texture2D] of the slot with the given [param slot_index]. + </description> + </method> <method name="get_slot_type_left" qualifiers="const"> <return type="int" /> <param index="0" name="slot_index" type="int" /> @@ -195,6 +209,22 @@ Sets the [Color] of the right (output) side of the slot with the given [param slot_index] to [param color]. </description> </method> + <method name="set_slot_custom_icon_left"> + <return type="void" /> + <param index="0" name="slot_index" type="int" /> + <param index="1" name="custom_icon" type="Texture2D" /> + <description> + Sets the custom [Texture2D] of the left (input) side of the slot with the given [param slot_index] to [param custom_icon]. + </description> + </method> + <method name="set_slot_custom_icon_right"> + <return type="void" /> + <param index="0" name="slot_index" type="int" /> + <param index="1" name="custom_icon" type="Texture2D" /> + <description> + Sets the custom [Texture2D] of the right (output) side of the slot with the given [param slot_index] to [param custom_icon]. + </description> + </method> <method name="set_slot_draw_stylebox"> <return type="void" /> <param index="0" name="slot_index" type="int" /> diff --git a/doc/classes/Image.xml b/doc/classes/Image.xml index 5677999d5d..872277b033 100644 --- a/doc/classes/Image.xml +++ b/doc/classes/Image.xml @@ -731,7 +731,7 @@ On the other hand, if the image already has mipmaps, they will be used, and a new set will be generated for the resulting image. </constant> <constant name="INTERPOLATE_LANCZOS" value="4" enum="Interpolation"> - Performs Lanczos interpolation. This is the slowest image resizing mode, but it typically gives the best results, especially when downscalng images. + Performs Lanczos interpolation. This is the slowest image resizing mode, but it typically gives the best results, especially when downscaling images. </constant> <constant name="ALPHA_NONE" value="0" enum="AlphaMode"> Image does not have alpha. diff --git a/doc/classes/LightmapGI.xml b/doc/classes/LightmapGI.xml index ec75d85898..fa3bfe513f 100644 --- a/doc/classes/LightmapGI.xml +++ b/doc/classes/LightmapGI.xml @@ -65,6 +65,9 @@ The quality preset to use when baking lightmaps. This affects bake times, but output file sizes remain mostly identical across quality levels. To further speed up bake times, decrease [member bounces], disable [member use_denoiser] and increase the lightmap texel size on 3D scenes in the Import doc. </member> + <member name="texel_scale" type="float" setter="set_texel_scale" getter="get_texel_scale" default="1.0"> + Scales the lightmap texel density of all meshes for the current bake. This is a multiplier that builds upon the existing lightmap texel size defined in each imported 3D scene, along with the per-mesh density multiplier (which is designed to be used when the same mesh is used at different scales). Lower values will result in faster bake times. + </member> <member name="use_denoiser" type="bool" setter="set_use_denoiser" getter="is_using_denoiser" default="true"> If [code]true[/code], uses a GPU-based denoising algorithm on the generated lightmap. This eliminates most noise within the generated lightmap at the cost of longer bake times. File sizes are generally not impacted significantly by the use of a denoiser, although lossless compression may do a better job at compressing a denoised image. </member> diff --git a/doc/classes/NavigationServer3D.xml b/doc/classes/NavigationServer3D.xml index 20af517397..c04017651c 100644 --- a/doc/classes/NavigationServer3D.xml +++ b/doc/classes/NavigationServer3D.xml @@ -278,7 +278,7 @@ <param index="1" name="enabled" type="bool" /> <description> Sets if the agent uses the 2D avoidance or the 3D avoidance while avoidance is enabled. - If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, unterwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's. + If [code]true[/code] the agent calculates avoidance velocities in 3D for the xyz-axis, e.g. for games that take place in air, underwater or space. The 3D using agent only avoids other 3D avoidance using agent's. The 3D using agent only reacts to radius based avoidance obstacles. The 3D using agent ignores any vertices based obstacles. The 3D using agent only avoids other 3D using agent's. If [code]false[/code] the agent calculates avoidance velocities in 2D along the xz-axis ignoring the y-axis. The 2D using agent only avoids other 2D avoidance using agent's. The 2D using agent reacts to radius avoidance obstacles. The 2D using agent reacts to vertices based avoidance obstacles. The 2D using agent only avoids other 2D using agent's. 2D using agents will ignore other 2D using agents or obstacles that are below their current position or above their current position including the agents height in 2D avoidance. </description> </method> diff --git a/doc/classes/Node.xml b/doc/classes/Node.xml index 4d31b5b8ed..8fb2e65faf 100644 --- a/doc/classes/Node.xml +++ b/doc/classes/Node.xml @@ -267,7 +267,7 @@ <param index="0" name="idx" type="int" /> <param index="1" name="include_internal" type="bool" default="false" /> <description> - Fetches a child node by its index. Each child node has an index relative its siblings (see [method get_index]). The first child is at index 0. Negative values can also be used to start from the end of the list. This method can be used in combination with [method get_child_count] to iterate over this node's children. + Fetches a child node by its index. Each child node has an index relative its siblings (see [method get_index]). The first child is at index 0. Negative values can also be used to start from the end of the list. This method can be used in combination with [method get_child_count] to iterate over this node's children. If no child exists at the given index, this method returns [code]null[/code] and an error is generated. If [param include_internal] is [code]false[/code], internal children are ignored (see [method add_child]'s [code]internal[/code] parameter). [codeblock] # Assuming the following are children of this node, in order: diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index cb36d28a41..8640879a4e 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -1108,7 +1108,7 @@ [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_swap_input_direction" type="Dictionary" setter="" getter=""> - Default [InputEventAction] to swap input direction, i.e. change between left-to-right to right-to-left modes. Affects text-editting controls ([LineEdit], [TextEdit]). + Default [InputEventAction] to swap input direction, i.e. change between left-to-right to right-to-left modes. Affects text-editing controls ([LineEdit], [TextEdit]). </member> <member name="input/ui_text_add_selection_for_next_occurrence" type="Dictionary" setter="" getter=""> If a selection is currently active with the last caret in text fields, searches for the next occurrence of the selection, adds a caret and selects the next occurrence. @@ -1219,15 +1219,15 @@ [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_text_completion_accept" type="Dictionary" setter="" getter=""> - Default [InputEventAction] to accept an autocompetion hint. + Default [InputEventAction] to accept an autocompletion hint. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_text_completion_query" type="Dictionary" setter="" getter=""> - Default [InputEventAction] to request autocompetion. + Default [InputEventAction] to request autocompletion. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_text_completion_replace" type="Dictionary" setter="" getter=""> - Default [InputEventAction] to accept an autocompetion hint, replacing existing text. + Default [InputEventAction] to accept an autocompletion hint, replacing existing text. [b]Note:[/b] Default [code]ui_*[/code] actions cannot be removed as they are necessary for the internal logic of several [Control]s. The events assigned to the action can however be modified. </member> <member name="input/ui_text_dedent" type="Dictionary" setter="" getter=""> @@ -2534,8 +2534,6 @@ Decreasing this value may improve GPU performance on certain setups, even if the maximum number of clustered elements is never reached in the project. [b]Note:[/b] This setting is only effective when using the Forward+ rendering method, not Mobile and Compatibility. </member> - <member name="rendering/limits/forward_renderer/threaded_render_minimum_instances" type="int" setter="" getter="" default="500"> - </member> <member name="rendering/limits/global_shader_variables/buffer_size" type="int" setter="" getter="" default="65536"> </member> <member name="rendering/limits/opengl/max_lights_per_object" type="int" setter="" getter="" default="8"> diff --git a/doc/classes/Quaternion.xml b/doc/classes/Quaternion.xml index 74dfae77fc..61bc4f4720 100644 --- a/doc/classes/Quaternion.xml +++ b/doc/classes/Quaternion.xml @@ -79,6 +79,7 @@ <method name="exp" qualifiers="const"> <return type="Quaternion" /> <description> + Returns the exponential of this quaternion. The rotation axis of the result is the normalized rotation axis of this quaternion, the angle of the result is the length of the vector part of this quaternion. </description> </method> <method name="from_euler" qualifiers="static"> @@ -91,11 +92,14 @@ <method name="get_angle" qualifiers="const"> <return type="float" /> <description> + Returns the angle of the rotation represented by this quaternion. + [b]Note:[/b] The quaternion must be normalized. </description> </method> <method name="get_axis" qualifiers="const"> <return type="Vector3" /> <description> + Returns the rotation axis of the rotation represented by this quaternion. </description> </method> <method name="get_euler" qualifiers="const"> @@ -145,6 +149,7 @@ <method name="log" qualifiers="const"> <return type="Quaternion" /> <description> + Returns the logarithm of this quaternion. The vector part of the result is the rotation axis of this quaternion multiplied by its rotation angle, the real part of the result is zero. </description> </method> <method name="normalized" qualifiers="const"> diff --git a/doc/classes/RDUniform.xml b/doc/classes/RDUniform.xml index 7aab8b2810..77aaf0aae7 100644 --- a/doc/classes/RDUniform.xml +++ b/doc/classes/RDUniform.xml @@ -13,16 +13,19 @@ <return type="void" /> <param index="0" name="id" type="RID" /> <description> + Binds the given id to the uniform. The data associated with the id is then used when the uniform is passed to a shader. </description> </method> <method name="clear_ids"> <return type="void" /> <description> + Unbinds all ids currently bound to the uniform. </description> </method> <method name="get_ids" qualifiers="const"> <return type="RID[]" /> <description> + Returns an array of all ids currently bound to the uniform. </description> </method> </methods> diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml index a841453ba0..495bec24dc 100644 --- a/doc/classes/RenderingDevice.xml +++ b/doc/classes/RenderingDevice.xml @@ -14,12 +14,12 @@ <link title="Using compute shaders">$DOCS_URL/tutorials/shaders/compute_shaders.html</link> </tutorials> <methods> - <method name="barrier"> + <method name="barrier" is_deprecated="true"> <return type="void" /> <param index="0" name="from" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <param index="1" name="to" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> - Puts a memory barrier in place. This is used for synchronization to avoid data races. See also [method full_barrier], which may be useful for debugging. + [i]Deprecated.[/i] Barriers are automatically inserted by RenderingDevice. </description> </method> <method name="buffer_clear"> @@ -27,9 +27,8 @@ <param index="0" name="buffer" type="RID" /> <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> - Clears the contents of the [param buffer], clearing [param size_bytes] bytes, starting at [param offset]. Always raises a memory barrier. + Clears the contents of the [param buffer], clearing [param size_bytes] bytes, starting at [param offset]. Prints an error if: - the size isn't a multiple of four - the region specified by [param offset] + [param size_bytes] exceeds the buffer @@ -37,6 +36,21 @@ - a compute list is currently active (created by [method compute_list_begin]) </description> </method> + <method name="buffer_copy"> + <return type="int" enum="Error" /> + <param index="0" name="src_buffer" type="RID" /> + <param index="1" name="dst_buffer" type="RID" /> + <param index="2" name="src_offset" type="int" /> + <param index="3" name="dst_offset" type="int" /> + <param index="4" name="size" type="int" /> + <description> + Copies [param size] bytes from the [param src_buffer] at [param src_offset] into [param dst_buffer] at [param dst_offset]. + Prints an error if: + - [param size] exceeds the size of either [param src_buffer] or [param dst_buffer] at their corresponding offsets + - a draw list is currently active (created by [method draw_list_begin]) + - a compute list is currently active (created by [method compute_list_begin]) + </description> + </method> <method name="buffer_get_data"> <return type="PackedByteArray" /> <param index="0" name="buffer" type="RID" /> @@ -52,9 +66,8 @@ <param index="1" name="offset" type="int" /> <param index="2" name="size_bytes" type="int" /> <param index="3" name="data" type="PackedByteArray" /> - <param index="4" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> - Updates a region of [param size_bytes] bytes, starting at [param offset], in the buffer, with the specified [param data]. Raises a memory barrier except when [param post_barrier] is set to [constant BARRIER_MASK_NO_BARRIER]. + Updates a region of [param size_bytes] bytes, starting at [param offset], in the buffer, with the specified [param data]. Prints an error if: - the region specified by [param offset] + [param size_bytes] exceeds the buffer - a draw list is currently active (created by [method draw_list_begin]) @@ -77,10 +90,9 @@ </method> <method name="compute_list_begin"> <return type="int" /> - <param index="0" name="allow_draw_overlap" type="bool" default="false" /> <description> Starts a list of compute commands created with the [code]compute_*[/code] methods. The returned value should be passed to other [code]compute_list_*[/code] functions. - If [param allow_draw_overlap] is [code]true[/code], you may have one draw list running at the same time as one compute list. Multiple compute lists cannot be created at the same time; you must finish the previous compute list first using [method compute_list_end]. + Multiple compute lists cannot be created at the same time; you must finish the previous compute list first using [method compute_list_end]. A simple compute operation might look like this (code is not a complete example): [codeblock] var rd = RenderingDevice.new() @@ -128,7 +140,6 @@ </method> <method name="compute_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Finishes a list of compute commands created with the [code]compute_*[/code] methods. </description> @@ -170,7 +181,7 @@ <param index="1" name="color" type="Color" /> <description> Create a command buffer debug label region that can be displayed in third-party tools such as [url=https://renderdoc.org/]RenderDoc[/url]. All regions must be ended with a [method draw_command_end_label] call. When viewed from the linear series of submissions to a single queue, calls to [method draw_command_begin_label] and [method draw_command_end_label] must be matched and balanced. - The [code]VK_EXT_DEBUG_UTILS_EXTENSION_NAME[/code] Vulkan extension must be available and enabled for command buffer debug label region to work. See also [method draw_command_insert_label] and [method draw_command_end_label]. + The [code]VK_EXT_DEBUG_UTILS_EXTENSION_NAME[/code] Vulkan extension must be available and enabled for command buffer debug label region to work. See also [method draw_command_end_label]. </description> </method> <method name="draw_command_end_label"> @@ -179,12 +190,12 @@ Ends the command buffer debug label region started by a [method draw_command_begin_label] call. </description> </method> - <method name="draw_command_insert_label"> + <method name="draw_command_insert_label" is_deprecated="true"> <return type="void" /> <param index="0" name="name" type="String" /> <param index="1" name="color" type="Color" /> <description> - Inserts a command buffer debug label region in the current command buffer. Unlike [method draw_command_begin_label], this region should not be ended with a [method draw_command_end_label] call. + [i]Deprecated.[/i] Inserting labels no longer applies due to command reordering. </description> </method> <method name="draw_list_begin"> @@ -198,14 +209,13 @@ <param index="6" name="clear_depth" type="float" default="1.0" /> <param index="7" name="clear_stencil" type="int" default="0" /> <param index="8" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" /> - <param index="9" name="storage_textures" type="RID[]" default="[]" /> <description> Starts a list of raster drawing commands created with the [code]draw_*[/code] methods. The returned value should be passed to other [code]draw_list_*[/code] functions. Multiple draw lists cannot be created at the same time; you must finish the previous draw list first using [method draw_list_end]. A simple drawing operation might look like this (code is not a complete example): [codeblock] var rd = RenderingDevice.new() - var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)] + var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)]) var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors) # Draw opaque. @@ -232,7 +242,7 @@ [b]Note:[/b] Cannot be used with local RenderingDevices, as these don't have a screen. If called on a local RenderingDevice, [method draw_list_begin_for_screen] returns [constant INVALID_ID]. </description> </method> - <method name="draw_list_begin_split"> + <method name="draw_list_begin_split" is_deprecated="true"> <return type="PackedInt64Array" /> <param index="0" name="framebuffer" type="RID" /> <param index="1" name="splits" type="int" /> @@ -246,7 +256,7 @@ <param index="9" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" /> <param index="10" name="storage_textures" type="RID[]" default="[]" /> <description> - Variant of [method draw_list_begin] with support for multiple splits. The [param splits] parameter determines how many splits are created. + [i]Deprecated.[/i] Split draw lists are used automatically by RenderingDevice. </description> </method> <method name="draw_list_bind_index_array"> @@ -310,7 +320,6 @@ </method> <method name="draw_list_end"> <return type="void" /> - <param index="0" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Finishes a list of raster drawing commands created with the [code]draw_*[/code] methods. </description> @@ -335,14 +344,14 @@ <method name="draw_list_switch_to_next_pass"> <return type="int" /> <description> - Switches to the next draw pass and returns the split's ID. Equivalent to [method draw_list_switch_to_next_pass_split] with [code]splits[/code] set to [code]1[/code]. + Switches to the next draw pass. </description> </method> - <method name="draw_list_switch_to_next_pass_split"> + <method name="draw_list_switch_to_next_pass_split" is_deprecated="true"> <return type="PackedInt64Array" /> <param index="0" name="splits" type="int" /> <description> - Switches to the next draw pass, with the number of splits allocated specified in [param splits]. The return value is an array containing the ID of each split. For single-split usage, see [method draw_list_switch_to_next_pass]. + [i]Deprecated.[/i] Split draw lists are used automatically by RenderingDevice. </description> </method> <method name="framebuffer_create"> @@ -430,10 +439,10 @@ Tries to free an object in the RenderingDevice. To avoid memory leaks, this should be called after using an object as memory management does not occur automatically when using RenderingDevice directly. </description> </method> - <method name="full_barrier"> + <method name="full_barrier" is_deprecated="true"> <return type="void" /> <description> - Puts a [i]full[/i] memory barrier in place. This is a memory [method barrier] with all flags enabled. [method full_barrier] it should only be used for debugging as it can severely impact performance. + [i]Deprecated.[/i] Barriers are automatically inserted by RenderingDevice. </description> </method> <method name="get_captured_timestamp_cpu_time" qualifiers="const"> @@ -704,7 +713,6 @@ <param index="3" name="mipmap_count" type="int" /> <param index="4" name="base_layer" type="int" /> <param index="5" name="layer_count" type="int" /> - <param index="6" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Clears the specified [param texture] by replacing all of its pixels with the specified [param color]. [param base_mipmap] and [param mipmap_count] determine which mipmaps of the texture are affected by this clear operation, while [param base_layer] and [param layer_count] determine which layers of a 3D texture (or texture array) are affected by this clear operation. For 2D textures (which only have one layer by design), [param base_layer] must be [code]0[/code] and [param layer_count] must be [code]1[/code]. [b]Note:[/b] [param texture] can't be cleared while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to clear this texture. @@ -721,7 +729,6 @@ <param index="6" name="dst_mipmap" type="int" /> <param index="7" name="src_layer" type="int" /> <param index="8" name="dst_layer" type="int" /> - <param index="9" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Copies the [param from_texture] to [param to_texture] with the specified [param from_pos], [param to_pos] and [param size] coordinates. The Z axis of the [param from_pos], [param to_pos] and [param size] must be [code]0[/code] for 2-dimensional textures. Source and destination mipmaps/layers must also be specified, with these parameters being [code]0[/code] for textures without mipmaps or single-layer textures. Returns [constant @GlobalScope.OK] if the texture copy was successful or [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] texture can't be copied while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to [constant FINAL_ACTION_CONTINUE]) to copy this texture. @@ -831,7 +838,6 @@ <return type="int" enum="Error" /> <param index="0" name="from_texture" type="RID" /> <param index="1" name="to_texture" type="RID" /> - <param index="2" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Resolves the [param from_texture] texture onto [param to_texture] with multisample antialiasing enabled. This must be used when rendering a framebuffer for MSAA to work. Returns [constant @GlobalScope.OK] if successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] [param from_texture] and [param to_texture] textures must have the same dimension, format and type (color or depth). @@ -848,7 +854,6 @@ <param index="0" name="texture" type="RID" /> <param index="1" name="layer" type="int" /> <param index="2" name="data" type="PackedByteArray" /> - <param index="3" name="post_barrier" type="int" enum="RenderingDevice.BarrierMask" is_bitfield="true" default="32767" /> <description> Updates texture data with new data, replacing the previous data in place. The updated texture data must have the same dimensions and format. For 2D textures (which only have one layer), [param layer] must be [code]0[/code]. Returns [constant @GlobalScope.OK] if the update was successful, [constant @GlobalScope.ERR_INVALID_PARAMETER] otherwise. [b]Note:[/b] Updating textures is forbidden during creation of a draw or compute list. @@ -2150,39 +2155,48 @@ </constant> <constant name="DYNAMIC_STATE_STENCIL_REFERENCE" value="64" enum="PipelineDynamicStateFlags" is_bitfield="true"> </constant> - <constant name="INITIAL_ACTION_CLEAR" value="0" enum="InitialAction"> - Start rendering and clear the whole framebuffer. + <constant name="INITIAL_ACTION_LOAD" value="0" enum="InitialAction"> + Load the previous contents of the framebuffer. </constant> - <constant name="INITIAL_ACTION_CLEAR_REGION" value="1" enum="InitialAction"> - Start rendering and clear the framebuffer in the specified region. + <constant name="INITIAL_ACTION_CLEAR" value="1" enum="InitialAction"> + Clear the whole framebuffer or its specified region. </constant> - <constant name="INITIAL_ACTION_CLEAR_REGION_CONTINUE" value="2" enum="InitialAction"> - Continue rendering and clear the framebuffer in the specified region. Framebuffer must have been left in [constant FINAL_ACTION_CONTINUE] state as the final action previously. + <constant name="INITIAL_ACTION_DISCARD" value="2" enum="InitialAction"> + Ignore the previous contents of the framebuffer. This is the fastest option if you'll overwrite all of the pixels and don't need to read any of them. </constant> - <constant name="INITIAL_ACTION_KEEP" value="3" enum="InitialAction"> - Start rendering, but keep attached color texture contents. If the framebuffer was previously used to read in a shader, this will automatically insert a layout transition. + <constant name="INITIAL_ACTION_MAX" value="3" enum="InitialAction"> + Represents the size of the [enum InitialAction] enum. </constant> - <constant name="INITIAL_ACTION_DROP" value="4" enum="InitialAction"> - Start rendering, ignore what is there; write above it. In general, this is the fastest option when you will be writing every single pixel and you don't need a clear color. + <constant name="INITIAL_ACTION_CLEAR_REGION" value="1" enum="InitialAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant INITIAL_ACTION_CLEAR] instead. </constant> - <constant name="INITIAL_ACTION_CONTINUE" value="5" enum="InitialAction"> - Continue rendering. Framebuffer must have been left in [constant FINAL_ACTION_CONTINUE] state as the final action previously. + <constant name="INITIAL_ACTION_CLEAR_REGION_CONTINUE" value="0" enum="InitialAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant INITIAL_ACTION_LOAD] instead. </constant> - <constant name="INITIAL_ACTION_MAX" value="6" enum="InitialAction"> - Represents the size of the [enum InitialAction] enum. + <constant name="INITIAL_ACTION_KEEP" value="0" enum="InitialAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant INITIAL_ACTION_LOAD] instead. </constant> - <constant name="FINAL_ACTION_READ" value="0" enum="FinalAction"> - Store the texture for reading and make it read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit (only applies to color, depth and stencil attachments). + <constant name="INITIAL_ACTION_DROP" value="2" enum="InitialAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant INITIAL_ACTION_DISCARD] instead. </constant> - <constant name="FINAL_ACTION_DISCARD" value="1" enum="FinalAction"> - Discard the texture data and make it read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit (only applies to color, depth and stencil attachments). + <constant name="INITIAL_ACTION_CONTINUE" value="0" enum="InitialAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant INITIAL_ACTION_LOAD] instead. </constant> - <constant name="FINAL_ACTION_CONTINUE" value="2" enum="FinalAction"> - Store the texture and continue for further processing. Similar to [constant FINAL_ACTION_READ], but does not make the texture read-only if it has the [constant TEXTURE_USAGE_SAMPLING_BIT] bit. + <constant name="FINAL_ACTION_STORE" value="0" enum="FinalAction"> + Store the result of the draw list in the framebuffer. This is generally what you want to do. </constant> - <constant name="FINAL_ACTION_MAX" value="3" enum="FinalAction"> + <constant name="FINAL_ACTION_DISCARD" value="1" enum="FinalAction"> + Discard the contents of the framebuffer. This is the fastest option if you don't need to use the results of the draw list. + </constant> + <constant name="FINAL_ACTION_MAX" value="2" enum="FinalAction"> Represents the size of the [enum FinalAction] enum. </constant> + <constant name="FINAL_ACTION_READ" value="0" enum="FinalAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant FINAL_ACTION_STORE] instead. + </constant> + <constant name="FINAL_ACTION_CONTINUE" value="0" enum="FinalAction" is_deprecated="true"> + [i]Deprecated.[/i] Use [constant FINAL_ACTION_STORE] instead. + </constant> <constant name="SHADER_STAGE_VERTEX" value="0" enum="ShaderStage"> Vertex shader stage. This can be used to manipulate vertices from a shader (but not create new vertices). </constant> diff --git a/doc/classes/String.xml b/doc/classes/String.xml index af4297afae..277d84ab90 100644 --- a/doc/classes/String.xml +++ b/doc/classes/String.xml @@ -318,7 +318,7 @@ <return type="int" /> <description> Returns the 32-bit hash value representing the string's contents. - [b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the countrary, strings with different hash values are guaranteed to be different. + [b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the contrary, strings with different hash values are guaranteed to be different. </description> </method> <method name="hex_decode" qualifiers="const"> diff --git a/doc/classes/StringName.xml b/doc/classes/StringName.xml index 1c5032be9b..4d9587f7aa 100644 --- a/doc/classes/StringName.xml +++ b/doc/classes/StringName.xml @@ -301,7 +301,7 @@ <return type="int" /> <description> Returns the 32-bit hash value representing the string's contents. - [b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the countrary, strings with different hash values are guaranteed to be different. + [b]Note:[/b] Strings with equal hash values are [i]not[/i] guaranteed to be the same, as a result of hash collisions. On the contrary, strings with different hash values are guaranteed to be different. </description> </method> <method name="hex_decode" qualifiers="const"> diff --git a/doc/classes/TabContainer.xml b/doc/classes/TabContainer.xml index b3a9264569..1f3c3531c6 100644 --- a/doc/classes/TabContainer.xml +++ b/doc/classes/TabContainer.xml @@ -193,6 +193,9 @@ <member name="tab_focus_mode" type="int" setter="set_tab_focus_mode" getter="get_tab_focus_mode" enum="Control.FocusMode" default="2"> The focus access mode for the internal [TabBar] node. </member> + <member name="tabs_position" type="int" setter="set_tabs_position" getter="get_tabs_position" enum="TabContainer.TabPosition" default="0"> + Sets the position of the tab bar. See [enum TabPosition] for details. + </member> <member name="tabs_rearrange_group" type="int" setter="set_tabs_rearrange_group" getter="get_tabs_rearrange_group" default="-1"> [TabContainer]s with the same rearrange group ID will allow dragging the tabs between them. Enable drag with [member drag_to_rearrange_enabled]. Setting this to [code]-1[/code] will disable rearranging between [TabContainer]s. @@ -247,6 +250,17 @@ </description> </signal> </signals> + <constants> + <constant name="POSITION_TOP" value="0" enum="TabPosition"> + Places the tab bar at the top. + </constant> + <constant name="POSITION_BOTTOM" value="1" enum="TabPosition"> + Places the tab bar at the bottom. The tab bar's [StyleBox] will be flipped vertically. + </constant> + <constant name="POSITION_MAX" value="2" enum="TabPosition"> + Represents the size of the [enum TabPosition] enum. + </constant> + </constants> <theme_items> <theme_item name="drop_mark_color" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> Modulation color for the [theme_item drop_mark] icon. diff --git a/doc/classes/TextServer.xml b/doc/classes/TextServer.xml index 09f484f523..cf4d92d119 100644 --- a/doc/classes/TextServer.xml +++ b/doc/classes/TextServer.xml @@ -1744,6 +1744,7 @@ Vertical BGR subpixel layout. </constant> <constant name="FONT_LCD_SUBPIXEL_LAYOUT_MAX" value="5" enum="FontLCDSubpixelLayout"> + Represents the size of the [enum FontLCDSubpixelLayout] enum. </constant> <constant name="DIRECTION_AUTO" value="0" enum="Direction"> Text direction is determined based on contents and current locale. @@ -1867,6 +1868,7 @@ Determines whether the ellipsis at the end of the text is enforced and may not be hidden. </constant> <constant name="OVERRUN_JUSTIFICATION_AWARE" value="16" enum="TextOverrunFlag" is_bitfield="true"> + Accounts for the text being justified before attempting to trim it (see [enum JustificationFlag]). </constant> <constant name="GRAPHEME_IS_VALID" value="1" enum="GraphemeFlag" is_bitfield="true"> Grapheme is supported by the font, and can be drawn. @@ -2005,6 +2007,7 @@ Spacing at the bottom of the line. </constant> <constant name="SPACING_MAX" value="4" enum="SpacingType"> + Represents the size of the [enum SpacingType] enum. </constant> <constant name="FONT_BOLD" value="1" enum="FontStyle" is_bitfield="true"> Font is bold. diff --git a/drivers/d3d12/d3d12_context.cpp b/drivers/d3d12/d3d12_context.cpp index 97f5d91f3a..37066a811d 100644 --- a/drivers/d3d12/d3d12_context.cpp +++ b/drivers/d3d12/d3d12_context.cpp @@ -76,6 +76,8 @@ char godot_nir_arch_name[32]; #endif #endif +#define D3D12_DEBUG_LAYER_BREAK_ON_ERROR 0 + void D3D12Context::_debug_message_func( D3D12_MESSAGE_CATEGORY p_category, D3D12_MESSAGE_SEVERITY p_severity, @@ -563,6 +565,11 @@ Error D3D12Context::_create_device(DeviceBasics &r_basics) { res = info_queue->PushStorageFilter(&filter); ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); + +#if D3D12_DEBUG_LAYER_BREAK_ON_ERROR + res = info_queue->SetBreakOnSeverity(D3D12_MESSAGE_SEVERITY_ERROR, true); + ERR_FAIL_COND_V(!SUCCEEDED(res), ERR_CANT_CREATE); +#endif } return OK; @@ -889,7 +896,9 @@ void D3D12Context::_wait_for_idle_queue(ID3D12CommandQueue *p_queue) { #endif } -void D3D12Context::flush(bool p_flush_setup, bool p_flush_pending) { +void D3D12Context::flush(bool p_flush_setup, bool p_flush_pending, bool p_sync) { + ERR_FAIL_COND_MSG(!p_sync, "Flush without sync is not supported."); // This is a special case for Vulkan on mobile XR hardware, not applicable to D3D12 + if (p_flush_setup && command_list_queue[0]) { md.queue->ExecuteCommandLists(1, command_list_queue.ptr()); command_list_queue[0] = nullptr; @@ -1054,27 +1063,6 @@ void D3D12Context::local_device_free(RID p_local_device) { local_device_owner.free(p_local_device); } -void D3D12Context::command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { -#ifdef PIX_ENABLED - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - PIXBeginEvent(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name.utf8().get_data()); -#endif -} - -void D3D12Context::command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { -#ifdef PIX_ENABLED - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - PIXSetMarker(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name.utf8().get_data()); -#endif -} - -void D3D12Context::command_end_label(RDD::CommandBufferID p_command_buffer) { -#ifdef PIX_ENABLED - const RenderingDeviceDriverD3D12::CommandBufferInfo *cmd_buf_info = (const RenderingDeviceDriverD3D12::CommandBufferInfo *)p_command_buffer.id; - PIXEndEvent(cmd_buf_info->cmd_list.Get()); -#endif -} - void D3D12Context::set_object_name(ID3D12Object *p_object, String p_object_name) { ERR_FAIL_NULL(p_object); int name_len = p_object_name.size(); @@ -1123,6 +1111,14 @@ RenderingDeviceDriver *D3D12Context::get_driver(RID p_local_device) { } } +bool D3D12Context::is_debug_utils_enabled() const { +#ifdef PIX_ENABLED + return true; +#else + return false; +#endif +} + D3D12Context::D3D12Context() { command_list_queue.resize(1); // First one is always the setup command. command_list_queue[0] = nullptr; diff --git a/drivers/d3d12/d3d12_context.h b/drivers/d3d12/d3d12_context.h index 27ec93bb7d..ac90d38467 100644 --- a/drivers/d3d12/d3d12_context.h +++ b/drivers/d3d12/d3d12_context.h @@ -234,15 +234,12 @@ public: virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) override final; virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) override final; void resize_notify(); - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false) override final; + virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) override final; virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) override final; virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) override final; virtual Error swap_buffers() override final; virtual Error initialize() override final; - virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; - virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; - virtual void command_end_label(RDD::CommandBufferID p_command_buffer) override final; void set_object_name(ID3D12Object *p_object, String p_object_name); virtual String get_device_vendor_name() const override final; @@ -255,6 +252,7 @@ public: virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const override final; virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) override final; + virtual bool is_debug_utils_enabled() const override final; D3D12Context(); virtual ~D3D12Context(); diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp index 6a2a3c32b0..d3a7344c9d 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.cpp +++ b/drivers/d3d12/rendering_device_driver_d3d12.cpp @@ -5162,6 +5162,20 @@ void RenderingDeviceDriverD3D12::command_timestamp_write(CommandBufferID p_cmd_b cmd_buf_info->cmd_list->ResolveQueryData(tqp_info->query_heap.Get(), D3D12_QUERY_TYPE_TIMESTAMP, p_index, tqp_info->query_count, results_buffer, p_index * sizeof(uint64_t)); } +void RenderingDeviceDriverD3D12::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) { +#ifdef PIX_ENABLED + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + PIXBeginEvent(cmd_buf_info->cmd_list.Get(), p_color.to_argb32(), p_label_name); +#endif +} + +void RenderingDeviceDriverD3D12::command_end_label(CommandBufferID p_cmd_buffer) { +#ifdef PIX_ENABLED + const CommandBufferInfo *cmd_buf_info = (const CommandBufferInfo *)p_cmd_buffer.id; + PIXEndEvent(cmd_buf_info->cmd_list.Get()); +#endif +} + /****************/ /**** SCREEN ****/ /****************/ diff --git a/drivers/d3d12/rendering_device_driver_d3d12.h b/drivers/d3d12/rendering_device_driver_d3d12.h index bd19572878..0da339c6fd 100644 --- a/drivers/d3d12/rendering_device_driver_d3d12.h +++ b/drivers/d3d12/rendering_device_driver_d3d12.h @@ -771,6 +771,13 @@ public: virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final; /****************/ + /**** LABELS ****/ + /****************/ + + virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final; + virtual void command_end_label(CommandBufferID p_cmd_buffer) override final; + + /****************/ /**** SCREEN ****/ /****************/ diff --git a/drivers/egl/egl_manager.h b/drivers/egl/egl_manager.h index c3a749cd19..61d3289b2d 100644 --- a/drivers/egl/egl_manager.h +++ b/drivers/egl/egl_manager.h @@ -45,7 +45,7 @@ class EGLManager { private: - // An EGL-side rappresentation of a display with its own rendering + // An EGL-side representation of a display with its own rendering // context. struct GLDisplay { void *display = nullptr; diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp index 996e7eee7f..c6fb6ca70b 100644 --- a/drivers/gles3/effects/copy_effects.cpp +++ b/drivers/gles3/effects/copy_effects.cpp @@ -125,6 +125,18 @@ void CopyEffects::copy_to_rect(const Rect2 &p_rect) { draw_screen_quad(); } +void CopyEffects::copy_to_and_from_rect(const Rect2 &p_rect) { + bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + if (!success) { + return; + } + + copy.shader.version_set_uniform(CopyShaderGLES3::COPY_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + copy.shader.version_set_uniform(CopyShaderGLES3::SOURCE_SECTION, p_rect.position.x, p_rect.position.y, p_rect.size.x, p_rect.size.y, copy.shader_version, CopyShaderGLES3::MODE_COPY_SECTION_SOURCE); + + draw_screen_quad(); +} + void CopyEffects::copy_screen() { bool success = copy.shader.version_bind_shader(copy.shader_version, CopyShaderGLES3::MODE_DEFAULT); if (!success) { diff --git a/drivers/gles3/effects/copy_effects.h b/drivers/gles3/effects/copy_effects.h index 6e2cb07382..509d07b955 100644 --- a/drivers/gles3/effects/copy_effects.h +++ b/drivers/gles3/effects/copy_effects.h @@ -62,6 +62,7 @@ public: // These functions assume that a framebuffer and texture are bound already. They only manage the shader, uniforms, and vertex array. void copy_to_rect(const Rect2 &p_rect); + void copy_to_and_from_rect(const Rect2 &p_rect); void copy_screen(); void copy_cube_to_rect(const Rect2 &p_rect); void bilinear_blur(GLuint p_source_texture, int p_mipmap_count, const Rect2i &p_region); diff --git a/drivers/gles3/rasterizer_gles3.cpp b/drivers/gles3/rasterizer_gles3.cpp index c048e9bf37..6ab98a9473 100644 --- a/drivers/gles3/rasterizer_gles3.cpp +++ b/drivers/gles3/rasterizer_gles3.cpp @@ -103,6 +103,11 @@ void RasterizerGLES3::begin_frame(double frame_step) { } void RasterizerGLES3::end_frame(bool p_swap_buffers) { + GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); + utils->capture_timestamps_end(); +} + +void RasterizerGLES3::end_viewport(bool p_swap_buffers) { if (p_swap_buffers) { DisplayServer::get_singleton()->swap_buffers(); } else { @@ -352,13 +357,6 @@ RasterizerGLES3::~RasterizerGLES3() { } void RasterizerGLES3::prepare_for_blitting_render_targets() { - // This is a hack, but this function is called one time after all viewports have been updated. - // So it marks the end of the frame for all viewports - // In the OpenGL renderer we have to call end_frame for each viewport so we can swap the - // buffers for each window before proceeding to the next. - // This allows us to only increment the frame after all viewports are done. - GLES3::Utilities *utils = GLES3::Utilities::get_singleton(); - utils->capture_timestamps_end(); } void RasterizerGLES3::_blit_render_target_to_screen(RID p_render_target, DisplayServer::WindowID p_screen, const Rect2 &p_screen_rect, uint32_t p_layer, bool p_first) { @@ -474,7 +472,7 @@ void RasterizerGLES3::set_boot_image(const Ref<Image> &p_image, const Color &p_c copy_effects->copy_to_rect(screenrect); glBindTexture(GL_TEXTURE_2D, 0); - end_frame(true); + end_viewport(true); texture_storage->texture_free(texture); } diff --git a/drivers/gles3/rasterizer_gles3.h b/drivers/gles3/rasterizer_gles3.h index b19ca0e9c9..d9f436a2ec 100644 --- a/drivers/gles3/rasterizer_gles3.h +++ b/drivers/gles3/rasterizer_gles3.h @@ -93,6 +93,7 @@ public: void prepare_for_blitting_render_targets(); void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); + void end_viewport(bool p_swap_buffers); void end_frame(bool p_swap_buffers); void finalize(); diff --git a/drivers/gles3/shaders/copy.glsl b/drivers/gles3/shaders/copy.glsl index 265acc1430..f37968a4fd 100644 --- a/drivers/gles3/shaders/copy.glsl +++ b/drivers/gles3/shaders/copy.glsl @@ -3,6 +3,7 @@ mode_default = #define MODE_SIMPLE_COPY mode_copy_section = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY +mode_copy_section_source = #define USE_COPY_SECTION \n#define MODE_SIMPLE_COPY \n#define MODE_COPY_FROM mode_gaussian_blur = #define MODE_GAUSSIAN_BLUR mode_mipmap = #define MODE_MIPMAP mode_simple_color = #define MODE_SIMPLE_COLOR \n#define USE_COPY_SECTION @@ -21,7 +22,7 @@ out vec2 uv_interp; // Defined in 0-1 coords. uniform highp vec4 copy_section; #endif -#ifdef MODE_GAUSSIAN_BLUR +#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM) uniform highp vec4 source_section; #endif @@ -32,7 +33,7 @@ void main() { #if defined(USE_COPY_SECTION) || defined(MODE_GAUSSIAN_BLUR) gl_Position.xy = (copy_section.xy + uv_interp.xy * copy_section.zw) * 2.0 - 1.0; #endif -#ifdef MODE_GAUSSIAN_BLUR +#if defined(MODE_GAUSSIAN_BLUR) || defined(MODE_COPY_FROM) uv_interp = source_section.xy + uv_interp * source_section.zw; #endif } diff --git a/drivers/gles3/shaders/scene.glsl b/drivers/gles3/shaders/scene.glsl index 1d9ba623c4..e95d684763 100644 --- a/drivers/gles3/shaders/scene.glsl +++ b/drivers/gles3/shaders/scene.glsl @@ -607,8 +607,7 @@ layout(std140) uniform GlobalShaderUniformData { //ubo:1 vec4 global_shader_uniforms[MAX_GLOBAL_SHADER_UNIFORMS]; }; - /* Material Uniforms */ - +/* Material Uniforms */ #ifdef MATERIAL_UNIFORMS_USED /* clang-format off */ diff --git a/drivers/gles3/storage/texture_storage.cpp b/drivers/gles3/storage/texture_storage.cpp index a44352a3e4..5a59f6c772 100644 --- a/drivers/gles3/storage/texture_storage.cpp +++ b/drivers/gles3/storage/texture_storage.cpp @@ -1475,7 +1475,7 @@ void TextureStorage::update_texture_atlas() { //generate atlas Vector<TextureAtlas::SortItem> itemsv; itemsv.resize(texture_atlas.textures.size()); - int base_size = 8; + uint32_t base_size = 8; int idx = 0; @@ -1488,7 +1488,7 @@ void TextureStorage::update_texture_atlas() { si.size.height = (src_tex->height / border) + 1; si.pixel_size = Size2i(src_tex->width, src_tex->height); - if (base_size < si.size.width) { + if (base_size < (uint32_t)si.size.width) { base_size = nearest_power_of_2_templated(si.size.width); } @@ -1519,7 +1519,7 @@ void TextureStorage::update_texture_atlas() { TextureAtlas::SortItem &si = items[i]; int best_idx = -1; int best_height = 0x7FFFFFFF; - for (int j = 0; j <= base_size - si.size.width; j++) { + for (uint32_t j = 0; j <= base_size - si.size.width; j++) { int height = 0; for (int k = 0; k < si.size.width; k++) { int h = v_offsets[k + j]; @@ -1550,7 +1550,7 @@ void TextureStorage::update_texture_atlas() { } } - if (max_height <= base_size * 2) { + if ((uint32_t)max_height <= base_size * 2) { atlas_height = max_height; break; //good ratio, break; } @@ -2647,7 +2647,10 @@ void TextureStorage::render_target_copy_to_back_buffer(RID p_render_target, cons glBindFramebuffer(GL_FRAMEBUFFER, rt->backbuffer_fbo); glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, rt->color); - GLES3::CopyEffects::get_singleton()->copy_screen(); + Rect2 normalized_region = region; + normalized_region.position = normalized_region.position / Size2(rt->size); + normalized_region.size = normalized_region.size / Size2(rt->size); + GLES3::CopyEffects::get_singleton()->copy_to_and_from_rect(normalized_region); if (p_gen_mipmaps) { GLES3::CopyEffects::get_singleton()->gaussian_blur(rt->backbuffer, rt->mipmap_count, region, rt->size); diff --git a/drivers/png/image_loader_png.cpp b/drivers/png/image_loader_png.cpp index cbcb54bc11..6f98f072dd 100644 --- a/drivers/png/image_loader_png.cpp +++ b/drivers/png/image_loader_png.cpp @@ -66,12 +66,14 @@ Ref<Image> ImageLoaderPNG::load_mem_png(const uint8_t *p_png, int p_size) { return img; } +Ref<Image> ImageLoaderPNG::unpack_mem_png(const uint8_t *p_png, int p_size) { + ERR_FAIL_COND_V(p_size < 4, Ref<Image>()); + ERR_FAIL_COND_V(p_png[0] != 'P' || p_png[1] != 'N' || p_png[2] != 'G' || p_png[3] != ' ', Ref<Image>()); + return load_mem_png(&p_png[4], p_size - 4); +} + Ref<Image> ImageLoaderPNG::lossless_unpack_png(const Vector<uint8_t> &p_data) { - const int len = p_data.size(); - ERR_FAIL_COND_V(len < 4, Ref<Image>()); - const uint8_t *r = p_data.ptr(); - ERR_FAIL_COND_V(r[0] != 'P' || r[1] != 'N' || r[2] != 'G' || r[3] != ' ', Ref<Image>()); - return load_mem_png(&r[4], len - 4); + return unpack_mem_png(p_data.ptr(), p_data.size()); } Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { @@ -99,6 +101,7 @@ Vector<uint8_t> ImageLoaderPNG::lossless_pack_png(const Ref<Image> &p_image) { ImageLoaderPNG::ImageLoaderPNG() { Image::_png_mem_loader_func = load_mem_png; + Image::_png_mem_unpacker_func = unpack_mem_png; Image::png_unpacker = lossless_unpack_png; Image::png_packer = lossless_pack_png; } diff --git a/drivers/png/image_loader_png.h b/drivers/png/image_loader_png.h index d587672dd1..ecce9a405b 100644 --- a/drivers/png/image_loader_png.h +++ b/drivers/png/image_loader_png.h @@ -37,6 +37,7 @@ class ImageLoaderPNG : public ImageFormatLoader { private: static Vector<uint8_t> lossless_pack_png(const Ref<Image> &p_image); static Ref<Image> lossless_unpack_png(const Vector<uint8_t> &p_data); + static Ref<Image> unpack_mem_png(const uint8_t *p_png, int p_size); static Ref<Image> load_mem_png(const uint8_t *p_png, int p_size); public: diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp index e18161c974..34acc0cc15 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.cpp +++ b/drivers/vulkan/rendering_device_driver_vulkan.cpp @@ -733,6 +733,9 @@ RDD::TextureID RenderingDeviceDriverVulkan::texture_create_shared_from_slice(Tex case TEXTURE_SLICE_2D_ARRAY: { image_view_create_info.viewType = VK_IMAGE_VIEW_TYPE_2D_ARRAY; } break; + default: { + return TextureID(nullptr); + } } image_view_create_info.format = RD_TO_VK_FORMAT[p_view.format]; image_view_create_info.components.r = (VkComponentSwizzle)p_view.swizzle_r; @@ -1172,7 +1175,7 @@ bool RenderingDeviceDriverVulkan::command_buffer_begin_secondary(CommandBufferID VkCommandBufferBeginInfo cmd_buf_begin_info = {}; cmd_buf_begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; - cmd_buf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_SIMULTANEOUS_USE_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; + cmd_buf_begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT | VK_COMMAND_BUFFER_USAGE_RENDER_PASS_CONTINUE_BIT; cmd_buf_begin_info.pInheritanceInfo = &inheritance_info; VkResult err = vkBeginCommandBuffer((VkCommandBuffer)p_cmd_buffer.id, &cmd_buf_begin_info); @@ -1557,11 +1560,9 @@ RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_bytecode(const Vec read_offset += sizeof(ShaderBinary::SpecializationConstant); } - struct Stage { - ShaderStage type = SHADER_STAGE_MAX; - Vector<uint8_t> spirv; - }; - Vector<Stage> stages; + Vector<Vector<uint8_t>> stages_spirv; + stages_spirv.resize(binary_data.stage_count); + r_shader_desc.stages.resize(binary_data.stage_count); for (uint32_t i = 0; i < binary_data.stage_count; i++) { ERR_FAIL_COND_V(read_offset + sizeof(uint32_t) * 3 >= binsize, ShaderID()); @@ -1587,17 +1588,14 @@ RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_bytecode(const Vec src_smolv = binptr + read_offset; } - Vector<uint8_t> spirv; + Vector<uint8_t> &spirv = stages_spirv.ptrw()[i]; uint32_t spirv_size = smolv::GetDecodedBufferSize(src_smolv, smolv_size); spirv.resize(spirv_size); if (!smolv::Decode(src_smolv, smolv_size, spirv.ptrw(), spirv_size)) { ERR_FAIL_V_MSG(ShaderID(), "Malformed smolv input uncompressing shader stage:" + String(SHADER_STAGE_NAMES[stage])); } - Stage stage_entry; - stage_entry.type = ShaderStage(stage); - stage_entry.spirv = spirv; - stages.push_back(stage_entry); + r_shader_desc.stages.set(i, ShaderStage(stage)); if (buf_size % 4 != 0) { buf_size += 4 - (buf_size % 4); @@ -1614,22 +1612,22 @@ RDD::ShaderID RenderingDeviceDriverVulkan::shader_create_from_bytecode(const Vec String error_text; - for (int i = 0; i < stages.size(); i++) { + for (int i = 0; i < r_shader_desc.stages.size(); i++) { VkShaderModuleCreateInfo shader_module_create_info = {}; shader_module_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; - shader_module_create_info.codeSize = stages[i].spirv.size(); - shader_module_create_info.pCode = (const uint32_t *)stages[i].spirv.ptr(); + shader_module_create_info.codeSize = stages_spirv[i].size(); + shader_module_create_info.pCode = (const uint32_t *)stages_spirv[i].ptr(); VkShaderModule vk_module = VK_NULL_HANDLE; VkResult res = vkCreateShaderModule(vk_device, &shader_module_create_info, nullptr, &vk_module); if (res) { - error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(SHADER_STAGE_NAMES[stages[i].type]); + error_text = "Error (" + itos(res) + ") creating shader module for stage: " + String(SHADER_STAGE_NAMES[r_shader_desc.stages[i]]); break; } VkPipelineShaderStageCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; - create_info.stage = RD_STAGE_TO_VK_SHADER_STAGE_BITS[stages[i].type]; + create_info.stage = RD_STAGE_TO_VK_SHADER_STAGE_BITS[r_shader_desc.stages[i]]; create_info.module = vk_module; create_info.pName = "main"; @@ -3054,6 +3052,26 @@ void RenderingDeviceDriverVulkan::command_timestamp_write(CommandBufferID p_cmd_ } /****************/ +/**** LABELS ****/ +/****************/ + +void RenderingDeviceDriverVulkan::command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) { + VkDebugUtilsLabelEXT label; + label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; + label.pNext = nullptr; + label.pLabelName = p_label_name; + label.color[0] = p_color[0]; + label.color[1] = p_color[1]; + label.color[2] = p_color[2]; + label.color[3] = p_color[3]; + vkCmdBeginDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id, &label); +} + +void RenderingDeviceDriverVulkan::command_end_label(CommandBufferID p_cmd_buffer) { + vkCmdEndDebugUtilsLabelEXT((VkCommandBuffer)p_cmd_buffer.id); +} + +/****************/ /**** SCREEN ****/ /****************/ diff --git a/drivers/vulkan/rendering_device_driver_vulkan.h b/drivers/vulkan/rendering_device_driver_vulkan.h index 6d8f6fd0e0..1edee6b76e 100644 --- a/drivers/vulkan/rendering_device_driver_vulkan.h +++ b/drivers/vulkan/rendering_device_driver_vulkan.h @@ -433,6 +433,13 @@ public: virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) override final; /****************/ + /**** LABELS ****/ + /****************/ + + virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final; + virtual void command_end_label(CommandBufferID p_cmd_buffer) override final; + + /****************/ /**** SCREEN ****/ /****************/ diff --git a/drivers/vulkan/vulkan_context.cpp b/drivers/vulkan/vulkan_context.cpp index 1b1d4fa50f..7db2a9cd66 100644 --- a/drivers/vulkan/vulkan_context.cpp +++ b/drivers/vulkan/vulkan_context.cpp @@ -2395,9 +2395,11 @@ void VulkanContext::append_command_buffer(RDD::CommandBufferID p_command_buffer) command_buffer_count++; } -void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { +void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending, bool p_sync) { // Ensure everything else pending is executed. - vkDeviceWaitIdle(device); + if (p_sync) { + vkDeviceWaitIdle(device); + } // Flush the pending setup buffer. @@ -2440,7 +2442,9 @@ void VulkanContext::flush(bool p_flush_setup, bool p_flush_pending) { ERR_FAIL_COND(err); } - vkDeviceWaitIdle(device); + if (p_sync) { + vkDeviceWaitIdle(device); + } } Error VulkanContext::prepare_buffers(RDD::CommandBufferID p_command_buffer) { @@ -2504,7 +2508,7 @@ Error VulkanContext::swap_buffers() { return OK; } - // print_line("swapbuffers?"); + //print_line("swap_buffers"); VkResult err; #if 0 @@ -2818,46 +2822,6 @@ void VulkanContext::local_device_free(RID p_local_device) { local_device_owner.free(p_local_device); } -void VulkanContext::command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - - CharString cs = p_label_name.utf8(); - VkDebugUtilsLabelEXT label; - label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; - label.pNext = nullptr; - label.pLabelName = cs.get_data(); - label.color[0] = p_color[0]; - label.color[1] = p_color[1]; - label.color[2] = p_color[2]; - label.color[3] = p_color[3]; - CmdBeginDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id, &label); -} - -void VulkanContext::command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - CharString cs = p_label_name.utf8(); - VkDebugUtilsLabelEXT label; - label.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_LABEL_EXT; - label.pNext = nullptr; - label.pLabelName = cs.get_data(); - label.color[0] = p_color[0]; - label.color[1] = p_color[1]; - label.color[2] = p_color[2]; - label.color[3] = p_color[3]; - CmdInsertDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id, &label); -} - -void VulkanContext::command_end_label(RDD::CommandBufferID p_command_buffer) { - if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { - return; - } - CmdEndDebugUtilsLabelEXT((VkCommandBuffer)p_command_buffer.id); -} - void VulkanContext::set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) { if (!is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME)) { return; @@ -2913,6 +2877,10 @@ RenderingDeviceDriver *VulkanContext::get_driver(RID p_local_device) { } } +bool VulkanContext::is_debug_utils_enabled() const { + return is_instance_extension_enabled(VK_EXT_DEBUG_UTILS_EXTENSION_NAME); +} + VulkanContext::VulkanContext() { command_buffer_queue.resize(1); // First one is always the setup command. command_buffer_queue[0] = nullptr; diff --git a/drivers/vulkan/vulkan_context.h b/drivers/vulkan/vulkan_context.h index ce1299a559..cbb6cf326f 100644 --- a/drivers/vulkan/vulkan_context.h +++ b/drivers/vulkan/vulkan_context.h @@ -321,15 +321,12 @@ public: virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) override final; virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) override final; void resize_notify(); - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false) override final; + virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) override final; virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) override final; virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) override final; virtual Error swap_buffers() override final; virtual Error initialize() override final; - virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; - virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) override final; - virtual void command_end_label(RDD::CommandBufferID p_command_buffer) override final; void set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name); virtual String get_device_vendor_name() const override final; @@ -342,6 +339,7 @@ public: virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const override final; virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) override final; + virtual bool is_debug_utils_enabled() const override final; VulkanContext(); virtual ~VulkanContext(); diff --git a/drivers/wasapi/audio_driver_wasapi.cpp b/drivers/wasapi/audio_driver_wasapi.cpp index e39373e7a0..64f2d1f203 100644 --- a/drivers/wasapi/audio_driver_wasapi.cpp +++ b/drivers/wasapi/audio_driver_wasapi.cpp @@ -557,13 +557,11 @@ Error AudioDriverWASAPI::init() { target_latency_ms = Engine::get_singleton()->get_audio_output_latency(); - Error err = init_output_device(); - if (err != OK) { - ERR_PRINT("WASAPI: init_output_device error"); - } - exit_thread.clear(); + Error err = init_output_device(); + ERR_FAIL_COND_V_MSG(err != OK, err, "WASAPI: init_output_device error."); + thread.start(thread_func, this); return OK; diff --git a/editor/animation_bezier_editor.cpp b/editor/animation_bezier_editor.cpp index 08306256f1..ce9bb1c92b 100644 --- a/editor/animation_bezier_editor.cpp +++ b/editor/animation_bezier_editor.cpp @@ -1520,7 +1520,7 @@ void AnimationBezierTrackEdit::_zoom_callback(float p_zoom_factor, Vector2 p_ori // Alternate zoom (doesn't affect timeline). timeline_v_zoom = CLAMP(timeline_v_zoom * p_zoom_factor, 0.000001, 100000); } else { - timeline->get_zoom()->set_value(timeline->get_zoom()->get_value() / p_zoom_factor); + timeline->_zoom_callback(p_zoom_factor, p_origin, p_event); } timeline_v_scroll = timeline_v_scroll + (p_origin.y - get_size().y / 2.0) * (timeline_v_zoom - v_zoom_orig); queue_redraw(); @@ -1688,6 +1688,7 @@ void AnimationBezierTrackEdit::_bind_methods() { AnimationBezierTrackEdit::AnimationBezierTrackEdit() { panner.instantiate(); panner->set_callbacks(callable_mp(this, &AnimationBezierTrackEdit::_pan_callback), callable_mp(this, &AnimationBezierTrackEdit::_zoom_callback)); + panner->set_scroll_zoom_factor(AnimationTimelineEdit::SCROLL_ZOOM_FACTOR); play_position = memnew(Control); play_position->set_mouse_filter(MOUSE_FILTER_PASS); diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp index d9d28e5c09..475ed6dc3c 100644 --- a/editor/animation_track_editor.cpp +++ b/editor/animation_track_editor.cpp @@ -1247,6 +1247,58 @@ void AnimationMultiTrackKeyEdit::set_use_fps(bool p_enable) { } void AnimationTimelineEdit::_zoom_changed(double) { + double zoom_pivot = 0; // Point on timeline to stay fixed. + double zoom_pivot_delta = 0; // Delta seconds from left-most point on timeline to zoom pivot. + + int timeline_width_pixels = get_size().width - get_buttons_width() - get_name_limit(); + double timeline_width_seconds = timeline_width_pixels / last_zoom_scale; // Length (in seconds) of visible part of timeline before zoom. + double updated_timeline_width_seconds = timeline_width_pixels / get_zoom_scale(); // Length after zoom. + double updated_timeline_half_width = updated_timeline_width_seconds / 2.0; + bool zooming = updated_timeline_width_seconds < timeline_width_seconds; + + double timeline_left = get_value(); + double timeline_right = timeline_left + timeline_width_seconds; + double timeline_center = timeline_left + timeline_width_seconds / 2.0; + + if (zoom_callback_occured) { // Zooming with scroll wheel will focus on the position of the mouse. + double zoom_scroll_origin_norm = (zoom_scroll_origin.x - get_name_limit()) / timeline_width_pixels; + zoom_scroll_origin_norm = MAX(zoom_scroll_origin_norm, 0); + zoom_pivot = timeline_left + timeline_width_seconds * zoom_scroll_origin_norm; + zoom_pivot_delta = updated_timeline_width_seconds * zoom_scroll_origin_norm; + zoom_callback_occured = false; + } else { // Zooming with slider will depend on the current play position. + // If the play position is not in range, or exactly in the center, zoom in on the center. + if (get_play_position() < timeline_left || get_play_position() > timeline_left + timeline_width_seconds || get_play_position() == timeline_center) { + zoom_pivot = timeline_center; + zoom_pivot_delta = updated_timeline_half_width; + } + // Zoom from right if play position is right of center, + // and shrink from right if play position is left of center. + else if ((get_play_position() > timeline_center) == zooming) { + // If play position crosses to other side of center, center it. + bool center_passed = (get_play_position() < timeline_right - updated_timeline_half_width) == zooming; + zoom_pivot = center_passed ? get_play_position() : timeline_right; + double center_offset = CMP_EPSILON * (zooming ? 1 : -1); // Small offset to prevent crossover. + zoom_pivot_delta = center_passed ? updated_timeline_half_width + center_offset : updated_timeline_width_seconds; + } + // Zoom from left if play position is left of center, + // and shrink from left if play position is right of center. + else if ((get_play_position() <= timeline_center) == zooming) { + // If play position crosses to other side of center, center it. + bool center_passed = (get_play_position() > timeline_left + updated_timeline_half_width) == zooming; + zoom_pivot = center_passed ? get_play_position() : timeline_left; + double center_offset = CMP_EPSILON * (zooming ? -1 : 1); // Small offset to prevent crossover. + zoom_pivot_delta = center_passed ? updated_timeline_half_width + center_offset : 0; + } + } + + double hscroll_pos = zoom_pivot - zoom_pivot_delta; + hscroll_pos = CLAMP(hscroll_pos, hscroll->get_min(), hscroll->get_max()); + + hscroll->set_value(hscroll_pos); + hscroll_on_zoom_buffer = hscroll_pos; // In case of page update. + last_zoom_scale = get_zoom_scale(); + queue_redraw(); play_position->queue_redraw(); emit_signal(SNAME("zoom_changed")); @@ -1428,6 +1480,11 @@ void AnimationTimelineEdit::_notification(int p_what) { set_page(zoomw / scale); + if (hscroll->is_visible() && hscroll_on_zoom_buffer >= 0) { + hscroll->set_value(hscroll_on_zoom_buffer); + hscroll_on_zoom_buffer = -1.0; + } + int end_px = (l - get_value()) * scale; int begin_px = -get_value() * scale; Color notimecol = get_theme_color(SNAME("dark_color_2"), EditorStringName(Editor)); @@ -1733,7 +1790,9 @@ void AnimationTimelineEdit::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> void AnimationTimelineEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { double current_zoom_value = get_zoom()->get_value(); - get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); + zoom_scroll_origin = p_origin; + zoom_callback_occured = true; + get_zoom()->set_value(MAX(0.01, current_zoom_value - (1.0 - p_zoom_factor))); } void AnimationTimelineEdit::set_use_fps(bool p_use_fps) { @@ -1809,6 +1868,7 @@ AnimationTimelineEdit::AnimationTimelineEdit() { len_hb->hide(); panner.instantiate(); + panner->set_scroll_zoom_factor(SCROLL_ZOOM_FACTOR); panner->set_callbacks(callable_mp(this, &AnimationTimelineEdit::_pan_callback), callable_mp(this, &AnimationTimelineEdit::_zoom_callback)); panner->set_pan_axis(ViewPanner::PAN_AXIS_HORIZONTAL); @@ -5493,8 +5553,7 @@ void AnimationTrackEditor::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p } void AnimationTrackEditor::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputEvent> p_event) { - double current_zoom_value = timeline->get_zoom()->get_value(); - timeline->get_zoom()->set_value(MAX(0.01, current_zoom_value * p_zoom_factor)); + timeline->_zoom_callback(p_zoom_factor, p_origin, p_event); } void AnimationTrackEditor::_cancel_bezier_edit() { @@ -6075,9 +6134,12 @@ void AnimationTrackEditor::_edit_menu_pressed(int p_option) { if (animation->track_get_type(sk.track) == Animation::TYPE_VALUE) { undo_redo->add_do_method(reset.ptr(), "value_track_set_update_mode", dst_track, animation->value_track_get_update_mode(sk.track)); - undo_redo->add_do_method(reset.ptr(), "track_set_interpolation_type", dst_track, animation->track_get_interpolation_type(sk.track)); - undo_redo->add_do_method(reset.ptr(), "track_set_interpolation_loop_wrap", dst_track, animation->track_get_interpolation_loop_wrap(sk.track)); } + if (animation->track_get_type(sk.track) == Animation::TYPE_AUDIO) { + undo_redo->add_do_method(reset.ptr(), "audio_track_set_use_blend", dst_track, animation->audio_track_is_use_blend(sk.track)); + } + undo_redo->add_do_method(reset.ptr(), "track_set_interpolation_type", dst_track, animation->track_get_interpolation_type(sk.track)); + undo_redo->add_do_method(reset.ptr(), "track_set_interpolation_loop_wrap", dst_track, animation->track_get_interpolation_loop_wrap(sk.track)); undo_redo->add_do_method(reset.ptr(), "track_insert_key", dst_track, 0, animation->track_get_key_value(sk.track, sk.key), animation->track_get_key_transition(sk.track, sk.key)); undo_redo->add_undo_method(reset.ptr(), "track_remove_key_at_time", dst_track, 0); @@ -6530,6 +6592,7 @@ AnimationTrackEditor::AnimationTrackEditor() { timeline->connect("length_changed", callable_mp(this, &AnimationTrackEditor::_update_length)); panner.instantiate(); + panner->set_scroll_zoom_factor(AnimationTimelineEdit::SCROLL_ZOOM_FACTOR); panner->set_callbacks(callable_mp(this, &AnimationTrackEditor::_pan_callback), callable_mp(this, &AnimationTrackEditor::_zoom_callback)); scroll = memnew(ScrollContainer); diff --git a/editor/animation_track_editor.h b/editor/animation_track_editor.h index 0667b8e80e..b5242e2f67 100644 --- a/editor/animation_track_editor.h +++ b/editor/animation_track_editor.h @@ -131,6 +131,11 @@ protected: class AnimationTimelineEdit : public Range { GDCLASS(AnimationTimelineEdit, Range); + friend class AnimationBezierTrackEdit; + friend class AnimationTrackEditor; + + static constexpr float SCROLL_ZOOM_FACTOR = 1.02f; // Zoom factor per mouse scroll in the animation editor. The closer to 1.0, the finer the control. + Ref<Animation> animation; bool read_only = false; @@ -167,6 +172,11 @@ class AnimationTimelineEdit : public Range { bool dragging_hsize = false; float dragging_hsize_from = 0.0f; float dragging_hsize_at = 0.0f; + double last_zoom_scale = 1.0; + double hscroll_on_zoom_buffer = -1.0; + + Vector2 zoom_scroll_origin; + bool zoom_callback_occured = false; virtual void gui_input(const Ref<InputEvent> &p_event) override; void _track_added(int p_track); diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.cpp b/editor/debugger/debug_adapter/debug_adapter_parser.cpp index 929f323e80..e854681010 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_parser.cpp @@ -44,7 +44,7 @@ void DebugAdapterParser::_bind_methods() { ClassDB::bind_method(D_METHOD("req_attach", "params"), &DebugAdapterParser::req_attach); ClassDB::bind_method(D_METHOD("req_restart", "params"), &DebugAdapterParser::req_restart); ClassDB::bind_method(D_METHOD("req_terminate", "params"), &DebugAdapterParser::req_terminate); - ClassDB::bind_method(D_METHOD("req_configurationDone", "params"), &DebugAdapterParser::prepare_success_response); + ClassDB::bind_method(D_METHOD("req_configurationDone", "params"), &DebugAdapterParser::req_configurationDone); ClassDB::bind_method(D_METHOD("req_pause", "params"), &DebugAdapterParser::req_pause); ClassDB::bind_method(D_METHOD("req_continue", "params"), &DebugAdapterParser::req_continue); ClassDB::bind_method(D_METHOD("req_threads", "params"), &DebugAdapterParser::req_threads); @@ -180,6 +180,13 @@ Dictionary DebugAdapterParser::req_launch(const Dictionary &p_params) const { DebugAdapterProtocol::get_singleton()->get_current_peer()->supportsCustomData = args["godot/custom_data"]; } + DebugAdapterProtocol::get_singleton()->get_current_peer()->pending_launch = p_params; + + return Dictionary(); +} + +Dictionary DebugAdapterParser::_launch_process(const Dictionary &p_params) const { + Dictionary args = p_params["arguments"]; ScriptEditorDebugger *dbg = EditorDebuggerNode::get_singleton()->get_default_debugger(); if ((bool)args["noDebug"] != dbg->is_skip_breakpoints()) { dbg->debug_skip_breakpoints(); @@ -246,7 +253,7 @@ Dictionary DebugAdapterParser::req_restart(const Dictionary &p_params) const { args = args["arguments"]; params["arguments"] = args; - Dictionary response = DebugAdapterProtocol::get_singleton()->get_current_peer()->attached ? req_attach(params) : req_launch(params); + Dictionary response = DebugAdapterProtocol::get_singleton()->get_current_peer()->attached ? req_attach(params) : _launch_process(params); if (!response["success"]) { response["command"] = p_params["command"]; return response; @@ -261,6 +268,16 @@ Dictionary DebugAdapterParser::req_terminate(const Dictionary &p_params) const { return prepare_success_response(p_params); } +Dictionary DebugAdapterParser::req_configurationDone(const Dictionary &p_params) const { + Ref<DAPeer> peer = DebugAdapterProtocol::get_singleton()->get_current_peer(); + if (!peer->pending_launch.is_empty()) { + peer->res_queue.push_back(_launch_process(peer->pending_launch)); + peer->pending_launch.clear(); + } + + return prepare_success_response(p_params); +} + Dictionary DebugAdapterParser::req_pause(const Dictionary &p_params) const { EditorRunBar::get_singleton()->get_pause_button()->set_pressed(true); EditorDebuggerNode::get_singleton()->_paused(); diff --git a/editor/debugger/debug_adapter/debug_adapter_parser.h b/editor/debugger/debug_adapter/debug_adapter_parser.h index 9995066ab4..e5493a4b9f 100644 --- a/editor/debugger/debug_adapter/debug_adapter_parser.h +++ b/editor/debugger/debug_adapter/debug_adapter_parser.h @@ -71,6 +71,7 @@ public: Dictionary req_attach(const Dictionary &p_params) const; Dictionary req_restart(const Dictionary &p_params) const; Dictionary req_terminate(const Dictionary &p_params) const; + Dictionary req_configurationDone(const Dictionary &p_params) const; Dictionary req_pause(const Dictionary &p_params) const; Dictionary req_continue(const Dictionary &p_params) const; Dictionary req_threads(const Dictionary &p_params) const; @@ -84,6 +85,9 @@ public: Dictionary req_evaluate(const Dictionary &p_params) const; Dictionary req_godot_put_msg(const Dictionary &p_params) const; + // Internal requests + Dictionary _launch_process(const Dictionary &p_params) const; + // Events Dictionary ev_initialized() const; Dictionary ev_process(const String &p_command) const; diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp index 26fb73570e..7417a3d8f7 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp @@ -678,7 +678,10 @@ bool DebugAdapterProtocol::process_message(const String &p_text) { if (!response.is_empty()) { _current_peer->res_queue.push_front(response); } else { - completed = false; + // Launch request needs to be deferred until we receive a configurationDone request. + if (command != "req_launch") { + completed = false; + } } } diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.h b/editor/debugger/debug_adapter/debug_adapter_protocol.h index ddc55816db..fb1c533bbb 100644 --- a/editor/debugger/debug_adapter/debug_adapter_protocol.h +++ b/editor/debugger/debug_adapter/debug_adapter_protocol.h @@ -63,6 +63,7 @@ struct DAPeer : RefCounted { // Internal client info bool attached = false; + Dictionary pending_launch; Error handle_data(); Error send_data(); diff --git a/editor/editor_command_palette.cpp b/editor/editor_command_palette.cpp index 337271ea3d..ce5d8619e1 100644 --- a/editor/editor_command_palette.cpp +++ b/editor/editor_command_palette.cpp @@ -74,7 +74,7 @@ void EditorCommandPalette::_update_command_search(const String &search_text) { CommandEntry r; r.key_name = E.key; r.display_name = E.value.name; - r.shortcut_text = E.value.shortcut; + r.shortcut_text = E.value.shortcut_text; r.last_used = E.value.last_used; bool is_subsequence_of_key_name = search_text.is_subsequence_ofn(r.key_name); @@ -166,6 +166,19 @@ void EditorCommandPalette::_notification(int p_what) { case NOTIFICATION_THEME_CHANGED: { command_search_box->set_right_icon(get_editor_theme_icon(SNAME("Search"))); } break; + + case EditorSettings::NOTIFICATION_EDITOR_SETTINGS_CHANGED: { + if (!EditorSettings::get_singleton()->check_changed_settings_in_group("shortcuts")) { + break; + } + + for (KeyValue<String, Command> &kv : commands) { + Command &c = kv.value; + if (c.shortcut.is_valid()) { + c.shortcut_text = c.shortcut->get_as_text(); + } + } + } break; } } @@ -219,7 +232,7 @@ void EditorCommandPalette::remove_command(String p_key_name) { commands.erase(p_key_name); } -void EditorCommandPalette::add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, String p_shortcut_text) { +void EditorCommandPalette::add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, const Ref<Shortcut> &p_shortcut) { ERR_FAIL_COND_MSG(commands.has(p_key_name), "The Command '" + String(p_command_name) + "' already exists. Unable to add it."); const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * arguments.size()); @@ -229,7 +242,12 @@ void EditorCommandPalette::add_command(String p_command_name, String p_key_name, Command command; command.name = p_command_name; command.callable = p_action.bindp(argptrs, arguments.size()); - command.shortcut = p_shortcut_text; + if (p_shortcut.is_null()) { + command.shortcut_text = "None"; + } else { + command.shortcut = p_shortcut; + command.shortcut_text = p_shortcut->get_as_text(); + } commands[p_key_name] = command; } @@ -240,7 +258,7 @@ void EditorCommandPalette::_add_command(String p_command_name, String p_key_name Command command; command.name = p_command_name; command.callable = p_binded_action; - command.shortcut = p_shortcut_text; + command.shortcut_text = p_shortcut_text; // Commands added from plugins don't exist yet when the history is loaded, so we assign the last use time here if it was recorded. Dictionary command_history = EditorSettings::get_singleton()->get_project_metadata("command_palette", "command_history", Dictionary()); @@ -273,8 +291,7 @@ void EditorCommandPalette::register_shortcuts_as_command() { Ref<InputEventShortcut> ev; ev.instantiate(); ev->set_shortcut(shortcut); - String shortcut_text = String(shortcut->get_as_text()); - add_command(command_name, E.key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), shortcut_text); + add_command(command_name, E.key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), shortcut); } unregistered_shortcuts.clear(); @@ -294,8 +311,7 @@ Ref<Shortcut> EditorCommandPalette::add_shortcut_command(const String &p_command Ref<InputEventShortcut> ev; ev.instantiate(); ev->set_shortcut(p_shortcut); - String shortcut_text = String(p_shortcut->get_as_text()); - add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), shortcut_text); + add_command(p_command, p_key, callable_mp(EditorNode::get_singleton()->get_viewport(), &Viewport::push_input), varray(ev, false), p_shortcut); } else { const String key_name = String(p_key); const String command_name = String(p_command); diff --git a/editor/editor_command_palette.h b/editor/editor_command_palette.h index 5d666aeaa3..cc0f726b3a 100644 --- a/editor/editor_command_palette.h +++ b/editor/editor_command_palette.h @@ -46,7 +46,8 @@ class EditorCommandPalette : public ConfirmationDialog { struct Command { Callable callable; String name; - String shortcut; + Ref<Shortcut> shortcut; + String shortcut_text; int last_used = 0; // Store time as int, because doubles have problems with text serialization. }; @@ -93,7 +94,7 @@ protected: public: void open_popup(); void get_actions_list(List<String> *p_list) const; - void add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, String p_shortcut_text = "None"); + void add_command(String p_command_name, String p_key_name, Callable p_action, Vector<Variant> arguments, const Ref<Shortcut> &p_shortcut); void execute_command(const String &p_command_name); void register_shortcuts_as_command(); Ref<Shortcut> add_shortcut_command(const String &p_command, const String &p_key, Ref<Shortcut> p_shortcut); diff --git a/editor/editor_interface.cpp b/editor/editor_interface.cpp index af1d23a4fe..b007f6d530 100644 --- a/editor/editor_interface.cpp +++ b/editor/editor_interface.cpp @@ -400,6 +400,21 @@ bool EditorInterface::is_movie_maker_enabled() const { } // Base. +void EditorInterface::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + String pf = p_function; + if (p_idx == 0) { + if (pf == "set_main_screen_editor") { + for (String E : { "\"2D\"", "\"3D\"", "\"Script\"", "\"AssetLib\"" }) { + r_options->push_back(E); + } + } else if (pf == "get_editor_viewport_3d") { + for (uint32_t i = 0; i < Node3DEditor::VIEWPORTS_COUNT; i++) { + r_options->push_back(String::num_int64(i)); + } + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} void EditorInterface::_bind_methods() { ClassDB::bind_method(D_METHOD("restart_editor", "save"), &EditorInterface::restart_editor, DEFVAL(true)); diff --git a/editor/editor_interface.h b/editor/editor_interface.h index 079fc169f2..73e89ae2f2 100644 --- a/editor/editor_interface.h +++ b/editor/editor_interface.h @@ -152,6 +152,8 @@ public: // Base. + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + static void create(); static void free(); diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index 183acbba12..549f62276d 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -113,9 +113,12 @@ #include "editor/gui/editor_title_bar.h" #include "editor/gui/editor_toaster.h" #include "editor/history_dock.h" +#include "editor/import/3d/editor_import_collada.h" +#include "editor/import/3d/resource_importer_obj.h" +#include "editor/import/3d/resource_importer_scene.h" +#include "editor/import/3d/scene_import_settings.h" #include "editor/import/audio_stream_import_settings.h" #include "editor/import/dynamic_font_import_settings.h" -#include "editor/import/editor_import_collada.h" #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_bmfont.h" #include "editor/import/resource_importer_csv_translation.h" @@ -123,12 +126,10 @@ #include "editor/import/resource_importer_image.h" #include "editor/import/resource_importer_imagefont.h" #include "editor/import/resource_importer_layered_texture.h" -#include "editor/import/resource_importer_obj.h" #include "editor/import/resource_importer_shader_file.h" #include "editor/import/resource_importer_texture.h" #include "editor/import/resource_importer_texture_atlas.h" #include "editor/import/resource_importer_wav.h" -#include "editor/import/scene_import_settings.h" #include "editor/import_dock.h" #include "editor/inspector_dock.h" #include "editor/multi_node_edit.h" @@ -3012,6 +3013,16 @@ void EditorNode::_menu_option_confirm(int p_option, bool p_confirmed) { } break; case SET_RENDERER_NAME_SAVE_AND_RESTART: { ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method", renderer_request); + if (renderer_request == "mobile" || renderer_request == "gl_compatibility") { + // Also change the mobile override if changing to a compatible rendering method. + // This prevents visual discrepancies between desktop and mobile platforms. + ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", renderer_request); + } else if (renderer_request == "forward_plus") { + // Use the equivalent mobile rendering method. This prevents the rendering method from staying + // on its old choice if moving from `gl_compatibility` to `forward_plus`. + ProjectSettings::get_singleton()->set("rendering/renderer/rendering_method.mobile", "mobile"); + } + ProjectSettings::get_singleton()->save(); save_all_scenes(); @@ -6159,7 +6170,8 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins if (edited_scene_map.size() > 0) { // Reload the new instance. Error err; - Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + Ref<PackedScene> instance_scene_packed_scene = ResourceLoader::load(p_instance_path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); + instance_scene_packed_scene->set_path(p_instance_path, true); ERR_FAIL_COND(err != OK); ERR_FAIL_COND(instance_scene_packed_scene.is_null()); @@ -6266,7 +6278,8 @@ void EditorNode::reload_instances_with_path_in_edited_scenes(const String &p_ins // be properly updated. for (String path : required_load_paths) { if (!local_scene_cache.find(path)) { - current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_REPLACE, &err); + current_packed_scene = ResourceLoader::load(path, "", ResourceFormatLoader::CACHE_MODE_IGNORE, &err); + current_packed_scene->set_path(path, true); local_scene_cache[path] = current_packed_scene; } else { current_packed_scene = local_scene_cache[path]; @@ -6575,6 +6588,9 @@ void EditorNode::_renderer_selected(int p_which) { } renderer_request = rendering_method; + video_restart_dialog->set_text( + vformat(TTR("Changing the renderer requires restarting the editor.\n\nChoosing Save & Restart will change the rendering method to:\n- Desktop platforms: %s\n- Mobile platforms: %s\n- Web platform: gl_compatibility"), + renderer_request, renderer_request.replace("forward_plus", "mobile"))); video_restart_dialog->popup_centered(); renderer->select(renderer_current); _update_renderer_color(); @@ -7552,7 +7568,7 @@ EditorNode::EditorNode() { renderer->set_focus_mode(Control::FOCUS_NONE); renderer->add_theme_font_override("font", theme->get_font(SNAME("bold"), EditorStringName(EditorFonts))); renderer->add_theme_font_size_override("font_size", theme->get_font_size(SNAME("bold_size"), EditorStringName(EditorFonts))); - renderer->set_tooltip_text(TTR("Choose a renderer.")); + renderer->set_tooltip_text(TTR("Choose a rendering method.\n\nNotes:\n- On mobile platforms, the Mobile rendering method is used if Forward+ is selected here.\n- On the web platform, the Compatibility rendering method is always used.")); right_menu_hb->add_child(renderer); @@ -7594,7 +7610,6 @@ EditorNode::EditorNode() { _update_renderer_color(); video_restart_dialog = memnew(ConfirmationDialog); - video_restart_dialog->set_text(TTR("Changing the renderer requires restarting the editor.")); video_restart_dialog->set_ok_button_text(TTR("Save & Restart")); video_restart_dialog->connect("confirmed", callable_mp(this, &EditorNode::_menu_option).bind(SET_RENDERER_NAME_SAVE_AND_RESTART)); gui_base->add_child(video_restart_dialog); diff --git a/editor/editor_plugin.cpp b/editor/editor_plugin.cpp index 94a49166b8..e79f662cc9 100644 --- a/editor/editor_plugin.cpp +++ b/editor/editor_plugin.cpp @@ -39,8 +39,8 @@ #include "editor/editor_undo_redo_manager.h" #include "editor/export/editor_export.h" #include "editor/gui/editor_title_bar.h" +#include "editor/import/3d/resource_importer_scene.h" #include "editor/import/editor_import_plugin.h" -#include "editor/import/resource_importer_scene.h" #include "editor/inspector_dock.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/editor_debugger_plugin.h" diff --git a/editor/editor_resource_picker.cpp b/editor/editor_resource_picker.cpp index ed20e50685..011cb26621 100644 --- a/editor/editor_resource_picker.cpp +++ b/editor/editor_resource_picker.cpp @@ -675,7 +675,7 @@ bool EditorResourcePicker::_is_drop_valid(const Dictionary &p_drag_data) const { return false; } -bool EditorResourcePicker::_is_type_valid(const String p_type_name, HashSet<StringName> p_allowed_types) const { +bool EditorResourcePicker::_is_type_valid(const String p_type_name, const HashSet<StringName> &p_allowed_types) const { for (const StringName &E : p_allowed_types) { String at = E; if (p_type_name == at || ClassDB::is_parent_class(p_type_name, at) || EditorNode::get_editor_data().script_class_is_parent(p_type_name, at)) { diff --git a/editor/editor_resource_picker.h b/editor/editor_resource_picker.h index 35703bcbeb..fb54455e89 100644 --- a/editor/editor_resource_picker.h +++ b/editor/editor_resource_picker.h @@ -99,7 +99,7 @@ class EditorResourcePicker : public HBoxContainer { String _get_resource_type(const Ref<Resource> &p_resource) const; void _get_allowed_types(bool p_with_convert, HashSet<StringName> *p_vector) const; bool _is_drop_valid(const Dictionary &p_drag_data) const; - bool _is_type_valid(const String p_type_name, HashSet<StringName> p_allowed_types) const; + bool _is_type_valid(const String p_type_name, const HashSet<StringName> &p_allowed_types) const; Variant get_drag_data_fw(const Point2 &p_point, Control *p_from); bool can_drop_data_fw(const Point2 &p_point, const Variant &p_data, Control *p_from) const; diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp index 5d84e4f78f..b22f70b0fa 100644 --- a/editor/export/project_export.cpp +++ b/editor/export/project_export.cpp @@ -1284,7 +1284,7 @@ ProjectExportDialog::ProjectExportDialog() { ClassDB::get_inheriters_from_class("Resource", &resource_names); PackedStringArray strippable; - for (StringName resource_name : resource_names) { + for (const StringName &resource_name : resource_names) { if (ClassDB::has_method(resource_name, "create_placeholder", true)) { strippable.push_back(resource_name); } diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 91528d90aa..12a3478e44 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -47,7 +47,7 @@ #include "editor/editor_string_names.h" #include "editor/gui/editor_dir_dialog.h" #include "editor/gui/editor_scene_tabs.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" #include "editor/import_dock.h" #include "editor/plugins/editor_resource_tooltip_plugins.h" #include "editor/scene_create_dialog.h" diff --git a/editor/groups_editor.cpp b/editor/groups_editor.cpp index 9fa9fe5a9d..77a867f34b 100644 --- a/editor/groups_editor.cpp +++ b/editor/groups_editor.cpp @@ -274,20 +274,22 @@ void GroupsEditor::_update_groups_and_tree() { _update_tree(); } -void GroupsEditor::_update_scene_groups(Node *p_node) { - if (scene_groups_cache.has(p_node)) { - scene_groups = scene_groups_cache[p_node]; - scene_groups_cache.erase(p_node); +void GroupsEditor::_update_scene_groups(const ObjectID &p_id) { + HashMap<ObjectID, HashMap<StringName, bool>>::Iterator I = scene_groups_cache.find(p_id); + if (I) { + scene_groups = I->value; + scene_groups_cache.remove(I); } else { scene_groups = HashMap<StringName, bool>(); } } -void GroupsEditor::_cache_scene_groups(Node *p_node) { +void GroupsEditor::_cache_scene_groups(const ObjectID &p_id) { const int edited_scene_count = EditorNode::get_editor_data().get_edited_scene_count(); for (int i = 0; i < edited_scene_count; i++) { - if (p_node == EditorNode::get_editor_data().get_edited_scene_root(i)) { - scene_groups_cache[p_node] = scene_groups_for_caching; + Node *edited_scene_root = EditorNode::get_editor_data().get_edited_scene_root(i); + if (edited_scene_root && p_id == edited_scene_root->get_instance_id()) { + scene_groups_cache[p_id] = scene_groups_for_caching; break; } } @@ -305,7 +307,7 @@ void GroupsEditor::set_current(Node *p_node) { if (scene_tree->get_edited_scene_root() != scene_root_node) { scene_root_node = scene_tree->get_edited_scene_root(); - _update_scene_groups(scene_root_node); + _update_scene_groups(scene_root_node->get_instance_id()); _update_groups(); } @@ -803,7 +805,7 @@ void GroupsEditor::_bind_methods() { void GroupsEditor::_node_removed(Node *p_node) { if (scene_root_node == p_node) { scene_groups_for_caching = scene_groups; - callable_mp(this, &GroupsEditor::_cache_scene_groups).call_deferred(p_node); + callable_mp(this, &GroupsEditor::_cache_scene_groups).call_deferred(p_node->get_instance_id()); scene_root_node = nullptr; } diff --git a/editor/groups_editor.h b/editor/groups_editor.h index cf74470b1b..8a0ca067de 100644 --- a/editor/groups_editor.h +++ b/editor/groups_editor.h @@ -78,14 +78,14 @@ class GroupsEditor : public VBoxContainer { Button *add = nullptr; Tree *tree = nullptr; - HashMap<Node *, HashMap<StringName, bool>> scene_groups_cache; + HashMap<ObjectID, HashMap<StringName, bool>> scene_groups_cache; HashMap<StringName, bool> scene_groups_for_caching; HashMap<StringName, bool> scene_groups; HashMap<StringName, String> global_groups; - void _update_scene_groups(Node *p_node); - void _cache_scene_groups(Node *p_node); + void _update_scene_groups(const ObjectID &p_id); + void _cache_scene_groups(const ObjectID &p_id); void _show_add_group_dialog(); void _show_rename_group_dialog(); diff --git a/editor/import/collada.cpp b/editor/import/3d/collada.cpp index ae8041d6fe..ae8041d6fe 100644 --- a/editor/import/collada.cpp +++ b/editor/import/3d/collada.cpp diff --git a/editor/import/collada.h b/editor/import/3d/collada.h index 7877b1e86d..7877b1e86d 100644 --- a/editor/import/collada.h +++ b/editor/import/3d/collada.h diff --git a/editor/import/editor_import_collada.cpp b/editor/import/3d/editor_import_collada.cpp index fb61412cf4..49fec679bf 100644 --- a/editor/import/editor_import_collada.cpp +++ b/editor/import/3d/editor_import_collada.cpp @@ -32,7 +32,7 @@ #include "core/os/os.h" #include "editor/editor_node.h" -#include "editor/import/collada.h" +#include "editor/import/3d/collada.h" #include "scene/3d/camera_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/light_3d.h" diff --git a/editor/import/editor_import_collada.h b/editor/import/3d/editor_import_collada.h index 2950f499cb..e0214c2fd0 100644 --- a/editor/import/editor_import_collada.h +++ b/editor/import/3d/editor_import_collada.h @@ -31,7 +31,7 @@ #ifndef EDITOR_IMPORT_COLLADA_H #define EDITOR_IMPORT_COLLADA_H -#include "editor/import/resource_importer_scene.h" +#include "editor/import/3d/resource_importer_scene.h" class EditorSceneFormatImporterCollada : public EditorSceneFormatImporter { GDCLASS(EditorSceneFormatImporterCollada, EditorSceneFormatImporter); diff --git a/editor/import/post_import_plugin_skeleton_renamer.cpp b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp index adeea51dae..ffe75f189c 100644 --- a/editor/import/post_import_plugin_skeleton_renamer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_renamer.cpp @@ -30,7 +30,7 @@ #include "post_import_plugin_skeleton_renamer.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/skeleton_3d.h" @@ -45,7 +45,7 @@ void PostImportPluginSkeletonRenamer::get_internal_import_options(InternalImport } } -void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, HashMap<String, String> p_rename_map) { +void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, const HashMap<String, String> &p_rename_map) { // Prepare objects. Object *map = p_options["retarget/bone_map"].get_validated_object(); if (!map || !bool(p_options["retarget/bone_renamer/rename_bones"])) { @@ -121,7 +121,7 @@ void PostImportPluginSkeletonRenamer::_internal_process(InternalImportCategory p // Rename bones in all Nodes by calling method. { Dictionary rename_map_dict; - for (HashMap<String, String>::Iterator E = p_rename_map.begin(); E; ++E) { + for (HashMap<String, String>::ConstIterator E = p_rename_map.begin(); E; ++E) { rename_map_dict[E->key] = E->value; } diff --git a/editor/import/post_import_plugin_skeleton_renamer.h b/editor/import/3d/post_import_plugin_skeleton_renamer.h index 98f778f6c2..a977117374 100644 --- a/editor/import/post_import_plugin_skeleton_renamer.h +++ b/editor/import/3d/post_import_plugin_skeleton_renamer.h @@ -40,7 +40,7 @@ public: virtual void get_internal_import_options(InternalImportCategory p_category, List<ResourceImporter::ImportOption> *r_options) override; virtual void internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options) override; - void _internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, HashMap<String, String> p_rename_map); + void _internal_process(InternalImportCategory p_category, Node *p_base_scene, Node *p_node, Ref<Resource> p_resource, const Dictionary &p_options, const HashMap<String, String> &p_rename_map); PostImportPluginSkeletonRenamer(); }; diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp index 3390bf4ed4..53bf24fb7e 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.cpp @@ -30,7 +30,7 @@ #include "post_import_plugin_skeleton_rest_fixer.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" #include "scene/3d/bone_attachment_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" #include "scene/3d/skeleton_3d.h" diff --git a/editor/import/post_import_plugin_skeleton_rest_fixer.h b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.h index c765169fd0..c765169fd0 100644 --- a/editor/import/post_import_plugin_skeleton_rest_fixer.h +++ b/editor/import/3d/post_import_plugin_skeleton_rest_fixer.h diff --git a/editor/import/post_import_plugin_skeleton_track_organizer.cpp b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp index e5a8e879fc..53bcc59fcb 100644 --- a/editor/import/post_import_plugin_skeleton_track_organizer.cpp +++ b/editor/import/3d/post_import_plugin_skeleton_track_organizer.cpp @@ -30,7 +30,7 @@ #include "post_import_plugin_skeleton_track_organizer.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" #include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/bone_map.h" diff --git a/editor/import/post_import_plugin_skeleton_track_organizer.h b/editor/import/3d/post_import_plugin_skeleton_track_organizer.h index 24a4f70aa9..24a4f70aa9 100644 --- a/editor/import/post_import_plugin_skeleton_track_organizer.h +++ b/editor/import/3d/post_import_plugin_skeleton_track_organizer.h diff --git a/editor/import/resource_importer_obj.cpp b/editor/import/3d/resource_importer_obj.cpp index 082e78fdbe..082e78fdbe 100644 --- a/editor/import/resource_importer_obj.cpp +++ b/editor/import/3d/resource_importer_obj.cpp diff --git a/editor/import/resource_importer_obj.h b/editor/import/3d/resource_importer_obj.h index faf0f336c0..faf0f336c0 100644 --- a/editor/import/resource_importer_obj.h +++ b/editor/import/3d/resource_importer_obj.h diff --git a/editor/import/resource_importer_scene.cpp b/editor/import/3d/resource_importer_scene.cpp index 46c62b1125..dfb14c6741 100644 --- a/editor/import/resource_importer_scene.cpp +++ b/editor/import/3d/resource_importer_scene.cpp @@ -35,7 +35,7 @@ #include "core/object/script_language.h" #include "editor/editor_node.h" #include "editor/editor_settings.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" #include "scene/3d/area_3d.h" #include "scene/3d/collision_shape_3d.h" #include "scene/3d/importer_mesh_instance_3d.h" diff --git a/editor/import/resource_importer_scene.h b/editor/import/3d/resource_importer_scene.h index 6ea4d1af7d..6ea4d1af7d 100644 --- a/editor/import/resource_importer_scene.h +++ b/editor/import/3d/resource_importer_scene.h diff --git a/editor/import/scene_import_settings.cpp b/editor/import/3d/scene_import_settings.cpp index 4ecc6dedbd..4ecc6dedbd 100644 --- a/editor/import/scene_import_settings.cpp +++ b/editor/import/3d/scene_import_settings.cpp diff --git a/editor/import/scene_import_settings.h b/editor/import/3d/scene_import_settings.h index 3e71d5da17..c6c416daba 100644 --- a/editor/import/scene_import_settings.h +++ b/editor/import/3d/scene_import_settings.h @@ -31,7 +31,7 @@ #ifndef SCENE_IMPORT_SETTINGS_H #define SCENE_IMPORT_SETTINGS_H -#include "editor/import/resource_importer_scene.h" +#include "editor/import/3d/resource_importer_scene.h" #include "scene/3d/camera_3d.h" #include "scene/3d/light_3d.h" #include "scene/3d/mesh_instance_3d.h" diff --git a/editor/import/SCsub b/editor/import/SCsub index 359d04e5df..a8c06cc406 100644 --- a/editor/import/SCsub +++ b/editor/import/SCsub @@ -3,3 +3,4 @@ Import("env") env.add_source_files(env.editor_sources, "*.cpp") +env.add_source_files(env.editor_sources, "3d/*.cpp") diff --git a/editor/import/audio_stream_import_settings.cpp b/editor/import/audio_stream_import_settings.cpp index 5414c6e74b..bc96191d33 100644 --- a/editor/import/audio_stream_import_settings.cpp +++ b/editor/import/audio_stream_import_settings.cpp @@ -277,6 +277,8 @@ void AudioStreamImportSettingsDialog::_draw_indicator() { rect.size.height -= y_ofs; } + _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); + float ofs_x = (_current - zoom_bar->get_value()) * rect.size.width / zoom_bar->get_page(); if (ofs_x < 0 || ofs_x >= rect.size.width) { return; @@ -310,8 +312,6 @@ void AudioStreamImportSettingsDialog::_draw_indicator() { } } } - - _current_label->set_text(String::num(_current, 2).pad_decimals(2) + " /"); } void AudioStreamImportSettingsDialog::_on_indicator_mouse_exited() { diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 5011c5cf18..5ffb89ac65 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -41,6 +41,7 @@ #include "editor/editor_string_names.h" #include "editor/editor_undo_redo_manager.h" #include "editor/gui/editor_file_dialog.h" +#include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/gui/check_box.h" #include "scene/gui/menu_button.h" diff --git a/editor/plugins/bone_map_editor_plugin.cpp b/editor/plugins/bone_map_editor_plugin.cpp index db61c95474..153f192838 100644 --- a/editor/plugins/bone_map_editor_plugin.cpp +++ b/editor/plugins/bone_map_editor_plugin.cpp @@ -32,10 +32,10 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" -#include "editor/import/post_import_plugin_skeleton_renamer.h" -#include "editor/import/post_import_plugin_skeleton_rest_fixer.h" -#include "editor/import/post_import_plugin_skeleton_track_organizer.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/post_import_plugin_skeleton_renamer.h" +#include "editor/import/3d/post_import_plugin_skeleton_rest_fixer.h" +#include "editor/import/3d/post_import_plugin_skeleton_track_organizer.h" +#include "editor/import/3d/scene_import_settings.h" #include "scene/gui/aspect_ratio_container.h" #include "scene/gui/separator.h" #include "scene/gui/texture_rect.h" @@ -95,7 +95,7 @@ void BoneMapperButton::_notification(int p_what) { } } -BoneMapperButton::BoneMapperButton(const StringName p_profile_bone_name, bool p_require, bool p_selected) { +BoneMapperButton::BoneMapperButton(const StringName &p_profile_bone_name, bool p_require, bool p_selected) { profile_bone_name = p_profile_bone_name; require = p_require; selected = p_selected; diff --git a/editor/plugins/bone_map_editor_plugin.h b/editor/plugins/bone_map_editor_plugin.h index 9ff32707c7..9479ed3730 100644 --- a/editor/plugins/bone_map_editor_plugin.h +++ b/editor/plugins/bone_map_editor_plugin.h @@ -78,7 +78,7 @@ public: bool is_require() const; - BoneMapperButton(const StringName p_profile_bone_name, bool p_require, bool p_selected); + BoneMapperButton(const StringName &p_profile_bone_name, bool p_require, bool p_selected); ~BoneMapperButton(); }; diff --git a/editor/plugins/canvas_item_editor_plugin.cpp b/editor/plugins/canvas_item_editor_plugin.cpp index 7d4502b69d..945b026a21 100644 --- a/editor/plugins/canvas_item_editor_plugin.cpp +++ b/editor/plugins/canvas_item_editor_plugin.cpp @@ -345,7 +345,7 @@ void CanvasItemEditor::_snap_other_nodes( } } -Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsigned int p_forced_modes, const CanvasItem *p_self_canvas_item, List<CanvasItem *> p_other_nodes_exceptions) { +Point2 CanvasItemEditor::snap_point(Point2 p_target, unsigned int p_modes, unsigned int p_forced_modes, const CanvasItem *p_self_canvas_item, const List<CanvasItem *> &p_other_nodes_exceptions) { snap_target[0] = SNAP_TARGET_NONE; snap_target[1] = SNAP_TARGET_NONE; @@ -535,7 +535,7 @@ void CanvasItemEditor::_keying_changed() { } } -Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(List<CanvasItem *> p_list) { +Rect2 CanvasItemEditor::_get_encompassing_rect_from_list(const List<CanvasItem *> &p_list) { ERR_FAIL_COND_V(p_list.is_empty(), Rect2()); // Handles the first element @@ -830,7 +830,7 @@ Vector2 CanvasItemEditor::_position_to_anchor(const Control *p_control, Vector2 return output; } -void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones) { +void CanvasItemEditor::_save_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool save_bones) { original_transform = Transform2D(); bool transform_stored = false; @@ -853,14 +853,14 @@ void CanvasItemEditor::_save_canvas_item_state(List<CanvasItem *> p_canvas_items } } -void CanvasItemEditor::_restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones) { +void CanvasItemEditor::_restore_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool restore_bones) { for (CanvasItem *ci : drag_selection) { CanvasItemEditorSelectedItem *se = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci); ci->_edit_set_state(se->undo_state); } } -void CanvasItemEditor::_commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones) { +void CanvasItemEditor::_commit_canvas_item_state(const List<CanvasItem *> &p_canvas_items, String action_name, bool commit_bones) { List<CanvasItem *> modified_canvas_items; for (CanvasItem *ci : p_canvas_items) { Dictionary old_state = editor_selection->get_node_editor_data<CanvasItemEditorSelectedItem>(ci)->undo_state; @@ -2014,14 +2014,14 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { if ((b->is_alt_pressed() && !b->is_command_or_control_pressed()) || tool == TOOL_MOVE) { List<CanvasItem *> selection = _get_edited_canvas_items(); - drag_selection.clear(); - for (int i = 0; i < selection.size(); i++) { - if (_is_node_movable(selection[i], true)) { - drag_selection.push_back(selection[i]); + if (selection.size() > 0) { + drag_selection.clear(); + for (int i = 0; i < selection.size(); i++) { + if (_is_node_movable(selection[i], true)) { + drag_selection.push_back(selection[i]); + } } - } - if (selection.size() > 0) { drag_type = DRAG_MOVE; CanvasItem *ci = selection[0]; @@ -2043,8 +2043,9 @@ bool CanvasItemEditor::_gui_input_move(const Ref<InputEvent> &p_event) { drag_from = transform.affine_inverse().xform(b->get_position()); _save_canvas_item_state(drag_selection); + + return true; } - return true; } } } @@ -2344,7 +2345,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { return true; } - if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && tool == TOOL_SELECT && !panner->is_panning()) { + if (b.is_valid() && b->get_button_index() == MouseButton::LEFT && b->is_pressed() && !panner->is_panning() && (tool == TOOL_SELECT || tool == TOOL_MOVE || tool == TOOL_SCALE || tool == TOOL_ROTATE)) { // Single item selection Point2 click = transform.affine_inverse().xform(b->get_position()); @@ -2379,7 +2380,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { } else { bool still_selected = _select_click_on_item(ci, click, b->is_shift_pressed()); // Start dragging - if (still_selected) { + if (still_selected && (tool == TOOL_SELECT || tool == TOOL_MOVE)) { // Drag the node(s) if requested drag_start_origin = click; drag_type = DRAG_QUEUED; @@ -2463,7 +2464,7 @@ bool CanvasItemEditor::_gui_input_select(const Ref<InputEvent> &p_event) { } } - if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true) && drag_type == DRAG_NONE && tool == TOOL_SELECT) { + if (k.is_valid() && k->is_action_pressed(SNAME("ui_cancel"), false, true) && drag_type == DRAG_NONE) { // Unselect everything editor_selection->clear(); viewport->queue_redraw(); @@ -5842,7 +5843,7 @@ void CanvasItemEditorViewport::_perform_drop_data() { Vector<String> error_files; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Create Node")); + undo_redo->create_action_for_history(TTR("Create Node"), EditorNode::get_editor_data().get_current_edited_scene_history_id()); for (int i = 0; i < selected_files.size(); i++) { String path = selected_files[i]; diff --git a/editor/plugins/canvas_item_editor_plugin.h b/editor/plugins/canvas_item_editor_plugin.h index 1939326321..7fe63e6282 100644 --- a/editor/plugins/canvas_item_editor_plugin.h +++ b/editor/plugins/canvas_item_editor_plugin.h @@ -393,9 +393,9 @@ private: CanvasItem *ref_item = nullptr; - void _save_canvas_item_state(List<CanvasItem *> p_canvas_items, bool save_bones = false); - void _restore_canvas_item_state(List<CanvasItem *> p_canvas_items, bool restore_bones = false); - void _commit_canvas_item_state(List<CanvasItem *> p_canvas_items, String action_name, bool commit_bones = false); + void _save_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool save_bones = false); + void _restore_canvas_item_state(const List<CanvasItem *> &p_canvas_items, bool restore_bones = false); + void _commit_canvas_item_state(const List<CanvasItem *> &p_canvas_items, String action_name, bool commit_bones = false); Vector2 _anchor_to_position(const Control *p_control, Vector2 anchor); Vector2 _position_to_anchor(const Control *p_control, Vector2 position); @@ -429,7 +429,7 @@ private: void _switch_theme_preview(int p_mode); List<CanvasItem *> _get_edited_canvas_items(bool retrieve_locked = false, bool remove_canvas_item_if_parent_in_selection = true) const; - Rect2 _get_encompassing_rect_from_list(List<CanvasItem *> p_list); + Rect2 _get_encompassing_rect_from_list(const List<CanvasItem *> &p_list); void _expand_encompassing_rect_using_children(Rect2 &r_rect, const Node *p_node, bool &r_first, const Transform2D &p_parent_xform = Transform2D(), const Transform2D &p_canvas_xform = Transform2D(), bool include_locked_nodes = true); Rect2 _get_encompassing_rect(const Node *p_node); @@ -545,7 +545,7 @@ public: SNAP_DEFAULT = SNAP_GRID | SNAP_GUIDES | SNAP_PIXEL, }; - Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, unsigned int p_forced_modes = 0, const CanvasItem *p_self_canvas_item = nullptr, List<CanvasItem *> p_other_nodes_exceptions = List<CanvasItem *>()); + Point2 snap_point(Point2 p_target, unsigned int p_modes = SNAP_DEFAULT, unsigned int p_forced_modes = 0, const CanvasItem *p_self_canvas_item = nullptr, const List<CanvasItem *> &p_other_nodes_exceptions = List<CanvasItem *>()); real_t snap_angle(real_t p_target, real_t p_start = 0) const; Transform2D get_canvas_transform() const { return transform; } diff --git a/editor/plugins/lightmap_gi_editor_plugin.cpp b/editor/plugins/lightmap_gi_editor_plugin.cpp index 11f60a166c..3b1b1e47cb 100644 --- a/editor/plugins/lightmap_gi_editor_plugin.cpp +++ b/editor/plugins/lightmap_gi_editor_plugin.cpp @@ -107,6 +107,9 @@ void LightmapGIEditorPlugin::_bake_select_file(const String &p_file) { case LightmapGI::BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL: { EditorNode::get_singleton()->show_warning(TTR("Maximum texture size is too small for the lightmap images.")); } break; + case LightmapGI::BAKE_ERROR_LIGHTMAP_TOO_SMALL: { + EditorNode::get_singleton()->show_warning(TTR("Failed creating lightmap images. Make sure all meshes selected to bake have `lightmap_size_hint` value set high enough, and `texel_scale` value of LightmapGI is not too low.")); + } break; default: { } break; } diff --git a/editor/plugins/node_3d_editor_plugin.cpp b/editor/plugins/node_3d_editor_plugin.cpp index 146fd54b6e..dc1f9f8bd2 100644 --- a/editor/plugins/node_3d_editor_plugin.cpp +++ b/editor/plugins/node_3d_editor_plugin.cpp @@ -1839,17 +1839,17 @@ void Node3DEditorViewport::_sinput(const Ref<InputEvent> &p_event) { clicked = ObjectID(); - if ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_or_control_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE) { + if (can_select_gizmos && ((spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SELECT && b->is_command_or_control_pressed()) || spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_ROTATE)) { begin_transform(TRANSFORM_ROTATE, false); break; } - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { + if (can_select_gizmos && spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_MOVE) { begin_transform(TRANSFORM_TRANSLATE, false); break; } - if (spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { + if (can_select_gizmos && spatial_editor->get_tool_mode() == Node3DEditor::TOOL_MODE_SCALE) { begin_transform(TRANSFORM_SCALE, false); break; } @@ -8061,6 +8061,8 @@ void Node3DEditor::_bind_methods() { ClassDB::bind_method("_clear_subgizmo_selection", &Node3DEditor::_clear_subgizmo_selection); ClassDB::bind_method("_refresh_menu_icons", &Node3DEditor::_refresh_menu_icons); + ClassDB::bind_method("update_all_gizmos", &Node3DEditor::update_all_gizmos); + ADD_SIGNAL(MethodInfo("transform_key_request")); ADD_SIGNAL(MethodInfo("item_lock_status_changed")); ADD_SIGNAL(MethodInfo("item_group_status_changed")); diff --git a/editor/plugins/path_2d_editor_plugin.cpp b/editor/plugins/path_2d_editor_plugin.cpp index 0d6086bb4d..6f44dfc755 100644 --- a/editor/plugins/path_2d_editor_plugin.cpp +++ b/editor/plugins/path_2d_editor_plugin.cpp @@ -37,6 +37,7 @@ #include "editor/editor_scale.h" #include "editor/editor_settings.h" #include "editor/editor_undo_redo_manager.h" +#include "scene/gui/dialogs.h" #include "scene/gui/menu_button.h" void Path2DEditor::_notification(int p_what) { @@ -48,6 +49,7 @@ void Path2DEditor::_notification(int p_what) { curve_create->set_icon(get_editor_theme_icon(SNAME("CurveCreate"))); curve_del->set_icon(get_editor_theme_icon(SNAME("CurveDelete"))); curve_close->set_icon(get_editor_theme_icon(SNAME("CurveClose"))); + curve_clear_points->set_icon(get_editor_theme_icon(SNAME("Clear"))); } break; } } @@ -68,7 +70,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { return false; } - if (!node->get_curve().is_valid()) { + if (node->get_curve().is_null()) { return false; } @@ -121,7 +123,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // Check for point deletion. EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - if ((mb->get_button_index() == MouseButton::RIGHT && mode == MODE_EDIT) || (mb->get_button_index() == MouseButton::LEFT && mode == MODE_DELETE)) { + if ((mb->get_button_index() == MouseButton::RIGHT && (mode == MODE_EDIT || mode == MODE_CREATE)) || (mb->get_button_index() == MouseButton::LEFT && mode == MODE_DELETE)) { if (dist_to_p < grab_threshold) { undo_redo->create_action(TTR("Remove Point from Curve")); undo_redo->add_do_method(curve.ptr(), "remove_point", i); @@ -154,16 +156,14 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // Check for point creation. if (mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT && ((mb->is_command_or_control_pressed() && mode == MODE_EDIT) || mode == MODE_CREATE)) { Ref<Curve2D> curve = node->get_curve(); + curve->add_point(cpoint); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Add Point to Curve")); undo_redo->add_do_method(curve.ptr(), "add_point", cpoint); - undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count()); - undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); - undo_redo->commit_action(); + undo_redo->add_undo_method(curve.ptr(), "remove_point", curve->get_point_count() - 1); - action = ACTION_MOVING_POINT; + action = ACTION_MOVING_NEW_POINT; action_point = curve->get_point_count() - 1; moving_from = curve->get_point_position(action_point); moving_screen_from = gpoint; @@ -191,15 +191,15 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { insertion_point = curve->get_point_count() - 2; } + const Vector2 new_point = xform.affine_inverse().xform(gpoint2); + curve->add_point(new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1); + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Split Curve")); - undo_redo->add_do_method(curve.ptr(), "add_point", xform.affine_inverse().xform(gpoint2), Vector2(0, 0), Vector2(0, 0), insertion_point + 1); + undo_redo->add_do_method(curve.ptr(), "add_point", new_point, Vector2(0, 0), Vector2(0, 0), insertion_point + 1); undo_redo->add_undo_method(curve.ptr(), "remove_point", insertion_point + 1); - undo_redo->add_do_method(canvas_item_editor, "update_viewport"); - undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); - undo_redo->commit_action(); - action = ACTION_MOVING_POINT; + action = ACTION_MOVING_NEW_POINT; action_point = insertion_point + 1; moving_from = curve->get_point_position(action_point); moving_screen_from = gpoint2; @@ -222,13 +222,16 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // N/A, handled in above condition. break; - case ACTION_MOVING_POINT: { - undo_redo->create_action(TTR("Move Point in Curve")); + case ACTION_MOVING_POINT: + case ACTION_MOVING_NEW_POINT: { + if (action == ACTION_MOVING_POINT) { + undo_redo->create_action(TTR("Move Point in Curve")); + undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from); + } undo_redo->add_do_method(curve.ptr(), "set_point_position", action_point, cpoint); - undo_redo->add_undo_method(curve.ptr(), "set_point_position", action_point, moving_from); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); - undo_redo->commit_action(); + undo_redo->commit_action(false); } break; @@ -334,7 +337,8 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { // N/A, handled in above condition. break; - case ACTION_MOVING_POINT: { + case ACTION_MOVING_POINT: + case ACTION_MOVING_NEW_POINT: { curve->set_point_position(action_point, cpoint); } break; @@ -364,7 +368,7 @@ bool Path2DEditor::forward_gui_input(const Ref<InputEvent> &p_event) { } void Path2DEditor::forward_canvas_draw_over_viewport(Control *p_overlay) { - if (!node || !node->is_visible_in_tree() || !node->get_curve().is_valid()) { + if (!node || !node->is_visible_in_tree() || node->get_curve().is_null()) { return; } @@ -437,12 +441,12 @@ void Path2DEditor::edit(Node *p_path2d) { if (p_path2d) { node = Object::cast_to<Path2D>(p_path2d); + if (!node->is_connected("visibility_changed", callable_mp(this, &Path2DEditor::_node_visibility_changed))) { node->connect("visibility_changed", callable_mp(this, &Path2DEditor::_node_visibility_changed)); } - } else { - // node may have been deleted at this point + // The node may have been deleted at this point. if (node && node->is_connected("visibility_changed", callable_mp(this, &Path2DEditor::_node_visibility_changed))) { node->disconnect("visibility_changed", callable_mp(this, &Path2DEditor::_node_visibility_changed)); } @@ -452,6 +456,8 @@ void Path2DEditor::edit(Node *p_path2d) { void Path2DEditor::_bind_methods() { //ClassDB::bind_method(D_METHOD("_menu_option"),&Path2DEditor::_menu_option); + ClassDB::bind_method(D_METHOD("_clear_curve_points"), &Path2DEditor::_clear_curve_points); + ClassDB::bind_method(D_METHOD("_restore_curve_points"), &Path2DEditor::_restore_curve_points); } void Path2DEditor::_mode_selected(int p_mode) { @@ -475,32 +481,46 @@ void Path2DEditor::_mode_selected(int p_mode) { curve_edit->set_pressed(false); curve_edit_curve->set_pressed(false); curve_del->set_pressed(true); - } else if (p_mode == ACTION_CLOSE) { - //? - - if (!node->get_curve().is_valid()) { + } else if (p_mode == MODE_CLOSE) { + if (node->get_curve().is_null()) { return; } if (node->get_curve()->get_point_count() < 3) { return; } - Vector2 begin = node->get_curve()->get_point_position(0); Vector2 end = node->get_curve()->get_point_position(node->get_curve()->get_point_count() - 1); + if (begin.is_equal_approx(end)) { return; } - EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - undo_redo->create_action(TTR("Remove Point from Curve")); + + undo_redo->create_action(TTR("Close the Curve")); undo_redo->add_do_method(node->get_curve().ptr(), "add_point", begin); undo_redo->add_undo_method(node->get_curve().ptr(), "remove_point", node->get_curve()->get_point_count()); undo_redo->add_do_method(canvas_item_editor, "update_viewport"); undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); undo_redo->commit_action(); return; - } + } else if (p_mode == MODE_CLEAR_POINTS) { + if (node->get_curve().is_null()) { + return; + } + if (node->get_curve()->get_point_count() == 0) { + return; + } + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + PackedVector2Array points = node->get_curve()->get_points().duplicate(); + undo_redo->create_action(TTR("Clear Curve Points"), UndoRedo::MERGE_DISABLE, node); + undo_redo->add_do_method(this, "_clear_curve_points", node); + undo_redo->add_undo_method(this, "_restore_curve_points", node, points); + undo_redo->add_do_method(canvas_item_editor, "update_viewport"); + undo_redo->add_undo_method(canvas_item_editor, "update_viewport"); + undo_redo->commit_action(); + return; + } mode = Mode(p_mode); } @@ -523,6 +543,52 @@ void Path2DEditor::_handle_option_pressed(int p_option) { } } +void Path2DEditor::_confirm_clear_points() { + if (!node || node->get_curve().is_null()) { + return; + } + if (node->get_curve()->get_point_count() == 0) { + return; + } + clear_points_dialog->reset_size(); + clear_points_dialog->popup_centered(); +} + +void Path2DEditor::_clear_curve_points(Path2D *p_path2d) { + if (!p_path2d || p_path2d->get_curve().is_null()) { + return; + } + Ref<Curve2D> curve = p_path2d->get_curve(); + + if (curve->get_point_count() == 0) { + return; + } + curve->clear_points(); + + if (node == p_path2d) { + _mode_selected(MODE_CREATE); + } +} + +void Path2DEditor::_restore_curve_points(Path2D *p_path2d, const PackedVector2Array &p_points) { + if (!p_path2d || p_path2d->get_curve().is_null()) { + return; + } + Ref<Curve2D> curve = p_path2d->get_curve(); + + if (curve->get_point_count() > 0) { + curve->clear_points(); + } + + for (int i = 0; i < p_points.size(); i += 3) { + curve->add_point(p_points[i + 2], p_points[i], p_points[i + 1]); // The Curve2D::points pattern is [point_in, point_out, point_position]. + } + + if (node == p_path2d) { + _mode_selected(MODE_EDIT); + } +} + Path2DEditor::Path2DEditor() { canvas_item_editor = nullptr; mirror_handle_angle = true; @@ -553,7 +619,7 @@ Path2DEditor::Path2DEditor() { curve_create->set_theme_type_variation("FlatButton"); curve_create->set_toggle_mode(true); curve_create->set_focus_mode(Control::FOCUS_NONE); - curve_create->set_tooltip_text(TTR("Add Point (in empty space)")); + curve_create->set_tooltip_text(TTR("Add Point (in empty space)") + "\n" + TTR("Right Click: Delete Point")); curve_create->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CREATE)); add_child(curve_create); @@ -569,9 +635,22 @@ Path2DEditor::Path2DEditor() { curve_close->set_theme_type_variation("FlatButton"); curve_close->set_focus_mode(Control::FOCUS_NONE); curve_close->set_tooltip_text(TTR("Close Curve")); - curve_close->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(ACTION_CLOSE)); + curve_close->connect("pressed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CLOSE)); add_child(curve_close); + curve_clear_points = memnew(Button); + curve_clear_points->set_theme_type_variation("FlatButton"); + curve_clear_points->set_focus_mode(Control::FOCUS_NONE); + curve_clear_points->set_tooltip_text(TTR("Clear Points")); + curve_clear_points->connect("pressed", callable_mp(this, &Path2DEditor::_confirm_clear_points)); + add_child(curve_clear_points); + + clear_points_dialog = memnew(ConfirmationDialog); + clear_points_dialog->set_title(TTR("Please Confirm...")); + clear_points_dialog->set_text(TTR("Remove all curve points?")); + clear_points_dialog->connect("confirmed", callable_mp(this, &Path2DEditor::_mode_selected).bind(MODE_CLEAR_POINTS)); + add_child(clear_points_dialog); + PopupMenu *menu; handle_menu = memnew(MenuButton); diff --git a/editor/plugins/path_2d_editor_plugin.h b/editor/plugins/path_2d_editor_plugin.h index b8816d9b1e..af9f307cc8 100644 --- a/editor/plugins/path_2d_editor_plugin.h +++ b/editor/plugins/path_2d_editor_plugin.h @@ -36,45 +36,51 @@ #include "scene/gui/box_container.h" class CanvasItemEditor; +class ConfirmationDialog; class MenuButton; class Path2DEditor : public HBoxContainer { GDCLASS(Path2DEditor, HBoxContainer); + friend class Path2DEditorPlugin; + CanvasItemEditor *canvas_item_editor = nullptr; Panel *panel = nullptr; Path2D *node = nullptr; - HBoxContainer *base_hb = nullptr; - enum Mode { MODE_CREATE, MODE_EDIT, MODE_EDIT_CURVE, MODE_DELETE, - ACTION_CLOSE + MODE_CLOSE, + MODE_CLEAR_POINTS, }; Mode mode; + Button *curve_clear_points = nullptr; + Button *curve_close = nullptr; Button *curve_create = nullptr; + Button *curve_del = nullptr; Button *curve_edit = nullptr; Button *curve_edit_curve = nullptr; - Button *curve_del = nullptr; - Button *curve_close = nullptr; MenuButton *handle_menu = nullptr; + ConfirmationDialog *clear_points_dialog = nullptr; + bool mirror_handle_angle; bool mirror_handle_length; bool on_edge; enum HandleOption { HANDLE_OPTION_ANGLE, - HANDLE_OPTION_LENGTH + HANDLE_OPTION_LENGTH, }; enum Action { ACTION_NONE, ACTION_MOVING_POINT, + ACTION_MOVING_NEW_POINT, ACTION_MOVING_IN, ACTION_MOVING_OUT, }; @@ -91,7 +97,10 @@ class Path2DEditor : public HBoxContainer { void _handle_option_pressed(int p_option); void _node_visibility_changed(); - friend class Path2DEditorPlugin; + + void _confirm_clear_points(); + void _clear_curve_points(Path2D *p_path2d); + void _restore_curve_points(Path2D *p_path2d, const PackedVector2Array &p_points); protected: void _notification(int p_what); diff --git a/editor/plugins/root_motion_editor_plugin.cpp b/editor/plugins/root_motion_editor_plugin.cpp index bc6507155a..b54a2f717d 100644 --- a/editor/plugins/root_motion_editor_plugin.cpp +++ b/editor/plugins/root_motion_editor_plugin.cpp @@ -32,6 +32,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" +#include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_mixer.h" #include "scene/gui/button.h" #include "scene/gui/dialogs.h" diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp index edf0b73356..eb1c5d249b 100644 --- a/editor/plugins/script_editor_plugin.cpp +++ b/editor/plugins/script_editor_plugin.cpp @@ -3386,7 +3386,6 @@ void ScriptEditor::_help_class_goto(const String &p_desc) { eh->set_name(cname); tab_container->add_child(eh); - _go_to_tab(tab_container->get_tab_count() - 1); eh->go_to_help(p_desc); eh->connect("go_to_help", callable_mp(this, &ScriptEditor::_help_class_goto)); _add_recent_script(eh->get_class()); diff --git a/editor/plugins/texture_editor_plugin.cpp b/editor/plugins/texture_editor_plugin.cpp index ec5785e605..9d640bf0dc 100644 --- a/editor/plugins/texture_editor_plugin.cpp +++ b/editor/plugins/texture_editor_plugin.cpp @@ -38,6 +38,7 @@ #include "scene/resources/atlas_texture.h" #include "scene/resources/compressed_texture.h" #include "scene/resources/image_texture.h" +#include "scene/resources/portable_compressed_texture.h" TextureRect *TexturePreview::get_texture_display() { return texture_display; @@ -158,7 +159,7 @@ TexturePreview::TexturePreview(Ref<Texture2D> p_texture, bool p_show_metadata) { } bool EditorInspectorPluginTexture::can_handle(Object *p_object) { - return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<CompressedTexture2D>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr || Object::cast_to<Image>(p_object) != nullptr; + return Object::cast_to<ImageTexture>(p_object) != nullptr || Object::cast_to<AtlasTexture>(p_object) != nullptr || Object::cast_to<CompressedTexture2D>(p_object) != nullptr || Object::cast_to<PortableCompressedTexture2D>(p_object) != nullptr || Object::cast_to<AnimatedTexture>(p_object) != nullptr || Object::cast_to<Image>(p_object) != nullptr; } void EditorInspectorPluginTexture::parse_begin(Object *p_object) { diff --git a/editor/plugins/theme_editor_plugin.cpp b/editor/plugins/theme_editor_plugin.cpp index bd66e5e9b7..cbe0f115d3 100644 --- a/editor/plugins/theme_editor_plugin.cpp +++ b/editor/plugins/theme_editor_plugin.cpp @@ -2374,7 +2374,7 @@ void ThemeTypeEditor::_update_type_list_debounced() { update_debounce_timer->start(); } -HashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default) { +HashMap<StringName, bool> ThemeTypeEditor::_get_type_items(String p_type_name, void (Theme::*get_list_func)(const StringName &, List<StringName> *) const, bool include_default) { HashMap<StringName, bool> items; List<StringName> names; diff --git a/editor/plugins/theme_editor_plugin.h b/editor/plugins/theme_editor_plugin.h index cf8c5ceb28..8ad262da55 100644 --- a/editor/plugins/theme_editor_plugin.h +++ b/editor/plugins/theme_editor_plugin.h @@ -373,7 +373,7 @@ class ThemeTypeEditor : public MarginContainer { VBoxContainer *_create_item_list(Theme::DataType p_data_type); void _update_type_list(); void _update_type_list_debounced(); - HashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(StringName, List<StringName> *) const, bool include_default); + HashMap<StringName, bool> _get_type_items(String p_type_name, void (Theme::*get_list_func)(const StringName &, List<StringName> *) const, bool include_default); HBoxContainer *_create_property_control(Theme::DataType p_data_type, String p_item_name, bool p_editable); void _add_focusable(Control *p_control); void _update_type_items(); diff --git a/editor/plugins/tiles/tile_data_editors.cpp b/editor/plugins/tiles/tile_data_editors.cpp index 221833d450..c8247e0551 100644 --- a/editor/plugins/tiles/tile_data_editors.cpp +++ b/editor/plugins/tiles/tile_data_editors.cpp @@ -112,16 +112,16 @@ bool DummyObject::_get(const StringName &p_name, Variant &r_ret) const { return false; } -bool DummyObject::has_dummy_property(StringName p_name) { +bool DummyObject::has_dummy_property(const StringName &p_name) { return properties.has(p_name); } -void DummyObject::add_dummy_property(StringName p_name) { +void DummyObject::add_dummy_property(const StringName &p_name) { ERR_FAIL_COND(properties.has(p_name)); properties[p_name] = Variant(); } -void DummyObject::remove_dummy_property(StringName p_name) { +void DummyObject::remove_dummy_property(const StringName &p_name) { ERR_FAIL_COND(!properties.has(p_name)); properties.erase(p_name); } @@ -719,11 +719,19 @@ void GenericTilePolygonEditor::set_tile_set(Ref<TileSet> p_tile_set) { Vector2 zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); while (zoomed_tile.y < default_control_y_size) { editor_zoom_widget->set_zoom_by_increments(6, false); - zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); + float current_zoom = editor_zoom_widget->get_zoom(); + zoomed_tile = current_zoom * tile_set->get_tile_size(); + if (Math::is_equal_approx(current_zoom, editor_zoom_widget->get_max_zoom())) { + break; + } } while (zoomed_tile.y > default_control_y_size) { editor_zoom_widget->set_zoom_by_increments(-6, false); - zoomed_tile = editor_zoom_widget->get_zoom() * tile_set->get_tile_size(); + float current_zoom = editor_zoom_widget->get_zoom(); + zoomed_tile = current_zoom * tile_set->get_tile_size(); + if (Math::is_equal_approx(current_zoom, editor_zoom_widget->get_min_zoom())) { + break; + } } editor_zoom_widget->set_zoom_by_increments(-6, false); _zoom_changed(); @@ -948,7 +956,7 @@ GenericTilePolygonEditor::GenericTilePolygonEditor() { _set_snap_option(EditorSettings::get_singleton()->get_project_metadata("editor_metadata", "tile_snap_option", SNAP_NONE)); } -void TileDataDefaultEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { +void TileDataDefaultEditor::_property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field) { ERR_FAIL_NULL(dummy_object); dummy_object->set(p_property, p_value); emit_signal(SNAME("needs_redraw")); @@ -981,7 +989,7 @@ Variant TileDataDefaultEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas_s return tile_data->get(property); } -void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { +void TileDataDefaultEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { Vector2i coords = E.key.get_atlas_coords(); @@ -1455,7 +1463,7 @@ Variant TileDataOcclusionShapeEditor::_get_value(TileSetAtlasSource *p_tile_set_ return tile_data->get_occluder(occlusion_layer); } -void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { +void TileDataOcclusionShapeEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { Vector2i coords = E.key.get_atlas_coords(); @@ -1481,11 +1489,11 @@ TileDataOcclusionShapeEditor::TileDataOcclusionShapeEditor() { add_child(polygon_editor); } -void TileDataCollisionEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { +void TileDataCollisionEditor::_property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field) { dummy_object->set(p_property, p_value); } -void TileDataCollisionEditor::_property_selected(StringName p_path, int p_focusable) { +void TileDataCollisionEditor::_property_selected(const StringName &p_path, int p_focusable) { // Deselect all other properties for (KeyValue<StringName, EditorProperty *> &editor : property_editors) { if (editor.key != p_path) { @@ -1634,10 +1642,10 @@ Variant TileDataCollisionEditor::_get_value(TileSetAtlasSource *p_tile_set_atlas return dict; } -void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { +void TileDataCollisionEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) { Dictionary new_dict = p_new_value; EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); - for (KeyValue<TileMapCell, Variant> &E : p_previous_values) { + for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { Vector2i coords = E.key.get_atlas_coords(); Dictionary old_dict = E.value; @@ -1802,7 +1810,7 @@ void TileDataTerrainsEditor::_update_terrain_selector() { } } -void TileDataTerrainsEditor::_property_value_changed(StringName p_property, Variant p_value, StringName p_field) { +void TileDataTerrainsEditor::_property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field) { Variant old_value = dummy_object->get(p_property); dummy_object->set(p_property, p_value); if (p_property == "terrain_set") { @@ -2871,7 +2879,7 @@ Variant TileDataNavigationEditor::_get_value(TileSetAtlasSource *p_tile_set_atla return tile_data->get_navigation_polygon(navigation_layer); } -void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) { +void TileDataNavigationEditor::_setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) { EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); for (const KeyValue<TileMapCell, Variant> &E : p_previous_values) { Vector2i coords = E.key.get_atlas_coords(); diff --git a/editor/plugins/tiles/tile_data_editors.h b/editor/plugins/tiles/tile_data_editors.h index 4bba5bb467..27fe4316a0 100644 --- a/editor/plugins/tiles/tile_data_editors.h +++ b/editor/plugins/tiles/tile_data_editors.h @@ -82,9 +82,9 @@ protected: bool _get(const StringName &p_name, Variant &r_ret) const; public: - bool has_dummy_property(StringName p_name); - void add_dummy_property(StringName p_name); - void remove_dummy_property(StringName p_name); + bool has_dummy_property(const StringName &p_name); + void add_dummy_property(const StringName &p_name); + void remove_dummy_property(const StringName &p_name); void clear_dummy_properties(); }; @@ -224,7 +224,7 @@ private: HashMap<TileMapCell, Variant, TileMapCell> drag_modified; Variant drag_painted_value; - void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + void _property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field); protected: DummyObject *dummy_object = memnew(DummyObject); @@ -238,7 +238,7 @@ protected: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value); virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile); - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value); + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value); public: virtual Control *get_toolbar() override { return toolbar; }; @@ -291,7 +291,7 @@ private: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) override; protected: virtual void _tile_set_changed() override; @@ -316,15 +316,15 @@ class TileDataCollisionEditor : public TileDataDefaultEditor { DummyObject *dummy_object = memnew(DummyObject); HashMap<StringName, EditorProperty *> property_editors; - void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); - void _property_selected(StringName p_path, int p_focusable); + void _property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field); + void _property_selected(const StringName &p_path, int p_focusable); void _polygons_changed(); virtual Variant _get_painted_value() override; virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) override; protected: virtual void _tile_set_changed() override; @@ -368,7 +368,7 @@ private: EditorPropertyEnum *terrain_set_property_editor = nullptr; EditorPropertyEnum *terrain_property_editor = nullptr; - void _property_value_changed(StringName p_property, Variant p_value, StringName p_field); + void _property_value_changed(const StringName &p_property, Variant p_value, const StringName &p_field); void _update_terrain_selector(); @@ -405,7 +405,7 @@ private: virtual void _set_painted_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; virtual void _set_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile, Variant p_value) override; virtual Variant _get_value(TileSetAtlasSource *p_tile_set_atlas_source, Vector2 p_coords, int p_alternative_tile) override; - virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, HashMap<TileMapCell, Variant, TileMapCell> p_previous_values, Variant p_new_value) override; + virtual void _setup_undo_redo_action(TileSetAtlasSource *p_tile_set_atlas_source, const HashMap<TileMapCell, Variant, TileMapCell> &p_previous_values, Variant p_new_value) override; protected: virtual void _tile_set_changed() override; diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp index a3fc6aa5f7..895df177ef 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.cpp +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.cpp @@ -507,7 +507,7 @@ void TileSetAtlasSourceEditor::AtlasTileProxyObject::_get_property_list(List<Pro } } -void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, RBSet<TileSelection> p_tiles) { +void TileSetAtlasSourceEditor::AtlasTileProxyObject::edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles) { ERR_FAIL_COND(!p_tile_set_atlas_source.is_valid()); ERR_FAIL_COND(p_tiles.is_empty()); for (const TileSelection &E : p_tiles) { diff --git a/editor/plugins/tiles/tile_set_atlas_source_editor.h b/editor/plugins/tiles/tile_set_atlas_source_editor.h index 7f6bab804d..e1e6a3113c 100644 --- a/editor/plugins/tiles/tile_set_atlas_source_editor.h +++ b/editor/plugins/tiles/tile_set_atlas_source_editor.h @@ -105,7 +105,7 @@ public: RBSet<TileSelection> get_edited_tiles() const { return tiles; }; // Update the proxyed object. - void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, RBSet<TileSelection> p_tiles = RBSet<TileSelection>()); + void edit(Ref<TileSetAtlasSource> p_tile_set_atlas_source, const RBSet<TileSelection> &p_tiles = RBSet<TileSelection>()); AtlasTileProxyObject(TileSetAtlasSourceEditor *p_tiles_set_atlas_source_editor) { tiles_set_atlas_source_editor = p_tiles_set_atlas_source_editor; diff --git a/editor/project_converter_3_to_4.cpp b/editor/project_converter_3_to_4.cpp index 634e6be7ae..2b0f61c1b0 100644 --- a/editor/project_converter_3_to_4.cpp +++ b/editor/project_converter_3_to_4.cpp @@ -1045,107 +1045,108 @@ bool ProjectConverter3To4::test_conversion(RegExContainer ®_container) { // get_object_of_execution { - { String base = "var roman = kieliszek."; - String expected = "kieliszek."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + String base = "var roman = kieliszek."; + String expected = "kieliszek."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String base = "r."; + String expected = "r."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String base = "mortadela("; + String expected = ""; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String base = "var node = $world/ukraine/lviv."; + String expected = "$world/ukraine/lviv."; + String got = get_object_of_execution(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + + // get_starting_space + { + String base = "\t\t\t var roman = kieliszek."; + String expected = "\t\t\t"; + String got = get_starting_space(base); + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); } - valid = valid && (got == expected); -} -{ - String base = "r."; - String expected = "r."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String base = "mortadela("; - String expected = ""; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String base = "var node = $world/ukraine/lviv."; - String expected = "$world/ukraine/lviv."; - String got = get_object_of_execution(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -} -// get_starting_space -{ - String base = "\t\t\t var roman = kieliszek."; - String expected = "\t\t\t"; - String got = get_starting_space(base); - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from get_object_of_execution. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", base, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -// Parse Arguments -{ - String line = "( )"; - Vector<String> got_vector = parse_arguments(line); - String got = ""; - String expected = ""; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , b , c)"; - Vector<String> got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||b|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , \"b,\" , c)"; - Vector<String> got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||\"b,\"|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -{ - String line = "(a , \"(,),,,,\" , c)"; - Vector<String> got_vector = parse_arguments(line); - String got = ""; - String expected = "a|||\"(,),,,,\"|||c|||"; - for (String &part : got_vector) { - got += part + "|||"; - } - if (got != expected) { - ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); - } - valid = valid && (got == expected); -} -return valid; + // Parse Arguments + { + String line = "( )"; + Vector<String> got_vector = parse_arguments(line); + String got = ""; + String expected = ""; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , b , c)"; + Vector<String> got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||b|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , \"b,\" , c)"; + Vector<String> got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||\"b,\"|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + { + String line = "(a , \"(,),,,,\" , c)"; + Vector<String> got_vector = parse_arguments(line); + String got = ""; + String expected = "a|||\"(,),,,,\"|||c|||"; + for (String &part : got_vector) { + got += part + "|||"; + } + if (got != expected) { + ERR_PRINT(vformat("Failed to get proper data from parse_arguments. \"%s\" should return \"%s\"(%d), got \"%s\"(%d), instead.", line, expected, expected.size(), got, got.size())); + } + valid = valid && (got == expected); + } + + return valid; } // Validate in all arrays if names don't do cyclic renames "Node" -> "Node2D" | "Node2D" -> "2DNode" diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index 1c8d55bd8e..a0ff858727 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -103,21 +103,25 @@ void ProjectDialog::_set_message(const String &p_msg, MessageType p_type, InputT } } +static bool is_zip_file(Ref<DirAccess> p_d, const String &p_path) { + return p_path.ends_with(".zip") && p_d->file_exists(p_path); +} + String ProjectDialog::_test_path() { Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + const String base_path = project_path->get_text(); String valid_path, valid_install_path; - if (d->change_dir(project_path->get_text()) == OK) { - valid_path = project_path->get_text(); - } else if (d->change_dir(project_path->get_text().strip_edges()) == OK) { - valid_path = project_path->get_text().strip_edges(); - } else if (project_path->get_text().ends_with(".zip")) { - if (d->file_exists(project_path->get_text())) { - valid_path = project_path->get_text(); - } - } else if (project_path->get_text().strip_edges().ends_with(".zip")) { - if (d->file_exists(project_path->get_text().strip_edges())) { - valid_path = project_path->get_text().strip_edges(); - } + bool is_zip = false; + if (d->change_dir(base_path) == OK) { + valid_path = base_path; + } else if (is_zip_file(d, base_path)) { + valid_path = base_path; + is_zip = true; + } else if (d->change_dir(base_path.strip_edges()) == OK) { + valid_path = base_path.strip_edges(); + } else if (is_zip_file(d, base_path.strip_edges())) { + valid_path = base_path.strip_edges(); + is_zip = true; } if (valid_path.is_empty()) { @@ -126,7 +130,7 @@ String ProjectDialog::_test_path() { return ""; } - if (mode == MODE_IMPORT && valid_path.ends_with(".zip")) { + if (mode == MODE_IMPORT && is_zip) { if (d->change_dir(install_path->get_text()) == OK) { valid_install_path = install_path->get_text(); } else if (d->change_dir(install_path->get_text().strip_edges()) == OK) { @@ -134,15 +138,15 @@ String ProjectDialog::_test_path() { } if (valid_install_path.is_empty()) { - _set_message(TTR("The path specified doesn't exist."), MESSAGE_ERROR, INSTALL_PATH); + _set_message(TTR("The install path specified doesn't exist."), MESSAGE_ERROR, INSTALL_PATH); get_ok_button()->set_disabled(true); return ""; } } if (mode == MODE_IMPORT || mode == MODE_RENAME) { - if (!valid_path.is_empty() && !d->file_exists("project.godot")) { - if (valid_path.ends_with(".zip")) { + if (!d->file_exists("project.godot")) { + if (is_zip) { Ref<FileAccess> io_fa; zlib_filefunc_def io = zipio_create_io(&io_fa); @@ -197,7 +201,7 @@ String ProjectDialog::_test_path() { d->list_dir_end(); if (!is_folder_empty) { - _set_message(TTR("Please choose an empty folder."), MESSAGE_WARNING, INSTALL_PATH); + _set_message(TTR("Please choose an empty install folder."), MESSAGE_WARNING, INSTALL_PATH); get_ok_button()->set_disabled(true); return ""; } @@ -209,8 +213,8 @@ String ProjectDialog::_test_path() { return ""; } - } else if (valid_path.ends_with("zip")) { - _set_message(TTR("This directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH); + } else if (is_zip) { + _set_message(TTR("The install directory already contains a Godot project."), MESSAGE_ERROR, INSTALL_PATH); get_ok_button()->set_disabled(true); return ""; } @@ -252,7 +256,7 @@ String ProjectDialog::_test_path() { return valid_path; } -void ProjectDialog::_path_text_changed(const String &p_path) { +void ProjectDialog::_update_path(const String &p_path) { String sp = _test_path(); if (!sp.is_empty()) { // If the project name is empty or default, infer the project name from the selected folder name @@ -277,6 +281,21 @@ void ProjectDialog::_path_text_changed(const String &p_path) { } } +void ProjectDialog::_path_text_changed(const String &p_path) { + Ref<DirAccess> d = DirAccess::create(DirAccess::ACCESS_FILESYSTEM); + if (mode == MODE_IMPORT && is_zip_file(d, p_path)) { + install_path->set_text(p_path.get_base_dir()); + install_path_container->show(); + } else if (mode == MODE_IMPORT && is_zip_file(d, p_path.strip_edges())) { + install_path->set_text(p_path.strip_edges().get_base_dir()); + install_path_container->show(); + } else { + install_path_container->hide(); + } + + _update_path(p_path.simplify_path()); +} + void ProjectDialog::_file_selected(const String &p_path) { // If not already shown. show_dialog(); @@ -300,7 +319,7 @@ void ProjectDialog::_file_selected(const String &p_path) { String sp = p.simplify_path(); project_path->set_text(sp); - _path_text_changed(sp); + _update_path(sp); if (p.ends_with(".zip")) { install_path->call_deferred(SNAME("grab_focus")); } else { @@ -314,14 +333,14 @@ void ProjectDialog::_path_selected(const String &p_path) { String sp = p_path.simplify_path(); project_path->set_text(sp); - _path_text_changed(sp); + _update_path(sp); get_ok_button()->call_deferred(SNAME("grab_focus")); } void ProjectDialog::_install_path_selected(const String &p_path) { String sp = p_path.simplify_path(); install_path->set_text(sp); - _path_text_changed(sp); + _update_path(sp); get_ok_button()->call_deferred(SNAME("grab_focus")); } @@ -359,7 +378,7 @@ void ProjectDialog::_create_folder() { d->change_dir(project_name_no_edges); String dir_str = d->get_current_dir(); project_path->set_text(dir_str); - _path_text_changed(dir_str); + _update_path(dir_str); created_folder_path = d->get_current_dir(); create_dir->set_disabled(true); } else { @@ -638,7 +657,7 @@ void ProjectDialog::cancel_pressed() { _remove_created_folder(); project_path->clear(); - _path_text_changed(""); + _update_path(""); project_name->clear(); _text_changed(""); @@ -968,7 +987,7 @@ ProjectDialog::ProjectDialog() { project_name->connect("text_changed", callable_mp(this, &ProjectDialog::_text_changed)); project_path->connect("text_changed", callable_mp(this, &ProjectDialog::_path_text_changed)); - install_path->connect("text_changed", callable_mp(this, &ProjectDialog::_path_text_changed)); + install_path->connect("text_changed", callable_mp(this, &ProjectDialog::_update_path)); fdialog->connect("dir_selected", callable_mp(this, &ProjectDialog::_path_selected)); fdialog->connect("file_selected", callable_mp(this, &ProjectDialog::_file_selected)); fdialog_install->connect("dir_selected", callable_mp(this, &ProjectDialog::_install_path_selected)); diff --git a/editor/project_manager.h b/editor/project_manager.h index 8a8b2ff99d..7b091050bd 100644 --- a/editor/project_manager.h +++ b/editor/project_manager.h @@ -104,6 +104,7 @@ private: void _set_message(const String &p_msg, MessageType p_type = MESSAGE_SUCCESS, InputType input_type = PROJECT_PATH); String _test_path(); + void _update_path(const String &p_path); void _path_text_changed(const String &p_path); void _path_selected(const String &p_path); void _file_selected(const String &p_path); diff --git a/editor/register_editor_types.cpp b/editor/register_editor_types.cpp index 075c856c1c..04a06ff732 100644 --- a/editor/register_editor_types.cpp +++ b/editor/register_editor_types.cpp @@ -50,6 +50,8 @@ #include "editor/filesystem_dock.h" #include "editor/gui/editor_file_dialog.h" #include "editor/gui/editor_spin_slider.h" +#include "editor/import/3d/resource_importer_obj.h" +#include "editor/import/3d/resource_importer_scene.h" #include "editor/import/editor_import_plugin.h" #include "editor/import/resource_importer_bitmask.h" #include "editor/import/resource_importer_bmfont.h" @@ -58,8 +60,6 @@ #include "editor/import/resource_importer_image.h" #include "editor/import/resource_importer_imagefont.h" #include "editor/import/resource_importer_layered_texture.h" -#include "editor/import/resource_importer_obj.h" -#include "editor/import/resource_importer_scene.h" #include "editor/import/resource_importer_shader_file.h" #include "editor/import/resource_importer_texture.h" #include "editor/import/resource_importer_texture_atlas.h" diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp index 3ea96a66cd..d416c4e509 100644 --- a/editor/scene_tree_dock.cpp +++ b/editor/scene_tree_dock.cpp @@ -29,7 +29,6 @@ /**************************************************************************/ #include "scene_tree_dock.h" -#include "node_dock.h" #include "core/config/project_settings.h" #include "core/input/input.h" @@ -50,6 +49,7 @@ #include "editor/gui/editor_file_dialog.h" #include "editor/inspector_dock.h" #include "editor/multi_node_edit.h" +#include "editor/node_dock.h" #include "editor/plugins/animation_player_editor_plugin.h" #include "editor/plugins/canvas_item_editor_plugin.h" #include "editor/plugins/node_3d_editor_plugin.h" @@ -1243,7 +1243,7 @@ void SceneTreeDock::_tool_selected(int p_tool, bool p_confirm_override) { if (cant_be_set_unique_names.size()) { String popup_text = TTR("Unique names already used by another node in the scene:"); popup_text += "\n"; - for (StringName name : cant_be_set_unique_names) { + for (const StringName &name : cant_be_set_unique_names) { popup_text += "\n" + String(name); } accept->set_text(popup_text); @@ -2320,18 +2320,121 @@ void SceneTreeDock::_toggle_placeholder_from_selection() { } } -void SceneTreeDock::_toggle_editable_children(Node *p_node) { - if (p_node) { - bool editable = !EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); - EditorNode::get_singleton()->get_edited_scene()->set_editable_instance(p_node, editable); - if (editable) { - p_node->set_scene_instance_load_placeholder(false); +void SceneTreeDock::_reparent_nodes_to_root(Node *p_root, const Array &p_nodes, Node *p_owner) { + List<Node *> nodes; + for (int i = 0; i < p_nodes.size(); i++) { + Node *node = Object::cast_to<Node>(p_nodes[i]); + ERR_FAIL_NULL(node); + nodes.push_back(node); + } + + for (Node *node : nodes) { + node->set_owner(p_owner); + List<Node *> owned; + node->get_owned_by(p_owner, &owned); + String original_name = node->get_name(); + node->reparent(p_root); + node->set_name(original_name); + + for (Node *F : owned) { + F->set_owner(p_owner); + } + } +} + +void SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name(Node *p_root, const Array &p_nodes, const Array &p_paths, const Array &p_transforms, const Array &p_names, Node *p_owner) { + ERR_FAIL_COND(p_nodes.size() != p_paths.size()); + ERR_FAIL_COND(p_nodes.size() != p_transforms.size()); + ERR_FAIL_COND(p_nodes.size() != p_names.size()); + + for (int i = 0; i < p_nodes.size(); i++) { + Node *node = Object::cast_to<Node>(p_nodes[i]); + ERR_FAIL_NULL(node); + const NodePath &np = p_paths[i]; + Node *parent_node = p_root->get_node_or_null(np); + ERR_FAIL_NULL(parent_node); + + List<Node *> owned; + node->get_owned_by(p_owner, &owned); + node->reparent(parent_node); + node->set_name(p_names[i]); + Node3D *node_3d = Object::cast_to<Node3D>(node); + if (node_3d) { + node_3d->set_transform(p_transforms[i]); + } else { + Node2D *node_2d = Object::cast_to<Node2D>(node); + if (node_2d) { + node_2d->set_transform(p_transforms[i]); + } } - Node3DEditor::get_singleton()->update_all_gizmos(p_node); + for (Node *F : owned) { + F->set_owner(p_owner); + } + } +} + +void SceneTreeDock::_toggle_editable_children(Node *p_node) { + if (!p_node) { + return; + } + + EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); + + undo_redo->create_action(TTR("Toggle Editable Children")); + + bool editable = !EditorNode::get_singleton()->get_edited_scene()->is_editable_instance(p_node); + + undo_redo->add_undo_method(EditorNode::get_singleton()->get_edited_scene(), "set_editable_instance", p_node, !editable); + undo_redo->add_do_method(EditorNode::get_singleton()->get_edited_scene(), "set_editable_instance", p_node, editable); + + if (editable) { + bool original_scene_instance_load_placeholder = p_node->get_scene_instance_load_placeholder(); + + undo_redo->add_undo_method(p_node, "set_scene_instance_load_placeholder", original_scene_instance_load_placeholder); + undo_redo->add_do_method(p_node, "set_scene_instance_load_placeholder", false); + } else { + List<Node *> owned; + p_node->get_owned_by(edited_scene, &owned); + + // Get the original paths, transforms, and names for undo. + Array owned_nodes_array; + Array paths_array; + Array transform_array; + Array name_array; + + for (Node *owned_node : owned) { + if (owned_node != p_node && owned_node != edited_scene && owned_node->get_owner() == edited_scene && owned_node->get_parent()->get_owner() != edited_scene) { + owned_nodes_array.push_back(owned_node); + paths_array.push_back(p_node->get_path_to(owned_node->get_parent())); + name_array.push_back(owned_node->get_name()); + Node3D *node_3d = Object::cast_to<Node3D>(owned_node); + if (node_3d) { + transform_array.push_back(node_3d->get_transform()); + } else { + Node2D *node_2d = Object::cast_to<Node2D>(owned_node); + if (node_2d) { + transform_array.push_back(node_2d->get_transform()); + } else { + transform_array.push_back(Variant()); + } + } + } + } - scene_tree->update_tree(); + if (!owned_nodes_array.is_empty()) { + undo_redo->add_undo_method(this, "_reparent_nodes_to_paths_with_transform_and_name", p_node, owned_nodes_array, paths_array, transform_array, name_array, edited_scene); + undo_redo->add_do_method(this, "_reparent_nodes_to_root", p_node, owned_nodes_array, edited_scene); + } } + + undo_redo->add_undo_method(Node3DEditor::get_singleton(), "update_all_gizmos", p_node); + undo_redo->add_do_method(Node3DEditor::get_singleton(), "update_all_gizmos", p_node); + + undo_redo->add_undo_method(scene_tree, "update_tree"); + undo_redo->add_do_method(scene_tree, "update_tree"); + + undo_redo->commit_action(); } void SceneTreeDock::_delete_confirm(bool p_cut) { @@ -3957,6 +4060,8 @@ void SceneTreeDock::_edit_subresource(int p_idx, const PopupMenu *p_from_menu) { void SceneTreeDock::_bind_methods() { ClassDB::bind_method(D_METHOD("_set_owners"), &SceneTreeDock::_set_owners); + ClassDB::bind_method(D_METHOD("_reparent_nodes_to_root"), &SceneTreeDock::_reparent_nodes_to_root); + ClassDB::bind_method(D_METHOD("_reparent_nodes_to_paths_with_transform_and_name"), &SceneTreeDock::_reparent_nodes_to_paths_with_transform_and_name); ClassDB::bind_method(D_METHOD("_update_script_button"), &SceneTreeDock::_update_script_button); diff --git a/editor/scene_tree_dock.h b/editor/scene_tree_dock.h index 0f1d046e4d..e765ef2a6b 100644 --- a/editor/scene_tree_dock.h +++ b/editor/scene_tree_dock.h @@ -223,6 +223,9 @@ class SceneTreeDock : public VBoxContainer { void _delete_dialog_closed(); void _toggle_editable_children_from_selection(); + + void _reparent_nodes_to_root(Node *p_root, const Array &p_nodes, Node *p_owner); + void _reparent_nodes_to_paths_with_transform_and_name(Node *p_root, const Array &p_nodes, const Array &p_paths, const Array &p_transforms, const Array &p_names, Node *p_owner); void _toggle_editable_children(Node *p_node); void _toggle_placeholder_from_selection(); diff --git a/editor/window_wrapper.cpp b/editor/window_wrapper.cpp index 2d89e4df46..aec4005ce5 100644 --- a/editor/window_wrapper.cpp +++ b/editor/window_wrapper.cpp @@ -74,7 +74,7 @@ class ShortcutBin : public Node { }; Rect2 WindowWrapper::_get_default_window_rect() const { - // Assume that the control rect is the desidered one for the window. + // Assume that the control rect is the desired one for the window. return wrapped_control->get_screen_rect(); } diff --git a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj index e5f7ca1144..bd2560c280 100644 --- a/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj +++ b/misc/dist/ios_xcode/godot_ios.xcodeproj/project.pbxproj @@ -254,7 +254,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = $min_version; "LD_CLASSIC_1000" = ""; "LD_CLASSIC_1100" = ""; "LD_CLASSIC_1200" = ""; @@ -299,7 +299,7 @@ GCC_WARN_UNINITIALIZED_AUTOS = YES; GCC_WARN_UNUSED_FUNCTION = YES; GCC_WARN_UNUSED_VARIABLE = YES; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = $min_version; "LD_CLASSIC_1000" = ""; "LD_CLASSIC_1100" = ""; "LD_CLASSIC_1200" = ""; @@ -325,7 +325,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; DEVELOPMENT_TEAM = $team_id; INFOPLIST_FILE = "$binary/$binary-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = $min_version; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", @@ -360,7 +360,7 @@ CONFIGURATION_BUILD_DIR = "$(BUILD_DIR)/$(CONFIGURATION)$(EFFECTIVE_PLATFORM_NAME)"; DEVELOPMENT_TEAM = $team_id; INFOPLIST_FILE = "$binary/$binary-Info.plist"; - IPHONEOS_DEPLOYMENT_TARGET = 12.0; + IPHONEOS_DEPLOYMENT_TARGET = $min_version; LD_RUNPATH_SEARCH_PATHS = ( "$(inherited)", "@executable_path/Frameworks", diff --git a/misc/extension_api_validation/4.2-stable.expected b/misc/extension_api_validation/4.2-stable.expected index 70358c683f..53303ebec4 100644 --- a/misc/extension_api_validation/4.2-stable.expected +++ b/misc/extension_api_validation/4.2-stable.expected @@ -21,3 +21,33 @@ GH-85393 Validate extension JSON: Error: Field 'classes/PhysicsShapeQueryParameters3D/properties/motion': type changed value in new API, from "Vector2" to "Vector3". The type was registered wrongly, this was a bug. + + +GH-86687 +-------- +Validate extension JSON: Error: Field 'classes/AnimationMixer/methods/_post_process_key_value/arguments/3': type changed value in new API, from "Object" to "int". + +Exposing the pointer was dangerous and it must be changed to avoid crash. Compatibility methods registered. + + +GH-84976 +-------- +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/FinalAction/values/FINAL_ACTION_CONTINUE': value changed value in new API, from 2.0 to 0. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/FinalAction/values/FINAL_ACTION_MAX': value changed value in new API, from 3.0 to 2. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_CLEAR': value changed value in new API, from 0.0 to 1. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_CLEAR_REGION_CONTINUE': value changed value in new API, from 2.0 to 0. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_CONTINUE': value changed value in new API, from 5.0 to 0. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_DROP': value changed value in new API, from 4.0 to 2. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_KEEP': value changed value in new API, from 3.0 to 0. +Validate extension JSON: Error: Field 'classes/RenderingDevice/enums/InitialAction/values/INITIAL_ACTION_MAX': value changed value in new API, from 6.0 to 3. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_clear/arguments': size changed value in new API, from 4 to 3. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/buffer_update/arguments': size changed value in new API, from 5 to 4. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/draw_list_begin/arguments': size changed value in new API, from 10 to 9. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_clear/arguments': size changed value in new API, from 7 to 6. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_copy/arguments': size changed value in new API, from 10 to 9. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_resolve_multisample/arguments': size changed value in new API, from 3 to 2. +Validate extension JSON: Error: Field 'classes/RenderingDevice/methods/texture_update/arguments': size changed value in new API, from 4 to 3. + +Barrier arguments have been removed from all relevant functions as they're no longer required. +Draw and compute list overlap no longer needs to be specified. +Initial and final actions have been simplified into fewer options. diff --git a/misc/hooks/README.md b/misc/hooks/README.md index 0aaeff8ae3..573f8fe350 100644 --- a/misc/hooks/README.md +++ b/misc/hooks/README.md @@ -29,7 +29,7 @@ so they should work out of the box on Linux/macOS. ##### clang-format - Download LLVM for Windows (version 13 or later) from - <https://releases.llvm.org/download.html> + <https://github.com/llvm/llvm-project/releases> - Make sure LLVM is added to the `PATH` during installation ##### black diff --git a/misc/hooks/pre-commit-clang-format b/misc/hooks/pre-commit-clang-format index fd0213c175..e5cf0c4aa2 100755 --- a/misc/hooks/pre-commit-clang-format +++ b/misc/hooks/pre-commit-clang-format @@ -80,7 +80,7 @@ fi # To get consistent formatting, we recommend contributors to use the same # clang-format version as CI. RECOMMENDED_CLANG_FORMAT_MAJOR_MIN="13" -RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="15" +RECOMMENDED_CLANG_FORMAT_MAJOR_MAX="16" if [ ! -x "$CLANG_FORMAT" ] ; then message="Error: clang-format executable not found. Please install clang-format $RECOMMENDED_CLANG_FORMAT_MAJOR_MAX." diff --git a/modules/SCsub b/modules/SCsub index fcc01e2c1b..7c9946170f 100644 --- a/modules/SCsub +++ b/modules/SCsub @@ -7,6 +7,9 @@ Import("env") env_modules = env.Clone() +# Allow modules to detect if they are being built as a module. +env_modules.Append(CPPDEFINES=["GODOT_MODULE"]) + Export("env_modules") # Header with MODULE_*_ENABLED defines. diff --git a/modules/gdscript/README.md b/modules/gdscript/README.md new file mode 100644 index 0000000000..30685e672c --- /dev/null +++ b/modules/gdscript/README.md @@ -0,0 +1,139 @@ +# Basic GDScript module architecture +This provides some basic information in how GDScript is implemented and integrates with the rest of the engine. You can learn more about GDScript in the [documentation](https://docs.godotengine.org/en/latest/tutorials/scripting/gdscript/index.html). It describes the syntax and user facing systems and concepts, and can be used as a reference for what user expectations are. + + +## General design + +GDScript is: + +1. A [gradually typed](https://en.wikipedia.org/wiki/Gradual_typing) language. Type hints are optional and help with static analysis and performance. However, typed code must easily interoperate with untyped code. +2. A tightly designed language. Features are added because they are _needed_, and not because they can be added or are interesting to develop. +3. Primarily an interpreted scripting language: it is compiled to GDScript byte code and interpreted in a GDScript virtual machine. It is meant to be easy to use and develop gameplay in. It is not meant for CPU-intensive algorithms or data processing, and is not optimized for it. For that, [C#](https://docs.godotengine.org/en/stable/tutorials/scripting/c_sharp/c_sharp_basics.html) or [GDExtension](https://docs.godotengine.org/en/stable/tutorials/scripting/gdextension/what_is_gdextension.html) may be used. + + +## Integration into Godot + +GDScript is integrated into Godot as a module. Since modules are optional, this means that Godot may be built without GDScript and work perfectly fine without it! + +The GDScript module interfaces with Godot's codebase by inheriting from the engine's scripting-related classes. New languages inherit from [`ScriptLanguage`](/core/object/script_language.h), and are registered in Godot's [`ScriptServer`](/core/object/script_language.h). Scripts, referring to a file containing code, are represented in the engine by the `Script` class. Instances of that script, which are used at runtime when actually executing the code, inherit from [`ScriptInstance`](/core/object/script_instance.h). + +To access Godot's internal classes, GDScript uses [`ClassDB`](/core/object/class_db.h). `ClassDB` is where Godot registers classes, methods and properties that it wants exposed to its scripting system. This is how GDScript understands that `Node2D` is a class it can use, and that it has a `get_parent()` method. + +[Built-in GDScript methods](https://docs.godotengine.org/en/latest/classes/class_@gdscript.html#methods) are defined and exported by [`GDScriptUtilityFunctions`](gdscript_utility_functions.h), whereas [global scope methods](https://docs.godotengine.org/en/latest/classes/class_%2540globalscope.html) are registered in [`Variant::_register_variant_utility_functions()`](/core/variant/variant_utility.cpp). + + +## Compilation + +Scripts can be at different stages of compilation. The process isn't entirely linear, but consists of this general order: tokenizing, parsing, analyzing, and finally compiling. This process is the same for scripts in the editor and scripts in an exported game. Scripts are stored as text files in both cases, and the compilation process must happen in full before the bytecode can be passed to the virtual machine and run. + +The main class of the GDScript module is the [`GDScript`](gdscript.h) class, which represents a class defined in GDScript. Each `.gd` file is called a _class file_ because it implicitly defines a class in GDScript, and thus results in an associated `GDScript` object. However, GDScript classes may define [_inner classes_](https://docs.godotengine.org/en/stable/tutorials/scripting/gdscript/gdscript_basics.html#inner-classes), and those are also represented by further `GDScript` objects, even though they are not in files of their own. + +The `GDScript` class contains all the information related to the corresponding GDScript class: its name and path, its members like variables, functions, symbols, signals, implicit methods like initializers, etc. This is the main class that the compilation step deals with. + +A secondary class is `GDScriptInstance`, defined in the same file, containing _runtime_ information for an instance of a `GDScript`, and is more related to the execution of a script by the virtual machine. + + +### Loading source code + +This mostly happens by calling `GDScript::load_source_code()` on a `GDScript` object. Parsing only requires a `String`, so it is entirely possible to parse a script without a `GDScript` object! + + +### Tokenizing (see [`GDScriptTokenizer`](gdscript_tokenizer.h)) + +Tokenizing is the process of converting the source code `String` into a sequence of tokens, which represent language constructs (such as `for` or `if`), identifiers, literals, etc. This happens almost exclusively during the parsing process, which asks for the next token in order to make sense of the source code. The tokenizer is only used outside of the parsing process in very rare exceptions. + + +### Parsing (see [`GDScriptParser`](gdscript_parser.h)) + +The parser takes a sequence of tokens and builds [the abstract syntax tree (AST)](https://en.wikipedia.org/wiki/Abstract_syntax_tree) of the GDScript program. The AST is used in the analyzing and compilation steps, and the source code `String` and sequence of tokens are discarded. The AST-building process finds syntax errors in a GDScript program and reports them to the user. + +The parser class also defines all the possible nodes of the AST as subtypes of `GDScriptParser::Node`, not to be confused with Godot's scene tree `Node`. For example, `GDScriptParser::IfNode` has two children nodes, one for the code in the `if` block, and one for the code in the `else` block. A `GDScriptParser::FunctionNode` contains children nodes for its name, parameters, return type, body, etc. The parser also defines typechecking data structures like `GDScriptParser::Datatype`. + +The parser was [intentionally designed](https://godotengine.org/article/gdscript-progress-report-writing-new-parser/#less-lookahead) with a look-ahead of a single token. This means that the parser only has access to the current token and the previous token (or, if you prefer, the current token and the next token). This parsing limitation ensures that GDScript will remain syntactically simple and accessible, and that the parsing process cannot become overly complex. + + +### Analysis and typechecking (see [`GDScriptAnalyzer`](gdscript_analyzer.h)) + +The analyzer takes in the AST of a program and verifies that "everything checks out". For example, when analyzing a method call with three parameters, it will check whether the function definition also contains three parameters. If the code is typed, it will check that argument and parameter types are compatible. + +There are two types of functions in the analyzer: `reduce` functions and `resolve` functions. Their parameters always include the AST node that they are attempting to reduce or resolve. +- The `reduce` functions work on GDScript expressions, which return values, and thus their main goal is to populate the `GDScriptParser::Datatype` of the underlying AST node. The datatype is then used to typecheck code that depends on this expression, and gives the compiler necessary information to generate appropriate, safe, and optimized bytecode. +For example, function calls are handled with `reduce_call()`, which must figure out what function is being called and check that the passed arguments match the function's parameters. The type of the underlying `CallNode` will be the return type of the function. +Another example is `reduce_identifier()`, which does _a lot_ of work: given the string of its `IdentifierNode`, it must figure out what that identifier refers to. It could be a local variable, class name, global or class function, function parameter, class or superclass member, or any number of other things. It has to check many different places to find this information! +A secondary goal of the `reduce` functions is to perform [constant folding](https://en.wikipedia.org/wiki/Constant_folding): to determine whether an expression is constant, and if it is, compute its _reduced value_ at this time so it does not need to be computed over and over at runtime! +- The resolve functions work on AST nodes that represent statements, and don't necessarily have values. Their goal is to do work related to program control flow, resolve their child AST nodes, deal with scoping, etc. One of the simplest examples is `resolve_if()`, which reduces the `if` condition, then resolves the `if` body and `else` body if it exists. +The `resolve_for()` function does more work than simply resolving its code block. With `for i in range(10)`, for example, it must also declare and type the new variable `i` within the scope of its code block, as well as make sure `range(10)` is iterable, among other things. +To understand classes and inheritance without introducing cyclic dependency problems that would come from immediate full class code analysis, the analyzer often asks only for class _interfaces_: it needs to know what member variables and methods exist as well as their types, but no more. +This is done through `resolve_class_interface()`, which populates `ClassNode`'s `Datatype` with that information. It first checks for superclass information with `resolve_class_inheritance()`, then populates its member information by calling `resolve_class_member()` on each member. Since this step is only about the class _interface_, methods are resolved with `resolve_function_signature()`, which gets all relevant typing information without resolving the function body! +The remaining steps of resolution, including member variable initialization code, method code, etc, can happen at a later time. + +In fully untyped code, very little static analysis is possible. For example, the analyzer cannot know whether `my_var.some_member` exists when it does not know the type of `my_var`. Therefore, it cannot emit a warning or error because `some_member` _could_ exist - or it could not. The analyzer must trust the programmer. If an error does occur, it will be at runtime. +However, GDScript is gradually typed, so all of these analyses must work when parts of the code are typed and others untyped. Static analysis in a gradually typed language is a best-effort situation: suppose there is a typed variable `var x : int`, and an untyped `var y = "some string"`. We can obviously tell this isn't going to work, but the analyzer will accept the assignment `x = y` without warnings or errors: it only knows that `y` is untyped and can therefore be anything, including the `int` that `x` expects. It must once again trust the programmer to have written code that works. In this instance, the code will error at runtime. +In both these cases, the analyzer handles the uncertainty of untyped code by calling `mark_node_unsafe()` on the respective AST node. This means it didn't have enough information to know whether the code was fully safe or necessarily wrong. Lines with unsafe AST nodes are represented by grey line numbers in the GDScript editor. Green line numbers indicate a line of code without any unsafe nodes. + +This analysis step is also where dependencies are introduced and that information stored for use later. If class `A` extends class `B` or contains a member with type `B` from some other script file, then the analyzer will attempt to load that second script. If `B` contains references to `A`, then a _cyclic_ dependency is introduced. This is OK in many cases, but impossible to resolve in others. + +Clearly, the analyzer is where a lot of the "magic" happens! It determines what constitutes proper code that can actually be compiled, and provides as many safety guarantees as possible with the typing information it is provided with. The more typed the code, the safer and more optimized it will be! + + +#### Cyclic dependencies and member resolution + +Cyclic dependencies from inheritance (`A extends B, B extends A`) are not supported in any programming language. Other cyclic dependencies are supported, such as `A extends B` and `B` uses, contains, or preloads, members of type `A`. + +To see why cyclic dependencies are complicated, suppose there is one between classes `A <-> B`. Partially through the analysis of `A`, we will need information about `B`, and therefore trigger its analysis. However, the analysis of `B` will eventually need information from `A`, which is incomplete because we never finished analyzing it. This would result in members not being found when they actually exist! + +GDScript supports cyclic dependencies due to a few features of the analyzer: + +1. Class interface resolution: when analyzing code of class `A` that depends on some other class `B`, we don't need to resolve the _code_ of `B` (its member initializers, function code, etc). We only need to know what members and methods the class has, as well as their types. These are the only things one class can use to work with, or _interface_ with, another. Because of inheritance, a class's interface depends on its superclass as well, so recursive interface resolution is needed. More details can be found in `GDScriptAnalyzer::resolve_class_interface()`. +2. Out of order member resolution: the analyzer may not even need an entire class interface to be resolved in order to figure out a specific type! For example, if class `A` contains code that references `B.is_alive`, then the analyzer doesn't need to immediately resolve `B`'s entire interface. It may simply check whether `is_alive` exists in `B`, and reduce it for its type information, on-demand. +A fundamental cyclic dependency problem occurs when the types of two different member variables are mutually dependent. This is commonly checked by a pattern that declares a temporary datatype with `GDScriptParser::DataType resolving_datatype;`, followed by `resolving_datatype.kind = GDScriptParser::DataType::RESOLVING;`. If the analyzer attempts to resolve a member on-demand that is already tagged as resolving, then a cyclic dependency problem has been found and can be reported. + + +### Compiling (see [`GDScriptCompiler`](gdscript_compiler.h)) + +Compiling is the final step in making a GDScript executable in the [virtual machine](gdscript_vm.h) (VM). The compiler takes a `GDScript` object and an AST, and uses another class, [`GDScriptByteCodeGenerator`](gdscript_byte_codegen.h), to generate bytecode corresponding to the class. In doing this, it creates the objects that the VM understands how to run, like [`GDScriptFunction`](gdscript_function.h), and completes a few extra tasks needed for compilation, such as populating runtime class member information. + +Importantly, the compilation process of a class, specifically the `GDScriptCompiler::_compile_class()` method, _cannot_ depend on information obtained by calling `GDScriptCompiler::_compile_class()` on another class, for the same cyclic dependency reasons explained in the previous section. +Any information that can only be obtained or populated during the compilation step, when `GDScript` objects become available, must be handled before `GDScriptCompiler::_compile_class()` is called. This process is centralized in `GDScriptCompiler::_prepare_compilation()` which works as the compile-time equivalent of `GDScriptAnalyzer::resolve_class_interface()`: it populates a `GDScript`'s "interface" exclusively with information from the analysis step, and without processing other external classes. This information may then be referenced by other classes without introducing problematic cycles. + +The more typing information a GDScript has, the more optimized the compiled bytecode can be. For example, if `my_var` is untyped, the bytecode for `my_var.some_member` will need to go through several layers of indirection to figure out the type of `my_var` at runtime, and from there determine how to obtain `some_member`. This varies depending on whether `my_var` is a dictionary, a script, or a native class. If the type of `my_var` was known at compile time, the bytecode can directly call the type-specific method for obtaining a member. +Similar optimizations are possible for `my_var.some_func()`. With untyped GDScript, the VM will need to resolve `my_var`'s type at runtime, then, depending on the type, use different methods to resolve the function and call it. When the function is fully resolved during static analysis, native function pointers or GDScript function objects can be compiled into the bytecode and directly called by the VM, removing several layers of indirection. + +Typed code is safer code and faster code! + + +## Loading scripts + +GDScripts can be loaded in a couple of different ways. The main method, used almost everywhere in the engine, is to load scripts through the `ResourceLoader` singleton. In this way, GDScripts are resources like any others: `ResourceLoader::load()` will simply reroute to `ResourceFormatLoaderGDScript::load()`, found in `gdscript.h/cpp`(gdscript.h). This generates a GDScript object which is compiled and ready to use. + +The other method is to manually load the source code, then pass it to a parser, then to an analyzer and then to a compiler. The previous approach does this behind the scenes, alongside some smart caching of scripts and other functionalities. It is used in the [GDScript test runner infrastructure](tests/gdscript_test_runner.h). + + +### Full and shallow scripts + +The `ResourceFormatLoaderGDScript::load()` method simply calls `GDScriptCache::get_full_script()`. The [`GDScriptCache`](gdscript_cache.h) is, as it sounds, a cache for GDScripts. Its two main methods, `get_shallow_script()` and `get_full_script()`, get and cache, respectively, scripts that have been merely parsed, and scripts which have been statically analyzed and fully compiled. Another internal class, `GDScriptParserRef`, found in the same file, provides even more granularity over the different steps of the parsing process, and is used extensively in the analyzer. + +Shallow, or "just parsed" scripts, provide information such as defined classes, class members, and so forth. This is sufficient for many purposes, like obtaining a class interface or checking whether a member exists on a specific class. Full scripts, on the other hand, have been analyzed and compiled and are ready to use. + +The distinction between full and shallow scripts is very important, as shallow scripts cannot create cyclic dependency problems, whereas full scripts can. The analyzer, for example, never asks for full scripts. Choosing when to request a shallow vs a full script is an important but subtle decision. + +In practice, full scripts are simply scripts where `GDScript::reload()` has been called. This critical function is the primary way in which scripts get compiled in Godot, and essentially does all the compilation steps covered so far in order. Whenever a script is loaded, or updated and reloaded in Godot, it will end up going through `GDScript::reload()`, except in very rare circumstances like the test runner. It is an excellent place to start reading and understanding the GDScript module! + + +## Special types of scripts + +Certain types of GDScripts behave slightly differently. For example, autoloads are loaded with `ResourceLoader::load()` during `Main::start()`, very soon after Godot is launched. Many systems aren't initialized at that time, so error reporting is often significantly reduced and may not even show up in the editor. + +Tool scripts, declared with the `@tool` annotation on a GDScript file, run in the editor itself as opposed to just when the game is launched. This leads to a significant increase in complexity, as many things that can be changed in the editor may affect a currently executing tool script. + + +## Other + +There are many other classes in the GDScript module. Here is a brief overview of some of them: + +- Declaration of GDScript warnings in [`GDScriptWarning`](gdscript_warning.h). +- [`GDScriptFunction`](gdscript_function.h), which represents an executable GDScript function. The relevant file contains both static as well as runtime information. +- The [virtual machine](gdscript_vm.cpp) is essentially defined as calling `GDScriptFunction::call()`. +- Editor-related functions can be found in parts of `GDScriptLanguage`, originally declared in [`gdscript.h`](gdscript.h) but defined in [`gdscript_editor.cpp`](gdscript_editor.cpp). Code highlighting can be found in [`GDScriptSyntaxHighlighter`](editor/gdscript_highlighter.h). +- GDScript decompilation is found in [`gdscript_disassembler.cpp`](gdscript_disassembler.h), defined as `GDScriptFunction::disassemble()`. +- Documentation generation from GDScript comments in [`GDScriptDocGen`](editor/gdscript_docgen.h)
\ No newline at end of file diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index a999acd1bd..1f0830aa17 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1381,51 +1381,43 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) { } #endif -GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_main_thread; -thread_local GDScript::UpdatableFuncPtr *GDScript::func_ptrs_to_update_thread_local = nullptr; - -GDScript::UpdatableFuncPtrElement GDScript::_add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr) { - UpdatableFuncPtrElement result = {}; - - { - MutexLock lock(func_ptrs_to_update_thread_local->mutex); - result.element = func_ptrs_to_update_thread_local->ptrs.push_back(p_func_ptr_ptr); - result.func_ptr = func_ptrs_to_update_thread_local; - - if (likely(func_ptrs_to_update_thread_local->initialized)) { - return result; - } - - func_ptrs_to_update_thread_local->initialized = true; +GDScript::UpdatableFuncPtr::UpdatableFuncPtr(GDScriptFunction *p_function) { + if (p_function == nullptr) { + return; } - MutexLock lock(func_ptrs_to_update_mutex); - func_ptrs_to_update.push_back(func_ptrs_to_update_thread_local); - func_ptrs_to_update_thread_local->rc++; - - return result; -} + ptr = p_function; + script = ptr->get_script(); + ERR_FAIL_NULL(script); -void GDScript::_remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element) { - ERR_FAIL_NULL(p_func_ptr_element.element); - ERR_FAIL_NULL(p_func_ptr_element.func_ptr); - MutexLock lock(p_func_ptr_element.func_ptr->mutex); - p_func_ptr_element.element->erase(); + MutexLock script_lock(script->func_ptrs_to_update_mutex); + list_element = script->func_ptrs_to_update.push_back(this); } -void GDScript::_fixup_thread_function_bookkeeping() { - // Transfer the ownership of these update items to the main thread, - // because the current one is dying, leaving theirs orphan, dangling. +GDScript::UpdatableFuncPtr::~UpdatableFuncPtr() { + ERR_FAIL_NULL(script); - DEV_ASSERT(!Thread::is_main_thread()); + if (list_element) { + MutexLock script_lock(script->func_ptrs_to_update_mutex); + list_element->erase(); + list_element = nullptr; + } +} - MutexLock lock(func_ptrs_to_update_main_thread.mutex); - MutexLock lock2(func_ptrs_to_update_thread_local->mutex); +void GDScript::_recurse_replace_function_ptrs(const HashMap<GDScriptFunction *, GDScriptFunction *> &p_replacements) const { + MutexLock lock(func_ptrs_to_update_mutex); + for (UpdatableFuncPtr *updatable : func_ptrs_to_update) { + HashMap<GDScriptFunction *, GDScriptFunction *>::ConstIterator replacement = p_replacements.find(updatable->ptr); + if (replacement) { + updatable->ptr = replacement->value; + } else { + // Probably a lambda from another reload, ignore. + updatable->ptr = nullptr; + } + } - while (!func_ptrs_to_update_thread_local->ptrs.is_empty()) { - List<GDScriptFunction **>::Element *E = func_ptrs_to_update_thread_local->ptrs.front(); - E->transfer_to_back(&func_ptrs_to_update_main_thread.ptrs); - func_ptrs_to_update_thread_local->transferred = true; + for (HashMap<StringName, Ref<GDScript>>::ConstIterator subscript = subclasses.begin(); subscript; ++subscript) { + subscript->value->_recurse_replace_function_ptrs(p_replacements); } } @@ -1447,30 +1439,9 @@ void GDScript::clear(ClearData *p_clear_data) { } { - MutexLock outer_lock(func_ptrs_to_update_mutex); + MutexLock lock(func_ptrs_to_update_mutex); for (UpdatableFuncPtr *updatable : func_ptrs_to_update) { - bool destroy = false; - { - MutexLock inner_lock(updatable->mutex); - if (updatable->transferred) { - func_ptrs_to_update_main_thread.mutex.lock(); - } - for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { - *func_ptr_ptr = nullptr; - } - DEV_ASSERT(updatable->rc != 0); - updatable->rc--; - if (updatable->rc == 0) { - destroy = true; - } - if (updatable->transferred) { - func_ptrs_to_update_main_thread.mutex.unlock(); - } - } - if (destroy) { - DEV_ASSERT(updatable != &func_ptrs_to_update_main_thread); - memdelete(updatable); - } + updatable->ptr = nullptr; } } @@ -1543,6 +1514,13 @@ GDScript::~GDScript() { } destructing = true; + if (is_print_verbose_enabled()) { + MutexLock lock(func_ptrs_to_update_mutex); + if (!func_ptrs_to_update.is_empty()) { + print_line(vformat("GDScript: %d orphaned lambdas becoming invalid at destruction of script '%s'.", func_ptrs_to_update.size(), fully_qualified_name)); + } + } + clear(); { @@ -2091,33 +2069,6 @@ void GDScriptLanguage::remove_named_global_constant(const StringName &p_name) { named_globals.erase(p_name); } -void GDScriptLanguage::thread_enter() { - GDScript::func_ptrs_to_update_thread_local = memnew(GDScript::UpdatableFuncPtr); -} - -void GDScriptLanguage::thread_exit() { - // This thread may have been created before GDScript was up - // (which also means it can't have run any GDScript code at all). - if (!GDScript::func_ptrs_to_update_thread_local) { - return; - } - - GDScript::_fixup_thread_function_bookkeeping(); - - bool destroy = false; - { - MutexLock lock(GDScript::func_ptrs_to_update_thread_local->mutex); - DEV_ASSERT(GDScript::func_ptrs_to_update_thread_local->rc != 0); - GDScript::func_ptrs_to_update_thread_local->rc--; - if (GDScript::func_ptrs_to_update_thread_local->rc == 0) { - destroy = true; - } - } - if (destroy) { - memdelete(GDScript::func_ptrs_to_update_thread_local); - } -} - void GDScriptLanguage::init() { //populate global constants int gcc = CoreConstants::get_global_constant_count(); @@ -2150,8 +2101,6 @@ void GDScriptLanguage::init() { _add_global(E.name, E.ptr); } - GDScript::func_ptrs_to_update_thread_local = &GDScript::func_ptrs_to_update_main_thread; - #ifdef TESTS_ENABLED GDScriptTests::GDScriptTestRunner::handle_cmdline(); #endif @@ -2201,8 +2150,6 @@ void GDScriptLanguage::finish() { } script_list.clear(); function_list.clear(); - - DEV_ASSERT(GDScript::func_ptrs_to_update_main_thread.rc == 1); } void GDScriptLanguage::profiling_start() { diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index 31811bba47..7b0e2136ed 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -117,29 +117,28 @@ class GDScript : public Script { HashMap<GDScriptFunction *, LambdaInfo> lambda_info; - // List is used here because a ptr to elements are stored, so the memory locations need to be stable - struct UpdatableFuncPtr { - List<GDScriptFunction **> ptrs; - Mutex mutex; - bool initialized : 1; - bool transferred : 1; - uint32_t rc = 1; - UpdatableFuncPtr() : - initialized(false), transferred(false) {} - }; - struct UpdatableFuncPtrElement { - List<GDScriptFunction **>::Element *element = nullptr; - UpdatableFuncPtr *func_ptr = nullptr; +public: + class UpdatableFuncPtr { + friend class GDScript; + + GDScriptFunction *ptr = nullptr; + GDScript *script = nullptr; + List<UpdatableFuncPtr *>::Element *list_element = nullptr; + + public: + GDScriptFunction *operator->() const { return ptr; } + operator GDScriptFunction *() const { return ptr; } + + UpdatableFuncPtr(GDScriptFunction *p_function); + ~UpdatableFuncPtr(); }; - static UpdatableFuncPtr func_ptrs_to_update_main_thread; - static thread_local UpdatableFuncPtr *func_ptrs_to_update_thread_local; + +private: + // List is used here because a ptr to elements are stored, so the memory locations need to be stable List<UpdatableFuncPtr *> func_ptrs_to_update; Mutex func_ptrs_to_update_mutex; - UpdatableFuncPtrElement _add_func_ptr_to_update(GDScriptFunction **p_func_ptr_ptr); - static void _remove_func_ptr_to_update(const UpdatableFuncPtrElement &p_func_ptr_element); - - static void _fixup_thread_function_bookkeeping(); + void _recurse_replace_function_ptrs(const HashMap<GDScriptFunction *, GDScriptFunction *> &p_replacements) const; #ifdef TOOLS_ENABLED // For static data storage during hot-reloading. @@ -541,7 +540,7 @@ public: virtual void get_string_delimiters(List<String> *p_delimiters) const override; virtual bool is_using_templates() override; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; - virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override; virtual bool validate(const String &p_script, const String &p_path = "", List<String> *r_functions = nullptr, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override; virtual Script *create_script() const override; #ifndef DISABLE_DEPRECATED @@ -562,11 +561,6 @@ public: virtual void add_named_global_constant(const StringName &p_name, const Variant &p_value) override; virtual void remove_named_global_constant(const StringName &p_name) override; - /* MULTITHREAD FUNCTIONS */ - - virtual void thread_enter() override; - virtual void thread_exit() override; - /* DEBUGGER FUNCTIONS */ virtual String debug_get_error() const override; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index b245df15a6..3fd5b3f519 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -845,7 +845,7 @@ GDScriptParser::DataType GDScriptAnalyzer::resolve_datatype(GDScriptParser::Type return result; } -void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source) { +void GDScriptAnalyzer::resolve_class_member(GDScriptParser::ClassNode *p_class, const StringName &p_name, const GDScriptParser::Node *p_source) { ERR_FAIL_COND(!p_class->has_member(p_name)); resolve_class_member(p_class, p_class->members_indices[p_name], p_source); } diff --git a/modules/gdscript/gdscript_analyzer.h b/modules/gdscript/gdscript_analyzer.h index ec155706df..4ed476a3df 100644 --- a/modules/gdscript/gdscript_analyzer.h +++ b/modules/gdscript/gdscript_analyzer.h @@ -62,7 +62,7 @@ class GDScriptAnalyzer { void decide_suite_type(GDScriptParser::Node *p_suite, GDScriptParser::Node *p_statement); void resolve_annotation(GDScriptParser::AnnotationNode *p_annotation); - void resolve_class_member(GDScriptParser::ClassNode *p_class, StringName p_name, const GDScriptParser::Node *p_source = nullptr); + void resolve_class_member(GDScriptParser::ClassNode *p_class, const StringName &p_name, const GDScriptParser::Node *p_source = nullptr); void resolve_class_member(GDScriptParser::ClassNode *p_class, int p_index, const GDScriptParser::Node *p_source = nullptr); void resolve_class_interface(GDScriptParser::ClassNode *p_class, const GDScriptParser::Node *p_source = nullptr); void resolve_class_interface(GDScriptParser::ClassNode *p_class, bool p_recursive); diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index ee360e581b..f6633f8bf6 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1375,7 +1375,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } - main_script->lambda_info.insert(function, { lambda->captures.size(), lambda->use_self }); + codegen.script->lambda_info.insert(function, { lambda->captures.size(), lambda->use_self }); gen->write_lambda(result, function, captures, lambda->use_self); for (int i = 0; i < captures.size(); i++) { @@ -3050,7 +3050,7 @@ GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement FunctionLambdaInfo info; info.function = p_func; info.parent = p_parent_func; - info.script = p_parent_func; + info.script = p_func->get_script(); info.name = p_func->get_name(); info.line = p_func->_initial_line; info.index = p_index; @@ -3061,10 +3061,14 @@ GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement info.default_arg_count = p_func->_default_arg_count; info.sublambdas = _get_function_lambda_replacement_info(p_func, p_depth, p_parent_func); - GDScript::LambdaInfo *extra_info = main_script->lambda_info.getptr(p_func); + ERR_FAIL_NULL_V(info.script, info); + GDScript::LambdaInfo *extra_info = info.script->lambda_info.getptr(p_func); if (extra_info != nullptr) { info.capture_count = extra_info->capture_count; info.use_self = extra_info->use_self; + } else { + info.capture_count = 0; + info.use_self = false; } return info; @@ -3199,22 +3203,7 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri HashMap<GDScriptFunction *, GDScriptFunction *> func_ptr_replacements; _get_function_ptr_replacements(func_ptr_replacements, old_lambda_info, &new_lambda_info); - - { - MutexLock outer_lock(main_script->func_ptrs_to_update_mutex); - for (GDScript::UpdatableFuncPtr *updatable : main_script->func_ptrs_to_update) { - MutexLock inner_lock(updatable->mutex); - for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { - GDScriptFunction **replacement = func_ptr_replacements.getptr(*func_ptr_ptr); - if (replacement != nullptr) { - *func_ptr_ptr = *replacement; - } else { - // Probably a lambda from another reload, ignore. - *func_ptr_ptr = nullptr; - } - } - } - } + main_script->_recurse_replace_function_ptrs(func_ptr_replacements); if (has_static_data && !root->annotated_static_unload) { GDScriptCache::add_static_script(p_script); diff --git a/modules/gdscript/gdscript_compiler.h b/modules/gdscript/gdscript_compiler.h index fd6b22f527..0adbe1ed8e 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -45,19 +45,19 @@ class GDScriptCompiler { GDScript *main_script = nullptr; struct FunctionLambdaInfo { - GDScriptFunction *function; - GDScriptFunction *parent; - Ref<GDScript> script; + GDScriptFunction *function = nullptr; + GDScriptFunction *parent = nullptr; + GDScript *script = nullptr; StringName name; - int line; - int index; - int depth; + int line = 0; + int index = 0; + int depth = 0; //uint64_t code_hash; //int code_size; - int capture_count; - int use_self; - int arg_count; - int default_arg_count; + int capture_count = 0; + bool use_self = false; + int arg_count = 0; + int default_arg_count = 0; //Vector<GDScriptDataType> argument_types; //GDScriptDataType return_type; Vector<FunctionLambdaInfo> sublambdas; diff --git a/modules/gdscript/gdscript_editor.cpp b/modules/gdscript/gdscript_editor.cpp index b8fd5de4fd..9ad2ba1914 100644 --- a/modules/gdscript/gdscript_editor.cpp +++ b/modules/gdscript/gdscript_editor.cpp @@ -104,7 +104,7 @@ Ref<Script> GDScriptLanguage::make_template(const String &p_template, const Stri return scr; } -Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(StringName p_object) { +Vector<ScriptLanguage::ScriptTemplate> GDScriptLanguage::get_built_in_templates(const StringName &p_object) { Vector<ScriptLanguage::ScriptTemplate> templates; #ifdef TOOLS_ENABLED for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { @@ -537,7 +537,7 @@ struct GDScriptCompletionIdentifier { // appears. For example, if you are completing code in a class that inherits Node2D, a property found on Node2D // will have a "better" (lower) location "score" than a property that is found on CanvasItem. -static int _get_property_location(StringName p_class, StringName p_property) { +static int _get_property_location(const StringName &p_class, const StringName &p_property) { if (!ClassDB::has_property(p_class, p_property)) { return ScriptLanguage::LOCATION_OTHER; } @@ -552,7 +552,7 @@ static int _get_property_location(StringName p_class, StringName p_property) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_constant_location(StringName p_class, StringName p_constant) { +static int _get_constant_location(const StringName &p_class, const StringName &p_constant) { if (!ClassDB::has_integer_constant(p_class, p_constant)) { return ScriptLanguage::LOCATION_OTHER; } @@ -567,7 +567,7 @@ static int _get_constant_location(StringName p_class, StringName p_constant) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_signal_location(StringName p_class, StringName p_signal) { +static int _get_signal_location(const StringName &p_class, const StringName &p_signal) { if (!ClassDB::has_signal(p_class, p_signal)) { return ScriptLanguage::LOCATION_OTHER; } @@ -582,7 +582,7 @@ static int _get_signal_location(StringName p_class, StringName p_signal) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_method_location(StringName p_class, StringName p_method) { +static int _get_method_location(const StringName &p_class, const StringName &p_method) { if (!ClassDB::has_method(p_class, p_method)) { return ScriptLanguage::LOCATION_OTHER; } @@ -597,7 +597,7 @@ static int _get_method_location(StringName p_class, StringName p_method) { return depth | ScriptLanguage::LOCATION_PARENT_MASK; } -static int _get_enum_constant_location(StringName p_class, StringName p_enum_constant) { +static int _get_enum_constant_location(const StringName &p_class, const StringName &p_enum_constant) { if (!ClassDB::get_integer_constant_enum(p_class, p_enum_constant)) { return ScriptLanguage::LOCATION_OTHER; } diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 547f5607d3..f6fa17c84f 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -139,20 +139,14 @@ void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, V } } -GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { +GDScriptLambdaCallable::GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures) : + function(p_function) { ERR_FAIL_NULL(p_script.ptr()); ERR_FAIL_NULL(p_function); script = p_script; - function = p_function; captures = p_captures; h = (uint32_t)hash_murmur3_one_64((uint64_t)this); - - updatable_func_ptr_element = p_script->_add_func_ptr_to_update(&function); -} - -GDScriptLambdaCallable::~GDScriptLambdaCallable() { - GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element); } bool GDScriptLambdaSelfCallable::compare_equal(const CallableCustom *p_a, const CallableCustom *p_b) { @@ -264,37 +258,23 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun } } -GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) : + function(p_function) { ERR_FAIL_NULL(p_self.ptr()); ERR_FAIL_NULL(p_function); reference = p_self; object = p_self.ptr(); - function = p_function; captures = p_captures; h = (uint32_t)hash_murmur3_one_64((uint64_t)this); - - GDScript *gds = p_function->get_script(); - if (gds != nullptr) { - updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function); - } } -GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { +GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) : + function(p_function) { ERR_FAIL_NULL(p_self); ERR_FAIL_NULL(p_function); object = p_self; - function = p_function; captures = p_captures; h = (uint32_t)hash_murmur3_one_64((uint64_t)this); - - GDScript *gds = p_function->get_script(); - if (gds != nullptr) { - updatable_func_ptr_element = gds->_add_func_ptr_to_update(&function); - } -} - -GDScriptLambdaSelfCallable::~GDScriptLambdaSelfCallable() { - GDScript::_remove_func_ptr_to_update(updatable_func_ptr_element); } diff --git a/modules/gdscript/gdscript_lambda_callable.h b/modules/gdscript/gdscript_lambda_callable.h index ee7d547544..2c5d01aa16 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -42,10 +42,9 @@ class GDScriptFunction; class GDScriptInstance; class GDScriptLambdaCallable : public CallableCustom { - GDScriptFunction *function = nullptr; + GDScript::UpdatableFuncPtr function; Ref<GDScript> script; uint32_t h; - GDScript::UpdatableFuncPtrElement updatable_func_ptr_element; Vector<Variant> captures; @@ -62,17 +61,18 @@ public: StringName get_method() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; + GDScriptLambdaCallable(GDScriptLambdaCallable &) = delete; + GDScriptLambdaCallable(const GDScriptLambdaCallable &) = delete; GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures); - virtual ~GDScriptLambdaCallable(); + virtual ~GDScriptLambdaCallable() = default; }; // Lambda callable that references a particular object, so it can use `self` in the body. class GDScriptLambdaSelfCallable : public CallableCustom { - GDScriptFunction *function = nullptr; + GDScript::UpdatableFuncPtr function; Ref<RefCounted> reference; // For objects that are RefCounted, keep a reference. Object *object = nullptr; // For non RefCounted objects, use a direct pointer. uint32_t h; - GDScript::UpdatableFuncPtrElement updatable_func_ptr_element; Vector<Variant> captures; @@ -88,9 +88,11 @@ public: ObjectID get_object() const override; void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; + GDScriptLambdaSelfCallable(GDScriptLambdaSelfCallable &) = delete; + GDScriptLambdaSelfCallable(const GDScriptLambdaSelfCallable &) = delete; GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); GDScriptLambdaSelfCallable(Object *p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures); - virtual ~GDScriptLambdaSelfCallable(); + virtual ~GDScriptLambdaSelfCallable() = default; }; #endif // GDSCRIPT_LAMBDA_CALLABLE_H diff --git a/modules/gdscript/tests/gdscript_test_runner.cpp b/modules/gdscript/tests/gdscript_test_runner.cpp index 361ca276bb..4d93a6fc18 100644 --- a/modules/gdscript/tests/gdscript_test_runner.cpp +++ b/modules/gdscript/tests/gdscript_test_runner.cpp @@ -266,7 +266,7 @@ bool GDScriptTestRunner::make_tests_for_dir(const String &p_dir) { while (!next.is_empty()) { if (dir->current_is_dir()) { - if (next == "." || next == "..") { + if (next == "." || next == ".." || next == "completion" || next == "lsp") { next = dir->get_next(); continue; } diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg new file mode 100644 index 0000000000..4edee46039 --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.cfg @@ -0,0 +1,4 @@ +[output] +expected=[ + {"display": "autoplay"}, +] diff --git a/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd new file mode 100644 index 0000000000..d41bbb970c --- /dev/null +++ b/modules/gdscript/tests/scripts/completion/get_node/get_node_member_annotated.gd @@ -0,0 +1,6 @@ +extends Node + +var test: AnimationPlayer = $AnimationPlayer + +func _ready(): + test.➡ diff --git a/modules/gdscript/tests/scripts/lsp/class.notest.gd b/modules/gdscript/tests/scripts/lsp/class.gd index 53d0b14d72..53d0b14d72 100644 --- a/modules/gdscript/tests/scripts/lsp/class.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/class.gd diff --git a/modules/gdscript/tests/scripts/lsp/enums.notest.gd b/modules/gdscript/tests/scripts/lsp/enums.gd index 38b9ec110a..38b9ec110a 100644 --- a/modules/gdscript/tests/scripts/lsp/enums.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/enums.gd diff --git a/modules/gdscript/tests/scripts/lsp/indentation.notest.gd b/modules/gdscript/tests/scripts/lsp/indentation.gd index c25d73a719..c25d73a719 100644 --- a/modules/gdscript/tests/scripts/lsp/indentation.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/indentation.gd diff --git a/modules/gdscript/tests/scripts/lsp/lambdas.notest.gd b/modules/gdscript/tests/scripts/lsp/lambdas.gd index 6f5d468eea..6f5d468eea 100644 --- a/modules/gdscript/tests/scripts/lsp/lambdas.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/lambdas.gd diff --git a/modules/gdscript/tests/scripts/lsp/local_variables.notest.gd b/modules/gdscript/tests/scripts/lsp/local_variables.gd index b6cc46f7da..b6cc46f7da 100644 --- a/modules/gdscript/tests/scripts/lsp/local_variables.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/local_variables.gd diff --git a/modules/gdscript/tests/scripts/lsp/properties.notest.gd b/modules/gdscript/tests/scripts/lsp/properties.gd index 8dfaee2e5b..8dfaee2e5b 100644 --- a/modules/gdscript/tests/scripts/lsp/properties.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/properties.gd diff --git a/modules/gdscript/tests/scripts/lsp/scopes.notest.gd b/modules/gdscript/tests/scripts/lsp/scopes.gd index 20b8fb9bd7..20b8fb9bd7 100644 --- a/modules/gdscript/tests/scripts/lsp/scopes.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/scopes.gd diff --git a/modules/gdscript/tests/scripts/lsp/shadowing_initializer.notest.gd b/modules/gdscript/tests/scripts/lsp/shadowing_initializer.gd index 338000fa0e..338000fa0e 100644 --- a/modules/gdscript/tests/scripts/lsp/shadowing_initializer.notest.gd +++ b/modules/gdscript/tests/scripts/lsp/shadowing_initializer.gd diff --git a/modules/gdscript/tests/scripts/project.godot b/modules/gdscript/tests/scripts/project.godot index 25b49c0abd..c500ef443d 100644 --- a/modules/gdscript/tests/scripts/project.godot +++ b/modules/gdscript/tests/scripts/project.godot @@ -3,7 +3,7 @@ ; It also helps for opening Godot to edit the scripts, but please don't ; let the editor changes be saved. -config_version=4 +config_version=5 [application] diff --git a/modules/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h new file mode 100644 index 0000000000..abc34bd4bf --- /dev/null +++ b/modules/gdscript/tests/test_completion.h @@ -0,0 +1,199 @@ +/**************************************************************************/ +/* test_completion.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TEST_COMPLETION_H +#define TEST_COMPLETION_H + +#ifdef TOOLS_ENABLED + +#include "core/io/config_file.h" +#include "core/io/dir_access.h" +#include "core/io/file_access.h" +#include "core/object/script_language.h" +#include "core/variant/dictionary.h" +#include "core/variant/variant.h" +#include "gdscript_test_runner.h" +#include "modules/modules_enabled.gen.h" // For mono. +#include "scene/resources/packed_scene.h" + +#include "../gdscript.h" +#include "tests/test_macros.h" + +#include "editor/editor_settings.h" +#include "scene/theme/theme_db.h" + +namespace GDScriptTests { + +static bool match_option(const Dictionary p_expected, const ScriptLanguage::CodeCompletionOption p_got) { + if (p_expected.get("display", p_got.display) != p_got.display) { + return false; + } + if (p_expected.get("insert_text", p_got.insert_text) != p_got.insert_text) { + return false; + } + if (p_expected.get("kind", p_got.kind) != Variant(p_got.kind)) { + return false; + } + if (p_expected.get("location", p_got.location) != Variant(p_got.location)) { + return false; + } + return true; +} + +static void to_dict_list(Variant p_variant, List<Dictionary> &p_list) { + ERR_FAIL_COND(!p_variant.is_array()); + + Array arr = p_variant; + for (int i = 0; i < arr.size(); i++) { + if (arr[i].get_type() == Variant::DICTIONARY) { + p_list.push_back(arr[i]); + } + } +} + +static void test_directory(const String &p_dir) { + Error err = OK; + Ref<DirAccess> dir = DirAccess::open(p_dir, &err); + + if (err != OK) { + FAIL("Invalid test directory."); + return; + } + + String path = dir->get_current_dir(); + + dir->list_dir_begin(); + String next = dir->get_next(); + + while (!next.is_empty()) { + if (dir->current_is_dir()) { + if (next == "." || next == "..") { + next = dir->get_next(); + continue; + } + test_directory(path.path_join(next)); + } else if (next.ends_with(".gd") && !next.ends_with(".notest.gd")) { + Ref<FileAccess> acc = FileAccess::open(path.path_join(next), FileAccess::READ, &err); + + if (err != OK) { + next = dir->get_next(); + continue; + } + + String code = acc->get_as_utf8_string(); + // For ease of reading ➡ (0x27A1) acts as sentinel char instead of 0xFFFF in the files. + code = code.replace_first(String::chr(0x27A1), String::chr(0xFFFF)); + // Require pointer sentinel char in scripts. + CHECK(code.find_char(0xFFFF) != -1); + + ConfigFile conf; + if (conf.load(path.path_join(next.get_basename() + ".cfg")) != OK) { + FAIL("No config file found."); + } + +#ifndef MODULE_MONO_ENABLED + if (conf.get_value("input", "cs", false)) { + next = dir->get_next(); + continue; + } +#endif + + EditorSettings::get_singleton()->set_setting("text_editor/completion/use_single_quotes", conf.get_value("input", "use_single_quotes", false)); + + List<Dictionary> include; + to_dict_list(conf.get_value("result", "include", Array()), include); + + List<Dictionary> exclude; + to_dict_list(conf.get_value("result", "exclude", Array()), exclude); + + List<ScriptLanguage::CodeCompletionOption> options; + String call_hint; + bool forced; + + Node *owner = nullptr; + if (dir->file_exists(next.get_basename() + ".tscn")) { + String project_path = "res://completion"; + Ref<PackedScene> scene = ResourceLoader::load(project_path.path_join(next.get_basename() + ".tscn"), "PackedScene"); + if (scene.is_valid()) { + owner = scene->instantiate(); + } + } + + GDScriptLanguage::get_singleton()->complete_code(code, path.path_join(next), owner, &options, forced, call_hint); + String contains_excluded; + for (ScriptLanguage::CodeCompletionOption &option : options) { + for (const Dictionary &E : exclude) { + if (match_option(E, option)) { + contains_excluded = option.display; + break; + } + } + if (!contains_excluded.is_empty()) { + break; + } + + for (const Dictionary &E : include) { + if (match_option(E, option)) { + include.erase(E); + break; + } + } + } + CHECK_MESSAGE(contains_excluded.is_empty(), "Autocompletion suggests illegal option '", contains_excluded, "' for '", path.path_join(next), "'."); + CHECK(include.is_empty()); + + String expected_call_hint = conf.get_value("result", "call_hint", call_hint); + bool expected_forced = conf.get_value("result", "forced", forced); + + CHECK(expected_call_hint == call_hint); + CHECK(expected_forced == forced); + + if (owner) { + memdelete(owner); + } + } + next = dir->get_next(); + } +} + +TEST_SUITE("[Modules][GDScript][Completion]") { + TEST_CASE("[Editor] Check suggestion list") { + // Set all editor settings that code completion relies on. + EditorSettings::get_singleton()->set_setting("text_editor/completion/use_single_quotes", false); + init_language("modules/gdscript/tests/scripts"); + + test_directory("modules/gdscript/tests/scripts/completion"); + } +} +} // namespace GDScriptTests + +#endif + +#endif // TEST_COMPLETION_H diff --git a/modules/gdscript/tests/test_lsp.h b/modules/gdscript/tests/test_lsp.h index e57df00e2d..6192272f80 100644 --- a/modules/gdscript/tests/test_lsp.h +++ b/modules/gdscript/tests/test_lsp.h @@ -76,7 +76,7 @@ namespace GDScriptTests { // LSP GDScript test scripts are located inside project of other GDScript tests: // Cannot reset `ProjectSettings` (singleton) -> Cannot load another workspace and resources in there. // -> Reuse GDScript test project. LSP specific scripts are then placed inside `lsp` folder. -// Access via `res://lsp/my_script.notest.gd`. +// Access via `res://lsp/my_script.gd`. const String root = "modules/gdscript/tests/scripts/"; /* @@ -394,7 +394,7 @@ func f(): Ref<GDScriptWorkspace> workspace = GDScriptLanguageProtocol::get_singleton()->get_workspace(); { - String path = "res://lsp/local_variables.notest.gd"; + String path = "res://lsp/local_variables.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -413,7 +413,7 @@ func f(): } SUBCASE("Can get correct ranges for indented variables") { - String path = "res://lsp/indentation.notest.gd"; + String path = "res://lsp/indentation.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -421,7 +421,7 @@ func f(): } SUBCASE("Can get correct ranges for scopes") { - String path = "res://lsp/scopes.notest.gd"; + String path = "res://lsp/scopes.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -429,7 +429,7 @@ func f(): } SUBCASE("Can get correct ranges for lambda") { - String path = "res://lsp/lambdas.notest.gd"; + String path = "res://lsp/lambdas.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -437,7 +437,7 @@ func f(): } SUBCASE("Can get correct ranges for inner class") { - String path = "res://lsp/class.notest.gd"; + String path = "res://lsp/class.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -445,7 +445,7 @@ func f(): } SUBCASE("Can get correct ranges for inner class") { - String path = "res://lsp/enums.notest.gd"; + String path = "res://lsp/enums.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -453,7 +453,7 @@ func f(): } SUBCASE("Can get correct ranges for shadowing & shadowed variables") { - String path = "res://lsp/shadowing_initializer.notest.gd"; + String path = "res://lsp/shadowing_initializer.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); @@ -461,7 +461,7 @@ func f(): } SUBCASE("Can get correct ranges for properties and getter/setter") { - String path = "res://lsp/properties.notest.gd"; + String path = "res://lsp/properties.gd"; assert_no_errors_in(path); String uri = workspace->get_file_uri(path); Vector<InlineTestData> all_test_data = read_tests(path); diff --git a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp index 6975bc1228..56e4bfbb32 100644 --- a/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp +++ b/modules/gltf/editor/editor_scene_exporter_gltf_plugin.cpp @@ -39,7 +39,7 @@ #include "editor/editor_node.h" #include "editor/editor_scale.h" #include "editor/gui/editor_file_dialog.h" -#include "editor/import/scene_import_settings.h" +#include "editor/import/3d/scene_import_settings.h" String SceneExporterGLTFPlugin::get_name() const { return "ConvertGLTF2"; diff --git a/modules/gltf/editor/editor_scene_importer_blend.cpp b/modules/gltf/editor/editor_scene_importer_blend.cpp index 9587604e56..24dab16b90 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.cpp +++ b/modules/gltf/editor/editor_scene_importer_blend.cpp @@ -134,7 +134,19 @@ Node *EditorSceneFormatImporterBlend::import_scene(const String &p_path, uint32_ // Get global paths for source and sink. // Escape paths to be valid Python strings to embed in the script. - const String source_global = ProjectSettings::get_singleton()->globalize_path(p_path).c_escape(); + String source_global = ProjectSettings::get_singleton()->globalize_path(p_path); + +#ifdef WINDOWS_ENABLED + // On Windows, when using a network share path, the above will return a path starting with "//" + // which once handed to Blender will be treated like a relative path. So we need to replace the + // first two characters with "\\" to make it absolute again. + if (source_global.is_network_share_path()) { + source_global = "\\\\" + source_global.substr(2); + } +#endif + + source_global = source_global.c_escape(); + const String blend_basename = p_path.get_file().get_basename(); const String sink = ProjectSettings::get_singleton()->get_imported_files_path().path_join( vformat("%s-%s.gltf", blend_basename, p_path.md5_text())); diff --git a/modules/gltf/editor/editor_scene_importer_blend.h b/modules/gltf/editor/editor_scene_importer_blend.h index ec467db457..c1f4280170 100644 --- a/modules/gltf/editor/editor_scene_importer_blend.h +++ b/modules/gltf/editor/editor_scene_importer_blend.h @@ -34,7 +34,7 @@ #ifdef TOOLS_ENABLED #include "editor/editor_file_system.h" -#include "editor/import/resource_importer_scene.h" +#include "editor/import/3d/resource_importer_scene.h" class Animation; class Node; diff --git a/modules/gltf/editor/editor_scene_importer_fbx.h b/modules/gltf/editor/editor_scene_importer_fbx.h index cc60830eac..86ee6568c9 100644 --- a/modules/gltf/editor/editor_scene_importer_fbx.h +++ b/modules/gltf/editor/editor_scene_importer_fbx.h @@ -35,7 +35,7 @@ #include "editor/editor_file_system.h" #include "editor/fbx_importer_manager.h" -#include "editor/import/resource_importer_scene.h" +#include "editor/import/3d/resource_importer_scene.h" class Animation; class Node; diff --git a/modules/gltf/editor/editor_scene_importer_gltf.h b/modules/gltf/editor/editor_scene_importer_gltf.h index 7726c845bf..ec563bf525 100644 --- a/modules/gltf/editor/editor_scene_importer_gltf.h +++ b/modules/gltf/editor/editor_scene_importer_gltf.h @@ -33,7 +33,7 @@ #ifdef TOOLS_ENABLED -#include "editor/import/resource_importer_scene.h" +#include "editor/import/3d/resource_importer_scene.h" class Animation; class Node; diff --git a/modules/gltf/extensions/gltf_document_extension.h b/modules/gltf/extensions/gltf_document_extension.h index 512b7aba91..761dff725c 100644 --- a/modules/gltf/extensions/gltf_document_extension.h +++ b/modules/gltf/extensions/gltf_document_extension.h @@ -33,6 +33,8 @@ #include "../gltf_state.h" +#include "scene/3d/node_3d.h" + class GLTFDocumentExtension : public Resource { GDCLASS(GLTFDocumentExtension, Resource); diff --git a/modules/gltf/gltf_document.cpp b/modules/gltf/gltf_document.cpp index 0b491ce1d6..8a60df85cf 100644 --- a/modules/gltf/gltf_document.cpp +++ b/modules/gltf/gltf_document.cpp @@ -5049,7 +5049,7 @@ Error GLTFDocument::_serialize_animations(Ref<GLTFState> p_state) { AnimationPlayer *animation_player = p_state->animation_players[player_i]; List<StringName> animations; animation_player->get_animation_list(&animations); - for (StringName animation_name : animations) { + for (const StringName &animation_name : animations) { _convert_animation(p_state, animation_player, animation_name); } } @@ -6288,6 +6288,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ animation->add_track(Animation::TYPE_POSITION_3D); animation->track_set_path(position_idx, transform_node_path); animation->track_set_imported(position_idx, true); //helps merging later + if (track.position_track.interpolation == GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(position_idx, Animation::InterpolationType::INTERPOLATION_NEAREST); + } base_idx++; } } @@ -6310,6 +6313,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ animation->add_track(Animation::TYPE_ROTATION_3D); animation->track_set_path(rotation_idx, transform_node_path); animation->track_set_imported(rotation_idx, true); //helps merging later + if (track.rotation_track.interpolation == GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(rotation_idx, Animation::InterpolationType::INTERPOLATION_NEAREST); + } base_idx++; } } @@ -6332,6 +6338,9 @@ void GLTFDocument::_import_animation(Ref<GLTFState> p_state, AnimationPlayer *p_ animation->add_track(Animation::TYPE_SCALE_3D); animation->track_set_path(scale_idx, transform_node_path); animation->track_set_imported(scale_idx, true); //helps merging later + if (track.scale_track.interpolation == GLTFAnimation::INTERP_STEP) { + animation->track_set_interpolation_type(scale_idx, Animation::InterpolationType::INTERPOLATION_NEAREST); + } base_idx++; } } diff --git a/modules/gltf/structures/gltf_skeleton.h b/modules/gltf/structures/gltf_skeleton.h index 72a4a06e5c..b2f2dcb2a2 100644 --- a/modules/gltf/structures/gltf_skeleton.h +++ b/modules/gltf/structures/gltf_skeleton.h @@ -82,7 +82,7 @@ public: //RBMap<int32_t, GLTFNodeIndex> get_godot_bone_node() { // return this->godot_bone_node; //} - //void set_godot_bone_node(RBMap<int32_t, GLTFNodeIndex> p_godot_bone_node) { + //void set_godot_bone_node(const RBMap<int32_t, GLTFNodeIndex> &p_godot_bone_node) { // this->godot_bone_node = p_godot_bone_node; //} Dictionary get_godot_bone_node(); diff --git a/modules/gltf/structures/gltf_skin.h b/modules/gltf/structures/gltf_skin.h index 164cabfe12..cd5f2d9ca2 100644 --- a/modules/gltf/structures/gltf_skin.h +++ b/modules/gltf/structures/gltf_skin.h @@ -34,6 +34,7 @@ #include "../gltf_defines.h" #include "core/io/resource.h" +#include "scene/resources/skin.h" template <typename T> class TypedArray; diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index 5c2c3f96de..4746ffb79b 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -703,7 +703,7 @@ void LightmapperRD::_raster_geometry(RenderingDevice *rd, Size2i atlas_size, int raster_push_constant.uv_offset[0] = -0.5f / float(atlas_size.x); raster_push_constant.uv_offset[1] = -0.5f / float(atlas_size.y); - RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); //draw opaque rd->draw_list_bind_render_pipeline(draw_list, raster_pipeline); rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0); @@ -1863,7 +1863,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d seams_push_constant.slice = uint32_t(i * subslices + k); seams_push_constant.debug = debug; - RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = rd->draw_list_begin(framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); rd->draw_list_bind_uniform_set(draw_list, raster_base_uniform, 0); rd->draw_list_bind_uniform_set(draw_list, blendseams_raster_uniform, 1); diff --git a/modules/mono/csharp_script.cpp b/modules/mono/csharp_script.cpp index 175e213579..2bbd56776a 100644 --- a/modules/mono/csharp_script.cpp +++ b/modules/mono/csharp_script.cpp @@ -371,7 +371,7 @@ Ref<Script> CSharpLanguage::make_template(const String &p_template, const String return scr; } -Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(StringName p_object) { +Vector<ScriptLanguage::ScriptTemplate> CSharpLanguage::get_built_in_templates(const StringName &p_object) { Vector<ScriptLanguage::ScriptTemplate> templates; #ifdef TOOLS_ENABLED for (int i = 0; i < TEMPLATES_ARRAY_SIZE; i++) { diff --git a/modules/mono/csharp_script.h b/modules/mono/csharp_script.h index 401f5c62e5..41e8d63be1 100644 --- a/modules/mono/csharp_script.h +++ b/modules/mono/csharp_script.h @@ -423,7 +423,7 @@ public: void get_string_delimiters(List<String> *p_delimiters) const override; bool is_using_templates() override; virtual Ref<Script> make_template(const String &p_template, const String &p_class_name, const String &p_base_class_name) const override; - virtual Vector<ScriptTemplate> get_built_in_templates(StringName p_object) override; + virtual Vector<ScriptTemplate> get_built_in_templates(const StringName &p_object) override; /* TODO */ bool validate(const String &p_script, const String &p_path, List<String> *r_functions, List<ScriptLanguage::ScriptError> *r_errors = nullptr, List<ScriptLanguage::Warning> *r_warnings = nullptr, HashSet<int> *r_safe_lines = nullptr) const override { return true; diff --git a/modules/multiplayer/scene_replication_interface.cpp b/modules/multiplayer/scene_replication_interface.cpp index c95e4ff9c9..31307e648d 100644 --- a/modules/multiplayer/scene_replication_interface.cpp +++ b/modules/multiplayer/scene_replication_interface.cpp @@ -707,7 +707,7 @@ MultiplayerSynchronizer *SceneReplicationInterface::_find_synchronizer(int p_pee return sync; } -void SceneReplicationInterface::_send_delta(int p_peer, const HashSet<ObjectID> p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> p_last_watch_usecs) { +void SceneReplicationInterface::_send_delta(int p_peer, const HashSet<ObjectID> &p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> &p_last_watch_usecs) { MAKE_ROOM(/* header */ 1 + /* element */ 4 + 8 + 4 + delta_mtu); uint8_t *ptr = packet_cache.ptrw(); ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC | (1 << SceneMultiplayer::CMD_FLAG_0_SHIFT); @@ -799,7 +799,7 @@ Error SceneReplicationInterface::on_delta_receive(int p_from, const uint8_t *p_b return OK; } -void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec) { +void SceneReplicationInterface::_send_sync(int p_peer, const HashSet<ObjectID> &p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec) { MAKE_ROOM(/* header */ 3 + /* element */ 4 + 4 + sync_mtu); uint8_t *ptr = packet_cache.ptrw(); ptr[0] = SceneMultiplayer::NETWORK_COMMAND_SYNC; diff --git a/modules/multiplayer/scene_replication_interface.h b/modules/multiplayer/scene_replication_interface.h index 3b3ec6a9ef..31211bb108 100644 --- a/modules/multiplayer/scene_replication_interface.h +++ b/modules/multiplayer/scene_replication_interface.h @@ -101,8 +101,8 @@ private: bool _verify_synchronizer(int p_peer, MultiplayerSynchronizer *p_sync, uint32_t &r_net_id); MultiplayerSynchronizer *_find_synchronizer(int p_peer, uint32_t p_net_ida); - void _send_sync(int p_peer, const HashSet<ObjectID> p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec); - void _send_delta(int p_peer, const HashSet<ObjectID> p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> p_last_watch_usecs); + void _send_sync(int p_peer, const HashSet<ObjectID> &p_synchronizers, uint16_t p_sync_net_time, uint64_t p_usec); + void _send_delta(int p_peer, const HashSet<ObjectID> &p_synchronizers, uint64_t p_usec, const HashMap<ObjectID, uint64_t> &p_last_watch_usecs); Error _make_spawn_packet(Node *p_node, MultiplayerSpawner *p_spawner, int &r_len); Error _make_despawn_packet(Node *p_node, int &r_len); Error _send_raw(const uint8_t *p_buffer, int p_size, int p_peer, bool p_reliable); diff --git a/modules/navigation/nav_agent.cpp b/modules/navigation/nav_agent.cpp index cb219ac6c0..5a87ff6c4b 100644 --- a/modules/navigation/nav_agent.cpp +++ b/modules/navigation/nav_agent.cpp @@ -326,7 +326,7 @@ const Dictionary NavAgent::get_avoidance_data() const { _avoidance_data["new_velocity"] = Vector3(rvo_agent_3d.newVelocity_.x(), rvo_agent_3d.newVelocity_.y(), rvo_agent_3d.newVelocity_.z()); _avoidance_data["velocity"] = Vector3(rvo_agent_3d.velocity_.x(), rvo_agent_3d.velocity_.y(), rvo_agent_3d.velocity_.z()); _avoidance_data["position"] = Vector3(rvo_agent_3d.position_.x(), rvo_agent_3d.position_.y(), rvo_agent_3d.position_.z()); - _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_3d.prefVelocity_.x(), rvo_agent_3d.prefVelocity_.y(), rvo_agent_3d.prefVelocity_.z()); + _avoidance_data["preferred_velocity"] = Vector3(rvo_agent_3d.prefVelocity_.x(), rvo_agent_3d.prefVelocity_.y(), rvo_agent_3d.prefVelocity_.z()); _avoidance_data["radius"] = float(rvo_agent_3d.radius_); _avoidance_data["time_horizon_agents"] = float(rvo_agent_3d.timeHorizon_); _avoidance_data["time_horizon_obstacles"] = 0.0; @@ -341,7 +341,7 @@ const Dictionary NavAgent::get_avoidance_data() const { _avoidance_data["new_velocity"] = Vector3(rvo_agent_2d.newVelocity_.x(), 0.0, rvo_agent_2d.newVelocity_.y()); _avoidance_data["velocity"] = Vector3(rvo_agent_2d.velocity_.x(), 0.0, rvo_agent_2d.velocity_.y()); _avoidance_data["position"] = Vector3(rvo_agent_2d.position_.x(), 0.0, rvo_agent_2d.position_.y()); - _avoidance_data["prefered_velocity"] = Vector3(rvo_agent_2d.prefVelocity_.x(), 0.0, rvo_agent_2d.prefVelocity_.y()); + _avoidance_data["preferred_velocity"] = Vector3(rvo_agent_2d.prefVelocity_.x(), 0.0, rvo_agent_2d.prefVelocity_.y()); _avoidance_data["radius"] = float(rvo_agent_2d.radius_); _avoidance_data["time_horizon_agents"] = float(rvo_agent_2d.timeHorizon_); _avoidance_data["time_horizon_obstacles"] = float(rvo_agent_2d.timeHorizonObst_); diff --git a/modules/openxr/doc_classes/OpenXRHand.xml b/modules/openxr/doc_classes/OpenXRHand.xml index cc7766507f..eb7decd30d 100644 --- a/modules/openxr/doc_classes/OpenXRHand.xml +++ b/modules/openxr/doc_classes/OpenXRHand.xml @@ -1,10 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="OpenXRHand" inherits="Node3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../../../doc/class.xsd"> <brief_description> - Node supporting finger tracking in OpenXR. + Node supporting hand and finger tracking in OpenXR. </brief_description> <description> - This node enables OpenXR's hand tracking functionality. The node should be a child node of an [XROrigin3D] node, tracking will update its position to where the player's actual hand is positioned. This node also updates the skeleton of a properly skinned hand model. The hand mesh should be a child node of this node. + This node enables OpenXR's hand tracking functionality. The node should be a child node of an [XROrigin3D] node, tracking will update its position to the player's tracked hand Palm joint location (the center of the middle finger's metacarpal bone). This node also updates the skeleton of a properly skinned hand or avatar model. + If the skeleton is a hand (one of the hand bones is the root node of the skeleton), then the skeleton will be placed relative to the hand palm location and the hand mesh and skeleton should be children of the OpenXRHand node. + If the hand bones are part of a full skeleton, then the root of the hand will keep its location with the assumption that IK is used to position the hand and arm. </description> <tutorials> </tutorials> @@ -18,6 +20,9 @@ <member name="motion_range" type="int" setter="set_motion_range" getter="get_motion_range" enum="OpenXRHand.MotionRange" default="0"> Set the motion range (if supported) limiting the hand motion. </member> + <member name="skeleton_rig" type="int" setter="set_skeleton_rig" getter="get_skeleton_rig" enum="OpenXRHand.SkeletonRig" default="0"> + Set the type of skeleton rig the [member hand_skeleton] is compliant with. + </member> </members> <constants> <constant name="HAND_LEFT" value="0" enum="Hands"> @@ -38,5 +43,14 @@ <constant name="MOTION_RANGE_MAX" value="2" enum="MotionRange"> Maximum supported motion ranges. </constant> + <constant name="SKELETON_RIG_OPENXR" value="0" enum="SkeletonRig"> + An OpenXR compliant skeleton. + </constant> + <constant name="SKELETON_RIG_HUMANOID" value="1" enum="SkeletonRig"> + A [SkeletonProfileHumanoid] compliant skeleton. + </constant> + <constant name="SKELETON_RIG_MAX" value="2" enum="SkeletonRig"> + Maximum supported hands. + </constant> </constants> </class> diff --git a/modules/openxr/scene/openxr_hand.cpp b/modules/openxr/scene/openxr_hand.cpp index c48fac8055..8ce33b55c3 100644 --- a/modules/openxr/scene/openxr_hand.cpp +++ b/modules/openxr/scene/openxr_hand.cpp @@ -46,9 +46,13 @@ void OpenXRHand::_bind_methods() { ClassDB::bind_method(D_METHOD("set_motion_range", "motion_range"), &OpenXRHand::set_motion_range); ClassDB::bind_method(D_METHOD("get_motion_range"), &OpenXRHand::get_motion_range); + ClassDB::bind_method(D_METHOD("set_skeleton_rig", "skeleton_rig"), &OpenXRHand::set_skeleton_rig); + ClassDB::bind_method(D_METHOD("get_skeleton_rig"), &OpenXRHand::get_skeleton_rig); + ADD_PROPERTY(PropertyInfo(Variant::INT, "hand", PROPERTY_HINT_ENUM, "Left,Right"), "set_hand", "get_hand"); ADD_PROPERTY(PropertyInfo(Variant::INT, "motion_range", PROPERTY_HINT_ENUM, "Unobstructed,Conform to controller"), "set_motion_range", "get_motion_range"); ADD_PROPERTY(PropertyInfo(Variant::NODE_PATH, "hand_skeleton", PROPERTY_HINT_NODE_PATH_VALID_TYPES, "Skeleton3D"), "set_hand_skeleton", "get_hand_skeleton"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "skeleton_rig", PROPERTY_HINT_ENUM, "OpenXR,Humanoid"), "set_skeleton_rig", "get_skeleton_rig"); BIND_ENUM_CONSTANT(HAND_LEFT); BIND_ENUM_CONSTANT(HAND_RIGHT); @@ -57,6 +61,10 @@ void OpenXRHand::_bind_methods() { BIND_ENUM_CONSTANT(MOTION_RANGE_UNOBSTRUCTED); BIND_ENUM_CONSTANT(MOTION_RANGE_CONFORM_TO_CONTROLLER); BIND_ENUM_CONSTANT(MOTION_RANGE_MAX); + + BIND_ENUM_CONSTANT(SKELETON_RIG_OPENXR); + BIND_ENUM_CONSTANT(SKELETON_RIG_HUMANOID); + BIND_ENUM_CONSTANT(SKELETON_RIG_MAX); } OpenXRHand::OpenXRHand() { @@ -64,7 +72,7 @@ OpenXRHand::OpenXRHand() { hand_tracking_ext = OpenXRHandTrackingExtension::get_singleton(); } -void OpenXRHand::set_hand(const Hands p_hand) { +void OpenXRHand::set_hand(Hands p_hand) { ERR_FAIL_INDEX(p_hand, HAND_MAX); hand = p_hand; @@ -80,7 +88,7 @@ void OpenXRHand::set_hand_skeleton(const NodePath &p_hand_skeleton) { // TODO if inside tree call _get_bones() } -void OpenXRHand::set_motion_range(const MotionRange p_motion_range) { +void OpenXRHand::set_motion_range(MotionRange p_motion_range) { ERR_FAIL_INDEX(p_motion_range, MOTION_RANGE_MAX); motion_range = p_motion_range; @@ -116,6 +124,16 @@ void OpenXRHand::_set_motion_range() { hand_tracking_ext->set_motion_range(OpenXRHandTrackingExtension::HandTrackedHands(hand), xr_motion_range); } +void OpenXRHand::set_skeleton_rig(SkeletonRig p_skeleton_rig) { + ERR_FAIL_INDEX(p_skeleton_rig, SKELETON_RIG_MAX); + + skeleton_rig = p_skeleton_rig; +} + +OpenXRHand::SkeletonRig OpenXRHand::get_skeleton_rig() const { + return skeleton_rig; +} + Skeleton3D *OpenXRHand::get_skeleton() { if (!has_node(hand_skeleton)) { return nullptr; @@ -130,39 +148,81 @@ Skeleton3D *OpenXRHand::get_skeleton() { return skeleton; } -void OpenXRHand::_get_bones() { - const char *bone_names[XR_HAND_JOINT_COUNT_EXT] = { - "Palm", - "Wrist", - "Thumb_Metacarpal", - "Thumb_Proximal", - "Thumb_Distal", - "Thumb_Tip", - "Index_Metacarpal", - "Index_Proximal", - "Index_Intermediate", - "Index_Distal", - "Index_Tip", - "Middle_Metacarpal", - "Middle_Proximal", - "Middle_Intermediate", - "Middle_Distal", - "Middle_Tip", - "Ring_Metacarpal", - "Ring_Proximal", - "Ring_Intermediate", - "Ring_Distal", - "Ring_Tip", - "Little_Metacarpal", - "Little_Proximal", - "Little_Intermediate", - "Little_Distal", - "Little_Tip", +void OpenXRHand::_get_joint_data() { + // Table of bone names for different rig types. + static const String bone_names[SKELETON_RIG_MAX][XR_HAND_JOINT_COUNT_EXT] = { + // SKELETON_RIG_OPENXR bone names. + { + "Palm", + "Wrist", + "Thumb_Metacarpal", + "Thumb_Proximal", + "Thumb_Distal", + "Thumb_Tip", + "Index_Metacarpal", + "Index_Proximal", + "Index_Intermediate", + "Index_Distal", + "Index_Tip", + "Middle_Metacarpal", + "Middle_Proximal", + "Middle_Intermediate", + "Middle_Distal", + "Middle_Tip", + "Ring_Metacarpal", + "Ring_Proximal", + "Ring_Intermediate", + "Ring_Distal", + "Ring_Tip", + "Little_Metacarpal", + "Little_Proximal", + "Little_Intermediate", + "Little_Distal", + "Little_Tip" }, + + // SKELETON_RIG_HUMANOID bone names. + { + "Palm", + "Hand", + "ThumbMetacarpal", + "ThumbProximal", + "ThumbDistal", + "ThumbTip", + "IndexMetacarpal", + "IndexProximal", + "IndexIntermediate", + "IndexDistal", + "IndexTip", + "MiddleMetacarpal", + "MiddleProximal", + "MiddleIntermediate", + "MiddleDistal", + "MiddleTip", + "RingMetacarpal", + "RingProximal", + "RingIntermediate", + "RingDistal", + "RingTip", + "LittleMetacarpal", + "LittleProximal", + "LittleIntermediate", + "LittleDistal", + "LittleTip" } + }; + + // Table of bone name formats for different rig types and left/right hands. + static const String bone_name_formats[SKELETON_RIG_MAX][2] = { + // SKELETON_RIG_OPENXR bone name format. + { "<bone>_L", "<bone>_R" }, + + // SKELETON_RIG_HUMANOID bone name format. + { "Left<bone>", "Right<bone>" } }; // reset JIC for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { - bones[i] = -1; + joints[i].bone = -1; + joints[i].parent_joint = -1; } Skeleton3D *skeleton = get_skeleton(); @@ -170,20 +230,46 @@ void OpenXRHand::_get_bones() { return; } - // We cast to spatials which should allow us to use any subclass of that. + // Find the skeleton-bones associated with each OpenXR joint. + int bones[XR_HAND_JOINT_COUNT_EXT]; for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { - String bone_name = bone_names[i]; - if (hand == 0) { - bone_name += String("_L"); - } else { - bone_name += String("_R"); - } + // Construct the expected bone name. + String bone_name = bone_name_formats[skeleton_rig][hand].replace("<bone>", bone_names[skeleton_rig][i]); + // Find the skeleton bone. bones[i] = skeleton->find_bone(bone_name); if (bones[i] == -1) { print_line("Couldn't obtain bone for", bone_name); } } + + // Assemble the OpenXR joint relationship to the available skeleton bones. + for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { + // Get the skeleton bone (skip if not found). + const int bone = bones[i]; + if (bone == -1) { + continue; + } + + // Find the parent skeleton-bone. + const int parent_bone = skeleton->get_bone_parent(bone); + if (parent_bone == -1) { + // If no parent skeleton-bone exists then drive this relative to palm joint. + joints[i].bone = bone; + joints[i].parent_joint = XR_HAND_JOINT_PALM_EXT; + continue; + } + + // Find the OpenXR joint associated with the parent skeleton-bone. + for (int j = 0; j < XR_HAND_JOINT_COUNT_EXT; ++j) { + if (bones[j] == parent_bone) { + // If a parent joint is found then drive this bone relative to it. + joints[i].bone = bone; + joints[i].parent_joint = j; + break; + } + } + } } void OpenXRHand::_update_skeleton() { @@ -198,12 +284,25 @@ void OpenXRHand::_update_skeleton() { return; } + // Table of bone adjustments for different rig types + static const Quaternion bone_adjustments[SKELETON_RIG_MAX] = { + // SKELETON_RIG_OPENXR bone adjustment. This is an identity quaternion + // because the incoming quaternions are already in OpenXR format. + Quaternion(), + + // SKELETON_RIG_HUMANOID bone adjustment. This rotation performs: + // OpenXR Z+ -> Godot Humanoid Y- (Back along the bone) + // OpenXR Y+ -> Godot Humanoid Z- (Out the back of the hand) + Quaternion(0.0, -Math_SQRT12, Math_SQRT12, 0.0), + }; + // we cache our transforms so we can quickly calculate local transforms XRPose::TrackingConfidence confidences[XR_HAND_JOINT_COUNT_EXT]; Quaternion quaternions[XR_HAND_JOINT_COUNT_EXT]; Quaternion inv_quaternions[XR_HAND_JOINT_COUNT_EXT]; Vector3 positions[XR_HAND_JOINT_COUNT_EXT]; + const Quaternion &rig_adjustment = bone_adjustments[skeleton_rig]; const OpenXRHandTrackingExtension::HandTracker *hand_tracker = hand_tracking_ext->get_hand_tracker(OpenXRHandTrackingExtension::HandTrackedHands(hand)); const float ws = XRServer::get_singleton()->get_world_scale(); @@ -218,7 +317,7 @@ void OpenXRHand::_update_skeleton() { if (location.locationFlags & XR_SPACE_LOCATION_ORIENTATION_VALID_BIT) { if (pose.orientation.x != 0 || pose.orientation.y != 0 || pose.orientation.z != 0 || pose.orientation.w != 0) { - quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w); + quaternions[i] = Quaternion(pose.orientation.x, pose.orientation.y, pose.orientation.z, pose.orientation.w) * rig_adjustment; inv_quaternions[i] = quaternions[i].inverse(); if (location.locationFlags & XR_SPACE_LOCATION_POSITION_VALID_BIT) { @@ -234,40 +333,25 @@ void OpenXRHand::_update_skeleton() { } if (confidences[XR_HAND_JOINT_PALM_EXT] != XRPose::XR_TRACKING_CONFIDENCE_NONE) { - // now update our skeleton - for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { - if (bones[i] != -1) { - int bone = bones[i]; - int parent = skeleton->get_bone_parent(bone); - - // Get our target quaternion - Quaternion q = quaternions[i]; + // Iterate over all the OpenXR joints. + for (int joint = 0; joint < XR_HAND_JOINT_COUNT_EXT; joint++) { + // Get the skeleton bone (skip if none). + const int bone = joints[joint].bone; + if (bone == -1) { + continue; + } - // Get our target position - Vector3 p = positions[i]; + // Calculate the relative relationship to the parent bone joint. + const int parent_joint = joints[joint].parent_joint; + const Quaternion q = inv_quaternions[parent_joint] * quaternions[joint]; + const Vector3 p = inv_quaternions[parent_joint].xform(positions[joint] - positions[parent_joint]); - // get local translation, parent should already be processed - if (parent == -1) { - // use our palm location here, that is what we are tracking - q = inv_quaternions[XR_HAND_JOINT_PALM_EXT] * q; - p = inv_quaternions[XR_HAND_JOINT_PALM_EXT].xform(p - positions[XR_HAND_JOINT_PALM_EXT]); - } else { - int found = false; - for (int b = 0; b < XR_HAND_JOINT_COUNT_EXT && !found; b++) { - if (bones[b] == parent) { - q = inv_quaternions[b] * q; - p = inv_quaternions[b].xform(p - positions[b]); - found = true; - } - } - } - - // and set our pose - skeleton->set_bone_pose_position(bones[i], p); - skeleton->set_bone_pose_rotation(bones[i], q); - } + // and set our pose + skeleton->set_bone_pose_position(joints[joint].bone, p); + skeleton->set_bone_pose_rotation(joints[joint].bone, q); } + // Transform the OpenXRHand to the skeleton pose. Transform3D t; t.basis = Basis(quaternions[XR_HAND_JOINT_PALM_EXT]); t.origin = positions[XR_HAND_JOINT_PALM_EXT]; @@ -288,7 +372,7 @@ void OpenXRHand::_update_skeleton() { void OpenXRHand::_notification(int p_what) { switch (p_what) { case NOTIFICATION_ENTER_TREE: { - _get_bones(); + _get_joint_data(); set_process_internal(true); } break; @@ -297,7 +381,8 @@ void OpenXRHand::_notification(int p_what) { // reset for (int i = 0; i < XR_HAND_JOINT_COUNT_EXT; i++) { - bones[i] = -1; + joints[i].bone = -1; + joints[i].parent_joint = -1; } } break; case NOTIFICATION_INTERNAL_PROCESS: { diff --git a/modules/openxr/scene/openxr_hand.h b/modules/openxr/scene/openxr_hand.h index edfb474ac7..14eb893bcc 100644 --- a/modules/openxr/scene/openxr_hand.h +++ b/modules/openxr/scene/openxr_hand.h @@ -55,20 +55,32 @@ public: MOTION_RANGE_MAX }; + enum SkeletonRig { + SKELETON_RIG_OPENXR, + SKELETON_RIG_HUMANOID, + SKELETON_RIG_MAX + }; + private: + struct JointData { + int bone = -1; + int parent_joint = -1; + }; + OpenXRAPI *openxr_api = nullptr; OpenXRHandTrackingExtension *hand_tracking_ext = nullptr; Hands hand = HAND_LEFT; MotionRange motion_range = MOTION_RANGE_UNOBSTRUCTED; NodePath hand_skeleton; + SkeletonRig skeleton_rig = SKELETON_RIG_OPENXR; - int64_t bones[XR_HAND_JOINT_COUNT_EXT]; + JointData joints[XR_HAND_JOINT_COUNT_EXT]; void _set_motion_range(); Skeleton3D *get_skeleton(); - void _get_bones(); + void _get_joint_data(); void _update_skeleton(); protected: @@ -77,19 +89,23 @@ protected: public: OpenXRHand(); - void set_hand(const Hands p_hand); + void set_hand(Hands p_hand); Hands get_hand() const; - void set_motion_range(const MotionRange p_motion_range); + void set_motion_range(MotionRange p_motion_range); MotionRange get_motion_range() const; void set_hand_skeleton(const NodePath &p_hand_skeleton); NodePath get_hand_skeleton() const; + void set_skeleton_rig(SkeletonRig p_skeleton_rig); + SkeletonRig get_skeleton_rig() const; + void _notification(int p_what); }; VARIANT_ENUM_CAST(OpenXRHand::Hands) VARIANT_ENUM_CAST(OpenXRHand::MotionRange) +VARIANT_ENUM_CAST(OpenXRHand::SkeletonRig) #endif // OPENXR_HAND_H diff --git a/modules/regex/regex.cpp b/modules/regex/regex.cpp index 704c107f20..d49578d2a9 100644 --- a/modules/regex/regex.cpp +++ b/modules/regex/regex.cpp @@ -270,16 +270,18 @@ Ref<RegExMatch> RegEx::search(const String &p_subject, int p_offset, int p_end) TypedArray<RegExMatch> RegEx::search_all(const String &p_subject, int p_offset, int p_end) const { ERR_FAIL_COND_V_MSG(p_offset < 0, Array(), "RegEx search offset must be >= 0"); - int last_end = -1; + int last_end = 0; TypedArray<RegExMatch> result; Ref<RegExMatch> match = search(p_subject, p_offset, p_end); + while (match.is_valid()) { - if (last_end == match->get_end(0)) { - break; + last_end = match->get_end(0); + if (match->get_start(0) == last_end) { + last_end++; } + result.push_back(match); - last_end = match->get_end(0); - match = search(p_subject, match->get_end(0), p_end); + match = search(p_subject, last_end, p_end); } return result; } diff --git a/modules/regex/tests/test_regex.h b/modules/regex/tests/test_regex.h index 3e4d769377..0b401da831 100644 --- a/modules/regex/tests/test_regex.h +++ b/modules/regex/tests/test_regex.h @@ -164,7 +164,7 @@ TEST_CASE("[RegEx] Uninitialized use") { ERR_PRINT_ON } -TEST_CASE("[RegEx] Empty Pattern") { +TEST_CASE("[RegEx] Empty pattern") { const String s = "Godot"; RegEx re; @@ -222,6 +222,143 @@ TEST_CASE("[RegEx] Match start and end positions") { CHECK(match->get_start("vowel") == 2); CHECK(match->get_end("vowel") == 3); } + +TEST_CASE("[RegEx] Asterisk search all") { + const String s = "Godot Engine"; + + RegEx re("o*"); + REQUIRE(re.is_valid()); + Ref<RegExMatch> match; + const Array all_results = re.search_all(s); + CHECK(all_results.size() == 13); + + match = all_results[0]; + CHECK(match->get_string(0) == ""); + match = all_results[1]; + CHECK(match->get_string(0) == "o"); + match = all_results[2]; + CHECK(match->get_string(0) == ""); + match = all_results[3]; + CHECK(match->get_string(0) == "o"); + + for (int i = 4; i < 13; i++) { + match = all_results[i]; + CHECK(match->get_string(0) == ""); + } +} + +TEST_CASE("[RegEx] Simple lookahead") { + const String s = "Godot Engine"; + + RegEx re("o(?=t)"); + REQUIRE(re.is_valid()); + Ref<RegExMatch> match = re.search(s); + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 3); + CHECK(match->get_end(0) == 4); +} + +TEST_CASE("[RegEx] Lookahead groups empty matches") { + const String s = "12"; + + RegEx re("(?=(\\d+))"); + REQUIRE(re.is_valid()); + Ref<RegExMatch> match = re.search(s); + CHECK(match->get_string(0) == ""); + CHECK(match->get_string(1) == "12"); + + const Array all_results = re.search_all(s); + CHECK(all_results.size() == 2); + + match = all_results[0]; + REQUIRE(match != nullptr); + CHECK(match->get_string(0) == String("")); + CHECK(match->get_string(1) == String("12")); + + match = all_results[1]; + REQUIRE(match != nullptr); + CHECK(match->get_string(0) == String("")); + CHECK(match->get_string(1) == String("2")); +} + +TEST_CASE("[RegEx] Simple lookbehind") { + const String s = "Godot Engine"; + + RegEx re("(?<=d)o"); + REQUIRE(re.is_valid()); + Ref<RegExMatch> match = re.search(s); + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 3); + CHECK(match->get_end(0) == 4); +} + +TEST_CASE("[RegEx] Simple lookbehind search all") { + const String s = "ababbaabab"; + + RegEx re("(?<=a)b"); + REQUIRE(re.is_valid()); + const Array all_results = re.search_all(s); + CHECK(all_results.size() == 4); + + Ref<RegExMatch> match = all_results[0]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 1); + CHECK(match->get_end(0) == 2); + + match = all_results[1]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 3); + CHECK(match->get_end(0) == 4); + + match = all_results[2]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 7); + CHECK(match->get_end(0) == 8); + + match = all_results[3]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 9); + CHECK(match->get_end(0) == 10); +} + +TEST_CASE("[RegEx] Lookbehind groups empty matches") { + const String s = "abaaabab"; + + RegEx re("(?<=(b))"); + REQUIRE(re.is_valid()); + Ref<RegExMatch> match; + + const Array all_results = re.search_all(s); + CHECK(all_results.size() == 3); + + match = all_results[0]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 2); + CHECK(match->get_end(0) == 2); + CHECK(match->get_start(1) == 1); + CHECK(match->get_end(1) == 2); + CHECK(match->get_string(0) == String("")); + CHECK(match->get_string(1) == String("b")); + + match = all_results[1]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 6); + CHECK(match->get_end(0) == 6); + CHECK(match->get_start(1) == 5); + CHECK(match->get_end(1) == 6); + CHECK(match->get_string(0) == String("")); + CHECK(match->get_string(1) == String("b")); + + match = all_results[2]; + REQUIRE(match != nullptr); + CHECK(match->get_start(0) == 8); + CHECK(match->get_end(0) == 8); + CHECK(match->get_start(1) == 7); + CHECK(match->get_end(1) == 8); + CHECK(match->get_string(0) == String("")); + CHECK(match->get_string(1) == String("b")); +} + } // namespace TestRegEx #endif // TEST_REGEX_H diff --git a/modules/svg/SCsub b/modules/svg/SCsub index d0c6250b11..ff0c3c9141 100644 --- a/modules/svg/SCsub +++ b/modules/svg/SCsub @@ -43,6 +43,8 @@ thirdparty_sources = [ "src/renderer/tvgShape.cpp", "src/renderer/tvgSwCanvas.cpp", "src/renderer/tvgTaskScheduler.cpp", + "src/renderer/tvgText.cpp", + # "src/renderer/tvgWgCanvas.cpp", # renderer sw_engine "src/renderer/sw_engine/tvgSwFill.cpp", "src/renderer/sw_engine/tvgSwImage.cpp", diff --git a/modules/text_server_adv/gdextension_build/SConstruct b/modules/text_server_adv/gdextension_build/SConstruct index f6d3b24cee..cd4331b60e 100644 --- a/modules/text_server_adv/gdextension_build/SConstruct +++ b/modules/text_server_adv/gdextension_build/SConstruct @@ -74,6 +74,8 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: "src/renderer/tvgShape.cpp", "src/renderer/tvgSwCanvas.cpp", "src/renderer/tvgTaskScheduler.cpp", + "src/renderer/tvgText.cpp", + # "src/renderer/tvgWgCanvas.cpp", # renderer sw_engine "src/renderer/sw_engine/tvgSwFill.cpp", "src/renderer/sw_engine/tvgSwImage.cpp", diff --git a/modules/text_server_adv/register_types.h b/modules/text_server_adv/register_types.h index c11765048d..477e030e03 100644 --- a/modules/text_server_adv/register_types.h +++ b/modules/text_server_adv/register_types.h @@ -34,7 +34,7 @@ #ifdef GDEXTENSION #include <godot_cpp/core/class_db.hpp> using namespace godot; -#else +#elif defined(GODOT_MODULE) #include "modules/register_module_types.h" #endif diff --git a/modules/text_server_adv/script_iterator.h b/modules/text_server_adv/script_iterator.h index 164fdec784..f7876d6cbc 100644 --- a/modules/text_server_adv/script_iterator.h +++ b/modules/text_server_adv/script_iterator.h @@ -40,7 +40,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/string/ustring.h" diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp index bcc7bff909..19f302423c 100644 --- a/modules/text_server_adv/text_server_adv.cpp +++ b/modules/text_server_adv/text_server_adv.cpp @@ -44,7 +44,7 @@ using namespace godot; #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var) -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/config/project_settings.h" @@ -370,7 +370,7 @@ bool TextServerAdvanced::_has_feature(Feature p_feature) const { String TextServerAdvanced::_get_name() const { #ifdef GDEXTENSION return "ICU / HarfBuzz / Graphite (GDExtension)"; -#else +#elif defined(GODOT_MODULE) return "ICU / HarfBuzz / Graphite (Built-in)"; #endif } @@ -4852,7 +4852,7 @@ RID TextServerAdvanced::_find_sys_font_for_text(const RID &p_fdef, const String #ifdef GDEXTENSION for (int fb = 0; fb < fallback_font_name.size(); fb++) { const String &E = fallback_font_name[fb]; -#else +#elif defined(GODOT_MODULE) for (const String &E : fallback_font_name) { #endif SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, p_fdef, this); @@ -6757,7 +6757,7 @@ String TextServerAdvanced::_strip_diacritics(const String &p_string) const { if (u_getCombiningClass(normalized_string[i]) == 0) { #ifdef GDEXTENSION result = result + String::chr(normalized_string[i]); -#else +#elif defined(GODOT_MODULE) result = result + normalized_string[i]; #endif } diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h index e5d0ab3105..0b5fe47b8b 100644 --- a/modules/text_server_adv/text_server_adv.h +++ b/modules/text_server_adv/text_server_adv.h @@ -78,7 +78,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/extension/ext_wrappers.gen.inc" diff --git a/modules/text_server_adv/thorvg_bounds_iterator.cpp b/modules/text_server_adv/thorvg_bounds_iterator.cpp index 807f356b83..d273eef97f 100644 --- a/modules/text_server_adv/thorvg_bounds_iterator.cpp +++ b/modules/text_server_adv/thorvg_bounds_iterator.cpp @@ -35,7 +35,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/typedefs.h" diff --git a/modules/text_server_adv/thorvg_bounds_iterator.h b/modules/text_server_adv/thorvg_bounds_iterator.h index a44cbb99a7..afa2c13764 100644 --- a/modules/text_server_adv/thorvg_bounds_iterator.h +++ b/modules/text_server_adv/thorvg_bounds_iterator.h @@ -39,7 +39,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/typedefs.h" diff --git a/modules/text_server_adv/thorvg_svg_in_ot.cpp b/modules/text_server_adv/thorvg_svg_in_ot.cpp index 828f8d7d56..136ccf3aaf 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.cpp +++ b/modules/text_server_adv/thorvg_svg_in_ot.cpp @@ -38,7 +38,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/error/error_macros.h" diff --git a/modules/text_server_adv/thorvg_svg_in_ot.h b/modules/text_server_adv/thorvg_svg_in_ot.h index 034fffb5e6..ce048674fd 100644 --- a/modules/text_server_adv/thorvg_svg_in_ot.h +++ b/modules/text_server_adv/thorvg_svg_in_ot.h @@ -40,7 +40,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/os/mutex.h" diff --git a/modules/text_server_fb/gdextension_build/SConstruct b/modules/text_server_fb/gdextension_build/SConstruct index 0d2fbd97fd..0efced0bfc 100644 --- a/modules/text_server_fb/gdextension_build/SConstruct +++ b/modules/text_server_fb/gdextension_build/SConstruct @@ -69,6 +69,8 @@ if env["thorvg_enabled"] and env["freetype_enabled"]: "src/renderer/tvgShape.cpp", "src/renderer/tvgSwCanvas.cpp", "src/renderer/tvgTaskScheduler.cpp", + "src/renderer/tvgText.cpp", + # "src/renderer/tvgWgCanvas.cpp", # renderer sw_engine "src/renderer/sw_engine/tvgSwFill.cpp", "src/renderer/sw_engine/tvgSwImage.cpp", diff --git a/modules/text_server_fb/register_types.h b/modules/text_server_fb/register_types.h index 97bc06a8f7..0933ea83c5 100644 --- a/modules/text_server_fb/register_types.h +++ b/modules/text_server_fb/register_types.h @@ -34,7 +34,7 @@ #ifdef GDEXTENSION #include <godot_cpp/core/class_db.hpp> using namespace godot; -#else +#elif defined(GODOT_MODULE) #include "modules/register_module_types.h" #endif diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp index 8fc7694aa4..0b486b7d7b 100644 --- a/modules/text_server_fb/text_server_fb.cpp +++ b/modules/text_server_fb/text_server_fb.cpp @@ -44,7 +44,7 @@ using namespace godot; #define GLOBAL_GET(m_var) ProjectSettings::get_singleton()->get_setting_with_override(m_var) -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/config/project_settings.h" @@ -95,7 +95,7 @@ bool TextServerFallback::_has_feature(Feature p_feature) const { String TextServerFallback::_get_name() const { #ifdef GDEXTENSION return "Fallback (GDExtension)"; -#else +#elif defined(GODOT_MODULE) return "Fallback (Built-in)"; #endif } @@ -3654,7 +3654,7 @@ RID TextServerFallback::_find_sys_font_for_text(const RID &p_fdef, const String #ifdef GDEXTENSION for (int fb = 0; fb < fallback_font_name.size(); fb++) { const String &E = fallback_font_name[fb]; -#else +#elif defined(GODOT_MODULE) for (const String &E : fallback_font_name) { #endif SystemFontKey key = SystemFontKey(E, font_style & TextServer::FONT_ITALIC, font_weight, font_stretch, p_fdef, this); diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h index 68e0cbff6c..2bb3724858 100644 --- a/modules/text_server_fb/text_server_fb.h +++ b/modules/text_server_fb/text_server_fb.h @@ -76,7 +76,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/extension/ext_wrappers.gen.inc" diff --git a/modules/text_server_fb/thorvg_bounds_iterator.cpp b/modules/text_server_fb/thorvg_bounds_iterator.cpp index 807f356b83..d273eef97f 100644 --- a/modules/text_server_fb/thorvg_bounds_iterator.cpp +++ b/modules/text_server_fb/thorvg_bounds_iterator.cpp @@ -35,7 +35,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/typedefs.h" diff --git a/modules/text_server_fb/thorvg_bounds_iterator.h b/modules/text_server_fb/thorvg_bounds_iterator.h index a44cbb99a7..afa2c13764 100644 --- a/modules/text_server_fb/thorvg_bounds_iterator.h +++ b/modules/text_server_fb/thorvg_bounds_iterator.h @@ -39,7 +39,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/typedefs.h" diff --git a/modules/text_server_fb/thorvg_svg_in_ot.cpp b/modules/text_server_fb/thorvg_svg_in_ot.cpp index 7c8fedabc8..1ad33a88d4 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.cpp +++ b/modules/text_server_fb/thorvg_svg_in_ot.cpp @@ -38,7 +38,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/error/error_macros.h" diff --git a/modules/text_server_fb/thorvg_svg_in_ot.h b/modules/text_server_fb/thorvg_svg_in_ot.h index 034fffb5e6..ce048674fd 100644 --- a/modules/text_server_fb/thorvg_svg_in_ot.h +++ b/modules/text_server_fb/thorvg_svg_in_ot.h @@ -40,7 +40,7 @@ using namespace godot; -#else +#elif defined(GODOT_MODULE) // Headers for building as built-in module. #include "core/os/mutex.h" diff --git a/modules/zip/zip_packer.cpp b/modules/zip/zip_packer.cpp index e67c65d4d1..e96d9da7a9 100644 --- a/modules/zip/zip_packer.cpp +++ b/modules/zip/zip_packer.cpp @@ -88,7 +88,7 @@ Error ZIPPacker::start_file(const String &p_path) { Z_DEFAULT_STRATEGY, nullptr, 0, - 0x0314, // "version made by", 0x03 - Unix, 0x14 - ZIP specification version 2.0, required to store Unix file permissions. + 0, // "version made by", indicates the compatibility of the file attribute information (the `external_fa` field above). 1 << 11); // Bit 11 is the language encoding flag. When set, filename and comment fields must be encoded using UTF-8. return err == ZIP_OK ? OK : FAILED; } diff --git a/platform/android/export/export.cpp b/platform/android/export/export.cpp index ee1ec2790d..e276048d40 100644 --- a/platform/android/export/export.cpp +++ b/platform/android/export/export.cpp @@ -48,6 +48,7 @@ void register_android_exporter() { EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks")); EDITOR_DEF("export/android/debug_keystore_user", "androiddebugkey"); EDITOR_DEF("export/android/debug_keystore_pass", "android"); + EditorSettings::get_singleton()->add_property_hint(PropertyInfo(Variant::STRING, "export/android/debug_keystore_pass", PROPERTY_HINT_PASSWORD)); EDITOR_DEF("export/android/force_system_user", false); EDITOR_DEF("export/android/shutdown_adb_on_exit", true); diff --git a/platform/android/export/export_plugin.cpp b/platform/android/export/export_plugin.cpp index 2bdf3073c9..762e5a8323 100644 --- a/platform/android/export/export_plugin.cpp +++ b/platform/android/export/export_plugin.cpp @@ -1823,10 +1823,10 @@ void EditorExportPlatformAndroid::get_export_options(List<ExportOption> *r_optio r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/debug_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release", PROPERTY_HINT_GLOBAL_FILE, "*.keystore,*.jks", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_user", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); - r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_NONE, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "keystore/release_password", PROPERTY_HINT_PASSWORD, "", PROPERTY_USAGE_DEFAULT | PROPERTY_USAGE_SECRET), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "version/code", PROPERTY_HINT_RANGE, "1,4096,1,or_greater"), 1)); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "version/name", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); diff --git a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt index 217e7a2b60..da86e67c7d 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/Godot.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/Godot.kt @@ -484,6 +484,14 @@ class Godot(private val context: Context) : SensorEventListener { return containerLayout } + fun onStart(host: GodotHost) { + if (host != primaryHost) { + return + } + + renderView!!.onActivityStarted() + } + fun onResume(host: GodotHost) { if (host != primaryHost) { return @@ -528,6 +536,14 @@ class Godot(private val context: Context) : SensorEventListener { } } + fun onStop(host: GodotHost) { + if (host != primaryHost) { + return + } + + renderView!!.onActivityStopped() + } + fun onDestroy(primaryHost: GodotHost) { if (this.primaryHost != primaryHost) { return diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java index f1c029e7a1..643c9a658e 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotFragment.java @@ -271,6 +271,32 @@ public class GodotFragment extends Fragment implements IDownloaderClient, GodotH } @Override + public void onStop() { + super.onStop(); + if (!godot.isInitialized()) { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.disconnect(getActivity()); + } + return; + } + + godot.onStop(this); + } + + @Override + public void onStart() { + super.onStart(); + if (!godot.isInitialized()) { + if (null != mDownloaderClientStub) { + mDownloaderClientStub.connect(getActivity()); + } + return; + } + + godot.onStart(this); + } + + @Override public void onResume() { super.onResume(); if (!godot.isInitialized()) { diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java index 52350c12a6..81043ce782 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotGLRenderView.java @@ -114,12 +114,30 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView @Override public void onActivityPaused() { - onPause(); + queueEvent(() -> { + GodotLib.focusout(); + // Pause the renderer + godotRenderer.onActivityPaused(); + }); + } + + @Override + public void onActivityStopped() { + pauseGLThread(); } @Override public void onActivityResumed() { - onResume(); + queueEvent(() -> { + // Resume the renderer + godotRenderer.onActivityResumed(); + GodotLib.focusin(); + }); + } + + @Override + public void onActivityStarted() { + resumeGLThread(); } @Override @@ -283,26 +301,4 @@ public class GodotGLRenderView extends GLSurfaceView implements GodotRenderView /* Set the renderer responsible for frame rendering */ setRenderer(godotRenderer); } - - @Override - public void onResume() { - super.onResume(); - - queueEvent(() -> { - // Resume the renderer - godotRenderer.onActivityResumed(); - GodotLib.focusin(); - }); - } - - @Override - public void onPause() { - super.onPause(); - - queueEvent(() -> { - GodotLib.focusout(); - // Pause the renderer - godotRenderer.onActivityPaused(); - }); - } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java index ebf3a6b2fb..5b2f9f57c7 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotRenderView.java @@ -47,8 +47,13 @@ public interface GodotRenderView { void queueOnRenderThread(Runnable event); void onActivityPaused(); + + void onActivityStopped(); + void onActivityResumed(); + void onActivityStarted(); + void onBackPressed(); GodotInputHandler getInputHandler(); diff --git a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java index 48708152be..a1ee9bd6b4 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/GodotVulkanRenderView.java @@ -92,12 +92,30 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV @Override public void onActivityPaused() { - onPause(); + queueOnVkThread(() -> { + GodotLib.focusout(); + // Pause the renderer + mRenderer.onVkPause(); + }); + } + + @Override + public void onActivityStopped() { + pauseRenderThread(); + } + + @Override + public void onActivityStarted() { + resumeRenderThread(); } @Override public void onActivityResumed() { - onResume(); + queueOnVkThread(() -> { + // Resume the renderer + mRenderer.onVkResume(); + GodotLib.focusin(); + }); } @Override @@ -211,26 +229,4 @@ public class GodotVulkanRenderView extends VkSurfaceView implements GodotRenderV } return super.onResolvePointerIcon(me, pointerIndex); } - - @Override - public void onResume() { - super.onResume(); - - queueOnVkThread(() -> { - // Resume the renderer - mRenderer.onVkResume(); - GodotLib.focusin(); - }); - } - - @Override - public void onPause() { - super.onPause(); - - queueOnVkThread(() -> { - GodotLib.focusout(); - // Pause the renderer - mRenderer.onVkPause(); - }); - } } diff --git a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java index 56397bb2c2..ef97aaeab9 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java +++ b/platform/android/java/lib/src/org/godotengine/godot/gl/GLSurfaceView.java @@ -122,8 +122,8 @@ import javax.microedition.khronos.opengles.GL10; * <p> * <h3>Activity Life-cycle</h3> * A GLSurfaceView must be notified when to pause and resume rendering. GLSurfaceView clients - * are required to call {@link #onPause()} when the activity stops and - * {@link #onResume()} when the activity starts. These calls allow GLSurfaceView to + * are required to call {@link #pauseGLThread()} when the activity stops and + * {@link #resumeGLThread()} when the activity starts. These calls allow GLSurfaceView to * pause and resume the rendering thread, and also allow GLSurfaceView to release and recreate * the OpenGL display. * <p> @@ -339,8 +339,8 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * setRenderer is called: * <ul> * <li>{@link #getRenderMode()} - * <li>{@link #onPause()} - * <li>{@link #onResume()} + * <li>{@link #pauseGLThread()} + * <li>{@link #resumeGLThread()} * <li>{@link #queueEvent(Runnable)} * <li>{@link #requestRender()} * <li>{@link #setRenderMode(int)} @@ -568,6 +568,7 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback } + // -- GODOT start -- /** * Pause the rendering thread, optionally tearing down the EGL context * depending upon the value of {@link #setPreserveEGLContextOnPause(boolean)}. @@ -578,22 +579,23 @@ public class GLSurfaceView extends SurfaceView implements SurfaceHolder.Callback * * Must not be called before a renderer has been set. */ - public void onPause() { + protected final void pauseGLThread() { mGLThread.onPause(); } /** * Resumes the rendering thread, re-creating the OpenGL context if necessary. It - * is the counterpart to {@link #onPause()}. + * is the counterpart to {@link #pauseGLThread()}. * * This method should typically be called in * {@link android.app.Activity#onStart Activity.onStart}. * * Must not be called before a renderer has been set. */ - public void onResume() { + protected final void resumeGLThread() { mGLThread.onResume(); } + // -- GODOT end -- /** * Queue a runnable to be run on the GL rendering thread. This can be used diff --git a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt index 3828004198..791b425444 100644 --- a/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt +++ b/platform/android/java/lib/src/org/godotengine/godot/vulkan/VkSurfaceView.kt @@ -99,7 +99,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf * * Must not be called before a [VkRenderer] has been set. */ - open fun onResume() { + protected fun resumeRenderThread() { vkThread.onResume() } @@ -108,7 +108,7 @@ open internal class VkSurfaceView(context: Context) : SurfaceView(context), Surf * * Must not be called before a [VkRenderer] has been set. */ - open fun onPause() { + protected fun pauseRenderThread() { vkThread.onPause() } diff --git a/platform/android/java_godot_wrapper.cpp b/platform/android/java_godot_wrapper.cpp index 4e401e633e..3c950bb1b1 100644 --- a/platform/android/java_godot_wrapper.cpp +++ b/platform/android/java_godot_wrapper.cpp @@ -334,7 +334,7 @@ void GodotJavaWrapper::vibrate(int p_duration_ms) { } } -int GodotJavaWrapper::create_new_godot_instance(List<String> args) { +int GodotJavaWrapper::create_new_godot_instance(const List<String> &args) { if (_create_new_godot_instance) { JNIEnv *env = get_jni_env(); ERR_FAIL_NULL_V(env, 0); diff --git a/platform/android/java_godot_wrapper.h b/platform/android/java_godot_wrapper.h index 52043c6027..93998021a9 100644 --- a/platform/android/java_godot_wrapper.h +++ b/platform/android/java_godot_wrapper.h @@ -104,7 +104,7 @@ public: void init_input_devices(); void vibrate(int p_duration_ms); String get_input_fallback_mapping(); - int create_new_godot_instance(List<String> args); + int create_new_godot_instance(const List<String> &args); void begin_benchmark_measure(const String &p_context, const String &p_label); void end_benchmark_measure(const String &p_context, const String &p_label); void dump_benchmark(const String &benchmark_file); diff --git a/platform/ios/doc_classes/EditorExportPlatformIOS.xml b/platform/ios/doc_classes/EditorExportPlatformIOS.xml index 8f16a8c03e..588b20669b 100644 --- a/platform/ios/doc_classes/EditorExportPlatformIOS.xml +++ b/platform/ios/doc_classes/EditorExportPlatformIOS.xml @@ -34,6 +34,9 @@ <member name="application/icon_interpolation" type="int" setter="" getter=""> Interpolation method used to resize application icon. </member> + <member name="application/min_ios_version" type="String" setter="" getter=""> + Minimum version of iOS required for this application to run in the [code]major.minor.patch[/code] or [code]major.minor[/code] format, can only contain numeric characters ([code]0-9[/code]) and periods ([code].[/code]). + </member> <member name="application/provisioning_profile_uuid_debug" type="String" setter="" getter=""> UUID of the provisioning profile. If left empty, Xcode will download or create a provisioning profile automatically. See [url=https://developer.apple.com/help/account/manage-profiles/edit-download-or-delete-profiles]Edit, download, or delete provisioning profiles[/url]. Can be overridden with the environment variable [code]GODOT_IOS_PROVISIONING_PROFILE_UUID_DEBUG[/code]. @@ -60,6 +63,14 @@ <member name="capabilities/access_wifi" type="bool" setter="" getter=""> If [code]true[/code], networking features related to Wi-Fi access are enabled. See [url=https://developer.apple.com/support/required-device-capabilities/]Required Device Capabilities[/url]. </member> + <member name="capabilities/performance_a12" type="bool" setter="" getter=""> + Requires the graphics performance and features of the A12 Bionic and later chips (devices supporting all Vulkan renderer features). + Enabling this option limits supported devices to: iPhone XS, iPhone XR, iPad Mini (5th gen.), iPad Air (3rd gen.), iPad (8th gen) and newer. + </member> + <member name="capabilities/performance_gaming_tier" type="bool" setter="" getter=""> + Requires the graphics performance and features of the A17 Pro and later chips. + Enabling this option limits supported devices to: iPhone 15 Pro and newer. + </member> <member name="capabilities/push_notifications" type="bool" setter="" getter=""> If [code]true[/code], push notifications are enabled. See [url=https://developer.apple.com/support/required-device-capabilities/]Required Device Capabilities[/url]. </member> diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp index 616c2e8bc9..5be4d21411 100644 --- a/platform/ios/export/export_plugin.cpp +++ b/platform/ios/export/export_plugin.cpp @@ -152,6 +152,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/short_version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/version", PROPERTY_HINT_PLACEHOLDER_TEXT, "Leave empty to use project version"), "")); + r_options->push_back(ExportOption(PropertyInfo(Variant::STRING, "application/min_ios_version"), "12.0")); + r_options->push_back(ExportOption(PropertyInfo(Variant::INT, "application/icon_interpolation", PROPERTY_HINT_ENUM, "Nearest neighbor,Bilinear,Cubic,Trilinear,Lanczos"), 4)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "application/export_project_only"), false)); @@ -187,6 +189,8 @@ void EditorExportPlatformIOS::get_export_options(List<ExportOption> *r_options) r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/access_wifi"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/push_notifications"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/performance_gaming_tier"), false)); + r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "capabilities/performance_a12"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_files_app"), false)); r_options->push_back(ExportOption(PropertyInfo(Variant::BOOL, "user_data/accessible_from_itunes_sharing"), false)); @@ -252,6 +256,8 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ strnew += lines[i].replace("$short_version", p_preset->get_version("application/short_version")) + "\n"; } else if (lines[i].find("$version") != -1) { strnew += lines[i].replace("$version", p_preset->get_version("application/version")) + "\n"; + } else if (lines[i].find("$min_version") != -1) { + strnew += lines[i].replace("$min_version", p_preset->get("application/min_ios_version")) + "\n"; } else if (lines[i].find("$signature") != -1) { strnew += lines[i].replace("$signature", p_preset->get("application/signature")) + "\n"; } else if (lines[i].find("$team_id") != -1) { @@ -324,7 +330,12 @@ void EditorExportPlatformIOS::_fix_config_file(const Ref<EditorExportPreset> &p_ if ((bool)p_preset->get("capabilities/access_wifi") && !capabilities_list.has("wifi")) { capabilities_list.push_back("wifi"); } - + if ((bool)p_preset->get("capabilities/performance_gaming_tier") && !capabilities_list.has("iphone-performance-gaming-tier")) { + capabilities_list.push_back("iphone-performance-gaming-tier"); + } + if ((bool)p_preset->get("capabilities/performance_a12") && !capabilities_list.has("iphone-ipad-minimum-performance-a12")) { + capabilities_list.push_back("iphone-ipad-minimum-performance-a12"); + } for (int idx = 0; idx < capabilities_list.size(); idx++) { capabilities += "<string>" + capabilities_list[idx] + "</string>\n"; } diff --git a/platform/macos/export/export_plugin.cpp b/platform/macos/export/export_plugin.cpp index 6f9db1427b..6e750d0a16 100644 --- a/platform/macos/export/export_plugin.cpp +++ b/platform/macos/export/export_plugin.cpp @@ -2051,13 +2051,17 @@ bool EditorExportPlatformMacOS::has_valid_export_configuration(const Ref<EditorE String architecture = p_preset->get("binary_format/architecture"); if (architecture == "universal" || architecture == "x86_64") { if (!ResourceImporterTextureSettings::should_import_s3tc_bptc()) { + err += TTR("Cannot export for universal or x86_64 if S3TC BPTC texture format is disabled. Enable it in the Project Settings (Rendering > Textures > VRAM Compression > Import S3TC BPTC).") + "\n"; valid = false; } - } else if (architecture == "arm64") { + } + if (architecture == "universal" || architecture == "arm64") { if (!ResourceImporterTextureSettings::should_import_etc2_astc()) { + err += TTR("Cannot export for universal or arm64 if ETC2 ASTC texture format is disabled. Enable it in the Project Settings (Rendering > Textures > VRAM Compression > Import ETC2 ASTC).") + "\n"; valid = false; } - } else { + } + if (architecture != "universal" && architecture != "x86_64" && architecture != "arm64") { ERR_PRINT("Invalid architecture"); } diff --git a/platform/web/export/export_plugin.cpp b/platform/web/export/export_plugin.cpp index a70812cf5b..ee170280e2 100644 --- a/platform/web/export/export_plugin.cpp +++ b/platform/web/export/export_plugin.cpp @@ -112,7 +112,7 @@ Error EditorExportPlatformWeb::_write_or_error(const uint8_t *p_content, int p_s return OK; } -void EditorExportPlatformWeb::_replace_strings(HashMap<String, String> p_replaces, Vector<uint8_t> &r_template) { +void EditorExportPlatformWeb::_replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template) { String str_template = String::utf8(reinterpret_cast<const char *>(r_template.ptr()), r_template.size()); String out; Vector<String> lines = str_template.split("\n"); diff --git a/platform/web/export/export_plugin.h b/platform/web/export/export_plugin.h index 887000ac45..9bb82d472e 100644 --- a/platform/web/export/export_plugin.h +++ b/platform/web/export/export_plugin.h @@ -90,7 +90,7 @@ class EditorExportPlatformWeb : public EditorExportPlatform { } Error _extract_template(const String &p_template, const String &p_dir, const String &p_name, bool pwa); - void _replace_strings(HashMap<String, String> p_replaces, Vector<uint8_t> &r_template); + void _replace_strings(const HashMap<String, String> &p_replaces, Vector<uint8_t> &r_template); void _fix_html(Vector<uint8_t> &p_html, const Ref<EditorExportPreset> &p_preset, const String &p_name, bool p_debug, int p_flags, const Vector<SharedObject> p_shared_objects, const Dictionary &p_file_sizes); Error _add_manifest_icon(const String &p_path, const String &p_icon, int p_size, Array &r_arr); Error _build_pwa(const Ref<EditorExportPreset> &p_preset, const String p_path, const Vector<SharedObject> &p_shared_objects); diff --git a/scene/2d/collision_object_2d.cpp b/scene/2d/collision_object_2d.cpp index 2fbe4eb409..4e5852984b 100644 --- a/scene/2d/collision_object_2d.cpp +++ b/scene/2d/collision_object_2d.cpp @@ -523,30 +523,22 @@ void CollisionObject2D::_input_event_call(Viewport *p_viewport, const Ref<InputE } void CollisionObject2D::_mouse_enter() { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_enter); - } + GDVIRTUAL_CALL(_mouse_enter); emit_signal(SceneStringNames::get_singleton()->mouse_entered); } void CollisionObject2D::_mouse_exit() { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_exit); - } + GDVIRTUAL_CALL(_mouse_exit); emit_signal(SceneStringNames::get_singleton()->mouse_exited); } void CollisionObject2D::_mouse_shape_enter(int p_shape) { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_shape_enter, p_shape); - } + GDVIRTUAL_CALL(_mouse_shape_enter, p_shape); emit_signal(SceneStringNames::get_singleton()->mouse_shape_entered, p_shape); } void CollisionObject2D::_mouse_shape_exit(int p_shape) { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_shape_exit, p_shape); - } + GDVIRTUAL_CALL(_mouse_shape_exit, p_shape); emit_signal(SceneStringNames::get_singleton()->mouse_shape_exited, p_shape); } diff --git a/scene/2d/gpu_particles_2d.cpp b/scene/2d/gpu_particles_2d.cpp index ee33ff88d4..71da9cc520 100644 --- a/scene/2d/gpu_particles_2d.cpp +++ b/scene/2d/gpu_particles_2d.cpp @@ -712,12 +712,15 @@ void GPUParticles2D::_notification(int p_what) { } break; case NOTIFICATION_INTERNAL_PROCESS: { - RS::get_singleton()->particles_set_emitter_velocity(particles, - Vector3((get_global_position() - previous_position).x, - (get_global_position() - previous_position).y, - 0.0) / - get_process_delta_time()); + const Vector3 velocity = Vector3((get_global_position() - previous_position).x, (get_global_position() - previous_position).y, 0.0) / + get_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } previous_position = get_global_position(); + if (one_shot) { time += get_process_delta_time(); if (time > emission_time) { diff --git a/scene/2d/gpu_particles_2d.h b/scene/2d/gpu_particles_2d.h index 40831cd30e..58996b0327 100644 --- a/scene/2d/gpu_particles_2d.h +++ b/scene/2d/gpu_particles_2d.h @@ -64,6 +64,7 @@ private: bool fractional_delta = false; bool interpolate = true; float interp_to_end_factor = 0; + Vector3 previous_velocity; Vector2 previous_position; #ifdef TOOLS_ENABLED bool show_visibility_rect = false; diff --git a/scene/2d/light_2d.cpp b/scene/2d/light_2d.cpp index 15b638ed92..c03786caef 100644 --- a/scene/2d/light_2d.cpp +++ b/scene/2d/light_2d.cpp @@ -425,6 +425,17 @@ real_t PointLight2D::get_texture_scale() const { return _scale; } +#ifndef DISABLE_DEPRECATED +bool PointLight2D::_set(const StringName &p_name, const Variant &p_value) { + if (p_name == "mode" && p_value.is_num()) { // Compatibility with Godot 3.x. + set_blend_mode((BlendMode)(int)p_value); + return true; + } + + return false; +} +#endif // DISABLE_DEPRECATED + void PointLight2D::_bind_methods() { ClassDB::bind_method(D_METHOD("set_texture", "texture"), &PointLight2D::set_texture); ClassDB::bind_method(D_METHOD("get_texture"), &PointLight2D::get_texture); diff --git a/scene/2d/light_2d.h b/scene/2d/light_2d.h index 6a454a421e..3c1171deae 100644 --- a/scene/2d/light_2d.h +++ b/scene/2d/light_2d.h @@ -146,6 +146,9 @@ private: Vector2 texture_offset; protected: +#ifndef DISABLE_DEPRECATED + bool _set(const StringName &p_name, const Variant &p_value); +#endif // DISABLE_DEPRECATED static void _bind_methods(); public: diff --git a/scene/2d/polygon_2d.cpp b/scene/2d/polygon_2d.cpp index ee186de5f1..4266060466 100644 --- a/scene/2d/polygon_2d.cpp +++ b/scene/2d/polygon_2d.cpp @@ -375,14 +375,14 @@ void Polygon2D::_notification(int p_what) { // Compute transform between mesh and skeleton for runtime AABB compute. const Transform2D mesh_transform = get_global_transform(); const Transform2D skeleton_transform = skeleton_node->get_global_transform(); - const Transform2D mesh_to_sk2d = mesh_transform * skeleton_transform.affine_inverse(); + const Transform2D mesh_to_sk2d = skeleton_transform.affine_inverse() * mesh_transform; // Convert 2d transform to 3d. sd.mesh_to_skeleton_xform.basis.rows[0][0] = mesh_to_sk2d.columns[0][0]; - sd.mesh_to_skeleton_xform.basis.rows[0][1] = mesh_to_sk2d.columns[0][1]; + sd.mesh_to_skeleton_xform.basis.rows[1][0] = mesh_to_sk2d.columns[0][1]; sd.mesh_to_skeleton_xform.origin.x = mesh_to_sk2d.get_origin().x; - sd.mesh_to_skeleton_xform.basis.rows[1][0] = mesh_to_sk2d.columns[1][0]; + sd.mesh_to_skeleton_xform.basis.rows[0][1] = mesh_to_sk2d.columns[1][0]; sd.mesh_to_skeleton_xform.basis.rows[1][1] = mesh_to_sk2d.columns[1][1]; sd.mesh_to_skeleton_xform.origin.y = mesh_to_sk2d.get_origin().y; } diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index f2be91cbf3..decbbab476 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -32,2858 +32,8 @@ #include "tile_map.compat.inc" #include "core/core_string_names.h" -#include "core/io/marshalls.h" -#include "scene/resources/world_2d.h" -#include "servers/navigation_server_2d.h" - -#ifdef DEBUG_ENABLED -#include "servers/navigation_server_3d.h" -#endif // DEBUG_ENABLED - -#ifdef DEBUG_ENABLED -/////////////////////////////// Debug ////////////////////////////////////////// -constexpr int TILE_MAP_DEBUG_QUADRANT_SIZE = 16; - -Vector2i TileMapLayer::_coords_to_debug_quadrant_coords(const Vector2i &p_coords) const { - return Vector2i( - p_coords.x > 0 ? p_coords.x / TILE_MAP_DEBUG_QUADRANT_SIZE : (p_coords.x - (TILE_MAP_DEBUG_QUADRANT_SIZE - 1)) / TILE_MAP_DEBUG_QUADRANT_SIZE, - p_coords.y > 0 ? p_coords.y / TILE_MAP_DEBUG_QUADRANT_SIZE : (p_coords.y - (TILE_MAP_DEBUG_QUADRANT_SIZE - 1)) / TILE_MAP_DEBUG_QUADRANT_SIZE); -} - -void TileMapLayer::_debug_update() { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - RenderingServer *rs = RenderingServer::get_singleton(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); - - if (forced_cleanup) { - for (KeyValue<Vector2i, Ref<DebugQuadrant>> &kv : debug_quadrant_map) { - // Free the quadrant. - Ref<DebugQuadrant> &debug_quadrant = kv.value; - if (debug_quadrant->canvas_item.is_valid()) { - rs->free(debug_quadrant->canvas_item); - } - } - debug_quadrant_map.clear(); - _debug_was_cleaned_up = true; - return; - } - - // Check if anything is dirty, in such a case, redraw debug. - bool anything_changed = false; - for (int i = 0; i < DIRTY_FLAGS_MAX; i++) { - if (dirty.flags[i]) { - anything_changed = true; - break; - } - } - - // List all debug quadrants to update, creating new ones if needed. - SelfList<DebugQuadrant>::List dirty_debug_quadrant_list; - - if (_debug_was_cleaned_up || anything_changed) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - CellData &cell_data = kv.value; - _debug_quadrants_update_cell(cell_data, dirty_debug_quadrant_list); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _debug_quadrants_update_cell(cell_data, dirty_debug_quadrant_list); - } - } - - // Update those quadrants. - for (SelfList<DebugQuadrant> *quadrant_list_element = dirty_debug_quadrant_list.first(); quadrant_list_element;) { - SelfList<DebugQuadrant> *next_quadrant_list_element = quadrant_list_element->next(); // "Hack" to clear the list while iterating. - - DebugQuadrant &debug_quadrant = *quadrant_list_element->self(); - - // Check if the quadrant has a tile. - bool has_a_tile = false; - RID &ci = debug_quadrant.canvas_item; - for (SelfList<CellData> *cell_data_list_element = debug_quadrant.cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { - has_a_tile = true; - break; - } - } - - if (has_a_tile) { - // Update the quadrant. - if (ci.is_valid()) { - rs->canvas_item_clear(ci); - } else { - ci = rs->canvas_item_create(); - rs->canvas_item_set_z_index(ci, RS::CANVAS_ITEM_Z_MAX - 1); - rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item()); - } - - const Vector2 quadrant_pos = tile_map_node->map_to_local(debug_quadrant.quadrant_coords * TILE_MAP_DEBUG_QUADRANT_SIZE); - Transform2D xform(0, quadrant_pos); - rs->canvas_item_set_transform(ci, xform); - - for (SelfList<CellData> *cell_data_list_element = debug_quadrant.cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { - _rendering_draw_cell_debug(ci, quadrant_pos, cell_data); - _physics_draw_cell_debug(ci, quadrant_pos, cell_data); - _navigation_draw_cell_debug(ci, quadrant_pos, cell_data); - _scenes_draw_cell_debug(ci, quadrant_pos, cell_data); - } - } - } else { - // Free the quadrant. - if (ci.is_valid()) { - rs->free(ci); - } - quadrant_list_element->remove_from_list(); - debug_quadrant_map.erase(debug_quadrant.quadrant_coords); - } - - quadrant_list_element = next_quadrant_list_element; - } - - dirty_debug_quadrant_list.clear(); - - _debug_was_cleaned_up = false; -} - -void TileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list) { - Vector2i quadrant_coords = _coords_to_debug_quadrant_coords(r_cell_data.coords); - - if (!debug_quadrant_map.has(quadrant_coords)) { - // Create a new quadrant and add it to the quadrant map. - Ref<DebugQuadrant> new_quadrant; - new_quadrant.instantiate(); - new_quadrant->quadrant_coords = quadrant_coords; - debug_quadrant_map[quadrant_coords] = new_quadrant; - } - - // Add the cell to its quadrant, if it is not already in there. - Ref<DebugQuadrant> &debug_quadrant = debug_quadrant_map[quadrant_coords]; - if (!r_cell_data.debug_quadrant_list_element.in_list()) { - debug_quadrant->cells.add(&r_cell_data.debug_quadrant_list_element); - } - - // Mark the quadrant as dirty. - if (!debug_quadrant->dirty_quadrant_list_element.in_list()) { - r_dirty_debug_quadrant_list.add(&debug_quadrant->dirty_quadrant_list_element); - } -} -#endif // DEBUG_ENABLED - -/////////////////////////////// Rendering ////////////////////////////////////// -void TileMapLayer::_rendering_update() { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - RenderingServer *rs = RenderingServer::get_singleton(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); - - // ----------- Layer level processing ----------- - if (forced_cleanup) { - // Cleanup. - if (canvas_item.is_valid()) { - rs->free(canvas_item); - canvas_item = RID(); - } - } else { - // Create/Update the layer's CanvasItem. - if (!canvas_item.is_valid()) { - RID ci = rs->canvas_item_create(); - rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item()); - canvas_item = ci; - } - RID &ci = canvas_item; - rs->canvas_item_set_draw_index(ci, layer_index_in_tile_map_node - (int64_t)0x80000000); - rs->canvas_item_set_sort_children_by_y(ci, y_sort_enabled); - rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); - rs->canvas_item_set_z_index(ci, z_index); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); - rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); - - // Modulate the layer. - Color layer_modulate = modulate; - int selected_layer = tile_map_node->get_selected_layer(); - if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) { - int z_selected = tile_map_node->get_layer_z_index(selected_layer); - if (z_index < z_selected || (z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) { - layer_modulate = layer_modulate.darkened(0.5); - } else if (z_index > z_selected || (z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) { - layer_modulate = layer_modulate.darkened(0.5); - layer_modulate.a *= 0.3; - } - } - rs->canvas_item_set_modulate(ci, layer_modulate); - } - - // ----------- Quadrants processing ----------- - - // List all rendering quadrants to update, creating new ones if needed. - SelfList<RenderingQuadrant>::List dirty_rendering_quadrant_list; - - // Check if anything changed that might change the quadrant shape. - // If so, recreate everything. - bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE] || - (tile_map_node->is_y_sort_enabled() && y_sort_enabled && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET])); - - // Free all quadrants. - if (forced_cleanup || quandrant_shape_changed) { - for (const KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { - for (int i = 0; i < kv.value->canvas_items.size(); i++) { - const RID &ci = kv.value->canvas_items[i]; - if (ci.is_valid()) { - rs->free(ci); - } - } - kv.value->cells.clear(); - } - rendering_quadrant_map.clear(); - _rendering_was_cleaned_up = true; - } - - if (!forced_cleanup) { - // List all quadrants to update, recreating them if needed. - if (dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || _rendering_was_cleaned_up) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - CellData &cell_data = kv.value; - _rendering_quadrants_update_cell(cell_data, dirty_rendering_quadrant_list); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _rendering_quadrants_update_cell(cell_data, dirty_rendering_quadrant_list); - } - } - - // Update all dirty quadrants. - for (SelfList<RenderingQuadrant> *quadrant_list_element = dirty_rendering_quadrant_list.first(); quadrant_list_element;) { - SelfList<RenderingQuadrant> *next_quadrant_list_element = quadrant_list_element->next(); // "Hack" to clear the list while iterating. - - const Ref<RenderingQuadrant> &rendering_quadrant = quadrant_list_element->self(); - - // Check if the quadrant has a tile. - bool has_a_tile = false; - for (SelfList<CellData> *cell_data_list_element = rendering_quadrant->cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { - has_a_tile = true; - break; - } - } - - if (has_a_tile) { - // Process the quadrant. - - // First, clear the quadrant's canvas items. - for (RID &ci : rendering_quadrant->canvas_items) { - rs->free(ci); - } - rendering_quadrant->canvas_items.clear(); - - // Sort the quadrant cells. - if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) { - // For compatibility reasons, we use another comparator for Y-sorted layers. - rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>(); - } else { - rendering_quadrant->cells.sort(); - } - - // Those allow to group cell per material or z-index. - Ref<Material> prev_material; - int prev_z_index = 0; - RID prev_ci; - - for (SelfList<CellData> *cell_data_quadrant_list_element = rendering_quadrant->cells.first(); cell_data_quadrant_list_element; cell_data_quadrant_list_element = cell_data_quadrant_list_element->next()) { - CellData &cell_data = *cell_data_quadrant_list_element->self(); - - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(cell_data.cell.source_id)); - - // Get the tile data. - const TileData *tile_data; - if (cell_data.runtime_tile_data_cache) { - tile_data = cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile); - } - - Ref<Material> mat = tile_data->get_material(); - int tile_z_index = tile_data->get_z_index(); - - // Quandrant pos. - - // --- CanvasItems --- - RID ci; - - // Check if the material or the z_index changed. - if (prev_ci == RID() || prev_material != mat || prev_z_index != tile_z_index) { - // If so, create a new CanvasItem. - ci = rs->canvas_item_create(); - if (mat.is_valid()) { - rs->canvas_item_set_material(ci, mat->get_rid()); - } - rs->canvas_item_set_parent(ci, canvas_item); - rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); - - Transform2D xform(0, rendering_quadrant->canvas_items_position); - rs->canvas_item_set_transform(ci, xform); - - rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); - rs->canvas_item_set_z_as_relative_to_parent(ci, true); - rs->canvas_item_set_z_index(ci, tile_z_index); - - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); - - rendering_quadrant->canvas_items.push_back(ci); - - prev_ci = ci; - prev_material = mat; - prev_z_index = tile_z_index; - - } else { - // Keep the same canvas_item to draw on. - ci = prev_ci; - } - - const Vector2 local_tile_pos = tile_map_node->map_to_local(cell_data.coords); - - // Random animation offset. - real_t random_animation_offset = 0.0; - if (atlas_source->get_tile_animation_mode(cell_data.cell.get_atlas_coords()) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) { - Array to_hash; - to_hash.push_back(local_tile_pos); - to_hash.push_back(get_instance_id()); // Use instance id as a random hash - random_animation_offset = RandomPCG(to_hash.hash()).randf(); - } - - // Drawing the tile in the canvas item. - tile_map_node->draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset); - } - } else { - // Free the quadrant. - for (int i = 0; i < rendering_quadrant->canvas_items.size(); i++) { - const RID &ci = rendering_quadrant->canvas_items[i]; - if (ci.is_valid()) { - rs->free(ci); - } - } - rendering_quadrant->cells.clear(); - rendering_quadrant_map.erase(rendering_quadrant->quadrant_coords); - } - - quadrant_list_element = next_quadrant_list_element; - } - - dirty_rendering_quadrant_list.clear(); - - // Reset the drawing indices. - { - int index = -(int64_t)0x80000000; // Always must be drawn below children. - - // Sort the quadrants coords per local coordinates. - RBMap<Vector2, Ref<RenderingQuadrant>, RenderingQuadrant::CoordsWorldComparator> local_to_map; - for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { - Ref<RenderingQuadrant> &rendering_quadrant = kv.value; - local_to_map[tile_map_node->map_to_local(rendering_quadrant->quadrant_coords)] = rendering_quadrant; - } - - // Sort the quadrants. - for (const KeyValue<Vector2, Ref<RenderingQuadrant>> &E : local_to_map) { - for (const RID &ci : E.value->canvas_items) { - RS::get_singleton()->canvas_item_set_draw_index(ci, index++); - } - } - } - - // Updates on TileMap changes. - if (dirty.flags[DIRTY_FLAGS_TILE_MAP_LIGHT_MASK] || - dirty.flags[DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL] || - dirty.flags[DIRTY_FLAGS_TILE_MAP_MATERIAL] || - dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER] || - dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT]) { - for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { - Ref<RenderingQuadrant> &rendering_quadrant = kv.value; - for (const RID &ci : rendering_quadrant->canvas_items) { - rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); - rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); - rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); - rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); - } - } - } - } - - // ----------- Occluders processing ----------- - if (forced_cleanup) { - // Clean everything. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _rendering_occluders_clear_cell(kv.value); - } - } else { - if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _rendering_occluders_update_cell(kv.value); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _rendering_occluders_update_cell(cell_data); - } - } - - // Updates on TileMap changes. - if (dirty.flags[DIRTY_FLAGS_TILE_MAP_IN_CANVAS] || dirty.flags[DIRTY_FLAGS_TILE_MAP_VISIBILITY]) { - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - CellData &cell_data = kv.value; - for (const RID &occluder : cell_data.occluders) { - if (occluder.is_null()) { - continue; - } - Transform2D xform(0, tile_map_node->map_to_local(kv.key)); - rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); - rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); - } - } - } - } - - // ----------- - // Mark the rendering state as up to date. - _rendering_was_cleaned_up = forced_cleanup; -} - -void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - // Check if the cell is valid and retrieve its y_sort_origin. - bool is_valid = false; - int tile_y_sort_origin = 0; - TileSetSource *source; - if (tile_set->has_source(r_cell_data.cell.source_id)) { - source = *tile_set->get_source(r_cell_data.cell.source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source && atlas_source->has_tile(r_cell_data.cell.get_atlas_coords()) && atlas_source->has_alternative_tile(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)) { - is_valid = true; - const TileData *tile_data; - if (r_cell_data.runtime_tile_data_cache) { - tile_data = r_cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); - } - tile_y_sort_origin = tile_data->get_y_sort_origin(); - } - } - - if (is_valid) { - // Get the quadrant coords. - Vector2 canvas_items_position; - Vector2i quadrant_coords; - if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) { - canvas_items_position = Vector2(0, tile_map_node->map_to_local(r_cell_data.coords).y + tile_y_sort_origin + y_sort_origin); - quadrant_coords = canvas_items_position * 100; - } else { - int quad_size = tile_map_node->get_rendering_quadrant_size(); - const Vector2i &coords = r_cell_data.coords; - - // Rounding down, instead of simply rounding towards zero (truncating). - quadrant_coords = Vector2i( - coords.x > 0 ? coords.x / quad_size : (coords.x - (quad_size - 1)) / quad_size, - coords.y > 0 ? coords.y / quad_size : (coords.y - (quad_size - 1)) / quad_size); - canvas_items_position = quad_size * quadrant_coords; - } - - Ref<RenderingQuadrant> rendering_quadrant; - if (rendering_quadrant_map.has(quadrant_coords)) { - // Reuse existing rendering quadrant. - rendering_quadrant = rendering_quadrant_map[quadrant_coords]; - } else { - // Create a new rendering quadrant. - rendering_quadrant.instantiate(); - rendering_quadrant->quadrant_coords = quadrant_coords; - rendering_quadrant->canvas_items_position = canvas_items_position; - rendering_quadrant_map[quadrant_coords] = rendering_quadrant; - } - - // Mark the old quadrant as dirty (if it exists). - if (r_cell_data.rendering_quadrant.is_valid()) { - if (!r_cell_data.rendering_quadrant->dirty_quadrant_list_element.in_list()) { - r_dirty_rendering_quadrant_list.add(&r_cell_data.rendering_quadrant->dirty_quadrant_list_element); - } - } - - // Remove the cell from that quadrant. - if (r_cell_data.rendering_quadrant_list_element.in_list()) { - r_cell_data.rendering_quadrant_list_element.remove_from_list(); - } - - // Add the cell to its new quadrant. - r_cell_data.rendering_quadrant = rendering_quadrant; - r_cell_data.rendering_quadrant->cells.add(&r_cell_data.rendering_quadrant_list_element); - - // Add the new quadrant to the dirty quadrant list. - if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) { - r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element); - } - } else { - Ref<RenderingQuadrant> rendering_quadrant = r_cell_data.rendering_quadrant; - - // Remove the cell from its quadrant. - r_cell_data.rendering_quadrant = Ref<RenderingQuadrant>(); - if (r_cell_data.rendering_quadrant_list_element.in_list()) { - rendering_quadrant->cells.remove(&r_cell_data.rendering_quadrant_list_element); - } - - if (rendering_quadrant.is_valid()) { - // Add the quadrant to the dirty quadrant list. - if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) { - r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element); - } - } - } -} - -void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) { - RenderingServer *rs = RenderingServer::get_singleton(); - - // Free the occluders. - for (const RID &rid : r_cell_data.occluders) { - rs->free(rid); - } - r_cell_data.occluders.clear(); -} - -void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { - bool node_visible = tile_map_node->is_visible_in_tree(); - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - RenderingServer *rs = RenderingServer::get_singleton(); - - // Free unused occluders then resize the occluders array. - for (uint32_t i = tile_set->get_occlusion_layers_count(); i < r_cell_data.occluders.size(); i++) { - RID occluder_id = r_cell_data.occluders[i]; - if (occluder_id.is_valid()) { - rs->free(occluder_id); - } - } - r_cell_data.occluders.resize(tile_set->get_occlusion_layers_count()); - - TileSetSource *source; - if (tile_set->has_source(r_cell_data.cell.source_id)) { - source = *tile_set->get_source(r_cell_data.cell.source_id); - - if (source->has_tile(r_cell_data.cell.get_atlas_coords()) && source->has_alternative_tile(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get the tile data. - const TileData *tile_data; - if (r_cell_data.runtime_tile_data_cache) { - tile_data = r_cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); - } - - // Transform flags. - bool flip_h = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - - // Create, update or clear occluders. - for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) { - Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer_index); - - RID &occluder = r_cell_data.occluders[occlusion_layer_index]; - - if (occluder_polygon.is_valid()) { - // Create or update occluder. - Transform2D xform; - xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); - if (!occluder.is_valid()) { - occluder = rs->canvas_light_occluder_create(); - } - rs->canvas_light_occluder_set_enabled(occluder, node_visible); - rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); - rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); - rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); - rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); - } else { - // Clear occluder. - if (occluder.is_valid()) { - rs->free(occluder); - occluder = RID(); - } - } - } - - return; - } - } - } - - // If we did not return earlier, clear the cell. - _rendering_occluders_clear_cell(r_cell_data); -} - -#ifdef DEBUG_ENABLED -void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for tiles needing one. - RenderingServer *rs = RenderingServer::get_singleton(); - const TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - Vector2i grid_size = atlas_source->get_atlas_grid_size(); - if (!atlas_source->get_runtime_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.get_atlas_coords()); - to_hash.push_back(c.alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw a placeholder tile. - Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); - rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); - rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } - } -} -#endif // DEBUG_ENABLED - -/////////////////////////////// Physics ////////////////////////////////////// - -void TileMapLayer::_physics_update() { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); - if (forced_cleanup) { - // Clean everything. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _physics_clear_cell(kv.value); - } - } else { - if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE]) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _physics_update_cell(kv.value); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _physics_update_cell(cell_data); - } - } - } - - // ----------- - // Mark the physics state as up to date. - _physics_was_cleaned_up = forced_cleanup; -} - -void TileMapLayer::_physics_notify_tilemap_change(TileMapLayer::DirtyFlags p_what) { - Transform2D gl_transform = tile_map_node->get_global_transform(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - bool in_editor = false; -#ifdef TOOLS_ENABLED - in_editor = Engine::get_singleton()->is_editor_hint(); -#endif - - if (p_what == DIRTY_FLAGS_TILE_MAP_XFORM) { - if (tile_map_node->is_inside_tree() && (!tile_map_node->is_collision_animatable() || in_editor)) { - // Move the collisison shapes along with the TileMap. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - const CellData &cell_data = kv.value; - - for (RID body : cell_data.bodies) { - if (body.is_valid()) { - Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body])); - xform = gl_transform * xform; - ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - } - } else if (p_what == DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM) { - // With collisions animatable, move the collisison shapes along with the TileMap only on local xform change (they are synchornized on physics tick instead). - if (tile_map_node->is_inside_tree() && tile_map_node->is_collision_animatable() && !in_editor) { - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - const CellData &cell_data = kv.value; - - for (RID body : cell_data.bodies) { - if (body.is_valid()) { - Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body])); - xform = gl_transform * xform; - ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - } - } - } - } - } else if (p_what == DIRTY_FLAGS_TILE_MAP_IN_TREE) { - // Changes in the tree may cause the space to change (e.g. when reparenting to a SubViewport). - if (tile_map_node->is_inside_tree()) { - RID space = tile_map_node->get_world_2d()->get_space(); - - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - const CellData &cell_data = kv.value; - - for (RID body : cell_data.bodies) { - if (body.is_valid()) { - ps->body_set_space(body, space); - } - } - } - } - } -} - -void TileMapLayer::_physics_clear_cell(CellData &r_cell_data) { - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - // Clear bodies. - for (RID body : r_cell_data.bodies) { - if (body.is_valid()) { - bodies_coords.erase(body); - ps->free(body); - } - } - r_cell_data.bodies.clear(); -} - -void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - Transform2D gl_transform = tile_map_node->get_global_transform(); - RID space = tile_map_node->get_world_2d()->get_space(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - // Recreate bodies and shapes. - TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - const TileData *tile_data; - if (r_cell_data.runtime_tile_data_cache) { - tile_data = r_cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); - } - - // Transform flags. - bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - - // Free unused bodies then resize the bodies array. - for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) { - RID body = r_cell_data.bodies[i]; - if (body.is_valid()) { - bodies_coords.erase(body); - ps->free(body); - } - } - r_cell_data.bodies.resize(tile_set->get_physics_layers_count()); - - for (uint32_t tile_set_physics_layer = 0; tile_set_physics_layer < (uint32_t)tile_set->get_physics_layers_count(); tile_set_physics_layer++) { - Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); - uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); - uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); - - RID body = r_cell_data.bodies[tile_set_physics_layer]; - if (tile_data->get_collision_polygons_count(tile_set_physics_layer) == 0) { - // No body needed, free it if it exists. - if (body.is_valid()) { - bodies_coords.erase(body); - ps->free(body); - } - body = RID(); - } else { - // Create or update the body. - if (!body.is_valid()) { - body = ps->body_create(); - } - bodies_coords[body] = r_cell_data.coords; - ps->body_set_mode(body, tile_map_node->is_collision_animatable() ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); - ps->body_set_space(body, space); - - Transform2D xform; - xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); - xform = gl_transform * xform; - ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); - - ps->body_attach_object_instance_id(body, tile_map_node->get_instance_id()); - ps->body_set_collision_layer(body, physics_layer); - ps->body_set_collision_mask(body, physics_mask); - ps->body_set_pickable(body, false); - ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer)); - ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer)); - - if (!physics_material.is_valid()) { - ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); - ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); - } else { - ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); - ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); - } - - // Clear body's shape if needed. - ps->body_clear_shapes(body); - - // Add the shapes to the body. - int body_shape_index = 0; - for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) { - // Iterate over the polygons. - bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index); - float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index); - int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index); - for (int shape_index = 0; shape_index < shapes_count; shape_index++) { - // Add decomposed convex shapes. - Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index, flip_h, flip_v, transpose); - ps->body_add_shape(body, shape->get_rid()); - ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin); - - body_shape_index++; - } - } - } - - // Set the body again. - r_cell_data.bodies[tile_set_physics_layer] = body; - } - - return; - } - } - } - - // If we did not return earlier, clear the cell. - _physics_clear_cell(r_cell_data); -} - -#ifdef DEBUG_ENABLED -void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { - // Draw the debug collision shapes. - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!tile_map_node->get_tree()) { - return; - } - - bool show_collision = false; - switch (tile_map_node->get_collision_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_collision = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_collisions_hint(); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_collision = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_collision = true; - break; - } - if (!show_collision) { - return; - } - - RenderingServer *rs = RenderingServer::get_singleton(); - PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); - - Color debug_collision_color = tile_map_node->get_tree()->get_debug_collisions_color(); - Vector<Color> color; - color.push_back(debug_collision_color); - - Transform2D quadrant_to_local(0, p_quadrant_pos); - Transform2D global_to_quadrant = (tile_map_node->get_global_transform() * quadrant_to_local).affine_inverse(); - - for (RID body : r_cell_data.bodies) { - if (body.is_valid()) { - Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)); - rs->canvas_item_add_set_transform(p_canvas_item, body_to_quadrant); - for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) { - const RID &shape = ps->body_get_shape(body, shape_index); - const PhysicsServer2D::ShapeType &type = ps->shape_get_type(shape); - if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) { - rs->canvas_item_add_polygon(p_canvas_item, ps->shape_get_data(shape), color); - } else { - WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON."); - } - } - rs->canvas_item_add_set_transform(p_canvas_item, Transform2D()); - } - } -}; -#endif // DEBUG_ENABLED - -/////////////////////////////// Navigation ////////////////////////////////////// - -void TileMapLayer::_navigation_update() { - ERR_FAIL_NULL(NavigationServer2D::get_singleton()); - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - NavigationServer2D *ns = NavigationServer2D::get_singleton(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !navigation_enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); - - // ----------- Layer level processing ----------- - if (forced_cleanup) { - if (navigation_map.is_valid() && !uses_world_navigation_map) { - ns->free(navigation_map); - navigation_map = RID(); - } - } else { - // Update navigation maps. - if (!navigation_map.is_valid()) { - if (layer_index_in_tile_map_node == 0) { - // Use the default World2D navigation map for the first layer when empty. - navigation_map = tile_map_node->get_world_2d()->get_navigation_map(); - uses_world_navigation_map = true; - } else { - RID new_layer_map = ns->map_create(); - // Set the default NavigationPolygon cell_size on the new map as a mismatch causes an error. - ns->map_set_cell_size(new_layer_map, 1.0); - ns->map_set_active(new_layer_map, true); - navigation_map = new_layer_map; - uses_world_navigation_map = false; - } - } - } - - // ----------- Navigation regions processing ----------- - if (forced_cleanup) { - // Clean everything. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _navigation_clear_cell(kv.value); - } - } else { - if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _navigation_update_cell(kv.value); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _navigation_update_cell(cell_data); - } - } - - if (dirty.flags[DIRTY_FLAGS_TILE_MAP_XFORM]) { - Transform2D tilemap_xform = tile_map_node->get_global_transform(); - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - const CellData &cell_data = kv.value; - // Update navigation regions transform. - for (const RID ®ion : cell_data.navigation_regions) { - if (!region.is_valid()) { - continue; - } - Transform2D tile_transform; - tile_transform.set_origin(tile_map_node->map_to_local(kv.key)); - NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); - } - } - } - } - - // ----------- - // Mark the navigation state as up to date. - _navigation_was_cleaned_up = forced_cleanup; -} - -void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) { - NavigationServer2D *ns = NavigationServer2D::get_singleton(); - // Clear navigation shapes. - for (uint32_t i = 0; i < r_cell_data.navigation_regions.size(); i++) { - const RID ®ion = r_cell_data.navigation_regions[i]; - if (region.is_valid()) { - ns->region_set_map(region, RID()); - ns->free(region); - } - } - r_cell_data.navigation_regions.clear(); -} - -void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - NavigationServer2D *ns = NavigationServer2D::get_singleton(); - Transform2D tilemap_xform = tile_map_node->get_global_transform(); - - // Get the navigation polygons and create regions. - TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - const TileData *tile_data; - if (r_cell_data.runtime_tile_data_cache) { - tile_data = r_cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); - } - - // Transform flags. - bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - - // Free unused regions then resize the regions array. - for (uint32_t i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) { - RID ®ion = r_cell_data.navigation_regions[i]; - if (region.is_valid()) { - ns->region_set_map(region, RID()); - ns->free(region); - region = RID(); - } - } - r_cell_data.navigation_regions.resize(tile_set->get_navigation_layers_count()); - - // Create, update or clear regions. - for (uint32_t navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) { - Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index, flip_h, flip_v, transpose); - - RID ®ion = r_cell_data.navigation_regions[navigation_layer_index]; - - if (navigation_polygon.is_valid() && (navigation_polygon->get_polygon_count() > 0 || navigation_polygon->get_outline_count() > 0)) { - // Create or update regions. - Transform2D tile_transform; - tile_transform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); - if (!region.is_valid()) { - region = ns->region_create(); - } - ns->region_set_owner_id(region, tile_map_node->get_instance_id()); - ns->region_set_map(region, navigation_map); - ns->region_set_transform(region, tilemap_xform * tile_transform); - ns->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(navigation_layer_index)); - ns->region_set_navigation_polygon(region, navigation_polygon); - } else { - // Clear region. - if (region.is_valid()) { - ns->region_set_map(region, RID()); - ns->free(region); - region = RID(); - } - } - } - - return; - } - } - } - - // If we did not return earlier, clear the cell. - _navigation_clear_cell(r_cell_data); -} - -#ifdef DEBUG_ENABLED -void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { - // Draw the debug collision shapes. - bool show_navigation = false; - switch (tile_map_node->get_navigation_visibility_mode()) { - case TileMap::VISIBILITY_MODE_DEFAULT: - show_navigation = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_navigation_hint(); - break; - case TileMap::VISIBILITY_MODE_FORCE_HIDE: - show_navigation = false; - break; - case TileMap::VISIBILITY_MODE_FORCE_SHOW: - show_navigation = true; - break; - } - if (!show_navigation) { - return; - } - - // Check if the navigation is used. - if (r_cell_data.navigation_regions.is_empty()) { - return; - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - RenderingServer *rs = RenderingServer::get_singleton(); - const NavigationServer2D *ns2d = NavigationServer2D::get_singleton(); - - bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color(); - bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines(); - - Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color(); - Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color(); - - RandomPCG rand; - - const TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - const TileData *tile_data; - if (r_cell_data.runtime_tile_data_cache) { - tile_data = r_cell_data.runtime_tile_data_cache; - } else { - tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); - } - - Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); - rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); - - for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { - bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); - bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); - bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); - Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index, flip_h, flip_v, transpose); - if (navigation_polygon.is_valid()) { - Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices(); - if (navigation_polygon_vertices.size() < 3) { - continue; - } - - for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { - // An array of vertices for this polygon. - Vector<int> polygon = navigation_polygon->get_polygon(i); - Vector<Vector2> debug_polygon_vertices; - debug_polygon_vertices.resize(polygon.size()); - for (int j = 0; j < polygon.size(); j++) { - ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); - debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]]; - } - - // Generate the polygon color, slightly randomly modified from the settings one. - Color random_variation_color = debug_face_color; - if (enabled_geometry_face_random_color) { - random_variation_color.set_hsv( - debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, - debug_face_color.get_s(), - debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); - } - random_variation_color.a = debug_face_color.a; - - Vector<Color> debug_face_colors; - debug_face_colors.push_back(random_variation_color); - rs->canvas_item_add_polygon(p_canvas_item, debug_polygon_vertices, debug_face_colors); - - if (enabled_edge_lines) { - Vector<Color> debug_edge_colors; - debug_edge_colors.push_back(debug_edge_color); - debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline. - rs->canvas_item_add_polyline(p_canvas_item, debug_polygon_vertices, debug_edge_colors); - } - } - } - } - } - } - } -} -#endif // DEBUG_ENABLED - -/////////////////////////////// Scenes ////////////////////////////////////// - -void TileMapLayer::_scenes_update() { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); - - if (forced_cleanup) { - // Clean everything. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _scenes_clear_cell(kv.value); - } - } else { - if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { - // Update all cells. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - _scenes_update_cell(kv.value); - } - } else { - // Update dirty cells. - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _scenes_update_cell(cell_data); - } - } - } - - // ----------- - // Mark the scenes state as up to date. - _scenes_was_cleaned_up = forced_cleanup; -} - -void TileMapLayer::_scenes_clear_cell(CellData &r_cell_data) { - // Cleanup existing scene. - Node *node = tile_map_node->get_node_or_null(r_cell_data.scene); - if (node) { - node->queue_free(); - } - r_cell_data.scene = ""; -} - -void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - // Clear the scene in any case. - _scenes_clear_cell(r_cell_data); - - // Create the scene. - const TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); - if (packed_scene.is_valid()) { - Node *scene = packed_scene->instantiate(); - Control *scene_as_control = Object::cast_to<Control>(scene); - Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); - if (scene_as_control) { - scene_as_control->set_position(tile_map_node->map_to_local(r_cell_data.coords) + scene_as_control->get_position()); - } else if (scene_as_node2d) { - Transform2D xform; - xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); - scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); - } - tile_map_node->add_child(scene); - r_cell_data.scene = scene->get_name(); - } - } - } - } -} - -#ifdef DEBUG_ENABLED -void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - - if (!Engine::get_singleton()->is_editor_hint()) { - return; - } - - // Draw a placeholder for scenes needing one. - RenderingServer *rs = RenderingServer::get_singleton(); - - const TileMapCell &c = r_cell_data.cell; - - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - return; - } - - TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); - if (scenes_collection_source) { - if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { - // Generate a random color from the hashed values of the tiles. - Array to_hash; - to_hash.push_back(c.source_id); - to_hash.push_back(c.alternative_tile); - uint32_t hash = RandomPCG(to_hash.hash()).rand(); - - Color color; - color = color.from_hsv( - (float)((hash >> 24) & 0xFF) / 256.0, - Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), - Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), - 0.8); - - // Draw a placeholder tile. - Transform2D cell_to_quadrant; - cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); - rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); - rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); - } - } - } -} -#endif // DEBUG_ENABLED - -///////////////////////////////////////////////////////////////////// - -void TileMapLayer::_build_runtime_update_tile_data() { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - // Check if we should cleanup everything. - bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); - if (!forced_cleanup) { - if (tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) { - if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { - for (KeyValue<Vector2i, CellData> &E : tile_map) { - _build_runtime_update_tile_data_for_cell(E.value); - } - } else if (dirty.flags[DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE]) { - for (KeyValue<Vector2i, CellData> &E : tile_map) { - _build_runtime_update_tile_data_for_cell(E.value, true); - } - } else { - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - _build_runtime_update_tile_data_for_cell(cell_data); - } - } - } - } - - // ----------- - // Mark the navigation state as up to date. - _runtime_update_tile_data_was_cleaned_up = forced_cleanup; -} - -void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - - TileMapCell &c = r_cell_data.cell; - TileSetSource *source; - if (tile_set->has_source(c.source_id)) { - source = *tile_set->get_source(c.source_id); - - if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - bool ret = false; - if (tile_map_node->GDVIRTUAL_CALL(_use_tile_data_runtime_update, layer_index_in_tile_map_node, r_cell_data.coords, ret) && ret) { - TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); - - // Create the runtime TileData. - TileData *tile_data_runtime_use = tile_data->duplicate(); - tile_data_runtime_use->set_allow_transform(true); - r_cell_data.runtime_tile_data_cache = tile_data_runtime_use; - - tile_map_node->GDVIRTUAL_CALL(_tile_data_runtime_update, layer_index_in_tile_map_node, r_cell_data.coords, tile_data_runtime_use); - - if (p_auto_add_to_dirty_list) { - dirty.cell_list.add(&r_cell_data.dirty_list_element); - } - } - } - } - } -} - -void TileMapLayer::_clear_runtime_update_tile_data() { - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - - // Clear the runtime tile data. - if (cell_data.runtime_tile_data_cache) { - memdelete(cell_data.runtime_tile_data_cache); - cell_data.runtime_tile_data_cache = nullptr; - } - } -} - -TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (!tile_set.is_valid()) { - return TileSet::TerrainsPattern(); - } - // Returns all tiles compatible with the given constraints. - RBMap<TileSet::TerrainsPattern, int> terrain_pattern_score; - RBSet<TileSet::TerrainsPattern> pattern_set = tile_set->get_terrains_pattern_set(p_terrain_set); - ERR_FAIL_COND_V(pattern_set.is_empty(), TileSet::TerrainsPattern()); - for (TileSet::TerrainsPattern &terrain_pattern : pattern_set) { - int score = 0; - - // Check the center bit constraint. - TerrainConstraint terrain_constraint = TerrainConstraint(tile_map_node, p_position, terrain_pattern.get_terrain()); - const RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint); - if (in_set_constraint_element) { - if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); - } - } else if (p_current_pattern.get_terrain() != terrain_pattern.get_terrain()) { - continue; // Ignore a pattern that cannot keep bits without constraints unmodified. - } - - // Check the surrounding bits - bool invalid_pattern = false; - for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - // Check if the bit is compatible with the constraints. - TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_map_node, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit)); - in_set_constraint_element = p_constraints.find(terrain_bit_constraint); - if (in_set_constraint_element) { - if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { - score += in_set_constraint_element->get().get_priority(); - } - } else if (p_current_pattern.get_terrain_peering_bit(bit) != terrain_pattern.get_terrain_peering_bit(bit)) { - invalid_pattern = true; // Ignore a pattern that cannot keep bits without constraints unmodified. - break; - } - } - } - if (invalid_pattern) { - continue; - } - - terrain_pattern_score[terrain_pattern] = score; - } - - // Compute the minimum score. - TileSet::TerrainsPattern min_score_pattern = p_current_pattern; - int min_score = INT32_MAX; - for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) { - if (E.value < min_score) { - min_score_pattern = E.key; - min_score = E.value; - } - } - - return min_score_pattern; -} - -RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (!tile_set.is_valid()) { - return RBSet<TerrainConstraint>(); - } - - // Compute the constraints needed from the surrounding tiles. - RBSet<TerrainConstraint> output; - output.insert(TerrainConstraint(tile_map_node, p_position, p_terrains_pattern.get_terrain())); - - for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { - TileSet::CellNeighbor side = TileSet::CellNeighbor(i); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) { - TerrainConstraint c = TerrainConstraint(tile_map_node, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side)); - output.insert(c); - } - } - - return output; -} - -RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (!tile_set.is_valid()) { - return RBSet<TerrainConstraint>(); - } - - ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>()); - - // Build a set of dummy constraints to get the constrained points. - RBSet<TerrainConstraint> dummy_constraints; - for (const Vector2i &E : p_painted) { - for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits. - TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - dummy_constraints.insert(TerrainConstraint(tile_map_node, E, bit, -1)); - } - } - } - - // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it. - RBSet<TerrainConstraint> constraints; - for (const TerrainConstraint &E_constraint : dummy_constraints) { - HashMap<int, int> terrain_count; - - // Count the number of occurrences per terrain. - HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = E_constraint.get_overlapping_coords_and_peering_bits(); - for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) { - TileData *neighbor_tile_data = nullptr; - TileMapCell neighbor_cell = get_cell(E_overlapping.key); - if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) { - Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id); - Ref<TileSetAtlasSource> atlas_source = source; - if (atlas_source.is_valid()) { - TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile); - if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { - neighbor_tile_data = tile_data; - } - } - } - - int terrain = neighbor_tile_data ? neighbor_tile_data->get_terrain_peering_bit(TileSet::CellNeighbor(E_overlapping.value)) : -1; - if (!p_ignore_empty_terrains || terrain >= 0) { - if (!terrain_count.has(terrain)) { - terrain_count[terrain] = 0; - } - terrain_count[terrain] += 1; - } - } - - // Get the terrain with the max number of occurrences. - int max = 0; - int max_terrain = -1; - for (const KeyValue<int, int> &E_terrain_count : terrain_count) { - if (E_terrain_count.value > max) { - max = E_terrain_count.value; - max_terrain = E_terrain_count.key; - } - } - - // Set the adequate terrain. - if (max > 0) { - TerrainConstraint c = E_constraint; - c.set_terrain(max_terrain); - constraints.insert(c); - } - } - - // Add the centers as constraints. - for (Vector2i E_coords : p_painted) { - TileData *tile_data = nullptr; - TileMapCell cell = get_cell(E_coords); - if (cell.source_id != TileSet::INVALID_SOURCE) { - Ref<TileSetSource> source = tile_set->get_source(cell.source_id); - Ref<TileSetAtlasSource> atlas_source = source; - if (atlas_source.is_valid()) { - tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - } - } - - int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1; - if (!p_ignore_empty_terrains || terrain >= 0) { - constraints.insert(TerrainConstraint(tile_map_node, E_coords, terrain)); - } - } - - return constraints; -} - -void TileMapLayer::set_tile_map(TileMap *p_tile_map) { - tile_map_node = p_tile_map; -} - -void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) { - if (p_index == layer_index_in_tile_map_node) { - return; - } - layer_index_in_tile_map_node = p_index; - dirty.flags[DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE] = true; - tile_map_node->queue_internal_update(); -} - -Rect2 TileMapLayer::get_rect(bool &r_changed) const { - // Compute the displayed area of the tilemap. - r_changed = false; -#ifdef DEBUG_ENABLED - - if (rect_cache_dirty) { - Rect2 r_total; - bool first = true; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - Rect2 r; - r.position = tile_map_node->map_to_local(E.key); - r.size = Size2(); - if (first) { - r_total = r; - first = false; - } else { - r_total = r_total.merge(r); - } - } - - r_changed = rect_cache != r_total; - - rect_cache = r_total; - rect_cache_dirty = false; - } -#endif - return rect_cache; -} - -HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (!tile_set.is_valid()) { - return HashMap<Vector2i, TileSet::TerrainsPattern>(); - } - - // Copy the constraints set. - RBSet<TerrainConstraint> constraints = p_constraints; - - // Output map. - HashMap<Vector2i, TileSet::TerrainsPattern> output; - - // Add all positions to a set. - for (int i = 0; i < p_to_replace.size(); i++) { - const Vector2i &coords = p_to_replace[i]; - - // Select the best pattern for the given constraints. - TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(coords); - if (cell.source_id != TileSet::INVALID_SOURCE) { - TileSetSource *source = *tile_set->get_source(cell.source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get tile data. - TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { - current_pattern = tile_data->get_terrains_pattern(); - } - } - } - TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern); - - // Update the constraint set with the new ones. - RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern); - for (const TerrainConstraint &E_constraint : new_constraints) { - if (constraints.has(E_constraint)) { - constraints.erase(E_constraint); - } - TerrainConstraint c = E_constraint; - c.set_priority(5); - constraints.insert(c); - } - - output[coords] = pattern; - } - return output; -} - -HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { - HashMap<Vector2i, TileSet::TerrainsPattern> output; - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND_V(!tile_set.is_valid(), output); - ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); - - // Build list and set of tiles that can be modified (painted and their surroundings). - Vector<Vector2i> can_modify_list; - RBSet<Vector2i> can_modify_set; - RBSet<Vector2i> painted_set; - for (int i = p_coords_array.size() - 1; i >= 0; i--) { - const Vector2i &coords = p_coords_array[i]; - can_modify_list.push_back(coords); - can_modify_set.insert(coords); - painted_set.insert(coords); - } - for (Vector2i coords : p_coords_array) { - // Find the adequate neighbor. - for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_map_node->is_existing_neighbor(bit)) { - Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); - if (!can_modify_set.has(neighbor)) { - can_modify_list.push_back(neighbor); - can_modify_set.insert(neighbor); - } - } - } - } - - // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain. - RBSet<Vector2i> cells_with_terrain_center_bit; - for (Vector2i coords : can_modify_set) { - bool connect = false; - if (painted_set.has(coords)) { - connect = true; - } else { - // Get the center bit of the cell. - TileData *tile_data = nullptr; - TileMapCell cell = get_cell(coords); - if (cell.source_id != TileSet::INVALID_SOURCE) { - Ref<TileSetSource> source = tile_set->get_source(cell.source_id); - Ref<TileSetAtlasSource> atlas_source = source; - if (atlas_source.is_valid()) { - tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - } - } - - if (tile_data && tile_data->get_terrain_set() == p_terrain_set && tile_data->get_terrain() == p_terrain) { - connect = true; - } - } - if (connect) { - cells_with_terrain_center_bit.insert(coords); - } - } - - RBSet<TerrainConstraint> constraints; - - // Add new constraints from the path drawn. - for (Vector2i coords : p_coords_array) { - // Constraints on the center bit. - TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); - c.set_priority(10); - constraints.insert(c); - - // Constraints on the connecting bits. - for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - c = TerrainConstraint(tile_map_node, coords, bit, p_terrain); - c.set_priority(10); - if ((int(bit) % 2) == 0) { - // Side peering bits: add the constraint if the center is of the same terrain. - Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); - if (cells_with_terrain_center_bit.has(neighbor)) { - constraints.insert(c); - } - } else { - // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit. - HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); - bool valid = true; - for (KeyValue<Vector2i, TileSet::CellNeighbor> kv : overlapping_terrain_bits) { - if (!cells_with_terrain_center_bit.has(kv.key)) { - valid = false; - break; - } - } - if (valid) { - constraints.insert(c); - } - } - } - } - } - - // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { - constraints.insert(c); - } - - // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); - return output; -} - -HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { - HashMap<Vector2i, TileSet::TerrainsPattern> output; - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND_V(!tile_set.is_valid(), output); - ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); - - // Make sure the path is correct and build the peering bit list while doing it. - Vector<TileSet::CellNeighbor> neighbor_list; - for (int i = 0; i < p_coords_array.size() - 1; i++) { - // Find the adequate neighbor. - TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX; - for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_map_node->is_existing_neighbor(bit)) { - if (tile_map_node->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) { - found_bit = bit; - break; - } - } - } - ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_coords_array[i + 1], p_coords_array[i])); - neighbor_list.push_back(found_bit); - } - - // Build list and set of tiles that can be modified (painted and their surroundings). - Vector<Vector2i> can_modify_list; - RBSet<Vector2i> can_modify_set; - RBSet<Vector2i> painted_set; - for (int i = p_coords_array.size() - 1; i >= 0; i--) { - const Vector2i &coords = p_coords_array[i]; - can_modify_list.push_back(coords); - can_modify_set.insert(coords); - painted_set.insert(coords); - } - for (Vector2i coords : p_coords_array) { - // Find the adequate neighbor. - for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); - if (!can_modify_set.has(neighbor)) { - can_modify_list.push_back(neighbor); - can_modify_set.insert(neighbor); - } - } - } - } - - RBSet<TerrainConstraint> constraints; - - // Add new constraints from the path drawn. - for (Vector2i coords : p_coords_array) { - // Constraints on the center bit. - TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); - c.set_priority(10); - constraints.insert(c); - } - for (int i = 0; i < p_coords_array.size() - 1; i++) { - // Constraints on the peering bits. - TerrainConstraint c = TerrainConstraint(tile_map_node, p_coords_array[i], neighbor_list[i], p_terrain); - c.set_priority(10); - constraints.insert(c); - } - - // Fills in the constraint list from existing tiles. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { - constraints.insert(c); - } - - // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); - return output; -} - -HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) { - HashMap<Vector2i, TileSet::TerrainsPattern> output; - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND_V(!tile_set.is_valid(), output); - ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); - - // Build list and set of tiles that can be modified (painted and their surroundings). - Vector<Vector2i> can_modify_list; - RBSet<Vector2i> can_modify_set; - RBSet<Vector2i> painted_set; - for (int i = p_coords_array.size() - 1; i >= 0; i--) { - const Vector2i &coords = p_coords_array[i]; - can_modify_list.push_back(coords); - can_modify_set.insert(coords); - painted_set.insert(coords); - } - for (Vector2i coords : p_coords_array) { - // Find the adequate neighbor. - for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { - TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); - if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { - Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); - if (!can_modify_set.has(neighbor)) { - can_modify_list.push_back(neighbor); - can_modify_set.insert(neighbor); - } - } - } - } - - // Add constraint by the new ones. - RBSet<TerrainConstraint> constraints; - - // Add new constraints from the path drawn. - for (Vector2i coords : p_coords_array) { - // Constraints on the center bit. - RBSet<TerrainConstraint> added_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, p_terrains_pattern); - for (TerrainConstraint c : added_constraints) { - c.set_priority(10); - constraints.insert(c); - } - } - - // Fills in the constraint list from modified tiles border. - for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { - constraints.insert(c); - } - - // Fill the terrains. - output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); - return output; -} - -TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { - if (!tile_map.has(p_coords)) { - return TileMapCell(); - } else { - TileMapCell c = tile_map.find(p_coords)->value.cell; - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); - c.source_id = proxyed[0]; - c.set_atlas_coords(proxyed[1]); - c.alternative_tile = proxyed[2]; - } - return c; - } -} - -void TileMapLayer::set_tile_data(TileMapLayer::DataFormat p_format, const Vector<int> &p_data) { - ERR_FAIL_COND(p_format > TileMapLayer::FORMAT_3); - - // Set data for a given tile from raw data. - - int c = p_data.size(); - const int *r = p_data.ptr(); - - int offset = (p_format >= TileMapLayer::FORMAT_2) ? 3 : 2; - ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset)); - - clear(); - -#ifdef DISABLE_DEPRECATED - ERR_FAIL_COND_MSG(p_format != TileMapLayer::FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", p_format)); -#endif - - for (int i = 0; i < c; i += offset) { - const uint8_t *ptr = (const uint8_t *)&r[i]; - uint8_t local[12]; - for (int j = 0; j < ((p_format >= TileMapLayer::FORMAT_2) ? 12 : 8); j++) { - local[j] = ptr[j]; - } - -#ifdef BIG_ENDIAN_ENABLED - - SWAP(local[0], local[3]); - SWAP(local[1], local[2]); - SWAP(local[4], local[7]); - SWAP(local[5], local[6]); - //TODO: ask someone to check this... - if (FORMAT >= FORMAT_2) { - SWAP(local[8], local[11]); - SWAP(local[9], local[10]); - } -#endif - // Extracts position in TileMap. - int16_t x = decode_uint16(&local[0]); - int16_t y = decode_uint16(&local[2]); - - if (p_format == TileMapLayer::FORMAT_3) { - uint16_t source_id = decode_uint16(&local[4]); - uint16_t atlas_coords_x = decode_uint16(&local[6]); - uint16_t atlas_coords_y = decode_uint16(&local[8]); - uint16_t alternative_tile = decode_uint16(&local[10]); - set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); - } else { -#ifndef DISABLE_DEPRECATED - // Previous decated format. - - uint32_t v = decode_uint32(&local[4]); - // Extract the transform flags that used to be in the tilemap. - bool flip_h = v & (1UL << 29); - bool flip_v = v & (1UL << 30); - bool transpose = v & (1UL << 31); - v &= (1UL << 29) - 1; - - // Extract autotile/atlas coords. - int16_t coord_x = 0; - int16_t coord_y = 0; - if (p_format == TileMapLayer::FORMAT_2) { - coord_x = decode_uint16(&local[8]); - coord_y = decode_uint16(&local[10]); - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (tile_set.is_valid()) { - Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); - if (a.size() == 3) { - set_cell(Vector2i(x, y), a[0], a[1], a[2]); - } else { - ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); - } - } else { - int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); - set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); - } -#endif - } - } -} - -Vector<int> TileMapLayer::get_tile_data() const { - // Export tile data to raw format. - Vector<int> tile_data; - tile_data.resize(tile_map.size() * 3); - int *w = tile_data.ptrw(); - - // Save in highest format. - - int idx = 0; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - uint8_t *ptr = (uint8_t *)&w[idx]; - encode_uint16((int16_t)(E.key.x), &ptr[0]); - encode_uint16((int16_t)(E.key.y), &ptr[2]); - encode_uint16(E.value.cell.source_id, &ptr[4]); - encode_uint16(E.value.cell.coord_x, &ptr[6]); - encode_uint16(E.value.cell.coord_y, &ptr[8]); - encode_uint16(E.value.cell.alternative_tile, &ptr[10]); - idx += 3; - } - - return tile_data; -} - -void TileMapLayer::notify_tile_map_change(DirtyFlags p_what) { - dirty.flags[p_what] = true; - tile_map_node->queue_internal_update(); - _physics_notify_tilemap_change(p_what); -} - -void TileMapLayer::internal_update() { - // Find TileData that need a runtime modification. - // This may add cells to the dirty list is a runtime modification has been notified. - _build_runtime_update_tile_data(); - - // Update all subsystems. - _rendering_update(); - _physics_update(); - _navigation_update(); - _scenes_update(); -#ifdef DEBUG_ENABLED - _debug_update(); -#endif // DEBUG_ENABLED - - _clear_runtime_update_tile_data(); - - // Clear the "what is dirty" flags. - for (int i = 0; i < DIRTY_FLAGS_MAX; i++) { - dirty.flags[i] = false; - } - - // List the cells to delete definitely. - Vector<Vector2i> to_delete; - for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { - CellData &cell_data = *cell_data_list_element->self(); - // Select the the cell from tile_map if it is invalid. - if (cell_data.cell.source_id == TileSet::INVALID_SOURCE) { - to_delete.push_back(cell_data.coords); - } - } - - // Remove cells that are empty after the cleanup. - for (const Vector2i &coords : to_delete) { - tile_map.erase(coords); - } - - // Clear the dirty cells list. - dirty.cell_list.clear(); -} - -void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { - // Set the current cell tile (using integer position). - Vector2i pk(p_coords); - HashMap<Vector2i, CellData>::Iterator E = tile_map.find(pk); - - int source_id = p_source_id; - Vector2i atlas_coords = p_atlas_coords; - int alternative_tile = p_alternative_tile; - - if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && - (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { - source_id = TileSet::INVALID_SOURCE; - atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; - alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; - } - - if (!E) { - if (source_id == TileSet::INVALID_SOURCE) { - return; // Nothing to do, the tile is already empty. - } - - // Insert a new cell in the tile map. - CellData new_cell_data; - new_cell_data.coords = pk; - E = tile_map.insert(pk, new_cell_data); - } else { - if (E->value.cell.source_id == source_id && E->value.cell.get_atlas_coords() == atlas_coords && E->value.cell.alternative_tile == alternative_tile) { - return; // Nothing changed. - } - } - - TileMapCell &c = E->value.cell; - c.source_id = source_id; - c.set_atlas_coords(atlas_coords); - c.alternative_tile = alternative_tile; - - // Make the given cell dirty. - if (!E->value.dirty_list_element.in_list()) { - dirty.cell_list.add(&(E->value.dirty_list_element)); - } - tile_map_node->queue_internal_update(); - - used_rect_cache_dirty = true; -} - -void TileMapLayer::erase_cell(const Vector2i &p_coords) { - set_cell(p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); -} - -int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSet::INVALID_SOURCE; - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); - return proxyed[0]; - } - - return E->value.cell.source_id; -} - -Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSetSource::INVALID_ATLAS_COORDS; - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); - return proxyed[1]; - } - - return E->value.cell.get_atlas_coords(); -} - -int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { - // Get a cell source id from position. - HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); - - if (!E) { - return TileSetSource::INVALID_TILE_ALTERNATIVE; - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - if (p_use_proxies && tile_set.is_valid()) { - Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); - return proxyed[2]; - } - - return E->value.cell.alternative_tile; -} - -TileData *TileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies) const { - int source_id = get_cell_source_id(p_coords, p_use_proxies); - if (source_id == TileSet::INVALID_SOURCE) { - return nullptr; - } - - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); - if (source.is_valid()) { - return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies)); - } - - return nullptr; -} - -void TileMapLayer::clear() { - // Remove all tiles. - for (KeyValue<Vector2i, CellData> &kv : tile_map) { - erase_cell(kv.key); - } - used_rect_cache_dirty = true; -} - -Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); - - Ref<TileMapPattern> output; - output.instantiate(); - if (p_coords_array.is_empty()) { - return output; - } - - Vector2i min = Vector2i(p_coords_array[0]); - for (int i = 1; i < p_coords_array.size(); i++) { - min = min.min(p_coords_array[i]); - } - - Vector<Vector2i> coords_in_pattern_array; - coords_in_pattern_array.resize(p_coords_array.size()); - Vector2i ensure_positive_offset; - for (int i = 0; i < p_coords_array.size(); i++) { - Vector2i coords = p_coords_array[i]; - Vector2i coords_in_pattern = coords - min; - if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { - if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { - coords_in_pattern.x -= 1; - if (coords_in_pattern.x < 0) { - ensure_positive_offset.x = 1; - } - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { - coords_in_pattern.y -= 1; - if (coords_in_pattern.y < 0) { - ensure_positive_offset.y = 1; - } - } - } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { - if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { - coords_in_pattern.x += 1; - } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { - coords_in_pattern.y += 1; - } - } - } - coords_in_pattern_array.write[i] = coords_in_pattern; - } - - for (int i = 0; i < coords_in_pattern_array.size(); i++) { - Vector2i coords = p_coords_array[i]; - Vector2i coords_in_pattern = coords_in_pattern_array[i]; - output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); - } - - return output; -} - -void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(tile_set.is_null()); - ERR_FAIL_COND(p_pattern.is_null()); - - TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); - for (int i = 0; i < used_cells.size(); i++) { - Vector2i coords = tile_map_node->map_pattern(p_position, used_cells[i], p_pattern); - set_cell(coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i])); - } -} - -void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); - - Vector<Vector2i> cells_vector; - HashSet<Vector2i> painted_set; - for (int i = 0; i < p_cells.size(); i++) { - cells_vector.push_back(p_cells[i]); - painted_set.insert(p_cells[i]); - } - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { - if (painted_set.has(kv.key)) { - // Paint a random tile with the correct terrain for the painted path. - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); - } else { - // Avoids updating the painted path from the output if the new pattern is the same as before. - TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(kv.key); - if (cell.source_id != TileSet::INVALID_SOURCE) { - TileSetSource *source = *tile_set->get_source(cell.source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get tile data. - TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { - in_map_terrain_pattern = tile_data->get_terrains_pattern(); - } - } - } - if (in_map_terrain_pattern != kv.value) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); - } - } - } -} - -void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { - const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); - ERR_FAIL_COND(!tile_set.is_valid()); - ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); - - Vector<Vector2i> vector_path; - HashSet<Vector2i> painted_set; - for (int i = 0; i < p_path.size(); i++) { - vector_path.push_back(p_path[i]); - painted_set.insert(p_path[i]); - } - - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); - for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { - if (painted_set.has(kv.key)) { - // Paint a random tile with the correct terrain for the painted path. - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); - } else { - // Avoids updating the painted path from the output if the new pattern is the same as before. - TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); - TileMapCell cell = get_cell(kv.key); - if (cell.source_id != TileSet::INVALID_SOURCE) { - TileSetSource *source = *tile_set->get_source(cell.source_id); - TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); - if (atlas_source) { - // Get tile data. - TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); - if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { - in_map_terrain_pattern = tile_data->get_terrains_pattern(); - } - } - } - if (in_map_terrain_pattern != kv.value) { - TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); - set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); - } - } - } -} - -TypedArray<Vector2i> TileMapLayer::get_used_cells() const { - // Returns the cells used in the tilemap. - TypedArray<Vector2i> a; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - const TileMapCell &c = E.value.cell; - if (c.source_id == TileSet::INVALID_SOURCE) { - continue; - } - a.push_back(E.key); - } - - return a; -} - -TypedArray<Vector2i> TileMapLayer::get_used_cells_by_id(int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const { - // Returns the cells used in the tilemap. - TypedArray<Vector2i> a; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - const TileMapCell &c = E.value.cell; - if (c.source_id == TileSet::INVALID_SOURCE) { - continue; - } - if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == c.source_id) && - (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == c.get_atlas_coords()) && - (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == c.alternative_tile)) { - a.push_back(E.key); - } - } - - return a; -} - -Rect2i TileMapLayer::get_used_rect() const { - // Return the rect of the currently used area. - if (used_rect_cache_dirty) { - used_rect_cache = Rect2i(); - - bool first = true; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - const TileMapCell &c = E.value.cell; - if (c.source_id == TileSet::INVALID_SOURCE) { - continue; - } - if (first) { - used_rect_cache = Rect2i(E.key.x, E.key.y, 0, 0); - first = false; - } else { - used_rect_cache.expand_to(E.key); - } - } - if (!first) { - // Only if we have at least one cell. - // The cache expands to top-left coordinate, so we add one full tile. - used_rect_cache.size += Vector2i(1, 1); - } - used_rect_cache_dirty = false; - } - - return used_rect_cache; -} - -void TileMapLayer::set_name(String p_name) { - if (name == p_name) { - return; - } - name = p_name; - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); -} - -String TileMapLayer::get_name() const { - return name; -} - -void TileMapLayer::set_enabled(bool p_enabled) { - if (enabled == p_enabled) { - return; - } - enabled = p_enabled; - dirty.flags[DIRTY_FLAGS_LAYER_ENABLED] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); - - tile_map_node->update_configuration_warnings(); -} - -bool TileMapLayer::is_enabled() const { - return enabled; -} - -void TileMapLayer::set_modulate(Color p_modulate) { - if (modulate == p_modulate) { - return; - } - modulate = p_modulate; - dirty.flags[DIRTY_FLAGS_LAYER_MODULATE] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); -} - -Color TileMapLayer::get_modulate() const { - return modulate; -} - -void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) { - if (y_sort_enabled == p_y_sort_enabled) { - return; - } - y_sort_enabled = p_y_sort_enabled; - dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); - - tile_map_node->update_configuration_warnings(); -} - -bool TileMapLayer::is_y_sort_enabled() const { - return y_sort_enabled; -} - -void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) { - if (y_sort_origin == p_y_sort_origin) { - return; - } - y_sort_origin = p_y_sort_origin; - dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); -} - -int TileMapLayer::get_y_sort_origin() const { - return y_sort_origin; -} - -void TileMapLayer::set_z_index(int p_z_index) { - if (z_index == p_z_index) { - return; - } - z_index = p_z_index; - dirty.flags[DIRTY_FLAGS_LAYER_Z_INDEX] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); - - tile_map_node->update_configuration_warnings(); -} - -int TileMapLayer::get_z_index() const { - return z_index; -} - -void TileMapLayer::set_navigation_enabled(bool p_enabled) { - if (navigation_enabled == p_enabled) { - return; - } - navigation_enabled = p_enabled; - dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED] = true; - tile_map_node->queue_internal_update(); - tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); -} - -bool TileMapLayer::is_navigation_enabled() const { - return navigation_enabled; -} - -void TileMapLayer::set_navigation_map(RID p_map) { - ERR_FAIL_COND_MSG(!tile_map_node->is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree."); - navigation_map = p_map; - uses_world_navigation_map = p_map == tile_map_node->get_world_2d()->get_navigation_map(); -} - -RID TileMapLayer::get_navigation_map() const { - if (navigation_map.is_valid()) { - return navigation_map; - } - return RID(); -} - -void TileMapLayer::fix_invalid_tiles() { - Ref<TileSet> tileset = tile_map_node->get_tileset(); - ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet."); - - RBSet<Vector2i> coords; - for (const KeyValue<Vector2i, CellData> &E : tile_map) { - TileSetSource *source = *tileset->get_source(E.value.cell.source_id); - if (!source || !source->has_tile(E.value.cell.get_atlas_coords()) || !source->has_alternative_tile(E.value.cell.get_atlas_coords(), E.value.cell.alternative_tile)) { - coords.insert(E.key); - } - } - for (const Vector2i &E : coords) { - set_cell(E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); - } -} - -bool TileMapLayer::has_body_rid(RID p_physics_body) const { - return bodies_coords.has(p_physics_body); -} - -Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const { - return bodies_coords[p_physics_body]; -} - -TileMapLayer::~TileMapLayer() { - if (!tile_map_node) { - // Temporary layer. - return; - } - - in_destructor = true; - clear(); - internal_update(); -} - -HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coords_and_peering_bits() const { - HashMap<Vector2i, TileSet::CellNeighbor> output; - - ERR_FAIL_COND_V(is_center_bit(), output); - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND_V(!ts.is_valid(), output); - - TileSet::TileShape shape = ts->get_tile_shape(); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else { - // Half offset shapes. - TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 4: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; - break; - case 5: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } else { - switch (bit) { - case 1: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - break; - case 2: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; - break; - case 3: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; - break; - case 4: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; - break; - case 5: - output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; - output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; - break; - default: - ERR_FAIL_V(output); - } - } - } - return output; -} - -TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) { - tile_map = p_tile_map; - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND(!ts.is_valid()); - - bit = 0; - base_cell_coords = p_position; - terrain = p_terrain; -} - -TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) { - // The way we build the constraint make it easy to detect conflicting constraints. - tile_map = p_tile_map; - - Ref<TileSet> ts = tile_map->get_tileset(); - ERR_FAIL_COND(!ts.is_valid()); - - TileSet::TileShape shape = ts->get_tile_shape(); - if (shape == TileSet::TILE_SHAPE_SQUARE) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else { - // Half-offset shapes. - TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); - if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: - bit = 4; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 5; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_LEFT_SIDE: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_CORNER: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 5; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } else { - switch (p_bit) { - case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: - bit = 1; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: - bit = 2; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: - bit = 3; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: - bit = 4; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: - bit = 5; - base_cell_coords = p_position; - break; - case TileSet::CELL_NEIGHBOR_LEFT_CORNER: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: - bit = 2; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: - bit = 1; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_SIDE: - bit = 4; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: - bit = 3; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); - break; - case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: - bit = 5; - base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); - break; - default: - ERR_FAIL(); - break; - } - } - } - terrain = p_terrain; -} +#include "scene/2d/tile_map_layer.h" +#include "scene/gui/control.h" #define TILEMAP_CALL_FOR_LAYER(layer, function, ...) \ if (layer < 0) { \ @@ -3622,7 +772,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { if (p_value.get_type() == Variant::INT) { - format = (TileMapLayer::DataFormat)(p_value.operator int64_t()); // Set format used for loading. + format = (TileMapDataFormat)(p_value.operator int64_t()); // Set format used for loading. return true; } } @@ -3701,7 +851,7 @@ bool TileMap::_set(const StringName &p_name, const Variant &p_value) { bool TileMap::_get(const StringName &p_name, Variant &r_ret) const { Vector<String> components = String(p_name).split("/", true, 2); if (p_name == "format") { - r_ret = TileMapLayer::FORMAT_MAX - 1; // When saving, always save highest format. + r_ret = TileMapDataFormat::FORMAT_MAX - 1; // When saving, always save highest format. return true; } #ifndef DISABLE_DEPRECATED @@ -4765,7 +1915,7 @@ void TileMap::_bind_methods() { ADD_ARRAY("layers", "layer_"); - ADD_PROPERTY_DEFAULT("format", TileMapLayer::FORMAT_1); + ADD_PROPERTY_DEFAULT("format", TileMapDataFormat::FORMAT_1); ADD_SIGNAL(MethodInfo(CoreStringNames::get_singleton()->changed)); diff --git a/scene/2d/tile_map.h b/scene/2d/tile_map.h index 1e4c6d0e66..29af0ad2b1 100644 --- a/scene/2d/tile_map.h +++ b/scene/2d/tile_map.h @@ -32,406 +32,17 @@ #define TILE_MAP_H #include "scene/2d/node_2d.h" -#include "scene/gui/control.h" #include "scene/resources/tile_set.h" -class TileSetAtlasSource; +class Control; +class TileMapLayer; +class TerrainConstraint; -class TerrainConstraint { -private: - const TileMap *tile_map = nullptr; - Vector2i base_cell_coords; - int bit = -1; - int terrain = -1; - - int priority = 1; - -public: - bool operator<(const TerrainConstraint &p_other) const { - if (base_cell_coords == p_other.base_cell_coords) { - return bit < p_other.bit; - } - return base_cell_coords < p_other.base_cell_coords; - } - - String to_string() const { - return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority); - } - - Vector2i get_base_cell_coords() const { - return base_cell_coords; - } - - bool is_center_bit() const { - return bit == 0; - } - - HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const; - - void set_terrain(int p_terrain) { - terrain = p_terrain; - } - - int get_terrain() const { - return terrain; - } - - void set_priority(int p_priority) { - priority = p_priority; - } - - int get_priority() const { - return priority; - } - - TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit - TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits - TerrainConstraint(){}; -}; - -#ifdef DEBUG_ENABLED -class DebugQuadrant; -#endif // DEBUG_ENABLED -class RenderingQuadrant; - -struct CellData { - Vector2i coords; - TileMapCell cell; - - // Debug. - SelfList<CellData> debug_quadrant_list_element; - - // Rendering. - Ref<RenderingQuadrant> rendering_quadrant; - SelfList<CellData> rendering_quadrant_list_element; - LocalVector<RID> occluders; - - // Physics. - LocalVector<RID> bodies; - - // Navigation. - LocalVector<RID> navigation_regions; - - // Scenes. - String scene; - - // Runtime TileData cache. - TileData *runtime_tile_data_cache = nullptr; - - // List elements. - SelfList<CellData> dirty_list_element; - - bool operator<(const CellData &p_other) const { - return coords < p_other.coords; - } - - // For those, copy everything but SelfList elements. - void operator=(const CellData &p_other) { - coords = p_other.coords; - cell = p_other.cell; - occluders = p_other.occluders; - bodies = p_other.bodies; - navigation_regions = p_other.navigation_regions; - scene = p_other.scene; - runtime_tile_data_cache = p_other.runtime_tile_data_cache; - } - - CellData(const CellData &p_other) : - debug_quadrant_list_element(this), - rendering_quadrant_list_element(this), - dirty_list_element(this) { - coords = p_other.coords; - cell = p_other.cell; - occluders = p_other.occluders; - bodies = p_other.bodies; - navigation_regions = p_other.navigation_regions; - scene = p_other.scene; - runtime_tile_data_cache = p_other.runtime_tile_data_cache; - } - - CellData() : - debug_quadrant_list_element(this), - rendering_quadrant_list_element(this), - dirty_list_element(this) { - } -}; - -// For compatibility reasons, we use another comparator for Y-sorted layers. -struct CellDataYSortedComparator { - _FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const { - return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x); - } -}; - -#ifdef DEBUG_ENABLED -class DebugQuadrant : public RefCounted { - GDCLASS(DebugQuadrant, RefCounted); - -public: - Vector2i quadrant_coords; - SelfList<CellData>::List cells; - RID canvas_item; - - SelfList<DebugQuadrant> dirty_quadrant_list_element; - - // For those, copy everything but SelfList elements. - DebugQuadrant(const DebugQuadrant &p_other) : - dirty_quadrant_list_element(this) { - quadrant_coords = p_other.quadrant_coords; - cells = p_other.cells; - canvas_item = p_other.canvas_item; - } - - DebugQuadrant() : - dirty_quadrant_list_element(this) { - } - - ~DebugQuadrant() { - cells.clear(); - } -}; -#endif // DEBUG_ENABLED - -class RenderingQuadrant : public RefCounted { - GDCLASS(RenderingQuadrant, RefCounted); - -public: - struct CoordsWorldComparator { - _ALWAYS_INLINE_ bool operator()(const Vector2 &p_a, const Vector2 &p_b) const { - // We sort the cells by their local coords, as it is needed by rendering. - if (p_a.y == p_b.y) { - return p_a.x > p_b.x; - } else { - return p_a.y < p_b.y; - } - } - }; - - Vector2i quadrant_coords; - SelfList<CellData>::List cells; - List<RID> canvas_items; - Vector2 canvas_items_position; - - SelfList<RenderingQuadrant> dirty_quadrant_list_element; - - // For those, copy everything but SelfList elements. - RenderingQuadrant(const RenderingQuadrant &p_other) : - dirty_quadrant_list_element(this) { - quadrant_coords = p_other.quadrant_coords; - cells = p_other.cells; - canvas_items = p_other.canvas_items; - } - - RenderingQuadrant() : - dirty_quadrant_list_element(this) { - } - - ~RenderingQuadrant() { - cells.clear(); - } -}; - -class TileMapLayer : public RefCounted { - GDCLASS(TileMapLayer, RefCounted); - -public: - enum DataFormat { - FORMAT_1 = 0, - FORMAT_2, - FORMAT_3, - FORMAT_MAX, - }; - - enum DirtyFlags { - DIRTY_FLAGS_LAYER_ENABLED = 0, - DIRTY_FLAGS_LAYER_MODULATE, - DIRTY_FLAGS_LAYER_Y_SORT_ENABLED, - DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN, - DIRTY_FLAGS_LAYER_Z_INDEX, - DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED, - DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE, - DIRTY_FLAGS_TILE_MAP_IN_TREE, - DIRTY_FLAGS_TILE_MAP_IN_CANVAS, - DIRTY_FLAGS_TILE_MAP_VISIBILITY, - DIRTY_FLAGS_TILE_MAP_XFORM, - DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM, - DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER, - DIRTY_FLAGS_TILE_MAP_LIGHT_MASK, - DIRTY_FLAGS_TILE_MAP_MATERIAL, - DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL, - DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER, - DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT, - DIRTY_FLAGS_TILE_MAP_TILE_SET, - DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE, - DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE, - DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE, - DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE, - DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED, - DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE, - DIRTY_FLAGS_MAX, - }; - -private: - // Exposed properties. - String name; - bool enabled = true; - Color modulate = Color(1, 1, 1, 1); - bool y_sort_enabled = false; - int y_sort_origin = 0; - int z_index = 0; - bool navigation_enabled = true; - RID navigation_map; - bool uses_world_navigation_map = false; - - // Internal. - TileMap *tile_map_node = nullptr; - int layer_index_in_tile_map_node = -1; - RID canvas_item; - HashMap<Vector2i, CellData> tile_map; - - // Dirty flag. Allows knowing what was modified since the last update. - struct { - bool flags[DIRTY_FLAGS_MAX] = { false }; - SelfList<CellData>::List cell_list; - } dirty; - bool in_destructor = false; - - // Rect cache. - mutable Rect2 rect_cache; - mutable bool rect_cache_dirty = true; - mutable Rect2i used_rect_cache; - mutable bool used_rect_cache_dirty = true; - - // Runtime tile data. - bool _runtime_update_tile_data_was_cleaned_up = false; - void _build_runtime_update_tile_data(); - void _build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list = false); - void _clear_runtime_update_tile_data(); - - // Per-system methods. -#ifdef DEBUG_ENABLED - HashMap<Vector2i, Ref<DebugQuadrant>> debug_quadrant_map; - Vector2i _coords_to_debug_quadrant_coords(const Vector2i &p_coords) const; - bool _debug_was_cleaned_up = false; - void _debug_update(); - void _debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list); -#endif // DEBUG_ENABLED - - HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map; - bool _rendering_was_cleaned_up = false; - void _rendering_update(); - void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list); - void _rendering_occluders_clear_cell(CellData &r_cell_data); - void _rendering_occluders_update_cell(CellData &r_cell_data); -#ifdef DEBUG_ENABLED - void _rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); -#endif // DEBUG_ENABLED - - HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords. - bool _physics_was_cleaned_up = false; - void _physics_update(); - void _physics_notify_tilemap_change(DirtyFlags p_what); - void _physics_clear_cell(CellData &r_cell_data); - void _physics_update_cell(CellData &r_cell_data); -#ifdef DEBUG_ENABLED - void _physics_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); -#endif // DEBUG_ENABLED - - bool _navigation_was_cleaned_up = false; - void _navigation_update(); - void _navigation_clear_cell(CellData &r_cell_data); - void _navigation_update_cell(CellData &r_cell_data); -#ifdef DEBUG_ENABLED - void _navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); -#endif // DEBUG_ENABLED - - bool _scenes_was_cleaned_up = false; - void _scenes_update(); - void _scenes_clear_cell(CellData &r_cell_data); - void _scenes_update_cell(CellData &r_cell_data); -#ifdef DEBUG_ENABLED - void _scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); -#endif // DEBUG_ENABLED - - // Terrains. - TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern); - RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; - RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const; - -public: - // TileMap node. - void set_tile_map(TileMap *p_tile_map); - void set_layer_index_in_tile_map_node(int p_index); - - // Rect caching. - Rect2 get_rect(bool &r_changed) const; - - // Terrains. - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); // Not exposed. - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. - HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed. - - // Not exposed to users. - TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const; - - // For TileMap node's use. - void set_tile_data(DataFormat p_format, const Vector<int> &p_data); - Vector<int> get_tile_data() const; - void notify_tile_map_change(DirtyFlags p_what); - void internal_update(); - - // --- Exposed in TileMap --- - - // Cells manipulation. - void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0); - void erase_cell(const Vector2i &p_coords); - - int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const; - Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const; - int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const; - TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier. - void clear(); - - // Patterns. - Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array); - void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern); - - // Terrains. - void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); - void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); - - // Cells usage. - TypedArray<Vector2i> get_used_cells() const; - TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const; - Rect2i get_used_rect() const; - - // Layer properties. - void set_name(String p_name); - String get_name() const; - void set_enabled(bool p_enabled); - bool is_enabled() const; - void set_modulate(Color p_modulate); - Color get_modulate() const; - void set_y_sort_enabled(bool p_y_sort_enabled); - bool is_y_sort_enabled() const; - void set_y_sort_origin(int p_y_sort_origin); - int get_y_sort_origin() const; - void set_z_index(int p_z_index); - int get_z_index() const; - void set_navigation_enabled(bool p_enabled); - bool is_navigation_enabled() const; - void set_navigation_map(RID p_map); - RID get_navigation_map() const; - - // Fixing and clearing methods. - void fix_invalid_tiles(); - - // Find coords for body. - bool has_body_rid(RID p_physics_body) const; - Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision. - - ~TileMapLayer(); +enum TileMapDataFormat { + FORMAT_1 = 0, + FORMAT_2, + FORMAT_3, + FORMAT_MAX, }; class TileMap : public Node2D { @@ -448,7 +59,7 @@ private: friend class TileSetPlugin; // A compatibility enum to specify how is the data if formatted. - mutable TileMapLayer::DataFormat format = TileMapLayer::FORMAT_3; + mutable TileMapDataFormat format = TileMapDataFormat::FORMAT_3; static constexpr float FP_ADJUST = 0.00001; diff --git a/scene/2d/tile_map_layer.cpp b/scene/2d/tile_map_layer.cpp new file mode 100644 index 0000000000..4ef47ba174 --- /dev/null +++ b/scene/2d/tile_map_layer.cpp @@ -0,0 +1,2886 @@ +/**************************************************************************/ +/* tile_map_layer.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "tile_map_layer.h" + +#include "core/core_string_names.h" +#include "core/io/marshalls.h" +#include "scene/gui/control.h" +#include "scene/resources/world_2d.h" +#include "servers/navigation_server_2d.h" + +#ifdef DEBUG_ENABLED +#include "servers/navigation_server_3d.h" +#endif // DEBUG_ENABLED + +#ifdef DEBUG_ENABLED +/////////////////////////////// Debug ////////////////////////////////////////// +constexpr int TILE_MAP_DEBUG_QUADRANT_SIZE = 16; + +Vector2i TileMapLayer::_coords_to_debug_quadrant_coords(const Vector2i &p_coords) const { + return Vector2i( + p_coords.x > 0 ? p_coords.x / TILE_MAP_DEBUG_QUADRANT_SIZE : (p_coords.x - (TILE_MAP_DEBUG_QUADRANT_SIZE - 1)) / TILE_MAP_DEBUG_QUADRANT_SIZE, + p_coords.y > 0 ? p_coords.y / TILE_MAP_DEBUG_QUADRANT_SIZE : (p_coords.y - (TILE_MAP_DEBUG_QUADRANT_SIZE - 1)) / TILE_MAP_DEBUG_QUADRANT_SIZE); +} + +void TileMapLayer::_debug_update() { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + RenderingServer *rs = RenderingServer::get_singleton(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); + + if (forced_cleanup) { + for (KeyValue<Vector2i, Ref<DebugQuadrant>> &kv : debug_quadrant_map) { + // Free the quadrant. + Ref<DebugQuadrant> &debug_quadrant = kv.value; + if (debug_quadrant->canvas_item.is_valid()) { + rs->free(debug_quadrant->canvas_item); + } + } + debug_quadrant_map.clear(); + _debug_was_cleaned_up = true; + return; + } + + // Check if anything is dirty, in such a case, redraw debug. + bool anything_changed = false; + for (int i = 0; i < DIRTY_FLAGS_MAX; i++) { + if (dirty.flags[i]) { + anything_changed = true; + break; + } + } + + // List all debug quadrants to update, creating new ones if needed. + SelfList<DebugQuadrant>::List dirty_debug_quadrant_list; + + if (_debug_was_cleaned_up || anything_changed) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + CellData &cell_data = kv.value; + _debug_quadrants_update_cell(cell_data, dirty_debug_quadrant_list); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _debug_quadrants_update_cell(cell_data, dirty_debug_quadrant_list); + } + } + + // Update those quadrants. + for (SelfList<DebugQuadrant> *quadrant_list_element = dirty_debug_quadrant_list.first(); quadrant_list_element;) { + SelfList<DebugQuadrant> *next_quadrant_list_element = quadrant_list_element->next(); // "Hack" to clear the list while iterating. + + DebugQuadrant &debug_quadrant = *quadrant_list_element->self(); + + // Check if the quadrant has a tile. + bool has_a_tile = false; + RID &ci = debug_quadrant.canvas_item; + for (SelfList<CellData> *cell_data_list_element = debug_quadrant.cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { + has_a_tile = true; + break; + } + } + + if (has_a_tile) { + // Update the quadrant. + if (ci.is_valid()) { + rs->canvas_item_clear(ci); + } else { + ci = rs->canvas_item_create(); + rs->canvas_item_set_z_index(ci, RS::CANVAS_ITEM_Z_MAX - 1); + rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item()); + } + + const Vector2 quadrant_pos = tile_map_node->map_to_local(debug_quadrant.quadrant_coords * TILE_MAP_DEBUG_QUADRANT_SIZE); + Transform2D xform(0, quadrant_pos); + rs->canvas_item_set_transform(ci, xform); + + for (SelfList<CellData> *cell_data_list_element = debug_quadrant.cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { + _rendering_draw_cell_debug(ci, quadrant_pos, cell_data); + _physics_draw_cell_debug(ci, quadrant_pos, cell_data); + _navigation_draw_cell_debug(ci, quadrant_pos, cell_data); + _scenes_draw_cell_debug(ci, quadrant_pos, cell_data); + } + } + } else { + // Free the quadrant. + if (ci.is_valid()) { + rs->free(ci); + } + quadrant_list_element->remove_from_list(); + debug_quadrant_map.erase(debug_quadrant.quadrant_coords); + } + + quadrant_list_element = next_quadrant_list_element; + } + + dirty_debug_quadrant_list.clear(); + + _debug_was_cleaned_up = false; +} + +void TileMapLayer::_debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list) { + Vector2i quadrant_coords = _coords_to_debug_quadrant_coords(r_cell_data.coords); + + if (!debug_quadrant_map.has(quadrant_coords)) { + // Create a new quadrant and add it to the quadrant map. + Ref<DebugQuadrant> new_quadrant; + new_quadrant.instantiate(); + new_quadrant->quadrant_coords = quadrant_coords; + debug_quadrant_map[quadrant_coords] = new_quadrant; + } + + // Add the cell to its quadrant, if it is not already in there. + Ref<DebugQuadrant> &debug_quadrant = debug_quadrant_map[quadrant_coords]; + if (!r_cell_data.debug_quadrant_list_element.in_list()) { + debug_quadrant->cells.add(&r_cell_data.debug_quadrant_list_element); + } + + // Mark the quadrant as dirty. + if (!debug_quadrant->dirty_quadrant_list_element.in_list()) { + r_dirty_debug_quadrant_list.add(&debug_quadrant->dirty_quadrant_list_element); + } +} +#endif // DEBUG_ENABLED + +/////////////////////////////// Rendering ////////////////////////////////////// +void TileMapLayer::_rendering_update() { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + RenderingServer *rs = RenderingServer::get_singleton(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); + + // ----------- Layer level processing ----------- + if (forced_cleanup) { + // Cleanup. + if (canvas_item.is_valid()) { + rs->free(canvas_item); + canvas_item = RID(); + } + } else { + // Create/Update the layer's CanvasItem. + if (!canvas_item.is_valid()) { + RID ci = rs->canvas_item_create(); + rs->canvas_item_set_parent(ci, tile_map_node->get_canvas_item()); + canvas_item = ci; + } + RID &ci = canvas_item; + rs->canvas_item_set_draw_index(ci, layer_index_in_tile_map_node - (int64_t)0x80000000); + rs->canvas_item_set_sort_children_by_y(ci, y_sort_enabled); + rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); + rs->canvas_item_set_z_index(ci, z_index); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); + rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); + + // Modulate the layer. + Color layer_modulate = modulate; + int selected_layer = tile_map_node->get_selected_layer(); + if (selected_layer >= 0 && layer_index_in_tile_map_node != selected_layer) { + int z_selected = tile_map_node->get_layer_z_index(selected_layer); + if (z_index < z_selected || (z_index == z_selected && layer_index_in_tile_map_node < selected_layer)) { + layer_modulate = layer_modulate.darkened(0.5); + } else if (z_index > z_selected || (z_index == z_selected && layer_index_in_tile_map_node > selected_layer)) { + layer_modulate = layer_modulate.darkened(0.5); + layer_modulate.a *= 0.3; + } + } + rs->canvas_item_set_modulate(ci, layer_modulate); + } + + // ----------- Quadrants processing ----------- + + // List all rendering quadrants to update, creating new ones if needed. + SelfList<RenderingQuadrant>::List dirty_rendering_quadrant_list; + + // Check if anything changed that might change the quadrant shape. + // If so, recreate everything. + bool quandrant_shape_changed = dirty.flags[DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE] || + (tile_map_node->is_y_sort_enabled() && y_sort_enabled && (dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] || dirty.flags[DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED] || dirty.flags[DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM] || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET])); + + // Free all quadrants. + if (forced_cleanup || quandrant_shape_changed) { + for (const KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { + for (int i = 0; i < kv.value->canvas_items.size(); i++) { + const RID &ci = kv.value->canvas_items[i]; + if (ci.is_valid()) { + rs->free(ci); + } + } + kv.value->cells.clear(); + } + rendering_quadrant_map.clear(); + _rendering_was_cleaned_up = true; + } + + if (!forced_cleanup) { + // List all quadrants to update, recreating them if needed. + if (dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || _rendering_was_cleaned_up) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + CellData &cell_data = kv.value; + _rendering_quadrants_update_cell(cell_data, dirty_rendering_quadrant_list); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _rendering_quadrants_update_cell(cell_data, dirty_rendering_quadrant_list); + } + } + + // Update all dirty quadrants. + for (SelfList<RenderingQuadrant> *quadrant_list_element = dirty_rendering_quadrant_list.first(); quadrant_list_element;) { + SelfList<RenderingQuadrant> *next_quadrant_list_element = quadrant_list_element->next(); // "Hack" to clear the list while iterating. + + const Ref<RenderingQuadrant> &rendering_quadrant = quadrant_list_element->self(); + + // Check if the quadrant has a tile. + bool has_a_tile = false; + for (SelfList<CellData> *cell_data_list_element = rendering_quadrant->cells.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + if (cell_data.cell.source_id != TileSet::INVALID_SOURCE) { + has_a_tile = true; + break; + } + } + + if (has_a_tile) { + // Process the quadrant. + + // First, clear the quadrant's canvas items. + for (RID &ci : rendering_quadrant->canvas_items) { + rs->free(ci); + } + rendering_quadrant->canvas_items.clear(); + + // Sort the quadrant cells. + if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) { + // For compatibility reasons, we use another comparator for Y-sorted layers. + rendering_quadrant->cells.sort_custom<CellDataYSortedComparator>(); + } else { + rendering_quadrant->cells.sort(); + } + + // Those allow to group cell per material or z-index. + Ref<Material> prev_material; + int prev_z_index = 0; + RID prev_ci; + + for (SelfList<CellData> *cell_data_quadrant_list_element = rendering_quadrant->cells.first(); cell_data_quadrant_list_element; cell_data_quadrant_list_element = cell_data_quadrant_list_element->next()) { + CellData &cell_data = *cell_data_quadrant_list_element->self(); + + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(*tile_set->get_source(cell_data.cell.source_id)); + + // Get the tile data. + const TileData *tile_data; + if (cell_data.runtime_tile_data_cache) { + tile_data = cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile); + } + + Ref<Material> mat = tile_data->get_material(); + int tile_z_index = tile_data->get_z_index(); + + // Quandrant pos. + + // --- CanvasItems --- + RID ci; + + // Check if the material or the z_index changed. + if (prev_ci == RID() || prev_material != mat || prev_z_index != tile_z_index) { + // If so, create a new CanvasItem. + ci = rs->canvas_item_create(); + if (mat.is_valid()) { + rs->canvas_item_set_material(ci, mat->get_rid()); + } + rs->canvas_item_set_parent(ci, canvas_item); + rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); + + Transform2D xform(0, rendering_quadrant->canvas_items_position); + rs->canvas_item_set_transform(ci, xform); + + rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); + rs->canvas_item_set_z_as_relative_to_parent(ci, true); + rs->canvas_item_set_z_index(ci, tile_z_index); + + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); + + rendering_quadrant->canvas_items.push_back(ci); + + prev_ci = ci; + prev_material = mat; + prev_z_index = tile_z_index; + + } else { + // Keep the same canvas_item to draw on. + ci = prev_ci; + } + + const Vector2 local_tile_pos = tile_map_node->map_to_local(cell_data.coords); + + // Random animation offset. + real_t random_animation_offset = 0.0; + if (atlas_source->get_tile_animation_mode(cell_data.cell.get_atlas_coords()) != TileSetAtlasSource::TILE_ANIMATION_MODE_DEFAULT) { + Array to_hash; + to_hash.push_back(local_tile_pos); + to_hash.push_back(get_instance_id()); // Use instance id as a random hash + random_animation_offset = RandomPCG(to_hash.hash()).randf(); + } + + // Drawing the tile in the canvas item. + tile_map_node->draw_tile(ci, local_tile_pos - rendering_quadrant->canvas_items_position, tile_set, cell_data.cell.source_id, cell_data.cell.get_atlas_coords(), cell_data.cell.alternative_tile, -1, tile_map_node->get_self_modulate(), tile_data, random_animation_offset); + } + } else { + // Free the quadrant. + for (int i = 0; i < rendering_quadrant->canvas_items.size(); i++) { + const RID &ci = rendering_quadrant->canvas_items[i]; + if (ci.is_valid()) { + rs->free(ci); + } + } + rendering_quadrant->cells.clear(); + rendering_quadrant_map.erase(rendering_quadrant->quadrant_coords); + } + + quadrant_list_element = next_quadrant_list_element; + } + + dirty_rendering_quadrant_list.clear(); + + // Reset the drawing indices. + { + int index = -(int64_t)0x80000000; // Always must be drawn below children. + + // Sort the quadrants coords per local coordinates. + RBMap<Vector2, Ref<RenderingQuadrant>, RenderingQuadrant::CoordsWorldComparator> local_to_map; + for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { + Ref<RenderingQuadrant> &rendering_quadrant = kv.value; + local_to_map[tile_map_node->map_to_local(rendering_quadrant->quadrant_coords)] = rendering_quadrant; + } + + // Sort the quadrants. + for (const KeyValue<Vector2, Ref<RenderingQuadrant>> &E : local_to_map) { + for (const RID &ci : E.value->canvas_items) { + RS::get_singleton()->canvas_item_set_draw_index(ci, index++); + } + } + } + + // Updates on TileMap changes. + if (dirty.flags[DIRTY_FLAGS_TILE_MAP_LIGHT_MASK] || + dirty.flags[DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL] || + dirty.flags[DIRTY_FLAGS_TILE_MAP_MATERIAL] || + dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER] || + dirty.flags[DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT]) { + for (KeyValue<Vector2i, Ref<RenderingQuadrant>> &kv : rendering_quadrant_map) { + Ref<RenderingQuadrant> &rendering_quadrant = kv.value; + for (const RID &ci : rendering_quadrant->canvas_items) { + rs->canvas_item_set_light_mask(ci, tile_map_node->get_light_mask()); + rs->canvas_item_set_use_parent_material(ci, tile_map_node->get_use_parent_material() || tile_map_node->get_material().is_valid()); + rs->canvas_item_set_default_texture_filter(ci, RS::CanvasItemTextureFilter(tile_map_node->get_texture_filter_in_tree())); + rs->canvas_item_set_default_texture_repeat(ci, RS::CanvasItemTextureRepeat(tile_map_node->get_texture_repeat_in_tree())); + } + } + } + } + + // ----------- Occluders processing ----------- + if (forced_cleanup) { + // Clean everything. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _rendering_occluders_clear_cell(kv.value); + } + } else { + if (_rendering_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _rendering_occluders_update_cell(kv.value); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _rendering_occluders_update_cell(cell_data); + } + } + + // Updates on TileMap changes. + if (dirty.flags[DIRTY_FLAGS_TILE_MAP_IN_CANVAS] || dirty.flags[DIRTY_FLAGS_TILE_MAP_VISIBILITY]) { + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + CellData &cell_data = kv.value; + for (const RID &occluder : cell_data.occluders) { + if (occluder.is_null()) { + continue; + } + Transform2D xform(0, tile_map_node->map_to_local(kv.key)); + rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); + rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + } + } + } + } + + // ----------- + // Mark the rendering state as up to date. + _rendering_was_cleaned_up = forced_cleanup; +} + +void TileMapLayer::_rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + // Check if the cell is valid and retrieve its y_sort_origin. + bool is_valid = false; + int tile_y_sort_origin = 0; + TileSetSource *source; + if (tile_set->has_source(r_cell_data.cell.source_id)) { + source = *tile_set->get_source(r_cell_data.cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source && atlas_source->has_tile(r_cell_data.cell.get_atlas_coords()) && atlas_source->has_alternative_tile(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)) { + is_valid = true; + const TileData *tile_data; + if (r_cell_data.runtime_tile_data_cache) { + tile_data = r_cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); + } + tile_y_sort_origin = tile_data->get_y_sort_origin(); + } + } + + if (is_valid) { + // Get the quadrant coords. + Vector2 canvas_items_position; + Vector2i quadrant_coords; + if (tile_map_node->is_y_sort_enabled() && is_y_sort_enabled()) { + canvas_items_position = Vector2(0, tile_map_node->map_to_local(r_cell_data.coords).y + tile_y_sort_origin + y_sort_origin); + quadrant_coords = canvas_items_position * 100; + } else { + int quad_size = tile_map_node->get_rendering_quadrant_size(); + const Vector2i &coords = r_cell_data.coords; + + // Rounding down, instead of simply rounding towards zero (truncating). + quadrant_coords = Vector2i( + coords.x > 0 ? coords.x / quad_size : (coords.x - (quad_size - 1)) / quad_size, + coords.y > 0 ? coords.y / quad_size : (coords.y - (quad_size - 1)) / quad_size); + canvas_items_position = tile_map_node->map_to_local(quad_size * quadrant_coords); + } + + Ref<RenderingQuadrant> rendering_quadrant; + if (rendering_quadrant_map.has(quadrant_coords)) { + // Reuse existing rendering quadrant. + rendering_quadrant = rendering_quadrant_map[quadrant_coords]; + } else { + // Create a new rendering quadrant. + rendering_quadrant.instantiate(); + rendering_quadrant->quadrant_coords = quadrant_coords; + rendering_quadrant->canvas_items_position = canvas_items_position; + rendering_quadrant_map[quadrant_coords] = rendering_quadrant; + } + + // Mark the old quadrant as dirty (if it exists). + if (r_cell_data.rendering_quadrant.is_valid()) { + if (!r_cell_data.rendering_quadrant->dirty_quadrant_list_element.in_list()) { + r_dirty_rendering_quadrant_list.add(&r_cell_data.rendering_quadrant->dirty_quadrant_list_element); + } + } + + // Remove the cell from that quadrant. + if (r_cell_data.rendering_quadrant_list_element.in_list()) { + r_cell_data.rendering_quadrant_list_element.remove_from_list(); + } + + // Add the cell to its new quadrant. + r_cell_data.rendering_quadrant = rendering_quadrant; + r_cell_data.rendering_quadrant->cells.add(&r_cell_data.rendering_quadrant_list_element); + + // Add the new quadrant to the dirty quadrant list. + if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) { + r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element); + } + } else { + Ref<RenderingQuadrant> rendering_quadrant = r_cell_data.rendering_quadrant; + + // Remove the cell from its quadrant. + r_cell_data.rendering_quadrant = Ref<RenderingQuadrant>(); + if (r_cell_data.rendering_quadrant_list_element.in_list()) { + rendering_quadrant->cells.remove(&r_cell_data.rendering_quadrant_list_element); + } + + if (rendering_quadrant.is_valid()) { + // Add the quadrant to the dirty quadrant list. + if (!rendering_quadrant->dirty_quadrant_list_element.in_list()) { + r_dirty_rendering_quadrant_list.add(&rendering_quadrant->dirty_quadrant_list_element); + } + } + } +} + +void TileMapLayer::_rendering_occluders_clear_cell(CellData &r_cell_data) { + RenderingServer *rs = RenderingServer::get_singleton(); + + // Free the occluders. + for (const RID &rid : r_cell_data.occluders) { + rs->free(rid); + } + r_cell_data.occluders.clear(); +} + +void TileMapLayer::_rendering_occluders_update_cell(CellData &r_cell_data) { + bool node_visible = tile_map_node->is_visible_in_tree(); + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + RenderingServer *rs = RenderingServer::get_singleton(); + + // Free unused occluders then resize the occluders array. + for (uint32_t i = tile_set->get_occlusion_layers_count(); i < r_cell_data.occluders.size(); i++) { + RID occluder_id = r_cell_data.occluders[i]; + if (occluder_id.is_valid()) { + rs->free(occluder_id); + } + } + r_cell_data.occluders.resize(tile_set->get_occlusion_layers_count()); + + TileSetSource *source; + if (tile_set->has_source(r_cell_data.cell.source_id)) { + source = *tile_set->get_source(r_cell_data.cell.source_id); + + if (source->has_tile(r_cell_data.cell.get_atlas_coords()) && source->has_alternative_tile(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get the tile data. + const TileData *tile_data; + if (r_cell_data.runtime_tile_data_cache) { + tile_data = r_cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(r_cell_data.cell.get_atlas_coords(), r_cell_data.cell.alternative_tile); + } + + // Transform flags. + bool flip_h = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (r_cell_data.cell.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + + // Create, update or clear occluders. + for (uint32_t occlusion_layer_index = 0; occlusion_layer_index < r_cell_data.occluders.size(); occlusion_layer_index++) { + Ref<OccluderPolygon2D> occluder_polygon = tile_data->get_occluder(occlusion_layer_index); + + RID &occluder = r_cell_data.occluders[occlusion_layer_index]; + + if (occluder_polygon.is_valid()) { + // Create or update occluder. + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); + if (!occluder.is_valid()) { + occluder = rs->canvas_light_occluder_create(); + } + rs->canvas_light_occluder_set_enabled(occluder, node_visible); + rs->canvas_light_occluder_set_transform(occluder, tile_map_node->get_global_transform() * xform); + rs->canvas_light_occluder_set_polygon(occluder, tile_data->get_occluder(occlusion_layer_index, flip_h, flip_v, transpose)->get_rid()); + rs->canvas_light_occluder_attach_to_canvas(occluder, tile_map_node->get_canvas()); + rs->canvas_light_occluder_set_light_mask(occluder, tile_set->get_occlusion_layer_light_mask(occlusion_layer_index)); + } else { + // Clear occluder. + if (occluder.is_valid()) { + rs->free(occluder); + occluder = RID(); + } + } + } + + return; + } + } + } + + // If we did not return earlier, clear the cell. + _rendering_occluders_clear_cell(r_cell_data); +} + +#ifdef DEBUG_ENABLED +void TileMapLayer::_rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { + return; + } + + // Draw a placeholder for tiles needing one. + RenderingServer *rs = RenderingServer::get_singleton(); + const TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + Vector2i grid_size = atlas_source->get_atlas_grid_size(); + if (!atlas_source->get_runtime_texture().is_valid() || c.get_atlas_coords().x >= grid_size.x || c.get_atlas_coords().y >= grid_size.y) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.get_atlas_coords()); + to_hash.push_back(c.alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw a placeholder tile. + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); + rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); + rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } + } +} +#endif // DEBUG_ENABLED + +/////////////////////////////// Physics ////////////////////////////////////// + +void TileMapLayer::_physics_update() { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); + if (forced_cleanup) { + // Clean everything. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _physics_clear_cell(kv.value); + } + } else { + if (_physics_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET] || dirty.flags[DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE]) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _physics_update_cell(kv.value); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _physics_update_cell(cell_data); + } + } + } + + // ----------- + // Mark the physics state as up to date. + _physics_was_cleaned_up = forced_cleanup; +} + +void TileMapLayer::_physics_notify_tilemap_change(TileMapLayer::DirtyFlags p_what) { + Transform2D gl_transform = tile_map_node->get_global_transform(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + bool in_editor = false; +#ifdef TOOLS_ENABLED + in_editor = Engine::get_singleton()->is_editor_hint(); +#endif + + if (p_what == DIRTY_FLAGS_TILE_MAP_XFORM) { + if (tile_map_node->is_inside_tree() && (!tile_map_node->is_collision_animatable() || in_editor)) { + // Move the collisison shapes along with the TileMap. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + const CellData &cell_data = kv.value; + + for (RID body : cell_data.bodies) { + if (body.is_valid()) { + Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body])); + xform = gl_transform * xform; + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } else if (p_what == DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM) { + // With collisions animatable, move the collisison shapes along with the TileMap only on local xform change (they are synchornized on physics tick instead). + if (tile_map_node->is_inside_tree() && tile_map_node->is_collision_animatable() && !in_editor) { + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + const CellData &cell_data = kv.value; + + for (RID body : cell_data.bodies) { + if (body.is_valid()) { + Transform2D xform(0, tile_map_node->map_to_local(bodies_coords[body])); + xform = gl_transform * xform; + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + } + } + } + } + } else if (p_what == DIRTY_FLAGS_TILE_MAP_IN_TREE) { + // Changes in the tree may cause the space to change (e.g. when reparenting to a SubViewport). + if (tile_map_node->is_inside_tree()) { + RID space = tile_map_node->get_world_2d()->get_space(); + + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + const CellData &cell_data = kv.value; + + for (RID body : cell_data.bodies) { + if (body.is_valid()) { + ps->body_set_space(body, space); + } + } + } + } + } +} + +void TileMapLayer::_physics_clear_cell(CellData &r_cell_data) { + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + // Clear bodies. + for (RID body : r_cell_data.bodies) { + if (body.is_valid()) { + bodies_coords.erase(body); + ps->free(body); + } + } + r_cell_data.bodies.clear(); +} + +void TileMapLayer::_physics_update_cell(CellData &r_cell_data) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + Transform2D gl_transform = tile_map_node->get_global_transform(); + RID space = tile_map_node->get_world_2d()->get_space(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + // Recreate bodies and shapes. + TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + const TileData *tile_data; + if (r_cell_data.runtime_tile_data_cache) { + tile_data = r_cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); + } + + // Transform flags. + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + + // Free unused bodies then resize the bodies array. + for (uint32_t i = tile_set->get_physics_layers_count(); i < r_cell_data.bodies.size(); i++) { + RID body = r_cell_data.bodies[i]; + if (body.is_valid()) { + bodies_coords.erase(body); + ps->free(body); + } + } + r_cell_data.bodies.resize(tile_set->get_physics_layers_count()); + + for (uint32_t tile_set_physics_layer = 0; tile_set_physics_layer < (uint32_t)tile_set->get_physics_layers_count(); tile_set_physics_layer++) { + Ref<PhysicsMaterial> physics_material = tile_set->get_physics_layer_physics_material(tile_set_physics_layer); + uint32_t physics_layer = tile_set->get_physics_layer_collision_layer(tile_set_physics_layer); + uint32_t physics_mask = tile_set->get_physics_layer_collision_mask(tile_set_physics_layer); + + RID body = r_cell_data.bodies[tile_set_physics_layer]; + if (tile_data->get_collision_polygons_count(tile_set_physics_layer) == 0) { + // No body needed, free it if it exists. + if (body.is_valid()) { + bodies_coords.erase(body); + ps->free(body); + } + body = RID(); + } else { + // Create or update the body. + if (!body.is_valid()) { + body = ps->body_create(); + } + bodies_coords[body] = r_cell_data.coords; + ps->body_set_mode(body, tile_map_node->is_collision_animatable() ? PhysicsServer2D::BODY_MODE_KINEMATIC : PhysicsServer2D::BODY_MODE_STATIC); + ps->body_set_space(body, space); + + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); + xform = gl_transform * xform; + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM, xform); + + ps->body_attach_object_instance_id(body, tile_map_node->get_instance_id()); + ps->body_set_collision_layer(body, physics_layer); + ps->body_set_collision_mask(body, physics_mask); + ps->body_set_pickable(body, false); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_LINEAR_VELOCITY, tile_data->get_constant_linear_velocity(tile_set_physics_layer)); + ps->body_set_state(body, PhysicsServer2D::BODY_STATE_ANGULAR_VELOCITY, tile_data->get_constant_angular_velocity(tile_set_physics_layer)); + + if (!physics_material.is_valid()) { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, 0); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, 1); + } else { + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_BOUNCE, physics_material->computed_bounce()); + ps->body_set_param(body, PhysicsServer2D::BODY_PARAM_FRICTION, physics_material->computed_friction()); + } + + // Clear body's shape if needed. + ps->body_clear_shapes(body); + + // Add the shapes to the body. + int body_shape_index = 0; + for (int polygon_index = 0; polygon_index < tile_data->get_collision_polygons_count(tile_set_physics_layer); polygon_index++) { + // Iterate over the polygons. + bool one_way_collision = tile_data->is_collision_polygon_one_way(tile_set_physics_layer, polygon_index); + float one_way_collision_margin = tile_data->get_collision_polygon_one_way_margin(tile_set_physics_layer, polygon_index); + int shapes_count = tile_data->get_collision_polygon_shapes_count(tile_set_physics_layer, polygon_index); + for (int shape_index = 0; shape_index < shapes_count; shape_index++) { + // Add decomposed convex shapes. + Ref<ConvexPolygonShape2D> shape = tile_data->get_collision_polygon_shape(tile_set_physics_layer, polygon_index, shape_index, flip_h, flip_v, transpose); + ps->body_add_shape(body, shape->get_rid()); + ps->body_set_shape_as_one_way_collision(body, body_shape_index, one_way_collision, one_way_collision_margin); + + body_shape_index++; + } + } + } + + // Set the body again. + r_cell_data.bodies[tile_set_physics_layer] = body; + } + + return; + } + } + } + + // If we did not return earlier, clear the cell. + _physics_clear_cell(r_cell_data); +} + +#ifdef DEBUG_ENABLED +void TileMapLayer::_physics_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { + // Draw the debug collision shapes. + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!tile_map_node->get_tree()) { + return; + } + + bool show_collision = false; + switch (tile_map_node->get_collision_visibility_mode()) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_collision = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_collisions_hint(); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_collision = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_collision = true; + break; + } + if (!show_collision) { + return; + } + + RenderingServer *rs = RenderingServer::get_singleton(); + PhysicsServer2D *ps = PhysicsServer2D::get_singleton(); + + Color debug_collision_color = tile_map_node->get_tree()->get_debug_collisions_color(); + Vector<Color> color; + color.push_back(debug_collision_color); + + Transform2D quadrant_to_local(0, p_quadrant_pos); + Transform2D global_to_quadrant = (tile_map_node->get_global_transform() * quadrant_to_local).affine_inverse(); + + for (RID body : r_cell_data.bodies) { + if (body.is_valid()) { + Transform2D body_to_quadrant = global_to_quadrant * Transform2D(ps->body_get_state(body, PhysicsServer2D::BODY_STATE_TRANSFORM)); + rs->canvas_item_add_set_transform(p_canvas_item, body_to_quadrant); + for (int shape_index = 0; shape_index < ps->body_get_shape_count(body); shape_index++) { + const RID &shape = ps->body_get_shape(body, shape_index); + const PhysicsServer2D::ShapeType &type = ps->shape_get_type(shape); + if (type == PhysicsServer2D::SHAPE_CONVEX_POLYGON) { + rs->canvas_item_add_polygon(p_canvas_item, ps->shape_get_data(shape), color); + } else { + WARN_PRINT("Wrong shape type for a tile, should be SHAPE_CONVEX_POLYGON."); + } + } + rs->canvas_item_add_set_transform(p_canvas_item, Transform2D()); + } + } +}; +#endif // DEBUG_ENABLED + +/////////////////////////////// Navigation ////////////////////////////////////// + +void TileMapLayer::_navigation_update() { + ERR_FAIL_NULL(NavigationServer2D::get_singleton()); + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + NavigationServer2D *ns = NavigationServer2D::get_singleton(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !navigation_enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); + + // ----------- Layer level processing ----------- + if (forced_cleanup) { + if (navigation_map.is_valid() && !uses_world_navigation_map) { + ns->free(navigation_map); + navigation_map = RID(); + } + } else { + // Update navigation maps. + if (!navigation_map.is_valid()) { + if (layer_index_in_tile_map_node == 0) { + // Use the default World2D navigation map for the first layer when empty. + navigation_map = tile_map_node->get_world_2d()->get_navigation_map(); + uses_world_navigation_map = true; + } else { + RID new_layer_map = ns->map_create(); + // Set the default NavigationPolygon cell_size on the new map as a mismatch causes an error. + ns->map_set_cell_size(new_layer_map, 1.0); + ns->map_set_active(new_layer_map, true); + navigation_map = new_layer_map; + uses_world_navigation_map = false; + } + } + } + + // ----------- Navigation regions processing ----------- + if (forced_cleanup) { + // Clean everything. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _navigation_clear_cell(kv.value); + } + } else { + if (_navigation_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _navigation_update_cell(kv.value); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _navigation_update_cell(cell_data); + } + } + + if (dirty.flags[DIRTY_FLAGS_TILE_MAP_XFORM]) { + Transform2D tilemap_xform = tile_map_node->get_global_transform(); + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + const CellData &cell_data = kv.value; + // Update navigation regions transform. + for (const RID ®ion : cell_data.navigation_regions) { + if (!region.is_valid()) { + continue; + } + Transform2D tile_transform; + tile_transform.set_origin(tile_map_node->map_to_local(kv.key)); + NavigationServer2D::get_singleton()->region_set_transform(region, tilemap_xform * tile_transform); + } + } + } + } + + // ----------- + // Mark the navigation state as up to date. + _navigation_was_cleaned_up = forced_cleanup; +} + +void TileMapLayer::_navigation_clear_cell(CellData &r_cell_data) { + NavigationServer2D *ns = NavigationServer2D::get_singleton(); + // Clear navigation shapes. + for (uint32_t i = 0; i < r_cell_data.navigation_regions.size(); i++) { + const RID ®ion = r_cell_data.navigation_regions[i]; + if (region.is_valid()) { + ns->region_set_map(region, RID()); + ns->free(region); + } + } + r_cell_data.navigation_regions.clear(); +} + +void TileMapLayer::_navigation_update_cell(CellData &r_cell_data) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + NavigationServer2D *ns = NavigationServer2D::get_singleton(); + Transform2D tilemap_xform = tile_map_node->get_global_transform(); + + // Get the navigation polygons and create regions. + TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + const TileData *tile_data; + if (r_cell_data.runtime_tile_data_cache) { + tile_data = r_cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); + } + + // Transform flags. + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + + // Free unused regions then resize the regions array. + for (uint32_t i = tile_set->get_navigation_layers_count(); i < r_cell_data.navigation_regions.size(); i++) { + RID ®ion = r_cell_data.navigation_regions[i]; + if (region.is_valid()) { + ns->region_set_map(region, RID()); + ns->free(region); + region = RID(); + } + } + r_cell_data.navigation_regions.resize(tile_set->get_navigation_layers_count()); + + // Create, update or clear regions. + for (uint32_t navigation_layer_index = 0; navigation_layer_index < r_cell_data.navigation_regions.size(); navigation_layer_index++) { + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(navigation_layer_index, flip_h, flip_v, transpose); + + RID ®ion = r_cell_data.navigation_regions[navigation_layer_index]; + + if (navigation_polygon.is_valid() && (navigation_polygon->get_polygon_count() > 0 || navigation_polygon->get_outline_count() > 0)) { + // Create or update regions. + Transform2D tile_transform; + tile_transform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); + if (!region.is_valid()) { + region = ns->region_create(); + } + ns->region_set_owner_id(region, tile_map_node->get_instance_id()); + ns->region_set_map(region, navigation_map); + ns->region_set_transform(region, tilemap_xform * tile_transform); + ns->region_set_navigation_layers(region, tile_set->get_navigation_layer_layers(navigation_layer_index)); + ns->region_set_navigation_polygon(region, navigation_polygon); + } else { + // Clear region. + if (region.is_valid()) { + ns->region_set_map(region, RID()); + ns->free(region); + region = RID(); + } + } + } + + return; + } + } + } + + // If we did not return earlier, clear the cell. + _navigation_clear_cell(r_cell_data); +} + +#ifdef DEBUG_ENABLED +void TileMapLayer::_navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { + // Draw the debug collision shapes. + bool show_navigation = false; + switch (tile_map_node->get_navigation_visibility_mode()) { + case TileMap::VISIBILITY_MODE_DEFAULT: + show_navigation = !Engine::get_singleton()->is_editor_hint() && tile_map_node->get_tree()->is_debugging_navigation_hint(); + break; + case TileMap::VISIBILITY_MODE_FORCE_HIDE: + show_navigation = false; + break; + case TileMap::VISIBILITY_MODE_FORCE_SHOW: + show_navigation = true; + break; + } + if (!show_navigation) { + return; + } + + // Check if the navigation is used. + if (r_cell_data.navigation_regions.is_empty()) { + return; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + RenderingServer *rs = RenderingServer::get_singleton(); + const NavigationServer2D *ns2d = NavigationServer2D::get_singleton(); + + bool enabled_geometry_face_random_color = ns2d->get_debug_navigation_enable_geometry_face_random_color(); + bool enabled_edge_lines = ns2d->get_debug_navigation_enable_edge_lines(); + + Color debug_face_color = ns2d->get_debug_navigation_geometry_face_color(); + Color debug_edge_color = ns2d->get_debug_navigation_geometry_edge_color(); + + RandomPCG rand; + + const TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + const TileData *tile_data; + if (r_cell_data.runtime_tile_data_cache) { + tile_data = r_cell_data.runtime_tile_data_cache; + } else { + tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); + } + + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); + rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); + + for (int layer_index = 0; layer_index < tile_set->get_navigation_layers_count(); layer_index++) { + bool flip_h = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_H); + bool flip_v = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_FLIP_V); + bool transpose = (c.alternative_tile & TileSetAtlasSource::TRANSFORM_TRANSPOSE); + Ref<NavigationPolygon> navigation_polygon = tile_data->get_navigation_polygon(layer_index, flip_h, flip_v, transpose); + if (navigation_polygon.is_valid()) { + Vector<Vector2> navigation_polygon_vertices = navigation_polygon->get_vertices(); + if (navigation_polygon_vertices.size() < 3) { + continue; + } + + for (int i = 0; i < navigation_polygon->get_polygon_count(); i++) { + // An array of vertices for this polygon. + Vector<int> polygon = navigation_polygon->get_polygon(i); + Vector<Vector2> debug_polygon_vertices; + debug_polygon_vertices.resize(polygon.size()); + for (int j = 0; j < polygon.size(); j++) { + ERR_FAIL_INDEX(polygon[j], navigation_polygon_vertices.size()); + debug_polygon_vertices.write[j] = navigation_polygon_vertices[polygon[j]]; + } + + // Generate the polygon color, slightly randomly modified from the settings one. + Color random_variation_color = debug_face_color; + if (enabled_geometry_face_random_color) { + random_variation_color.set_hsv( + debug_face_color.get_h() + rand.random(-1.0, 1.0) * 0.1, + debug_face_color.get_s(), + debug_face_color.get_v() + rand.random(-1.0, 1.0) * 0.2); + } + random_variation_color.a = debug_face_color.a; + + Vector<Color> debug_face_colors; + debug_face_colors.push_back(random_variation_color); + rs->canvas_item_add_polygon(p_canvas_item, debug_polygon_vertices, debug_face_colors); + + if (enabled_edge_lines) { + Vector<Color> debug_edge_colors; + debug_edge_colors.push_back(debug_edge_color); + debug_polygon_vertices.push_back(debug_polygon_vertices[0]); // Add first again for closing polyline. + rs->canvas_item_add_polyline(p_canvas_item, debug_polygon_vertices, debug_edge_colors); + } + } + } + } + } + } + } +} +#endif // DEBUG_ENABLED + +/////////////////////////////// Scenes ////////////////////////////////////// + +void TileMapLayer::_scenes_update() { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid(); + + if (forced_cleanup) { + // Clean everything. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _scenes_clear_cell(kv.value); + } + } else { + if (_scenes_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { + // Update all cells. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + _scenes_update_cell(kv.value); + } + } else { + // Update dirty cells. + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _scenes_update_cell(cell_data); + } + } + } + + // ----------- + // Mark the scenes state as up to date. + _scenes_was_cleaned_up = forced_cleanup; +} + +void TileMapLayer::_scenes_clear_cell(CellData &r_cell_data) { + // Cleanup existing scene. + Node *node = tile_map_node->get_node_or_null(r_cell_data.scene); + if (node) { + node->queue_free(); + } + r_cell_data.scene = ""; +} + +void TileMapLayer::_scenes_update_cell(CellData &r_cell_data) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + // Clear the scene in any case. + _scenes_clear_cell(r_cell_data); + + // Create the scene. + const TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + Ref<PackedScene> packed_scene = scenes_collection_source->get_scene_tile_scene(c.alternative_tile); + if (packed_scene.is_valid()) { + Node *scene = packed_scene->instantiate(); + Control *scene_as_control = Object::cast_to<Control>(scene); + Node2D *scene_as_node2d = Object::cast_to<Node2D>(scene); + if (scene_as_control) { + scene_as_control->set_position(tile_map_node->map_to_local(r_cell_data.coords) + scene_as_control->get_position()); + } else if (scene_as_node2d) { + Transform2D xform; + xform.set_origin(tile_map_node->map_to_local(r_cell_data.coords)); + scene_as_node2d->set_transform(xform * scene_as_node2d->get_transform()); + } + tile_map_node->add_child(scene); + r_cell_data.scene = scene->get_name(); + } + } + } + } +} + +#ifdef DEBUG_ENABLED +void TileMapLayer::_scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + + if (!Engine::get_singleton()->is_editor_hint()) { + return; + } + + // Draw a placeholder for scenes needing one. + RenderingServer *rs = RenderingServer::get_singleton(); + + const TileMapCell &c = r_cell_data.cell; + + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (!source->has_tile(c.get_atlas_coords()) || !source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + return; + } + + TileSetScenesCollectionSource *scenes_collection_source = Object::cast_to<TileSetScenesCollectionSource>(source); + if (scenes_collection_source) { + if (!scenes_collection_source->get_scene_tile_scene(c.alternative_tile).is_valid() || scenes_collection_source->get_scene_tile_display_placeholder(c.alternative_tile)) { + // Generate a random color from the hashed values of the tiles. + Array to_hash; + to_hash.push_back(c.source_id); + to_hash.push_back(c.alternative_tile); + uint32_t hash = RandomPCG(to_hash.hash()).rand(); + + Color color; + color = color.from_hsv( + (float)((hash >> 24) & 0xFF) / 256.0, + Math::lerp(0.5, 1.0, (float)((hash >> 16) & 0xFF) / 256.0), + Math::lerp(0.5, 1.0, (float)((hash >> 8) & 0xFF) / 256.0), + 0.8); + + // Draw a placeholder tile. + Transform2D cell_to_quadrant; + cell_to_quadrant.set_origin(tile_map_node->map_to_local(r_cell_data.coords) - p_quadrant_pos); + rs->canvas_item_add_set_transform(p_canvas_item, cell_to_quadrant); + rs->canvas_item_add_circle(p_canvas_item, Vector2(), MIN(tile_set->get_tile_size().x, tile_set->get_tile_size().y) / 4.0, color); + } + } + } +} +#endif // DEBUG_ENABLED + +///////////////////////////////////////////////////////////////////// + +void TileMapLayer::_build_runtime_update_tile_data() { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + // Check if we should cleanup everything. + bool forced_cleanup = in_destructor || !enabled || !tile_map_node->is_inside_tree() || !tile_set.is_valid() || !tile_map_node->is_visible_in_tree(); + if (!forced_cleanup) { + if (tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_use_tile_data_runtime_update) && tile_map_node->GDVIRTUAL_IS_OVERRIDDEN(_tile_data_runtime_update)) { + if (_runtime_update_tile_data_was_cleaned_up || dirty.flags[DIRTY_FLAGS_TILE_MAP_TILE_SET]) { + for (KeyValue<Vector2i, CellData> &E : tile_map) { + _build_runtime_update_tile_data_for_cell(E.value); + } + } else if (dirty.flags[DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE]) { + for (KeyValue<Vector2i, CellData> &E : tile_map) { + _build_runtime_update_tile_data_for_cell(E.value, true); + } + } else { + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + _build_runtime_update_tile_data_for_cell(cell_data); + } + } + } + } + + // ----------- + // Mark the navigation state as up to date. + _runtime_update_tile_data_was_cleaned_up = forced_cleanup; +} + +void TileMapLayer::_build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + + TileMapCell &c = r_cell_data.cell; + TileSetSource *source; + if (tile_set->has_source(c.source_id)) { + source = *tile_set->get_source(c.source_id); + + if (source->has_tile(c.get_atlas_coords()) && source->has_alternative_tile(c.get_atlas_coords(), c.alternative_tile)) { + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + bool ret = false; + if (tile_map_node->GDVIRTUAL_CALL(_use_tile_data_runtime_update, layer_index_in_tile_map_node, r_cell_data.coords, ret) && ret) { + TileData *tile_data = atlas_source->get_tile_data(c.get_atlas_coords(), c.alternative_tile); + + // Create the runtime TileData. + TileData *tile_data_runtime_use = tile_data->duplicate(); + tile_data_runtime_use->set_allow_transform(true); + r_cell_data.runtime_tile_data_cache = tile_data_runtime_use; + + tile_map_node->GDVIRTUAL_CALL(_tile_data_runtime_update, layer_index_in_tile_map_node, r_cell_data.coords, tile_data_runtime_use); + + if (p_auto_add_to_dirty_list) { + dirty.cell_list.add(&r_cell_data.dirty_list_element); + } + } + } + } + } +} + +void TileMapLayer::_clear_runtime_update_tile_data() { + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + + // Clear the runtime tile data. + if (cell_data.runtime_tile_data_cache) { + memdelete(cell_data.runtime_tile_data_cache); + cell_data.runtime_tile_data_cache = nullptr; + } + } +} + +TileSet::TerrainsPattern TileMapLayer::_get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (!tile_set.is_valid()) { + return TileSet::TerrainsPattern(); + } + // Returns all tiles compatible with the given constraints. + RBMap<TileSet::TerrainsPattern, int> terrain_pattern_score; + RBSet<TileSet::TerrainsPattern> pattern_set = tile_set->get_terrains_pattern_set(p_terrain_set); + ERR_FAIL_COND_V(pattern_set.is_empty(), TileSet::TerrainsPattern()); + for (TileSet::TerrainsPattern &terrain_pattern : pattern_set) { + int score = 0; + + // Check the center bit constraint. + TerrainConstraint terrain_constraint = TerrainConstraint(tile_map_node, p_position, terrain_pattern.get_terrain()); + const RBSet<TerrainConstraint>::Element *in_set_constraint_element = p_constraints.find(terrain_constraint); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain() != terrain_pattern.get_terrain()) { + continue; // Ignore a pattern that cannot keep bits without constraints unmodified. + } + + // Check the surrounding bits + bool invalid_pattern = false; + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + // Check if the bit is compatible with the constraints. + TerrainConstraint terrain_bit_constraint = TerrainConstraint(tile_map_node, p_position, bit, terrain_pattern.get_terrain_peering_bit(bit)); + in_set_constraint_element = p_constraints.find(terrain_bit_constraint); + if (in_set_constraint_element) { + if (in_set_constraint_element->get().get_terrain() != terrain_bit_constraint.get_terrain()) { + score += in_set_constraint_element->get().get_priority(); + } + } else if (p_current_pattern.get_terrain_peering_bit(bit) != terrain_pattern.get_terrain_peering_bit(bit)) { + invalid_pattern = true; // Ignore a pattern that cannot keep bits without constraints unmodified. + break; + } + } + } + if (invalid_pattern) { + continue; + } + + terrain_pattern_score[terrain_pattern] = score; + } + + // Compute the minimum score. + TileSet::TerrainsPattern min_score_pattern = p_current_pattern; + int min_score = INT32_MAX; + for (KeyValue<TileSet::TerrainsPattern, int> E : terrain_pattern_score) { + if (E.value < min_score) { + min_score_pattern = E.key; + min_score = E.value; + } + } + + return min_score_pattern; +} + +RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (!tile_set.is_valid()) { + return RBSet<TerrainConstraint>(); + } + + // Compute the constraints needed from the surrounding tiles. + RBSet<TerrainConstraint> output; + output.insert(TerrainConstraint(tile_map_node, p_position, p_terrains_pattern.get_terrain())); + + for (uint32_t i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { + TileSet::CellNeighbor side = TileSet::CellNeighbor(i); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, side)) { + TerrainConstraint c = TerrainConstraint(tile_map_node, p_position, side, p_terrains_pattern.get_terrain_peering_bit(side)); + output.insert(c); + } + } + + return output; +} + +RBSet<TerrainConstraint> TileMapLayer::_get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (!tile_set.is_valid()) { + return RBSet<TerrainConstraint>(); + } + + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), RBSet<TerrainConstraint>()); + + // Build a set of dummy constraints to get the constrained points. + RBSet<TerrainConstraint> dummy_constraints; + for (const Vector2i &E : p_painted) { + for (int i = 0; i < TileSet::CELL_NEIGHBOR_MAX; i++) { // Iterates over neighbor bits. + TileSet::CellNeighbor bit = TileSet::CellNeighbor(i); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + dummy_constraints.insert(TerrainConstraint(tile_map_node, E, bit, -1)); + } + } + } + + // For each constrained point, we get all overlapping tiles, and select the most adequate terrain for it. + RBSet<TerrainConstraint> constraints; + for (const TerrainConstraint &E_constraint : dummy_constraints) { + HashMap<int, int> terrain_count; + + // Count the number of occurrences per terrain. + HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = E_constraint.get_overlapping_coords_and_peering_bits(); + for (const KeyValue<Vector2i, TileSet::CellNeighbor> &E_overlapping : overlapping_terrain_bits) { + TileData *neighbor_tile_data = nullptr; + TileMapCell neighbor_cell = get_cell(E_overlapping.key); + if (neighbor_cell.source_id != TileSet::INVALID_SOURCE) { + Ref<TileSetSource> source = tile_set->get_source(neighbor_cell.source_id); + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + TileData *tile_data = atlas_source->get_tile_data(neighbor_cell.get_atlas_coords(), neighbor_cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + neighbor_tile_data = tile_data; + } + } + } + + int terrain = neighbor_tile_data ? neighbor_tile_data->get_terrain_peering_bit(TileSet::CellNeighbor(E_overlapping.value)) : -1; + if (!p_ignore_empty_terrains || terrain >= 0) { + if (!terrain_count.has(terrain)) { + terrain_count[terrain] = 0; + } + terrain_count[terrain] += 1; + } + } + + // Get the terrain with the max number of occurrences. + int max = 0; + int max_terrain = -1; + for (const KeyValue<int, int> &E_terrain_count : terrain_count) { + if (E_terrain_count.value > max) { + max = E_terrain_count.value; + max_terrain = E_terrain_count.key; + } + } + + // Set the adequate terrain. + if (max > 0) { + TerrainConstraint c = E_constraint; + c.set_terrain(max_terrain); + constraints.insert(c); + } + } + + // Add the centers as constraints. + for (Vector2i E_coords : p_painted) { + TileData *tile_data = nullptr; + TileMapCell cell = get_cell(E_coords); + if (cell.source_id != TileSet::INVALID_SOURCE) { + Ref<TileSetSource> source = tile_set->get_source(cell.source_id); + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + } + } + + int terrain = (tile_data && tile_data->get_terrain_set() == p_terrain_set) ? tile_data->get_terrain() : -1; + if (!p_ignore_empty_terrains || terrain >= 0) { + constraints.insert(TerrainConstraint(tile_map_node, E_coords, terrain)); + } + } + + return constraints; +} + +void TileMapLayer::set_tile_map(TileMap *p_tile_map) { + tile_map_node = p_tile_map; +} + +void TileMapLayer::set_layer_index_in_tile_map_node(int p_index) { + if (p_index == layer_index_in_tile_map_node) { + return; + } + layer_index_in_tile_map_node = p_index; + dirty.flags[DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE] = true; + tile_map_node->queue_internal_update(); +} + +Rect2 TileMapLayer::get_rect(bool &r_changed) const { + // Compute the displayed area of the tilemap. + r_changed = false; +#ifdef DEBUG_ENABLED + + if (rect_cache_dirty) { + Rect2 r_total; + bool first = true; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + Rect2 r; + r.position = tile_map_node->map_to_local(E.key); + r.size = Size2(); + if (first) { + r_total = r; + first = false; + } else { + r_total = r_total.merge(r); + } + } + + r_changed = rect_cache != r_total; + + rect_cache = r_total; + rect_cache_dirty = false; + } +#endif + return rect_cache; +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (!tile_set.is_valid()) { + return HashMap<Vector2i, TileSet::TerrainsPattern>(); + } + + // Copy the constraints set. + RBSet<TerrainConstraint> constraints = p_constraints; + + // Output map. + HashMap<Vector2i, TileSet::TerrainsPattern> output; + + // Add all positions to a set. + for (int i = 0; i < p_to_replace.size(); i++) { + const Vector2i &coords = p_to_replace[i]; + + // Select the best pattern for the given constraints. + TileSet::TerrainsPattern current_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(coords); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + current_pattern = tile_data->get_terrains_pattern(); + } + } + } + TileSet::TerrainsPattern pattern = _get_best_terrain_pattern_for_constraints(p_terrain_set, coords, constraints, current_pattern); + + // Update the constraint set with the new ones. + RBSet<TerrainConstraint> new_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, pattern); + for (const TerrainConstraint &E_constraint : new_constraints) { + if (constraints.has(E_constraint)) { + constraints.erase(E_constraint); + } + TerrainConstraint c = E_constraint; + c.set_priority(5); + constraints.insert(c); + } + + output[coords] = pattern; + } + return output; +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), output); + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); + + // Build list and set of tiles that can be modified (painted and their surroundings). + Vector<Vector2i> can_modify_list; + RBSet<Vector2i> can_modify_set; + RBSet<Vector2i> painted_set; + for (int i = p_coords_array.size() - 1; i >= 0; i--) { + const Vector2i &coords = p_coords_array[i]; + can_modify_list.push_back(coords); + can_modify_set.insert(coords); + painted_set.insert(coords); + } + for (Vector2i coords : p_coords_array) { + // Find the adequate neighbor. + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_map_node->is_existing_neighbor(bit)) { + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); + if (!can_modify_set.has(neighbor)) { + can_modify_list.push_back(neighbor); + can_modify_set.insert(neighbor); + } + } + } + } + + // Build a set, out of the possibly modified tiles, of the one with a center bit that is set (or will be) to the painted terrain. + RBSet<Vector2i> cells_with_terrain_center_bit; + for (Vector2i coords : can_modify_set) { + bool connect = false; + if (painted_set.has(coords)) { + connect = true; + } else { + // Get the center bit of the cell. + TileData *tile_data = nullptr; + TileMapCell cell = get_cell(coords); + if (cell.source_id != TileSet::INVALID_SOURCE) { + Ref<TileSetSource> source = tile_set->get_source(cell.source_id); + Ref<TileSetAtlasSource> atlas_source = source; + if (atlas_source.is_valid()) { + tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + } + } + + if (tile_data && tile_data->get_terrain_set() == p_terrain_set && tile_data->get_terrain() == p_terrain) { + connect = true; + } + } + if (connect) { + cells_with_terrain_center_bit.insert(coords); + } + } + + RBSet<TerrainConstraint> constraints; + + // Add new constraints from the path drawn. + for (Vector2i coords : p_coords_array) { + // Constraints on the center bit. + TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); + c.set_priority(10); + constraints.insert(c); + + // Constraints on the connecting bits. + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + c = TerrainConstraint(tile_map_node, coords, bit, p_terrain); + c.set_priority(10); + if ((int(bit) % 2) == 0) { + // Side peering bits: add the constraint if the center is of the same terrain. + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); + if (cells_with_terrain_center_bit.has(neighbor)) { + constraints.insert(c); + } + } else { + // Corner peering bits: add the constraint if all tiles on the constraint has the same center bit. + HashMap<Vector2i, TileSet::CellNeighbor> overlapping_terrain_bits = c.get_overlapping_coords_and_peering_bits(); + bool valid = true; + for (KeyValue<Vector2i, TileSet::CellNeighbor> kv : overlapping_terrain_bits) { + if (!cells_with_terrain_center_bit.has(kv.key)) { + valid = false; + break; + } + } + if (valid) { + constraints.insert(c); + } + } + } + } + } + + // Fills in the constraint list from existing tiles. + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { + constraints.insert(c); + } + + // Fill the terrains. + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + return output; +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), output); + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); + + // Make sure the path is correct and build the peering bit list while doing it. + Vector<TileSet::CellNeighbor> neighbor_list; + for (int i = 0; i < p_coords_array.size() - 1; i++) { + // Find the adequate neighbor. + TileSet::CellNeighbor found_bit = TileSet::CELL_NEIGHBOR_MAX; + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_map_node->is_existing_neighbor(bit)) { + if (tile_map_node->get_neighbor_cell(p_coords_array[i], bit) == p_coords_array[i + 1]) { + found_bit = bit; + break; + } + } + } + ERR_FAIL_COND_V_MSG(found_bit == TileSet::CELL_NEIGHBOR_MAX, output, vformat("Invalid terrain path, %s is not a neighboring tile of %s", p_coords_array[i + 1], p_coords_array[i])); + neighbor_list.push_back(found_bit); + } + + // Build list and set of tiles that can be modified (painted and their surroundings). + Vector<Vector2i> can_modify_list; + RBSet<Vector2i> can_modify_set; + RBSet<Vector2i> painted_set; + for (int i = p_coords_array.size() - 1; i >= 0; i--) { + const Vector2i &coords = p_coords_array[i]; + can_modify_list.push_back(coords); + can_modify_set.insert(coords); + painted_set.insert(coords); + } + for (Vector2i coords : p_coords_array) { + // Find the adequate neighbor. + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); + if (!can_modify_set.has(neighbor)) { + can_modify_list.push_back(neighbor); + can_modify_set.insert(neighbor); + } + } + } + } + + RBSet<TerrainConstraint> constraints; + + // Add new constraints from the path drawn. + for (Vector2i coords : p_coords_array) { + // Constraints on the center bit. + TerrainConstraint c = TerrainConstraint(tile_map_node, coords, p_terrain); + c.set_priority(10); + constraints.insert(c); + } + for (int i = 0; i < p_coords_array.size() - 1; i++) { + // Constraints on the peering bits. + TerrainConstraint c = TerrainConstraint(tile_map_node, p_coords_array[i], neighbor_list[i], p_terrain); + c.set_priority(10); + constraints.insert(c); + } + + // Fills in the constraint list from existing tiles. + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { + constraints.insert(c); + } + + // Fill the terrains. + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + return output; +} + +HashMap<Vector2i, TileSet::TerrainsPattern> TileMapLayer::terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains) { + HashMap<Vector2i, TileSet::TerrainsPattern> output; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), output); + ERR_FAIL_INDEX_V(p_terrain_set, tile_set->get_terrain_sets_count(), output); + + // Build list and set of tiles that can be modified (painted and their surroundings). + Vector<Vector2i> can_modify_list; + RBSet<Vector2i> can_modify_set; + RBSet<Vector2i> painted_set; + for (int i = p_coords_array.size() - 1; i >= 0; i--) { + const Vector2i &coords = p_coords_array[i]; + can_modify_list.push_back(coords); + can_modify_set.insert(coords); + painted_set.insert(coords); + } + for (Vector2i coords : p_coords_array) { + // Find the adequate neighbor. + for (int j = 0; j < TileSet::CELL_NEIGHBOR_MAX; j++) { + TileSet::CellNeighbor bit = TileSet::CellNeighbor(j); + if (tile_set->is_valid_terrain_peering_bit(p_terrain_set, bit)) { + Vector2i neighbor = tile_map_node->get_neighbor_cell(coords, bit); + if (!can_modify_set.has(neighbor)) { + can_modify_list.push_back(neighbor); + can_modify_set.insert(neighbor); + } + } + } + } + + // Add constraint by the new ones. + RBSet<TerrainConstraint> constraints; + + // Add new constraints from the path drawn. + for (Vector2i coords : p_coords_array) { + // Constraints on the center bit. + RBSet<TerrainConstraint> added_constraints = _get_terrain_constraints_from_added_pattern(coords, p_terrain_set, p_terrains_pattern); + for (TerrainConstraint c : added_constraints) { + c.set_priority(10); + constraints.insert(c); + } + } + + // Fills in the constraint list from modified tiles border. + for (TerrainConstraint c : _get_terrain_constraints_from_painted_cells_list(painted_set, p_terrain_set, p_ignore_empty_terrains)) { + constraints.insert(c); + } + + // Fill the terrains. + output = terrain_fill_constraints(can_modify_list, p_terrain_set, constraints); + return output; +} + +TileMapCell TileMapLayer::get_cell(const Vector2i &p_coords, bool p_use_proxies) const { + if (!tile_map.has(p_coords)) { + return TileMapCell(); + } else { + TileMapCell c = tile_map.find(p_coords)->value.cell; + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(c.source_id, c.get_atlas_coords(), c.alternative_tile); + c.source_id = proxyed[0]; + c.set_atlas_coords(proxyed[1]); + c.alternative_tile = proxyed[2]; + } + return c; + } +} + +void TileMapLayer::set_tile_data(TileMapDataFormat p_format, const Vector<int> &p_data) { + ERR_FAIL_COND(p_format > TileMapDataFormat::FORMAT_3); + + // Set data for a given tile from raw data. + + int c = p_data.size(); + const int *r = p_data.ptr(); + + int offset = (p_format >= TileMapDataFormat::FORMAT_2) ? 3 : 2; + ERR_FAIL_COND_MSG(c % offset != 0, vformat("Corrupted tile data. Got size: %s. Expected modulo: %s", offset)); + + clear(); + +#ifdef DISABLE_DEPRECATED + ERR_FAIL_COND_MSG(p_format != TileMapDataFormat::FORMAT_3, vformat("Cannot handle deprecated TileMap data format version %d. This Godot version was compiled with no support for deprecated data.", p_format)); +#endif + + for (int i = 0; i < c; i += offset) { + const uint8_t *ptr = (const uint8_t *)&r[i]; + uint8_t local[12]; + for (int j = 0; j < ((p_format >= TileMapDataFormat::FORMAT_2) ? 12 : 8); j++) { + local[j] = ptr[j]; + } + +#ifdef BIG_ENDIAN_ENABLED + + SWAP(local[0], local[3]); + SWAP(local[1], local[2]); + SWAP(local[4], local[7]); + SWAP(local[5], local[6]); + //TODO: ask someone to check this... + if (FORMAT >= FORMAT_2) { + SWAP(local[8], local[11]); + SWAP(local[9], local[10]); + } +#endif + // Extracts position in TileMap. + int16_t x = decode_uint16(&local[0]); + int16_t y = decode_uint16(&local[2]); + + if (p_format == TileMapDataFormat::FORMAT_3) { + uint16_t source_id = decode_uint16(&local[4]); + uint16_t atlas_coords_x = decode_uint16(&local[6]); + uint16_t atlas_coords_y = decode_uint16(&local[8]); + uint16_t alternative_tile = decode_uint16(&local[10]); + set_cell(Vector2i(x, y), source_id, Vector2i(atlas_coords_x, atlas_coords_y), alternative_tile); + } else { +#ifndef DISABLE_DEPRECATED + // Previous decated format. + + uint32_t v = decode_uint32(&local[4]); + // Extract the transform flags that used to be in the tilemap. + bool flip_h = v & (1UL << 29); + bool flip_v = v & (1UL << 30); + bool transpose = v & (1UL << 31); + v &= (1UL << 29) - 1; + + // Extract autotile/atlas coords. + int16_t coord_x = 0; + int16_t coord_y = 0; + if (p_format == TileMapDataFormat::FORMAT_2) { + coord_x = decode_uint16(&local[8]); + coord_y = decode_uint16(&local[10]); + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (tile_set.is_valid()) { + Array a = tile_set->compatibility_tilemap_map(v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose); + if (a.size() == 3) { + set_cell(Vector2i(x, y), a[0], a[1], a[2]); + } else { + ERR_PRINT(vformat("No valid tile in Tileset for: tile:%s coords:%s flip_h:%s flip_v:%s transpose:%s", v, Vector2i(coord_x, coord_y), flip_h, flip_v, transpose)); + } + } else { + int compatibility_alternative_tile = ((int)flip_h) + ((int)flip_v << 1) + ((int)transpose << 2); + set_cell(Vector2i(x, y), v, Vector2i(coord_x, coord_y), compatibility_alternative_tile); + } +#endif + } + } +} + +Vector<int> TileMapLayer::get_tile_data() const { + // Export tile data to raw format. + Vector<int> tile_data; + tile_data.resize(tile_map.size() * 3); + int *w = tile_data.ptrw(); + + // Save in highest format. + + int idx = 0; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + uint8_t *ptr = (uint8_t *)&w[idx]; + encode_uint16((int16_t)(E.key.x), &ptr[0]); + encode_uint16((int16_t)(E.key.y), &ptr[2]); + encode_uint16(E.value.cell.source_id, &ptr[4]); + encode_uint16(E.value.cell.coord_x, &ptr[6]); + encode_uint16(E.value.cell.coord_y, &ptr[8]); + encode_uint16(E.value.cell.alternative_tile, &ptr[10]); + idx += 3; + } + + return tile_data; +} + +void TileMapLayer::notify_tile_map_change(DirtyFlags p_what) { + dirty.flags[p_what] = true; + tile_map_node->queue_internal_update(); + _physics_notify_tilemap_change(p_what); +} + +void TileMapLayer::internal_update() { + // Find TileData that need a runtime modification. + // This may add cells to the dirty list is a runtime modification has been notified. + _build_runtime_update_tile_data(); + + // Update all subsystems. + _rendering_update(); + _physics_update(); + _navigation_update(); + _scenes_update(); +#ifdef DEBUG_ENABLED + _debug_update(); +#endif // DEBUG_ENABLED + + _clear_runtime_update_tile_data(); + + // Clear the "what is dirty" flags. + for (int i = 0; i < DIRTY_FLAGS_MAX; i++) { + dirty.flags[i] = false; + } + + // List the cells to delete definitely. + Vector<Vector2i> to_delete; + for (SelfList<CellData> *cell_data_list_element = dirty.cell_list.first(); cell_data_list_element; cell_data_list_element = cell_data_list_element->next()) { + CellData &cell_data = *cell_data_list_element->self(); + // Select the the cell from tile_map if it is invalid. + if (cell_data.cell.source_id == TileSet::INVALID_SOURCE) { + to_delete.push_back(cell_data.coords); + } + } + + // Remove cells that are empty after the cleanup. + for (const Vector2i &coords : to_delete) { + tile_map.erase(coords); + } + + // Clear the dirty cells list. + dirty.cell_list.clear(); +} + +void TileMapLayer::set_cell(const Vector2i &p_coords, int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) { + // Set the current cell tile (using integer position). + Vector2i pk(p_coords); + HashMap<Vector2i, CellData>::Iterator E = tile_map.find(pk); + + int source_id = p_source_id; + Vector2i atlas_coords = p_atlas_coords; + int alternative_tile = p_alternative_tile; + + if ((source_id == TileSet::INVALID_SOURCE || atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE) && + (source_id != TileSet::INVALID_SOURCE || atlas_coords != TileSetSource::INVALID_ATLAS_COORDS || alternative_tile != TileSetSource::INVALID_TILE_ALTERNATIVE)) { + source_id = TileSet::INVALID_SOURCE; + atlas_coords = TileSetSource::INVALID_ATLAS_COORDS; + alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE; + } + + if (!E) { + if (source_id == TileSet::INVALID_SOURCE) { + return; // Nothing to do, the tile is already empty. + } + + // Insert a new cell in the tile map. + CellData new_cell_data; + new_cell_data.coords = pk; + E = tile_map.insert(pk, new_cell_data); + } else { + if (E->value.cell.source_id == source_id && E->value.cell.get_atlas_coords() == atlas_coords && E->value.cell.alternative_tile == alternative_tile) { + return; // Nothing changed. + } + } + + TileMapCell &c = E->value.cell; + c.source_id = source_id; + c.set_atlas_coords(atlas_coords); + c.alternative_tile = alternative_tile; + + // Make the given cell dirty. + if (!E->value.dirty_list_element.in_list()) { + dirty.cell_list.add(&(E->value.dirty_list_element)); + } + tile_map_node->queue_internal_update(); + + used_rect_cache_dirty = true; +} + +void TileMapLayer::erase_cell(const Vector2i &p_coords) { + set_cell(p_coords, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); +} + +int TileMapLayer::get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSet::INVALID_SOURCE; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); + return proxyed[0]; + } + + return E->value.cell.source_id; +} + +Vector2i TileMapLayer::get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSetSource::INVALID_ATLAS_COORDS; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); + return proxyed[1]; + } + + return E->value.cell.get_atlas_coords(); +} + +int TileMapLayer::get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies) const { + // Get a cell source id from position. + HashMap<Vector2i, CellData>::ConstIterator E = tile_map.find(p_coords); + + if (!E) { + return TileSetSource::INVALID_TILE_ALTERNATIVE; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + if (p_use_proxies && tile_set.is_valid()) { + Array proxyed = tile_set->map_tile_proxy(E->value.cell.source_id, E->value.cell.get_atlas_coords(), E->value.cell.alternative_tile); + return proxyed[2]; + } + + return E->value.cell.alternative_tile; +} + +TileData *TileMapLayer::get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies) const { + int source_id = get_cell_source_id(p_coords, p_use_proxies); + if (source_id == TileSet::INVALID_SOURCE) { + return nullptr; + } + + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + Ref<TileSetAtlasSource> source = tile_set->get_source(source_id); + if (source.is_valid()) { + return source->get_tile_data(get_cell_atlas_coords(p_coords, p_use_proxies), get_cell_alternative_tile(p_coords, p_use_proxies)); + } + + return nullptr; +} + +void TileMapLayer::clear() { + // Remove all tiles. + for (KeyValue<Vector2i, CellData> &kv : tile_map) { + erase_cell(kv.key); + } + used_rect_cache_dirty = true; +} + +Ref<TileMapPattern> TileMapLayer::get_pattern(TypedArray<Vector2i> p_coords_array) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND_V(!tile_set.is_valid(), nullptr); + + Ref<TileMapPattern> output; + output.instantiate(); + if (p_coords_array.is_empty()) { + return output; + } + + Vector2i min = Vector2i(p_coords_array[0]); + for (int i = 1; i < p_coords_array.size(); i++) { + min = min.min(p_coords_array[i]); + } + + Vector<Vector2i> coords_in_pattern_array; + coords_in_pattern_array.resize(p_coords_array.size()); + Vector2i ensure_positive_offset; + for (int i = 0; i < p_coords_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords - min; + if (tile_set->get_tile_shape() != TileSet::TILE_SHAPE_SQUARE) { + if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x -= 1; + if (coords_in_pattern.x < 0) { + ensure_positive_offset.x = 1; + } + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y -= 1; + if (coords_in_pattern.y < 0) { + ensure_positive_offset.y = 1; + } + } + } else if (tile_set->get_tile_layout() == TileSet::TILE_LAYOUT_STACKED_OFFSET) { + if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_HORIZONTAL && bool(min.y % 2) && bool(coords_in_pattern.y % 2)) { + coords_in_pattern.x += 1; + } else if (tile_set->get_tile_offset_axis() == TileSet::TILE_OFFSET_AXIS_VERTICAL && bool(min.x % 2) && bool(coords_in_pattern.x % 2)) { + coords_in_pattern.y += 1; + } + } + } + coords_in_pattern_array.write[i] = coords_in_pattern; + } + + for (int i = 0; i < coords_in_pattern_array.size(); i++) { + Vector2i coords = p_coords_array[i]; + Vector2i coords_in_pattern = coords_in_pattern_array[i]; + output->set_cell(coords_in_pattern + ensure_positive_offset, get_cell_source_id(coords), get_cell_atlas_coords(coords), get_cell_alternative_tile(coords)); + } + + return output; +} + +void TileMapLayer::set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(tile_set.is_null()); + ERR_FAIL_COND(p_pattern.is_null()); + + TypedArray<Vector2i> used_cells = p_pattern->get_used_cells(); + for (int i = 0; i < used_cells.size(); i++) { + Vector2i coords = tile_map_node->map_pattern(p_position, used_cells[i], p_pattern); + set_cell(coords, p_pattern->get_cell_source_id(used_cells[i]), p_pattern->get_cell_atlas_coords(used_cells[i]), p_pattern->get_cell_alternative_tile(used_cells[i])); + } +} + +void TileMapLayer::set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); + + Vector<Vector2i> cells_vector; + HashSet<Vector2i> painted_set; + for (int i = 0; i < p_cells.size(); i++) { + cells_vector.push_back(p_cells[i]); + painted_set.insert(p_cells[i]); + } + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_connect(cells_vector, p_terrain_set, p_terrain, p_ignore_empty_terrains); + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { + // Paint a random tile with the correct terrain for the painted path. + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(kv.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + if (in_map_terrain_pattern != kv.value) { + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } + } + } +} + +void TileMapLayer::set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains) { + const Ref<TileSet> &tile_set = tile_map_node->get_tileset(); + ERR_FAIL_COND(!tile_set.is_valid()); + ERR_FAIL_INDEX(p_terrain_set, tile_set->get_terrain_sets_count()); + + Vector<Vector2i> vector_path; + HashSet<Vector2i> painted_set; + for (int i = 0; i < p_path.size(); i++) { + vector_path.push_back(p_path[i]); + painted_set.insert(p_path[i]); + } + + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_output = terrain_fill_path(vector_path, p_terrain_set, p_terrain, p_ignore_empty_terrains); + for (const KeyValue<Vector2i, TileSet::TerrainsPattern> &kv : terrain_fill_output) { + if (painted_set.has(kv.key)) { + // Paint a random tile with the correct terrain for the painted path. + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } else { + // Avoids updating the painted path from the output if the new pattern is the same as before. + TileSet::TerrainsPattern in_map_terrain_pattern = TileSet::TerrainsPattern(*tile_set, p_terrain_set); + TileMapCell cell = get_cell(kv.key); + if (cell.source_id != TileSet::INVALID_SOURCE) { + TileSetSource *source = *tile_set->get_source(cell.source_id); + TileSetAtlasSource *atlas_source = Object::cast_to<TileSetAtlasSource>(source); + if (atlas_source) { + // Get tile data. + TileData *tile_data = atlas_source->get_tile_data(cell.get_atlas_coords(), cell.alternative_tile); + if (tile_data && tile_data->get_terrain_set() == p_terrain_set) { + in_map_terrain_pattern = tile_data->get_terrains_pattern(); + } + } + } + if (in_map_terrain_pattern != kv.value) { + TileMapCell c = tile_set->get_random_tile_from_terrains_pattern(p_terrain_set, kv.value); + set_cell(kv.key, c.source_id, c.get_atlas_coords(), c.alternative_tile); + } + } + } +} + +TypedArray<Vector2i> TileMapLayer::get_used_cells() const { + // Returns the cells used in the tilemap. + TypedArray<Vector2i> a; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + const TileMapCell &c = E.value.cell; + if (c.source_id == TileSet::INVALID_SOURCE) { + continue; + } + a.push_back(E.key); + } + + return a; +} + +TypedArray<Vector2i> TileMapLayer::get_used_cells_by_id(int p_source_id, const Vector2i p_atlas_coords, int p_alternative_tile) const { + // Returns the cells used in the tilemap. + TypedArray<Vector2i> a; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + const TileMapCell &c = E.value.cell; + if (c.source_id == TileSet::INVALID_SOURCE) { + continue; + } + if ((p_source_id == TileSet::INVALID_SOURCE || p_source_id == c.source_id) && + (p_atlas_coords == TileSetSource::INVALID_ATLAS_COORDS || p_atlas_coords == c.get_atlas_coords()) && + (p_alternative_tile == TileSetSource::INVALID_TILE_ALTERNATIVE || p_alternative_tile == c.alternative_tile)) { + a.push_back(E.key); + } + } + + return a; +} + +Rect2i TileMapLayer::get_used_rect() const { + // Return the rect of the currently used area. + if (used_rect_cache_dirty) { + used_rect_cache = Rect2i(); + + bool first = true; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + const TileMapCell &c = E.value.cell; + if (c.source_id == TileSet::INVALID_SOURCE) { + continue; + } + if (first) { + used_rect_cache = Rect2i(E.key.x, E.key.y, 0, 0); + first = false; + } else { + used_rect_cache.expand_to(E.key); + } + } + if (!first) { + // Only if we have at least one cell. + // The cache expands to top-left coordinate, so we add one full tile. + used_rect_cache.size += Vector2i(1, 1); + } + used_rect_cache_dirty = false; + } + + return used_rect_cache; +} + +void TileMapLayer::set_name(String p_name) { + if (name == p_name) { + return; + } + name = p_name; + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} + +String TileMapLayer::get_name() const { + return name; +} + +void TileMapLayer::set_enabled(bool p_enabled) { + if (enabled == p_enabled) { + return; + } + enabled = p_enabled; + dirty.flags[DIRTY_FLAGS_LAYER_ENABLED] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); + + tile_map_node->update_configuration_warnings(); +} + +bool TileMapLayer::is_enabled() const { + return enabled; +} + +void TileMapLayer::set_modulate(Color p_modulate) { + if (modulate == p_modulate) { + return; + } + modulate = p_modulate; + dirty.flags[DIRTY_FLAGS_LAYER_MODULATE] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} + +Color TileMapLayer::get_modulate() const { + return modulate; +} + +void TileMapLayer::set_y_sort_enabled(bool p_y_sort_enabled) { + if (y_sort_enabled == p_y_sort_enabled) { + return; + } + y_sort_enabled = p_y_sort_enabled; + dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ENABLED] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); + + tile_map_node->update_configuration_warnings(); +} + +bool TileMapLayer::is_y_sort_enabled() const { + return y_sort_enabled; +} + +void TileMapLayer::set_y_sort_origin(int p_y_sort_origin) { + if (y_sort_origin == p_y_sort_origin) { + return; + } + y_sort_origin = p_y_sort_origin; + dirty.flags[DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} + +int TileMapLayer::get_y_sort_origin() const { + return y_sort_origin; +} + +void TileMapLayer::set_z_index(int p_z_index) { + if (z_index == p_z_index) { + return; + } + z_index = p_z_index; + dirty.flags[DIRTY_FLAGS_LAYER_Z_INDEX] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); + + tile_map_node->update_configuration_warnings(); +} + +int TileMapLayer::get_z_index() const { + return z_index; +} + +void TileMapLayer::set_navigation_enabled(bool p_enabled) { + if (navigation_enabled == p_enabled) { + return; + } + navigation_enabled = p_enabled; + dirty.flags[DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED] = true; + tile_map_node->queue_internal_update(); + tile_map_node->emit_signal(CoreStringNames::get_singleton()->changed); +} + +bool TileMapLayer::is_navigation_enabled() const { + return navigation_enabled; +} + +void TileMapLayer::set_navigation_map(RID p_map) { + ERR_FAIL_COND_MSG(!tile_map_node->is_inside_tree(), "A TileMap navigation map can only be changed while inside the SceneTree."); + navigation_map = p_map; + uses_world_navigation_map = p_map == tile_map_node->get_world_2d()->get_navigation_map(); +} + +RID TileMapLayer::get_navigation_map() const { + if (navigation_map.is_valid()) { + return navigation_map; + } + return RID(); +} + +void TileMapLayer::fix_invalid_tiles() { + Ref<TileSet> tileset = tile_map_node->get_tileset(); + ERR_FAIL_COND_MSG(tileset.is_null(), "Cannot call fix_invalid_tiles() on a TileMap without a valid TileSet."); + + RBSet<Vector2i> coords; + for (const KeyValue<Vector2i, CellData> &E : tile_map) { + TileSetSource *source = *tileset->get_source(E.value.cell.source_id); + if (!source || !source->has_tile(E.value.cell.get_atlas_coords()) || !source->has_alternative_tile(E.value.cell.get_atlas_coords(), E.value.cell.alternative_tile)) { + coords.insert(E.key); + } + } + for (const Vector2i &E : coords) { + set_cell(E, TileSet::INVALID_SOURCE, TileSetSource::INVALID_ATLAS_COORDS, TileSetSource::INVALID_TILE_ALTERNATIVE); + } +} + +bool TileMapLayer::has_body_rid(RID p_physics_body) const { + return bodies_coords.has(p_physics_body); +} + +Vector2i TileMapLayer::get_coords_for_body_rid(RID p_physics_body) const { + return bodies_coords[p_physics_body]; +} + +TileMapLayer::~TileMapLayer() { + if (!tile_map_node) { + // Temporary layer. + return; + } + + in_destructor = true; + clear(); + internal_update(); +} + +HashMap<Vector2i, TileSet::CellNeighbor> TerrainConstraint::get_overlapping_coords_and_peering_bits() const { + HashMap<Vector2i, TileSet::CellNeighbor> output; + + ERR_FAIL_COND_V(is_center_bit(), output); + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND_V(!ts.is_valid(), output); + + TileSet::TileShape shape = ts->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_CORNER)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + // Half offset shapes. + TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_SIDE; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_CORNER; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER; + break; + case 5: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } else { + switch (bit) { + case 1: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 2: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE; + break; + case 3: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE)] = TileSet::CELL_NEIGHBOR_LEFT_CORNER; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER; + break; + case 4: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_SIDE; + break; + case 5: + output[base_cell_coords] = TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE; + output[tile_map->get_neighbor_cell(base_cell_coords, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE)] = TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE; + break; + default: + ERR_FAIL_V(output); + } + } + } + return output; +} + +TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain) { + tile_map = p_tile_map; + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); + + bit = 0; + base_cell_coords = p_position; + terrain = p_terrain; +} + +TerrainConstraint::TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain) { + // The way we build the constraint make it easy to detect conflicting constraints. + tile_map = p_tile_map; + + Ref<TileSet> ts = tile_map->get_tileset(); + ERR_FAIL_COND(!ts.is_valid()); + + TileSet::TileShape shape = ts->get_tile_shape(); + if (shape == TileSet::TILE_SHAPE_SQUARE) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else if (shape == TileSet::TILE_SHAPE_ISOMETRIC) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_CORNER); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else { + // Half-offset shapes. + TileSet::TileOffsetAxis offset_axis = ts->get_tile_offset_axis(); + if (offset_axis == TileSet::TILE_OFFSET_AXIS_HORIZONTAL) { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_SIDE: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_CORNER: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 5; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_LEFT_SIDE: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_CORNER: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 5; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } else { + switch (p_bit) { + case TileSet::CELL_NEIGHBOR_RIGHT_CORNER: + bit = 1; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_SIDE: + bit = 2; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_RIGHT_CORNER: + bit = 3; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_SIDE: + bit = 4; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_BOTTOM_LEFT_SIDE: + bit = 5; + base_cell_coords = p_position; + break; + case TileSet::CELL_NEIGHBOR_LEFT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE: + bit = 2; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_LEFT_CORNER: + bit = 1; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_LEFT_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_SIDE: + bit = 4; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_CORNER: + bit = 3; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_SIDE); + break; + case TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE: + bit = 5; + base_cell_coords = p_tile_map->get_neighbor_cell(p_position, TileSet::CELL_NEIGHBOR_TOP_RIGHT_SIDE); + break; + default: + ERR_FAIL(); + break; + } + } + } + terrain = p_terrain; +}
\ No newline at end of file diff --git a/scene/2d/tile_map_layer.h b/scene/2d/tile_map_layer.h new file mode 100644 index 0000000000..3125fcb488 --- /dev/null +++ b/scene/2d/tile_map_layer.h @@ -0,0 +1,413 @@ +/**************************************************************************/ +/* tile_map_layer.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef TILE_MAP_LAYER_H +#define TILE_MAP_LAYER_H + +#include "scene/2d/tile_map.h" +#include "scene/resources/tile_set.h" + +class TileSetAtlasSource; + +class TerrainConstraint { +private: + const TileMap *tile_map = nullptr; + Vector2i base_cell_coords; + int bit = -1; + int terrain = -1; + + int priority = 1; + +public: + bool operator<(const TerrainConstraint &p_other) const { + if (base_cell_coords == p_other.base_cell_coords) { + return bit < p_other.bit; + } + return base_cell_coords < p_other.base_cell_coords; + } + + String to_string() const { + return vformat("Constraint {pos:%s, bit:%d, terrain:%d, priority:%d}", base_cell_coords, bit, terrain, priority); + } + + Vector2i get_base_cell_coords() const { + return base_cell_coords; + } + + bool is_center_bit() const { + return bit == 0; + } + + HashMap<Vector2i, TileSet::CellNeighbor> get_overlapping_coords_and_peering_bits() const; + + void set_terrain(int p_terrain) { + terrain = p_terrain; + } + + int get_terrain() const { + return terrain; + } + + void set_priority(int p_priority) { + priority = p_priority; + } + + int get_priority() const { + return priority; + } + + TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, int p_terrain); // For the center terrain bit + TerrainConstraint(const TileMap *p_tile_map, const Vector2i &p_position, const TileSet::CellNeighbor &p_bit, int p_terrain); // For peering bits + TerrainConstraint(){}; +}; + +#ifdef DEBUG_ENABLED +class DebugQuadrant; +#endif // DEBUG_ENABLED +class RenderingQuadrant; + +struct CellData { + Vector2i coords; + TileMapCell cell; + + // Debug. + SelfList<CellData> debug_quadrant_list_element; + + // Rendering. + Ref<RenderingQuadrant> rendering_quadrant; + SelfList<CellData> rendering_quadrant_list_element; + LocalVector<RID> occluders; + + // Physics. + LocalVector<RID> bodies; + + // Navigation. + LocalVector<RID> navigation_regions; + + // Scenes. + String scene; + + // Runtime TileData cache. + TileData *runtime_tile_data_cache = nullptr; + + // List elements. + SelfList<CellData> dirty_list_element; + + bool operator<(const CellData &p_other) const { + return coords < p_other.coords; + } + + // For those, copy everything but SelfList elements. + void operator=(const CellData &p_other) { + coords = p_other.coords; + cell = p_other.cell; + occluders = p_other.occluders; + bodies = p_other.bodies; + navigation_regions = p_other.navigation_regions; + scene = p_other.scene; + runtime_tile_data_cache = p_other.runtime_tile_data_cache; + } + + CellData(const CellData &p_other) : + debug_quadrant_list_element(this), + rendering_quadrant_list_element(this), + dirty_list_element(this) { + coords = p_other.coords; + cell = p_other.cell; + occluders = p_other.occluders; + bodies = p_other.bodies; + navigation_regions = p_other.navigation_regions; + scene = p_other.scene; + runtime_tile_data_cache = p_other.runtime_tile_data_cache; + } + + CellData() : + debug_quadrant_list_element(this), + rendering_quadrant_list_element(this), + dirty_list_element(this) { + } +}; + +// For compatibility reasons, we use another comparator for Y-sorted layers. +struct CellDataYSortedComparator { + _FORCE_INLINE_ bool operator()(const CellData &p_a, const CellData &p_b) const { + return p_a.coords.x == p_b.coords.x ? (p_a.coords.y < p_b.coords.y) : (p_a.coords.x > p_b.coords.x); + } +}; + +#ifdef DEBUG_ENABLED +class DebugQuadrant : public RefCounted { + GDCLASS(DebugQuadrant, RefCounted); + +public: + Vector2i quadrant_coords; + SelfList<CellData>::List cells; + RID canvas_item; + + SelfList<DebugQuadrant> dirty_quadrant_list_element; + + DebugQuadrant() : + dirty_quadrant_list_element(this) { + } + + ~DebugQuadrant() { + cells.clear(); + } +}; +#endif // DEBUG_ENABLED + +class RenderingQuadrant : public RefCounted { + GDCLASS(RenderingQuadrant, RefCounted); + +public: + struct CoordsWorldComparator { + _ALWAYS_INLINE_ bool operator()(const Vector2 &p_a, const Vector2 &p_b) const { + // We sort the cells by their local coords, as it is needed by rendering. + if (p_a.y == p_b.y) { + return p_a.x > p_b.x; + } else { + return p_a.y < p_b.y; + } + } + }; + + Vector2i quadrant_coords; + SelfList<CellData>::List cells; + List<RID> canvas_items; + Vector2 canvas_items_position; + + SelfList<RenderingQuadrant> dirty_quadrant_list_element; + + RenderingQuadrant() : + dirty_quadrant_list_element(this) { + } + + ~RenderingQuadrant() { + cells.clear(); + } +}; + +class TileMapLayer : public RefCounted { + GDCLASS(TileMapLayer, RefCounted); + +public: + enum DirtyFlags { + DIRTY_FLAGS_LAYER_ENABLED = 0, + DIRTY_FLAGS_LAYER_MODULATE, + DIRTY_FLAGS_LAYER_Y_SORT_ENABLED, + DIRTY_FLAGS_LAYER_Y_SORT_ORIGIN, + DIRTY_FLAGS_LAYER_Z_INDEX, + DIRTY_FLAGS_LAYER_NAVIGATION_ENABLED, + DIRTY_FLAGS_LAYER_INDEX_IN_TILE_MAP_NODE, + DIRTY_FLAGS_TILE_MAP_IN_TREE, + DIRTY_FLAGS_TILE_MAP_IN_CANVAS, + DIRTY_FLAGS_TILE_MAP_VISIBILITY, + DIRTY_FLAGS_TILE_MAP_XFORM, + DIRTY_FLAGS_TILE_MAP_LOCAL_XFORM, + DIRTY_FLAGS_TILE_MAP_SELECTED_LAYER, + DIRTY_FLAGS_TILE_MAP_LIGHT_MASK, + DIRTY_FLAGS_TILE_MAP_MATERIAL, + DIRTY_FLAGS_TILE_MAP_USE_PARENT_MATERIAL, + DIRTY_FLAGS_TILE_MAP_TEXTURE_FILTER, + DIRTY_FLAGS_TILE_MAP_TEXTURE_REPEAT, + DIRTY_FLAGS_TILE_MAP_TILE_SET, + DIRTY_FLAGS_TILE_MAP_QUADRANT_SIZE, + DIRTY_FLAGS_TILE_MAP_COLLISION_ANIMATABLE, + DIRTY_FLAGS_TILE_MAP_COLLISION_VISIBILITY_MODE, + DIRTY_FLAGS_TILE_MAP_NAVIGATION_VISIBILITY_MODE, + DIRTY_FLAGS_TILE_MAP_Y_SORT_ENABLED, + DIRTY_FLAGS_TILE_MAP_RUNTIME_UPDATE, + DIRTY_FLAGS_MAX, + }; + +private: + // Exposed properties. + String name; + bool enabled = true; + Color modulate = Color(1, 1, 1, 1); + bool y_sort_enabled = false; + int y_sort_origin = 0; + int z_index = 0; + bool navigation_enabled = true; + RID navigation_map; + bool uses_world_navigation_map = false; + + // Internal. + TileMap *tile_map_node = nullptr; + int layer_index_in_tile_map_node = -1; + RID canvas_item; + HashMap<Vector2i, CellData> tile_map; + + // Dirty flag. Allows knowing what was modified since the last update. + struct { + bool flags[DIRTY_FLAGS_MAX] = { false }; + SelfList<CellData>::List cell_list; + } dirty; + bool in_destructor = false; + + // Rect cache. + mutable Rect2 rect_cache; + mutable bool rect_cache_dirty = true; + mutable Rect2i used_rect_cache; + mutable bool used_rect_cache_dirty = true; + + // Runtime tile data. + bool _runtime_update_tile_data_was_cleaned_up = false; + void _build_runtime_update_tile_data(); + void _build_runtime_update_tile_data_for_cell(CellData &r_cell_data, bool p_auto_add_to_dirty_list = false); + void _clear_runtime_update_tile_data(); + + // Per-system methods. +#ifdef DEBUG_ENABLED + HashMap<Vector2i, Ref<DebugQuadrant>> debug_quadrant_map; + Vector2i _coords_to_debug_quadrant_coords(const Vector2i &p_coords) const; + bool _debug_was_cleaned_up = false; + void _debug_update(); + void _debug_quadrants_update_cell(CellData &r_cell_data, SelfList<DebugQuadrant>::List &r_dirty_debug_quadrant_list); +#endif // DEBUG_ENABLED + + HashMap<Vector2i, Ref<RenderingQuadrant>> rendering_quadrant_map; + bool _rendering_was_cleaned_up = false; + void _rendering_update(); + void _rendering_quadrants_update_cell(CellData &r_cell_data, SelfList<RenderingQuadrant>::List &r_dirty_rendering_quadrant_list); + void _rendering_occluders_clear_cell(CellData &r_cell_data); + void _rendering_occluders_update_cell(CellData &r_cell_data); +#ifdef DEBUG_ENABLED + void _rendering_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); +#endif // DEBUG_ENABLED + + HashMap<RID, Vector2i> bodies_coords; // Mapping for RID to coords. + bool _physics_was_cleaned_up = false; + void _physics_update(); + void _physics_notify_tilemap_change(DirtyFlags p_what); + void _physics_clear_cell(CellData &r_cell_data); + void _physics_update_cell(CellData &r_cell_data); +#ifdef DEBUG_ENABLED + void _physics_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); +#endif // DEBUG_ENABLED + + bool _navigation_was_cleaned_up = false; + void _navigation_update(); + void _navigation_clear_cell(CellData &r_cell_data); + void _navigation_update_cell(CellData &r_cell_data); +#ifdef DEBUG_ENABLED + void _navigation_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); +#endif // DEBUG_ENABLED + + bool _scenes_was_cleaned_up = false; + void _scenes_update(); + void _scenes_clear_cell(CellData &r_cell_data); + void _scenes_update_cell(CellData &r_cell_data); +#ifdef DEBUG_ENABLED + void _scenes_draw_cell_debug(const RID &p_canvas_item, const Vector2i &p_quadrant_pos, const CellData &r_cell_data); +#endif // DEBUG_ENABLED + + // Terrains. + TileSet::TerrainsPattern _get_best_terrain_pattern_for_constraints(int p_terrain_set, const Vector2i &p_position, const RBSet<TerrainConstraint> &p_constraints, TileSet::TerrainsPattern p_current_pattern); + RBSet<TerrainConstraint> _get_terrain_constraints_from_added_pattern(const Vector2i &p_position, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern) const; + RBSet<TerrainConstraint> _get_terrain_constraints_from_painted_cells_list(const RBSet<Vector2i> &p_painted, int p_terrain_set, bool p_ignore_empty_terrains) const; + +public: + // TileMap node. + void set_tile_map(TileMap *p_tile_map); + void set_layer_index_in_tile_map_node(int p_index); + + // Rect caching. + Rect2 get_rect(bool &r_changed) const; + + // Terrains. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_constraints(const Vector<Vector2i> &p_to_replace, int p_terrain_set, const RBSet<TerrainConstraint> &p_constraints); // Not exposed. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_connect(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_path(const Vector<Vector2i> &p_coords_array, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); // Not exposed. + HashMap<Vector2i, TileSet::TerrainsPattern> terrain_fill_pattern(const Vector<Vector2i> &p_coords_array, int p_terrain_set, TileSet::TerrainsPattern p_terrains_pattern, bool p_ignore_empty_terrains = true); // Not exposed. + + // Not exposed to users. + TileMapCell get_cell(const Vector2i &p_coords, bool p_use_proxies = false) const; + + // For TileMap node's use. + void set_tile_data(TileMapDataFormat p_format, const Vector<int> &p_data); + Vector<int> get_tile_data() const; + void notify_tile_map_change(DirtyFlags p_what); + void internal_update(); + + // --- Exposed in TileMap --- + + // Cells manipulation. + void set_cell(const Vector2i &p_coords, int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = 0); + void erase_cell(const Vector2i &p_coords); + + int get_cell_source_id(const Vector2i &p_coords, bool p_use_proxies = false) const; + Vector2i get_cell_atlas_coords(const Vector2i &p_coords, bool p_use_proxies = false) const; + int get_cell_alternative_tile(const Vector2i &p_coords, bool p_use_proxies = false) const; + TileData *get_cell_tile_data(const Vector2i &p_coords, bool p_use_proxies = false) const; // Helper method to make accessing the data easier. + void clear(); + + // Patterns. + Ref<TileMapPattern> get_pattern(TypedArray<Vector2i> p_coords_array); + void set_pattern(const Vector2i &p_position, const Ref<TileMapPattern> p_pattern); + + // Terrains. + void set_cells_terrain_connect(TypedArray<Vector2i> p_cells, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); + void set_cells_terrain_path(TypedArray<Vector2i> p_path, int p_terrain_set, int p_terrain, bool p_ignore_empty_terrains = true); + + // Cells usage. + TypedArray<Vector2i> get_used_cells() const; + TypedArray<Vector2i> get_used_cells_by_id(int p_source_id = TileSet::INVALID_SOURCE, const Vector2i p_atlas_coords = TileSetSource::INVALID_ATLAS_COORDS, int p_alternative_tile = TileSetSource::INVALID_TILE_ALTERNATIVE) const; + Rect2i get_used_rect() const; + + // Layer properties. + void set_name(String p_name); + String get_name() const; + void set_enabled(bool p_enabled); + bool is_enabled() const; + void set_modulate(Color p_modulate); + Color get_modulate() const; + void set_y_sort_enabled(bool p_y_sort_enabled); + bool is_y_sort_enabled() const; + void set_y_sort_origin(int p_y_sort_origin); + int get_y_sort_origin() const; + void set_z_index(int p_z_index); + int get_z_index() const; + void set_navigation_enabled(bool p_enabled); + bool is_navigation_enabled() const; + void set_navigation_map(RID p_map); + RID get_navigation_map() const; + + // Fixing and clearing methods. + void fix_invalid_tiles(); + + // Find coords for body. + bool has_body_rid(RID p_physics_body) const; + Vector2i get_coords_for_body_rid(RID p_physics_body) const; // For finding tiles from collision. + + ~TileMapLayer(); +}; + +#endif // TILE_MAP_LAYER_H diff --git a/scene/3d/collision_object_3d.cpp b/scene/3d/collision_object_3d.cpp index 4562ecfb5f..97b1e282ad 100644 --- a/scene/3d/collision_object_3d.cpp +++ b/scene/3d/collision_object_3d.cpp @@ -295,16 +295,12 @@ void CollisionObject3D::_input_event_call(Camera3D *p_camera, const Ref<InputEve } void CollisionObject3D::_mouse_enter() { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_enter); - } + GDVIRTUAL_CALL(_mouse_enter); emit_signal(SceneStringNames::get_singleton()->mouse_entered); } void CollisionObject3D::_mouse_exit() { - if (get_script_instance()) { - get_script_instance()->call(SceneStringNames::get_singleton()->_mouse_exit); - } + GDVIRTUAL_CALL(_mouse_exit); emit_signal(SceneStringNames::get_singleton()->mouse_exited); } diff --git a/scene/3d/gpu_particles_3d.cpp b/scene/3d/gpu_particles_3d.cpp index fc84b3308e..dfb039d709 100644 --- a/scene/3d/gpu_particles_3d.cpp +++ b/scene/3d/gpu_particles_3d.cpp @@ -460,7 +460,12 @@ void GPUParticles3D::_notification(int p_what) { // Use internal process when emitting and one_shot is on so that when // the shot ends the editor can properly update. case NOTIFICATION_INTERNAL_PROCESS: { - RS::get_singleton()->particles_set_emitter_velocity(particles, (get_global_position() - previous_position) / get_process_delta_time()); + const Vector3 velocity = (get_global_position() - previous_position) / get_process_delta_time(); + + if (velocity != previous_velocity) { + RS::get_singleton()->particles_set_emitter_velocity(particles, velocity); + previous_velocity = velocity; + } previous_position = get_global_position(); if (one_shot) { diff --git a/scene/3d/gpu_particles_3d.h b/scene/3d/gpu_particles_3d.h index ae9349817c..0c9f2c1378 100644 --- a/scene/3d/gpu_particles_3d.h +++ b/scene/3d/gpu_particles_3d.h @@ -95,6 +95,7 @@ private: double emission_time = 0.0; double active_time = 0.0; float interp_to_end_factor = 0; + Vector3 previous_velocity; Vector3 previous_position; void _attach_sub_emitter(); diff --git a/scene/3d/lightmap_gi.cpp b/scene/3d/lightmap_gi.cpp index 76933cd956..92b783392d 100644 --- a/scene/3d/lightmap_gi.cpp +++ b/scene/3d/lightmap_gi.cpp @@ -734,15 +734,15 @@ LightmapGI::BakeError LightmapGI::bake(Node *p_from_node, String p_image_data_pa MeshesFound &mf = meshes_found.write[m_i]; - Size2i lightmap_size = mf.mesh->get_lightmap_size_hint(); - - if (lightmap_size == Size2i(0, 0)) { + Size2i mesh_lightmap_size = mf.mesh->get_lightmap_size_hint(); + if (mesh_lightmap_size == Size2i(0, 0)) { // TODO we should compute a size if no lightmap hint is set, as we did in 3.x. // For now set to basic size to avoid crash. - lightmap_size = Size2i(64, 64); + mesh_lightmap_size = Size2i(64, 64); } + Size2i lightmap_size = Size2i(Size2(mesh_lightmap_size) * mf.lightmap_scale * texel_scale); + ERR_FAIL_COND_V(lightmap_size.x == 0 || lightmap_size.y == 0, BAKE_ERROR_LIGHTMAP_TOO_SMALL); - lightmap_size *= mf.lightmap_scale; TypedArray<RID> overrides; overrides.resize(mf.overrides.size()); for (int i = 0; i < mf.overrides.size(); i++) { @@ -1493,6 +1493,15 @@ float LightmapGI::get_bias() const { return bias; } +void LightmapGI::set_texel_scale(float p_multiplier) { + ERR_FAIL_COND(p_multiplier < (0.01 - CMP_EPSILON)); + texel_scale = p_multiplier; +} + +float LightmapGI::get_texel_scale() const { + return texel_scale; +} + void LightmapGI::set_max_texture_size(int p_size) { ERR_FAIL_COND_MSG(p_size < 2048, vformat("The LightmapGI maximum texture size supplied (%d) is too small. The minimum allowed value is 2048.", p_size)); ERR_FAIL_COND_MSG(p_size > 16384, vformat("The LightmapGI maximum texture size supplied (%d) is too large. The maximum allowed value is 16384.", p_size)); @@ -1576,6 +1585,9 @@ void LightmapGI::_bind_methods() { ClassDB::bind_method(D_METHOD("set_environment_custom_energy", "energy"), &LightmapGI::set_environment_custom_energy); ClassDB::bind_method(D_METHOD("get_environment_custom_energy"), &LightmapGI::get_environment_custom_energy); + ClassDB::bind_method(D_METHOD("set_texel_scale", "texel_scale"), &LightmapGI::set_texel_scale); + ClassDB::bind_method(D_METHOD("get_texel_scale"), &LightmapGI::get_texel_scale); + ClassDB::bind_method(D_METHOD("set_max_texture_size", "max_texture_size"), &LightmapGI::set_max_texture_size); ClassDB::bind_method(D_METHOD("get_max_texture_size"), &LightmapGI::get_max_texture_size); @@ -1609,6 +1621,7 @@ void LightmapGI::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_denoiser"), "set_use_denoiser", "is_using_denoiser"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "denoiser_strength", PROPERTY_HINT_RANGE, "0.001,0.2,0.001,or_greater"), "set_denoiser_strength", "get_denoiser_strength"); ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "bias", PROPERTY_HINT_RANGE, "0.00001,0.1,0.00001,or_greater"), "set_bias", "get_bias"); + ADD_PROPERTY(PropertyInfo(Variant::FLOAT, "texel_scale", PROPERTY_HINT_RANGE, "0.01,100.0,0.01"), "set_texel_scale", "get_texel_scale"); ADD_PROPERTY(PropertyInfo(Variant::INT, "max_texture_size", PROPERTY_HINT_RANGE, "2048,16384,1"), "set_max_texture_size", "get_max_texture_size"); ADD_GROUP("Environment", "environment_"); ADD_PROPERTY(PropertyInfo(Variant::INT, "environment_mode", PROPERTY_HINT_ENUM, "Disabled,Scene,Custom Sky,Custom Color"), "set_environment_mode", "get_environment_mode"); diff --git a/scene/3d/lightmap_gi.h b/scene/3d/lightmap_gi.h index fec0075693..765e4a731d 100644 --- a/scene/3d/lightmap_gi.h +++ b/scene/3d/lightmap_gi.h @@ -142,6 +142,7 @@ public: BAKE_ERROR_CANT_CREATE_IMAGE, BAKE_ERROR_USER_ABORTED, BAKE_ERROR_TEXTURE_SIZE_TOO_SMALL, + BAKE_ERROR_LIGHTMAP_TOO_SMALL, }; enum EnvironmentMode { @@ -158,6 +159,7 @@ private: int bounces = 3; float bounce_indirect_energy = 1.0; float bias = 0.0005; + float texel_scale = 1.0; int max_texture_size = 16384; bool interior = false; EnvironmentMode environment_mode = ENVIRONMENT_MODE_SCENE; @@ -284,6 +286,9 @@ public: void set_bias(float p_bias); float get_bias() const; + void set_texel_scale(float p_multiplier); + float get_texel_scale() const; + void set_max_texture_size(int p_size); int get_max_texture_size() const; diff --git a/scene/3d/visual_instance_3d.cpp b/scene/3d/visual_instance_3d.cpp index 33f865382d..503c39ae3e 100644 --- a/scene/3d/visual_instance_3d.cpp +++ b/scene/3d/visual_instance_3d.cpp @@ -250,7 +250,7 @@ GeometryInstance3D::VisibilityRangeFadeMode GeometryInstance3D::get_visibility_r return visibility_range_fade_mode; } -const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName p_name) const { +const StringName *GeometryInstance3D::_instance_uniform_get_remap(const StringName &p_name) const { StringName *r = instance_shader_parameter_property_remap.getptr(p_name); if (!r) { String s = p_name; diff --git a/scene/3d/visual_instance_3d.h b/scene/3d/visual_instance_3d.h index ef0f7966e2..59ede26ac1 100644 --- a/scene/3d/visual_instance_3d.h +++ b/scene/3d/visual_instance_3d.h @@ -135,7 +135,7 @@ private: GIMode gi_mode = GI_MODE_STATIC; bool ignore_occlusion_culling = false; - const StringName *_instance_uniform_get_remap(const StringName p_name) const; + const StringName *_instance_uniform_get_remap(const StringName &p_name) const; protected: bool _set(const StringName &p_name, const Variant &p_value); diff --git a/scene/3d/xr_nodes.cpp b/scene/3d/xr_nodes.cpp index c12e78eddc..70e32c1a31 100644 --- a/scene/3d/xr_nodes.cpp +++ b/scene/3d/xr_nodes.cpp @@ -58,13 +58,13 @@ void XRCamera3D::_unbind_tracker() { tracker.unref(); } -void XRCamera3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { +void XRCamera3D::_changed_tracker(const StringName &p_tracker_name, int p_tracker_type) { if (p_tracker_name == tracker_name) { _bind_tracker(); } } -void XRCamera3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { +void XRCamera3D::_removed_tracker(const StringName &p_tracker_name, int p_tracker_type) { if (p_tracker_name == tracker_name) { _unbind_tracker(); } @@ -258,7 +258,7 @@ void XRNode3D::_validate_property(PropertyInfo &p_property) const { } } -void XRNode3D::set_tracker(const StringName p_tracker_name) { +void XRNode3D::set_tracker(const StringName &p_tracker_name) { if (tracker.is_valid() && tracker->get_tracker_name() == p_tracker_name) { // didn't change return; @@ -282,7 +282,7 @@ StringName XRNode3D::get_tracker() const { return tracker_name; } -void XRNode3D::set_pose_name(const StringName p_pose_name) { +void XRNode3D::set_pose_name(const StringName &p_pose_name) { pose_name = p_pose_name; // Update pose if we are bound to a tracker with a valid pose @@ -363,7 +363,7 @@ void XRNode3D::_unbind_tracker() { } } -void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_type) { +void XRNode3D::_changed_tracker(const StringName &p_tracker_name, int p_tracker_type) { if (tracker_name == p_tracker_name) { // just in case unref our current tracker _unbind_tracker(); @@ -373,7 +373,7 @@ void XRNode3D::_changed_tracker(const StringName p_tracker_name, int p_tracker_t } } -void XRNode3D::_removed_tracker(const StringName p_tracker_name, int p_tracker_type) { +void XRNode3D::_removed_tracker(const StringName &p_tracker_name, int p_tracker_type) { if (tracker_name == p_tracker_name) { // unref our tracker, it's no longer available _unbind_tracker(); diff --git a/scene/3d/xr_nodes.h b/scene/3d/xr_nodes.h index 185a6361d3..ad52cf113d 100644 --- a/scene/3d/xr_nodes.h +++ b/scene/3d/xr_nodes.h @@ -50,8 +50,8 @@ protected: void _bind_tracker(); void _unbind_tracker(); - void _changed_tracker(const StringName p_tracker_name, int p_tracker_type); - void _removed_tracker(const StringName p_tracker_name, int p_tracker_type); + void _changed_tracker(const StringName &p_tracker_name, int p_tracker_type); + void _removed_tracker(const StringName &p_tracker_name, int p_tracker_type); void _pose_changed(const Ref<XRPose> &p_pose); public: @@ -87,8 +87,8 @@ protected: virtual void _bind_tracker(); virtual void _unbind_tracker(); - void _changed_tracker(const StringName p_tracker_name, int p_tracker_type); - void _removed_tracker(const StringName p_tracker_name, int p_tracker_type); + void _changed_tracker(const StringName &p_tracker_name, int p_tracker_type); + void _removed_tracker(const StringName &p_tracker_name, int p_tracker_type); void _pose_changed(const Ref<XRPose> &p_pose); void _pose_lost_tracking(const Ref<XRPose> &p_pose); @@ -96,10 +96,10 @@ protected: public: void _validate_property(PropertyInfo &p_property) const; - void set_tracker(const StringName p_tracker_name); + void set_tracker(const StringName &p_tracker_name); StringName get_tracker() const; - void set_pose_name(const StringName p_pose); + void set_pose_name(const StringName &p_pose); StringName get_pose_name() const; bool get_is_active() const; diff --git a/scene/animation/animation_mixer.compat.inc b/scene/animation/animation_mixer.compat.inc new file mode 100644 index 0000000000..09c7d7a977 --- /dev/null +++ b/scene/animation/animation_mixer.compat.inc @@ -0,0 +1,44 @@ +/**************************************************************************/ +/* animation_mixer.compat.inc */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef DISABLE_DEPRECATED + +Variant AnimationMixer::_post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx) { + if (!p_object) { + return Variant(); + } + return _post_process_key_value(p_anim, p_track, p_value, p_object->get_instance_id(), p_object_idx); +} + +void AnimationMixer::_bind_compatibility_methods() { + ClassDB::bind_compatibility_method(D_METHOD("_post_process_key_value_bind_compat", "animation", "track", "value", "object", "object_idx"), &AnimationMixer::_post_process_key_value_bind_compat_86687); +} + +#endif // DISABLE_DEPRECATED diff --git a/scene/animation/animation_mixer.cpp b/scene/animation/animation_mixer.cpp index da00d1dd7b..faf1153178 100644 --- a/scene/animation/animation_mixer.cpp +++ b/scene/animation/animation_mixer.cpp @@ -29,8 +29,12 @@ /**************************************************************************/ #include "animation_mixer.h" +#include "animation_mixer.compat.inc" #include "core/config/engine.h" +#include "scene/3d/mesh_instance_3d.h" +#include "scene/3d/node_3d.h" +#include "scene/3d/skeleton_3d.h" #include "scene/animation/animation_player.h" #include "scene/resources/animation.h" #include "scene/scene_string_names.h" @@ -543,7 +547,7 @@ void AnimationMixer::_clear_caches() { _init_root_motion_cache(); _clear_audio_streams(); _clear_playing_caches(); - for (KeyValue<NodePath, TrackCache *> &K : track_cache) { + for (KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { memdelete(K.value); } track_cache.clear(); @@ -562,8 +566,9 @@ void AnimationMixer::_clear_audio_streams() { void AnimationMixer::_clear_playing_caches() { for (const TrackCache *E : playing_caches) { - if (ObjectDB::get_instance(E->object_id)) { - E->object->call(SNAME("stop"), true); + Object *t_obj = ObjectDB::get_instance(E->object_id); + if (t_obj) { + t_obj->call(SNAME("stop"), true); } } playing_caches.clear(); @@ -606,23 +611,20 @@ bool AnimationMixer::_update_caches() { Ref<Animation> anim = get_animation(E); for (int i = 0; i < anim->get_track_count(); i++) { NodePath path = anim->track_get_path(i); - Animation::TrackType track_type = anim->track_get_type(i); - - Animation::TrackType track_cache_type = track_type; - if (track_cache_type == Animation::TYPE_POSITION_3D || track_cache_type == Animation::TYPE_ROTATION_3D || track_cache_type == Animation::TYPE_SCALE_3D) { - track_cache_type = Animation::TYPE_POSITION_3D; // Reference them as position3D tracks, even if they modify rotation or scale. - } + Animation::TypeHash thash = anim->track_get_type_hash(i); + Animation::TrackType track_src_type = anim->track_get_type(i); + Animation::TrackType track_cache_type = Animation::get_cache_type(track_src_type); TrackCache *track = nullptr; - if (track_cache.has(path)) { - track = track_cache.get(path); + if (track_cache.has(thash)) { + track = track_cache.get(thash); } // If not valid, delete track. if (track && (track->type != track_cache_type || ObjectDB::get_instance(track->object_id) == nullptr)) { playing_caches.erase(track); memdelete(track); - track_cache.erase(path); + track_cache.erase(thash); track = nullptr; } @@ -636,7 +638,8 @@ bool AnimationMixer::_update_caches() { continue; } - switch (track_type) { + switch (track_src_type) { + case Animation::TYPE_BEZIER: case Animation::TYPE_VALUE: { // If a value track without a key is cached first, the initial value cannot be determined. // It is a corner case, but which may cause problems with blending. @@ -645,16 +648,20 @@ bool AnimationMixer::_update_caches() { TrackCacheValue *track_value = memnew(TrackCacheValue); if (resource.is_valid()) { - track_value->object = resource.ptr(); + track_value->object_id = resource->get_instance_id(); } else { - track_value->object = child; + track_value->object_id = child->get_instance_id(); } - track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE; - track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + if (track_src_type == Animation::TYPE_VALUE) { + track_value->is_continuous = anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE; + track_value->is_using_angle = anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + } else { + track_value->is_continuous = true; + track_value->is_using_angle = false; + } track_value->subpath = leftover_path; - track_value->object_id = track_value->object->get_instance_id(); track = track_value; @@ -663,9 +670,9 @@ bool AnimationMixer::_update_caches() { // If there is a Reset Animation, it takes precedence by overwriting. if (has_reset_anim) { - int rt = reset_anim->find_track(path, track_type); + int rt = reset_anim->find_track(path, track_src_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_value->init_value = reset_anim->track_get_key_value(rt, 0); + track_value->init_value = track_src_type == Animation::TYPE_VALUE ? reset_anim->track_get_key_value(rt, 0) : (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; } } @@ -684,14 +691,12 @@ bool AnimationMixer::_update_caches() { TrackCacheTransform *track_xform = memnew(TrackCacheTransform); track_xform->type = Animation::TYPE_POSITION_3D; - track_xform->node_3d = node_3d; - track_xform->skeleton = nullptr; track_xform->bone_idx = -1; bool has_rest = false; - if (path.get_subname_count() == 1 && Object::cast_to<Skeleton3D>(node_3d)) { - Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d); - track_xform->skeleton = sk; + Skeleton3D *sk = Object::cast_to<Skeleton3D>(node_3d); + if (sk && path.get_subname_count() == 1) { + track_xform->skeleton_id = sk->get_instance_id(); int bone_idx = sk->find_bone(path.get_subname(0)); if (bone_idx != -1) { has_rest = true; @@ -703,12 +708,11 @@ bool AnimationMixer::_update_caches() { } } - track_xform->object = node_3d; - track_xform->object_id = track_xform->object->get_instance_id(); + track_xform->object_id = node_3d->get_instance_id(); track = track_xform; - switch (track_type) { + switch (track_src_type) { case Animation::TYPE_POSITION_3D: { track_xform->loc_used = true; } break; @@ -724,9 +728,9 @@ bool AnimationMixer::_update_caches() { // For non Skeleton3D bone animation. if (has_reset_anim && !has_rest) { - int rt = reset_anim->find_track(path, track_type); + int rt = reset_anim->find_track(path, track_src_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - switch (track_type) { + switch (track_src_type) { case Animation::TYPE_POSITION_3D: { track_xform->init_loc = reset_anim->track_get_key_value(rt, 0); } break; @@ -765,15 +769,12 @@ bool AnimationMixer::_update_caches() { TrackCacheBlendShape *track_bshape = memnew(TrackCacheBlendShape); - track_bshape->mesh_3d = mesh_3d; track_bshape->shape_index = blend_shape_idx; - - track_bshape->object = mesh_3d; track_bshape->object_id = mesh_3d->get_instance_id(); track = track_bshape; if (has_reset_anim) { - int rt = reset_anim->find_track(path, track_type); + int rt = reset_anim->find_track(path, track_src_type); if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { track_bshape->init_value = reset_anim->track_get_key_value(rt, 0); } @@ -784,43 +785,18 @@ bool AnimationMixer::_update_caches() { TrackCacheMethod *track_method = memnew(TrackCacheMethod); if (resource.is_valid()) { - track_method->object = resource.ptr(); + track_method->object_id = resource->get_instance_id(); } else { - track_method->object = child; + track_method->object_id = child->get_instance_id(); } - track_method->object_id = track_method->object->get_instance_id(); - track = track_method; } break; - case Animation::TYPE_BEZIER: { - TrackCacheBezier *track_bezier = memnew(TrackCacheBezier); - - if (resource.is_valid()) { - track_bezier->object = resource.ptr(); - } else { - track_bezier->object = child; - } - - track_bezier->subpath = leftover_path; - track_bezier->object_id = track_bezier->object->get_instance_id(); - - track = track_bezier; - - if (has_reset_anim) { - int rt = reset_anim->find_track(path, track_type); - if (rt >= 0 && reset_anim->track_get_key_count(rt) > 0) { - track_bezier->init_value = (reset_anim->track_get_key_value(rt, 0).operator Array())[0]; - } - } - - } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *track_audio = memnew(TrackCacheAudio); - track_audio->object = child; - track_audio->object_id = track_audio->object->get_instance_id(); + track_audio->object_id = child->get_instance_id(); track_audio->audio_stream.instantiate(); track_audio->audio_stream->set_polyphony(audio_max_polyphony); @@ -830,8 +806,7 @@ bool AnimationMixer::_update_caches() { case Animation::TYPE_ANIMATION: { TrackCacheAnimation *track_animation = memnew(TrackCacheAnimation); - track_animation->object = child; - track_animation->object_id = track_animation->object->get_instance_id(); + track_animation->object_id = child->get_instance_id(); track = track_animation; @@ -841,8 +816,8 @@ bool AnimationMixer::_update_caches() { continue; } } - - track_cache[path] = track; + track->path = path; + track_cache[thash] = track; } else if (track_cache_type == Animation::TYPE_POSITION_3D) { TrackCacheTransform *track_xform = static_cast<TrackCacheTransform *>(track); if (track->setup_pass != setup_pass) { @@ -850,7 +825,7 @@ bool AnimationMixer::_update_caches() { track_xform->rot_used = false; track_xform->scale_used = false; } - switch (track_type) { + switch (track_src_type) { case Animation::TYPE_POSITION_3D: { track_xform->loc_used = true; } break; @@ -868,8 +843,13 @@ bool AnimationMixer::_update_caches() { TrackCacheValue *track_value = static_cast<TrackCacheValue *>(track); bool was_continuous = track_value->is_continuous; bool was_using_angle = track_value->is_using_angle; - track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE; - track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + + if (track_src_type == Animation::TYPE_VALUE) { + track_value->is_continuous |= anim->value_track_get_update_mode(i) != Animation::UPDATE_DISCRETE; + track_value->is_using_angle |= anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_LINEAR_ANGLE || anim->track_get_interpolation_type(i) == Animation::INTERPOLATION_CUBIC_ANGLE; + } else { + track_value->is_continuous |= true; + } // TODO: Currently, misc type cannot be blended. // In the future, it should have a separate blend weight, just as bool is converted to 0 and 1. @@ -898,27 +878,26 @@ bool AnimationMixer::_update_caches() { } } - List<NodePath> to_delete; + List<Animation::TypeHash> to_delete; - for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { - TrackCache *tc = track_cache[K.key]; - if (tc->setup_pass != setup_pass) { + for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { + if (K.value->setup_pass != setup_pass) { to_delete.push_back(K.key); } } while (to_delete.front()) { - NodePath np = to_delete.front()->get(); - memdelete(track_cache[np]); - track_cache.erase(np); + Animation::TypeHash thash = to_delete.front()->get(); + memdelete(track_cache[thash]); + track_cache.erase(thash); to_delete.pop_front(); } track_map.clear(); int idx = 0; - for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { - track_map[K.key] = idx; + for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { + track_map[K.value->path] = idx; idx++; } @@ -944,28 +923,30 @@ void AnimationMixer::_process_animation(double p_delta, bool p_update_only) { clear_animation_instances(); } -Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { +Variant AnimationMixer::post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) { Variant res; - if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, const_cast<Object *>(p_object), p_object_idx, res)) { + if (GDVIRTUAL_CALL(_post_process_key_value, p_anim, p_track, p_value, p_object_id, p_object_sub_idx, res)) { return res; } - return _post_process_key_value(p_anim, p_track, p_value, p_object, p_object_idx); + return _post_process_key_value(p_anim, p_track, p_value, p_object_id, p_object_sub_idx); } -Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx) { - switch (p_anim->track_get_type(p_track)) { +Variant AnimationMixer::_post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx) { #ifndef _3D_DISABLED + switch (p_anim->track_get_type(p_track)) { case Animation::TYPE_POSITION_3D: { - if (p_object_idx >= 0) { - const Skeleton3D *skel = Object::cast_to<Skeleton3D>(p_object); - return Vector3(p_value) * skel->get_motion_scale(); + if (p_object_sub_idx >= 0) { + Skeleton3D *skel = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(p_object_id)); + if (skel) { + return Vector3(p_value) * skel->get_motion_scale(); + } } return p_value; } break; -#endif // _3D_DISABLED default: { } break; } +#endif // _3D_DISABLED return p_value; } @@ -985,7 +966,7 @@ void AnimationMixer::_blend_init() { } // Init all value/transform/blend/bezier tracks that track_cache has. - for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { TrackCache *track = K.value; track->total_weight = 0.0; @@ -1011,10 +992,6 @@ void AnimationMixer::_blend_init() { t->value = Animation::cast_to_blendwise(t->init_value); t->element_size = t->init_value.is_string() ? (real_t)(t->init_value.operator String()).length() : 0; } break; - case Animation::TYPE_BEZIER: { - TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); - t->value = t->init_value; - } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); for (KeyValue<ObjectID, PlayingAudioTrackInfo> &L : t->playing_streams) { @@ -1046,12 +1023,12 @@ void AnimationMixer::_blend_calc_total_weight() { if (!a->track_is_enabled(i)) { continue; } - NodePath path = a->track_get_path(i); - if (!track_cache.has(path)) { + Animation::TypeHash thash = a->track_get_type_hash(i); + if (!track_cache.has(thash)) { continue; // No path, but avoid error spamming. } - TrackCache *track = track_cache[path]; - int blend_idx = track_map[path]; + TrackCache *track = track_cache[thash]; + int blend_idx = track_map[track->path]; if (processed_indices.has(blend_idx)) { continue; // There is the case different track type with same path... Is there more faster iterating way than has()? } @@ -1086,13 +1063,13 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (!a->track_is_enabled(i)) { continue; } - NodePath path = a->track_get_path(i); - if (!track_cache.has(path)) { + Animation::TypeHash thash = a->track_get_type_hash(i); + if (!track_cache.has(thash)) { continue; // No path, but avoid error spamming. } - TrackCache *track = track_cache[path]; - ERR_CONTINUE(!track_map.has(path)); - int blend_idx = track_map[path]; + TrackCache *track = track_cache[thash]; + ERR_CONTINUE(!track_map.has(track->path)); + int blend_idx = track_map[track->path]; ERR_CONTINUE(blend_idx < 0 || blend_idx >= track_count); real_t blend = blend_idx < track_weights.size() ? track_weights[blend_idx] * weight : weight; if (!deterministic) { @@ -1104,11 +1081,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { blend = blend / track->total_weight; } Animation::TrackType ttype = a->track_get_type(i); - if (ttype != Animation::TYPE_POSITION_3D && ttype != Animation::TYPE_ROTATION_3D && ttype != Animation::TYPE_SCALE_3D && track->type != ttype) { - // Broken animation, but avoid error spamming. - continue; - } - track->root_motion = root_motion_track == path; + track->root_motion = root_motion_track == a->track_get_path(i); switch (ttype) { case Animation::TYPE_POSITION_3D: { #ifndef _3D_DISABLED @@ -1158,9 +1131,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); a->try_position_track_interpolate(i, (double)a->get_length(), &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; prev_time = 0; } @@ -1170,9 +1143,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); a->try_position_track_interpolate(i, 0, &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; prev_time = (double)a->get_length(); } @@ -1181,9 +1154,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - loc[0] = post_process_key_value(a, i, loc[0], t->object, t->bone_idx); + loc[0] = post_process_key_value(a, i, loc[0], t->object_id, t->bone_idx); a->try_position_track_interpolate(i, time, &loc[1]); - loc[1] = post_process_key_value(a, i, loc[1], t->object, t->bone_idx); + loc[1] = post_process_key_value(a, i, loc[1], t->object_id, t->bone_idx); root_motion_cache.loc += (loc[1] - loc[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); } @@ -1193,7 +1166,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - loc = post_process_key_value(a, i, loc, t->object, t->bone_idx); + loc = post_process_key_value(a, i, loc, t->object_id, t->bone_idx); t->loc += (loc - t->init_loc) * blend; } #endif // _3D_DISABLED @@ -1246,9 +1219,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); a->try_rotation_track_interpolate(i, (double)a->get_length(), &rot[1]); - rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = 0; } @@ -1258,7 +1231,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); a->try_rotation_track_interpolate(i, 0, &rot[1]); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = (double)a->get_length(); @@ -1268,9 +1241,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - rot[0] = post_process_key_value(a, i, rot[0], t->object, t->bone_idx); + rot[0] = post_process_key_value(a, i, rot[0], t->object_id, t->bone_idx); a->try_rotation_track_interpolate(i, time, &rot[1]); - rot[1] = post_process_key_value(a, i, rot[1], t->object, t->bone_idx); + rot[1] = post_process_key_value(a, i, rot[1], t->object_id, t->bone_idx); root_motion_cache.rot = (root_motion_cache.rot * Quaternion().slerp(rot[0].inverse() * rot[1], blend)).normalized(); prev_time = !backward ? 0 : (double)a->get_length(); } @@ -1280,7 +1253,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - rot = post_process_key_value(a, i, rot, t->object, t->bone_idx); + rot = post_process_key_value(a, i, rot, t->object_id, t->bone_idx); t->rot = (t->rot * Quaternion().slerp(t->init_rot.inverse() * rot, blend)).normalized(); } #endif // _3D_DISABLED @@ -1333,10 +1306,10 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx); a->try_scale_track_interpolate(i, (double)a->get_length(), &scale[1]); root_motion_cache.scale += (scale[1] - scale[0]) * blend; - scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); prev_time = 0; } } else { @@ -1345,9 +1318,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx); a->try_scale_track_interpolate(i, 0, &scale[1]); - scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); root_motion_cache.scale += (scale[1] - scale[0]) * blend; prev_time = (double)a->get_length(); } @@ -1356,9 +1329,9 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - scale[0] = post_process_key_value(a, i, scale[0], t->object, t->bone_idx); + scale[0] = post_process_key_value(a, i, scale[0], t->object_id, t->bone_idx); a->try_scale_track_interpolate(i, time, &scale[1]); - scale[1] = post_process_key_value(a, i, scale[1], t->object, t->bone_idx); + scale[1] = post_process_key_value(a, i, scale[1], t->object_id, t->bone_idx); root_motion_cache.scale += (scale[1] - scale[0]) * blend; prev_time = !backward ? 0 : (double)a->get_length(); } @@ -1368,7 +1341,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - scale = post_process_key_value(a, i, scale, t->object, t->bone_idx); + scale = post_process_key_value(a, i, scale, t->object_id, t->bone_idx); t->scale += (scale - t->init_scale) * blend; } #endif // _3D_DISABLED @@ -1385,18 +1358,19 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (err != OK) { continue; } - value = post_process_key_value(a, i, value, t->object, t->shape_index); + value = post_process_key_value(a, i, value, t->object_id, t->shape_index); t->value += (value - t->init_value) * blend; #endif // _3D_DISABLED } break; + case Animation::TYPE_BEZIER: case Animation::TYPE_VALUE: { if (Math::is_zero_approx(blend)) { continue; // Nothing to blend. } TrackCacheValue *t = static_cast<TrackCacheValue *>(track); if (t->is_continuous) { - Variant value = a->value_track_interpolate(i, time); - value = post_process_key_value(a, i, value, t->object); + Variant value = ttype == Animation::TYPE_VALUE ? a->value_track_interpolate(i, time) : Variant(a->bezier_track_interpolate(i, time)); + value = post_process_key_value(a, i, value, t->object_id); if (value == Variant()) { continue; } @@ -1436,15 +1410,21 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { continue; } Variant value = a->track_get_key_value(i, idx); - value = post_process_key_value(a, i, value, t->object); - t->object->set_indexed(t->subpath, value); + value = post_process_key_value(a, i, value, t->object_id); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (t_obj) { + t_obj->set_indexed(t->subpath, value); + } } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { Variant value = a->track_get_key_value(i, F); - value = post_process_key_value(a, i, value, t->object); - t->object->set_indexed(t->subpath, value); + value = post_process_key_value(a, i, value, t->object_id); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (t_obj) { + t_obj->set_indexed(t->subpath, value); + } } } } @@ -1466,31 +1446,23 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { } StringName method = a->method_track_get_name(i, idx); Vector<Variant> params = a->method_track_get_params(i, idx); - _call_object(t->object, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED); + _call_object(t->object_id, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED); } else { List<int> indices; a->track_get_key_indices_in_range(i, time, delta, &indices, looped_flag); for (int &F : indices) { StringName method = a->method_track_get_name(i, F); Vector<Variant> params = a->method_track_get_params(i, F); - _call_object(t->object, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED); + _call_object(t->object_id, method, params, callback_mode_method == ANIMATION_CALLBACK_MODE_METHOD_DEFERRED); } } } break; - case Animation::TYPE_BEZIER: { - if (Math::is_zero_approx(blend)) { - continue; // Nothing to blend. - } - TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); - real_t bezier = a->bezier_track_interpolate(i, time); - bezier = post_process_key_value(a, i, bezier, t->object); - t->value += (bezier - t->init_value) * blend; - } break; case Animation::TYPE_AUDIO: { // The end of audio should be observed even if the blend value is 0, build up the information and store to the cache for that. TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); - Node *asp = Object::cast_to<Node>(t->object); - if (!asp) { + Object *t_obj = ObjectDB::get_instance(t->object_id); + Node *asp = t_obj ? Object::cast_to<Node>(t_obj) : nullptr; + if (!t_obj || !asp) { t->playing_streams.clear(); continue; } @@ -1540,22 +1512,22 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { if (seeked) { start_ofs += time - a->track_get_key_time(i, idx); } - if (t->object->call(SNAME("get_stream")) != t->audio_stream) { - t->object->call(SNAME("set_stream"), t->audio_stream); + if (t_obj->call(SNAME("get_stream")) != t->audio_stream) { + t_obj->call(SNAME("set_stream"), t->audio_stream); t->audio_stream_playback.unref(); if (!playing_audio_stream_players.has(asp)) { playing_audio_stream_players.push_back(asp); } } - if (!t->object->call(SNAME("is_playing"))) { - t->object->call(SNAME("play")); + if (!t_obj->call(SNAME("is_playing"))) { + t_obj->call(SNAME("play")); } - if (!t->object->call(SNAME("has_stream_playback"))) { + if (!t_obj->call(SNAME("has_stream_playback"))) { t->audio_stream_playback.unref(); continue; } if (t->audio_stream_playback.is_null()) { - t->audio_stream_playback = t->object->call(SNAME("get_stream_playback")); + t->audio_stream_playback = t_obj->call(SNAME("get_stream_playback")); } PlayingAudioStreamInfo pasi; pasi.index = t->audio_stream_playback->play_stream(stream, start_ofs); @@ -1573,7 +1545,11 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { continue; } TrackCacheAnimation *t = static_cast<TrackCacheAnimation *>(track); - AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t->object); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (!t_obj) { + continue; + } + AnimationPlayer *player2 = Object::cast_to<AnimationPlayer>(t_obj); if (!player2) { continue; } @@ -1640,7 +1616,7 @@ void AnimationMixer::_blend_process(double p_delta, bool p_update_only) { void AnimationMixer::_blend_apply() { // Finally, set the tracks. - for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { TrackCache *track = K.value; if (!deterministic && Math::is_zero_approx(track->total_weight)) { continue; @@ -1657,26 +1633,34 @@ void AnimationMixer::_blend_apply() { root_motion_position_accumulator = t->loc; root_motion_rotation_accumulator = t->rot; root_motion_scale_accumulator = t->scale; - } else if (t->skeleton && t->bone_idx >= 0) { + } else if (t->skeleton_id.is_valid() && t->bone_idx >= 0) { + Skeleton3D *t_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(t->skeleton_id)); + if (!t_skeleton) { + return; + } if (t->loc_used) { - t->skeleton->set_bone_pose_position(t->bone_idx, t->loc); + t_skeleton->set_bone_pose_position(t->bone_idx, t->loc); } if (t->rot_used) { - t->skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); + t_skeleton->set_bone_pose_rotation(t->bone_idx, t->rot); } if (t->scale_used) { - t->skeleton->set_bone_pose_scale(t->bone_idx, t->scale); + t_skeleton->set_bone_pose_scale(t->bone_idx, t->scale); } - } else if (!t->skeleton) { + } else if (!t->skeleton_id.is_valid()) { + Node3D *t_node_3d = Object::cast_to<Node3D>(ObjectDB::get_instance(t->object_id)); + if (!t_node_3d) { + return; + } if (t->loc_used) { - t->node_3d->set_position(t->loc); + t_node_3d->set_position(t->loc); } if (t->rot_used) { - t->node_3d->set_rotation(t->rot.get_euler()); + t_node_3d->set_rotation(t->rot.get_euler()); } if (t->scale_used) { - t->node_3d->set_scale(t->scale); + t_node_3d->set_scale(t->scale); } } #endif // _3D_DISABLED @@ -1685,8 +1669,9 @@ void AnimationMixer::_blend_apply() { #ifndef _3D_DISABLED TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); - if (t->mesh_3d) { - t->mesh_3d->set_blend_shape_value(t->shape_index, t->value); + MeshInstance3D *t_mesh_3d = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(t->object_id)); + if (t_mesh_3d) { + t_mesh_3d->set_blend_shape_value(t->shape_index, t->value); } #endif // _3D_DISABLED } break; @@ -1710,19 +1695,12 @@ void AnimationMixer::_blend_apply() { } } - // t->object isn't safe here, get instance from id (GH-85365). - Object *obj = ObjectDB::get_instance(t->object_id); - if (obj) { - obj->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type())); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (t_obj) { + t_obj->set_indexed(t->subpath, Animation::cast_from_blendwise(t->value, t->init_value.get_type())); } } break; - case Animation::TYPE_BEZIER: { - TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); - - t->object->set_indexed(t->subpath, t->value); - - } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); @@ -1787,7 +1765,7 @@ void AnimationMixer::_blend_apply() { } } -void AnimationMixer::_call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) { +void AnimationMixer::_call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred) { // Separate function to use alloca() more efficiently const Variant **argptrs = (const Variant **)alloca(sizeof(Variant *) * p_params.size()); const Variant *args = p_params.ptr(); @@ -1795,11 +1773,15 @@ void AnimationMixer::_call_object(Object *p_object, const StringName &p_method, for (uint32_t i = 0; i < argcount; i++) { argptrs[i] = &args[i]; } + Object *t_obj = ObjectDB::get_instance(p_object_id); + if (!t_obj) { + return; + } if (p_deferred) { - MessageQueue::get_singleton()->push_callp(p_object, p_method, argptrs, argcount); + MessageQueue::get_singleton()->push_callp(t_obj, p_method, argptrs, argcount); } else { Callable::CallError ce; - p_object->callp(p_method, argptrs, argcount, ce); + t_obj->callp(p_method, argptrs, argcount, ce); } } @@ -1879,7 +1861,7 @@ bool AnimationMixer::can_apply_reset() const { } void AnimationMixer::_build_backup_track_cache() { - for (const KeyValue<NodePath, TrackCache *> &K : track_cache) { + for (const KeyValue<Animation::TypeHash, TrackCache *> &K : track_cache) { TrackCache *track = K.value; track->total_weight = 1.0; switch (track->type) { @@ -1888,25 +1870,33 @@ void AnimationMixer::_build_backup_track_cache() { TrackCacheTransform *t = static_cast<TrackCacheTransform *>(track); if (t->root_motion) { // Do nothing. - } else if (t->skeleton && t->bone_idx >= 0) { + } else if (t->skeleton_id.is_valid() && t->bone_idx >= 0) { + Skeleton3D *t_skeleton = Object::cast_to<Skeleton3D>(ObjectDB::get_instance(t->skeleton_id)); + if (!t_skeleton) { + return; + } if (t->loc_used) { - t->loc = t->skeleton->get_bone_pose_position(t->bone_idx); + t->loc = t_skeleton->get_bone_pose_position(t->bone_idx); } if (t->rot_used) { - t->rot = t->skeleton->get_bone_pose_rotation(t->bone_idx); + t->rot = t_skeleton->get_bone_pose_rotation(t->bone_idx); } if (t->scale_used) { - t->scale = t->skeleton->get_bone_pose_scale(t->bone_idx); + t->scale = t_skeleton->get_bone_pose_scale(t->bone_idx); + } + } else if (!t->skeleton_id.is_valid()) { + Node3D *t_node_3d = Object::cast_to<Node3D>(ObjectDB::get_instance(t->object_id)); + if (!t_node_3d) { + return; } - } else if (!t->skeleton) { if (t->loc_used) { - t->loc = t->node_3d->get_position(); + t->loc = t_node_3d->get_position(); } if (t->rot_used) { - t->rot = t->node_3d->get_quaternion(); + t->rot = t_node_3d->get_quaternion(); } if (t->scale_used) { - t->scale = t->node_3d->get_scale(); + t->scale = t_node_3d->get_scale(); } } #endif // _3D_DISABLED @@ -1914,25 +1904,28 @@ void AnimationMixer::_build_backup_track_cache() { case Animation::TYPE_BLEND_SHAPE: { #ifndef _3D_DISABLED TrackCacheBlendShape *t = static_cast<TrackCacheBlendShape *>(track); - if (t->mesh_3d) { - t->value = t->mesh_3d->get_blend_shape_value(t->shape_index); + MeshInstance3D *t_mesh_3d = Object::cast_to<MeshInstance3D>(ObjectDB::get_instance(t->object_id)); + if (t_mesh_3d) { + t->value = t_mesh_3d->get_blend_shape_value(t->shape_index); } #endif // _3D_DISABLED } break; case Animation::TYPE_VALUE: { TrackCacheValue *t = static_cast<TrackCacheValue *>(track); - t->value = t->object->get_indexed(t->subpath); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (t_obj) { + t->value = t_obj->get_indexed(t->subpath); + } t->is_continuous = true; } break; - case Animation::TYPE_BEZIER: { - TrackCacheBezier *t = static_cast<TrackCacheBezier *>(track); - t->value = t->object->get_indexed(t->subpath); - } break; case Animation::TYPE_AUDIO: { TrackCacheAudio *t = static_cast<TrackCacheAudio *>(track); - Node *asp = Object::cast_to<Node>(t->object); - if (asp) { - t->object->call(SNAME("set_stream"), Ref<AudioStream>()); + Object *t_obj = ObjectDB::get_instance(t->object_id); + if (t_obj) { + Node *asp = Object::cast_to<Node>(t_obj); + if (asp) { + asp->call(SNAME("set_stream"), Ref<AudioStream>()); + } } } break; default: { @@ -1989,7 +1982,7 @@ void AnimationMixer::restore(const Ref<AnimatedValuesBackup> &p_backup) { ERR_FAIL_COND(p_backup.is_null()); track_cache = p_backup->get_data(); _blend_apply(); - track_cache = HashMap<NodePath, AnimationMixer::TrackCache *>(); + track_cache = HashMap<Animation::TypeHash, AnimationMixer::TrackCache *>(); cache_valid = false; } @@ -2054,6 +2047,26 @@ void AnimationMixer::_notification(int p_what) { } } +void AnimationMixer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + String pf = p_function; + if (p_idx == 0) { + if (pf == "get_animation" || pf == "has_animation") { + List<StringName> al; + get_animation_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } else if (pf == "get_animation_library" || pf == "has_animation_library" || pf == "remove_animation_library" || pf == "rename_animation_library") { + List<StringName> al; + get_animation_library_list(&al); + for (const StringName &name : al) { + r_options->push_back(String(name).quote()); + } + } + } + Node::get_argument_options(p_function, p_idx, r_options); +} + void AnimationMixer::_bind_methods() { /* ---- Data lists ---- */ ClassDB::bind_method(D_METHOD("add_animation_library", "name", "library"), &AnimationMixer::add_animation_library); @@ -2100,7 +2113,7 @@ void AnimationMixer::_bind_methods() { /* ---- Blending processor ---- */ ClassDB::bind_method(D_METHOD("clear_caches"), &AnimationMixer::clear_caches); ClassDB::bind_method(D_METHOD("advance", "delta"), &AnimationMixer::advance); - GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object", "object_idx"); + GDVIRTUAL_BIND(_post_process_key_value, "animation", "track", "value", "object_id", "object_sub_idx"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "active"), "set_active", "is_active"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "deterministic"), "set_deterministic", "is_deterministic"); @@ -2146,10 +2159,10 @@ AnimationMixer::AnimationMixer() { AnimationMixer::~AnimationMixer() { } -void AnimatedValuesBackup::set_data(const HashMap<NodePath, AnimationMixer::TrackCache *> p_data) { +void AnimatedValuesBackup::set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data) { clear_data(); - for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : p_data) { + for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : p_data) { AnimationMixer::TrackCache *track = get_cache_copy(E.value); if (!track) { continue; // Some types of tracks do not get a copy and must be ignored. @@ -2159,9 +2172,9 @@ void AnimatedValuesBackup::set_data(const HashMap<NodePath, AnimationMixer::Trac } } -HashMap<NodePath, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const { - HashMap<NodePath, AnimationMixer::TrackCache *> ret; - for (const KeyValue<NodePath, AnimationMixer::TrackCache *> &E : data) { +HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() const { + HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> ret; + for (const KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &E : data) { AnimationMixer::TrackCache *track = get_cache_copy(E.value); ERR_CONTINUE(!track); // Backup shouldn't contain tracks that cannot be copied, this is a mistake. @@ -2171,7 +2184,7 @@ HashMap<NodePath, AnimationMixer::TrackCache *> AnimatedValuesBackup::get_data() } void AnimatedValuesBackup::clear_data() { - for (KeyValue<NodePath, AnimationMixer::TrackCache *> &K : data) { + for (KeyValue<Animation::TypeHash, AnimationMixer::TrackCache *> &K : data) { memdelete(K.value); } data.clear(); @@ -2179,6 +2192,7 @@ void AnimatedValuesBackup::clear_data() { AnimationMixer::TrackCache *AnimatedValuesBackup::get_cache_copy(AnimationMixer::TrackCache *p_cache) const { switch (p_cache->type) { + case Animation::TYPE_BEZIER: case Animation::TYPE_VALUE: { AnimationMixer::TrackCacheValue *src = static_cast<AnimationMixer::TrackCacheValue *>(p_cache); AnimationMixer::TrackCacheValue *tc = memnew(AnimationMixer::TrackCacheValue(*src)); @@ -2199,12 +2213,6 @@ AnimationMixer::TrackCache *AnimatedValuesBackup::get_cache_copy(AnimationMixer: return tc; } - case Animation::TYPE_BEZIER: { - AnimationMixer::TrackCacheBezier *src = static_cast<AnimationMixer::TrackCacheBezier *>(p_cache); - AnimationMixer::TrackCacheBezier *tc = memnew(AnimationMixer::TrackCacheBezier(*src)); - return tc; - } - case Animation::TYPE_AUDIO: { AnimationMixer::TrackCacheAudio *src = static_cast<AnimationMixer::TrackCacheAudio *>(p_cache); AnimationMixer::TrackCacheAudio *tc = memnew(AnimationMixer::TrackCacheAudio(*src)); diff --git a/scene/animation/animation_mixer.h b/scene/animation/animation_mixer.h index 0cd204b384..098c2c3b86 100644 --- a/scene/animation/animation_mixer.h +++ b/scene/animation/animation_mixer.h @@ -31,9 +31,7 @@ #ifndef ANIMATION_MIXER_H #define ANIMATION_MIXER_H -#include "scene/3d/mesh_instance_3d.h" -#include "scene/3d/node_3d.h" -#include "scene/3d/skeleton_3d.h" +#include "scene/main/node.h" #include "scene/resources/animation.h" #include "scene/resources/animation_library.h" #include "scene/resources/audio_stream_polyphonic.h" @@ -138,7 +136,7 @@ protected: bool root_motion = false; uint64_t setup_pass = 0; Animation::TrackType type = Animation::TrackType::TYPE_ANIMATION; - Object *object = nullptr; + NodePath path; ObjectID object_id; real_t total_weight = 0.0; @@ -147,7 +145,6 @@ protected: root_motion(p_other.root_motion), setup_pass(p_other.setup_pass), type(p_other.type), - object(p_other.object), object_id(p_other.object_id), total_weight(p_other.total_weight) {} @@ -156,8 +153,7 @@ protected: struct TrackCacheTransform : public TrackCache { #ifndef _3D_DISABLED - Node3D *node_3d = nullptr; - Skeleton3D *skeleton = nullptr; + ObjectID skeleton_id; #endif // _3D_DISABLED int bone_idx = -1; bool loc_used = false; @@ -173,8 +169,7 @@ protected: TrackCacheTransform(const TrackCacheTransform &p_other) : TrackCache(p_other), #ifndef _3D_DISABLED - node_3d(p_other.node_3d), - skeleton(p_other.skeleton), + skeleton_id(p_other.skeleton_id), #endif bone_idx(p_other.bone_idx), loc_used(p_other.loc_used), @@ -201,14 +196,12 @@ protected: }; struct TrackCacheBlendShape : public TrackCache { - MeshInstance3D *mesh_3d = nullptr; float init_value = 0; float value = 0; int shape_index = -1; TrackCacheBlendShape(const TrackCacheBlendShape &p_other) : TrackCache(p_other), - mesh_3d(p_other.mesh_3d), init_value(p_other.init_value), value(p_other.value), shape_index(p_other.shape_index) {} @@ -247,23 +240,6 @@ protected: ~TrackCacheMethod() {} }; - struct TrackCacheBezier : public TrackCache { - real_t init_value = 0.0; - real_t value = 0.0; - Vector<StringName> subpath; - - TrackCacheBezier(const TrackCacheBezier &p_other) : - TrackCache(p_other), - init_value(p_other.init_value), - value(p_other.value), - subpath(p_other.subpath) {} - - TrackCacheBezier() { - type = Animation::TYPE_BEZIER; - } - ~TrackCacheBezier() {} - }; - // Audio stream information for each audio stream placed on the track. struct PlayingAudioStreamInfo { AudioStreamPlaybackPolyphonic::ID index = -1; // ID retrieved from AudioStreamPlaybackPolyphonic. @@ -309,7 +285,7 @@ protected: }; RootMotionCache root_motion_cache; - HashMap<NodePath, TrackCache *> track_cache; + HashMap<Animation::TypeHash, TrackCache *> track_cache; HashSet<TrackCache *> playing_caches; Vector<Node *> playing_audio_stream_players; @@ -340,6 +316,7 @@ protected: void _get_property_list(List<PropertyInfo> *p_list) const; void _notification(int p_what); virtual void _validate_property(PropertyInfo &p_property) const; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; static void _bind_methods(); void _node_removed(Node *p_node); @@ -351,9 +328,9 @@ protected: /* ---- Blending processor ---- */ virtual void _process_animation(double p_delta, bool p_update_only = false); - virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); - Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, const Object *p_object, int p_object_idx = -1); - GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, Object *, int); + virtual Variant _post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1); + Variant post_process_key_value(const Ref<Animation> &p_anim, int p_track, Variant p_value, ObjectID p_object_id, int p_object_sub_idx = -1); + GDVIRTUAL5RC(Variant, _post_process_key_value, Ref<Animation>, int, Variant, ObjectID, int); void _blend_init(); virtual bool _blend_pre_process(double p_delta, int p_track_count, const HashMap<NodePath, int> &p_track_map); @@ -361,7 +338,13 @@ protected: void _blend_process(double p_delta, bool p_update_only = false); void _blend_apply(); virtual void _blend_post_process(); - void _call_object(Object *p_object, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred); + void _call_object(ObjectID p_object_id, const StringName &p_method, const Vector<Variant> &p_params, bool p_deferred); + +#ifndef DISABLE_DEPRECATED + virtual Variant _post_process_key_value_bind_compat_86687(const Ref<Animation> &p_anim, int p_track, Variant p_value, Object *p_object, int p_object_idx = -1); + + static void _bind_compatibility_methods(); +#endif // DISABLE_DEPRECATED public: /* ---- Data lists ---- */ @@ -443,11 +426,11 @@ public: class AnimatedValuesBackup : public RefCounted { GDCLASS(AnimatedValuesBackup, RefCounted); - HashMap<NodePath, AnimationMixer::TrackCache *> data; + HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> data; public: - void set_data(const HashMap<NodePath, AnimationMixer::TrackCache *> p_data); - HashMap<NodePath, AnimationMixer::TrackCache *> get_data() const; + void set_data(const HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> p_data); + HashMap<Animation::TypeHash, AnimationMixer::TrackCache *> get_data() const; void clear_data(); AnimationMixer::TrackCache *get_cache_copy(AnimationMixer::TrackCache *p_cache) const; diff --git a/scene/animation/root_motion_view.cpp b/scene/animation/root_motion_view.cpp index 645850015b..fd520dadd6 100644 --- a/scene/animation/root_motion_view.cpp +++ b/scene/animation/root_motion_view.cpp @@ -28,6 +28,8 @@ /* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ /**************************************************************************/ +#ifndef _3D_DISABLED + #include "root_motion_view.h" #include "scene/animation/animation_tree.h" @@ -203,3 +205,5 @@ RootMotionView::RootMotionView() { RootMotionView::~RootMotionView() { set_base(RID()); } + +#endif // _3D_DISABLED diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp index 98e340d7fc..2f4a31130e 100644 --- a/scene/gui/color_picker.cpp +++ b/scene/gui/color_picker.cpp @@ -557,6 +557,24 @@ void ColorPicker::_html_submitted(const String &p_html) { } Color new_color = Color::from_string(p_html.strip_edges(), color); + String html_no_prefix = p_html.strip_edges().trim_prefix("#"); + if (html_no_prefix.is_valid_hex_number(false)) { + // Convert invalid HTML color codes that software like Figma supports. + if (html_no_prefix.length() == 1) { + // Turn `#1` into `#111111`. + html_no_prefix = html_no_prefix.repeat(6); + } else if (html_no_prefix.length() == 2) { + // Turn `#12` into `#121212`. + html_no_prefix = html_no_prefix.repeat(3); + } else if (html_no_prefix.length() == 5) { + // Turn `#12345` into `#11223344`. + html_no_prefix = html_no_prefix.left(4); + } else if (html_no_prefix.length() == 7) { + // Turn `#1234567` into `#123456`. + html_no_prefix = html_no_prefix.left(6); + } + } + new_color = Color::from_string(html_no_prefix, new_color); if (!is_editing_alpha()) { new_color.a = color.a; diff --git a/scene/gui/graph_edit_arranger.cpp b/scene/gui/graph_edit_arranger.cpp index 345421db31..29c3056b3b 100644 --- a/scene/gui/graph_edit_arranger.cpp +++ b/scene/gui/graph_edit_arranger.cpp @@ -421,7 +421,7 @@ void GraphEditArranger::_calculate_inner_shifts(Dictionary &r_inner_shifts, cons } } -float GraphEditArranger::_calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) { +float GraphEditArranger::_calculate_threshold(const StringName &p_v, const StringName &p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions) { #define MAX_ORDER 2147483647 #define ORDER(node, layers) \ for (unsigned int i = 0; i < layers.size(); i++) { \ @@ -503,7 +503,7 @@ float GraphEditArranger::_calculate_threshold(StringName p_v, StringName p_w, co return threshold; } -void GraphEditArranger::_place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) { +void GraphEditArranger::_place_block(const StringName &p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions) { #define PRED(node, layers) \ for (unsigned int i = 0; i < layers.size(); i++) { \ int index = layers[i].find(node); \ diff --git a/scene/gui/graph_edit_arranger.h b/scene/gui/graph_edit_arranger.h index e79944e5dd..925b58d428 100644 --- a/scene/gui/graph_edit_arranger.h +++ b/scene/gui/graph_edit_arranger.h @@ -54,8 +54,8 @@ class GraphEditArranger : public RefCounted { void _horizontal_alignment(Dictionary &r_root, Dictionary &r_align, const HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours, const HashSet<StringName> &r_selected_nodes); void _crossing_minimisation(HashMap<int, Vector<StringName>> &r_layers, const HashMap<StringName, HashSet<StringName>> &r_upper_neighbours); void _calculate_inner_shifts(Dictionary &r_inner_shifts, const Dictionary &r_root, const Dictionary &r_node_names, const Dictionary &r_align, const HashSet<StringName> &r_block_heads, const HashMap<StringName, Pair<int, int>> &r_port_info); - float _calculate_threshold(StringName p_v, StringName p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions); - void _place_block(StringName p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions); + float _calculate_threshold(const StringName &p_v, const StringName &p_w, const Dictionary &r_node_names, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_inner_shift, real_t p_current_threshold, const HashMap<StringName, Vector2> &r_node_positions); + void _place_block(const StringName &p_v, float p_delta, const HashMap<int, Vector<StringName>> &r_layers, const Dictionary &r_root, const Dictionary &r_align, const Dictionary &r_node_name, const Dictionary &r_inner_shift, Dictionary &r_sink, Dictionary &r_shift, HashMap<StringName, Vector2> &r_node_positions); public: void arrange_nodes(); diff --git a/scene/gui/graph_node.cpp b/scene/gui/graph_node.cpp index 3b1c1a153f..1b960a9b62 100644 --- a/scene/gui/graph_node.cpp +++ b/scene/gui/graph_node.cpp @@ -482,6 +482,27 @@ Color GraphNode::get_slot_color_left(int p_slot_index) const { return slot_table[p_slot_index].color_left; } +void GraphNode::set_slot_custom_icon_left(int p_slot_index, const Ref<Texture2D> &p_custom_icon) { + ERR_FAIL_COND_MSG(!slot_table.has(p_slot_index), vformat("Cannot set custom_port_icon_left for the slot with index '%d' because it hasn't been enabled.", p_slot_index)); + + if (slot_table[p_slot_index].custom_port_icon_left == p_custom_icon) { + return; + } + + slot_table[p_slot_index].custom_port_icon_left = p_custom_icon; + queue_redraw(); + port_pos_dirty = true; + + emit_signal(SNAME("slot_updated"), p_slot_index); +} + +Ref<Texture2D> GraphNode::get_slot_custom_icon_left(int p_slot_index) const { + if (!slot_table.has(p_slot_index)) { + return Ref<Texture2D>(); + } + return slot_table[p_slot_index].custom_port_icon_left; +} + bool GraphNode::is_slot_enabled_right(int p_slot_index) const { if (!slot_table.has(p_slot_index)) { return false; @@ -545,6 +566,27 @@ Color GraphNode::get_slot_color_right(int p_slot_index) const { return slot_table[p_slot_index].color_right; } +void GraphNode::set_slot_custom_icon_right(int p_slot_index, const Ref<Texture2D> &p_custom_icon) { + ERR_FAIL_COND_MSG(!slot_table.has(p_slot_index), vformat("Cannot set custom_port_icon_right for the slot with index '%d' because it hasn't been enabled.", p_slot_index)); + + if (slot_table[p_slot_index].custom_port_icon_right == p_custom_icon) { + return; + } + + slot_table[p_slot_index].custom_port_icon_right = p_custom_icon; + queue_redraw(); + port_pos_dirty = true; + + emit_signal(SNAME("slot_updated"), p_slot_index); +} + +Ref<Texture2D> GraphNode::get_slot_custom_icon_right(int p_slot_index) const { + if (!slot_table.has(p_slot_index)) { + return Ref<Texture2D>(); + } + return slot_table[p_slot_index].custom_port_icon_right; +} + bool GraphNode::is_slot_draw_stylebox(int p_slot_index) const { if (!slot_table.has(p_slot_index)) { return false; @@ -797,6 +839,9 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slot_color_left", "slot_index", "color"), &GraphNode::set_slot_color_left); ClassDB::bind_method(D_METHOD("get_slot_color_left", "slot_index"), &GraphNode::get_slot_color_left); + ClassDB::bind_method(D_METHOD("set_slot_custom_icon_left", "slot_index", "custom_icon"), &GraphNode::set_slot_custom_icon_left); + ClassDB::bind_method(D_METHOD("get_slot_custom_icon_left", "slot_index"), &GraphNode::get_slot_custom_icon_left); + ClassDB::bind_method(D_METHOD("is_slot_enabled_right", "slot_index"), &GraphNode::is_slot_enabled_right); ClassDB::bind_method(D_METHOD("set_slot_enabled_right", "slot_index", "enable"), &GraphNode::set_slot_enabled_right); @@ -806,6 +851,9 @@ void GraphNode::_bind_methods() { ClassDB::bind_method(D_METHOD("set_slot_color_right", "slot_index", "color"), &GraphNode::set_slot_color_right); ClassDB::bind_method(D_METHOD("get_slot_color_right", "slot_index"), &GraphNode::get_slot_color_right); + ClassDB::bind_method(D_METHOD("set_slot_custom_icon_right", "slot_index", "custom_icon"), &GraphNode::set_slot_custom_icon_right); + ClassDB::bind_method(D_METHOD("get_slot_custom_icon_right", "slot_index"), &GraphNode::get_slot_custom_icon_right); + ClassDB::bind_method(D_METHOD("is_slot_draw_stylebox", "slot_index"), &GraphNode::is_slot_draw_stylebox); ClassDB::bind_method(D_METHOD("set_slot_draw_stylebox", "slot_index", "enable"), &GraphNode::set_slot_draw_stylebox); diff --git a/scene/gui/graph_node.h b/scene/gui/graph_node.h index 04ca9e7cb4..a0610b37fb 100644 --- a/scene/gui/graph_node.h +++ b/scene/gui/graph_node.h @@ -129,6 +129,9 @@ public: void set_slot_color_left(int p_slot_index, const Color &p_color); Color get_slot_color_left(int p_slot_index) const; + void set_slot_custom_icon_left(int p_slot_index, const Ref<Texture2D> &p_custom_icon); + Ref<Texture2D> get_slot_custom_icon_left(int p_slot_index) const; + bool is_slot_enabled_right(int p_slot_index) const; void set_slot_enabled_right(int p_slot_index, bool p_enable); @@ -138,6 +141,9 @@ public: void set_slot_color_right(int p_slot_index, const Color &p_color); Color get_slot_color_right(int p_slot_index) const; + void set_slot_custom_icon_right(int p_slot_index, const Ref<Texture2D> &p_custom_icon); + Ref<Texture2D> get_slot_custom_icon_right(int p_slot_index) const; + bool is_slot_draw_stylebox(int p_slot_index) const; void set_slot_draw_stylebox(int p_slot_index, bool p_enable); diff --git a/scene/gui/tab_bar.cpp b/scene/gui/tab_bar.cpp index 718ccccc2c..90a6626b74 100644 --- a/scene/gui/tab_bar.cpp +++ b/scene/gui/tab_bar.cpp @@ -516,7 +516,13 @@ void TabBar::_draw_tab(Ref<StyleBox> &p_tab_style, Color &p_font_color, int p_in bool rtl = is_layout_rtl(); Rect2 sb_rect = Rect2(p_x, 0, tabs[p_index].size_cache, get_size().height); + if (tab_style_v_flip) { + draw_set_transform(Point2(0.0, p_tab_style->get_draw_rect(sb_rect).size.y), 0.0, Size2(1.0, -1.0)); + } p_tab_style->draw(ci, sb_rect); + if (tab_style_v_flip) { + draw_set_transform(Point2(), 0.0, Size2(1.0, 1.0)); + } if (p_focus) { Ref<StyleBox> focus_style = theme_cache.tab_focus_style; focus_style->draw(ci, sb_rect); @@ -1367,6 +1373,10 @@ bool TabBar::get_clip_tabs() const { return clip_tabs; } +void TabBar::set_tab_style_v_flip(bool p_tab_style_v_flip) { + tab_style_v_flip = p_tab_style_v_flip; +} + void TabBar::move_tab(int p_from, int p_to) { if (p_from == p_to) { return; diff --git a/scene/gui/tab_bar.h b/scene/gui/tab_bar.h index 28e3411f3d..9674187fb3 100644 --- a/scene/gui/tab_bar.h +++ b/scene/gui/tab_bar.h @@ -91,6 +91,7 @@ private: bool clip_tabs = true; int rb_hover = -1; bool rb_pressing = false; + bool tab_style_v_flip = false; bool select_with_rmb = false; @@ -210,6 +211,8 @@ public: void set_clip_tabs(bool p_clip_tabs); bool get_clip_tabs() const; + void set_tab_style_v_flip(bool p_tab_style_v_flip); + void move_tab(int p_from, int p_to); void set_tab_close_display_policy(CloseButtonDisplayPolicy p_policy); diff --git a/scene/gui/tab_container.cpp b/scene/gui/tab_container.cpp index 0f461f4865..ef01d9ec5d 100644 --- a/scene/gui/tab_container.cpp +++ b/scene/gui/tab_container.cpp @@ -35,7 +35,7 @@ #include "scene/gui/texture_rect.h" #include "scene/theme/theme_db.h" -int TabContainer::_get_top_margin() const { +int TabContainer::_get_tab_height() const { int height = 0; if (tabs_visible && get_tab_count() > 0) { height = tab_bar->get_minimum_size().height; @@ -54,31 +54,33 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { if (mb.is_valid() && mb->is_pressed() && mb->get_button_index() == MouseButton::LEFT) { Point2 pos = mb->get_position(); Size2 size = get_size(); + real_t content_height = size.height - _get_tab_height(); // Click must be on tabs in the tab header area. - if (pos.y > _get_top_margin()) { + if (tabs_position == POSITION_TOP && pos.y > _get_tab_height()) { + return; + } + if (tabs_position == POSITION_BOTTOM && pos.y < content_height) { return; } // Handle menu button. - if (is_layout_rtl()) { - if (popup && pos.x < theme_cache.menu_icon->get_width()) { - emit_signal(SNAME("pre_popup_pressed")); - - Vector2 popup_pos = get_screen_position(); - popup_pos.y += theme_cache.menu_icon->get_height(); - - popup->set_position(popup_pos); - popup->popup(); - return; - } - } else { - if (popup && pos.x > size.width - theme_cache.menu_icon->get_width()) { + if (popup) { + if (is_layout_rtl() ? pos.x < theme_cache.menu_icon->get_width() : pos.x > size.width - theme_cache.menu_icon->get_width()) { emit_signal(SNAME("pre_popup_pressed")); Vector2 popup_pos = get_screen_position(); - popup_pos.x += size.width - popup->get_size().width; - popup_pos.y += theme_cache.menu_icon->get_height(); + if (!is_layout_rtl()) { + popup_pos.x += size.width - popup->get_size().width; + } + popup_pos.y += _get_tab_height() / 2.0; + if (tabs_position == POSITION_BOTTOM) { + popup_pos.y += content_height; + popup_pos.y -= popup->get_size().height; + popup_pos.y -= theme_cache.menu_icon->get_height() / 2.0; + } else { + popup_pos.y += theme_cache.menu_icon->get_height() / 2.0; + } popup->set_position(popup_pos); popup->popup(); @@ -94,7 +96,14 @@ void TabContainer::gui_input(const Ref<InputEvent> &p_event) { Size2 size = get_size(); // Mouse must be on tabs in the tab header area. - if (pos.y > _get_top_margin()) { + if (tabs_position == POSITION_TOP && pos.y > _get_tab_height()) { + if (menu_hovered) { + menu_hovered = false; + queue_redraw(); + } + return; + } + if (tabs_position == POSITION_BOTTOM && pos.y < size.height - _get_tab_height()) { if (menu_hovered) { menu_hovered = false; queue_redraw(); @@ -165,21 +174,22 @@ void TabContainer::_notification(int p_what) { return; } - int header_height = _get_top_margin(); + int header_height = _get_tab_height(); + int header_voffset = int(tabs_position == POSITION_BOTTOM) * (size.height - header_height); // Draw background for the tabbar. - theme_cache.tabbar_style->draw(canvas, Rect2(0, 0, size.width, header_height)); + theme_cache.tabbar_style->draw(canvas, Rect2(0, header_voffset, size.width, header_height)); // Draw the background for the tab's content. - theme_cache.panel_style->draw(canvas, Rect2(0, header_height, size.width, size.height - header_height)); + theme_cache.panel_style->draw(canvas, Rect2(0, int(tabs_position == POSITION_TOP) * header_height, size.width, size.height - header_height)); // Draw the popup menu. if (get_popup()) { int x = is_layout_rtl() ? 0 : get_size().width - theme_cache.menu_icon->get_width(); if (menu_hovered) { - theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_hl_icon->get_height()) / 2)); + theme_cache.menu_hl_icon->draw(get_canvas_item(), Point2(x, header_voffset + (header_height - theme_cache.menu_hl_icon->get_height()) / 2)); } else { - theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, (header_height - theme_cache.menu_icon->get_height()) / 2)); + theme_cache.menu_icon->draw(get_canvas_item(), Point2(x, header_voffset + (header_height - theme_cache.menu_icon->get_height()) / 2)); } } } break; @@ -243,6 +253,12 @@ void TabContainer::_repaint() { Vector<Control *> controls = _get_tab_controls(); int current = get_current_tab(); + if (tabs_position == POSITION_BOTTOM) { + tab_bar->set_anchors_and_offsets_preset(PRESET_BOTTOM_WIDE); + } else { + tab_bar->set_anchors_and_offsets_preset(PRESET_TOP_WIDE); + } + for (int i = 0; i < controls.size(); i++) { Control *c = controls[i]; @@ -251,7 +267,11 @@ void TabContainer::_repaint() { c->set_anchors_and_offsets_preset(Control::PRESET_FULL_RECT); if (tabs_visible) { - c->set_offset(SIDE_TOP, _get_top_margin()); + if (tabs_position == POSITION_BOTTOM) { + c->set_offset(SIDE_BOTTOM, -_get_tab_height()); + } else { + c->set_offset(SIDE_TOP, _get_tab_height()); + } } c->set_offset(SIDE_TOP, c->get_offset(SIDE_TOP) + theme_cache.panel_style->get_margin(SIDE_TOP)); @@ -263,6 +283,7 @@ void TabContainer::_repaint() { } } + _update_margins(); update_minimum_size(); } @@ -609,6 +630,23 @@ TabBar::AlignmentMode TabContainer::get_tab_alignment() const { return tab_bar->get_tab_alignment(); } +void TabContainer::set_tabs_position(TabPosition p_tabs_position) { + ERR_FAIL_INDEX(p_tabs_position, POSITION_MAX); + if (p_tabs_position == tabs_position) { + return; + } + tabs_position = p_tabs_position; + + tab_bar->set_tab_style_v_flip(tabs_position == POSITION_BOTTOM); + + callable_mp(this, &TabContainer::_repaint).call_deferred(); + queue_redraw(); +} + +TabContainer::TabPosition TabContainer::get_tabs_position() const { + return tabs_position; +} + void TabContainer::set_tab_focus_mode(Control::FocusMode p_focus_mode) { tab_bar->set_focus_mode(p_focus_mode); } @@ -633,18 +671,8 @@ void TabContainer::set_tabs_visible(bool p_visible) { tabs_visible = p_visible; tab_bar->set_visible(tabs_visible); - Vector<Control *> controls = _get_tab_controls(); - for (int i = 0; i < controls.size(); i++) { - Control *c = controls[i]; - if (tabs_visible) { - c->set_offset(SIDE_TOP, _get_top_margin()); - } else { - c->set_offset(SIDE_TOP, 0); - } - } - + callable_mp(this, &TabContainer::_repaint).call_deferred(); queue_redraw(); - update_minimum_size(); } bool TabContainer::are_tabs_visible() const { @@ -890,6 +918,8 @@ void TabContainer::_bind_methods() { ClassDB::bind_method(D_METHOD("get_tab_control", "tab_idx"), &TabContainer::get_tab_control); ClassDB::bind_method(D_METHOD("set_tab_alignment", "alignment"), &TabContainer::set_tab_alignment); ClassDB::bind_method(D_METHOD("get_tab_alignment"), &TabContainer::get_tab_alignment); + ClassDB::bind_method(D_METHOD("set_tabs_position", "tabs_position"), &TabContainer::set_tabs_position); + ClassDB::bind_method(D_METHOD("get_tabs_position"), &TabContainer::get_tabs_position); ClassDB::bind_method(D_METHOD("set_clip_tabs", "clip_tabs"), &TabContainer::set_clip_tabs); ClassDB::bind_method(D_METHOD("get_clip_tabs"), &TabContainer::get_clip_tabs); ClassDB::bind_method(D_METHOD("set_tabs_visible", "visible"), &TabContainer::set_tabs_visible); @@ -931,6 +961,7 @@ void TabContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_alignment", PROPERTY_HINT_ENUM, "Left,Center,Right"), "set_tab_alignment", "get_tab_alignment"); ADD_PROPERTY(PropertyInfo(Variant::INT, "current_tab", PROPERTY_HINT_RANGE, "-1,4096,1"), "set_current_tab", "get_current_tab"); + ADD_PROPERTY(PropertyInfo(Variant::INT, "tabs_position", PROPERTY_HINT_ENUM, "Top,Bottom"), "set_tabs_position", "get_tabs_position"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "clip_tabs"), "set_clip_tabs", "get_clip_tabs"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "tabs_visible"), "set_tabs_visible", "are_tabs_visible"); ADD_PROPERTY(PropertyInfo(Variant::BOOL, "all_tabs_in_front"), "set_all_tabs_in_front", "is_all_tabs_in_front"); @@ -939,6 +970,10 @@ void TabContainer::_bind_methods() { ADD_PROPERTY(PropertyInfo(Variant::BOOL, "use_hidden_tabs_for_min_size"), "set_use_hidden_tabs_for_min_size", "get_use_hidden_tabs_for_min_size"); ADD_PROPERTY(PropertyInfo(Variant::INT, "tab_focus_mode", PROPERTY_HINT_ENUM, "None,Click,All"), "set_tab_focus_mode", "get_tab_focus_mode"); + BIND_ENUM_CONSTANT(POSITION_TOP); + BIND_ENUM_CONSTANT(POSITION_BOTTOM); + BIND_ENUM_CONSTANT(POSITION_MAX); + BIND_THEME_ITEM(Theme::DATA_TYPE_CONSTANT, TabContainer, side_margin); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, TabContainer, panel_style, "panel"); diff --git a/scene/gui/tab_container.h b/scene/gui/tab_container.h index 450143cd0c..0c645b4598 100644 --- a/scene/gui/tab_container.h +++ b/scene/gui/tab_container.h @@ -38,9 +38,18 @@ class TabContainer : public Container { GDCLASS(TabContainer, Container); +public: + enum TabPosition { + POSITION_TOP, + POSITION_BOTTOM, + POSITION_MAX, + }; + +private: TabBar *tab_bar = nullptr; bool tabs_visible = true; bool all_tabs_in_front = false; + TabPosition tabs_position = POSITION_TOP; bool menu_hovered = false; mutable ObjectID popup_obj_id; bool use_hidden_tabs_for_min_size = false; @@ -86,7 +95,7 @@ class TabContainer : public Container { int tab_font_size; } theme_cache; - int _get_top_margin() const; + int _get_tab_height() const; Vector<Control *> _get_tab_controls() const; void _on_theme_changed(); void _repaint(); @@ -124,6 +133,9 @@ public: void set_tab_alignment(TabBar::AlignmentMode p_alignment); TabBar::AlignmentMode get_tab_alignment() const; + void set_tabs_position(TabPosition p_tab_position); + TabPosition get_tabs_position() const; + void set_tab_focus_mode(FocusMode p_focus_mode); FocusMode get_tab_focus_mode() const; @@ -185,4 +197,6 @@ public: TabContainer(); }; +VARIANT_ENUM_CAST(TabContainer::TabPosition); + #endif // TAB_CONTAINER_H diff --git a/scene/main/node.cpp b/scene/main/node.cpp index dac0b64853..1dd3b8bd1b 100644 --- a/scene/main/node.cpp +++ b/scene/main/node.cpp @@ -3065,7 +3065,7 @@ static void _add_nodes_to_options(const Node *p_base, const Node *p_node, List<S void Node::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { String pf = p_function; - if (p_idx == 0 && (pf == "has_node" || pf == "get_node")) { + if (p_idx == 0 && (pf == "has_node" || pf == "get_node" || pf == "get_node_or_null")) { _add_nodes_to_options(this, this, r_options); } else if (p_idx == 0 && (pf == "add_to_group" || pf == "remove_from_group" || pf == "is_in_group")) { HashMap<StringName, String> global_groups = ProjectSettings::get_singleton()->get_global_groups_list(); diff --git a/scene/register_scene_types.cpp b/scene/register_scene_types.cpp index baaa78f9f2..111d6447a0 100644 --- a/scene/register_scene_types.cpp +++ b/scene/register_scene_types.cpp @@ -78,7 +78,6 @@ #include "scene/animation/animation_node_state_machine.h" #include "scene/animation/animation_player.h" #include "scene/animation/animation_tree.h" -#include "scene/animation/root_motion_view.h" #include "scene/animation/tween.h" #include "scene/audio/audio_stream_player.h" #include "scene/debugger/scene_debugger.h" @@ -273,6 +272,7 @@ #include "scene/3d/voxel_gi.h" #include "scene/3d/world_environment.h" #include "scene/3d/xr_nodes.h" +#include "scene/animation/root_motion_view.h" #include "scene/resources/environment.h" #include "scene/resources/fog_material.h" #include "scene/resources/importer_mesh.h" diff --git a/scene/resources/animation.cpp b/scene/resources/animation.cpp index 1ab11cfdf0..e503c79bed 100644 --- a/scene/resources/animation.cpp +++ b/scene/resources/animation.cpp @@ -979,6 +979,7 @@ Animation::TrackType Animation::track_get_type(int p_track) const { void Animation::track_set_path(int p_track, const NodePath &p_path) { ERR_FAIL_INDEX(p_track, tracks.size()); tracks[p_track]->path = p_path; + _track_update_hash(p_track); emit_changed(); } @@ -996,6 +997,27 @@ int Animation::find_track(const NodePath &p_path, const TrackType p_type) const return -1; }; +Animation::TrackType Animation::get_cache_type(TrackType p_type) { + if (p_type == Animation::TYPE_BEZIER) { + return Animation::TYPE_VALUE; + } + if (p_type == Animation::TYPE_ROTATION_3D || p_type == Animation::TYPE_SCALE_3D) { + return Animation::TYPE_POSITION_3D; // Reference them as position3D tracks, even if they modify rotation or scale. + } + return p_type; +} + +void Animation::_track_update_hash(int p_track) { + NodePath track_path = tracks[p_track]->path; + TrackType track_cache_type = get_cache_type(tracks[p_track]->type); + tracks[p_track]->thash = StringName(String(track_path.get_concatenated_names()) + String(track_path.get_concatenated_subnames()) + itos(track_cache_type)).hash(); +} + +Animation::TypeHash Animation::track_get_type_hash(int p_track) const { + ERR_FAIL_INDEX_V(p_track, tracks.size(), 0); + return tracks[p_track]->thash; +} + void Animation::track_set_interpolation_type(int p_track, InterpolationType p_interp) { ERR_FAIL_INDEX(p_track, tracks.size()); tracks[p_track]->interpolation = p_interp; diff --git a/scene/resources/animation.h b/scene/resources/animation.h index 54a78fffa4..6c31bbcd29 100644 --- a/scene/resources/animation.h +++ b/scene/resources/animation.h @@ -41,6 +41,8 @@ class Animation : public Resource { RES_BASE_EXTENSION("anim"); public: + typedef uint32_t TypeHash; + enum TrackType { TYPE_VALUE, ///< Set a value in a property, can be interpolated. TYPE_POSITION_3D, ///< Position 3D track @@ -104,7 +106,8 @@ private: TrackType type = TrackType::TYPE_ANIMATION; InterpolationType interpolation = INTERPOLATION_LINEAR; bool loop_wrap = true; - NodePath path; // path to something + NodePath path; // Path to something. + TypeHash thash = 0; // Hash by Path + SubPath + TrackType. bool imported = false; bool enabled = true; Track() {} @@ -268,6 +271,8 @@ private: real_t step = 0.1; LoopMode loop_mode = LOOP_NONE; + void _track_update_hash(int p_track); + /* Animation compression page format (version 1): * * Animation uses bitwidth based compression separated into small pages. The intention is that pages fit easily in the cache, so decoding is cache efficient. @@ -386,6 +391,8 @@ public: NodePath track_get_path(int p_track) const; int find_track(const NodePath &p_path, const TrackType p_type) const; + TypeHash track_get_type_hash(int p_track) const; + void track_move_up(int p_track); void track_move_down(int p_track); void track_move_to(int p_track, int p_to_index); @@ -504,6 +511,8 @@ public: static Variant interpolate_variant(const Variant &a, const Variant &b, float c, bool p_snap_array_element = false); static Variant cubic_interpolate_in_time_variant(const Variant &pre_a, const Variant &a, const Variant &b, const Variant &post_b, float c, real_t p_pre_a_t, real_t p_b_t, real_t p_post_b_t, bool p_snap_array_element = false); + static TrackType get_cache_type(TrackType p_type); + Animation(); ~Animation(); }; diff --git a/scene/resources/animation_library.cpp b/scene/resources/animation_library.cpp index 436bf88ec9..fac72a3150 100644 --- a/scene/resources/animation_library.cpp +++ b/scene/resources/animation_library.cpp @@ -143,6 +143,18 @@ Dictionary AnimationLibrary::_get_data() const { return ret; } +void AnimationLibrary::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + String pf = p_function; + if (p_idx == 0 && (pf == "get_animation" || pf == "has_animation" || pf == "rename_animation" || pf == "remove_animation")) { + List<StringName> names; + get_animation_list(&names); + for (const StringName &E : names) { + r_options->push_back(E.operator String().quote()); + } + } + Resource::get_argument_options(p_function, p_idx, r_options); +} + void AnimationLibrary::_bind_methods() { ClassDB::bind_method(D_METHOD("add_animation", "name", "animation"), &AnimationLibrary::add_animation); ClassDB::bind_method(D_METHOD("remove_animation", "name"), &AnimationLibrary::remove_animation); diff --git a/scene/resources/animation_library.h b/scene/resources/animation_library.h index b2152fd7c5..c1d78068bb 100644 --- a/scene/resources/animation_library.h +++ b/scene/resources/animation_library.h @@ -62,6 +62,8 @@ public: Ref<Animation> get_animation(const StringName &p_name) const; void get_animation_list(List<StringName> *p_animations) const; + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; + AnimationLibrary(); }; diff --git a/scene/resources/bone_map.cpp b/scene/resources/bone_map.cpp index 5363b8ec79..97e3af726a 100644 --- a/scene/resources/bone_map.cpp +++ b/scene/resources/bone_map.cpp @@ -77,22 +77,22 @@ void BoneMap::set_profile(const Ref<SkeletonProfile> &p_profile) { notify_property_list_changed(); } -StringName BoneMap::get_skeleton_bone_name(StringName p_profile_bone_name) const { +StringName BoneMap::get_skeleton_bone_name(const StringName &p_profile_bone_name) const { ERR_FAIL_COND_V(!bone_map.has(p_profile_bone_name), StringName()); return bone_map.get(p_profile_bone_name); } -void BoneMap::_set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { +void BoneMap::_set_skeleton_bone_name(const StringName &p_profile_bone_name, const StringName &p_skeleton_bone_name) { ERR_FAIL_COND(!bone_map.has(p_profile_bone_name)); bone_map.insert(p_profile_bone_name, p_skeleton_bone_name); } -void BoneMap::set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name) { +void BoneMap::set_skeleton_bone_name(const StringName &p_profile_bone_name, const StringName &p_skeleton_bone_name) { _set_skeleton_bone_name(p_profile_bone_name, p_skeleton_bone_name); emit_signal("bone_map_updated"); } -StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) const { +StringName BoneMap::find_profile_bone_name(const StringName &p_skeleton_bone_name) const { StringName profile_bone_name; HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); while (E) { @@ -105,7 +105,7 @@ StringName BoneMap::find_profile_bone_name(StringName p_skeleton_bone_name) cons return profile_bone_name; } -int BoneMap::get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const { +int BoneMap::get_skeleton_bone_name_count(const StringName &p_skeleton_bone_name) const { int count = 0; HashMap<StringName, StringName>::ConstIterator E = bone_map.begin(); while (E) { diff --git a/scene/resources/bone_map.h b/scene/resources/bone_map.h index 983a3b7b5a..fbd2f92dee 100644 --- a/scene/resources/bone_map.h +++ b/scene/resources/bone_map.h @@ -53,13 +53,13 @@ public: Ref<SkeletonProfile> get_profile() const; void set_profile(const Ref<SkeletonProfile> &p_profile); - int get_skeleton_bone_name_count(const StringName p_skeleton_bone_name) const; + int get_skeleton_bone_name_count(const StringName &p_skeleton_bone_name) const; - StringName get_skeleton_bone_name(StringName p_profile_bone_name) const; - void set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); - void _set_skeleton_bone_name(StringName p_profile_bone_name, const StringName p_skeleton_bone_name); // Avoid to emit signal for editor. + StringName get_skeleton_bone_name(const StringName &p_profile_bone_name) const; + void set_skeleton_bone_name(const StringName &p_profile_bone_name, const StringName &p_skeleton_bone_name); + void _set_skeleton_bone_name(const StringName &p_profile_bone_name, const StringName &p_skeleton_bone_name); // Avoid to emit signal for editor. - StringName find_profile_bone_name(StringName p_skeleton_bone_name) const; + StringName find_profile_bone_name(const StringName &p_skeleton_bone_name) const; BoneMap(); ~BoneMap(); diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp index b6a40a03a1..2b54acef75 100644 --- a/scene/resources/curve.cpp +++ b/scene/resources/curve.cpp @@ -1046,6 +1046,10 @@ real_t Curve2D::get_bake_interval() const { return bake_interval; } +PackedVector2Array Curve2D::get_points() const { + return _get_data()["points"]; +} + Vector2 Curve2D::get_closest_point(const Vector2 &p_to_point) const { // Brute force method. diff --git a/scene/resources/curve.h b/scene/resources/curve.h index 440e4466f5..e085dfedbd 100644 --- a/scene/resources/curve.h +++ b/scene/resources/curve.h @@ -237,6 +237,7 @@ public: real_t get_baked_length() const; Vector2 sample_baked(real_t p_offset, bool p_cubic = false) const; Transform2D sample_baked_with_rotation(real_t p_offset, bool p_cubic = false) const; + PackedVector2Array get_points() const; PackedVector2Array get_baked_points() const; //useful for going through Vector2 get_closest_point(const Vector2 &p_to_point) const; real_t get_closest_offset(const Vector2 &p_to_point) const; diff --git a/scene/resources/material.cpp b/scene/resources/material.cpp index dba4e4eb34..74b1157e5f 100644 --- a/scene/resources/material.cpp +++ b/scene/resources/material.cpp @@ -1947,7 +1947,7 @@ Ref<Texture2D> BaseMaterial3D::get_texture(TextureParam p_param) const { return textures[p_param]; } -Ref<Texture2D> BaseMaterial3D::get_texture_by_name(StringName p_name) const { +Ref<Texture2D> BaseMaterial3D::get_texture_by_name(const StringName &p_name) const { for (int i = 0; i < (int)BaseMaterial3D::TEXTURE_MAX; i++) { TextureParam param = TextureParam(i); if (p_name == shader_names->texture_names[param]) { diff --git a/scene/resources/material.h b/scene/resources/material.h index 1c698cb104..06522e6470 100644 --- a/scene/resources/material.h +++ b/scene/resources/material.h @@ -682,7 +682,7 @@ public: void set_texture(TextureParam p_param, const Ref<Texture2D> &p_texture); Ref<Texture2D> get_texture(TextureParam p_param) const; // Used only for shader material conversion - Ref<Texture2D> get_texture_by_name(StringName p_name) const; + Ref<Texture2D> get_texture_by_name(const StringName &p_name) const; void set_texture_filter(TextureFilter p_filter); TextureFilter get_texture_filter() const; diff --git a/scene/resources/navigation_mesh.cpp b/scene/resources/navigation_mesh.cpp index 82b5c6257c..dd2e7ef268 100644 --- a/scene/resources/navigation_mesh.cpp +++ b/scene/resources/navigation_mesh.cpp @@ -127,7 +127,7 @@ NavigationMesh::SourceGeometryMode NavigationMesh::get_source_geometry_mode() co return source_geometry_mode; } -void NavigationMesh::set_source_group_name(StringName p_group_name) { +void NavigationMesh::set_source_group_name(const StringName &p_group_name) { source_group_name = p_group_name; } diff --git a/scene/resources/navigation_mesh.h b/scene/resources/navigation_mesh.h index 8b9b810038..cb8880eb94 100644 --- a/scene/resources/navigation_mesh.h +++ b/scene/resources/navigation_mesh.h @@ -122,7 +122,7 @@ public: void set_source_geometry_mode(SourceGeometryMode p_geometry_mode); SourceGeometryMode get_source_geometry_mode() const; - void set_source_group_name(StringName p_group_name); + void set_source_group_name(const StringName &p_group_name); StringName get_source_group_name() const; void set_cell_size(float p_value); diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp index 52840eaa65..2770cb0b87 100644 --- a/scene/resources/navigation_polygon.cpp +++ b/scene/resources/navigation_polygon.cpp @@ -393,7 +393,7 @@ NavigationPolygon::SourceGeometryMode NavigationPolygon::get_source_geometry_mod return source_geometry_mode; } -void NavigationPolygon::set_source_geometry_group_name(StringName p_group_name) { +void NavigationPolygon::set_source_geometry_group_name(const StringName &p_group_name) { source_geometry_group_name = p_group_name; } diff --git a/scene/resources/navigation_polygon.h b/scene/resources/navigation_polygon.h index 4a6a97e2e7..e589ad6dce 100644 --- a/scene/resources/navigation_polygon.h +++ b/scene/resources/navigation_polygon.h @@ -127,7 +127,7 @@ public: void set_source_geometry_mode(SourceGeometryMode p_geometry_mode); SourceGeometryMode get_source_geometry_mode() const; - void set_source_geometry_group_name(StringName p_group_name); + void set_source_geometry_group_name(const StringName &p_group_name); StringName get_source_geometry_group_name() const; void set_agent_radius(real_t p_value); diff --git a/scene/resources/portable_compressed_texture.cpp b/scene/resources/portable_compressed_texture.cpp index a61799c7d6..918b5c0b41 100644 --- a/scene/resources/portable_compressed_texture.cpp +++ b/scene/resources/portable_compressed_texture.cpp @@ -30,6 +30,7 @@ #include "portable_compressed_texture.h" +#include "core/config/project_settings.h" #include "core/io/marshalls.h" #include "scene/resources/bit_map.h" @@ -41,7 +42,8 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { const uint8_t *data = p_data.ptr(); uint32_t data_size = p_data.size(); ERR_FAIL_COND(data_size < 20); - compression_mode = CompressionMode(decode_uint32(data + 0)); + compression_mode = CompressionMode(decode_uint16(data)); + DataFormat data_format = DataFormat(decode_uint16(data + 2)); format = Image::Format(decode_uint32(data + 4)); uint32_t mipmap_count = decode_uint32(data + 8); size.width = decode_uint32(data + 12); @@ -56,6 +58,16 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { switch (compression_mode) { case COMPRESSION_MODE_LOSSLESS: case COMPRESSION_MODE_LOSSY: { + ImageMemLoadFunc loader_func; + if (data_format == DATA_FORMAT_UNDEFINED) { + loader_func = nullptr; + } else if (data_format == DATA_FORMAT_PNG) { + loader_func = Image::_png_mem_unpacker_func; + } else if (data_format == DATA_FORMAT_WEBP) { + loader_func = Image::_webp_mem_loader_func; + } else { + ERR_FAIL(); + } Vector<uint8_t> image_data; ERR_FAIL_COND(data_size < 4); @@ -63,8 +75,10 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { uint32_t mipsize = decode_uint32(data); data += 4; data_size -= 4; - ERR_FAIL_COND(mipsize < data_size); - Ref<Image> img = memnew(Image(data, data_size)); + ERR_FAIL_COND(mipsize > data_size); + Ref<Image> img = loader_func == nullptr + ? memnew(Image(data, data_size)) + : Ref<Image>(loader_func(data, data_size)); ERR_FAIL_COND(img->is_empty()); if (img->get_format() != format) { // May happen due to webp/png in the tiny mipmaps. img->convert(format); @@ -75,7 +89,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { data_size -= mipsize; } - image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, image_data))); + image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, image_data))); } break; case COMPRESSION_MODE_BASIS_UNIVERSAL: { @@ -86,7 +100,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_ETC2: case COMPRESSION_MODE_BPTC: { - image = Ref<Image>(memnew(Image(size.width, size.height, mipmap_count > 1, format, p_data.slice(20)))); + image = Ref<Image>(memnew(Image(size.width, size.height, mipmaps, format, p_data.slice(20)))); } break; } ERR_FAIL_COND(image.is_null()); @@ -99,6 +113,7 @@ void PortableCompressedTexture2D::_set_data(const Vector<uint8_t> &p_data) { } image_stored = true; + size_override = size; RenderingServer::get_singleton()->texture_set_size_override(texture, size_override.width, size_override.height); alpha_cache.unref(); @@ -122,7 +137,8 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C Vector<uint8_t> buffer; buffer.resize(20); - encode_uint32(p_compression_mode, buffer.ptrw()); + encode_uint16(p_compression_mode, buffer.ptrw()); + encode_uint16(DATA_FORMAT_UNDEFINED, buffer.ptrw() + 2); encode_uint32(p_image->get_format(), buffer.ptrw() + 4); encode_uint32(p_image->get_mipmap_count() + 1, buffer.ptrw() + 8); encode_uint32(p_image->get_width(), buffer.ptrw() + 12); @@ -131,12 +147,22 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C switch (p_compression_mode) { case COMPRESSION_MODE_LOSSLESS: case COMPRESSION_MODE_LOSSY: { + bool lossless_force_png = GLOBAL_GET("rendering/textures/lossless_compression/force_png") || + !Image::_webp_mem_loader_func; // WebP module disabled. + bool use_webp = !lossless_force_png && p_image->get_width() <= 16383 && p_image->get_height() <= 16383; // WebP has a size limit. for (int i = 0; i < p_image->get_mipmap_count() + 1; i++) { Vector<uint8_t> data; if (p_compression_mode == COMPRESSION_MODE_LOSSY) { data = Image::webp_lossy_packer(p_image->get_image_from_mipmap(i), p_lossy_quality); + encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); } else { - data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + if (use_webp) { + data = Image::webp_lossless_packer(p_image->get_image_from_mipmap(i)); + encode_uint16(DATA_FORMAT_WEBP, buffer.ptrw() + 2); + } else { + data = Image::png_packer(p_image->get_image_from_mipmap(i)); + encode_uint16(DATA_FORMAT_PNG, buffer.ptrw() + 2); + } } int data_len = data.size(); buffer.resize(buffer.size() + 4); @@ -145,6 +171,7 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C } } break; case COMPRESSION_MODE_BASIS_UNIVERSAL: { + encode_uint16(DATA_FORMAT_BASIS_UNIVERSAL, buffer.ptrw() + 2); Image::UsedChannels uc = p_image->detect_used_channels(p_normal_map ? Image::COMPRESS_SOURCE_NORMAL : Image::COMPRESS_SOURCE_GENERIC); Vector<uint8_t> budata = Image::basis_universal_packer(p_image, uc); buffer.append_array(budata); @@ -153,6 +180,7 @@ void PortableCompressedTexture2D::create_from_image(const Ref<Image> &p_image, C case COMPRESSION_MODE_S3TC: case COMPRESSION_MODE_ETC2: case COMPRESSION_MODE_BPTC: { + encode_uint16(DATA_FORMAT_IMAGE, buffer.ptrw() + 2); Ref<Image> copy = p_image->duplicate(); switch (p_compression_mode) { case COMPRESSION_MODE_S3TC: diff --git a/scene/resources/portable_compressed_texture.h b/scene/resources/portable_compressed_texture.h index 86d80e39f7..3103c2daba 100644 --- a/scene/resources/portable_compressed_texture.h +++ b/scene/resources/portable_compressed_texture.h @@ -39,6 +39,14 @@ class PortableCompressedTexture2D : public Texture2D { GDCLASS(PortableCompressedTexture2D, Texture2D); public: + enum DataFormat { + DATA_FORMAT_UNDEFINED, + DATA_FORMAT_IMAGE, + DATA_FORMAT_PNG, + DATA_FORMAT_WEBP, + DATA_FORMAT_BASIS_UNIVERSAL, + }; + enum CompressionMode { COMPRESSION_MODE_LOSSLESS, COMPRESSION_MODE_LOSSY, diff --git a/scene/resources/resource_format_text.cpp b/scene/resources/resource_format_text.cpp index 29cd9f648d..a97ff5054d 100644 --- a/scene/resources/resource_format_text.cpp +++ b/scene/resources/resource_format_text.cpp @@ -2289,10 +2289,10 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso } String connstr = "[connection"; - connstr += " signal=\"" + String(state->get_connection_signal(i)) + "\""; - connstr += " from=\"" + String(state->get_connection_source(i).simplified()) + "\""; - connstr += " to=\"" + String(state->get_connection_target(i).simplified()) + "\""; - connstr += " method=\"" + String(state->get_connection_method(i)) + "\""; + connstr += " signal=\"" + String(state->get_connection_signal(i)).c_escape() + "\""; + connstr += " from=\"" + String(state->get_connection_source(i).simplified()).c_escape() + "\""; + connstr += " to=\"" + String(state->get_connection_target(i).simplified()).c_escape() + "\""; + connstr += " method=\"" + String(state->get_connection_method(i)).c_escape() + "\""; int flags = state->get_connection_flags(i); if (flags != Object::CONNECT_PERSIST) { connstr += " flags=" + itos(flags); @@ -2319,7 +2319,7 @@ Error ResourceFormatSaverTextInstance::save(const String &p_path, const Ref<Reso if (i == 0) { f->store_line(""); } - f->store_line("[editable path=\"" + editable_instances[i].operator String() + "\"]"); + f->store_line("[editable path=\"" + editable_instances[i].operator String().c_escape() + "\"]"); } } diff --git a/scene/resources/skeleton_profile.cpp b/scene/resources/skeleton_profile.cpp index da4b1f7311..24ed480289 100644 --- a/scene/resources/skeleton_profile.cpp +++ b/scene/resources/skeleton_profile.cpp @@ -180,7 +180,7 @@ StringName SkeletonProfile::get_root_bone() { return root_bone; } -void SkeletonProfile::set_root_bone(StringName p_bone_name) { +void SkeletonProfile::set_root_bone(const StringName &p_bone_name) { if (is_read_only) { return; } @@ -191,7 +191,7 @@ StringName SkeletonProfile::get_scale_base_bone() { return scale_base_bone; } -void SkeletonProfile::set_scale_base_bone(StringName p_bone_name) { +void SkeletonProfile::set_scale_base_bone(const StringName &p_bone_name) { if (is_read_only) { return; } @@ -217,7 +217,7 @@ StringName SkeletonProfile::get_group_name(int p_group_idx) const { return groups[p_group_idx].group_name; } -void SkeletonProfile::set_group_name(int p_group_idx, const StringName p_group_name) { +void SkeletonProfile::set_group_name(int p_group_idx, const StringName &p_group_name) { if (is_read_only) { return; } @@ -254,7 +254,7 @@ void SkeletonProfile::set_bone_size(int p_size) { notify_property_list_changed(); } -int SkeletonProfile::find_bone(StringName p_bone_name) const { +int SkeletonProfile::find_bone(const StringName &p_bone_name) const { if (p_bone_name == StringName()) { return -1; } @@ -271,7 +271,7 @@ StringName SkeletonProfile::get_bone_name(int p_bone_idx) const { return bones[p_bone_idx].bone_name; } -void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName p_bone_name) { +void SkeletonProfile::set_bone_name(int p_bone_idx, const StringName &p_bone_name) { if (is_read_only) { return; } @@ -285,7 +285,7 @@ StringName SkeletonProfile::get_bone_parent(int p_bone_idx) const { return bones[p_bone_idx].bone_parent; } -void SkeletonProfile::set_bone_parent(int p_bone_idx, const StringName p_bone_parent) { +void SkeletonProfile::set_bone_parent(int p_bone_idx, const StringName &p_bone_parent) { if (is_read_only) { return; } @@ -314,7 +314,7 @@ StringName SkeletonProfile::get_bone_tail(int p_bone_idx) const { return bones[p_bone_idx].bone_tail; } -void SkeletonProfile::set_bone_tail(int p_bone_idx, const StringName p_bone_tail) { +void SkeletonProfile::set_bone_tail(int p_bone_idx, const StringName &p_bone_tail) { if (is_read_only) { return; } @@ -356,7 +356,7 @@ StringName SkeletonProfile::get_group(int p_bone_idx) const { return bones[p_bone_idx].group; } -void SkeletonProfile::set_group(int p_bone_idx, const StringName p_group) { +void SkeletonProfile::set_group(int p_bone_idx, const StringName &p_group) { if (is_read_only) { return; } @@ -379,7 +379,7 @@ void SkeletonProfile::set_require(int p_bone_idx, const bool p_require) { emit_signal("profile_updated"); } -bool SkeletonProfile::has_bone(StringName p_bone_name) { +bool SkeletonProfile::has_bone(const StringName &p_bone_name) { bool is_found = false; for (int i = 0; i < bones.size(); i++) { if (bones[i].bone_name == p_bone_name) { diff --git a/scene/resources/skeleton_profile.h b/scene/resources/skeleton_profile.h index 418a051976..143f495c61 100644 --- a/scene/resources/skeleton_profile.h +++ b/scene/resources/skeleton_profile.h @@ -78,16 +78,16 @@ protected: public: StringName get_root_bone(); - void set_root_bone(StringName p_bone_name); + void set_root_bone(const StringName &p_bone_name); StringName get_scale_base_bone(); - void set_scale_base_bone(StringName p_bone_name); + void set_scale_base_bone(const StringName &p_bone_name); int get_group_size(); void set_group_size(int p_size); StringName get_group_name(int p_group_idx) const; - void set_group_name(int p_group_idx, const StringName p_group_name); + void set_group_name(int p_group_idx, const StringName &p_group_name); Ref<Texture2D> get_texture(int p_group_idx) const; void set_texture(int p_group_idx, const Ref<Texture2D> &p_texture); @@ -95,19 +95,19 @@ public: int get_bone_size(); void set_bone_size(int p_size); - int find_bone(const StringName p_bone_name) const; + int find_bone(const StringName &p_bone_name) const; StringName get_bone_name(int p_bone_idx) const; - void set_bone_name(int p_bone_idx, const StringName p_bone_name); + void set_bone_name(int p_bone_idx, const StringName &p_bone_name); StringName get_bone_parent(int p_bone_idx) const; - void set_bone_parent(int p_bone_idx, const StringName p_bone_parent); + void set_bone_parent(int p_bone_idx, const StringName &p_bone_parent); TailDirection get_tail_direction(int p_bone_idx) const; void set_tail_direction(int p_bone_idx, const TailDirection p_tail_direction); StringName get_bone_tail(int p_bone_idx) const; - void set_bone_tail(int p_bone_idx, const StringName p_bone_tail); + void set_bone_tail(int p_bone_idx, const StringName &p_bone_tail); Transform3D get_reference_pose(int p_bone_idx) const; void set_reference_pose(int p_bone_idx, const Transform3D p_reference_pose); @@ -116,12 +116,12 @@ public: void set_handle_offset(int p_bone_idx, const Vector2 p_handle_offset); StringName get_group(int p_bone_idx) const; - void set_group(int p_bone_idx, const StringName p_group); + void set_group(int p_bone_idx, const StringName &p_group); bool is_require(int p_bone_idx) const; void set_require(int p_bone_idx, const bool p_require); - bool has_bone(StringName p_bone_name); + bool has_bone(const StringName &p_bone_name); SkeletonProfile(); ~SkeletonProfile(); diff --git a/scene/resources/theme.cpp b/scene/resources/theme.cpp index d57a0f6b38..b6cd6f6dfe 100644 --- a/scene/resources/theme.cpp +++ b/scene/resources/theme.cpp @@ -322,7 +322,7 @@ void Theme::clear_icon(const StringName &p_name, const StringName &p_theme_type) _emit_theme_changed(true); } -void Theme::get_icon_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_icon_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!icon_map.has(p_theme_type)) { @@ -432,7 +432,7 @@ void Theme::clear_stylebox(const StringName &p_name, const StringName &p_theme_t _emit_theme_changed(true); } -void Theme::get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_stylebox_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!style_map.has(p_theme_type)) { @@ -544,7 +544,7 @@ void Theme::clear_font(const StringName &p_name, const StringName &p_theme_type) _emit_theme_changed(true); } -void Theme::get_font_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_font_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!font_map.has(p_theme_type)) { @@ -643,7 +643,7 @@ void Theme::clear_font_size(const StringName &p_name, const StringName &p_theme_ _emit_theme_changed(true); } -void Theme::get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_font_size_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!font_size_map.has(p_theme_type)) { @@ -729,7 +729,7 @@ void Theme::clear_color(const StringName &p_name, const StringName &p_theme_type _emit_theme_changed(true); } -void Theme::get_color_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_color_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!color_map.has(p_theme_type)) { @@ -815,7 +815,7 @@ void Theme::clear_constant(const StringName &p_name, const StringName &p_theme_t _emit_theme_changed(true); } -void Theme::get_constant_list(StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_constant_list(const StringName &p_theme_type, List<StringName> *p_list) const { ERR_FAIL_NULL(p_list); if (!constant_map.has(p_theme_type)) { @@ -1009,7 +1009,7 @@ void Theme::clear_theme_item(DataType p_data_type, const StringName &p_name, con } } -void Theme::get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const { +void Theme::get_theme_item_list(DataType p_data_type, const StringName &p_theme_type, List<StringName> *p_list) const { switch (p_data_type) { case DATA_TYPE_COLOR: get_color_list(p_theme_type, p_list); @@ -1613,6 +1613,17 @@ void Theme::merge_with(const Ref<Theme> &p_other) { } } + // Defaults. + if (p_other->has_default_font()) { + set_default_font(p_other->default_font); + } + if (p_other->has_default_font_size()) { + set_default_font_size(p_other->default_font_size); + } + if (p_other->has_default_base_scale()) { + set_default_base_scale(p_other->default_base_scale); + } + _unfreeze_and_propagate_changes(); } diff --git a/scene/resources/theme.h b/scene/resources/theme.h index b26b5b5e84..73f1167c29 100644 --- a/scene/resources/theme.h +++ b/scene/resources/theme.h @@ -136,7 +136,7 @@ public: bool has_icon_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_icon(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_icon(const StringName &p_name, const StringName &p_theme_type); - void get_icon_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_icon_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_icon_type(const StringName &p_theme_type); void remove_icon_type(const StringName &p_theme_type); void get_icon_type_list(List<StringName> *p_list) const; @@ -147,7 +147,7 @@ public: bool has_stylebox_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_stylebox(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_stylebox(const StringName &p_name, const StringName &p_theme_type); - void get_stylebox_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_stylebox_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_stylebox_type(const StringName &p_theme_type); void remove_stylebox_type(const StringName &p_theme_type); void get_stylebox_type_list(List<StringName> *p_list) const; @@ -158,7 +158,7 @@ public: bool has_font_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_font(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_font(const StringName &p_name, const StringName &p_theme_type); - void get_font_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_font_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_font_type(const StringName &p_theme_type); void remove_font_type(const StringName &p_theme_type); void get_font_type_list(List<StringName> *p_list) const; @@ -169,7 +169,7 @@ public: bool has_font_size_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_font_size(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_font_size(const StringName &p_name, const StringName &p_theme_type); - void get_font_size_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_font_size_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_font_size_type(const StringName &p_theme_type); void remove_font_size_type(const StringName &p_theme_type); void get_font_size_type_list(List<StringName> *p_list) const; @@ -180,7 +180,7 @@ public: bool has_color_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_color(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_color(const StringName &p_name, const StringName &p_theme_type); - void get_color_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_color_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_color_type(const StringName &p_theme_type); void remove_color_type(const StringName &p_theme_type); void get_color_type_list(List<StringName> *p_list) const; @@ -191,7 +191,7 @@ public: bool has_constant_nocheck(const StringName &p_name, const StringName &p_theme_type) const; void rename_constant(const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_constant(const StringName &p_name, const StringName &p_theme_type); - void get_constant_list(StringName p_theme_type, List<StringName> *p_list) const; + void get_constant_list(const StringName &p_theme_type, List<StringName> *p_list) const; void add_constant_type(const StringName &p_theme_type); void remove_constant_type(const StringName &p_theme_type); void get_constant_type_list(List<StringName> *p_list) const; @@ -202,7 +202,7 @@ public: bool has_theme_item_nocheck(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type) const; void rename_theme_item(DataType p_data_type, const StringName &p_old_name, const StringName &p_name, const StringName &p_theme_type); void clear_theme_item(DataType p_data_type, const StringName &p_name, const StringName &p_theme_type); - void get_theme_item_list(DataType p_data_type, StringName p_theme_type, List<StringName> *p_list) const; + void get_theme_item_list(DataType p_data_type, const StringName &p_theme_type, List<StringName> *p_list) const; void add_theme_item_type(DataType p_data_type, const StringName &p_theme_type); void remove_theme_item_type(DataType p_data_type, const StringName &p_theme_type); void get_theme_item_type_list(DataType p_data_type, List<StringName> *p_list) const; diff --git a/scene/theme/theme_owner.cpp b/scene/theme/theme_owner.cpp index 2852a64e39..b559c8c58d 100644 --- a/scene/theme/theme_owner.cpp +++ b/scene/theme/theme_owner.cpp @@ -249,7 +249,7 @@ void ThemeOwner::get_theme_type_dependencies(const Node *p_for_node, const Strin ThemeDB::get_singleton()->get_native_type_dependencies(p_theme_type, r_list); } -Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { +Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const List<StringName> &p_theme_types) { ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, Variant(), "At least one theme type must be specified."); // First, look through each control or window node in the branch, until no valid parent can be found. @@ -285,7 +285,7 @@ Variant ThemeOwner::get_theme_item_in_types(Theme::DataType p_data_type, const S return global_context->get_fallback_theme()->get_theme_item(p_data_type, p_name, StringName()); } -bool ThemeOwner::has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types) { +bool ThemeOwner::has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const List<StringName> &p_theme_types) { ERR_FAIL_COND_V_MSG(p_theme_types.size() == 0, false, "At least one theme type must be specified."); // First, look through each control or window node in the branch, until no valid parent can be found. diff --git a/scene/theme/theme_owner.h b/scene/theme/theme_owner.h index 4923ccb00b..7e19279c2a 100644 --- a/scene/theme/theme_owner.h +++ b/scene/theme/theme_owner.h @@ -71,8 +71,8 @@ public: void get_theme_type_dependencies(const Node *p_for_node, const StringName &p_theme_type, List<StringName> *r_list) const; - Variant get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); - bool has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, List<StringName> p_theme_types); + Variant get_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const List<StringName> &p_theme_types); + bool has_theme_item_in_types(Theme::DataType p_data_type, const StringName &p_name, const List<StringName> &p_theme_types); float get_theme_default_base_scale(); Ref<Font> get_theme_default_font(); diff --git a/scu_builders.py b/scu_builders.py index 71427eb717..56effb393c 100644 --- a/scu_builders.py +++ b/scu_builders.py @@ -277,6 +277,7 @@ def generate_scu_files(max_includes_per_scu): process_folder(["editor/export"]) process_folder(["editor/gui"]) process_folder(["editor/import"]) + process_folder(["editor/import/3d"]) process_folder(["editor/plugins"]) process_folder(["editor/plugins/gizmos"]) process_folder(["editor/plugins/tiles"]) diff --git a/servers/audio_server.cpp b/servers/audio_server.cpp index 026bc6675a..86bdd13c80 100644 --- a/servers/audio_server.cpp +++ b/servers/audio_server.cpp @@ -1120,7 +1120,7 @@ float AudioServer::get_playback_speed_scale() const { return playback_speed_scale; } -void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time, float p_pitch_scale) { +void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time, float p_pitch_scale) { ERR_FAIL_COND(p_playback.is_null()); HashMap<StringName, Vector<AudioFrame>> map; @@ -1129,7 +1129,7 @@ void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, Str start_playback_stream(p_playback, map, p_start_time, p_pitch_scale); } -void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, HashMap<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time, float p_pitch_scale, float p_highshelf_gain, float p_attenuation_cutoff_hz) { +void AudioServer::start_playback_stream(Ref<AudioStreamPlayback> p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes, float p_start_time, float p_pitch_scale, float p_highshelf_gain, float p_attenuation_cutoff_hz) { ERR_FAIL_COND(p_playback.is_null()); AudioStreamPlaybackListNode *playback_node = new AudioStreamPlaybackListNode(); @@ -1188,7 +1188,7 @@ void AudioServer::stop_playback_stream(Ref<AudioStreamPlayback> p_playback) { } while (!playback_node->state.compare_exchange_strong(old_state, new_state)); } -void AudioServer::set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes) { +void AudioServer::set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volumes) { ERR_FAIL_COND(p_volumes.size() != MAX_CHANNELS_PER_BUS); HashMap<StringName, Vector<AudioFrame>> map; @@ -1197,7 +1197,7 @@ void AudioServer::set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback set_playback_bus_volumes_linear(p_playback, map); } -void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, HashMap<StringName, Vector<AudioFrame>> p_bus_volumes) { +void AudioServer::set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes) { ERR_FAIL_COND(p_bus_volumes.size() > MAX_BUSES_PER_PLAYBACK); AudioStreamPlaybackListNode *playback_node = _find_playback_list_node(p_playback); diff --git a/servers/audio_server.h b/servers/audio_server.h index 9ffa95bc00..4606299c47 100644 --- a/servers/audio_server.h +++ b/servers/audio_server.h @@ -372,13 +372,13 @@ public: float get_playback_speed_scale() const; // Convenience method. - void start_playback_stream(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0, float p_pitch_scale = 1); + void start_playback_stream(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volume_db_vector, float p_start_time = 0, float p_pitch_scale = 1); // Expose all parameters. - void start_playback_stream(Ref<AudioStreamPlayback> p_playback, HashMap<StringName, Vector<AudioFrame>> p_bus_volumes, float p_start_time = 0, float p_pitch_scale = 1, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0); + void start_playback_stream(Ref<AudioStreamPlayback> p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes, float p_start_time = 0, float p_pitch_scale = 1, float p_highshelf_gain = 0, float p_attenuation_cutoff_hz = 0); void stop_playback_stream(Ref<AudioStreamPlayback> p_playback); - void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, StringName p_bus, Vector<AudioFrame> p_volumes); - void set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, HashMap<StringName, Vector<AudioFrame>> p_bus_volumes); + void set_playback_bus_exclusive(Ref<AudioStreamPlayback> p_playback, const StringName &p_bus, Vector<AudioFrame> p_volumes); + void set_playback_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, const HashMap<StringName, Vector<AudioFrame>> &p_bus_volumes); void set_playback_all_bus_volumes_linear(Ref<AudioStreamPlayback> p_playback, Vector<AudioFrame> p_volumes); void set_playback_pitch_scale(Ref<AudioStreamPlayback> p_playback, float p_pitch_scale); void set_playback_paused(Ref<AudioStreamPlayback> p_playback, bool p_paused); diff --git a/servers/rendering/dummy/rasterizer_dummy.h b/servers/rendering/dummy/rasterizer_dummy.h index 179c41f286..929c661009 100644 --- a/servers/rendering/dummy/rasterizer_dummy.h +++ b/servers/rendering/dummy/rasterizer_dummy.h @@ -89,6 +89,8 @@ public: void prepare_for_blitting_render_targets() override {} void blit_render_targets_to_screen(int p_screen, const BlitToScreen *p_render_targets, int p_amount) override {} + void end_viewport(bool p_swap_buffers) override {} + void end_frame(bool p_swap_buffers) override { if (p_swap_buffers) { DisplayServer::get_singleton()->swap_buffers(); diff --git a/servers/rendering/renderer_canvas_cull.cpp b/servers/rendering/renderer_canvas_cull.cpp index d21a985a60..caeac5cd95 100644 --- a/servers/rendering/renderer_canvas_cull.cpp +++ b/servers/rendering/renderer_canvas_cull.cpp @@ -2165,6 +2165,30 @@ bool RendererCanvasCull::free(RID p_rid) { return true; } +template <class T> +void RendererCanvasCull::_free_rids(T &p_owner, const char *p_type) { + List<RID> owned; + p_owner.get_owned_list(&owned); + if (owned.size()) { + if (owned.size() == 1) { + WARN_PRINT(vformat("1 RID of type \"%s\" was leaked.", p_type)); + } else { + WARN_PRINT(vformat("%d RIDs of type \"%s\" were leaked.", owned.size(), p_type)); + } + for (const RID &E : owned) { + free(E); + } + } +} + +void RendererCanvasCull::finalize() { + _free_rids(canvas_owner, "Canvas"); + _free_rids(canvas_item_owner, "CanvasItem"); + _free_rids(canvas_light_owner, "CanvasLight"); + _free_rids(canvas_light_occluder_owner, "CanvasLightOccluder"); + _free_rids(canvas_light_occluder_polygon_owner, "CanvasLightOccluderPolygon"); +} + RendererCanvasCull::RendererCanvasCull() { z_list = (RendererCanvasRender::Item **)memalloc(z_range * sizeof(RendererCanvasRender::Item *)); z_last_list = (RendererCanvasRender::Item **)memalloc(z_range * sizeof(RendererCanvasRender::Item *)); diff --git a/servers/rendering/renderer_canvas_cull.h b/servers/rendering/renderer_canvas_cull.h index ae0f29f10e..de61e28204 100644 --- a/servers/rendering/renderer_canvas_cull.h +++ b/servers/rendering/renderer_canvas_cull.h @@ -170,6 +170,9 @@ public: RID_Owner<Item, true> canvas_item_owner; RID_Owner<RendererCanvasRender::Light, true> canvas_light_owner; + template <class T> + void _free_rids(T &p_owner, const char *p_type); + bool disable_scale; bool sdf_used = false; bool snapping_2d_transforms_to_pixel = false; @@ -329,6 +332,9 @@ public: Rect2 _debug_canvas_item_get_rect(RID p_item); bool free(RID p_rid); + + void finalize(); + RendererCanvasCull(); ~RendererCanvasCull(); }; diff --git a/servers/rendering/renderer_canvas_render.cpp b/servers/rendering/renderer_canvas_render.cpp index af0ec621e5..6eb7bab9c9 100644 --- a/servers/rendering/renderer_canvas_render.cpp +++ b/servers/rendering/renderer_canvas_render.cpp @@ -31,6 +31,8 @@ #include "renderer_canvas_render.h" #include "servers/rendering/rendering_server_globals.h" +RendererCanvasRender *RendererCanvasRender::singleton = nullptr; + const Rect2 &RendererCanvasRender::Item::get_rect() const { if (custom_rect || (!rect_dirty && !update_when_visible && skeleton == RID())) { return rect; diff --git a/servers/rendering/renderer_compositor.cpp b/servers/rendering/renderer_compositor.cpp index a6083fe70d..c36da51e50 100644 --- a/servers/rendering/renderer_compositor.cpp +++ b/servers/rendering/renderer_compositor.cpp @@ -31,8 +31,6 @@ #include "renderer_compositor.h" #include "core/config/project_settings.h" -#include "core/os/os.h" -#include "core/string/print_string.h" #include "servers/xr_server.h" RendererCompositor *RendererCompositor::singleton = nullptr; @@ -57,5 +55,3 @@ RendererCompositor::RendererCompositor() { xr_enabled = XRServer::get_xr_mode() == XRServer::XRMODE_ON; } } - -RendererCanvasRender *RendererCanvasRender::singleton = nullptr; diff --git a/servers/rendering/renderer_compositor.h b/servers/rendering/renderer_compositor.h index ff7792741c..13767a3875 100644 --- a/servers/rendering/renderer_compositor.h +++ b/servers/rendering/renderer_compositor.h @@ -99,6 +99,7 @@ public: virtual void prepare_for_blitting_render_targets() = 0; virtual void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount) = 0; + virtual void end_viewport(bool p_swap_buffers) = 0; virtual void end_frame(bool p_swap_buffers) = 0; virtual void finalize() = 0; virtual uint64_t get_frame_number() const = 0; diff --git a/servers/rendering/renderer_rd/api_context_rd.h b/servers/rendering/renderer_rd/api_context_rd.h index 22167be3c1..fd3be80605 100644 --- a/servers/rendering/renderer_rd/api_context_rd.h +++ b/servers/rendering/renderer_rd/api_context_rd.h @@ -58,16 +58,12 @@ public: virtual void set_setup_buffer(RDD::CommandBufferID p_command_buffer) = 0; virtual void append_command_buffer(RDD::CommandBufferID p_command_buffer) = 0; - virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false) = 0; + virtual void flush(bool p_flush_setup = false, bool p_flush_pending = false, bool p_sync = true) = 0; virtual Error prepare_buffers(RDD::CommandBufferID p_command_buffer) = 0; virtual void postpare_buffers(RDD::CommandBufferID p_command_buffer) = 0; virtual Error swap_buffers() = 0; virtual Error initialize() = 0; - virtual void command_begin_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) = 0; - virtual void command_insert_label(RDD::CommandBufferID p_command_buffer, String p_label_name, const Color &p_color) = 0; - virtual void command_end_label(RDD::CommandBufferID p_command_buffer) = 0; - virtual String get_device_vendor_name() const = 0; virtual String get_device_name() const = 0; virtual RDD::DeviceType get_device_type() const = 0; @@ -78,6 +74,7 @@ public: virtual DisplayServer::VSyncMode get_vsync_mode(DisplayServer::WindowID p_window = 0) const = 0; virtual RenderingDeviceDriver *get_driver(RID p_local_device = RID()) = 0; + virtual bool is_debug_utils_enabled() const = 0; virtual ~ApiContextRD(); }; diff --git a/servers/rendering/renderer_rd/cluster_builder_rd.cpp b/servers/rendering/renderer_rd/cluster_builder_rd.cpp index 0d1721039c..e661fd9217 100644 --- a/servers/rendering/renderer_rd/cluster_builder_rd.cpp +++ b/servers/rendering/renderer_rd/cluster_builder_rd.cpp @@ -420,11 +420,11 @@ void ClusterBuilderRD::bake_cluster() { RD::get_singleton()->draw_command_begin_label("Bake Light Cluster"); // Clear cluster buffer. - RD::get_singleton()->buffer_clear(cluster_buffer, 0, cluster_buffer_size, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_clear(cluster_buffer, 0, cluster_buffer_size); if (render_element_count > 0) { // Clear render buffer. - RD::get_singleton()->buffer_clear(cluster_render_buffer, 0, cluster_render_buffer_size, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_clear(cluster_render_buffer, 0, cluster_render_buffer_size); { // Fill state uniform. @@ -439,18 +439,18 @@ void ClusterBuilderRD::bake_cluster() { state.cluster_depth_offset = (render_element_max / 32); state.cluster_data_size = state.cluster_depth_offset + render_element_max; - RD::get_singleton()->buffer_update(state_uniform, 0, sizeof(StateUniform), &state, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(state_uniform, 0, sizeof(StateUniform), &state); } // Update instances. - RD::get_singleton()->buffer_update(element_buffer, 0, sizeof(RenderElementData) * render_element_count, render_elements, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(element_buffer, 0, sizeof(RenderElementData) * render_element_count, render_elements); RENDER_TIMESTAMP("Render 3D Cluster Elements"); // Render elements. { - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); ClusterBuilderSharedDataRD::ClusterRender::PushConstant push_constant = {}; RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, shared->cluster_render.shader_pipelines[use_msaa ? ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_MSAA : ClusterBuilderSharedDataRD::ClusterRender::PIPELINE_NORMAL]); @@ -488,7 +488,7 @@ void ClusterBuilderRD::bake_cluster() { RD::get_singleton()->draw_list_draw(draw_list, true, instances); i += instances; } - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->draw_list_end(); } // Store elements. RENDER_TIMESTAMP("Pack 3D Cluster Elements"); @@ -513,10 +513,8 @@ void ClusterBuilderRD::bake_cluster() { RD::get_singleton()->compute_list_dispatch_threads(compute_list, cluster_screen_size.x, cluster_screen_size.y, 1); - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->compute_list_end(); } - } else { - RD::get_singleton()->barrier(RD::BARRIER_MASK_TRANSFER, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); } RENDER_TIMESTAMP("< Bake 3D Cluster"); RD::get_singleton()->draw_command_end_label(); diff --git a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp index cc5031823e..e6262c83e2 100644 --- a/servers/rendering/renderer_rd/effects/bokeh_dof.cpp +++ b/servers/rendering/renderer_rd/effects/bokeh_dof.cpp @@ -356,7 +356,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr ERR_FAIL_COND(shader.is_null()); RID framebuffer = p_buffers.base_weight_fb; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[BOKEH_GEN_BLUR_SIZE].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_depth_texture), 0); @@ -388,7 +388,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; // Pass 1 - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); @@ -412,7 +412,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RD::Uniform texture = bokeh.push_constant.half_size ? u_half_texture0 : u_secondary_texture; RD::Uniform weight = bokeh.push_constant.half_size ? u_weight_texture2 : u_weight_texture1; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, weight), 1); @@ -430,7 +430,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr framebuffer = p_buffers.base_fb; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture1), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture3), 1); @@ -463,7 +463,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr RID framebuffer = bokeh.push_constant.half_size ? p_buffers.half_fb[0] : p_buffers.secondary_fb; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_base_texture), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture0), 1); @@ -481,7 +481,7 @@ void BokehDOF::bokeh_dof_raster(const BokehBuffers &p_buffers, RID p_camera_attr framebuffer = p_buffers.base_fb; - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, bokeh.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture0), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_weight_texture2), 1); diff --git a/servers/rendering/renderer_rd/effects/copy_effects.cpp b/servers/rendering/renderer_rd/effects/copy_effects.cpp index bb584beb52..fd6409d6bf 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.cpp +++ b/servers/rendering/renderer_rd/effects/copy_effects.cpp @@ -583,7 +583,7 @@ void CopyEffects::copy_to_fb_rect(RID p_source_rd_texture, RID p_dest_framebuffe RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_rect); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); if (p_secondary.is_valid()) { @@ -650,7 +650,7 @@ void CopyEffects::copy_raster(RID p_source_texture, RID p_dest_framebuffer) { ERR_FAIL_COND(shader.is_null()); // Just copy it back (we use our blur raster shader here).. - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[BLUR_MODE_COPY].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); @@ -724,7 +724,7 @@ void CopyEffects::gaussian_blur_raster(RID p_source_rd_texture, RID p_dest_textu RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, blur_mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); @@ -826,7 +826,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu ERR_FAIL_COND(shader.is_null()); //HORIZONTAL - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(half_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(half_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); if (p_auto_exposure.is_valid() && p_first_pass) { @@ -846,7 +846,7 @@ void CopyEffects::gaussian_glow_raster(RID p_source_rd_texture, RID p_half_textu ERR_FAIL_COND(shader.is_null()); //VERTICAL - draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[blur_mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_half_texture), 0); @@ -916,7 +916,7 @@ void CopyEffects::make_mipmap_raster(RID p_source_rd_texture, RID p_dest_texture RID shader = blur_raster.shader.version_get_shader(blur_raster.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, blur_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); @@ -982,7 +982,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con RID shader = copy_to_fb.shader.version_get_shader(copy_to_fb.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, p_region); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, copy_to_fb.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(dest_framebuffer))); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, ©_to_fb.push_constant, sizeof(CopyToFbPushConstant)); @@ -990,7 +990,7 @@ void CopyEffects::set_color_raster(RID p_dest_texture, const Color &p_color, con RD::get_singleton()->draw_list_end(); } -void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier) { +void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -1015,14 +1015,14 @@ void CopyEffects::copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuf RID shader = cube_to_dp.shader.version_get_shader(cube_to_dp.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cube_to_dp.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); RD::get_singleton()->draw_list_bind_index_array(draw_list, material_storage->get_quad_index_array()); RD::get_singleton()->draw_list_set_push_constant(draw_list, &push_constant, sizeof(CopyToDPPushConstant)); RD::get_singleton()->draw_list_draw(draw_list, true); - RD::get_singleton()->draw_list_end(p_post_barrier); + RD::get_singleton()->draw_list_end(); } void CopyEffects::cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size) { @@ -1080,7 +1080,7 @@ void CopyEffects::cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_fra RID shader = cubemap_downsampler.raster_shader.version_get_shader(cubemap_downsampler.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, cubemap_downsampler.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); @@ -1159,7 +1159,7 @@ void CopyEffects::cubemap_filter_raster(RID p_source_cubemap, RID p_dest_framebu RID shader = filter.raster_shader.version_get_shader(filter.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, filter.raster_pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_cubemap), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, filter.uniform_set, 1); @@ -1237,7 +1237,7 @@ void CopyEffects::cubemap_roughness_raster(RID p_source_rd_texture, RID p_dest_f RID shader = roughness.raster_shader.version_get_shader(roughness.shader_version, 0); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, roughness.raster_pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); @@ -1257,7 +1257,7 @@ void CopyEffects::merge_specular(RID p_dest_framebuffer, RID p_specular, RID p_b RD::get_singleton()->draw_command_begin_label("Merge specular"); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, Vector<Color>()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, Vector<Color>()); int mode; if (p_reflection.is_valid()) { diff --git a/servers/rendering/renderer_rd/effects/copy_effects.h b/servers/rendering/renderer_rd/effects/copy_effects.h index 60272a2eab..d18971a676 100644 --- a/servers/rendering/renderer_rd/effects/copy_effects.h +++ b/servers/rendering/renderer_rd/effects/copy_effects.h @@ -345,7 +345,7 @@ public: void set_color(RID p_dest_texture, const Color &p_color, const Rect2i &p_region, bool p_8bit_dst = false); void set_color_raster(RID p_dest_texture, const Color &p_color, const Rect2i &p_region); - void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip, BitField<RD::BarrierMask> p_post_barrier = RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_TRANSFER); + void copy_cubemap_to_dp(RID p_source_rd_texture, RID p_dst_framebuffer, const Rect2 &p_rect, const Vector2 &p_dst_size, float p_z_near, float p_z_far, bool p_dp_flip); void cubemap_downsample(RID p_source_cubemap, RID p_dest_cubemap, const Size2i &p_size); void cubemap_downsample_raster(RID p_source_cubemap, RID p_dest_framebuffer, uint32_t p_face_id, const Size2i &p_size); void cubemap_filter(RID p_source_cubemap, Vector<RID> p_dest_cubemap, bool p_use_array); diff --git a/servers/rendering/renderer_rd/effects/debug_effects.cpp b/servers/rendering/renderer_rd/effects/debug_effects.cpp index 3033d42375..a57a65fd5a 100644 --- a/servers/rendering/renderer_rd/effects/debug_effects.cpp +++ b/servers/rendering/renderer_rd/effects/debug_effects.cpp @@ -282,7 +282,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj // And draw our frustum. RD::FramebufferFormatID fb_format_id = RD::get_singleton()->framebuffer_get_format(p_dest_fb); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect); RID pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline); @@ -326,7 +326,7 @@ void DebugEffects::draw_shadow_frustum(RID p_light, const Projection &p_cam_proj rect.size.x *= atlas_rect_norm.size.x; rect.size.y *= atlas_rect_norm.size.y; - draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect); + draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, rect); pipeline = shadow_frustum.pipelines[SFP_TRANSPARENT].get_render_pipeline(frustum.vertex_format, fb_format_id); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, pipeline); @@ -351,7 +351,7 @@ void DebugEffects::draw_motion_vectors(RID p_velocity, RID p_depth, RID p_dest_f RD::Uniform u_source_velocity(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, p_velocity })); RD::Uniform u_source_depth(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 1, Vector<RID>({ default_sampler, p_depth })); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_fb, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, motion_vectors.pipeline.get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_fb), false, RD::get_singleton()->draw_list_get_current_pass())); Projection reprojection = p_previous_projection.flipped_y() * p_previous_transform.affine_inverse() * p_current_transform * p_current_projection.flipped_y().inverse(); diff --git a/servers/rendering/renderer_rd/effects/fsr.cpp b/servers/rendering/renderer_rd/effects/fsr.cpp index 0c51adf9ee..5599a5a162 100644 --- a/servers/rendering/renderer_rd/effects/fsr.cpp +++ b/servers/rendering/renderer_rd/effects/fsr.cpp @@ -124,5 +124,5 @@ void FSR::fsr_upscale(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_source_r RD::get_singleton()->compute_list_dispatch(compute_list, dispatch_x, dispatch_y, 1); - RD::get_singleton()->compute_list_end(compute_list); + RD::get_singleton()->compute_list_end(); } diff --git a/servers/rendering/renderer_rd/effects/fsr2.cpp b/servers/rendering/renderer_rd/effects/fsr2.cpp index 0c38989304..bebbf51d51 100644 --- a/servers/rendering/renderer_rd/effects/fsr2.cpp +++ b/servers/rendering/renderer_rd/effects/fsr2.cpp @@ -377,10 +377,7 @@ static FfxErrorCode execute_gpu_job_copy_rd(FSR2Context::Scratch &p_scratch, con ERR_FAIL_COND_V(dst_desc.type == FFX_RESOURCE_TYPE_BUFFER, FFX_ERROR_INVALID_ARGUMENT); for (uint32_t mip_level = 0; mip_level < src_desc.mipCount; mip_level++) { - // Only push the barriers on the last copy. - // FIXME: This could be optimized if RenderingDevice was able to copy multiple mip levels in a single command. - BitField<RD::BarrierMask> post_barrier = (mip_level == (src_desc.mipCount - 1)) ? RD::BARRIER_MASK_ALL_BARRIERS : RD::BARRIER_MASK_NO_BARRIER; - RD::get_singleton()->texture_copy(src, dst, Vector3(0, 0, 0), Vector3(0, 0, 0), Vector3(src_desc.width, src_desc.height, src_desc.depth), mip_level, mip_level, 0, 0, post_barrier); + RD::get_singleton()->texture_copy(src, dst, Vector3(0, 0, 0), Vector3(0, 0, 0), Vector3(src_desc.width, src_desc.height, src_desc.depth), mip_level, mip_level, 0, 0); } return FFX_OK; @@ -435,8 +432,7 @@ static FfxErrorCode execute_gpu_job_compute_rd(FSR2Context::Scratch &p_scratch, RID buffer_rid = p_scratch.ubo_ring_buffer[p_scratch.ubo_ring_buffer_index]; p_scratch.ubo_ring_buffer_index = (p_scratch.ubo_ring_buffer_index + 1) % FSR2_UBO_RING_BUFFER_SIZE; - BitField<RD::BarrierMask> post_barrier = (i == (p_job.pipeline.constCount - 1)) ? RD::BARRIER_MASK_ALL_BARRIERS : RD::BARRIER_MASK_NO_BARRIER; - RD::get_singleton()->buffer_update(buffer_rid, 0, p_job.cbs[i].uint32Size * sizeof(uint32_t), p_job.cbs[i].data, post_barrier); + RD::get_singleton()->buffer_update(buffer_rid, 0, p_job.cbs[i].uint32Size * sizeof(uint32_t), p_job.cbs[i].data); RD::Uniform buffer_uniform(RD::UNIFORM_TYPE_UNIFORM_BUFFER, p_job.pipeline.cbResourceBindings[i].slotIndex, buffer_rid); compute_uniforms.push_back(buffer_uniform); @@ -566,7 +562,6 @@ FSR2Effect::FSR2Effect() { FfxResourceBinding{ 2, 0, L"r_dilatedDepth" }, FfxResourceBinding{ 3, 0, L"r_reactive_mask" }, FfxResourceBinding{ 4, 0, L"r_transparency_and_composition_mask" }, - FfxResourceBinding{ 5, 0, L"r_prepared_input_color" }, FfxResourceBinding{ 6, 0, L"r_previous_dilated_motion_vectors" }, FfxResourceBinding{ 7, 0, L"r_input_motion_vectors" }, FfxResourceBinding{ 8, 0, L"r_input_color_jittered" }, diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp index 3aa5f5706e..61b2248b5c 100644 --- a/servers/rendering/renderer_rd/effects/luminance.cpp +++ b/servers/rendering/renderer_rd/effects/luminance.cpp @@ -184,7 +184,7 @@ void Luminance::luminance_reduction(RID p_source_texture, const Size2i p_source_ RD::Uniform u_source_texture(RD::UNIFORM_TYPE_SAMPLER_WITH_TEXTURE, 0, Vector<RID>({ default_sampler, i == 0 ? p_source_texture : p_luminance_buffers->reduce[i - 1] })); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, luminance_reduce_raster.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_texture), 0); if (final) { diff --git a/servers/rendering/renderer_rd/effects/resolve.cpp b/servers/rendering/renderer_rd/effects/resolve.cpp index 18671d06e1..feb0e6ed1e 100644 --- a/servers/rendering/renderer_rd/effects/resolve.cpp +++ b/servers/rendering/renderer_rd/effects/resolve.cpp @@ -54,7 +54,7 @@ Resolve::~Resolve() { resolve.shader.version_free(resolve.shader_version); } -void Resolve::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) { +void Resolve::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -93,10 +93,10 @@ void Resolve::resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1); - RD::get_singleton()->compute_list_end(p_barrier); + RD::get_singleton()->compute_list_end(); } -void Resolve::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier) { +void Resolve::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples) { UniformSetCacheRD *uniform_set_cache = UniformSetCacheRD::get_singleton(); ERR_FAIL_NULL(uniform_set_cache); MaterialStorage *material_storage = MaterialStorage::get_singleton(); @@ -126,5 +126,5 @@ void Resolve::resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_scr RD::get_singleton()->compute_list_dispatch_threads(compute_list, p_screen_size.x, p_screen_size.y, 1); - RD::get_singleton()->compute_list_end(p_barrier); + RD::get_singleton()->compute_list_end(); } diff --git a/servers/rendering/renderer_rd/effects/resolve.h b/servers/rendering/renderer_rd/effects/resolve.h index fcc1021904..14477f90e4 100644 --- a/servers/rendering/renderer_rd/effects/resolve.h +++ b/servers/rendering/renderer_rd/effects/resolve.h @@ -65,8 +65,8 @@ public: Resolve(); ~Resolve(); - void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); - void resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); + void resolve_gi(RID p_source_depth, RID p_source_normal_roughness, RID p_source_voxel_gi, RID p_dest_depth, RID p_dest_normal_roughness, RID p_dest_voxel_gi, Vector2i p_screen_size, int p_samples); + void resolve_depth(RID p_source_depth, RID p_dest_depth, Vector2i p_screen_size, int p_samples); }; } // namespace RendererRD diff --git a/servers/rendering/renderer_rd/effects/ss_effects.cpp b/servers/rendering/renderer_rd/effects/ss_effects.cpp index 628edc0127..46fb0a75d6 100644 --- a/servers/rendering/renderer_rd/effects/ss_effects.cpp +++ b/servers/rendering/renderer_rd/effects/ss_effects.cpp @@ -525,7 +525,7 @@ void SSEffects::downsample_depth(Ref<RenderSceneBuffersRD> p_render_buffers, uin RD::get_singleton()->compute_list_add_barrier(compute_list); RD::get_singleton()->draw_command_end_label(); - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->compute_list_end(); ss_effects.used_full_mips_last_frame = use_full_mips; ss_effects.used_half_size_last_frame = use_half_size; @@ -950,10 +950,10 @@ void SSEffects::screen_space_indirect_lighting(Ref<RenderSceneBuffersRD> p_rende RD::get_singleton()->draw_command_end_label(); // SSIL - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. + RD::get_singleton()->compute_list_end(); int zero[1] = { 0 }; - RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier + RD::get_singleton()->buffer_update(ssil.importance_map_load_counter, 0, sizeof(uint32_t), &zero); } /* SSAO */ @@ -1332,10 +1332,10 @@ void SSEffects::generate_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, SSAORe RD::get_singleton()->draw_command_end_label(); // Interleave } RD::get_singleton()->draw_command_end_label(); //SSAO - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_TRANSFER); // Zeroing importance_map_load_counter depends on us. + RD::get_singleton()->compute_list_end(); int zero[1] = { 0 }; - RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero, 0); //no barrier + RD::get_singleton()->buffer_update(ssao.importance_map_load_counter, 0, sizeof(uint32_t), &zero); } /* Screen Space Reflection */ @@ -1394,7 +1394,7 @@ void SSEffects::screen_space_reflection(Ref<RenderSceneBuffersRD> p_render_buffe scene_data.eye_offset[v][3] = 0.0; } - RD::get_singleton()->buffer_update(ssr.ubo, 0, sizeof(ScreenSpaceReflectionSceneData), &scene_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(ssr.ubo, 0, sizeof(ScreenSpaceReflectionSceneData), &scene_data); } uint32_t pipeline_specialization = 0; diff --git a/servers/rendering/renderer_rd/effects/tone_mapper.cpp b/servers/rendering/renderer_rd/effects/tone_mapper.cpp index 48c6511408..ee0b6adb4d 100644 --- a/servers/rendering/renderer_rd/effects/tone_mapper.cpp +++ b/servers/rendering/renderer_rd/effects/tone_mapper.cpp @@ -166,7 +166,7 @@ void ToneMapper::tonemapper(RID p_source_color, RID p_dst_framebuffer, const Ton RID shader = tonemap.shader.version_get_shader(tonemap.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dst_framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, tonemap.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dst_framebuffer), false, RD::get_singleton()->draw_list_get_current_pass())); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_color), 0); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 1, u_exposure_texture), 1); diff --git a/servers/rendering/renderer_rd/effects/vrs.cpp b/servers/rendering/renderer_rd/effects/vrs.cpp index 63c99facdd..41a6b2d622 100644 --- a/servers/rendering/renderer_rd/effects/vrs.cpp +++ b/servers/rendering/renderer_rd/effects/vrs.cpp @@ -82,7 +82,7 @@ void VRS::copy_vrs(RID p_source_rd_texture, RID p_dest_framebuffer, bool p_multi RID shader = vrs_shader.shader.version_get_shader(vrs_shader.shader_version, mode); ERR_FAIL_COND(shader.is_null()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, Vector<Color>()); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_dest_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, Vector<Color>()); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, vrs_shader.pipelines[mode].get_render_pipeline(RD::INVALID_ID, RD::get_singleton()->framebuffer_get_format(p_dest_framebuffer))); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, uniform_set_cache->get_cache(shader, 0, u_source_rd_texture), 0); // RD::get_singleton()->draw_list_set_push_constant(draw_list, &vrs_shader.push_constant, sizeof(VRSPushConstant)); diff --git a/servers/rendering/renderer_rd/environment/fog.cpp b/servers/rendering/renderer_rd/environment/fog.cpp index 2befb194f7..78ab6f3650 100644 --- a/servers/rendering/renderer_rd/environment/fog.cpp +++ b/servers/rendering/renderer_rd/environment/fog.cpp @@ -570,7 +570,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RendererRD::MaterialStorage::store_transform(to_prev_cam_view, params.to_prev_view); RendererRD::MaterialStorage::store_transform(p_cam_transform, params.transform); - RD::get_singleton()->buffer_update(volumetric_fog.volume_ubo, 0, sizeof(VolumetricFogShader::VolumeUBO), ¶ms, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(volumetric_fog.volume_ubo, 0, sizeof(VolumetricFogShader::VolumeUBO), ¶ms); if (fog->fog_uniform_set.is_null() || !RD::get_singleton()->uniform_set_is_valid(fog->fog_uniform_set)) { Vector<RD::Uniform> uniforms; @@ -1086,7 +1086,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->draw_command_begin_label("Render Volumetric Fog"); RENDER_TIMESTAMP("Render Fog"); - RD::get_singleton()->buffer_update(volumetric_fog.params_ubo, 0, sizeof(VolumetricFogShader::ParamsUBO), ¶ms, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(volumetric_fog.params_ubo, 0, sizeof(VolumetricFogShader::ParamsUBO), ¶ms); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); @@ -1140,7 +1140,7 @@ void Fog::volumetric_fog_update(const VolumetricFogSettings &p_settings, const P RD::get_singleton()->compute_list_bind_uniform_set(compute_list, fog->gi_dependent_sets.process_uniform_set, 0); RD::get_singleton()->compute_list_dispatch_threads(compute_list, fog->width, fog->height, 1); - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_RASTER); + RD::get_singleton()->compute_list_end(); RENDER_TIMESTAMP("< Volumetric Fog"); RD::get_singleton()->draw_command_end_label(); diff --git a/servers/rendering/renderer_rd/environment/gi.cpp b/servers/rendering/renderer_rd/environment/gi.cpp index d968736037..6da828df45 100644 --- a/servers/rendering/renderer_rd/environment/gi.cpp +++ b/servers/rendering/renderer_rd/environment/gi.cpp @@ -583,7 +583,8 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re /* Buffers */ cascade.solid_cell_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGI::Cascade::SolidCell) * solid_cell_count); - cascade.solid_cell_dispatch_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); + cascade.solid_cell_dispatch_buffer_storage = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>()); + cascade.solid_cell_dispatch_buffer_call = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4, Vector<uint8_t>(), RD::STORAGE_BUFFER_USAGE_DISPATCH_INDIRECT); cascade.lights_buffer = RD::get_singleton()->storage_buffer_create(sizeof(SDFGIShader::Light) * MAX(SDFGI::MAX_STATIC_LIGHTS, SDFGI::MAX_DYNAMIC_LIGHTS)); { Vector<RD::Uniform> uniforms; @@ -650,7 +651,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 10; - u.append_id(cascade.solid_cell_dispatch_buffer); + u.append_id(cascade.solid_cell_dispatch_buffer_storage); uniforms.push_back(u); } { @@ -698,7 +699,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 5; - u.append_id(cascade.solid_cell_dispatch_buffer); + u.append_id(cascade.solid_cell_dispatch_buffer_storage); uniforms.push_back(u); } { @@ -761,7 +762,7 @@ void GI::SDFGI::create(RID p_env, const Vector3 &p_world_position, uint32_t p_re RD::Uniform u; u.binding = 3; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; - u.append_id(cascade.solid_cell_dispatch_buffer); + u.append_id(cascade.solid_cell_dispatch_buffer_storage); uniforms.push_back(u); } { @@ -1129,7 +1130,8 @@ GI::SDFGI::~SDFGI() { RD::get_singleton()->free(c.light_aniso_0_tex); RD::get_singleton()->free(c.light_aniso_1_tex); RD::get_singleton()->free(c.sdf_tex); - RD::get_singleton()->free(c.solid_cell_dispatch_buffer); + RD::get_singleton()->free(c.solid_cell_dispatch_buffer_storage); + RD::get_singleton()->free(c.solid_cell_dispatch_buffer_call); RD::get_singleton()->free(c.solid_cell_buffer); RD::get_singleton()->free(c.lightprobe_history_tex); RD::get_singleton()->free(c.lightprobe_average_tex); @@ -1238,6 +1240,10 @@ void GI::SDFGI::update(RID p_env, const Vector3 &p_world_position) { void GI::SDFGI::update_light() { RD::get_singleton()->draw_command_begin_label("SDFGI Update dynamic Light"); + for (uint32_t i = 0; i < cascades.size(); i++) { + RD::get_singleton()->buffer_copy(cascades[i].solid_cell_dispatch_buffer_storage, cascades[i].solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); + } + /* Update dynamic light */ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); @@ -1276,9 +1282,9 @@ void GI::SDFGI::update_light() { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascade.sdf_direct_light_dynamic_uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::DirectLightPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer, 0); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascade.solid_cell_dispatch_buffer_call, 0); } - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } @@ -1351,7 +1357,7 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { render_pass++; - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.integrate_pipeline[SDFGIShader::INTEGRATE_MODE_PROCESS]); int32_t probe_divisor = cascade_size / SDFGI::PROBE_DIVISOR; @@ -1368,14 +1374,11 @@ void GI::SDFGI::update_probes(RID p_env, SkyRD::Sky *p_sky) { RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count, probe_axis_count, 1); } - //end later after raster to avoid barriering on layout changes - //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); - + RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } void GI::SDFGI::store_probes() { - RD::get_singleton()->barrier(RD::BARRIER_MASK_COMPUTE, RD::BARRIER_MASK_COMPUTE); RD::get_singleton()->draw_command_begin_label("SDFGI Store Probes"); SDFGIShader::IntegratePushConstant push_constant; @@ -1414,7 +1417,7 @@ void GI::SDFGI::store_probes() { RD::get_singleton()->compute_list_dispatch_threads(compute_list, probe_axis_count * probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, probe_axis_count * SDFGI::LIGHTPROBE_OCT_SIZE, 1); } - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } @@ -1493,7 +1496,7 @@ void GI::SDFGI::update_cascades() { cascade_data[i].pad = 0; } - RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(cascades_ubo, 0, sizeof(SDFGI::Cascade::UBO) * SDFGI::MAX_CASCADES, cascade_data); } void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector<RID> &p_texture_views) { @@ -1636,7 +1639,7 @@ void GI::SDFGI::debug_draw(uint32_t p_view_count, const Projection *p_projection copy_effects->copy_to_fb_rect(p_texture, texture_storage->render_target_get_rd_framebuffer(p_render_target), Rect2i(Point2i(), rtsize), true, false, false, false, RID(), p_view_count > 1); } -void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) { +void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { RendererRD::MaterialStorage *material_storage = RendererRD::MaterialStorage::get_singleton(); // setup scene data @@ -1651,7 +1654,7 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con RendererRD::MaterialStorage::store_camera(p_camera_with_transforms[v], scene_data.projection[v]); } - RD::get_singleton()->buffer_update(debug_probes_scene_data_ubo, 0, sizeof(SDFGIShader::DebugProbesSceneData), &scene_data, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(debug_probes_scene_data_ubo, 0, sizeof(SDFGIShader::DebugProbesSceneData), &scene_data); } // setup push constant @@ -1718,7 +1721,7 @@ void GI::SDFGI::debug_probes(RID p_framebuffer, const uint32_t p_view_count, con SDFGIShader::ProbeDebugMode mode = p_view_count > 1 ? SDFGIShader::PROBE_DEBUG_PROBES_MULTIVIEW : SDFGIShader::PROBE_DEBUG_PROBES; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CONTINUE, p_will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, p_will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_command_begin_label("Debug SDFGI"); RD::get_singleton()->draw_list_bind_render_pipeline(draw_list, gi->sdfgi_shader.debug_probes_pipeline[mode].get_render_pipeline(RD::INVALID_FORMAT_ID, RD::get_singleton()->framebuffer_get_format(p_framebuffer))); @@ -1861,7 +1864,7 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } } - RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(gi->sdfgi_ubo, 0, sizeof(SDFGIData), &sdfgi_data); /* Update dynamic lights in SDFGI cascades */ @@ -1983,7 +1986,7 @@ void GI::SDFGI::pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_r } if (idx > 0) { - RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDFGIShader::Light), lights, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(cascade.lights_buffer, 0, idx * sizeof(SDFGIShader::Light), lights); } cascade_dynamic_light_count[i] = idx; @@ -2046,6 +2049,8 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ push_constant.cascade = cascade; if (cascades[cascade].dirty_regions != SDFGI::Cascade::DIRTY_ALL) { + RD::get_singleton()->buffer_copy(cascades[cascade].solid_cell_dispatch_buffer_storage, cascades[cascade].solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); //must pre scroll existing data because not all is dirty @@ -2053,7 +2058,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cascades[cascade].scroll_uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &push_constant, sizeof(SDFGIShader::PreprocessPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascades[cascade].solid_cell_dispatch_buffer, 0); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cascades[cascade].solid_cell_dispatch_buffer_call, 0); // no barrier do all together RD::get_singleton()->compute_list_bind_compute_pipeline(compute_list, gi->sdfgi_shader.preprocess_pipeline[SDFGIShader::PRE_PROCESS_SCROLL_OCCLUSION]); @@ -2142,7 +2147,7 @@ void GI::SDFGI::render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_ //clear dispatch indirect data uint32_t dispatch_indirct_data[4] = { 0, 0, 0, 0 }; - RD::get_singleton()->buffer_update(cascades[cascade].solid_cell_dispatch_buffer, 0, sizeof(uint32_t) * 4, dispatch_indirct_data); + RD::get_singleton()->buffer_update(cascades[cascade].solid_cell_dispatch_buffer_storage, 0, sizeof(uint32_t) * 4, dispatch_indirct_data); RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); @@ -2451,6 +2456,15 @@ void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref<RenderScen } } + for (uint32_t i = 0; i < p_cascade_count; i++) { + ERR_CONTINUE(p_cascade_indices[i] >= cascades.size()); + + SDFGI::Cascade &cc = cascades[p_cascade_indices[i]]; + if (light_count[i] > 0) { + RD::get_singleton()->buffer_copy(cc.solid_cell_dispatch_buffer_storage, cc.solid_cell_dispatch_buffer_call, 0, 0, sizeof(uint32_t) * 4); + } + } + /* Static Lights */ RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); @@ -2482,7 +2496,7 @@ void GI::SDFGI::render_static_lights(RenderDataRD *p_render_data, Ref<RenderScen if (dl_push_constant.light_count > 0) { RD::get_singleton()->compute_list_bind_uniform_set(compute_list, cc.sdf_direct_light_static_uniform_set, 0); RD::get_singleton()->compute_list_set_push_constant(compute_list, &dl_push_constant, sizeof(SDFGIShader::DirectLightPushConstant)); - RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer, 0); + RD::get_singleton()->compute_list_dispatch_indirect(compute_list, cc.solid_cell_dispatch_buffer_call, 0); } } @@ -3716,7 +3730,7 @@ void GI::setup_voxel_gi_instances(RenderDataRD *p_render_data, Ref<RenderSceneBu if (p_voxel_gi_instances.size() > 0) { RD::get_singleton()->draw_command_begin_label("VoxelGIs Setup"); - RD::get_singleton()->buffer_update(voxel_gi_buffer, 0, sizeof(VoxelGIData) * MIN((uint64_t)MAX_VOXEL_GI_INSTANCES, p_voxel_gi_instances.size()), voxel_gi_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(voxel_gi_buffer, 0, sizeof(VoxelGIData) * MIN((uint64_t)MAX_VOXEL_GI_INSTANCES, p_voxel_gi_instances.size()), voxel_gi_data); RD::get_singleton()->draw_command_end_label(); } @@ -3804,11 +3818,11 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor scene_data.screen_size[0] = internal_size.x; scene_data.screen_size[1] = internal_size.y; - RD::get_singleton()->buffer_update(rbgi->scene_data_ubo, 0, sizeof(SceneData), &scene_data, RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(rbgi->scene_data_ubo, 0, sizeof(SceneData), &scene_data); } // Now compute the contents of our buffers. - RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(true); + RD::ComputeListID compute_list = RD::get_singleton()->compute_list_begin(); // Render each eye separately. // We need to look into whether we can make our compute shader use Multiview but not sure that works or makes a difference.. @@ -4038,8 +4052,7 @@ void GI::process_gi(Ref<RenderSceneBuffersRD> p_render_buffers, const RID *p_nor } } - //do barrier later to allow oeverlap - //RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //no barriers, let other compute, raster and transfer happen at the same time + RD::get_singleton()->compute_list_end(); RD::get_singleton()->draw_command_end_label(); } diff --git a/servers/rendering/renderer_rd/environment/gi.h b/servers/rendering/renderer_rd/environment/gi.h index c46d4cbd25..011493f1f6 100644 --- a/servers/rendering/renderer_rd/environment/gi.h +++ b/servers/rendering/renderer_rd/environment/gi.h @@ -584,7 +584,9 @@ public: uint32_t static_light_aniso; }; - RID solid_cell_dispatch_buffer; //buffer for indirect compute dispatch + // Buffers for indirect compute dispatch. + RID solid_cell_dispatch_buffer_storage; + RID solid_cell_dispatch_buffer_call; RID solid_cell_buffer; RID lightprobe_history_tex; @@ -686,7 +688,7 @@ public: void update_cascades(); void debug_draw(uint32_t p_view_count, const Projection *p_projections, const Transform3D &p_transform, int p_width, int p_height, RID p_render_target, RID p_texture, const Vector<RID> &p_texture_views); - void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); + void debug_probes(RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms); void pre_process_gi(const Transform3D &p_transform, RenderDataRD *p_render_data); void render_region(Ref<RenderSceneBuffersRD> p_render_buffers, int p_region, const PagedArray<RenderGeometryInstance *> &p_instances, float p_exposure_normalization); diff --git a/servers/rendering/renderer_rd/environment/sky.cpp b/servers/rendering/renderer_rd/environment/sky.cpp index ba72ab3b08..41609dc74d 100644 --- a/servers/rendering/renderer_rd/environment/sky.cpp +++ b/servers/rendering/renderer_rd/environment/sky.cpp @@ -1307,7 +1307,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_QUARTER_RES, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[2].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[2].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1328,7 +1328,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP_HALF_RES, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[1].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[1].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1345,7 +1345,7 @@ void SkyRD::update_radiance_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, Basis local_view = Basis::looking_at(view_normals[i], view_up[i]); RID texture_uniform_set = sky->get_textures(SKY_TEXTURE_SET_CUBEMAP, sky_shader.default_shader_rd, p_render_buffers); - cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + cubemap_draw_list = RD::get_singleton()->draw_list_begin(sky->reflection.layers[0].mipmaps[0].framebuffers[i], RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); _render_sky(cubemap_draw_list, p_time, sky->reflection.layers[0].mipmaps[0].framebuffers[i], pipeline, material->uniform_set, texture_uniform_set, cm, local_view, p_global_pos, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1469,7 +1469,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p Vector<Color> clear_colors; clear_colors.push_back(Color(0.0, 0.0, 0.0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); _render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } @@ -1488,7 +1488,7 @@ void SkyRD::update_res_buffers(Ref<RenderSceneBuffersRD> p_render_buffers, RID p Vector<Color> clear_colors; clear_colors.push_back(Color(0.0, 0.0, 0.0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, clear_colors); _render_sky(draw_list, p_time, framebuffer, pipeline, material->uniform_set, texture_uniform_set, projection, sky_transform, sky_scene_state.cam_transform.origin, p_luminance_multiplier); RD::get_singleton()->draw_list_end(); } diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp index 76e814e1ee..0c0062662a 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -576,31 +576,13 @@ void RenderForwardClustered::_render_list(RenderingDevice::DrawListID p_draw_lis } } -void RenderForwardClustered::_render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params) { - uint32_t render_total = p_params->element_count; - uint32_t total_threads = WorkerThreadPool::get_singleton()->get_thread_count(); - uint32_t render_from = p_thread * render_total / total_threads; - uint32_t render_to = (p_thread + 1 == total_threads) ? render_total : ((p_thread + 1) * render_total / total_threads); - _render_list(thread_draw_lists[p_thread], p_params->framebuffer_format, p_params, render_from, render_to); -} - -void RenderForwardClustered::_render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { +void RenderForwardClustered::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer); p_params->framebuffer_format = fb_format; - if ((uint32_t)p_params->element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time - //multi threaded - thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count()); - RD::get_singleton()->draw_list_begin_split(p_framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures); - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardClustered::_render_list_thread_function, p_params, thread_draw_lists.size(), -1, true, SNAME("ForwardClusteredRenderList")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); - RD::get_singleton()->draw_list_end(p_params->barrier); - } else { - //single threaded - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures); - _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); - RD::get_singleton()->draw_list_end(p_params->barrier); - } + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); + _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); + RD::get_singleton()->draw_list_end(); } void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_data, bool p_no_fog, const Size2i &p_screen_size, bool p_flip_y, const Color &p_default_bg_color, bool p_opaque_render_buffers, bool p_apply_alpha_multiplier, bool p_pancake_shadows, int p_index) { @@ -683,7 +665,7 @@ void RenderForwardClustered::_setup_environment(const RenderDataRD *p_render_dat } } - RD::get_singleton()->buffer_update(scene_state.implementation_uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.implementation_uniform_buffers[p_index], 0, sizeof(SceneState::UBO), &scene_state.ubo); } void RenderForwardClustered::_update_instance_data_buffer(RenderListType p_render_list) { @@ -696,7 +678,7 @@ void RenderForwardClustered::_update_instance_data_buffer(RenderListType p_rende scene_state.instance_buffer[p_render_list] = RD::get_singleton()->storage_buffer_create(new_size * sizeof(SceneState::InstanceData)); scene_state.instance_buffer_size[p_render_list] = new_size; } - RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr(), RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr()); } } void RenderForwardClustered::_fill_instance_data(RenderListType p_render_list, int *p_render_info, uint32_t p_offset, int32_t p_max_elements, bool p_update_buffer) { @@ -1097,7 +1079,7 @@ void RenderForwardClustered::_fill_render_list(RenderListType p_render_list, con } if (p_render_list == RENDER_LIST_OPAQUE && lightmap_captures_used) { - RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.lightmap_capture_buffer, 0, sizeof(LightmapCaptureData) * lightmap_captures_used, scene_state.lightmap_captures); } } @@ -1135,7 +1117,7 @@ void RenderForwardClustered::_setup_lightmaps(const RenderDataRD *p_render_data, scene_state.lightmaps_used++; } if (scene_state.lightmaps_used > 0) { - RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps); } } @@ -1427,7 +1409,7 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo if (p_render_data->directional_shadows.size()) { //open the pass for directional shadows light_storage->update_directional_shadow_atlas(); - RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE); + RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_list_end(); } } @@ -1461,18 +1443,12 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo _render_shadow_process(); } - //start GI if (render_gi) { gi.process_gi(rb, p_normal_roughness_slices, p_voxel_gi_buffer, p_render_data->environment, p_render_data->scene_data->view_count, p_render_data->scene_data->view_projection, p_render_data->scene_data->view_eye_offset, p_render_data->scene_data->cam_transform, *p_render_data->voxel_gi_instances); } - //Do shadow rendering (in parallel with GI) if (render_shadows) { - _render_shadow_end(RD::BARRIER_MASK_NO_BARRIER); - } - - if (render_gi) { - RD::get_singleton()->compute_list_end(RD::BARRIER_MASK_NO_BARRIER); //use a later barrier + _render_shadow_end(); } if (rb_data.is_valid() && ss_effects) { @@ -1496,9 +1472,6 @@ void RenderForwardClustered::_pre_opaque_render(RenderDataRD *p_render_data, boo } } - //full barrier here, we need raster, transfer and compute and it depends from the previous work - RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS); - if (current_cluster_builder) { // Note: when rendering stereoscopic (multiview) we are using our combined frustum projection to create // our cluster data. We use reprojection in the shader to adjust for our left/right eye. @@ -1814,7 +1787,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co float sky_energy_multiplier = 1.0 / _render_buffers_get_luminance_multiplier(); Color clear_color; - bool keep_color = false; + bool load_color = false; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { clear_color = Color(0, 0, 0, 1); //in overdraw mode, BG should always be black @@ -1857,10 +1830,10 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool convert_to_linear = !RendererRD::TextureStorage::get_singleton()->render_target_is_using_hdr(rb->get_render_target()); copy_effects->copy_to_fb_rect(texture, color_only_framebuffer, Rect2i(), false, false, false, false, RID(), false, false, convert_to_linear); } - keep_color = true; + load_color = true; } break; case RS::ENV_BG_KEEP: { - keep_color = true; + load_color = true; } break; case RS::ENV_BG_CAMERA_FEED: { } break; @@ -1912,7 +1885,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool depth_pre_pass = bool(GLOBAL_GET("rendering/driver/depth_prepass/enable")) && depth_framebuffer.is_valid(); bool using_ssao = depth_pre_pass && !is_reflection_probe && p_render_data->environment.is_valid() && environment_get_ssao_enabled(p_render_data->environment); - bool continue_depth = false; if (depth_pre_pass) { //depth pre pass bool needs_pre_resolve = _needs_post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); @@ -1923,7 +1895,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } if (needs_pre_resolve) { //pre clear the depth framebuffer, as AMD (and maybe others?) use compute for it, and barrier other compute shaders. - RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE, depth_pass_clear); + RD::get_singleton()->draw_list_begin(depth_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pass_clear); RD::get_singleton()->draw_list_end(); //start compute processes here, so they run at the same time as depth pre-pass _post_prepass_render(p_render_data, using_sdfgi || using_voxelgi); @@ -1935,21 +1907,14 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co bool finish_depth = using_ssao || using_ssil || using_sdfgi || using_voxelgi; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, depth_pass_mode, 0, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - _render_list_with_threads(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, needs_pre_resolve ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_CLEAR, finish_depth ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear); + _render_list_with_draw_list(&render_list_params, depth_framebuffer, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, needs_pre_resolve ? Vector<Color>() : depth_pass_clear); RD::get_singleton()->draw_command_end_label(); - if (needs_pre_resolve) { - _pre_resolve_render(p_render_data, using_sdfgi || using_voxelgi); - } - if (rb->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { RENDER_TIMESTAMP("Resolve Depth Pre-Pass (MSAA)"); RD::get_singleton()->draw_command_begin_label("Resolve Depth Pre-Pass (MSAA)"); if (depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS || depth_pass_mode == PASS_MODE_DEPTH_NORMAL_ROUGHNESS_VOXEL_GI) { - if (needs_pre_resolve) { - RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, RD::BARRIER_MASK_COMPUTE); - } for (uint32_t v = 0; v < rb->get_view_count(); v++) { resolve_effects->resolve_gi(rb->get_depth_msaa(v), rb_data->get_normal_roughness_msaa(v), using_voxelgi ? rb_data->get_voxelgi_msaa(v) : RID(), rb->get_depth_texture(v), rb_data->get_normal_roughness(v), using_voxelgi ? rb_data->get_voxelgi(v) : RID(), rb->get_internal_size(), texture_multisamples[rb->get_msaa_3d()]); } @@ -1960,8 +1925,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } RD::get_singleton()->draw_command_end_label(); } - - continue_depth = !finish_depth; } RID normal_roughness_views[RendererSceneRender::MAX_RENDER_VIEWS]; @@ -1990,10 +1953,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co { bool render_motion_pass = !render_list[RENDER_LIST_MOTION].elements.is_empty(); - bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes); - bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only || debug_voxelgis || debug_sdfgi_probes); - RD::FinalAction final_color_action = will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ; - RD::FinalAction final_depth_action = will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ; { Vector<Color> c; @@ -2014,7 +1973,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co uint32_t opaque_color_pass_flags = using_motion_pass ? (color_pass_flags & ~COLOR_PASS_FLAG_MOTION_VECTORS) : color_pass_flags; RID opaque_framebuffer = using_motion_pass ? rb_data->get_color_pass_fb(opaque_color_pass_flags) : color_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_OPAQUE].elements.ptr(), render_list[RENDER_LIST_OPAQUE].element_info.ptr(), render_list[RENDER_LIST_OPAQUE].elements.size(), reverse_cull, PASS_MODE_COLOR, opaque_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - _render_list_with_threads(&render_list_params, opaque_framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : final_color_action, depth_pre_pass ? (continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP) : RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : final_depth_action, c, 1.0, 0); + _render_list_with_draw_list(&render_list_params, opaque_framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, depth_pre_pass ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 1.0, 0); } RD::get_singleton()->draw_command_end_label(); @@ -2022,7 +1981,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (using_motion_pass) { Vector<Color> motion_vector_clear_colors; motion_vector_clear_colors.push_back(Color(-1, -1, 0, 0)); - RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, render_motion_pass ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE, motion_vector_clear_colors); + RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, motion_vector_clear_colors); RD::get_singleton()->draw_list_end(); } @@ -2034,33 +1993,17 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co rp_uniform_set = _setup_render_pass_uniform_set(RENDER_LIST_MOTION, p_render_data, radiance_texture, samplers, true); RenderListParameters render_list_params(render_list[RENDER_LIST_MOTION].elements.ptr(), render_list[RENDER_LIST_MOTION].element_info.ptr(), render_list[RENDER_LIST_MOTION].elements.size(), reverse_cull, PASS_MODE_COLOR, color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - _render_list_with_threads(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_CONTINUE, final_color_action, RD::INITIAL_ACTION_CONTINUE, final_depth_action); + _render_list_with_draw_list(&render_list_params, color_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_command_end_label(); - - if (will_continue_color) { - // Close the motion vectors framebuffer as it'll no longer be used. - RD::get_singleton()->draw_list_begin(rb_data->get_velocity_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE); - RD::get_singleton()->draw_list_end(); - } - } - - if (will_continue_color && using_separate_specular) { - // Close the specular framebuffer as it'll no longer be used. - RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, RD::FINAL_ACTION_CONTINUE); - RD::get_singleton()->draw_list_end(); } } if (debug_voxelgis) { - //debug voxelgis - bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only); - bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only); - Projection dc; dc.set_depth_correction(true); Projection cm = (dc * p_render_data->scene_data->cam_projection) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_CONTINUE, will_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, will_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_command_begin_label("Debug VoxelGIs"); for (int i = 0; i < (int)p_render_data->voxel_gi_instances->size(); i++) { gi.debug_voxel_gi((*p_render_data->voxel_gi_instances)[i], draw_list, color_only_framebuffer, cm, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_LIGHTING, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_VOXEL_GI_EMISSION, 1.0); @@ -2070,24 +2013,20 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co } if (debug_sdfgi_probes) { - //debug sdfgi - bool will_continue_color = (can_continue_color || draw_sky || draw_sky_fog_only); - bool will_continue_depth = (can_continue_depth || draw_sky || draw_sky_fog_only); - Projection dc; dc.set_depth_correction(true); Projection cms[RendererSceneRender::MAX_RENDER_VIEWS]; for (uint32_t v = 0; v < p_render_data->scene_data->view_count; v++) { cms[v] = (dc * p_render_data->scene_data->view_projection[v]) * Projection(p_render_data->scene_data->cam_transform.affine_inverse()); } - _debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms, will_continue_color, will_continue_depth); + _debug_sdfgi_probes(rb, color_only_framebuffer, p_render_data->scene_data->view_count, cms); } if (draw_sky || draw_sky_fog_only) { RENDER_TIMESTAMP("Render Sky"); RD::get_singleton()->draw_command_begin_label("Draw Sky"); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_CONTINUE, can_continue_color ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CONTINUE, can_continue_depth ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(color_only_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); sky.draw_sky(draw_list, rb, p_render_data->environment, color_only_framebuffer, time, sky_energy_multiplier); @@ -2146,7 +2085,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RENDER_TIMESTAMP("Clear Separate Specular (Canvas Background Mode)"); Vector<Color> blank_clear_color; blank_clear_color.push_back(Color(0.0, 0.0, 0.0)); - RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, blank_clear_color); + RD::get_singleton()->draw_list_begin(rb_data->get_specular_only_fb(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, blank_clear_color); RD::get_singleton()->draw_list_end(); } @@ -2187,7 +2126,7 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RID alpha_framebuffer = rb_data.is_valid() ? rb_data->get_color_pass_fb(transparent_color_pass_flags) : color_only_framebuffer; RenderListParameters render_list_params(render_list[RENDER_LIST_ALPHA].elements.ptr(), render_list[RENDER_LIST_ALPHA].element_info.ptr(), render_list[RENDER_LIST_ALPHA].elements.size(), false, PASS_MODE_COLOR, transparent_color_pass_flags, rb_data.is_null(), p_render_data->directional_light_soft_shadows, rp_uniform_set, get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_WIREFRAME, Vector2(), p_render_data->scene_data->lod_distance_multiplier, p_render_data->scene_data->screen_mesh_lod_threshold, p_render_data->scene_data->view_count); - _render_list_with_threads(&render_list_params, alpha_framebuffer, can_continue_color ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, can_continue_depth ? RD::INITIAL_ACTION_CONTINUE : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + _render_list_with_draw_list(&render_list_params, alpha_framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); } RD::get_singleton()->draw_command_end_label(); @@ -2226,7 +2165,9 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co exposure = luminance->get_current_luminance_buffer(rb); } + RD::get_singleton()->draw_command_begin_label("FSR2"); RENDER_TIMESTAMP("FSR2"); + for (uint32_t v = 0; v < rb->get_view_count(); v++) { real_t fov = p_render_data->scene_data->cam_projection.get_fov(); real_t aspect = p_render_data->scene_data->cam_projection.get_aspect(); @@ -2257,9 +2198,13 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co fsr2_effect->upscale(params); } + + RD::get_singleton()->draw_command_end_label(); } else if (using_taa) { + RD::get_singleton()->draw_command_begin_label("TAA"); RENDER_TIMESTAMP("TAA"); taa->process(rb, _render_buffers_get_color_format(), p_render_data->scene_data->z_near, p_render_data->scene_data->z_far); + RD::get_singleton()->draw_command_end_label(); } } @@ -2571,8 +2516,7 @@ void RenderForwardClustered::_render_shadow_append(RID p_framebuffer, const Page shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; shadow_pass.framebuffer = p_framebuffer; - shadow_pass.initial_depth_action = p_begin ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE); - shadow_pass.final_depth_action = p_end ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE; + shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD); shadow_pass.rect = p_rect; scene_state.shadow_passes.push_back(shadow_pass); @@ -2591,17 +2535,14 @@ void RenderForwardClustered::_render_shadow_process() { RD::get_singleton()->draw_command_end_label(); } -void RenderForwardClustered::_render_shadow_end(uint32_t p_barrier) { +void RenderForwardClustered::_render_shadow_end() { RD::get_singleton()->draw_command_begin_label("Shadow Render"); for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) { - RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); - _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); + RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, 0, true, false, shadow_pass.rp_uniform_set, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from); + _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0, 0, shadow_pass.rect); } - if (p_barrier != RD::BARRIER_MASK_NO_BARRIER) { - RD::get_singleton()->barrier(RD::BARRIER_MASK_RASTER, p_barrier); - } RD::get_singleton()->draw_command_end_label(); } @@ -2644,7 +2585,7 @@ void RenderForwardClustered::_render_particle_collider_heightfield(RID p_fb, con { //regular forward for now RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, 0, true, false, rp_uniform_set); - _render_list_with_threads(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ); + _render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); } RD::get_singleton()->draw_command_end_label(); } @@ -2697,7 +2638,7 @@ void RenderForwardClustered::_render_material(const Transform3D &p_cam_transform Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); } @@ -2747,7 +2688,7 @@ void RenderForwardClustered::_render_uv2(const PagedArray<RenderGeometryInstance Color(0, 0, 0, 0), Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region); const int uv_offset_count = 9; static const Vector2 uv_offsets[uv_offset_count] = { @@ -2803,13 +2744,6 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu Vector3 half_size = p_bounds.size * 0.5; Vector3 center = p_bounds.position + half_size; - Vector<RID> sbs = { - p_albedo_texture, - p_emission_texture, - p_emission_aniso_texture, - p_geom_facing_texture - }; - //print_line("re-render " + p_from + " - " + p_size + " bounds " + p_bounds); for (int i = 0; i < 3; i++) { scene_state.ubo.sdf_offset[i] = p_from[i]; @@ -2860,7 +2794,7 @@ void RenderForwardClustered::_render_sdfgi(Ref<RenderSceneBuffersRD> p_render_bu } RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), true, pass_mode, 0, true, false, rp_uniform_set, false); - _render_list_with_threads(&render_list_params, E->value, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2(), sbs); + _render_list_with_draw_list(&render_list_params, E->value, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, Vector<Color>(), 1.0, 0, Rect2()); } RD::get_singleton()->draw_command_end_label(); @@ -4280,8 +4214,6 @@ RenderForwardClustered::RenderForwardClustered() { best_fit_normal.shader.version_free(best_fit_normal.shader_version); } - render_list_thread_threshold = GLOBAL_GET("rendering/limits/forward_renderer/threaded_render_minimum_instances"); - _update_shader_quality_settings(); resolve_effects = memnew(RendererRD::Resolve()); diff --git a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h index 5ff3d9f52a..5af213bc02 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.h @@ -209,10 +209,9 @@ class RenderForwardClustered : public RendererSceneRenderRD { float screen_mesh_lod_threshold = 0.0; RD::FramebufferFormatID framebuffer_format = 0; uint32_t element_offset = 0; - uint32_t barrier = RD::BARRIER_MASK_ALL_BARRIERS; bool use_directional_soft_shadow = false; - RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS) { + RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, uint32_t p_color_pass_flags, bool p_no_gi, bool p_use_directional_soft_shadows, RID p_render_pass_uniform_set, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) { elements = p_elements; element_info = p_element_info; element_count = p_element_count; @@ -227,7 +226,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { lod_distance_multiplier = p_lod_distance_multiplier; screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; element_offset = p_element_offset; - barrier = p_barrier; use_directional_soft_shadow = p_use_directional_soft_shadows; } }; @@ -352,7 +350,6 @@ class RenderForwardClustered : public RendererSceneRenderRD { RID framebuffer; RD::InitialAction initial_depth_action; - RD::FinalAction final_depth_action; Rect2i rect; }; @@ -378,14 +375,8 @@ class RenderForwardClustered : public RendererSceneRenderRD { template <PassMode p_pass_mode, uint32_t p_color_pass_flags = 0> _FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - - LocalVector<RD::DrawListID> thread_draw_lists; - void _render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params); - void _render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - - uint32_t render_list_thread_threshold = 500; + void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); void _update_instance_data_buffer(RenderListType p_render_list); void _fill_instance_data(RenderListType p_render_list, int *p_render_info = nullptr, uint32_t p_offset = 0, int32_t p_max_elements = -1, bool p_update_buffer = true); @@ -604,7 +595,7 @@ class RenderForwardClustered : public RendererSceneRenderRD { void _render_shadow_begin(); void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_reverse_cull_face, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr, const Size2i &p_viewport_size = Size2i(1, 1)); void _render_shadow_process(); - void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); + void _render_shadow_end(); /* Render Scene */ void _process_ssao(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_environment, const RID *p_normal_buffers, const Projection *p_projections); 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 bba1f62023..86852ce020 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 @@ -446,7 +446,7 @@ void SceneShaderForwardClustered::MaterialData::set_next_pass(RID p_pass) { bool SceneShaderForwardClustered::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { SceneShaderForwardClustered *shader_singleton = (SceneShaderForwardClustered *)SceneShaderForwardClustered::singleton; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardClustered::MATERIAL_UNIFORM_SET, true, true); } SceneShaderForwardClustered::MaterialData::~MaterialData() { diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp index b7d7105daa..da04e6f938 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp @@ -596,7 +596,7 @@ void RenderForwardMobile::_setup_lightmaps(const RenderDataRD *p_render_data, co scene_state.lightmaps_used++; } if (scene_state.lightmaps_used > 0) { - RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.lightmap_buffer, 0, sizeof(LightmapData) * scene_state.lightmaps_used, scene_state.lightmaps); } } @@ -631,7 +631,7 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { if (p_render_data->directional_shadows.size()) { //open the pass for directional shadows light_storage->update_directional_shadow_atlas(); - RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_CONTINUE); + RD::get_singleton()->draw_list_begin(light_storage->direction_shadow_get_fb(), RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); RD::get_singleton()->draw_list_end(); } } @@ -655,11 +655,8 @@ void RenderForwardMobile::_pre_opaque_render(RenderDataRD *p_render_data) { _render_shadow_process(); - _render_shadow_end(RD::BARRIER_MASK_NO_BARRIER); + _render_shadow_end(); } - - //full barrier here, we need raster, transfer and compute and it depends from the previous work - RD::get_singleton()->barrier(RD::BARRIER_MASK_ALL_BARRIERS, RD::BARRIER_MASK_ALL_BARRIERS); } void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color &p_default_bg_color) { @@ -811,7 +808,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color float sky_energy_multiplier = inverse_luminance_multiplier; Color clear_color = p_default_bg_color; - bool keep_color = false; + bool load_color = false; bool copy_canvas = false; if (get_debug_draw_mode() == RS::VIEWPORT_DEBUG_DRAW_OVERDRAW) { @@ -855,7 +852,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } break; case RS::ENV_BG_KEEP: { - keep_color = true; + load_color = true; } break; case RS::ENV_BG_CAMERA_FEED: { } break; @@ -955,6 +952,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color if (rb_data.is_valid()) { cc.a = 0; // For transparent viewport backgrounds. } + c.push_back(cc); // Our render buffer. if (rb_data.is_valid()) { if (p_render_data->render_buffers->get_msaa_3d() != RS::VIEWPORT_MSAA_DISABLED) { @@ -966,7 +964,7 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color } } - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, keep_color ? RD::INITIAL_ACTION_KEEP : RD::INITIAL_ACTION_CLEAR, merge_transparent_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, RD::INITIAL_ACTION_CLEAR, merge_transparent_pass ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE, c, 1.0, 0); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, load_color ? RD::INITIAL_ACTION_LOAD : RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, c, 1.0, 0); RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); if (copy_canvas) { @@ -1026,12 +1024,12 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); + RD::get_singleton()->draw_list_end(); } else { // We're done with our subpasses so end our container pass // note, if MSAA is used we should get an automatic resolve here - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); + RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_command_end_label(); // Render 3D Pass / Render Reflection Probe Pass @@ -1062,9 +1060,9 @@ void RenderForwardMobile::_render_scene(RenderDataRD *p_render_data, const Color render_list_params.framebuffer_format = fb_format; render_list_params.subpass = RD::get_singleton()->draw_list_get_current_pass(); // Should now always be 0. - draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ); + draw_list = RD::get_singleton()->draw_list_begin(framebuffer, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE); _render_list(draw_list, fb_format, &render_list_params, 0, render_list_params.element_count); - RD::get_singleton()->draw_list_end(RD::BARRIER_MASK_ALL_BARRIERS); + RD::get_singleton()->draw_list_end(); RD::get_singleton()->draw_command_end_label(); // Render Transparent Pass } @@ -1248,15 +1246,15 @@ void RenderForwardMobile::_render_shadow_pass(RID p_light, RID p_shadow_atlas, i _render_shadow_append(render_fb, p_instances, light_projection, light_transform, zfar, 0, 0, false, false, use_pancake, p_camera_plane, p_lod_distance_multiplier, p_screen_mesh_lod_threshold, Rect2(), false, true, true, true, p_render_info); if (finalize_cubemap) { _render_shadow_process(); - _render_shadow_end(RD::BARRIER_MASK_FRAGMENT); + _render_shadow_end(); // reblit Rect2 atlas_rect_norm = atlas_rect; atlas_rect_norm.position /= float(atlas_size); atlas_rect_norm.size /= float(atlas_size); - copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false, RD::BARRIER_MASK_NO_BARRIER); + copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), false); atlas_rect_norm.position += Vector2(dual_paraboloid_offset) * atlas_rect_norm.size; - copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true, RD::BARRIER_MASK_NO_BARRIER); + copy_effects->copy_cubemap_to_dp(render_texture, atlas_fb, atlas_rect_norm, atlas_rect.size, light_projection.get_z_near(), light_projection.get_z_far(), true); //restore transform so it can be properly used light_storage->light_instance_set_shadow_transform(p_light, Projection(), light_storage->light_instance_get_base_transform(p_light), zfar, 0, 0, 0); @@ -1337,8 +1335,7 @@ void RenderForwardMobile::_render_shadow_append(RID p_framebuffer, const PagedAr shadow_pass.lod_distance_multiplier = scene_data.lod_distance_multiplier; shadow_pass.framebuffer = p_framebuffer; - shadow_pass.initial_depth_action = p_begin ? (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR) : (p_clear_region ? RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE : RD::INITIAL_ACTION_CONTINUE); - shadow_pass.final_depth_action = p_end ? RD::FINAL_ACTION_READ : RD::FINAL_ACTION_CONTINUE; + shadow_pass.initial_depth_action = p_begin ? RD::INITIAL_ACTION_CLEAR : (p_clear_region ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD); shadow_pass.rect = p_rect; scene_state.shadow_passes.push_back(shadow_pass); @@ -1357,17 +1354,14 @@ void RenderForwardMobile::_render_shadow_process() { RD::get_singleton()->draw_command_end_label(); } -void RenderForwardMobile::_render_shadow_end(uint32_t p_barrier) { +void RenderForwardMobile::_render_shadow_end() { RD::get_singleton()->draw_command_begin_label("Shadow Render"); for (SceneState::ShadowPass &shadow_pass : scene_state.shadow_passes) { - RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from, RD::BARRIER_MASK_NO_BARRIER); - _render_list_with_threads(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, shadow_pass.final_depth_action, Vector<Color>(), 1.0, 0, shadow_pass.rect); + RenderListParameters render_list_parameters(render_list[RENDER_LIST_SECONDARY].elements.ptr() + shadow_pass.element_from, render_list[RENDER_LIST_SECONDARY].element_info.ptr() + shadow_pass.element_from, shadow_pass.element_count, shadow_pass.flip_cull, shadow_pass.pass_mode, shadow_pass.rp_uniform_set, 0, false, Vector2(), shadow_pass.lod_distance_multiplier, shadow_pass.screen_mesh_lod_threshold, 1, shadow_pass.element_from); + _render_list_with_draw_list(&render_list_parameters, shadow_pass.framebuffer, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, shadow_pass.initial_depth_action, RD::FINAL_ACTION_STORE, Vector<Color>(), 1.0, 0, shadow_pass.rect); } - if (p_barrier != RD::BARRIER_MASK_NO_BARRIER) { - RD::get_singleton()->barrier(RD::BARRIER_MASK_FRAGMENT, p_barrier); - } RD::get_singleton()->draw_command_end_label(); } @@ -1416,7 +1410,7 @@ void RenderForwardMobile::_render_material(const Transform3D &p_cam_transform, c Color(0, 0, 0, 0), Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region); _render_list(draw_list, RD::get_singleton()->framebuffer_get_format(p_framebuffer), &render_list_params, 0, render_list_params.element_count); RD::get_singleton()->draw_list_end(); } @@ -1462,7 +1456,7 @@ void RenderForwardMobile::_render_uv2(const PagedArray<RenderGeometryInstance *> Color(0, 0, 0, 0) }; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, clear, 1.0, 0, p_region); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, clear, 1.0, 0, p_region); const int uv_offset_count = 9; static const Vector2 uv_offsets[uv_offset_count] = { @@ -1535,7 +1529,7 @@ void RenderForwardMobile::_render_particle_collider_heightfield(RID p_fb, const { //regular forward for now RenderListParameters render_list_params(render_list[RENDER_LIST_SECONDARY].elements.ptr(), render_list[RENDER_LIST_SECONDARY].element_info.ptr(), render_list[RENDER_LIST_SECONDARY].elements.size(), false, pass_mode, rp_uniform_set, 0); - _render_list_with_threads(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ); + _render_list_with_draw_list(&render_list_params, p_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE); } RD::get_singleton()->draw_command_end_label(); } @@ -1672,7 +1666,7 @@ void RenderForwardMobile::_update_instance_data_buffer(RenderListType p_render_l scene_state.instance_buffer[p_render_list] = RD::get_singleton()->storage_buffer_create(new_size * sizeof(SceneState::InstanceData)); scene_state.instance_buffer_size[p_render_list] = new_size; } - RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr(), RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(scene_state.instance_buffer[p_render_list], 0, sizeof(SceneState::InstanceData) * scene_state.instance_data[p_render_list].size(), scene_state.instance_data[p_render_list].ptr()); } } @@ -1991,32 +1985,13 @@ void RenderForwardMobile::_render_list(RenderingDevice::DrawListID p_draw_list, } } -void RenderForwardMobile::_render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params) { - uint32_t render_total = p_params->element_count; - uint32_t total_threads = WorkerThreadPool::get_singleton()->get_thread_count(); - uint32_t render_from = p_thread * render_total / total_threads; - uint32_t render_to = (p_thread + 1 == total_threads) ? render_total : ((p_thread + 1) * render_total / total_threads); - _render_list(thread_draw_lists[p_thread], p_params->framebuffer_format, p_params, render_from, render_to); -} - -void RenderForwardMobile::_render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { +void RenderForwardMobile::_render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(p_framebuffer); p_params->framebuffer_format = fb_format; - if ((uint32_t)p_params->element_count > render_list_thread_threshold && false) { // secondary command buffers need more testing at this time - //multi threaded - thread_draw_lists.resize(WorkerThreadPool::get_singleton()->get_thread_count()); - RD::get_singleton()->draw_list_begin_split(p_framebuffer, thread_draw_lists.size(), thread_draw_lists.ptr(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures); - WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &RenderForwardMobile::_render_list_thread_function, p_params, thread_draw_lists.size(), -1, true, SNAME("ForwardMobileRenderSubpass")); - WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task); - - RD::get_singleton()->draw_list_end(p_params->barrier); - } else { - //single threaded - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, p_storage_textures); - _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); - RD::get_singleton()->draw_list_end(p_params->barrier); - } + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); + _render_list(draw_list, fb_format, p_params, 0, p_params->element_count); + RD::get_singleton()->draw_list_end(); } template <RenderForwardMobile::PassMode p_pass_mode> @@ -2813,9 +2788,6 @@ RenderForwardMobile::RenderForwardMobile() { scene_shader.init(defines); - // !BAS! maybe we need a mobile version of this setting? - render_list_thread_threshold = GLOBAL_GET("rendering/limits/forward_renderer/threaded_render_minimum_instances"); - _update_shader_quality_settings(); } diff --git a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h index da96ca2124..f1f6bb3db4 100644 --- a/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h +++ b/servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.h @@ -155,10 +155,9 @@ private: float screen_mesh_lod_threshold = 0.0; RD::FramebufferFormatID framebuffer_format = 0; uint32_t element_offset = 0; - uint32_t barrier = RD::BARRIER_MASK_ALL_BARRIERS; uint32_t subpass = 0; - RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS) { + RenderListParameters(GeometryInstanceSurfaceDataCache **p_elements, RenderElementInfo *p_element_info, int p_element_count, bool p_reverse_cull, PassMode p_pass_mode, RID p_render_pass_uniform_set, uint32_t p_spec_constant_base_flags = 0, bool p_force_wireframe = false, const Vector2 &p_uv_offset = Vector2(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, uint32_t p_view_count = 1, uint32_t p_element_offset = 0) { elements = p_elements; element_info = p_element_info; element_count = p_element_count; @@ -172,7 +171,6 @@ private: lod_distance_multiplier = p_lod_distance_multiplier; screen_mesh_lod_threshold = p_screen_mesh_lod_threshold; element_offset = p_element_offset; - barrier = p_barrier; spec_constant_base_flags = p_spec_constant_base_flags; } }; @@ -183,7 +181,7 @@ private: void _render_shadow_begin(); void _render_shadow_append(RID p_framebuffer, const PagedArray<RenderGeometryInstance *> &p_instances, const Projection &p_projection, const Transform3D &p_transform, float p_zfar, float p_bias, float p_normal_bias, bool p_use_dp, bool p_use_dp_flip, bool p_use_pancake, const Plane &p_camera_plane = Plane(), float p_lod_distance_multiplier = 0.0, float p_screen_mesh_lod_threshold = 0.0, const Rect2i &p_rect = Rect2i(), bool p_flip_y = false, bool p_clear_region = true, bool p_begin = true, bool p_end = true, RenderingMethod::RenderInfo *p_render_info = nullptr); void _render_shadow_process(); - void _render_shadow_end(uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); + void _render_shadow_end(); /* Render Scene */ @@ -277,7 +275,6 @@ private: RID framebuffer; RD::InitialAction initial_depth_action; - RD::FinalAction final_depth_action; Rect2i rect; }; @@ -351,14 +348,8 @@ private: template <PassMode p_pass_mode> _FORCE_INLINE_ void _render_list_template(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - void _render_list(RenderingDevice::DrawListID p_draw_list, RenderingDevice::FramebufferFormatID p_framebuffer_Format, RenderListParameters *p_params, uint32_t p_from_element, uint32_t p_to_element); - - LocalVector<RD::DrawListID> thread_draw_lists; - void _render_list_thread_function(uint32_t p_thread, RenderListParameters *p_params); - void _render_list_with_threads(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - - uint32_t render_list_thread_threshold = 500; + void _render_list_with_draw_list(RenderListParameters *p_params, RID p_framebuffer, RD::InitialAction p_initial_color_action, RD::FinalAction p_final_color_action, RD::InitialAction p_initial_depth_action, RD::FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); RenderList render_list[RENDER_LIST_MAX]; 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 f1cec0e07c..043cdbc8e5 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 @@ -398,7 +398,7 @@ void SceneShaderForwardMobile::MaterialData::set_next_pass(RID p_pass) { bool SceneShaderForwardMobile::MaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { SceneShaderForwardMobile *shader_singleton = (SceneShaderForwardMobile *)SceneShaderForwardMobile::singleton; - return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true, RD::BARRIER_MASK_RASTER); + return update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, shader_singleton->shader.version_get_shader(shader_data->version, 0), RenderForwardMobile::MATERIAL_UNIFORM_SET, true, true); } SceneShaderForwardMobile::MaterialData::~MaterialData() { diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp index 657628111a..28fccbaf88 100644 --- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp @@ -1177,7 +1177,7 @@ void RendererCanvasRenderRD::_render_items(RID p_to_render_target, int p_item_co RD::FramebufferFormatID fb_format = RD::get_singleton()->framebuffer_get_format(framebuffer); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(framebuffer, clear ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, fb_uniform_set, BASE_UNIFORM_SET); RD::get_singleton()->draw_list_bind_uniform_set(draw_list, state.default_transforms_uniform_set, TRANSFORMS_UNIFORM_SET); @@ -1721,8 +1721,8 @@ void RendererCanvasRenderRD::light_update_shadow(RID p_rid, int p_shadow_index, //light.basis.scale(Vector3(to_light.elements[0].length(),to_light.elements[1].length(),1)); Rect2i rect((state.shadow_texture_size / 4) * i, p_shadow_index * 2, (state.shadow_texture_size / 4), 2); - RD::InitialAction initial_action = i == 0 ? RD::INITIAL_ACTION_CLEAR_REGION : RD::INITIAL_ACTION_CLEAR_REGION_CONTINUE; - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, initial_action, i != 3 ? RD::FINAL_ACTION_CONTINUE : RD::FINAL_ACTION_READ, initial_action, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); + RD::InitialAction initial_action = i == 0 ? RD::INITIAL_ACTION_CLEAR : RD::INITIAL_ACTION_LOAD; + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, initial_action, RD::FINAL_ACTION_STORE, initial_action, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); Projection projection; { @@ -1811,7 +1811,7 @@ void RendererCanvasRenderRD::light_update_directional_shadow(RID p_rid, int p_sh cc.push_back(Color(1, 1, 1, 1)); Rect2i rect(0, p_shadow_index * 2, state.shadow_texture_size, 2); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR_REGION, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR_REGION, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(state.shadow_fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc, 1.0, 0, rect); Projection projection; projection.set_orthogonal(-half_size, half_size, -0.5, 0.5, 0.0, distance); @@ -1881,7 +1881,7 @@ void RendererCanvasRenderRD::render_sdf(RID p_render_target, LightOccluderInstan Vector<Color> cc; cc.push_back(Color(0, 0, 0, 0)); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_DISCARD, cc); Projection projection; @@ -2371,8 +2371,8 @@ RendererRD::MaterialStorage::ShaderData *RendererCanvasRenderRD::_create_shader_ bool RendererCanvasRenderRD::CanvasMaterialData::update_parameters(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty) { RendererCanvasRenderRD *canvas_singleton = static_cast<RendererCanvasRenderRD *>(RendererCanvasRender::singleton); - bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false, RD::BARRIER_MASK_ALL_BARRIERS); - bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false, RD::BARRIER_MASK_ALL_BARRIERS); + bool uniform_set_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, true, false); + bool uniform_set_srgb_changed = update_parameters_uniform_set(p_parameters, p_uniform_dirty, p_textures_dirty, shader_data->uniforms, shader_data->ubo_offsets.ptr(), shader_data->texture_uniforms, shader_data->default_texture_params, shader_data->ubo_size, uniform_set_srgb, canvas_singleton->shader.canvas_shader.version_get_shader(shader_data->version, 0), MATERIAL_UNIFORM_SET, false, false); return uniform_set_changed || uniform_set_srgb_changed; } diff --git a/servers/rendering/renderer_rd/renderer_compositor_rd.h b/servers/rendering/renderer_rd/renderer_compositor_rd.h index 705fb9e8e5..9b073821c2 100644 --- a/servers/rendering/renderer_rd/renderer_compositor_rd.h +++ b/servers/rendering/renderer_rd/renderer_compositor_rd.h @@ -123,6 +123,7 @@ public: void prepare_for_blitting_render_targets(); void blit_render_targets_to_screen(DisplayServer::WindowID p_screen, const BlitToScreen *p_render_targets, int p_amount); + void end_viewport(bool p_swap_buffers) {} void end_frame(bool p_swap_buffers); void finalize(); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 30c9b97aa4..3b05431f4a 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -220,7 +220,7 @@ void RendererSceneRenderRD::voxel_gi_update(RID p_probe, bool p_update_light_ins gi.voxel_gi_update(p_probe, p_update_light_instances, p_light_instances, p_dynamic_objects); } -void RendererSceneRenderRD::_debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth) { +void RendererSceneRenderRD::_debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, const uint32_t p_view_count, const Projection *p_camera_with_transforms) { ERR_FAIL_COND(p_render_buffers.is_null()); if (!p_render_buffers->has_custom_data(RB_SCOPE_SDFGI)) { @@ -229,7 +229,7 @@ void RendererSceneRenderRD::_debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_rend Ref<RendererRD::GI::SDFGI> sdfgi = p_render_buffers->get_custom_data(RB_SCOPE_SDFGI); - sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms, p_will_continue_color, p_will_continue_depth); + sdfgi->debug_probes(p_framebuffer, p_view_count, p_camera_with_transforms); } //////////////////////////////// @@ -987,14 +987,6 @@ void RendererSceneRenderRD::_post_prepass_render(RenderDataRD *p_render_data, bo } } -void RendererSceneRenderRD::_pre_resolve_render(RenderDataRD *p_render_data, bool p_use_gi) { - if (p_render_data->render_buffers.is_valid()) { - if (p_use_gi) { - RD::get_singleton()->compute_list_end(); - } - } -} - void RendererSceneRenderRD::render_scene(const Ref<RenderSceneBuffers> &p_render_buffers, const CameraData *p_camera_data, const CameraData *p_prev_camera_data, const PagedArray<RenderGeometryInstance *> &p_instances, const PagedArray<RID> &p_lights, const PagedArray<RID> &p_reflection_probes, const PagedArray<RID> &p_voxel_gi_instances, const PagedArray<RID> &p_decals, const PagedArray<RID> &p_lightmaps, const PagedArray<RID> &p_fog_volumes, RID p_environment, RID p_camera_attributes, RID p_shadow_atlas, RID p_occluder_debug_tex, RID p_reflection_atlas, RID p_reflection_probe, int p_reflection_probe_pass, float p_screen_mesh_lod_threshold, const RenderShadowData *p_render_shadows, int p_render_shadow_count, const RenderSDFGIData *p_render_sdfgi_regions, int p_render_sdfgi_region_count, const RenderSDFGIUpdateData *p_sdfgi_update_data, RenderingMethod::RenderInfo *r_render_info) { RendererRD::LightStorage *light_storage = RendererRD::LightStorage::get_singleton(); RendererRD::TextureStorage *texture_storage = RendererRD::TextureStorage::get_singleton(); diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.h b/servers/rendering/renderer_rd/renderer_scene_render_rd.h index 211d191039..4811ae3b44 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.h +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.h @@ -137,14 +137,13 @@ protected: virtual void _render_sdfgi(Ref<RenderSceneBuffersRD> p_render_buffers, const Vector3i &p_from, const Vector3i &p_size, const AABB &p_bounds, const PagedArray<RenderGeometryInstance *> &p_instances, const RID &p_albedo_texture, const RID &p_emission_texture, const RID &p_emission_aniso_texture, const RID &p_geom_facing_texture, float p_exposure_normalization) = 0; virtual void _render_particle_collider_heightfield(RID p_fb, const Transform3D &p_cam_transform, const Projection &p_cam_projection, const PagedArray<RenderGeometryInstance *> &p_instances) = 0; - void _debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms, bool p_will_continue_color, bool p_will_continue_depth); + void _debug_sdfgi_probes(Ref<RenderSceneBuffersRD> p_render_buffers, RID p_framebuffer, uint32_t p_view_count, const Projection *p_camera_with_transforms); virtual RID _render_buffers_get_normal_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0; virtual RID _render_buffers_get_velocity_texture(Ref<RenderSceneBuffersRD> p_render_buffers) = 0; bool _needs_post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi); void _post_prepass_render(RenderDataRD *p_render_data, bool p_use_gi); - void _pre_resolve_render(RenderDataRD *p_render_data, bool p_use_gi); void _render_buffers_copy_screen_texture(const RenderDataRD *p_render_data); void _render_buffers_copy_depth_texture(const RenderDataRD *p_render_data); diff --git a/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub b/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub index f06a2d86e2..5b8bbc343b 100644 --- a/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub +++ b/servers/rendering/renderer_rd/shaders/effects/fsr2/SCsub @@ -6,6 +6,11 @@ if "RD_GLSL" in env["BUILDERS"]: # find all include files gl_include_files = [str(f) for f in Glob("*_inc.glsl")] + [str(f) for f in Glob("../*_inc.glsl")] + # Add all FSR2 shader and header files. + fsr2_dir = "#thirdparty/amd-fsr2/shaders" + gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.h")] + gl_include_files += [str(f) for f in Glob(fsr2_dir + "/*.glsl")] + # find all shader code(all glsl files excluding our include files) glsl_files = [str(f) for f in Glob("*.glsl") if str(f) not in gl_include_files] diff --git a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp index 5f4bf6c8ed..21c6425a87 100644 --- a/servers/rendering/renderer_rd/storage_rd/light_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/light_storage.cpp @@ -996,15 +996,15 @@ void LightStorage::update_light_buffers(RenderDataRD *p_render_data, const Paged //update without barriers if (omni_light_count) { - RD::get_singleton()->buffer_update(omni_light_buffer, 0, sizeof(LightData) * omni_light_count, omni_lights, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(omni_light_buffer, 0, sizeof(LightData) * omni_light_count, omni_lights); } if (spot_light_count) { - RD::get_singleton()->buffer_update(spot_light_buffer, 0, sizeof(LightData) * spot_light_count, spot_lights, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(spot_light_buffer, 0, sizeof(LightData) * spot_light_count, spot_lights); } if (r_directional_light_count) { - RD::get_singleton()->buffer_update(directional_light_buffer, 0, sizeof(DirectionalLightData) * r_directional_light_count, directional_lights, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(directional_light_buffer, 0, sizeof(DirectionalLightData) * r_directional_light_count, directional_lights); } } @@ -1722,7 +1722,7 @@ void LightStorage::update_reflection_probe_buffer(RenderDataRD *p_render_data, c } if (reflection_count) { - RD::get_singleton()->buffer_update(reflection_buffer, 0, reflection_count * sizeof(ReflectionData), reflections, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(reflection_buffer, 0, reflection_count * sizeof(ReflectionData), reflections); } } diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp index 2e8c9d7f8e..1c3076b128 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.cpp @@ -986,7 +986,7 @@ void MaterialStorage::MaterialData::free_parameters_uniform_set(RID p_uniform_se } } -bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier) { +bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material) { if ((uint32_t)ubo_data.size() != p_ubo_size) { p_uniform_dirty = true; if (uniform_buffer.is_valid()) { @@ -1011,7 +1011,7 @@ bool MaterialStorage::MaterialData::update_parameters_uniform_set(const HashMap< //check whether buffer changed if (p_uniform_dirty && ubo_data.size()) { update_uniform_buffer(p_uniforms, p_uniform_offsets, p_parameters, ubo_data.ptrw(), ubo_data.size(), p_use_linear_color); - RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw(), p_barrier); + RD::get_singleton()->buffer_update(uniform_buffer, 0, ubo_data.size(), ubo_data.ptrw()); } uint32_t tex_uniform_count = 0U; diff --git a/servers/rendering/renderer_rd/storage_rd/material_storage.h b/servers/rendering/renderer_rd/storage_rd/material_storage.h index 403fd286b4..fe769a778d 100644 --- a/servers/rendering/renderer_rd/storage_rd/material_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/material_storage.h @@ -87,7 +87,7 @@ public: virtual ~MaterialData(); //to be used internally by update_parameters, in the most common configuration of material parameters - bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &r_uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material, uint32_t p_barrier = RD::BARRIER_MASK_ALL_BARRIERS); + bool update_parameters_uniform_set(const HashMap<StringName, Variant> &p_parameters, bool p_uniform_dirty, bool p_textures_dirty, const HashMap<StringName, ShaderLanguage::ShaderNode::Uniform> &p_uniforms, const uint32_t *p_uniform_offsets, const Vector<ShaderCompiler::GeneratedCode::Texture> &p_texture_uniforms, const HashMap<StringName, HashMap<int, RID>> &p_default_texture_params, uint32_t p_ubo_size, RID &r_uniform_set, RID p_shader, uint32_t p_shader_uniform_set, bool p_use_linear_color, bool p_3d_material); void free_parameters_uniform_set(RID p_uniform_set); private: diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp index 876bdf5c71..b97ce2d006 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.cpp @@ -657,7 +657,8 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { Skeleton *skeleton = skeleton_owner.get_or_null(p_skeleton); - if (!skeleton || skeleton->size == 0 || mesh->skeleton_aabb_version == skeleton->version) { + // A mesh can be shared by multiple skeletons and we need to avoid using the AABB from a different skeleton. + if (!skeleton || skeleton->size == 0 || (mesh->skeleton_aabb_version == skeleton->version && mesh->skeleton_aabb_rid == p_skeleton)) { return mesh->aabb; } @@ -763,6 +764,7 @@ AABB MeshStorage::mesh_get_aabb(RID p_mesh, RID p_skeleton) { mesh->aabb = aabb; mesh->skeleton_aabb_version = skeleton->version; + mesh->skeleton_aabb_rid = p_skeleton; return aabb; } @@ -1456,8 +1458,7 @@ void MeshStorage::_multimesh_enable_motion_vectors(MultiMesh *multimesh) { if (multimesh->buffer_set && multimesh->data_cache.is_empty()) { // If the buffer was set but there's no data cached in the CPU, we copy the buffer directly on the GPU. - RD::get_singleton()->barrier(); - RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size, RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, 0, buffer_size); RD::get_singleton()->buffer_copy(multimesh->buffer, new_buffer, 0, buffer_size, buffer_size); } else if (!multimesh->data_cache.is_empty()) { // Simply upload the data cached in the CPU, which should already be doubled in size. @@ -2035,10 +2036,9 @@ void MeshStorage::_update_dirty_multimeshes() { uint32_t offset = i * region_size; uint32_t size = multimesh->stride_cache * (uint32_t)multimesh->instances * (uint32_t)sizeof(float); uint32_t region_start_index = multimesh->stride_cache * MULTIMESH_DIRTY_REGION_SIZE * i; - RD::get_singleton()->buffer_update(multimesh->buffer, buffer_offset * sizeof(float) + offset, MIN(region_size, size - offset), &data[region_start_index], RD::BARRIER_MASK_NO_BARRIER); + RD::get_singleton()->buffer_update(multimesh->buffer, buffer_offset * sizeof(float) + offset, MIN(region_size, size - offset), &data[region_start_index]); } } - RD::get_singleton()->barrier(RD::BARRIER_MASK_NO_BARRIER, RD::BARRIER_MASK_ALL_BARRIERS); } memcpy(multimesh->previous_data_cache_dirty_regions, multimesh->data_cache_dirty_regions, data_cache_dirty_region_count * sizeof(bool)); diff --git a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h index 0fc1a6f320..a1e2ffcf7e 100644 --- a/servers/rendering/renderer_rd/storage_rd/mesh_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/mesh_storage.h @@ -153,6 +153,7 @@ private: AABB aabb; AABB custom_aabb; uint64_t skeleton_aabb_version = 0; + RID skeleton_aabb_rid; Vector<RID> material_cache; diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp index 3d3cb585ac..a854e78f53 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp @@ -307,6 +307,11 @@ void ParticlesStorage::_particles_free_data(Particles *particles) { particles->emission_storage_buffer = RID(); } + if (particles->unused_storage_buffer.is_valid()) { + RD::get_singleton()->free(particles->unused_storage_buffer); + particles->unused_storage_buffer = RID(); + } + if (RD::get_singleton()->uniform_set_is_valid(particles->particles_material_uniform_set)) { //will need to be re-created RD::get_singleton()->free(particles->particles_material_uniform_set); @@ -530,6 +535,12 @@ void ParticlesStorage::_particles_allocate_emission_buffer(Particles *particles) } } +void ParticlesStorage::_particles_ensure_unused_buffer(Particles *particles) { + if (particles->unused_storage_buffer.is_null()) { + particles->unused_storage_buffer = RD::get_singleton()->storage_buffer_create(sizeof(uint32_t) * 4); + } +} + void ParticlesStorage::particles_set_subemitter(RID p_particles, RID p_subemitter_particles) { Particles *particles = particles_owner.get_or_null(p_particles); ERR_FAIL_NULL(particles); @@ -757,7 +768,8 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta if (p_particles->emission_storage_buffer.is_valid()) { u.append_id(p_particles->emission_storage_buffer); } else { - u.append_id(MeshStorage::get_singleton()->get_default_rd_storage_buffer()); + _particles_ensure_unused_buffer(p_particles); + u.append_id(p_particles->unused_storage_buffer); } uniforms.push_back(u); } @@ -772,7 +784,8 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta } u.append_id(sub_emitter->emission_storage_buffer); } else { - u.append_id(MeshStorage::get_singleton()->get_default_rd_storage_buffer()); + _particles_ensure_unused_buffer(p_particles); + u.append_id(p_particles->unused_storage_buffer); } uniforms.push_back(u); } @@ -1463,7 +1476,8 @@ void ParticlesStorage::update_particles() { if (particles->trail_bind_pose_buffer.is_valid()) { u.append_id(particles->trail_bind_pose_buffer); } else { - u.append_id(MeshStorage::get_singleton()->get_default_rd_storage_buffer()); + _particles_ensure_unused_buffer(particles); + u.append_id(particles->unused_storage_buffer); } uniforms.push_back(u); } diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.h b/servers/rendering/renderer_rd/storage_rd/particles_storage.h index a28d7b4154..33f44f3045 100644 --- a/servers/rendering/renderer_rd/storage_rd/particles_storage.h +++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.h @@ -247,6 +247,8 @@ private: ParticleEmissionBuffer *emission_buffer = nullptr; RID emission_storage_buffer; + RID unused_storage_buffer; + HashSet<RID> collisions; Dependency dependency; @@ -263,6 +265,7 @@ private: void _particles_process(Particles *p_particles, double p_delta); void _particles_allocate_emission_buffer(Particles *particles); + void _particles_ensure_unused_buffer(Particles *particles); void _particles_free_data(Particles *particles); void _particles_update_buffers(Particles *particles); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp index 6a99eb4108..e77a2e9567 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.cpp @@ -330,7 +330,7 @@ RID RenderSceneBuffersRD::create_texture_from_format(const StringName &p_context return named_texture.texture; } -RID RenderSceneBuffersRD::_create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, const Ref<RDTextureView> p_view) { +RID RenderSceneBuffersRD::_create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, const Ref<RDTextureView> p_view) { RD::TextureView texture_view; if (p_view.is_valid()) { // only use when supplied, else default. texture_view = p_view->base; @@ -339,7 +339,7 @@ RID RenderSceneBuffersRD::_create_texture_view(const StringName &p_context, cons return create_texture_view(p_context, p_texture_name, p_view_name, texture_view); } -RID RenderSceneBuffersRD::create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, RD::TextureView p_view) { +RID RenderSceneBuffersRD::create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, RD::TextureView p_view) { NTKey view_key(p_context, p_view_name); // check if this is a known texture diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h index b2946e6bbc..5b8a74de83 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_buffers_rd.h @@ -106,7 +106,7 @@ private: } NTKey() {} - NTKey(const StringName p_context, const StringName p_texture_name) { + NTKey(const StringName &p_context, const StringName &p_texture_name) { context = p_context; buffer_name = p_texture_name; } @@ -196,7 +196,7 @@ public: bool has_texture(const StringName &p_context, const StringName &p_texture_name) const; RID create_texture(const StringName &p_context, const StringName &p_texture_name, const RD::DataFormat p_data_format, const uint32_t p_usage_bits, const RD::TextureSamples p_texture_samples = RD::TEXTURE_SAMPLES_1, const Size2i p_size = Size2i(0, 0), const uint32_t p_layers = 0, const uint32_t p_mipmaps = 1, bool p_unique = true); RID create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const RD::TextureFormat &p_texture_format, RD::TextureView p_view = RD::TextureView(), bool p_unique = true); - RID create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, RD::TextureView p_view = RD::TextureView()); + RID create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, RD::TextureView p_view = RD::TextureView()); RID get_texture(const StringName &p_context, const StringName &p_texture_name) const; const RD::TextureFormat get_texture_format(const StringName &p_context, const StringName &p_texture_name) const; RID get_texture_slice(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap, const uint32_t p_layers = 1, const uint32_t p_mipmaps = 1); @@ -310,7 +310,7 @@ public: private: RID _create_texture_from_format(const StringName &p_context, const StringName &p_texture_name, const Ref<RDTextureFormat> &p_texture_format, const Ref<RDTextureView> &p_view = Ref<RDTextureView>(), bool p_unique = true); - RID _create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName p_view_name, const Ref<RDTextureView> p_view = Ref<RDTextureView>()); + RID _create_texture_view(const StringName &p_context, const StringName &p_texture_name, const StringName &p_view_name, const Ref<RDTextureView> p_view = Ref<RDTextureView>()); Ref<RDTextureFormat> _get_texture_format(const StringName &p_context, const StringName &p_texture_name) const; RID _get_texture_slice_view(const StringName &p_context, const StringName &p_texture_name, const uint32_t p_layer, const uint32_t p_mipmap, const uint32_t p_layers = 1, const uint32_t p_mipmaps = 1, const Ref<RDTextureView> p_view = Ref<RDTextureView>()); diff --git a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp index 40891f9a63..f2231664fa 100644 --- a/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp +++ b/servers/rendering/renderer_rd/storage_rd/render_scene_data_rd.cpp @@ -252,7 +252,7 @@ void RenderSceneDataRD::update_ubo(RID p_uniform_buffer, RS::ViewportDebugDraw p } uniform_buffer = p_uniform_buffer; - RD::get_singleton()->buffer_update(uniform_buffer, 0, sizeof(UBODATA), &ubo, RD::BARRIER_MASK_RASTER); + RD::get_singleton()->buffer_update(uniform_buffer, 0, sizeof(UBODATA), &ubo); } RID RenderSceneDataRD::get_uniform_buffer() { diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp index 380e325ffa..d8baf260f9 100644 --- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp +++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp @@ -2703,7 +2703,7 @@ void TextureStorage::update_decal_atlas() { Vector<Color> cc; cc.push_back(clear_color); - RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_DROP, RD::FINAL_ACTION_DISCARD, cc); + RD::DrawListID draw_list = RD::get_singleton()->draw_list_begin(mm.fb, RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_DISCARD, RD::FINAL_ACTION_DISCARD, cc); for (const KeyValue<RID, DecalAtlas::Texture> &E : decal_atlas.textures) { DecalAtlas::Texture *t = decal_atlas.textures.getptr(E.key); @@ -2981,7 +2981,7 @@ void TextureStorage::update_decal_buffer(const PagedArray<RID> &p_decals, const } if (decal_count > 0) { - RD::get_singleton()->buffer_update(decal_buffer, 0, sizeof(DecalData) * decal_count, decals, RD::BARRIER_MASK_RASTER | RD::BARRIER_MASK_COMPUTE); + RD::get_singleton()->buffer_update(decal_buffer, 0, sizeof(DecalData) * decal_count, decals); } } @@ -3384,7 +3384,7 @@ void TextureStorage::render_target_do_msaa_resolve(RID p_render_target) { if (!rt->msaa_needs_resolve) { return; } - RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD); + RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD); RD::get_singleton()->draw_list_end(); rt->msaa_needs_resolve = false; } @@ -3501,7 +3501,7 @@ void TextureStorage::render_target_do_clear_request(RID p_render_target) { } Vector<Color> clear_colors; clear_colors.push_back(rt->use_hdr ? rt->clear_color.srgb_to_linear() : rt->clear_color); - RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_READ, RD::INITIAL_ACTION_KEEP, RD::FINAL_ACTION_DISCARD, clear_colors); + RD::get_singleton()->draw_list_begin(rt->get_framebuffer(), RD::INITIAL_ACTION_CLEAR, RD::FINAL_ACTION_STORE, RD::INITIAL_ACTION_LOAD, RD::FINAL_ACTION_DISCARD, clear_colors); RD::get_singleton()->draw_list_end(); rt->clear_requested = false; rt->msaa_needs_resolve = false; diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp index 97e9694fe1..7a2415442d 100644 --- a/servers/rendering/renderer_viewport.cpp +++ b/servers/rendering/renderer_viewport.cpp @@ -754,7 +754,6 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { if (blits.size() > 0) { RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blits.ptr(), blits.size()); } - RSG::rasterizer->end_frame(true); } else if (blits.size() > 0) { if (!blit_to_screen_list.has(vp->viewport_to_screen)) { blit_to_screen_list[vp->viewport_to_screen] = Vector<BlitToScreen>(); @@ -764,6 +763,7 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { blit_to_screen_list[vp->viewport_to_screen].push_back(blits[b]); } } + RSG::rasterizer->end_viewport(p_swap_buffers && blits.size() > 0); } } } else { @@ -793,10 +793,10 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { Vector<BlitToScreen> blit_to_screen_vec; blit_to_screen_vec.push_back(blit); RSG::rasterizer->blit_render_targets_to_screen(vp->viewport_to_screen, blit_to_screen_vec.ptr(), 1); - RSG::rasterizer->end_frame(true); } else { blit_to_screen_list[vp->viewport_to_screen].push_back(blit); } + RSG::rasterizer->end_viewport(p_swap_buffers); } } @@ -823,8 +823,8 @@ void RendererViewport::draw_viewports(bool p_swap_buffers) { RENDER_TIMESTAMP("< Render Viewports"); - if (p_swap_buffers) { - //this needs to be called to make screen swapping more efficient + if (p_swap_buffers && !blit_to_screen_list.is_empty()) { + // This needs to be called to make screen swapping more efficient. RSG::rasterizer->prepare_for_blitting_render_targets(); for (const KeyValue<int, Vector<BlitToScreen>> &E : blit_to_screen_list) { diff --git a/servers/rendering/rendering_device.compat.inc b/servers/rendering/rendering_device.compat.inc index 8e7beda6f3..f79c9c9f7c 100644 --- a/servers/rendering/rendering_device.compat.inc +++ b/servers/rendering/rendering_device.compat.inc @@ -34,39 +34,87 @@ RID RenderingDevice::_shader_create_from_bytecode_bind_compat_79606(const Vector return shader_create_from_bytecode(p_shader_binary, RID()); } -BitField<RenderingDevice::BarrierMask> RenderingDevice::_convert_barrier_mask_81356(BitField<BarrierMask> p_old_barrier) { - if (p_old_barrier == 7) { - return BARRIER_MASK_ALL_BARRIERS; - } else if (p_old_barrier == 16) { - return BARRIER_MASK_NO_BARRIER; - } +void RenderingDevice::_draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier) { + draw_list_end(); +} - BitField<BarrierMask> new_barrier; - if (p_old_barrier & 1) { - new_barrier.set_flag(BARRIER_MASK_VERTEX); - } - if (p_old_barrier & 2) { - new_barrier.set_flag(BARRIER_MASK_FRAGMENT); - } - if (p_old_barrier & 4) { - new_barrier.set_flag(BARRIER_MASK_COMPUTE); +void RenderingDevice::_compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier) { + compute_list_end(); +} + +void RenderingDevice::_barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { + // Does nothing. +} + +void RenderingDevice::_draw_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier) { + draw_list_end(); +} + +void RenderingDevice::_compute_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier) { + compute_list_end(); +} + +RenderingDevice::InitialAction RenderingDevice::_convert_initial_action_84976(InitialAction p_old_initial_action) { + switch (uint32_t(p_old_initial_action)) { + case 0: // INITIAL_ACTION_CLEAR + return INITIAL_ACTION_CLEAR; + case 1: // INITIAL_ACTION_CLEAR_REGION + return INITIAL_ACTION_CLEAR; + case 2: // INITIAL_ACTION_CLEAR_REGION_CONTINUE + case 3: // INITIAL_ACTION_KEEP + return INITIAL_ACTION_LOAD; + case 4: // INITIAL_ACTION_DROP + return INITIAL_ACTION_DISCARD; + case 5: // INITIAL_ACTION_CONTINUE + return INITIAL_ACTION_LOAD; + default: + return INITIAL_ACTION_LOAD; } - if (p_old_barrier & 8) { - new_barrier.set_flag(BARRIER_MASK_TRANSFER); +} + +RenderingDevice::FinalAction RenderingDevice::_convert_final_action_84976(FinalAction p_old_final_action) { + switch (uint32_t(p_old_final_action)) { + case 0: // FINAL_ACTION_READ + return FINAL_ACTION_STORE; + case 1: // FINAL_ACTION_DISCARD + return FINAL_ACTION_DISCARD; + case 2: // FINAL_ACTION_CONTINUE + return FINAL_ACTION_STORE; + default: + return FINAL_ACTION_STORE; } - return new_barrier; } -void RenderingDevice::_draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier) { - draw_list_end(_convert_barrier_mask_81356(p_post_barrier)); +RenderingDevice::DrawListID RenderingDevice::_draw_list_begin_bind_compat_84976(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { + return draw_list_begin(p_framebuffer, _convert_initial_action_84976(p_initial_color_action), _convert_final_action_84976(p_final_color_action), _convert_initial_action_84976(p_initial_depth_action), _convert_final_action_84976(p_final_depth_action), p_clear_color_values, p_clear_depth, p_clear_stencil, p_region); } -void RenderingDevice::_compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier) { - compute_list_end(_convert_barrier_mask_81356(p_post_barrier)); +RenderingDevice::ComputeListID RenderingDevice::_compute_list_begin_bind_compat_84976(bool p_allow_draw_overlap) { + return compute_list_begin(); } -void RenderingDevice::_barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { - barrier(_convert_barrier_mask_81356(p_from), _convert_barrier_mask_81356(p_to)); +Error RenderingDevice::_buffer_update_bind_compat_84976(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { + return _buffer_update_bind(p_buffer, p_offset, p_size, p_data); +} + +Error RenderingDevice::_buffer_clear_bind_compat_84976(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { + return buffer_clear(p_buffer, p_offset, p_size); +} + +Error RenderingDevice::_texture_update_bind_compat_84976(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { + return texture_update(p_texture, p_layer, p_data); +} + +Error RenderingDevice::_texture_copy_bind_compat_84976(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier) { + return texture_copy(p_from_texture, p_to_texture, p_from, p_to, p_size, p_src_mipmap, p_dst_mipmap, p_src_layer, p_dst_layer); +} + +Error RenderingDevice::_texture_clear_bind_compat_84976(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier) { + return texture_clear(p_texture, p_color, p_base_mipmap, p_mipmaps, p_base_layer, p_layers); +} + +Error RenderingDevice::_texture_resolve_multisample_bind_compat_84976(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier) { + return texture_resolve_multisample(p_from_texture, p_to_texture); } void RenderingDevice::_bind_compatibility_methods() { @@ -74,6 +122,16 @@ void RenderingDevice::_bind_compatibility_methods() { ClassDB::bind_compatibility_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::_draw_list_end_bind_compat_81356, DEFVAL(7)); ClassDB::bind_compatibility_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::_compute_list_end_bind_compat_81356, DEFVAL(7)); ClassDB::bind_compatibility_method(D_METHOD("barrier", "from", "to"), &RenderingDevice::_barrier_bind_compat_81356, DEFVAL(7), DEFVAL(7)); + ClassDB::bind_compatibility_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::_draw_list_end_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::_compute_list_end_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_bind_compat_84976, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>())); + ClassDB::bind_compatibility_method(D_METHOD("compute_list_begin", "allow_draw_overlap"), &RenderingDevice::_compute_list_begin_bind_compat_84976, DEFVAL(false)); + ClassDB::bind_compatibility_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data", "post_barrier"), &RenderingDevice::_buffer_update_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::_buffer_clear_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("texture_update", "texture", "layer", "data", "post_barrier"), &RenderingDevice::_texture_update_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer", "post_barrier"), &RenderingDevice::_texture_copy_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::_texture_clear_bind_compat_84976, DEFVAL(0x7FFF)); + ClassDB::bind_compatibility_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::_texture_resolve_multisample_bind_compat_84976, DEFVAL(0x7FFF)); } #endif diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp index 30bf47a096..8e03796d33 100644 --- a/servers/rendering/rendering_device.cpp +++ b/servers/rendering/rendering_device.cpp @@ -37,7 +37,26 @@ #include "core/io/dir_access.h" #include "servers/rendering/renderer_rd/api_context_rd.h" -//#define FORCE_FULL_BARRIER +// When true, the command graph will attempt to reorder the rendering commands submitted by the user based on the dependencies detected from +// the commands automatically. This should improve rendering performance in most scenarios at the cost of some extra CPU overhead. +// +// This behavior can be disabled if it's suspected that the graph is not detecting dependencies correctly and more control over the order of +// the commands is desired (e.g. debugging). + +#define RENDER_GRAPH_REORDER 1 + +// Synchronization barriers are issued between the graph's levels only with the necessary amount of detail to achieve the correct result. If +// it's suspected that the graph is not doing this correctly, full barriers can be issued instead that will block all types of operations +// between the synchronization levels. This setting will have a very negative impact on performance when enabled, so it's only intended for +// debugging purposes. + +#define RENDER_GRAPH_FULL_BARRIERS 0 + +// The command graph can automatically issue secondary command buffers and record them on background threads when they reach an arbitrary +// size threshold. This can be very beneficial towards reducing the time the main thread takes to record all the rendering commands. However, +// this setting is not enabled by default as it's been shown to cause some strange issues with certain IHVs that have yet to be understood. + +#define SECONDARY_COMMAND_BUFFERS_PER_FRAME 0 RenderingDevice *RenderingDevice::singleton = nullptr; @@ -131,127 +150,23 @@ RID RenderingDevice::shader_create_from_spirv(const Vector<ShaderStageSPIRVData> return shader_create_from_bytecode(bytecode); } -/******************/ -/**** BARRIERS ****/ -/******************/ - -void RenderingDevice::_full_barrier(bool p_sync_with_draw) { - // Used for debug. - - RDD::MemoryBarrier mb; - mb.src_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | - RDD::BARRIER_ACCESS_INDEX_READ_BIT | - RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | - RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | - RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | - RDD::BARRIER_ACCESS_HOST_READ_BIT | - RDD::BARRIER_ACCESS_HOST_WRITE_BIT); - mb.dst_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | - RDD::BARRIER_ACCESS_INDEX_READ_BIT | - RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | - RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | - RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | - RDD::BARRIER_ACCESS_HOST_READ_BIT | - RDD::BARRIER_ACCESS_HOST_WRITE_BIT); - - RDD::CommandBufferID cmd_buffer = p_sync_with_draw ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer; - driver->command_pipeline_barrier(cmd_buffer, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, mb, {}, {}); -} - /***************************/ /**** BUFFER MANAGEMENT ****/ /***************************/ -RenderingDevice::Buffer *RenderingDevice::_get_buffer_from_owner(RID p_buffer, BitField<RDD::PipelineStageBits> &r_stages, BitField<RDD::BarrierAccessBits> &r_access, BitField<BarrierMask> p_post_barrier) { +RenderingDevice::Buffer *RenderingDevice::_get_buffer_from_owner(RID p_buffer) { Buffer *buffer = nullptr; - r_stages.clear(); - r_access.clear(); if (vertex_buffer_owner.owns(p_buffer)) { buffer = vertex_buffer_owner.get_or_null(p_buffer); - - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); - if (buffer->usage & RDD::BUFFER_USAGE_STORAGE_BIT) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - } - } } else if (index_buffer_owner.owns(p_buffer)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT); buffer = index_buffer_owner.get_or_null(p_buffer); } else if (uniform_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - } - r_access.set_flag(RDD::BARRIER_ACCESS_UNIFORM_READ_BIT); buffer = uniform_buffer_owner.get_or_null(p_buffer); } else if (texture_buffer_owner.owns(p_buffer)) { - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT); - } - - // FIXME: Broken. + DEV_ASSERT(false && "FIXME: Broken."); //buffer = texture_buffer_owner.get_or_null(p_buffer)->buffer; } else if (storage_buffer_owner.owns(p_buffer)) { buffer = storage_buffer_owner.get_or_null(p_buffer); - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - - if (buffer->usage.has_flag(RDD::BUFFER_USAGE_INDIRECT_BIT)) { - r_stages.set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); - r_access.set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); - } } return buffer; } @@ -269,10 +184,11 @@ Error RenderingDevice::_insert_staging_block() { return OK; } -Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment) { +Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment) { // Determine a block to use. r_alloc_size = p_amount; + r_required_action = STAGING_REQUIRED_ACTION_NONE; while (true) { r_alloc_offset = 0; @@ -324,23 +240,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re // and this frame is not even done. // If this is the main thread, it means the user is likely loading a lot of resources at once,. // Otherwise, the thread should just be blocked until the next frame (currently unimplemented). - - if (false) { // Separate thread from render. - - //block_until_next_frame() - continue; - } else { - // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. - _flush(true); - - // Clear the whole staging buffer. - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - staging_buffer_blocks.write[i].frame_used = 0; - staging_buffer_blocks.write[i].fill_amount = 0; - } - // Claim current. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } + r_required_action = STAGING_REQUIRED_ACTION_FLUSH_CURRENT; } } else { @@ -368,28 +268,7 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re // Let's flush older frames. // The logic here is that if a game is loading a lot of data from the main thread, it will need to be stalled anyway. // If loading from a separate thread, we can block that thread until next frame when more room is made (not currently implemented, though). - - if (false) { - // Separate thread from render. - //block_until_next_frame() - continue; // And try again. - } else { - _flush(false); - - for (int i = 0; i < staging_buffer_blocks.size(); i++) { - // Clear all blocks but the ones from this frame. - int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); - if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { - break; // Ok, we reached something from this frame, abort. - } - - staging_buffer_blocks.write[block_idx].frame_used = 0; - staging_buffer_blocks.write[block_idx].fill_amount = 0; - } - - // Claim for current frame. - staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; - } + r_required_action = STAGING_REQUIRED_ACTION_FLUSH_OLDER; } } @@ -402,20 +281,78 @@ Error RenderingDevice::_staging_buffer_allocate(uint32_t p_amount, uint32_t p_re return OK; } -Error RenderingDevice::_buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer, uint32_t p_required_align) { +void RenderingDevice::_staging_buffer_execute_required_action(StagingRequiredAction p_required_action) { + switch (p_required_action) { + case STAGING_REQUIRED_ACTION_NONE: { + // Do nothing. + } break; + case STAGING_REQUIRED_ACTION_FLUSH_CURRENT: { + // Flush EVERYTHING including setup commands. IF not immediate, also need to flush the draw commands. + _flush(true); + + // Clear the whole staging buffer. + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + staging_buffer_blocks.write[i].frame_used = 0; + staging_buffer_blocks.write[i].fill_amount = 0; + } + + // Claim for current frame. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } break; + case STAGING_REQUIRED_ACTION_FLUSH_OLDER: { + _flush(false); + + for (int i = 0; i < staging_buffer_blocks.size(); i++) { + // Clear all blocks but the ones from this frame. + int block_idx = (i + staging_buffer_current) % staging_buffer_blocks.size(); + if (staging_buffer_blocks[block_idx].frame_used == frames_drawn) { + break; // Ok, we reached something from this frame, abort. + } + + staging_buffer_blocks.write[block_idx].frame_used = 0; + staging_buffer_blocks.write[block_idx].fill_amount = 0; + } + + // Claim for current frame. + staging_buffer_blocks.write[staging_buffer_current].frame_used = frames_drawn; + } break; + default: { + DEV_ASSERT(false && "Unknown required action."); + } break; + } +} + +Error RenderingDevice::_buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_queue, uint32_t p_required_align) { // Submitting may get chunked for various reasons, so convert this to a task. size_t to_submit = p_data_size; size_t submit_from = 0; + thread_local LocalVector<RDG::RecordedBufferCopy> command_buffer_copies_vector; + command_buffer_copies_vector.clear(); + while (to_submit > 0) { uint32_t block_write_offset; uint32_t block_write_amount; + StagingRequiredAction required_action; - Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount); + Error err = _staging_buffer_allocate(MIN(to_submit, staging_buffer_block_size), p_required_align, block_write_offset, block_write_amount, required_action); if (err) { return err; } + if (p_use_draw_queue && !command_buffer_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_CURRENT) { + if (_buffer_make_mutable(p_buffer, p_buffer_id)) { + // The buffer must be mutable to be used as a copy destination. + draw_graph.add_synchronization(); + } + + // If we're using the draw queue and the staging buffer requires flushing everything, we submit the command early and clear the current vector. + draw_graph.add_buffer_update(p_buffer->driver_id, p_buffer->draw_tracker, command_buffer_copies_vector); + command_buffer_copies_vector.clear(); + } + + _staging_buffer_execute_required_action(required_action); + // Map staging buffer (It's CPU and coherent). uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); @@ -427,12 +364,19 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, size_t p_offset, const u driver->buffer_unmap(staging_buffer_blocks[staging_buffer_current].driver_id); // Insert a command to copy this. - RDD::BufferCopyRegion region; region.src_offset = block_write_offset; region.dst_offset = submit_from + p_offset; region.size = block_write_amount; - driver->command_copy_buffer(p_use_draw_command_buffer ? frames[frame].draw_command_buffer : frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, p_buffer->driver_id, region); + + if (p_use_draw_queue) { + RDG::RecordedBufferCopy buffer_copy; + buffer_copy.source = staging_buffer_blocks[staging_buffer_current].driver_id; + buffer_copy.region = region; + command_buffer_copies_vector.push_back(buffer_copy); + } else { + driver->command_copy_buffer(frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, p_buffer->driver_id, region); + } staging_buffer_blocks.write[staging_buffer_current].fill_amount = block_write_offset + block_write_amount; @@ -440,10 +384,19 @@ Error RenderingDevice::_buffer_update(Buffer *p_buffer, size_t p_offset, const u submit_from += block_write_amount; } + if (p_use_draw_queue && !command_buffer_copies_vector.is_empty()) { + if (_buffer_make_mutable(p_buffer, p_buffer_id)) { + // The buffer must be mutable to be used as a copy destination. + draw_graph.add_synchronization(); + } + + draw_graph.add_buffer_update(p_buffer->driver_id, p_buffer->draw_tracker, command_buffer_copies_vector); + } + return OK; } -Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, @@ -451,25 +404,12 @@ Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, "Copying buffers is forbidden during creation of a compute list"); - // This method assumes the barriers have been pushed prior to being called, therefore no barriers are pushed - // for the source or destination buffers before performing the copy. These masks are effectively ignored. - BitField<RDD::PipelineStageBits> src_stages; - BitField<RDD::BarrierAccessBits> src_access; - Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer, src_stages, src_access, BARRIER_MASK_NO_BARRIER); + Buffer *src_buffer = _get_buffer_from_owner(p_src_buffer); if (!src_buffer) { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Source buffer argument is not a valid buffer of any type."); } - BitField<RDD::PipelineStageBits> dst_stages; - BitField<RDD::BarrierAccessBits> dst_access; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // If the post barrier mask defines it, we indicate the destination buffer will require a barrier with these flags set - // after the copy command is queued. - dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer, dst_stages, dst_access, p_post_barrier); + Buffer *dst_buffer = _get_buffer_from_owner(p_dst_buffer); if (!dst_buffer) { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Destination buffer argument is not a valid buffer of any type."); } @@ -483,31 +423,18 @@ Error RenderingDevice::buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t region.src_offset = p_src_offset; region.dst_offset = p_dst_offset; region.size = p_size; - driver->command_copy_buffer(frames[frame].draw_command_buffer, src_buffer->driver_id, dst_buffer->driver_id, region); -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS) && p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { - if (dst_stages.is_empty()) { - dst_stages = RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; - } - - // As indicated by the post barrier mask, push a new barrier. - RDD::BufferBarrier bb; - bb.buffer = dst_buffer->driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = dst_access; - bb.offset = p_dst_offset; - bb.size = p_size; - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); + if (_buffer_make_mutable(dst_buffer, p_dst_buffer)) { + // The destination buffer must be mutable to be used as a copy destination. + draw_graph.add_synchronization(); } -#endif + + draw_graph.add_buffer_copy(src_buffer->driver_id, src_buffer->draw_tracker, dst_buffer->driver_id, dst_buffer->draw_tracker, region); return OK; } -Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list, ERR_INVALID_PARAMETER, @@ -515,14 +442,7 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, "Updating buffers is forbidden during creation of a compute list"); - BitField<RDD::PipelineStageBits> dst_stages; - BitField<RDD::BarrierAccessBits> dst_access; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // Protect subsequent updates. - dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stages, dst_access, p_post_barrier); + Buffer *buffer = _get_buffer_from_owner(p_buffer); if (!buffer) { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); } @@ -530,33 +450,10 @@ Error RenderingDevice::buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - Error err = _buffer_update(buffer, p_offset, (uint8_t *)p_data, p_size, true); - if (err) { - return err; - } - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (dst_stages.is_empty()) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS) && p_post_barrier != RD::BARRIER_MASK_NO_BARRIER) { - RDD::BufferBarrier bb; - bb.buffer = buffer->driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = dst_access; - bb.offset = p_offset; - bb.size = p_size; - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); - } - -#endif - return err; + return _buffer_update(buffer, p_buffer, p_offset, (uint8_t *)p_data, p_size, true); } -Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG((p_size % 4) != 0, ERR_INVALID_PARAMETER, @@ -566,15 +463,7 @@ Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_ ERR_FAIL_COND_V_MSG(compute_list, ERR_INVALID_PARAMETER, "Updating buffers is forbidden during creation of a compute list"); - BitField<RDD::PipelineStageBits> dst_stages; - BitField<RDD::BarrierAccessBits> dst_access; - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - // Protect subsequent updates. - dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - Buffer *buffer = _get_buffer_from_owner(p_buffer, dst_stages, dst_access, p_post_barrier); + Buffer *buffer = _get_buffer_from_owner(p_buffer); if (!buffer) { ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "Buffer argument is not a valid buffer of any type."); } @@ -582,51 +471,24 @@ Error RenderingDevice::buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_ ERR_FAIL_COND_V_MSG(p_offset + p_size > buffer->size, ERR_INVALID_PARAMETER, "Attempted to write buffer (" + itos((p_offset + p_size) - buffer->size) + " bytes) past the end."); - driver->command_clear_buffer(frames[frame].draw_command_buffer, buffer->driver_id, p_offset, p_size); - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#else - if (dst_stages.is_empty()) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); + if (_buffer_make_mutable(buffer, p_buffer)) { + // The destination buffer must be mutable to be used as a clear destination. + draw_graph.add_synchronization(); } - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = buffer->driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = dst_access; - bb.offset = p_offset; - bb.size = p_size; - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, dst_stages, {}, bb, {}); - } + draw_graph.add_buffer_clear(buffer->driver_id, buffer->draw_tracker, p_offset, p_size); -#endif return OK; } Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset, uint32_t p_size) { _THREAD_SAFE_METHOD_ - // It could be this buffer was just created. - BitField<RDD::PipelineStageBits> src_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; - BitField<RDD::BarrierAccessBits> src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - // Get the vulkan buffer and the potential stage/access possible. - Buffer *buffer = _get_buffer_from_owner(p_buffer, src_stages, src_access, BARRIER_MASK_ALL_BARRIERS); + Buffer *buffer = _get_buffer_from_owner(p_buffer); if (!buffer) { ERR_FAIL_V_MSG(Vector<uint8_t>(), "Buffer is either invalid or this type of buffer can't be retrieved. Only Index and Vertex buffers allow retrieving."); } - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - // Make sure no one is using the buffer -- the "true" gets us to the same command buffer as below. - RDD::BufferBarrier bb; - bb.buffer = buffer->driver_id; - bb.src_access = src_access; - bb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - bb.size = buffer->size; - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stages, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, bb, {}); - } - // Size of buffer to retrieve. if (!p_size) { p_size = buffer->size; @@ -641,7 +503,9 @@ Vector<uint8_t> RenderingDevice::buffer_get_data(RID p_buffer, uint32_t p_offset RDD::BufferCopyRegion region; region.src_offset = p_offset; region.size = p_size; - driver->command_copy_buffer(frames[frame].draw_command_buffer, buffer->driver_id, tmp_buffer, region); + + draw_graph.add_buffer_get_data(buffer->driver_id, buffer->draw_tracker, tmp_buffer, region); + // Flush everything so memory can be safely mapped. _flush(true); @@ -676,23 +540,21 @@ RID RenderingDevice::storage_buffer_create(uint32_t p_size_bytes, const Vector<u buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); + // Storage buffers are assumed to be mutable. + buffer.draw_tracker = RDG::resource_tracker_create(); + buffer.draw_tracker->buffer_driver_id = buffer.driver_id; + if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = buffer.driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - bb.size = data_size; - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, bb, {}); - } + _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size()); } buffer_memory += buffer.size; - return storage_buffer_owner.make_rid(buffer); + RID id = storage_buffer_owner.make_rid(buffer); +#ifdef DEV_ENABLED + set_resource_name(id, "RID:" + itos(id.get_id())); +#endif + return id; } RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat p_format, const Vector<uint8_t> &p_data) { @@ -710,6 +572,12 @@ RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat texture_buffer.driver_id = driver->buffer_create(size_bytes, usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!texture_buffer.driver_id, RID()); + // Texture buffers are assumed to be immutable unless they don't have initial data. + if (p_data.is_empty()) { + texture_buffer.draw_tracker = RDG::resource_tracker_create(); + texture_buffer.draw_tracker->buffer_driver_id = texture_buffer.driver_id; + } + bool ok = driver->buffer_set_texel_format(texture_buffer.driver_id, p_format); if (!ok) { driver->buffer_free(texture_buffer.driver_id); @@ -717,15 +585,7 @@ RID RenderingDevice::texture_buffer_create(uint32_t p_size_elements, DataFormat } if (p_data.size()) { - _buffer_update(&texture_buffer, 0, p_data.ptr(), p_data.size()); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = texture_buffer.driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; - bb.size = size_bytes; - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, (RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT), {}, bb, {}); - } + _buffer_update(&texture_buffer, RID(), 0, p_data.ptr(), p_data.size()); } buffer_memory += size_bytes; @@ -787,10 +647,8 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture ERR_FAIL_COND_V_MSG(required_mipmaps < format.mipmaps, RID(), "Too many mipmaps requested for texture format and dimensions (" + itos(format.mipmaps) + "), maximum allowed: (" + itos(required_mipmaps) + ")."); + uint32_t forced_usage_bits = 0; if (p_data.size()) { - ERR_FAIL_COND_V_MSG(!(format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT), RID(), - "Texture needs the TEXTURE_USAGE_CAN_UPDATE_BIT usage flag in order to be updated at initialization or later"); - ERR_FAIL_COND_V_MSG(p_data.size() != (int)format.array_layers, RID(), "Default supplied data for image format is of invalid length (" + itos(p_data.size()) + "), should be (" + itos(format.array_layers) + ")."); @@ -799,6 +657,10 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture ERR_FAIL_COND_V_MSG((uint32_t)p_data[i].size() != required_size, RID(), "Data for slice index " + itos(i) + " (mapped to layer " + itos(i) + ") differs in size (supplied: " + itos(p_data[i].size()) + ") than what is required by the format (" + itos(required_size) + ")."); } + + if (!(format.usage_bits & TEXTURE_USAGE_CAN_UPDATE_BIT)) { + forced_usage_bits = TEXTURE_USAGE_CAN_UPDATE_BIT; + } } { @@ -849,7 +711,7 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture // Create. Texture texture; - + format.usage_bits |= forced_usage_bits; texture.driver_id = driver->texture_create(format, tv); ERR_FAIL_COND_V(!texture.driver_id, RID()); texture.type = format.texture_type; @@ -862,26 +724,10 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture texture.base_mipmap = 0; texture.base_layer = 0; texture.is_resolve_buffer = format.is_resolve_buffer; - texture.usage_flags = format.usage_bits; + texture.usage_flags = format.usage_bits & ~forced_usage_bits; texture.samples = format.samples; texture.allowed_shared_formats = format.shareable_formats; - - // Set base layout based on usage priority. - - if ((format.usage_bits & TEXTURE_USAGE_SAMPLING_BIT)) { - // First priority, readable. - texture.layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } else if ((format.usage_bits & TEXTURE_USAGE_STORAGE_BIT)) { - // Second priority, storage. - texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; - } else if ((format.usage_bits & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - // Third priority, color or depth. - texture.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - texture.layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } else { - texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; - } + texture.has_initial_data = !p_data.is_empty(); if ((format.usage_bits & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); @@ -896,18 +742,10 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture texture.bound = false; - // Barrier to set layout. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier tb; - tb.texture = texture.driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; - tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; - tb.next_layout = texture.layout; - tb.subresources.aspect = texture.barrier_aspect_flags; - tb.subresources.mipmap_count = format.mipmaps; - tb.subresources.layer_count = format.array_layers; - - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, tb); + // Textures are only assumed to be immutable if they have initial data and none of the other bits that indicate write usage are enabled. + bool texture_mutable_by_default = texture.usage_flags & (TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT | TEXTURE_USAGE_STORAGE_BIT | TEXTURE_USAGE_STORAGE_ATOMIC_BIT | TEXTURE_USAGE_VRS_ATTACHMENT_BIT); + if (p_data.is_empty() || texture_mutable_by_default) { + _texture_make_mutable(&texture, RID()); } texture_memory += driver->texture_get_allocation_size(texture.driver_id); @@ -919,9 +757,15 @@ RID RenderingDevice::texture_create(const TextureFormat &p_format, const Texture if (p_data.size()) { for (uint32_t i = 0; i < p_format.array_layers; i++) { - _texture_update(id, i, p_data[i], BARRIER_MASK_ALL_BARRIERS, true); + _texture_update(id, i, p_data[i], true, false); + } + + if (texture.draw_tracker != nullptr) { + // Draw tracker can assume the texture will be in transfer destination. + texture.draw_tracker->usage = RDG::RESOURCE_USAGE_TRANSFER_TO; } } + return id; } @@ -959,6 +803,12 @@ RID RenderingDevice::texture_create_shared(const TextureView &p_view, RID p_with texture.driver_id = driver->texture_create_shared(texture.driver_id, tv); ERR_FAIL_COND_V(!texture.driver_id, RID()); + texture.slice_trackers.clear(); + + if (texture.draw_tracker != nullptr) { + texture.draw_tracker->reference_count++; + } + texture.owner = p_with_texture; RID id = texture_owner.make_rid(texture); #ifdef DEV_ENABLED @@ -988,23 +838,6 @@ RID RenderingDevice::texture_create_from_extension(TextureType p_type, DataForma texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_UNORM); texture.allowed_shared_formats.push_back(RD::DATA_FORMAT_R8G8B8A8_SRGB); - // Set base layout based on usage priority. - - if (p_usage.has_flag(TEXTURE_USAGE_SAMPLING_BIT)) { - // First priority, readable. - texture.layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } else if (p_usage.has_flag(TEXTURE_USAGE_STORAGE_BIT)) { - // Second priority, storage. - texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; - } else if (p_usage.has_flag(TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - // Third priority, color or depth. - texture.layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if (p_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - texture.layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } else { - texture.layout = RDD::TEXTURE_LAYOUT_GENERAL; - } - if (p_usage.has_flag(TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { texture.read_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); texture.barrier_aspect_flags.set_flag(RDD::TEXTURE_ASPECT_DEPTH_BIT); @@ -1019,19 +852,7 @@ RID RenderingDevice::texture_create_from_extension(TextureType p_type, DataForma texture.driver_id = driver->texture_create_from_extension(p_image, p_type, p_format, p_layers, (texture.usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)); ERR_FAIL_COND_V(!texture.driver_id, RID()); - // Barrier to set layout. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier tb; - tb.texture = texture.driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; - tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; - tb.next_layout = texture.layout; - tb.subresources.aspect = texture.barrier_aspect_flags; - tb.subresources.mipmap_count = texture.mipmaps; - tb.subresources.layer_count = texture.layers; - - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT, {}, {}, tb); - } + _texture_make_mutable(&texture, RID()); RID id = texture_owner.make_rid(texture); #ifdef DEV_ENABLED @@ -1081,6 +902,7 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, } Texture texture = *src_texture; + get_image_format_required_size(texture.format, texture.width, texture.height, texture.depth, p_mipmap + 1, &texture.width, &texture.height); texture.mipmaps = p_mipmaps; texture.layers = slice_layers; @@ -1118,7 +940,17 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, texture.driver_id = driver->texture_create_shared_from_slice(src_texture->driver_id, tv, p_slice_type, p_layer, slice_layers, p_mipmap, p_mipmaps); ERR_FAIL_COND_V(!texture.driver_id, RID()); + const Rect2i slice_rect(p_mipmap, p_layer, p_mipmaps, slice_layers); texture.owner = p_with_texture; + texture.slice_type = p_slice_type; + texture.slice_rect = slice_rect; + + // If parent is mutable, make slice mutable by default. + if (src_texture->draw_tracker != nullptr) { + texture.draw_tracker = nullptr; + _texture_make_mutable(&texture, RID()); + } + RID id = texture_owner.make_rid(texture); #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); @@ -1128,8 +960,8 @@ RID RenderingDevice::texture_create_shared_from_slice(const TextureView &p_view, return id; } -Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { - return _texture_update(p_texture, p_layer, p_data, p_post_barrier, false); +Error RenderingDevice::texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data) { + return _texture_update(p_texture, p_layer, p_data, false, true); } static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_t *__restrict p_dst, uint32_t p_src_x, uint32_t p_src_y, uint32_t p_src_w, uint32_t p_src_h, uint32_t p_src_full_w, uint32_t p_dst_pitch, uint32_t p_unit_size) { @@ -1148,7 +980,7 @@ static _ALWAYS_INLINE_ void _copy_region(uint8_t const *__restrict p_src, uint8_ } } -Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue) { +Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_use_setup_queue, bool p_validate_can_update) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG((draw_list || compute_list) && !p_use_setup_queue, ERR_INVALID_PARAMETER, @@ -1166,7 +998,7 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve ERR_FAIL_COND_V_MSG(texture->bound, ERR_CANT_ACQUIRE_RESOURCE, "Texture can't be updated while a draw list that uses it as part of a framebuffer is being created. Ensure the draw list is finalized (and that the color/depth texture using it is not set to `RenderingDevice.FINAL_ACTION_CONTINUE`) to update this texture."); - ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, + ERR_FAIL_COND_V_MSG(p_validate_can_update && !(texture->usage_flags & TEXTURE_USAGE_CAN_UPDATE_BIT), ERR_INVALID_PARAMETER, "Texture requires the `RenderingDevice.TEXTURE_USAGE_CAN_UPDATE_BIT` to be set to be updatable."); uint32_t layer_count = texture->layers; @@ -1191,21 +1023,22 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve const uint8_t *r = p_data.ptr(); - RDD::CommandBufferID command_buffer = p_use_setup_queue ? frames[frame].setup_command_buffer : frames[frame].draw_command_buffer; + thread_local LocalVector<RDG::RecordedBufferToTextureCopy> command_buffer_to_texture_copies_vector; + command_buffer_to_texture_copies_vector.clear(); - // Barrier to transfer. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + if (p_use_setup_queue && driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // When using the setup queue directly, we transition the texture to the optimal layout. RDD::TextureBarrier tb; tb.texture = texture->driver_id; tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.prev_layout = texture->layout; + tb.prev_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; tb.subresources.aspect = texture->barrier_aspect_flags; tb.subresources.mipmap_count = texture->mipmaps; tb.subresources.base_layer = p_layer; tb.subresources.layer_count = 1; - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); } uint32_t mipmap_offset = 0; @@ -1240,12 +1073,26 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve uint32_t pitch_step = driver->api_trait_get(RDD::API_TRAIT_TEXTURE_DATA_ROW_PITCH_STEP); region_pitch = STEPIFY(region_pitch, pitch_step); uint32_t to_allocate = region_pitch * region_h; - uint32_t alloc_offset = 0, alloc_size = 0; - Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, false); + StagingRequiredAction required_action; + Error err = _staging_buffer_allocate(to_allocate, required_align, alloc_offset, alloc_size, required_action, false); ERR_FAIL_COND_V(err, ERR_CANT_CREATE); - uint8_t *write_ptr = nullptr; + if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty() && required_action == STAGING_REQUIRED_ACTION_FLUSH_CURRENT) { + if (_texture_make_mutable(texture, p_texture)) { + // The texture must be mutable to be used as a copy destination. + draw_graph.add_synchronization(); + } + + // If we're using the draw queue and the staging buffer requires flushing everything, we submit the command early and clear the current vector. + draw_graph.add_texture_update(texture->driver_id, texture->draw_tracker, command_buffer_to_texture_copies_vector); + command_buffer_to_texture_copies_vector.clear(); + } + + _staging_buffer_execute_required_action(required_action); + + uint8_t *write_ptr; + { // Map. uint8_t *data_ptr = driver->buffer_map(staging_buffer_blocks[staging_buffer_current].driver_id); ERR_FAIL_NULL_V(data_ptr, ERR_CANT_CREATE); @@ -1288,7 +1135,14 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve copy_region.texture_offset = Vector3i(x, y, z); copy_region.texture_region_size = Vector3i(region_logic_w, region_logic_h, 1); - driver->command_copy_buffer_to_texture(command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, texture->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, copy_region); + if (p_use_setup_queue) { + driver->command_copy_buffer_to_texture(frames[frame].setup_command_buffer, staging_buffer_blocks[staging_buffer_current].driver_id, texture->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, copy_region); + } else { + RDG::RecordedBufferToTextureCopy buffer_to_texture_copy; + buffer_to_texture_copy.from_buffer = staging_buffer_blocks[staging_buffer_current].driver_id; + buffer_to_texture_copy.region = copy_region; + command_buffer_to_texture_copies_vector.push_back(buffer_to_texture_copy); + } staging_buffer_blocks.write[staging_buffer_current].fill_amount = alloc_offset + alloc_size; } @@ -1300,50 +1154,25 @@ Error RenderingDevice::_texture_update(RID p_texture, uint32_t p_layer, const Ve logic_height = MAX(1u, logic_height >> 1); } - // Barrier to restore layout. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - BitField<RDD::PipelineStageBits> stages; - BitField<RDD::BarrierAccessBits> access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - if (stages.is_empty()) { - stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - + if (p_use_setup_queue && (texture->draw_tracker == nullptr) && driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { + // If the texture does not have a tracker, it means it must be transitioned to the sampling state. RDD::TextureBarrier tb; tb.texture = texture->driver_id; tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.dst_access = access; tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - tb.next_layout = texture->layout; + tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; tb.subresources.aspect = texture->barrier_aspect_flags; tb.subresources.mipmap_count = texture->mipmaps; tb.subresources.base_layer = p_layer; tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); - - if (texture->used_in_frame != frames_drawn) { - texture->used_in_raster = false; - texture->used_in_compute = false; - texture->used_in_frame = frames_drawn; + driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, {}, tb); + } else if (!p_use_setup_queue && !command_buffer_to_texture_copies_vector.is_empty()) { + if (_texture_make_mutable(texture, p_texture)) { + // The texture must be mutable to be used as a copy destination. + draw_graph.add_synchronization(); } - texture->used_in_transfer = true; + + draw_graph.add_texture_update(texture->driver_id, texture->draw_tracker, command_buffer_to_texture_copies_vector); } return OK; @@ -1455,103 +1284,71 @@ Vector<uint8_t> RenderingDevice::texture_get_data(RID p_texture, uint32_t p_laye RDD::BufferID tmp_buffer = driver->buffer_create(work_buffer_size, RDD::BUFFER_USAGE_TRANSFER_TO_BIT, RDD::MEMORY_ALLOCATION_TYPE_CPU); ERR_FAIL_COND_V(!tmp_buffer, Vector<uint8_t>()); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; // Makes more sense to retrieve. - - // Pre-copy barrier. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier tb; - tb.texture = tex->driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.prev_layout = tex->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.subresources.aspect = tex->barrier_aspect_flags; - tb.subresources.mipmap_count = tex->mipmaps; - tb.subresources.base_layer = p_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } + thread_local LocalVector<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_vector; + command_buffer_texture_copy_regions_vector.clear(); - { - uint32_t w = tex->width; - uint32_t h = tex->height; - uint32_t d = tex->depth; - for (uint32_t i = 0; i < tex->mipmaps; i++) { - RDD::BufferTextureCopyRegion copy_region; - copy_region.buffer_offset = mip_layouts[i].offset; - copy_region.texture_subresources.aspect = tex->read_aspect_flags; - copy_region.texture_subresources.mipmap = i; - copy_region.texture_subresources.base_layer = p_layer; - copy_region.texture_subresources.layer_count = 1; - copy_region.texture_region_size.x = w; - copy_region.texture_region_size.y = h; - copy_region.texture_region_size.z = d; - driver->command_copy_texture_to_buffer(command_buffer, tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, tmp_buffer, copy_region); - - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); - d = MAX(1u, d >> 1); - } + uint32_t w = tex->width; + uint32_t h = tex->height; + uint32_t d = tex->depth; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + RDD::BufferTextureCopyRegion copy_region; + copy_region.buffer_offset = mip_layouts[i].offset; + copy_region.texture_subresources.aspect = tex->read_aspect_flags; + copy_region.texture_subresources.mipmap = i; + copy_region.texture_subresources.base_layer = p_layer; + copy_region.texture_subresources.layer_count = 1; + copy_region.texture_region_size.x = w; + copy_region.texture_region_size.y = h; + copy_region.texture_region_size.z = d; + command_buffer_texture_copy_regions_vector.push_back(copy_region); + + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); } - // Post-copy barrier. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier tb; - tb.texture = tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.dst_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; - if ((tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { - tb.dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.next_layout = tex->layout; - tb.subresources.aspect = tex->barrier_aspect_flags; - tb.subresources.mipmap_count = tex->mipmaps; - tb.subresources.base_layer = p_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, tb); + if (_texture_make_mutable(tex, p_texture)) { + // The texture must be mutable to be used as a copy source due to layout transitions. + draw_graph.add_synchronization(); } + draw_graph.add_texture_get_data(tex->driver_id, tex->draw_tracker, tmp_buffer, command_buffer_texture_copy_regions_vector); + _flush(true); const uint8_t *read_ptr = driver->buffer_map(tmp_buffer); ERR_FAIL_NULL_V(read_ptr, Vector<uint8_t>()); Vector<uint8_t> buffer_data; - { - uint32_t tight_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps); - buffer_data.resize(tight_buffer_size); - - uint8_t *write_ptr = buffer_data.ptrw(); - - uint32_t w = tex->width; - uint32_t h = tex->height; - uint32_t d = tex->depth; - for (uint32_t i = 0; i < tex->mipmaps; i++) { - uint32_t width = 0, height = 0, depth = 0; - uint32_t tight_mip_size = get_image_format_required_size(tex->format, w, h, d, 1, &width, &height, &depth); - uint32_t block_w = 0, block_h = 0; - get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); - uint32_t tight_row_pitch = tight_mip_size / ((height / block_h) * depth); - - { - // Copy row-by-row to erase padding due to alignments. - const uint8_t *rp = read_ptr; - uint8_t *wp = write_ptr; - for (uint32_t row = h * d / block_h; row != 0; row--) { - memcpy(wp, rp, tight_row_pitch); - rp += mip_layouts[i].row_pitch; - wp += tight_row_pitch; - } - } + uint32_t tight_buffer_size = get_image_format_required_size(tex->format, tex->width, tex->height, tex->depth, tex->mipmaps); + buffer_data.resize(tight_buffer_size); + + uint8_t *write_ptr = buffer_data.ptrw(); - w = MAX(1u, w >> 1); - h = MAX(1u, h >> 1); - d = MAX(1u, d >> 1); - read_ptr += mip_layouts[i].size; - write_ptr += tight_mip_size; + w = tex->width; + h = tex->height; + d = tex->depth; + for (uint32_t i = 0; i < tex->mipmaps; i++) { + uint32_t width = 0, height = 0, depth = 0; + uint32_t tight_mip_size = get_image_format_required_size(tex->format, w, h, d, 1, &width, &height, &depth); + uint32_t block_w = 0, block_h = 0; + get_compressed_image_format_block_dimensions(tex->format, block_w, block_h); + uint32_t tight_row_pitch = tight_mip_size / ((height / block_h) * depth); + + // Copy row-by-row to erase padding due to alignments. + const uint8_t *rp = read_ptr; + uint8_t *wp = write_ptr; + for (uint32_t row = h * d / block_h; row != 0; row--) { + memcpy(wp, rp, tight_row_pitch); + rp += mip_layouts[i].row_pitch; + wp += tight_row_pitch; } + + w = MAX(1u, w >> 1); + h = MAX(1u, h >> 1); + d = MAX(1u, d >> 1); + read_ptr += mip_layouts[i].size; + write_ptr += tight_mip_size; } driver->buffer_unmap(tmp_buffer); @@ -1610,7 +1407,7 @@ uint64_t RenderingDevice::texture_get_native_handle(RID p_texture) { } #endif -Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer) { _THREAD_SAFE_METHOD_ Texture *src_tex = texture_owner.get_or_null(p_from_texture); @@ -1658,133 +1455,34 @@ Error RenderingDevice::texture_copy(RID p_from_texture, RID p_to_texture, const ERR_FAIL_COND_V_MSG(src_tex->read_aspect_flags != dst_tex->read_aspect_flags, ERR_INVALID_PARAMETER, "Source and destination texture must be of the same type (color or depth)."); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; - - // PRE Copy the image. - - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - { // Source. - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.prev_layout = src_tex->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.subresources.aspect = src_tex->barrier_aspect_flags; - tb.subresources.base_mipmap = p_src_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = p_src_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } - { // Dest. - RDD::TextureBarrier tb; - tb.texture = dst_tex->driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.prev_layout = dst_tex->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - tb.subresources.aspect = dst_tex->read_aspect_flags; - tb.subresources.base_mipmap = p_dst_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = p_dst_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } - } - - // COPY. - - { - RDD::TextureCopyRegion copy_region; - copy_region.src_subresources.aspect = src_tex->read_aspect_flags; - copy_region.src_subresources.mipmap = p_src_mipmap; - copy_region.src_subresources.base_layer = p_src_layer; - copy_region.src_subresources.layer_count = 1; - copy_region.src_offset = p_from; + RDD::TextureCopyRegion copy_region; + copy_region.src_subresources.aspect = src_tex->read_aspect_flags; + copy_region.src_subresources.mipmap = p_src_mipmap; + copy_region.src_subresources.base_layer = p_src_layer; + copy_region.src_subresources.layer_count = 1; + copy_region.src_offset = p_from; - copy_region.dst_subresources.aspect = dst_tex->read_aspect_flags; - copy_region.dst_subresources.mipmap = p_dst_mipmap; - copy_region.dst_subresources.base_layer = p_dst_layer; - copy_region.dst_subresources.layer_count = 1; - copy_region.dst_offset = p_to; + copy_region.dst_subresources.aspect = dst_tex->read_aspect_flags; + copy_region.dst_subresources.mipmap = p_dst_mipmap; + copy_region.dst_subresources.base_layer = p_dst_layer; + copy_region.dst_subresources.layer_count = 1; + copy_region.dst_offset = p_to; - copy_region.size = p_size; + copy_region.size = p_size; - driver->command_copy_texture(command_buffer, src_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, copy_region); + // The textures must be mutable to be used in the copy operation. + bool src_made_mutable = _texture_make_mutable(src_tex, p_from_texture); + bool dst_made_mutable = _texture_make_mutable(dst_tex, p_to_texture); + if (src_made_mutable || dst_made_mutable) { + draw_graph.add_synchronization(); } - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - // RESTORE LAYOUT for SRC and DST. - - BitField<RDD::PipelineStageBits> stages; - BitField<RDD::BarrierAccessBits> access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - if (stages.is_empty()) { - stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - - { // Restore src. - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.dst_access = access; - tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.next_layout = src_tex->layout; - tb.subresources.aspect = src_tex->barrier_aspect_flags; - tb.subresources.base_mipmap = p_src_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = p_src_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); - } - - { // Make dst readable. - - RDD::TextureBarrier tb; - tb.texture = dst_tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.dst_access = access; - tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - tb.next_layout = dst_tex->layout; - tb.subresources.aspect = dst_tex->read_aspect_flags; - tb.subresources.base_mipmap = p_dst_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = p_dst_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); - } - - if (dst_tex->used_in_frame != frames_drawn) { - dst_tex->used_in_raster = false; - dst_tex->used_in_compute = false; - dst_tex->used_in_frame = frames_drawn; - } - dst_tex->used_in_transfer = true; - } + draw_graph.add_texture_copy(src_tex->driver_id, src_tex->draw_tracker, dst_tex->driver_id, dst_tex->draw_tracker, copy_region); return OK; } -Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_texture) { _THREAD_SAFE_METHOD_ Texture *src_tex = texture_owner.get_or_null(p_from_texture); @@ -1815,108 +1513,19 @@ Error RenderingDevice::texture_resolve_multisample(RID p_from_texture, RID p_to_ ERR_FAIL_COND_V_MSG(src_tex->read_aspect_flags != dst_tex->read_aspect_flags, ERR_INVALID_PARAMETER, "Source and destination texture must be of the same type (color or depth)."); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; - - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - // PRE Copy the image. - - { // Source. - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.prev_layout = src_tex->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.subresources.aspect = src_tex->barrier_aspect_flags; - tb.subresources.base_mipmap = src_tex->base_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = src_tex->base_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } - { // Dest. - RDD::TextureBarrier tb; - tb.texture = dst_tex->driver_id; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.prev_layout = dst_tex->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - tb.subresources.aspect = dst_tex->barrier_aspect_flags; - tb.subresources.base_mipmap = dst_tex->base_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = dst_tex->base_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } + // The textures must be mutable to be used in the resolve operation. + bool src_made_mutable = _texture_make_mutable(src_tex, p_from_texture); + bool dst_made_mutable = _texture_make_mutable(dst_tex, p_to_texture); + if (src_made_mutable || dst_made_mutable) { + draw_graph.add_synchronization(); } - // RESOLVE. - driver->command_resolve_texture(command_buffer, src_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, src_tex->base_layer, src_tex->base_mipmap, dst_tex->driver_id, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, dst_tex->base_layer, dst_tex->base_mipmap); - - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - // RESTORE LAYOUT for SRC and DST. - - BitField<RDD::PipelineStageBits> stages; - BitField<RDD::BarrierAccessBits> access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - if (stages.is_empty()) { - stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - - { // Restore src. - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; - tb.dst_access = access; - tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; - tb.next_layout = src_tex->layout; - tb.subresources.aspect = src_tex->barrier_aspect_flags; - tb.subresources.base_mipmap = src_tex->base_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = src_tex->base_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT, stages, {}, {}, tb); - } - - { // Make dst readable. - - RDD::TextureBarrier tb; - tb.texture = dst_tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.dst_access = access; - tb.prev_layout = RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - tb.next_layout = dst_tex->layout; - tb.subresources.aspect = RDD::TEXTURE_ASPECT_COLOR_BIT; - tb.subresources.base_mipmap = dst_tex->base_mipmap; - tb.subresources.mipmap_count = 1; - tb.subresources.base_layer = dst_tex->base_layer; - tb.subresources.layer_count = 1; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); - } - } + draw_graph.add_texture_resolve(src_tex->driver_id, src_tex->draw_tracker, dst_tex->driver_id, dst_tex->draw_tracker, src_tex->base_layer, src_tex->base_mipmap, dst_tex->base_layer, dst_tex->base_mipmap); return OK; } -Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier) { +Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers) { _THREAD_SAFE_METHOD_ Texture *src_tex = texture_owner.get_or_null(p_texture); @@ -1939,33 +1548,6 @@ Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32 ERR_FAIL_COND_V(p_base_mipmap + p_mipmaps > src_tex->mipmaps, ERR_INVALID_PARAMETER); ERR_FAIL_COND_V(p_base_layer + p_layers > src_layer_count, ERR_INVALID_PARAMETER); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; - - RDD::TextureLayout clear_layout = (src_tex->layout == RDD::TEXTURE_LAYOUT_GENERAL) ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; - - // NOTE: Perhaps the valid stages/accesses for a given owner should be a property of the owner. (Here and places like _get_buffer_from_owner.) - const BitField<RDD::PipelineStageBits> valid_texture_stages = RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT; - constexpr BitField<RDD::BarrierAccessBits> read_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT; - constexpr BitField<RDD::BarrierAccessBits> read_write_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; - const BitField<RDD::BarrierAccessBits> valid_texture_access = (src_tex->usage_flags & TEXTURE_USAGE_STORAGE_BIT) ? read_write_access : read_access; - - // Barrier from previous access with optional layout change (see clear_layout logic above). - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.src_access = valid_texture_access; - tb.dst_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.prev_layout = src_tex->layout; - tb.next_layout = clear_layout; - tb.subresources.aspect = src_tex->read_aspect_flags; - tb.subresources.base_mipmap = src_tex->base_mipmap + p_base_mipmap; - tb.subresources.mipmap_count = p_mipmaps; - tb.subresources.base_layer = src_tex->base_layer + p_base_layer; - tb.subresources.layer_count = p_layers; - - driver->command_pipeline_barrier(command_buffer, valid_texture_stages, RDD::PIPELINE_STAGE_TRANSFER_BIT, {}, {}, tb); - } - RDD::TextureSubresourceRange range; range.aspect = src_tex->read_aspect_flags; range.base_mipmap = src_tex->base_mipmap + p_base_mipmap; @@ -1973,55 +1555,13 @@ Error RenderingDevice::texture_clear(RID p_texture, const Color &p_color, uint32 range.base_layer = src_tex->base_layer + p_base_layer; range.layer_count = p_layers; - driver->command_clear_color_texture(command_buffer, src_tex->driver_id, clear_layout, p_color, range); - - // Barrier to post clear accesses (changing back the layout if needed). - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - BitField<RDD::PipelineStageBits> stages; - BitField<RDD::BarrierAccessBits> access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); - } - - if (stages.is_empty()) { - stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - - RDD::TextureBarrier tb; - tb.texture = src_tex->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - tb.dst_access = access; - tb.prev_layout = clear_layout; - tb.next_layout = src_tex->layout; - tb.subresources.aspect = src_tex->read_aspect_flags; - tb.subresources.base_mipmap = src_tex->base_mipmap + p_base_mipmap; - tb.subresources.mipmap_count = p_mipmaps; - tb.subresources.base_layer = src_tex->base_layer + p_base_layer; - tb.subresources.layer_count = p_layers; - - driver->command_pipeline_barrier(command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, stages, {}, {}, tb); - - if (src_tex->used_in_frame != frames_drawn) { - src_tex->used_in_raster = false; - src_tex->used_in_compute = false; - src_tex->used_in_frame = frames_drawn; - } - src_tex->used_in_transfer = true; + if (_texture_make_mutable(src_tex, p_texture)) { + // The texture must be mutable to be used as a clear destination. + draw_graph.add_synchronization(); } + draw_graph.add_texture_clear(src_tex->driver_id, src_tex->draw_tracker, p_color, range); + return OK; } @@ -2040,6 +1580,30 @@ bool RenderingDevice::texture_is_format_supported_for_usage(DataFormat p_format, /**** FRAMEBUFFER ****/ /*********************/ +static RDD::AttachmentLoadOp initial_action_to_load_op(RenderingDevice::InitialAction p_action) { + switch (p_action) { + case RenderingDevice::INITIAL_ACTION_LOAD: + return RDD::ATTACHMENT_LOAD_OP_LOAD; + case RenderingDevice::INITIAL_ACTION_CLEAR: + return RDD::ATTACHMENT_LOAD_OP_CLEAR; + case RenderingDevice::INITIAL_ACTION_DISCARD: + return RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + default: + ERR_FAIL_V_MSG(RDD::ATTACHMENT_LOAD_OP_DONT_CARE, "Invalid initial action value (" + itos(p_action) + ")"); + } +} + +static RDD::AttachmentStoreOp final_action_to_store_op(RenderingDevice::FinalAction p_action) { + switch (p_action) { + case RenderingDevice::FINAL_ACTION_STORE: + return RDD::ATTACHMENT_STORE_OP_STORE; + case RenderingDevice::FINAL_ACTION_DISCARD: + return RDD::ATTACHMENT_STORE_OP_DONT_CARE; + default: + ERR_FAIL_V_MSG(RDD::ATTACHMENT_STORE_OP_DONT_CARE, "Invalid final action value (" + itos(p_action) + ")"); + } +} + RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFormat> &p_attachments, const Vector<FramebufferPass> &p_passes, InitialAction p_initial_action, FinalAction p_final_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, uint32_t p_view_count, Vector<TextureSamples> *r_samples) { // NOTE: // Before the refactor to RenderingDevice-RenderingDeviceDriver, there was commented out code to @@ -2077,209 +1641,40 @@ RDD::RenderPassID RenderingDevice::_render_pass_create(const Vector<AttachmentFo description.format = p_attachments[i].format; description.samples = p_attachments[i].samples; - bool is_sampled = (p_attachments[i].usage_flags & TEXTURE_USAGE_SAMPLING_BIT); - bool is_storage = (p_attachments[i].usage_flags & TEXTURE_USAGE_STORAGE_BIT); - bool is_depth = (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); - // We can setup a framebuffer where we write to our VRS texture to set it up. // We make the assumption here that if our texture is actually used as our VRS attachment. // It is used as such for each subpass. This is fairly certain seeing the restrictions on subpasses. bool is_vrs = (p_attachments[i].usage_flags & TEXTURE_USAGE_VRS_ATTACHMENT_BIT) && i == p_passes[0].vrs_attachment; if (is_vrs) { - // For VRS we only read, there is no writing to this texture. description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - description.initial_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - } else { - // For each UNDEFINED, assume the prior use was a *read*, as we'd be discarding the output of a write. - // Also, each UNDEFINED will do an immediate layout transition (write), s.t. we must ensure execution synchronization vs - // the read. If this is a performance issue, one could track the actual last accessor of each resource, adding only that - // stage. - - switch (is_depth ? p_initial_depth_action : p_initial_action) { - case INITIAL_ACTION_CLEAR_REGION: - case INITIAL_ACTION_CLEAR: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; - description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; - description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_CLEAR; - } else { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - case INITIAL_ACTION_KEEP: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - } else { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - case INITIAL_ACTION_DROP: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - } else { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - case INITIAL_ACTION_CLEAR_REGION_CONTINUE: - case INITIAL_ACTION_CONTINUE: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - description.initial_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - description.initial_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; - } else { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - default: { - ERR_FAIL_V(RDD::RenderPassID()); // Should never reach here. - } - } - } - - bool used_last = false; - - { - int last_pass = p_passes.size() - 1; - - if (is_depth) { - // Likely missing depth resolve? - if (p_passes[last_pass].depth_attachment == i) { - used_last = true; - } - } else if (is_vrs) { - if (p_passes[last_pass].vrs_attachment == i) { - used_last = true; - } - } else { - if (p_passes[last_pass].resolve_attachments.size()) { - // If using resolve attachments, check resolve attachments. - for (int j = 0; j < p_passes[last_pass].resolve_attachments.size(); j++) { - if (p_passes[last_pass].resolve_attachments[j] == i) { - used_last = true; - break; - } - } - } - if (!used_last) { - for (int j = 0; j < p_passes[last_pass].color_attachments.size(); j++) { - if (p_passes[last_pass].color_attachments[j] == i) { - used_last = true; - break; - } - } - } - } - - if (!used_last) { - for (int j = 0; j < p_passes[last_pass].preserve_attachments.size(); j++) { - if (p_passes[last_pass].preserve_attachments[j] == i) { - used_last = true; - break; - } - } - } - } - - FinalAction final_action = p_final_action; - FinalAction final_depth_action = p_final_depth_action; - - if (!used_last) { - if (is_depth) { - final_depth_action = FINAL_ACTION_DISCARD; - - } else { - final_action = FINAL_ACTION_DISCARD; - } - } - - if (is_vrs) { - // We don't change our VRS texture during this process. - description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_LOAD; description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; description.final_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; } else { - switch (is_depth ? final_depth_action : final_action) { - case FINAL_ACTION_READ: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } else { - description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; - description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - // TODO: What does this mean about the next usage (and thus appropriate dependency masks. - } - } break; - case FINAL_ACTION_DISCARD: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL); - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = is_sampled ? RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL : (is_storage ? RDD::TEXTURE_LAYOUT_GENERAL : RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL); - } else { - description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - } break; - case FINAL_ACTION_CONTINUE: { - if ((p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; - } else if ((p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - description.store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_STORE; - description.final_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; - } else { - description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; - description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; // Don't care what is there. - } - - } break; - default: { - ERR_FAIL_V(RDD::RenderPassID()); // Should never reach here. - } + if (p_attachments[i].usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + description.load_op = initial_action_to_load_op(p_initial_action); + description.store_op = final_action_to_store_op(p_final_action); + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + description.final_layout = RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + } else if (p_attachments[i].usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { + description.load_op = initial_action_to_load_op(p_initial_depth_action); + description.store_op = final_action_to_store_op(p_final_depth_action); + description.stencil_load_op = initial_action_to_load_op(p_initial_depth_action); + description.stencil_store_op = final_action_to_store_op(p_final_depth_action); + description.initial_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + description.final_layout = RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + } else { + description.load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.stencil_load_op = RDD::ATTACHMENT_LOAD_OP_DONT_CARE; + description.stencil_store_op = RDD::ATTACHMENT_STORE_OP_DONT_CARE; + description.initial_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; + description.final_layout = RDD::TEXTURE_LAYOUT_UNDEFINED; } } @@ -2465,7 +1860,7 @@ RenderingDevice::FramebufferFormatID RenderingDevice::framebuffer_format_create_ } Vector<TextureSamples> samples; - RDD::RenderPassID render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, INITIAL_ACTION_CLEAR, FINAL_ACTION_READ, p_view_count, &samples); // Actions don't matter for this use case. + RDD::RenderPassID render_pass = _render_pass_create(p_attachments, p_passes, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, INITIAL_ACTION_CLEAR, FINAL_ACTION_STORE, p_view_count, &samples); // Actions don't matter for this use case. if (!render_pass) { // Was likely invalid. return INVALID_ID; @@ -2701,18 +2096,14 @@ RID RenderingDevice::vertex_buffer_create(uint32_t p_size_bytes, const Vector<ui buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); + // Vertex buffers are assumed to be immutable unless they don't have initial data or they've been marked for storage explicitly. + if (p_data.is_empty() || p_use_as_storage) { + buffer.draw_tracker = RDG::resource_tracker_create(); + buffer.draw_tracker->buffer_driver_id = buffer.driver_id; + } + if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = buffer.driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; - bb.size = data_size; - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT, {}, bb, {}); - } + _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size()); } buffer_memory += buffer.size; @@ -2809,6 +2200,12 @@ RID RenderingDevice::vertex_array_create(uint32_t p_vertex_count, VertexFormatID } vertex_array.buffers.push_back(buffer->driver_id); + + if (buffer->draw_tracker != nullptr) { + vertex_array.draw_trackers.push_back(buffer->draw_tracker); + } else { + vertex_array.untracked_buffers.insert(p_src_buffers[i]); + } } RID id = vertex_array_owner.make_rid(vertex_array); @@ -2863,18 +2260,14 @@ RID RenderingDevice::index_buffer_create(uint32_t p_index_count, IndexBufferForm index_buffer.driver_id = driver->buffer_create(index_buffer.size, index_buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!index_buffer.driver_id, RID()); + // Index buffers are assumed to be immutable unless they don't have initial data. + if (p_data.is_empty()) { + index_buffer.draw_tracker = RDG::resource_tracker_create(); + index_buffer.draw_tracker->buffer_driver_id = index_buffer.driver_id; + } + if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&index_buffer, 0, r, data_size); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = index_buffer.driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = RDD::BARRIER_ACCESS_INDEX_READ_BIT; - bb.size = data_size; - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT, {}, bb, {}); - } + _buffer_update(&index_buffer, RID(), 0, p_data.ptr(), p_data.size()); } buffer_memory += index_buffer.size; @@ -2899,6 +2292,7 @@ RID RenderingDevice::index_array_create(RID p_index_buffer, uint32_t p_index_off IndexArray index_array; index_array.max_index = index_buffer->max_index; index_array.driver_id = index_buffer->driver_id; + index_array.draw_tracker = index_buffer->draw_tracker; index_array.offset = p_index_offset; index_array.indices = p_index_count; index_array.format = index_buffer->format; @@ -2991,6 +2385,29 @@ RID RenderingDevice::shader_create_from_bytecode(const Vector<uint8_t> &p_shader shader->set_formats.push_back(format); } + for (ShaderStage stage : shader_desc.stages) { + switch (stage) { + case SHADER_STAGE_VERTEX: + shader->stage_bits.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); + break; + case SHADER_STAGE_FRAGMENT: + shader->stage_bits.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); + break; + case SHADER_STAGE_TESSELATION_CONTROL: + shader->stage_bits.set_flag(RDD::PIPELINE_STAGE_TESSELLATION_CONTROL_SHADER_BIT); + break; + case SHADER_STAGE_TESSELATION_EVALUATION: + shader->stage_bits.set_flag(RDD::PIPELINE_STAGE_TESSELLATION_EVALUATION_SHADER_BIT); + break; + case SHADER_STAGE_COMPUTE: + shader->stage_bits.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); + break; + default: + DEV_ASSERT(false && "Unknown shader stage."); + break; + } + } + #ifdef DEV_ENABLED set_resource_name(id, "RID:" + itos(id.get_id())); #endif @@ -3025,18 +2442,14 @@ RID RenderingDevice::uniform_buffer_create(uint32_t p_size_bytes, const Vector<u buffer.driver_id = driver->buffer_create(buffer.size, buffer.usage, RDD::MEMORY_ALLOCATION_TYPE_GPU); ERR_FAIL_COND_V(!buffer.driver_id, RID()); + // Uniform buffers are assumed to be immutable unless they don't have initial data. + if (p_data.is_empty()) { + buffer.draw_tracker = RDG::resource_tracker_create(); + buffer.draw_tracker->buffer_driver_id = buffer.driver_id; + } + if (p_data.size()) { - uint64_t data_size = p_data.size(); - const uint8_t *r = p_data.ptr(); - _buffer_update(&buffer, 0, r, data_size); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::BufferBarrier bb; - bb.buffer = buffer.driver_id; - bb.src_access = RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; - bb.dst_access = RDD::BARRIER_ACCESS_UNIFORM_READ_BIT; - bb.size = data_size; - driver->command_pipeline_barrier(frames[frame].setup_command_buffer, RDD::PIPELINE_STAGE_TRANSFER_BIT, RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT | RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, bb, {}); - } + _buffer_update(&buffer, RID(), 0, p_data.ptr(), p_data.size()); } buffer_memory += buffer.size; @@ -3073,8 +2486,9 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p // Used for verification to make sure a uniform set does not use a framebuffer bound texture. LocalVector<UniformSet::AttachableTexture> attachable_textures; - Vector<Texture *> mutable_sampled_textures; - Vector<Texture *> mutable_storage_textures; + Vector<RDG::ResourceTracker *> draw_trackers; + Vector<RDG::ResourceUsage> draw_trackers_usage; + HashMap<RID, RDG::ResourceUsage> untracked_usage; for (uint32_t i = 0; i < set_uniform_count; i++) { const ShaderUniform &set_uniform = set_uniforms[i]; @@ -3126,7 +2540,8 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler_driver_id, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - Texture *texture = texture_owner.get_or_null(uniform.get_id(j + 1)); + RID texture_id = uniform.get_id(j + 1); + Texture *texture = texture_owner.get_or_null(texture_id); ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -3139,8 +2554,11 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p attachable_textures.push_back(attachable_texture); } - if ((texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { - mutable_sampled_textures.push_back(texture); + if (texture->draw_tracker != nullptr) { + draw_trackers.push_back(texture->draw_tracker); + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_TEXTURE_SAMPLE); + } else { + untracked_usage[texture_id] = RDG::RESOURCE_USAGE_TEXTURE_SAMPLE; } DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); @@ -3159,7 +2577,8 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p } for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + RID texture_id = uniform.get_id(j); + Texture *texture = texture_owner.get_or_null(texture_id); ERR_FAIL_NULL_V_MSG(texture, RID(), "Texture (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT), RID(), @@ -3172,8 +2591,11 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p attachable_textures.push_back(attachable_texture); } - if ((texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT)) { - mutable_sampled_textures.push_back(texture); + if (texture->draw_tracker != nullptr) { + draw_trackers.push_back(texture->draw_tracker); + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_TEXTURE_SAMPLE); + } else { + untracked_usage[texture_id] = RDG::RESOURCE_USAGE_TEXTURE_SAMPLE; } DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); @@ -3191,7 +2613,8 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p } for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + RID texture_id = uniform.get_id(j); + Texture *texture = texture_owner.get_or_null(texture_id); ERR_FAIL_NULL_V_MSG(texture, RID(), "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -3199,8 +2622,19 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p ERR_FAIL_COND_V_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), RID(), "Image (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") needs the TEXTURE_USAGE_STORAGE_BIT usage flag set in order to be used as uniform."); - if ((texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT)) { - mutable_storage_textures.push_back(texture); + if (_texture_make_mutable(texture, texture_id)) { + // The texture must be mutable as a layout transition will be required. + draw_graph.add_synchronization(); + } + + if (texture->draw_tracker != nullptr) { + draw_trackers.push_back(texture->draw_tracker); + + if (set_uniform.writable) { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE); + } else { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_STORAGE_IMAGE_READ); + } } DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); @@ -3218,9 +2652,27 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p } for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Buffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j)); + RID buffer_id = uniform.get_id(j); + Buffer *buffer = texture_buffer_owner.get_or_null(buffer_id); ERR_FAIL_NULL_V_MSG(buffer, RID(), "Texture Buffer (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture buffer."); + if (set_uniform.writable && _buffer_make_mutable(buffer, buffer_id)) { + // The buffer must be mutable if it's used for writing. + draw_graph.add_synchronization(); + } + + if (buffer->draw_tracker != nullptr) { + draw_trackers.push_back(buffer->draw_tracker); + + if (set_uniform.writable) { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE); + } else { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ); + } + } else { + untracked_usage[buffer_id] = RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ; + } + driver_uniform.ids.push_back(buffer->driver_id); } } break; @@ -3237,9 +2689,17 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p RDD::SamplerID *sampler_driver_id = sampler_owner.get_or_null(uniform.get_id(j + 0)); ERR_FAIL_COND_V_MSG(!sampler_driver_id, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid sampler."); - Buffer *buffer = texture_buffer_owner.get_or_null(uniform.get_id(j + 1)); + RID buffer_id = uniform.get_id(j + 1); + Buffer *buffer = texture_buffer_owner.get_or_null(buffer_id); ERR_FAIL_NULL_V_MSG(buffer, RID(), "SamplerBuffer (binding: " + itos(uniform.binding) + ", index " + itos(j + 1) + ") is not a valid texture buffer."); + if (buffer->draw_tracker != nullptr) { + draw_trackers.push_back(buffer->draw_tracker); + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ); + } else { + untracked_usage[buffer_id] = RDG::RESOURCE_USAGE_TEXTURE_BUFFER_READ; + } + driver_uniform.ids.push_back(*sampler_driver_id); driver_uniform.ids.push_back(buffer->driver_id); } @@ -3251,12 +2711,20 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p ERR_FAIL_COND_V_MSG(uniform.get_id_count() != 1, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") must provide one ID (" + itos(uniform.get_id_count()) + " provided)."); - Buffer *buffer = uniform_buffer_owner.get_or_null(uniform.get_id(0)); + RID buffer_id = uniform.get_id(0); + Buffer *buffer = uniform_buffer_owner.get_or_null(buffer_id); ERR_FAIL_NULL_V_MSG(buffer, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") is invalid."); ERR_FAIL_COND_V_MSG(buffer->size < (uint32_t)set_uniform.length, RID(), "Uniform buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " is smaller than size of shader uniform: (" + itos(set_uniform.length) + ")."); + if (buffer->draw_tracker != nullptr) { + draw_trackers.push_back(buffer->draw_tracker); + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_UNIFORM_BUFFER_READ); + } else { + untracked_usage[buffer_id] = RDG::RESOURCE_USAGE_UNIFORM_BUFFER_READ; + } + driver_uniform.ids.push_back(buffer->driver_id); } break; case UNIFORM_TYPE_STORAGE_BUFFER: { @@ -3265,10 +2733,11 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p Buffer *buffer = nullptr; - if (storage_buffer_owner.owns(uniform.get_id(0))) { - buffer = storage_buffer_owner.get_or_null(uniform.get_id(0)); - } else if (vertex_buffer_owner.owns(uniform.get_id(0))) { - buffer = vertex_buffer_owner.get_or_null(uniform.get_id(0)); + RID buffer_id = uniform.get_id(0); + if (storage_buffer_owner.owns(buffer_id)) { + buffer = storage_buffer_owner.get_or_null(buffer_id); + } else if (vertex_buffer_owner.owns(buffer_id)) { + buffer = vertex_buffer_owner.get_or_null(buffer_id); ERR_FAIL_COND_V_MSG(!(buffer->usage.has_flag(RDD::BUFFER_USAGE_STORAGE_BIT)), RID(), "Vertex buffer supplied (binding: " + itos(uniform.binding) + ") was not created with storage flag."); } @@ -3278,6 +2747,23 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p ERR_FAIL_COND_V_MSG(set_uniform.length > 0 && buffer->size != (uint32_t)set_uniform.length, RID(), "Storage buffer supplied (binding: " + itos(uniform.binding) + ") size (" + itos(buffer->size) + " does not match size of shader uniform: (" + itos(set_uniform.length) + ")."); + if (set_uniform.writable && _buffer_make_mutable(buffer, buffer_id)) { + // The buffer must be mutable if it's used for writing. + draw_graph.add_synchronization(); + } + + if (buffer->draw_tracker != nullptr) { + draw_trackers.push_back(buffer->draw_tracker); + + if (set_uniform.writable) { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE); + } else { + draw_trackers_usage.push_back(RDG::RESOURCE_USAGE_STORAGE_BUFFER_READ); + } + } else { + untracked_usage[buffer_id] = RDG::RESOURCE_USAGE_STORAGE_BUFFER_READ; + } + driver_uniform.ids.push_back(buffer->driver_id); } break; case UNIFORM_TYPE_INPUT_ATTACHMENT: { @@ -3292,7 +2778,8 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p } for (uint32_t j = 0; j < uniform.get_id_count(); j++) { - Texture *texture = texture_owner.get_or_null(uniform.get_id(j)); + RID texture_id = uniform.get_id(j); + Texture *texture = texture_owner.get_or_null(texture_id); ERR_FAIL_NULL_V_MSG(texture, RID(), "InputAttachment (binding: " + itos(uniform.binding) + ", index " + itos(j) + ") is not a valid texture."); @@ -3302,6 +2789,17 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p DEV_ASSERT(!texture->owner.is_valid() || texture_owner.get_or_null(texture->owner)); + if (_texture_make_mutable(texture, texture_id)) { + // The texture must be mutable as a layout transition will be required. + draw_graph.add_synchronization(); + } + + if (texture->draw_tracker != nullptr) { + bool depth_stencil_read = (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT); + draw_trackers.push_back(texture->draw_tracker); + draw_trackers_usage.push_back(depth_stencil_read ? RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ : RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ); + } + driver_uniform.ids.push_back(texture->driver_id); } } break; @@ -3317,8 +2815,9 @@ RID RenderingDevice::uniform_set_create(const Vector<Uniform> &p_uniforms, RID p uniform_set.driver_id = driver_uniform_set; uniform_set.format = shader->set_formats[p_shader_set]; uniform_set.attachable_textures = attachable_textures; - uniform_set.mutable_sampled_textures = mutable_sampled_textures; - uniform_set.mutable_storage_textures = mutable_storage_textures; + uniform_set.draw_trackers = draw_trackers; + uniform_set.draw_trackers_usage = draw_trackers_usage; + uniform_set.untracked_usage = untracked_usage; uniform_set.shader_set = p_shader_set; uniform_set.shader_id = p_shader; @@ -3486,6 +2985,7 @@ RID RenderingDevice::render_pipeline_create(RID p_shader, FramebufferFormatID p_ pipeline.shader_layout_hash = shader->layout_hash; pipeline.set_formats = shader->set_formats; pipeline.push_constant_size = shader->push_constant_size; + pipeline.stage_bits = shader->stage_bits; #ifdef DEBUG_ENABLED pipeline.validation.dynamic_state = p_dynamic_state_flags; @@ -3623,15 +3123,13 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; - if (!context->window_is_valid_swapchain(p_screen)) { return INVALID_ID; } - Size2i size = Size2i(context->window_get_width(p_screen), context->window_get_height(p_screen)); + Rect2i viewport = Rect2i(0, 0, context->window_get_width(p_screen), context->window_get_height(p_screen)); - _draw_list_allocate(Rect2i(Vector2i(), size), 0, 0); + _draw_list_allocate(viewport, 0); #ifdef DEBUG_ENABLED draw_list_framebuffer_format = screen_get_framebuffer_format(); #endif @@ -3639,16 +3137,11 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin_for_screen(DisplayS RDD::RenderPassClearValue clear_value; clear_value.color = p_clear_color; - driver->command_begin_render_pass( - command_buffer, - context->window_get_render_pass(p_screen), - context->window_get_framebuffer(p_screen), - RDD::COMMAND_BUFFER_TYPE_PRIMARY, - Rect2i(0, 0, size.width, size.height), - VectorView(&clear_value, 1)); - driver->command_render_set_viewport(command_buffer, Rect2i(Point2i(), size)); - driver->command_render_set_scissor(command_buffer, Rect2i(Point2i(), size)); + draw_graph.add_draw_list_begin(context->window_get_render_pass(p_screen), context->window_get_framebuffer(p_screen), viewport, clear_value, true, false); + + _draw_list_set_viewport(viewport); + _draw_list_set_scissor(viewport); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } @@ -3694,8 +3187,12 @@ Error RenderingDevice::_draw_list_setup_framebuffer(Framebuffer *p_framebuffer, return OK; } -Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, RDD::CommandBufferID p_command_buffer, RDD::CommandBufferType p_cmd_buffer_mode, const Vector<RID> &p_storage_textures, bool p_constrained_to_region) { +Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass) { LocalVector<RDD::RenderPassClearValue> clear_values; + LocalVector<RDG::ResourceTracker *> resource_trackers; + LocalVector<RDG::ResourceUsage> resource_usages; + bool uses_color = false; + bool uses_depth = false; clear_values.resize(p_framebuffer->texture_ids.size()); int clear_values_count = 0; { @@ -3709,69 +3206,33 @@ Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, continue; } - if (color_index < p_clear_colors.size() && texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { - ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. - clear_value.color = p_clear_colors[color_index]; - color_index++; + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { + if (color_index < p_clear_colors.size()) { + ERR_FAIL_INDEX_V(color_index, p_clear_colors.size(), ERR_BUG); // A bug. + clear_value.color = p_clear_colors[color_index]; + color_index++; + } + + resource_trackers.push_back(texture->draw_tracker); + resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE); + uses_color = true; } else if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { clear_value.depth = p_clear_depth; clear_value.stencil = p_clear_stencil; + resource_trackers.push_back(texture->draw_tracker); + resource_usages.push_back(RDG::RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE); + uses_depth = true; } clear_values[clear_values_count++] = clear_value; } } - for (int i = 0; i < p_storage_textures.size(); i++) { - Texture *texture = texture_owner.get_or_null(p_storage_textures[i]); - if (!texture) { - continue; - } - ERR_CONTINUE_MSG(!(texture->usage_flags & TEXTURE_USAGE_STORAGE_BIT), "Supplied storage texture " + itos(i) + " for draw list is not set to be used for storage."); - - if (texture->usage_flags & TEXTURE_USAGE_SAMPLING_BIT) { - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - // Must change layout to general. - RDD::TextureBarrier tb; - tb.texture = texture->driver_id; - tb.src_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - tb.prev_layout = texture->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_GENERAL; - tb.subresources.aspect = texture->read_aspect_flags; - tb.subresources.base_mipmap = texture->base_mipmap; - tb.subresources.mipmap_count = texture->mipmaps; - tb.subresources.base_layer = texture->base_layer; - tb.subresources.layer_count = texture->layers; - - driver->command_pipeline_barrier(p_command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT | RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT, {}, {}, tb); - - texture->layout = RDD::TEXTURE_LAYOUT_GENERAL; - } - - draw_list_storage_textures.push_back(p_storage_textures[i]); - } - } - - Rect2i region; - if (p_constrained_to_region) { - region = Rect2i(p_viewport_offset, p_viewport_size); - } else { - region = Rect2i(Point2i(), p_framebuffer->size); - } - - driver->command_begin_render_pass( - p_command_buffer, - p_render_pass, - p_framebuffer_driver_id, - p_cmd_buffer_mode, - region, - clear_values); + draw_graph.add_draw_list_begin(p_render_pass, p_framebuffer_driver_id, Rect2i(p_viewport_offset, p_viewport_size), clear_values, uses_color, uses_depth); + draw_graph.add_draw_list_usages(resource_trackers, resource_usages); // Mark textures as bound. draw_list_bound_textures.clear(); - draw_list_unbind_color_textures = p_final_color_action != FINAL_ACTION_CONTINUE; - draw_list_unbind_depth_textures = p_final_depth_action != FINAL_ACTION_CONTINUE; for (int i = 0; i < p_framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(p_framebuffer->texture_ids[i]); @@ -3785,6 +3246,14 @@ Error RenderingDevice::_draw_list_render_pass_begin(Framebuffer *p_framebuffer, return OK; } +void RenderingDevice::_draw_list_set_viewport(Rect2i p_rect) { + draw_graph.add_draw_list_set_viewport(p_rect); +} + +void RenderingDevice::_draw_list_set_scissor(Rect2i p_rect) { + draw_graph.add_draw_list_set_scissor(p_rect); +} + void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil) { LocalVector<RDD::AttachmentClear> clear_attachments; int color_index = 0; @@ -3818,24 +3287,19 @@ void RenderingDevice::_draw_list_insert_clear_region(DrawList *p_draw_list, Fram } Rect2i rect = Rect2i(p_viewport_offset, p_viewport_size); - - driver->command_render_clear_attachments(p_draw_list->command_buffer, clear_attachments, rect); + draw_graph.add_draw_list_clear_attachments(clear_attachments, rect); } -RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { +RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region) { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_V_MSG(draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, INVALID_ID, "Only one draw/compute list can be active at the same time."); Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); ERR_FAIL_NULL_V(framebuffer, INVALID_ID); Point2i viewport_offset; Point2i viewport_size = framebuffer->size; - bool constrained_to_region = false; - bool needs_clear_color = false; - bool needs_clear_depth = false; if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. Rect2i viewport(viewport_offset, viewport_size); @@ -3848,34 +3312,9 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, viewport_offset = regioni.position; viewport_size = regioni.size; - - // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears - // and we constrain the render area to the region. - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - constrained_to_region = true; - p_initial_color_action = INITIAL_ACTION_CLEAR; - p_initial_depth_action = INITIAL_ACTION_CLEAR; - } else { - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; - } - } } - if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. + if (p_initial_color_action == INITIAL_ACTION_CLEAR) { // Check clear values. int color_count = 0; for (int i = 0; i < framebuffer->texture_ids.size(); i++) { Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); @@ -3897,8 +3336,7 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); ERR_FAIL_COND_V(err != OK, INVALID_ID); - RDD::CommandBufferID command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY, p_storage_textures, constrained_to_region); + err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass); if (err != OK) { return INVALID_ID; @@ -3907,135 +3345,23 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_begin(RID p_framebuffer, draw_list_render_pass = render_pass; draw_list_vkframebuffer = fb_driver_id; - _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0, 0); + _draw_list_allocate(Rect2i(viewport_offset, viewport_size), 0); #ifdef DEBUG_ENABLED draw_list_framebuffer_format = framebuffer->format_id; #endif draw_list_current_subpass = 0; - if (needs_clear_color || needs_clear_depth) { - DEV_ASSERT(!constrained_to_region); - _draw_list_insert_clear_region(draw_list, framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } - - driver->command_render_set_viewport(command_buffer, Rect2i(viewport_offset, viewport_size)); - driver->command_render_set_scissor(command_buffer, Rect2i(viewport_offset, viewport_size)); + _draw_list_set_viewport(Rect2i(viewport_offset, viewport_size)); + _draw_list_set_scissor(Rect2i(viewport_offset, viewport_size)); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } +#ifndef DISABLE_DEPRECATED Error RenderingDevice::draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const Vector<RID> &p_storage_textures) { - _THREAD_SAFE_METHOD_ - - ERR_FAIL_COND_V_MSG(draw_list != nullptr, ERR_BUSY, "Only one draw list can be active at the same time."); - ERR_FAIL_COND_V_MSG(compute_list != nullptr && !compute_list->state.allow_draw_overlap, ERR_BUSY, "Only one draw/compute list can be active at the same time."); - - ERR_FAIL_COND_V(p_splits < 1, ERR_INVALID_DECLARATION); - - Framebuffer *framebuffer = framebuffer_owner.get_or_null(p_framebuffer); - ERR_FAIL_NULL_V(framebuffer, ERR_INVALID_DECLARATION); - - Point2i viewport_offset; - Point2i viewport_size = framebuffer->size; - bool constrained_to_region = false; - bool needs_clear_color = false; - bool needs_clear_depth = false; - - if (p_region != Rect2() && p_region != Rect2(Vector2(), viewport_size)) { // Check custom region. - Rect2i viewport(viewport_offset, viewport_size); - Rect2i regioni = p_region; - if (!(regioni.position.x >= viewport.position.x) && (regioni.position.y >= viewport.position.y) && - ((regioni.position.x + regioni.size.x) <= (viewport.position.x + viewport.size.x)) && - ((regioni.position.y + regioni.size.y) <= (viewport.position.y + viewport.size.y))) { - ERR_FAIL_V_MSG(ERR_INVALID_PARAMETER, "When supplying a custom region, it must be contained within the framebuffer rectangle"); - } - - viewport_offset = regioni.position; - viewport_size = regioni.size; - - // If clearing regions both in color and depth, we can switch to a fast path where we let Vulkan to the clears - // and we constrain the render area to the region. - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION && p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - constrained_to_region = true; - p_initial_color_action = INITIAL_ACTION_CLEAR; - p_initial_depth_action = INITIAL_ACTION_CLEAR; - } else { - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION_CONTINUE) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_CONTINUE; - } - if (p_initial_color_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_color = true; - p_initial_color_action = INITIAL_ACTION_KEEP; - } - if (p_initial_depth_action == INITIAL_ACTION_CLEAR_REGION) { - needs_clear_depth = true; - p_initial_depth_action = INITIAL_ACTION_KEEP; - } - } - } - - if (p_initial_color_action == INITIAL_ACTION_CLEAR || needs_clear_color) { // Check clear values. - - int color_count = 0; - for (int i = 0; i < framebuffer->texture_ids.size(); i++) { - Texture *texture = texture_owner.get_or_null(framebuffer->texture_ids[i]); - - if (!texture || !(texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { - color_count++; - } - } - - ERR_FAIL_COND_V_MSG(p_clear_color_values.size() != color_count, ERR_INVALID_PARAMETER, - "Clear color values supplied (" + itos(p_clear_color_values.size()) + ") differ from the amount required for framebuffer (" + itos(color_count) + ")."); - } - - RDD::FramebufferID fb_driver_id; - RDD::RenderPassID render_pass; - - Error err = _draw_list_setup_framebuffer(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, &fb_driver_id, &render_pass, &draw_list_subpass_count); - ERR_FAIL_COND_V(err != OK, ERR_CANT_CREATE); - - RDD::CommandBufferID frame_command_buffer = frames[frame].draw_command_buffer; - err = _draw_list_render_pass_begin(framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, viewport_offset, viewport_size, fb_driver_id, render_pass, frame_command_buffer, RDD::COMMAND_BUFFER_TYPE_SECONDARY, p_storage_textures, constrained_to_region); - - if (err != OK) { - return ERR_CANT_CREATE; - } - - draw_list_current_subpass = 0; - -#ifdef DEBUG_ENABLED - draw_list_framebuffer_format = framebuffer->format_id; -#endif - draw_list_render_pass = render_pass; - draw_list_vkframebuffer = fb_driver_id; - - err = _draw_list_allocate(Rect2i(viewport_offset, viewport_size), p_splits, 0); - if (err != OK) { - return err; - } - - if (needs_clear_color || needs_clear_depth) { - DEV_ASSERT(!constrained_to_region); - _draw_list_insert_clear_region(&draw_list[0], framebuffer, viewport_offset, viewport_size, needs_clear_color, p_clear_color_values, needs_clear_depth, p_clear_depth, p_clear_stencil); - } - - bool secondary_viewport_scissor = driver->api_trait_get(RDD::API_TRAIT_SECONDARY_VIEWPORT_SCISSOR); - for (uint32_t i = 0; i < p_splits; i++) { - if (secondary_viewport_scissor) { - driver->command_render_set_viewport(draw_list[i].command_buffer, Rect2i(viewport_offset, viewport_size)); - driver->command_render_set_scissor(draw_list[i].command_buffer, Rect2i(viewport_offset, viewport_size)); - } - r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - } - - return OK; + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Deprecated. Split draw lists are used automatically by RenderingDevice."); } +#endif RenderingDevice::DrawList *RenderingDevice::_get_draw_list_ptr(DrawListID p_id) { if (p_id < 0) { @@ -4045,22 +3371,7 @@ RenderingDevice::DrawList *RenderingDevice::_get_draw_list_ptr(DrawListID p_id) if (!draw_list) { return nullptr; } else if (p_id == (int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT)) { - if (draw_list_split) { - return nullptr; - } return draw_list; - } else if (p_id >> DrawListID(ID_BASE_SHIFT) == ID_TYPE_SPLIT_DRAW_LIST) { - if (!draw_list_split) { - return nullptr; - } - - uint64_t index = p_id & ((DrawListID(1) << DrawListID(ID_BASE_SHIFT)) - 1); // Mask. - - if (index >= draw_list_count) { - return nullptr; - } - - return &draw_list[index]; } else { return nullptr; } @@ -4073,7 +3384,7 @@ void RenderingDevice::draw_list_set_blend_constants(DrawListID p_list, const Col ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - driver->command_render_set_blend_constants(dl->command_buffer, p_color); + draw_graph.add_draw_list_set_blend_constants(p_color); } void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline) { @@ -4095,7 +3406,7 @@ void RenderingDevice::draw_list_bind_render_pipeline(DrawListID p_list, RID p_re dl->state.pipeline = p_render_pipeline; - driver->command_bind_render_pipeline(dl->command_buffer, pipeline->driver_id); + draw_graph.add_draw_list_bind_pipeline(pipeline->driver_id, pipeline->stage_bits); if (dl->state.pipeline_shader != pipeline->shader) { // Shader changed, so descriptor sets may become incompatible. @@ -4183,21 +3494,6 @@ void RenderingDevice::draw_list_bind_uniform_set(DrawListID p_list, RID p_unifor dl->state.sets[p_index].uniform_set_format = uniform_set->format; dl->state.sets[p_index].uniform_set = p_uniform_set; - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - uint32_t mst_count = uniform_set->mutable_storage_textures.size(); - if (mst_count) { - Texture **mst_textures = const_cast<UniformSet *>(uniform_set)->mutable_storage_textures.ptrw(); - for (uint32_t i = 0; i < mst_count; i++) { - if (mst_textures[i]->used_in_frame != frames_drawn) { - mst_textures[i]->used_in_frame = frames_drawn; - mst_textures[i]->used_in_transfer = false; - mst_textures[i]->used_in_compute = false; - } - mst_textures[i]->used_in_raster = true; - } - } - } - #ifdef DEBUG_ENABLED { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); @@ -4235,7 +3531,12 @@ void RenderingDevice::draw_list_bind_vertex_array(DrawListID p_list, RID p_verte dl->validation.vertex_max_instances_allowed = vertex_array->max_instances_allowed; #endif dl->validation.vertex_array_size = vertex_array->vertex_count; - driver->command_render_bind_vertex_buffers(dl->command_buffer, vertex_array->buffers.size(), vertex_array->buffers.ptr(), vertex_array->offsets.ptr()); + + draw_graph.add_draw_list_bind_vertex_buffers(vertex_array->buffers, vertex_array->offsets); + + for (int i = 0; i < vertex_array->draw_trackers.size(); i++) { + draw_graph.add_draw_list_usage(vertex_array->draw_trackers[i], RDG::RESOURCE_USAGE_VERTEX_BUFFER_READ); + } } void RenderingDevice::draw_list_bind_index_array(DrawListID p_list, RID p_index_array) { @@ -4256,10 +3557,14 @@ void RenderingDevice::draw_list_bind_index_array(DrawListID p_list, RID p_index_ #ifdef DEBUG_ENABLED dl->validation.index_array_max_index = index_array->max_index; #endif - dl->validation.index_array_size = index_array->indices; - dl->validation.index_array_offset = index_array->offset; + dl->validation.index_array_count = index_array->indices; + + const uint64_t offset_bytes = index_array->offset * (index_array->format == INDEX_BUFFER_FORMAT_UINT16 ? sizeof(uint16_t) : sizeof(uint32_t)); + draw_graph.add_draw_list_bind_index_buffer(index_array->driver_id, index_array->format, offset_bytes); - driver->command_render_bind_index_buffer(dl->command_buffer, index_array->driver_id, index_array->format, index_array->offset); + if (index_array->draw_tracker != nullptr) { + draw_graph.add_draw_list_usage(index_array->draw_tracker, RDG::RESOURCE_USAGE_INDEX_BUFFER_READ); + } } void RenderingDevice::draw_list_set_line_width(DrawListID p_list, float p_width) { @@ -4269,7 +3574,7 @@ void RenderingDevice::draw_list_set_line_width(DrawListID p_list, float p_width) ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - driver->command_render_set_line_width(dl->command_buffer, p_width); + draw_graph.add_draw_list_set_line_width(p_width); } void RenderingDevice::draw_list_set_push_constant(DrawListID p_list, const void *p_data, uint32_t p_data_size) { @@ -4284,7 +3589,9 @@ void RenderingDevice::draw_list_set_push_constant(DrawListID p_list, const void ERR_FAIL_COND_MSG(p_data_size != dl->validation.pipeline_push_constant_size, "This render pipeline requires (" + itos(dl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); #endif - driver->command_bind_push_constants(dl->command_buffer, dl->state.pipeline_shader_driver_id, 0, VectorView((const uint32_t *)p_data, p_data_size / sizeof(uint32_t))); + + draw_graph.add_draw_list_set_push_constant(dl->state.pipeline_shader_driver_id, p_data, p_data_size); + #ifdef DEBUG_ENABLED dl->validation.pipeline_push_constant_supplied = true; #endif @@ -4338,14 +3645,19 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint } } #endif - driver->command_uniform_set_prepare_for_use(dl->command_buffer, dl->state.sets[i].uniform_set_driver_id, dl->state.pipeline_shader_driver_id, i); + draw_graph.add_draw_list_uniform_set_prepare_for_use(dl->state.pipeline_shader_driver_id, dl->state.sets[i].uniform_set_driver_id, i); } for (uint32_t i = 0; i < dl->state.set_count; i++) { if (dl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. } if (!dl->state.sets[i].bound) { - driver->command_bind_render_uniform_set(dl->command_buffer, dl->state.sets[i].uniform_set_driver_id, dl->state.pipeline_shader_driver_id, i); + // All good, see if this requires re-binding. + draw_graph.add_draw_list_bind_uniform_set(dl->state.pipeline_shader_driver_id, dl->state.sets[i].uniform_set_driver_id, i); + + UniformSet *uniform_set = uniform_set_owner.get_or_null(dl->state.sets[i].uniform_set); + draw_graph.add_draw_list_usages(uniform_set->draw_trackers, uniform_set->draw_trackers_usage); + dl->state.sets[i].bound = true; } } @@ -4355,13 +3667,13 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint ERR_FAIL_COND_MSG(p_procedural_vertices > 0, "Procedural vertices can't be used together with indices."); - ERR_FAIL_COND_MSG(!dl->validation.index_array_size, + ERR_FAIL_COND_MSG(!dl->validation.index_array_count, "Draw command requested indices, but no index buffer was set."); ERR_FAIL_COND_MSG(dl->validation.pipeline_uses_restart_indices != dl->validation.index_buffer_uses_restart_indices, "The usage of restart indices in index buffer does not match the render primitive in the pipeline."); #endif - uint32_t to_draw = dl->validation.index_array_size; + uint32_t to_draw = dl->validation.index_array_count; #ifdef DEBUG_ENABLED ERR_FAIL_COND_MSG(to_draw < dl->validation.pipeline_primitive_minimum, @@ -4370,7 +3682,8 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint ERR_FAIL_COND_MSG((to_draw % dl->validation.pipeline_primitive_divisor) != 0, "Index amount (" + itos(to_draw) + ") must be a multiple of the amount of indices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); #endif - driver->command_render_draw_indexed(dl->command_buffer, to_draw, p_instances, dl->validation.index_array_offset, 0, 0); + + draw_graph.add_draw_list_draw_indexed(to_draw, p_instances, 0); } else { uint32_t to_draw; @@ -4396,7 +3709,7 @@ void RenderingDevice::draw_list_draw(DrawListID p_list, bool p_use_indices, uint "Vertex amount (" + itos(to_draw) + ") must be a multiple of the amount of vertices required by the render primitive (" + itos(dl->validation.pipeline_primitive_divisor) + ")."); #endif - driver->command_render_draw(dl->command_buffer, to_draw, p_instances, 0, 0); + draw_graph.add_draw_list_draw(to_draw, p_instances); } } @@ -4416,7 +3729,7 @@ void RenderingDevice::draw_list_enable_scissor(DrawListID p_list, const Rect2 &p return; } - driver->command_render_set_scissor(dl->command_buffer, rect); + _draw_list_set_scissor(rect); } void RenderingDevice::draw_list_disable_scissor(DrawListID p_list) { @@ -4426,7 +3739,7 @@ void RenderingDevice::draw_list_disable_scissor(DrawListID p_list) { ERR_FAIL_COND_MSG(!dl->validation.active, "Submitted Draw Lists can no longer be modified."); #endif - driver->command_render_set_scissor(dl->command_buffer, dl->viewport); + _draw_list_set_scissor(dl->viewport); } uint32_t RenderingDevice::draw_list_get_current_pass() { @@ -4443,230 +3756,80 @@ RenderingDevice::DrawListID RenderingDevice::draw_list_switch_to_next_pass() { Rect2i viewport; _draw_list_free(&viewport); - driver->command_next_render_subpass(frames[frame].draw_command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY); + draw_graph.add_draw_list_next_subpass(RDD::COMMAND_BUFFER_TYPE_PRIMARY); - _draw_list_allocate(viewport, 0, draw_list_current_subpass); + _draw_list_allocate(viewport, draw_list_current_subpass); return int64_t(ID_TYPE_DRAW_LIST) << ID_BASE_SHIFT; } -Error RenderingDevice::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { - _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V(draw_list == nullptr, ERR_INVALID_PARAMETER); - ERR_FAIL_COND_V(draw_list_current_subpass >= draw_list_subpass_count - 1, ERR_INVALID_PARAMETER); - draw_list_current_subpass++; - - Rect2i viewport; - _draw_list_free(&viewport); - - driver->command_next_render_subpass(frames[frame].draw_command_buffer, RDD::COMMAND_BUFFER_TYPE_PRIMARY); - - _draw_list_allocate(viewport, p_splits, draw_list_current_subpass); - - for (uint32_t i = 0; i < p_splits; i++) { - r_split_ids[i] = (int64_t(ID_TYPE_SPLIT_DRAW_LIST) << ID_BASE_SHIFT) + i; - } - - return OK; +#ifndef DISABLE_DEPRECATED +Error RenderingDevice::draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids) { + ERR_FAIL_V_MSG(ERR_UNAVAILABLE, "Deprecated. Split draw lists are used automatically by RenderingDevice."); } +#endif -Error RenderingDevice::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass) { +Error RenderingDevice::_draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass) { // Lock while draw_list is active. _THREAD_SAFE_LOCK_ - if (p_splits == 0) { - draw_list = memnew(DrawList); - draw_list->command_buffer = frames[frame].draw_command_buffer; - draw_list->viewport = p_viewport; - draw_list_count = 0; - draw_list_split = false; - } else { - if (p_splits > (uint32_t)split_draw_list_allocators.size()) { - uint32_t from = split_draw_list_allocators.size(); - split_draw_list_allocators.resize(p_splits); - for (uint32_t i = from; i < p_splits; i++) { - RDD::CommandPoolID cmd_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY); - ERR_FAIL_COND_V(!cmd_pool, ERR_CANT_CREATE); - split_draw_list_allocators.write[i].command_pool = cmd_pool; - - for (int j = 0; j < frame_count; j++) { - RDD::CommandBufferID cmd_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY, cmd_pool); - ERR_FAIL_COND_V(!cmd_buffer, ERR_CANT_CREATE); - split_draw_list_allocators.write[i].command_buffers.push_back(cmd_buffer); - } - } - } - draw_list = memnew_arr(DrawList, p_splits); - draw_list_count = p_splits; - draw_list_split = true; - - for (uint32_t i = 0; i < p_splits; i++) { - // Take a command buffer and initialize it. - RDD::CommandBufferID cmd_buffer = split_draw_list_allocators[i].command_buffers[frame]; - - bool ok = driver->command_buffer_begin_secondary(cmd_buffer, draw_list_render_pass, p_subpass, draw_list_vkframebuffer); - if (!ok) { - memdelete_arr(draw_list); - draw_list = nullptr; - ERR_FAIL_V(ERR_CANT_CREATE); - } - - draw_list[i].command_buffer = cmd_buffer; - draw_list[i].viewport = p_viewport; - } - } + draw_list = memnew(DrawList); + draw_list->viewport = p_viewport; + draw_list_count = 0; return OK; } void RenderingDevice::_draw_list_free(Rect2i *r_last_viewport) { - if (draw_list_split) { - // Send all command buffers. - RDD::CommandBufferID *command_buffers = (RDD::CommandBufferID *)alloca(sizeof(RDD::CommandBufferID) * draw_list_count); - for (uint32_t i = 0; i < draw_list_count; i++) { - driver->command_buffer_end(draw_list[i].command_buffer); - command_buffers[i] = draw_list[i].command_buffer; - if (r_last_viewport) { - if (i == 0 || draw_list[i].viewport_set) { - *r_last_viewport = draw_list[i].viewport; - } - } - } - - driver->command_buffer_execute_secondary(frames[frame].draw_command_buffer, VectorView(command_buffers, draw_list_count)); - memdelete_arr(draw_list); - draw_list = nullptr; - - } else { - if (r_last_viewport) { - *r_last_viewport = draw_list->viewport; - } - // Just end the list. - memdelete(draw_list); - draw_list = nullptr; + if (r_last_viewport) { + *r_last_viewport = draw_list->viewport; } + // Just end the list. + memdelete(draw_list); + draw_list = nullptr; // Draw_list is no longer active. _THREAD_SAFE_UNLOCK_ } -void RenderingDevice::draw_list_end(BitField<BarrierMask> p_post_barrier) { +void RenderingDevice::draw_list_end() { _THREAD_SAFE_METHOD_ ERR_FAIL_COND_MSG(!draw_list, "Immediate draw list is already inactive."); - _draw_list_free(); + draw_graph.add_draw_list_end(); - driver->command_end_render_pass(frames[frame].draw_command_buffer); + _draw_list_free(); for (int i = 0; i < draw_list_bound_textures.size(); i++) { Texture *texture = texture_owner.get_or_null(draw_list_bound_textures[i]); ERR_CONTINUE(!texture); // Wtf. - if (draw_list_unbind_color_textures && (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT)) { + if (texture->usage_flags & TEXTURE_USAGE_COLOR_ATTACHMENT_BIT) { texture->bound = false; } - if (draw_list_unbind_depth_textures && (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT)) { + if (texture->usage_flags & TEXTURE_USAGE_DEPTH_STENCIL_ATTACHMENT_BIT) { texture->bound = false; } } - draw_list_bound_textures.clear(); - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - BitField<RDD::PipelineStageBits> dst_stages; - BitField<RDD::BarrierAccessBits> dst_access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); // RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT); // RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); // RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); // RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); - } - - if (dst_stages.is_empty()) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); - } - - RDD::TextureBarrier *texture_barriers = nullptr; - - uint32_t texture_barrier_count = draw_list_storage_textures.size(); - - if (texture_barrier_count) { - texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * draw_list_storage_textures.size()); - } - - BitField<RDD::PipelineStageBits> src_stage(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT | - RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT | RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); - BitField<RDD::BarrierAccessBits> src_access( - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); - - if (texture_barrier_count) { - src_stage.set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - - for (uint32_t i = 0; i < texture_barrier_count; i++) { - Texture *texture = texture_owner.get_or_null(draw_list_storage_textures[i]); - - RDD::TextureBarrier &tb = texture_barriers[i]; - tb.texture = texture->driver_id; - tb.src_access = src_access; - tb.dst_access = dst_access; - tb.prev_layout = texture->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - tb.subresources.aspect = texture->read_aspect_flags; - tb.subresources.base_mipmap = texture->base_mipmap; - tb.subresources.mipmap_count = texture->mipmaps; - tb.subresources.base_layer = texture->base_layer; - tb.subresources.layer_count = texture->layers; - - texture->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - // To ensure proper synchronization, we must make sure rendering is done before: - // * Some buffer is copied. - // * Another render pass happens (since we may be done). - - RDD::MemoryBarrier mb; - mb.src_access = src_access; - mb.dst_access = dst_access; - - if (texture_barrier_count > 0 || p_post_barrier != BARRIER_MASK_NO_BARRIER) { - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stage, dst_stages, mb, {}, VectorView(texture_barriers, texture_barrier_count)); - } - } - - draw_list_storage_textures.clear(); - -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#endif + draw_list_bound_textures.clear(); } /***********************/ /**** COMPUTE LISTS ****/ /***********************/ -RenderingDevice::ComputeListID RenderingDevice::compute_list_begin(bool p_allow_draw_overlap) { +RenderingDevice::ComputeListID RenderingDevice::compute_list_begin() { _THREAD_SAFE_METHOD_ - ERR_FAIL_COND_V_MSG(!p_allow_draw_overlap && draw_list != nullptr, INVALID_ID, "Only one draw list can be active at the same time."); ERR_FAIL_COND_V_MSG(compute_list != nullptr, INVALID_ID, "Only one draw/compute list can be active at the same time."); // Lock while compute_list is active. _THREAD_SAFE_LOCK_ compute_list = memnew(ComputeList); - compute_list->command_buffer = frames[frame].draw_command_buffer; - compute_list->state.allow_draw_overlap = p_allow_draw_overlap; + + draw_graph.add_compute_list_begin(); return ID_TYPE_COMPUTE_LIST; } @@ -4688,7 +3851,7 @@ void RenderingDevice::compute_list_bind_compute_pipeline(ComputeListID p_list, R cl->state.pipeline = p_compute_pipeline; - driver->command_bind_compute_pipeline(cl->command_buffer, pipeline->driver_id); + draw_graph.add_compute_list_bind_pipeline(pipeline->driver_id); if (cl->state.pipeline_shader != pipeline->shader) { // Shader changed, so descriptor sets may become incompatible. @@ -4779,109 +3942,6 @@ void RenderingDevice::compute_list_bind_uniform_set(ComputeListID p_list, RID p_ cl->state.sets[p_index].uniform_set_format = uniform_set->format; cl->state.sets[p_index].uniform_set = p_uniform_set; - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - uint32_t textures_to_sampled_count = uniform_set->mutable_sampled_textures.size(); - uint32_t textures_to_storage_count = uniform_set->mutable_storage_textures.size(); - - Texture **textures_to_sampled = uniform_set->mutable_sampled_textures.ptrw(); - - RDD::TextureBarrier *texture_barriers = nullptr; - - if (textures_to_sampled_count + textures_to_storage_count) { - texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * (textures_to_sampled_count + textures_to_storage_count)); - } - uint32_t texture_barrier_count = 0; - - BitField<RDD::PipelineStageBits> src_stages; - - for (uint32_t i = 0; i < textures_to_sampled_count; i++) { - if (textures_to_sampled[i]->layout != RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - - RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; - tb.texture = textures_to_sampled[i]->driver_id; - tb.src_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - tb.prev_layout = textures_to_sampled[i]->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - tb.subresources.aspect = textures_to_sampled[i]->read_aspect_flags; - tb.subresources.base_mipmap = textures_to_sampled[i]->base_mipmap; - tb.subresources.mipmap_count = textures_to_sampled[i]->mipmaps; - tb.subresources.base_layer = textures_to_sampled[i]->base_layer; - tb.subresources.layer_count = textures_to_sampled[i]->layers; - - textures_to_sampled[i]->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - - cl->state.textures_to_sampled_layout.erase(textures_to_sampled[i]); - } - - if (textures_to_sampled[i]->used_in_frame != frames_drawn) { - textures_to_sampled[i]->used_in_frame = frames_drawn; - textures_to_sampled[i]->used_in_transfer = false; - textures_to_sampled[i]->used_in_raster = false; - } - textures_to_sampled[i]->used_in_compute = true; - } - - Texture **textures_to_storage = uniform_set->mutable_storage_textures.ptrw(); - - for (uint32_t i = 0; i < textures_to_storage_count; i++) { - if (textures_to_storage[i]->layout != RDD::TEXTURE_LAYOUT_GENERAL) { - BitField<RDD::BarrierAccessBits> src_access; - - if (textures_to_storage[i]->used_in_frame == frames_drawn) { - if (textures_to_storage[i]->used_in_compute) { - src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (textures_to_storage[i]->used_in_raster) { - src_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (textures_to_storage[i]->used_in_transfer) { - src_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); - } - - textures_to_storage[i]->used_in_compute = false; - textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_transfer = false; - - } else { - src_access.clear(); - textures_to_storage[i]->used_in_compute = false; - textures_to_storage[i]->used_in_raster = false; - textures_to_storage[i]->used_in_transfer = false; - textures_to_storage[i]->used_in_frame = frames_drawn; - } - - RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; - tb.texture = textures_to_storage[i]->driver_id; - tb.src_access = src_access; - tb.dst_access = (RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - tb.prev_layout = textures_to_storage[i]->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_GENERAL; - tb.subresources.aspect = textures_to_storage[i]->read_aspect_flags; - tb.subresources.base_mipmap = textures_to_storage[i]->base_mipmap; - tb.subresources.mipmap_count = textures_to_storage[i]->mipmaps; - tb.subresources.base_layer = textures_to_storage[i]->base_layer; - tb.subresources.layer_count = textures_to_storage[i]->layers; - - textures_to_storage[i]->layout = RDD::TEXTURE_LAYOUT_GENERAL; - - cl->state.textures_to_sampled_layout.insert(textures_to_storage[i]); // Needs to go back to sampled layout afterwards. - } - } - - if (texture_barrier_count) { - if (src_stages.is_empty()) { - src_stages.set_flag(RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT); - } - - driver->command_pipeline_barrier(cl->command_buffer, src_stages, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, {}, {}, VectorView(texture_barriers, texture_barrier_count)); - } - } - #if 0 { // Validate that textures bound are not attached as framebuffer bindings. uint32_t attachable_count = uniform_set->attachable_textures.size(); @@ -4901,6 +3961,7 @@ void RenderingDevice::compute_list_bind_uniform_set(ComputeListID p_list, RID p_ void RenderingDevice::compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size) { ERR_FAIL_COND(p_list != ID_TYPE_COMPUTE_LIST); ERR_FAIL_NULL(compute_list); + ERR_FAIL_COND_MSG(p_data_size > MAX_PUSH_CONSTANT_SIZE, "Push constants can't be bigger than 128 bytes to maintain compatibility."); ComputeList *cl = compute_list; @@ -4912,7 +3973,13 @@ void RenderingDevice::compute_list_set_push_constant(ComputeListID p_list, const ERR_FAIL_COND_MSG(p_data_size != cl->validation.pipeline_push_constant_size, "This compute pipeline requires (" + itos(cl->validation.pipeline_push_constant_size) + ") bytes of push constant data, supplied: (" + itos(p_data_size) + ")"); #endif - driver->command_bind_push_constants(cl->command_buffer, cl->state.pipeline_shader_driver_id, 0, VectorView((const uint32_t *)p_data, p_data_size / sizeof(uint32_t))); + + draw_graph.add_compute_list_set_push_constant(cl->state.pipeline_shader_driver_id, p_data, p_data_size); + + // Store it in the state in case we need to restart the compute list. + memcpy(cl->state.push_constant_data, p_data, p_data_size); + cl->state.push_constant_size = p_data_size; + #ifdef DEBUG_ENABLED cl->validation.pipeline_push_constant_supplied = true; #endif @@ -4970,19 +4037,24 @@ void RenderingDevice::compute_list_dispatch(ComputeListID p_list, uint32_t p_x_g } } #endif - driver->command_uniform_set_prepare_for_use(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); } for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. } if (!cl->state.sets[i].bound) { - driver->command_bind_compute_uniform_set(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + // All good, see if this requires re-binding. + draw_graph.add_compute_list_bind_uniform_set(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + + UniformSet *uniform_set = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); + draw_graph.add_compute_list_usages(uniform_set->draw_trackers, uniform_set->draw_trackers_usage); + cl->state.sets[i].bound = true; } } - driver->command_compute_dispatch(cl->command_buffer, p_x_groups, p_y_groups, p_z_groups); + draw_graph.add_compute_list_dispatch(p_x_groups, p_y_groups, p_z_groups); } void RenderingDevice::compute_list_dispatch_threads(ComputeListID p_list, uint32_t p_x_threads, uint32_t p_y_threads, uint32_t p_z_threads) { @@ -5058,106 +4130,56 @@ void RenderingDevice::compute_list_dispatch_indirect(ComputeListID p_list, RID p } } #endif - driver->command_uniform_set_prepare_for_use(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + draw_graph.add_compute_list_uniform_set_prepare_for_use(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); } for (uint32_t i = 0; i < cl->state.set_count; i++) { if (cl->state.sets[i].pipeline_expected_format == 0) { continue; // Nothing expected by this pipeline. } if (!cl->state.sets[i].bound) { - driver->command_bind_compute_uniform_set(cl->command_buffer, cl->state.sets[i].uniform_set_driver_id, cl->state.pipeline_shader_driver_id, i); + // All good, see if this requires re-binding. + draw_graph.add_compute_list_bind_uniform_set(cl->state.pipeline_shader_driver_id, cl->state.sets[i].uniform_set_driver_id, i); + + UniformSet *uniform_set = uniform_set_owner.get_or_null(cl->state.sets[i].uniform_set); + draw_graph.add_compute_list_usages(uniform_set->draw_trackers, uniform_set->draw_trackers_usage); + cl->state.sets[i].bound = true; } } - driver->command_compute_dispatch_indirect(cl->command_buffer, buffer->driver_id, p_offset); + draw_graph.add_compute_list_dispatch_indirect(buffer->driver_id, p_offset); + + if (buffer->draw_tracker != nullptr) { + draw_graph.add_compute_list_usage(buffer->draw_tracker, RDG::RESOURCE_USAGE_INDIRECT_BUFFER_READ); + } } void RenderingDevice::compute_list_add_barrier(ComputeListID p_list) { // Must be called within a compute list, the class mutex is locked during that time - BitField<RDD::PipelineStageBits> stages(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - BitField<RDD::BarrierAccessBits> access(RDD::BARRIER_ACCESS_SHADER_READ_BIT); - _compute_list_add_barrier(BARRIER_MASK_COMPUTE, stages, access); -} - -void RenderingDevice::_compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, BitField<RDD::PipelineStageBits> p_stages, BitField<RDD::BarrierAccessBits> p_access) { - ERR_FAIL_NULL(compute_list); - - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::TextureBarrier *texture_barriers = nullptr; - - uint32_t texture_barrier_count = compute_list->state.textures_to_sampled_layout.size(); - - if (texture_barrier_count) { - texture_barriers = (RDD::TextureBarrier *)alloca(sizeof(RDD::TextureBarrier) * texture_barrier_count); - } - - texture_barrier_count = 0; // We'll count how many we end up issuing. - - for (Texture *E : compute_list->state.textures_to_sampled_layout) { - if (E->layout != RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL) { - RDD::TextureBarrier &tb = texture_barriers[texture_barrier_count++]; - tb.texture = E->driver_id; - tb.src_access = RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; - tb.dst_access = p_access; - tb.prev_layout = E->layout; - tb.next_layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - tb.subresources.aspect = E->read_aspect_flags; - tb.subresources.base_mipmap = E->base_mipmap; - tb.subresources.mipmap_count = E->mipmaps; - tb.subresources.base_layer = E->base_layer; - tb.subresources.layer_count = E->layers; - - E->layout = RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; - } - - if (E->used_in_frame != frames_drawn) { - E->used_in_transfer = false; - E->used_in_raster = false; - E->used_in_compute = false; - E->used_in_frame = frames_drawn; - } - } + compute_list_barrier_state = compute_list->state; + compute_list_end(); + compute_list_begin(); - if (p_stages) { - RDD::MemoryBarrier mb; - mb.src_access = RDD::BARRIER_ACCESS_SHADER_WRITE_BIT; - mb.dst_access = p_access; - driver->command_pipeline_barrier(compute_list->command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, p_stages, mb, {}, VectorView(texture_barriers, texture_barrier_count)); + if (compute_list_barrier_state.pipeline.is_valid()) { + compute_list_bind_compute_pipeline(p_list, compute_list_barrier_state.pipeline); + } - } else if (texture_barrier_count) { - driver->command_pipeline_barrier(compute_list->command_buffer, RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT, RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, {}, {}, VectorView(texture_barriers, texture_barrier_count)); + for (uint32_t i = 0; i < compute_list_barrier_state.set_count; i++) { + if (compute_list_barrier_state.sets[i].uniform_set.is_valid()) { + compute_list_bind_uniform_set(p_list, compute_list_barrier_state.sets[i].uniform_set, i); } } -#ifdef FORCE_FULL_BARRIER - _full_barrier(true); -#endif + if (compute_list_barrier_state.push_constant_size > 0) { + compute_list_set_push_constant(p_list, compute_list_barrier_state.push_constant_data, compute_list_barrier_state.push_constant_size); + } } -void RenderingDevice::compute_list_end(BitField<BarrierMask> p_post_barrier) { +void RenderingDevice::compute_list_end() { ERR_FAIL_NULL(compute_list); - BitField<RDD::PipelineStageBits> stages; - BitField<RDD::BarrierAccessBits> access; - if (p_post_barrier.has_flag(BARRIER_MASK_COMPUTE)) { - stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_VERTEX)) { - stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_FRAGMENT)) { - stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); - access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); - } - if (p_post_barrier.has_flag(BARRIER_MASK_TRANSFER)) { - stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); - } - _compute_list_add_barrier(p_post_barrier, stages, access); + draw_graph.add_compute_list_end(); memdelete(compute_list); compute_list = nullptr; @@ -5166,66 +4188,168 @@ void RenderingDevice::compute_list_end(BitField<BarrierMask> p_post_barrier) { _THREAD_SAFE_UNLOCK_ } +#ifndef DISABLE_DEPRECATED void RenderingDevice::barrier(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to) { - if (!driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - return; - } + WARN_PRINT("Deprecated. Barriers are automatically inserted by RenderingDevice."); +} - BitField<RDD::PipelineStageBits> src_stages; - BitField<RDD::BarrierAccessBits> src_access; +void RenderingDevice::full_barrier() { + WARN_PRINT("Deprecated. Barriers are automatically inserted by RenderingDevice."); +} +#endif - if (p_from == 0) { - src_stages.set_flag(RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT); +/***********************/ +/**** COMMAND GRAPH ****/ +/***********************/ + +bool RenderingDevice::_texture_make_mutable(Texture *p_texture, RID p_texture_id) { + if (p_texture->draw_tracker != nullptr) { + // Texture already has a tracker. + return false; } else { - if (p_from.has_flag(BARRIER_MASK_COMPUTE)) { - src_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_from.has_flag(BARRIER_MASK_FRAGMENT)) { - src_stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT).set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT).set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + if (p_texture->owner.is_valid()) { + // Texture has an owner. + Texture *owner_texture = texture_owner.get_or_null(p_texture->owner); + ERR_FAIL_COND_V(!owner_texture, false); + + if (owner_texture->draw_tracker != nullptr) { + // Create a tracker for this dependency in particular. + if (p_texture->slice_type == TEXTURE_SLICE_MAX) { + // Shared texture. + p_texture->draw_tracker = owner_texture->draw_tracker; + p_texture->draw_tracker->reference_count++; + } else { + // Slice texture. + HashMap<Rect2i, RDG::ResourceTracker *>::ConstIterator draw_tracker_iterator = owner_texture->slice_trackers.find(p_texture->slice_rect); + RDG::ResourceTracker *draw_tracker = nullptr; + if (draw_tracker_iterator != owner_texture->slice_trackers.end()) { + // Reuse the tracker at the matching rectangle. + draw_tracker = draw_tracker_iterator->value; + } else { + // Create a new tracker and store it on the map. + draw_tracker = RDG::resource_tracker_create(); + draw_tracker->parent = owner_texture->draw_tracker; + draw_tracker->texture_driver_id = p_texture->driver_id; + draw_tracker->texture_subresources = p_texture->barrier_range(); + draw_tracker->texture_slice_or_dirty_rect = p_texture->slice_rect; + owner_texture->slice_trackers[p_texture->slice_rect] = draw_tracker; + } + + p_texture->slice_trackers.clear(); + p_texture->draw_tracker = draw_tracker; + p_texture->draw_tracker->reference_count++; + } + + if (p_texture_id.is_valid()) { + _dependencies_make_mutable(p_texture_id, p_texture->draw_tracker); + } + } else { + // Delegate this to the owner instead, as it'll make all its dependencies mutable. + _texture_make_mutable(owner_texture, p_texture->owner); + } + } else { + // Regular texture. + p_texture->draw_tracker = RDG::resource_tracker_create(); + p_texture->draw_tracker->texture_driver_id = p_texture->driver_id; + p_texture->draw_tracker->texture_subresources = p_texture->barrier_range(); + p_texture->draw_tracker->reference_count = 1; + + if (p_texture_id.is_valid()) { + if (p_texture->has_initial_data) { + // If the texture was initialized with initial data but wasn't made mutable from the start, assume the texture sampling usage. + p_texture->draw_tracker->usage = RDG::RESOURCE_USAGE_TEXTURE_SAMPLE; + } + + _dependencies_make_mutable(p_texture_id, p_texture->draw_tracker); + } } - if (p_from.has_flag(BARRIER_MASK_TRANSFER)) { - src_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - src_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT); + + return true; + } +} + +bool RenderingDevice::_buffer_make_mutable(Buffer *p_buffer, RID p_buffer_id) { + if (p_buffer->draw_tracker != nullptr) { + // Buffer already has a tracker. + return false; + } else { + // Create a tracker for the buffer and make all its dependencies mutable. + p_buffer->draw_tracker = RDG::resource_tracker_create(); + p_buffer->draw_tracker->buffer_driver_id = p_buffer->driver_id; + if (p_buffer_id.is_valid()) { + _dependencies_make_mutable(p_buffer_id, p_buffer->draw_tracker); } + + return true; } +} - BitField<RDD::PipelineStageBits> dst_stages; - BitField<RDD::BarrierAccessBits> dst_access; +bool RenderingDevice::_vertex_array_make_mutable(VertexArray *p_vertex_array, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker) { + if (!p_vertex_array->untracked_buffers.has(p_resource_id)) { + // Vertex array thinks the buffer is already tracked or does not use it. + return false; + } else { + // Vertex array is aware of the buffer but it isn't being tracked. + p_vertex_array->draw_trackers.push_back(p_resource_tracker); + p_vertex_array->untracked_buffers.erase(p_resource_id); + return true; + } +} - if (p_to == 0) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT); +bool RenderingDevice::_index_array_make_mutable(IndexArray *p_index_array, RDG::ResourceTracker *p_resource_tracker) { + if (p_index_array->draw_tracker != nullptr) { + // Index array already has a tracker. + return false; } else { - if (p_to.has_flag(BARRIER_MASK_COMPUTE)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); - } - if (p_to.has_flag(BARRIER_MASK_VERTEX)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT).set_flag(RDD::PIPELINE_STAGE_VERTEX_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDEX_READ_BIT).set_flag(RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); - } - if (p_to.has_flag(BARRIER_MASK_FRAGMENT)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_FRAGMENT_SHADER_BIT).set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_SHADER_READ_BIT).set_flag(RDD::BARRIER_ACCESS_SHADER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT); - } - if (p_to.has_flag(BARRIER_MASK_TRANSFER)) { - dst_stages.set_flag(RDD::PIPELINE_STAGE_TRANSFER_BIT); - dst_access.set_flag(RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT).set_flag(RDD::BARRIER_ACCESS_TRANSFER_READ_BIT); - } + // Index array should assign the tracker from the buffer. + p_index_array->draw_tracker = p_resource_tracker; + return true; } +} - RDD::MemoryBarrier mb; - mb.src_access = src_access; - mb.dst_access = dst_access; - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, src_stages, dst_stages, mb, {}, {}); +bool RenderingDevice::_uniform_set_make_mutable(UniformSet *p_uniform_set, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker) { + HashMap<RID, RDG::ResourceUsage>::Iterator E = p_uniform_set->untracked_usage.find(p_resource_id); + if (!E) { + // Uniform set thinks the resource is already tracked or does not use it. + return false; + } else { + // Uniform set has seen the resource but hasn't added its tracker yet. + p_uniform_set->draw_trackers.push_back(p_resource_tracker); + p_uniform_set->draw_trackers_usage.push_back(E->value); + p_uniform_set->untracked_usage.remove(E); + return true; + } } -void RenderingDevice::full_barrier() { -#ifndef DEBUG_ENABLED - ERR_PRINT("Full barrier is debug-only, should not be used in production"); -#endif - _full_barrier(true); +bool RenderingDevice::_dependency_make_mutable(RID p_id, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker) { + if (texture_owner.owns(p_id)) { + Texture *texture = texture_owner.get_or_null(p_id); + return _texture_make_mutable(texture, p_id); + } else if (vertex_array_owner.owns(p_id)) { + VertexArray *vertex_array = vertex_array_owner.get_or_null(p_id); + return _vertex_array_make_mutable(vertex_array, p_resource_id, p_resource_tracker); + } else if (index_array_owner.owns(p_id)) { + IndexArray *index_array = index_array_owner.get_or_null(p_id); + return _index_array_make_mutable(index_array, p_resource_tracker); + } else if (uniform_set_owner.owns(p_id)) { + UniformSet *uniform_set = uniform_set_owner.get_or_null(p_id); + return _uniform_set_make_mutable(uniform_set, p_resource_id, p_resource_tracker); + } else { + DEV_ASSERT(false && "Unknown resource type to make mutable."); + return false; + } +} + +bool RenderingDevice::_dependencies_make_mutable(RID p_id, RDG::ResourceTracker *p_resource_tracker) { + bool made_mutable = false; + HashMap<RID, HashSet<RID>>::Iterator E = dependency_map.find(p_id); + if (E) { + for (RID rid : E->value) { + made_mutable = _dependency_make_mutable(rid, p_id, p_resource_tracker) || made_mutable; + } + } + + return made_mutable; } /**************************/ @@ -5251,6 +4375,22 @@ void RenderingDevice::_free_internal(RID p_id) { // Push everything so it's disposed of next time this frame index is processed (means, it's safe to do it). if (texture_owner.owns(p_id)) { Texture *texture = texture_owner.get_or_null(p_id); + RDG::ResourceTracker *draw_tracker = texture->draw_tracker; + if (draw_tracker != nullptr) { + draw_tracker->reference_count--; + if (draw_tracker->reference_count == 0) { + RDG::resource_tracker_free(draw_tracker); + + if (texture->owner.is_valid() && (texture->slice_type != TEXTURE_SLICE_MAX)) { + // If this was a texture slice, erase the tracker from the map. + Texture *owner_texture = texture_owner.get_or_null(texture->owner); + if (owner_texture != nullptr) { + owner_texture->slice_trackers.erase(texture->slice_rect); + } + } + } + } + frames[frame].textures_to_dispose_of.push_back(*texture); texture_owner.free(p_id); } else if (framebuffer_owner.owns(p_id)) { @@ -5268,12 +4408,14 @@ void RenderingDevice::_free_internal(RID p_id) { sampler_owner.free(p_id); } else if (vertex_buffer_owner.owns(p_id)) { Buffer *vertex_buffer = vertex_buffer_owner.get_or_null(p_id); + RDG::resource_tracker_free(vertex_buffer->draw_tracker); frames[frame].buffers_to_dispose_of.push_back(*vertex_buffer); vertex_buffer_owner.free(p_id); } else if (vertex_array_owner.owns(p_id)) { vertex_array_owner.free(p_id); } else if (index_buffer_owner.owns(p_id)) { IndexBuffer *index_buffer = index_buffer_owner.get_or_null(p_id); + RDG::resource_tracker_free(index_buffer->draw_tracker); frames[frame].buffers_to_dispose_of.push_back(*index_buffer); index_buffer_owner.free(p_id); } else if (index_array_owner.owns(p_id)) { @@ -5286,14 +4428,17 @@ void RenderingDevice::_free_internal(RID p_id) { shader_owner.free(p_id); } else if (uniform_buffer_owner.owns(p_id)) { Buffer *uniform_buffer = uniform_buffer_owner.get_or_null(p_id); + RDG::resource_tracker_free(uniform_buffer->draw_tracker); frames[frame].buffers_to_dispose_of.push_back(*uniform_buffer); uniform_buffer_owner.free(p_id); } else if (texture_buffer_owner.owns(p_id)) { Buffer *texture_buffer = texture_buffer_owner.get_or_null(p_id); + RDG::resource_tracker_free(texture_buffer->draw_tracker); frames[frame].buffers_to_dispose_of.push_back(*texture_buffer); texture_buffer_owner.free(p_id); } else if (storage_buffer_owner.owns(p_id)) { Buffer *storage_buffer = storage_buffer_owner.get_or_null(p_id); + RDG::resource_tracker_free(storage_buffer->draw_tracker); frames[frame].buffers_to_dispose_of.push_back(*storage_buffer); storage_buffer_owner.free(p_id); } else if (uniform_set_owner.owns(p_id)) { @@ -5370,18 +4515,21 @@ void RenderingDevice::set_resource_name(RID p_id, const String &p_name) { } void RenderingDevice::draw_command_begin_label(String p_label_name, const Color &p_color) { - _THREAD_SAFE_METHOD_ - context->command_begin_label(frames[frame].draw_command_buffer, p_label_name, p_color); + if (!context->is_debug_utils_enabled()) { + return; + } + + draw_graph.begin_label(p_label_name, p_color); } +#ifndef DISABLE_DEPRECATED void RenderingDevice::draw_command_insert_label(String p_label_name, const Color &p_color) { - _THREAD_SAFE_METHOD_ - context->command_insert_label(frames[frame].draw_command_buffer, p_label_name, p_color); + WARN_PRINT("Deprecated. Inserting labels no longer applies due to command reordering."); } +#endif void RenderingDevice::draw_command_end_label() { - _THREAD_SAFE_METHOD_ - context->command_end_label(frames[frame].draw_command_buffer); + draw_graph.end_label(); } String RenderingDevice::get_device_vendor_name() const { @@ -5404,7 +4552,7 @@ String RenderingDevice::get_device_pipeline_cache_uuid() const { return context->get_device_pipeline_cache_uuid(); } -void RenderingDevice::_finalize_command_bufers() { +void RenderingDevice::_finalize_command_buffers(bool p_postpare) { if (draw_list) { ERR_PRINT("Found open draw list at the end of the frame, this should never happen (further drawing will likely not work)."); } @@ -5413,7 +4561,13 @@ void RenderingDevice::_finalize_command_bufers() { ERR_PRINT("Found open compute list at the end of the frame, this should never happen (further compute will likely not work)."); } - { + { // Complete the setup buffer (that needs to be processed before anything else). + draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); + + if (p_postpare) { + context->postpare_buffers(frames[frame].draw_command_buffer); + } + driver->end_segment(); driver->command_buffer_end(frames[frame].setup_command_buffer); driver->command_buffer_end(frames[frame].draw_command_buffer); @@ -5421,6 +4575,8 @@ void RenderingDevice::_finalize_command_bufers() { } void RenderingDevice::_begin_frame() { + draw_graph.begin(); + // Erase pending resources. _free_pending_resources(frame); @@ -5464,12 +4620,15 @@ void RenderingDevice::swap_buffers() { ERR_FAIL_COND_MSG(local_device.is_valid(), "Local devices can't swap buffers."); _THREAD_SAFE_METHOD_ - context->postpare_buffers(frames[frame].draw_command_buffer); - _finalize_command_bufers(); + _finalize_command_buffers(true); - screen_prepared = false; // Swap buffers. - context->swap_buffers(); + if (!screen_prepared) { + context->flush(true, true, false); + } else { + screen_prepared = false; + context->swap_buffers(); + } frame = (frame + 1) % frame_count; @@ -5482,7 +4641,7 @@ void RenderingDevice::submit() { ERR_FAIL_COND_MSG(local_device.is_null(), "Only local devices can submit and sync."); ERR_FAIL_COND_MSG(local_device_processing, "device already submitted, call sync to wait until done."); - _finalize_command_bufers(); + _finalize_command_buffers(false); RDD::CommandBufferID command_buffers[2] = { frames[frame].setup_command_buffer, frames[frame].draw_command_buffer }; context->local_device_push_command_buffers(local_device, command_buffers, 2); @@ -5614,11 +4773,14 @@ void RenderingDevice::_flush(bool p_current_frame) { if (local_device.is_valid() && !p_current_frame) { return; // Flushing previous frames has no effect with local device. } + // Not doing this crashes RADV (undefined behavior). if (p_current_frame) { + draw_graph.end(frames[frame].draw_command_buffer, RENDER_GRAPH_REORDER, RENDER_GRAPH_FULL_BARRIERS); driver->end_segment(); driver->command_buffer_end(frames[frame].setup_command_buffer); driver->command_buffer_end(frames[frame].draw_command_buffer); + draw_graph.begin(); } if (local_device.is_valid()) { @@ -5739,7 +4901,6 @@ void RenderingDevice::initialize(ApiContextRD *p_context, bool p_local_device) { draw_list = nullptr; draw_list_count = 0; - draw_list_split = false; compute_list = nullptr; @@ -5756,6 +4917,8 @@ void RenderingDevice::initialize(ApiContextRD *p_context, bool p_local_device) { pipelines_cache_size = driver->pipeline_cache_query_size(); print_verbose(vformat("Startup PSO cache (%.1f MiB)", pipelines_cache_size / (1024.0f * 1024.0f))); } + + draw_graph.initialize(driver, frame_count, SECONDARY_COMMAND_BUFFERS_PER_FRAME); } Vector<uint8_t> RenderingDevice::_load_pipeline_cache() { @@ -5853,46 +5016,11 @@ void RenderingDevice::_free_rids(T &p_owner, const char *p_type) { void RenderingDevice::capture_timestamp(const String &p_name) { ERR_FAIL_COND_MSG(draw_list != nullptr, "Capturing timestamps during draw list creation is not allowed. Offending timestamp was: " + p_name); + ERR_FAIL_COND_MSG(compute_list != nullptr, "Capturing timestamps during compute list creation is not allowed. Offending timestamp was: " + p_name); ERR_FAIL_COND(frames[frame].timestamp_count >= max_timestamp_query_elements); - // This should be optional for profiling, else it will slow things down. - if (driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS)) { - RDD::MemoryBarrier mb; - mb.src_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | - RDD::BARRIER_ACCESS_INDEX_READ_BIT | - RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | - RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | - RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | - RDD::BARRIER_ACCESS_HOST_READ_BIT | - RDD::BARRIER_ACCESS_HOST_WRITE_BIT); - mb.dst_access = (RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT | - RDD::BARRIER_ACCESS_INDEX_READ_BIT | - RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT | - RDD::BARRIER_ACCESS_UNIFORM_READ_BIT | - RDD::BARRIER_ACCESS_INPUT_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_READ_BIT | - RDD::BARRIER_ACCESS_SHADER_WRITE_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | - RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT | - RDD::BARRIER_ACCESS_TRANSFER_READ_BIT | - RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT | - RDD::BARRIER_ACCESS_HOST_READ_BIT | - RDD::BARRIER_ACCESS_HOST_WRITE_BIT); - - driver->command_pipeline_barrier(frames[frame].draw_command_buffer, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT, mb, {}, {}); - } - - driver->command_timestamp_write(frames[frame].draw_command_buffer, frames[frame].timestamp_pool, frames[frame].timestamp_count); + draw_graph.add_capture_timestamp(frames[frame].timestamp_pool, frames[frame].timestamp_count); + frames[frame].timestamp_names[frames[frame].timestamp_count] = p_name; frames[frame].timestamp_cpu_values[frames[frame].timestamp_count] = OS::get_singleton()->get_ticks_usec(); frames[frame].timestamp_count++; @@ -6060,10 +5188,6 @@ void RenderingDevice::finalize() { driver->pipeline_cache_free(); } - for (int i = 0; i < split_draw_list_allocators.size(); i++) { - driver->command_pool_free(split_draw_list_allocators[i].command_pool); - } - frames.clear(); for (int i = 0; i < staging_buffer_blocks.size(); i++) { @@ -6102,7 +5226,7 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_create_shared_from_slice", "view", "with_texture", "layer", "mipmap", "mipmaps", "slice_type"), &RenderingDevice::_texture_create_shared_from_slice, DEFVAL(1), DEFVAL(TEXTURE_SLICE_2D)); ClassDB::bind_method(D_METHOD("texture_create_from_extension", "type", "format", "samples", "usage_flags", "image", "width", "height", "depth", "layers"), &RenderingDevice::texture_create_from_extension); - ClassDB::bind_method(D_METHOD("texture_update", "texture", "layer", "data", "post_barrier"), &RenderingDevice::texture_update, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("texture_update", "texture", "layer", "data"), &RenderingDevice::texture_update); ClassDB::bind_method(D_METHOD("texture_get_data", "texture", "layer"), &RenderingDevice::texture_get_data); ClassDB::bind_method(D_METHOD("texture_is_format_supported_for_usage", "format", "usage_flags"), &RenderingDevice::texture_is_format_supported_for_usage); @@ -6110,9 +5234,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("texture_is_shared", "texture"), &RenderingDevice::texture_is_shared); ClassDB::bind_method(D_METHOD("texture_is_valid", "texture"), &RenderingDevice::texture_is_valid); - ClassDB::bind_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer", "post_barrier"), &RenderingDevice::texture_copy, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); - ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count", "post_barrier"), &RenderingDevice::texture_clear, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); - ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture", "post_barrier"), &RenderingDevice::texture_resolve_multisample, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("texture_copy", "from_texture", "to_texture", "from_pos", "to_pos", "size", "src_mipmap", "dst_mipmap", "src_layer", "dst_layer"), &RenderingDevice::texture_copy); + ClassDB::bind_method(D_METHOD("texture_clear", "texture", "color", "base_mipmap", "mipmap_count", "base_layer", "layer_count"), &RenderingDevice::texture_clear); + ClassDB::bind_method(D_METHOD("texture_resolve_multisample", "from_texture", "to_texture"), &RenderingDevice::texture_resolve_multisample); ClassDB::bind_method(D_METHOD("texture_get_format", "texture"), &RenderingDevice::_texture_get_format); #ifndef DISABLE_DEPRECATED @@ -6154,8 +5278,9 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("uniform_set_create", "uniforms", "shader", "shader_set"), &RenderingDevice::_uniform_set_create); ClassDB::bind_method(D_METHOD("uniform_set_is_valid", "uniform_set"), &RenderingDevice::uniform_set_is_valid); - ClassDB::bind_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data", "post_barrier"), &RenderingDevice::_buffer_update_bind, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); - ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes", "post_barrier"), &RenderingDevice::buffer_clear, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("buffer_copy", "src_buffer", "dst_buffer", "src_offset", "dst_offset", "size"), &RenderingDevice::buffer_copy); + ClassDB::bind_method(D_METHOD("buffer_update", "buffer", "offset", "size_bytes", "data"), &RenderingDevice::_buffer_update_bind); + ClassDB::bind_method(D_METHOD("buffer_clear", "buffer", "offset", "size_bytes"), &RenderingDevice::buffer_clear); ClassDB::bind_method(D_METHOD("buffer_get_data", "buffer", "offset_bytes", "size_bytes"), &RenderingDevice::buffer_get_data, DEFVAL(0), DEFVAL(0)); ClassDB::bind_method(D_METHOD("render_pipeline_create", "shader", "framebuffer_format", "vertex_format", "primitive", "rasterization_state", "multisample_state", "stencil_state", "color_blend_state", "dynamic_state_flags", "for_render_pass", "specialization_constants"), &RenderingDevice::_render_pipeline_create, DEFVAL(0), DEFVAL(0), DEFVAL(TypedArray<RDPipelineSpecializationConstant>())); @@ -6170,8 +5295,10 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_list_begin_for_screen", "screen", "clear_color"), &RenderingDevice::draw_list_begin_for_screen, DEFVAL(DisplayServer::MAIN_WINDOW_ID), DEFVAL(Color())); - ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>())); + ClassDB::bind_method(D_METHOD("draw_list_begin", "framebuffer", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region"), &RenderingDevice::draw_list_begin, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2())); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("draw_list_begin_split", "framebuffer", "splits", "initial_color_action", "final_color_action", "initial_depth_action", "final_depth_action", "clear_color_values", "clear_depth", "clear_stencil", "region", "storage_textures"), &RenderingDevice::_draw_list_begin_split, DEFVAL(Vector<Color>()), DEFVAL(1.0), DEFVAL(0), DEFVAL(Rect2()), DEFVAL(TypedArray<RID>())); +#endif ClassDB::bind_method(D_METHOD("draw_list_set_blend_constants", "draw_list", "color"), &RenderingDevice::draw_list_set_blend_constants); ClassDB::bind_method(D_METHOD("draw_list_bind_render_pipeline", "draw_list", "render_pipeline"), &RenderingDevice::draw_list_bind_render_pipeline); @@ -6186,17 +5313,19 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("draw_list_disable_scissor", "draw_list"), &RenderingDevice::draw_list_disable_scissor); ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass"), &RenderingDevice::draw_list_switch_to_next_pass); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("draw_list_switch_to_next_pass_split", "splits"), &RenderingDevice::_draw_list_switch_to_next_pass_split); +#endif - ClassDB::bind_method(D_METHOD("draw_list_end", "post_barrier"), &RenderingDevice::draw_list_end, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("draw_list_end"), &RenderingDevice::draw_list_end); - ClassDB::bind_method(D_METHOD("compute_list_begin", "allow_draw_overlap"), &RenderingDevice::compute_list_begin, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("compute_list_begin"), &RenderingDevice::compute_list_begin); ClassDB::bind_method(D_METHOD("compute_list_bind_compute_pipeline", "compute_list", "compute_pipeline"), &RenderingDevice::compute_list_bind_compute_pipeline); ClassDB::bind_method(D_METHOD("compute_list_set_push_constant", "compute_list", "buffer", "size_bytes"), &RenderingDevice::_compute_list_set_push_constant); ClassDB::bind_method(D_METHOD("compute_list_bind_uniform_set", "compute_list", "uniform_set", "set_index"), &RenderingDevice::compute_list_bind_uniform_set); ClassDB::bind_method(D_METHOD("compute_list_dispatch", "compute_list", "x_groups", "y_groups", "z_groups"), &RenderingDevice::compute_list_dispatch); ClassDB::bind_method(D_METHOD("compute_list_add_barrier", "compute_list"), &RenderingDevice::compute_list_add_barrier); - ClassDB::bind_method(D_METHOD("compute_list_end", "post_barrier"), &RenderingDevice::compute_list_end, DEFVAL(BARRIER_MASK_ALL_BARRIERS)); + ClassDB::bind_method(D_METHOD("compute_list_end"), &RenderingDevice::compute_list_end); ClassDB::bind_method(D_METHOD("free_rid", "rid"), &RenderingDevice::free); @@ -6212,15 +5341,19 @@ void RenderingDevice::_bind_methods() { ClassDB::bind_method(D_METHOD("submit"), &RenderingDevice::submit); ClassDB::bind_method(D_METHOD("sync"), &RenderingDevice::sync); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("barrier", "from", "to"), &RenderingDevice::barrier, DEFVAL(BARRIER_MASK_ALL_BARRIERS), DEFVAL(BARRIER_MASK_ALL_BARRIERS)); ClassDB::bind_method(D_METHOD("full_barrier"), &RenderingDevice::full_barrier); +#endif ClassDB::bind_method(D_METHOD("create_local_device"), &RenderingDevice::create_local_device); ClassDB::bind_method(D_METHOD("set_resource_name", "id", "name"), &RenderingDevice::set_resource_name); ClassDB::bind_method(D_METHOD("draw_command_begin_label", "name", "color"), &RenderingDevice::draw_command_begin_label); +#ifndef DISABLE_DEPRECATED ClassDB::bind_method(D_METHOD("draw_command_insert_label", "name", "color"), &RenderingDevice::draw_command_insert_label); +#endif ClassDB::bind_method(D_METHOD("draw_command_end_label"), &RenderingDevice::draw_command_end_label); ClassDB::bind_method(D_METHOD("get_device_vendor_name"), &RenderingDevice::get_device_vendor_name); @@ -6487,6 +5620,7 @@ void RenderingDevice::_bind_methods() { BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM); BIND_ENUM_CONSTANT(DATA_FORMAT_MAX); +#ifndef DISABLE_DEPRECATED BIND_BITFIELD_FLAG(BARRIER_MASK_VERTEX); BIND_BITFIELD_FLAG(BARRIER_MASK_FRAGMENT); BIND_BITFIELD_FLAG(BARRIER_MASK_COMPUTE); @@ -6494,6 +5628,7 @@ void RenderingDevice::_bind_methods() { BIND_BITFIELD_FLAG(BARRIER_MASK_RASTER); BIND_BITFIELD_FLAG(BARRIER_MASK_ALL_BARRIERS); BIND_BITFIELD_FLAG(BARRIER_MASK_NO_BARRIER); +#endif BIND_ENUM_CONSTANT(TEXTURE_TYPE_1D); BIND_ENUM_CONSTANT(TEXTURE_TYPE_2D); @@ -6668,18 +5803,25 @@ void RenderingDevice::_bind_methods() { BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_WRITE_MASK); BIND_BITFIELD_FLAG(DYNAMIC_STATE_STENCIL_REFERENCE); - BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR); //start rendering and clear the framebuffer (supply params) - BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION); //start rendering and clear the framebuffer (supply params) - BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION_CONTINUE); //continue rendering and clear the framebuffer (supply params) - BIND_ENUM_CONSTANT(INITIAL_ACTION_KEEP); //start rendering); but keep attached color texture contents (depth will be cleared) - BIND_ENUM_CONSTANT(INITIAL_ACTION_DROP); //start rendering); ignore what is there); just write above it - BIND_ENUM_CONSTANT(INITIAL_ACTION_CONTINUE); //continue rendering (framebuffer must have been left in "continue" state as final action previously) + BIND_ENUM_CONSTANT(INITIAL_ACTION_LOAD); + BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR); + BIND_ENUM_CONSTANT(INITIAL_ACTION_DISCARD); BIND_ENUM_CONSTANT(INITIAL_ACTION_MAX); +#ifndef DISABLE_DEPRECATED + BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION); + BIND_ENUM_CONSTANT(INITIAL_ACTION_CLEAR_REGION_CONTINUE); + BIND_ENUM_CONSTANT(INITIAL_ACTION_KEEP); + BIND_ENUM_CONSTANT(INITIAL_ACTION_DROP); + BIND_ENUM_CONSTANT(INITIAL_ACTION_CONTINUE); +#endif - BIND_ENUM_CONSTANT(FINAL_ACTION_READ); //will no longer render to it); allows attached textures to be read again); but depth buffer contents will be dropped (Can't be read from) - BIND_ENUM_CONSTANT(FINAL_ACTION_DISCARD); // discard contents after rendering - BIND_ENUM_CONSTANT(FINAL_ACTION_CONTINUE); //will continue rendering later); attached textures can't be read until re-bound with "finish" + BIND_ENUM_CONSTANT(FINAL_ACTION_STORE); + BIND_ENUM_CONSTANT(FINAL_ACTION_DISCARD); BIND_ENUM_CONSTANT(FINAL_ACTION_MAX); +#ifndef DISABLE_DEPRECATED + BIND_ENUM_CONSTANT(FINAL_ACTION_READ); + BIND_ENUM_CONSTANT(FINAL_ACTION_CONTINUE); +#endif BIND_ENUM_CONSTANT(SHADER_STAGE_VERTEX); BIND_ENUM_CONSTANT(SHADER_STAGE_FRAGMENT); @@ -6946,8 +6088,8 @@ RID RenderingDevice::_uniform_set_create(const TypedArray<RDUniform> &p_uniforms return uniform_set_create(uniforms, p_shader, p_shader_set); } -Error RenderingDevice::_buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier) { - return buffer_update(p_buffer, p_offset, p_size, p_data.ptr(), p_post_barrier); +Error RenderingDevice::_buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data) { + return buffer_update(p_buffer, p_offset, p_size, p_data.ptr()); } static Vector<RenderingDevice::PipelineSpecializationConstant> _get_spec_constants(const TypedArray<RDPipelineSpecializationConstant> &p_constants) { @@ -7018,47 +6160,15 @@ RID RenderingDevice::_compute_pipeline_create(RID p_shader, const TypedArray<RDP return compute_pipeline_create(p_shader, _get_spec_constants(p_specialization_constants)); } -RenderingDevice::DrawListID RenderingDevice::_draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { - Vector<RID> stextures; - for (int i = 0; i < p_storage_textures.size(); i++) { - stextures.push_back(p_storage_textures[i]); - } - return draw_list_begin(p_framebuffer, p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); -} - +#ifndef DISABLE_DEPRECATED Vector<int64_t> RenderingDevice::_draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures) { - Vector<DrawListID> splits; - splits.resize(p_splits); - Vector<RID> stextures; - for (int i = 0; i < p_storage_textures.size(); i++) { - stextures.push_back(p_storage_textures[i]); - } - draw_list_begin_split(p_framebuffer, p_splits, splits.ptrw(), p_initial_color_action, p_final_color_action, p_initial_depth_action, p_final_depth_action, p_clear_color_values, p_clear_depth, p_clear_stencil, p_region, stextures); - - Vector<int64_t> split_ids; - split_ids.resize(splits.size()); - for (int i = 0; i < splits.size(); i++) { - split_ids.write[i] = splits[i]; - } - - return split_ids; + ERR_FAIL_V_MSG(Vector<int64_t>(), "Deprecated. Split draw lists are used automatically by RenderingDevice."); } Vector<int64_t> RenderingDevice::_draw_list_switch_to_next_pass_split(uint32_t p_splits) { - Vector<DrawListID> splits; - splits.resize(p_splits); - - Error err = draw_list_switch_to_next_pass_split(p_splits, splits.ptrw()); - ERR_FAIL_COND_V(err != OK, Vector<int64_t>()); - - Vector<int64_t> split_ids; - split_ids.resize(splits.size()); - for (int i = 0; i < splits.size(); i++) { - split_ids.write[i] = splits[i]; - } - - return split_ids; + ERR_FAIL_V_MSG(Vector<int64_t>(), "Deprecated. Split draw lists are used automatically by RenderingDevice."); } +#endif void RenderingDevice::_draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size) { ERR_FAIL_COND((uint32_t)p_data.size() > p_data_size); diff --git a/servers/rendering/rendering_device.h b/servers/rendering/rendering_device.h index 90ed2828c0..6c98ccfeda 100644 --- a/servers/rendering/rendering_device.h +++ b/servers/rendering/rendering_device.h @@ -42,6 +42,7 @@ #include "servers/rendering/rendering_device.h" #include "servers/rendering/rendering_device_commons.h" #include "servers/rendering/rendering_device_driver.h" +#include "servers/rendering/rendering_device_graph.h" class RDTextureFormat; class RDTextureView; @@ -93,6 +94,9 @@ public: uint32_t version_minor = 0; }; + typedef int64_t DrawListID; + typedef int64_t ComputeListID; + typedef String (*ShaderSPIRVGetCacheKeyFunction)(const RenderingDevice *p_render_device); typedef Vector<uint8_t> (*ShaderCompileToSPIRVFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language, String *r_error, const RenderingDevice *p_render_device); typedef Vector<uint8_t> (*ShaderCacheFunction)(ShaderStage p_stage, const String &p_source_code, ShaderLanguage p_language); @@ -131,8 +135,7 @@ public: ID_TYPE_FRAMEBUFFER_FORMAT, ID_TYPE_VERTEX_FORMAT, ID_TYPE_DRAW_LIST, - ID_TYPE_SPLIT_DRAW_LIST, - ID_TYPE_COMPUTE_LIST, + ID_TYPE_COMPUTE_LIST = 4, ID_TYPE_MAX, ID_BASE_SHIFT = 58, // 5 bits for ID types. ID_MASK = (ID_BASE_SHIFT - 1), @@ -145,25 +148,7 @@ private: void _add_dependency(RID p_id, RID p_depends_on); void _free_dependencies(RID p_id); - /*****************/ - /**** BARRIER ****/ - /*****************/ - -public: - enum BarrierMask { - BARRIER_MASK_VERTEX = 1, - BARRIER_MASK_FRAGMENT = 8, - BARRIER_MASK_COMPUTE = 2, - BARRIER_MASK_TRANSFER = 4, - - BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 9, - BARRIER_MASK_ALL_BARRIERS = 0x7FFF, // all flags set - BARRIER_MASK_NO_BARRIER = 0x8000, - }; - private: - void _full_barrier(bool p_sync_with_draw); - /***************************/ /**** BUFFER MANAGEMENT ****/ /***************************/ @@ -201,26 +186,34 @@ private: uint64_t staging_buffer_max_size = 0; bool staging_buffer_used = false; - Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, bool p_can_segment = true); + enum StagingRequiredAction { + STAGING_REQUIRED_ACTION_NONE, + STAGING_REQUIRED_ACTION_FLUSH_CURRENT, + STAGING_REQUIRED_ACTION_FLUSH_OLDER + }; + + Error _staging_buffer_allocate(uint32_t p_amount, uint32_t p_required_align, uint32_t &r_alloc_offset, uint32_t &r_alloc_size, StagingRequiredAction &r_required_action, bool p_can_segment = true); + void _staging_buffer_execute_required_action(StagingRequiredAction p_required_action); Error _insert_staging_block(); struct Buffer { RDD::BufferID driver_id; uint32_t size = 0; BitField<RDD::BufferUsageBits> usage; + RDG::ResourceTracker *draw_tracker = nullptr; }; - Buffer *_get_buffer_from_owner(RID p_buffer, BitField<RDD::PipelineStageBits> &r_stages, BitField<RDD::BarrierAccessBits> &r_access, BitField<BarrierMask> p_post_barrier); - Error _buffer_update(Buffer *p_buffer, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_command_buffer = false, uint32_t p_required_align = 32); + Buffer *_get_buffer_from_owner(RID p_buffer); + Error _buffer_update(Buffer *p_buffer, RID p_buffer_id, size_t p_offset, const uint8_t *p_data, size_t p_data_size, bool p_use_draw_queue = false, uint32_t p_required_align = 32); RID_Owner<Buffer> uniform_buffer_owner; RID_Owner<Buffer> storage_buffer_owner; RID_Owner<Buffer> texture_buffer_owner; public: - Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error buffer_copy(RID p_src_buffer, RID p_dst_buffer, uint32_t p_src_offset, uint32_t p_dst_offset, uint32_t p_size); + Error buffer_update(RID p_buffer, uint32_t p_offset, uint32_t p_size, const void *p_data); + Error buffer_clear(RID p_buffer, uint32_t p_offset, uint32_t p_size); Vector<uint8_t> buffer_get_data(RID p_buffer, uint32_t p_offset = 0, uint32_t p_size = 0); // This causes stall, only use to retrieve large buffers for saving. /*****************/ @@ -245,6 +238,8 @@ public: TextureType type = TEXTURE_TYPE_MAX; DataFormat format = DATA_FORMAT_MAX; TextureSamples samples = TEXTURE_SAMPLES_MAX; + TextureSliceType slice_type = TEXTURE_SLICE_MAX; + Rect2i slice_rect; uint32_t width = 0; uint32_t height = 0; uint32_t depth = 0; @@ -256,26 +251,33 @@ public: Vector<DataFormat> allowed_shared_formats; - RDD::TextureLayout layout = RDD::TEXTURE_LAYOUT_UNDEFINED; - - uint64_t used_in_frame = 0; - bool used_in_transfer = false; - bool used_in_raster = false; - bool used_in_compute = false; - bool is_resolve_buffer = false; + bool has_initial_data = false; BitField<RDD::TextureAspectBits> read_aspect_flags; BitField<RDD::TextureAspectBits> barrier_aspect_flags; - bool bound = false; // Bound to framebffer. + bool bound = false; // Bound to framebuffer. RID owner; + + RDG::ResourceTracker *draw_tracker = nullptr; + HashMap<Rect2i, RDG::ResourceTracker *> slice_trackers; + + RDD::TextureSubresourceRange barrier_range() const { + RDD::TextureSubresourceRange r; + r.aspect = barrier_aspect_flags; + r.base_mipmap = base_mipmap; + r.mipmap_count = mipmaps; + r.base_layer = base_layer; + r.layer_count = layers; + return r; + } }; RID_Owner<Texture> texture_owner; uint32_t texture_upload_region_size_px = 0; Vector<uint8_t> _texture_get_data(Texture *tex, uint32_t p_layer, bool p_2d = false); - Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier, bool p_use_setup_queue); + Error _texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, bool p_use_setup_queue, bool p_validate_can_update); public: struct TextureView { @@ -306,7 +308,7 @@ public: RID texture_create_shared(const TextureView &p_view, RID p_with_texture); RID texture_create_from_extension(TextureType p_type, DataFormat p_format, TextureSamples p_samples, BitField<RenderingDevice::TextureUsageBits> p_usage, uint64_t p_image, uint64_t p_width, uint64_t p_height, uint64_t p_depth, uint64_t p_layers); RID texture_create_shared_from_slice(const TextureView &p_view, RID p_with_texture, uint32_t p_layer, uint32_t p_mipmap, uint32_t p_mipmaps = 1, TextureSliceType p_slice_type = TEXTURE_SLICE_2D, uint32_t p_layers = 0); - Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error texture_update(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data); Vector<uint8_t> texture_get_data(RID p_texture, uint32_t p_layer); // CPU textures will return immediately, while GPU textures will most likely force a flush bool texture_is_format_supported_for_usage(DataFormat p_format, BitField<TextureUsageBits> p_usage) const; @@ -318,29 +320,36 @@ public: uint64_t texture_get_native_handle(RID p_texture); #endif - Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); - Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error texture_copy(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer); + Error texture_clear(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers); + Error texture_resolve_multisample(RID p_from_texture, RID p_to_texture); /************************/ /**** DRAW LISTS (I) ****/ /************************/ enum InitialAction { - INITIAL_ACTION_CLEAR, // Start rendering and clear the whole framebuffer. - INITIAL_ACTION_CLEAR_REGION, // Start rendering and clear the framebuffer in the specified region. - INITIAL_ACTION_CLEAR_REGION_CONTINUE, // Continue rendering and clear the framebuffer in the specified region. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. - INITIAL_ACTION_KEEP, // Start rendering, but keep attached color texture contents. If the framebuffer was previously used to read in a shader, this will automatically insert a layout transition. - INITIAL_ACTION_DROP, // Start rendering, ignore what is there; write above it. In general, this is the fastest option when you will be writing every single pixel and you don't need a clear color. - INITIAL_ACTION_CONTINUE, // Continue rendering. Framebuffer must have been left in `FINAL_ACTION_CONTINUE` state as the final action previously. - INITIAL_ACTION_MAX + INITIAL_ACTION_LOAD, + INITIAL_ACTION_CLEAR, + INITIAL_ACTION_DISCARD, + INITIAL_ACTION_MAX, +#ifndef DISABLE_DEPRECATED + INITIAL_ACTION_CLEAR_REGION = INITIAL_ACTION_CLEAR, + INITIAL_ACTION_CLEAR_REGION_CONTINUE = INITIAL_ACTION_LOAD, + INITIAL_ACTION_KEEP = INITIAL_ACTION_LOAD, + INITIAL_ACTION_DROP = INITIAL_ACTION_DISCARD, + INITIAL_ACTION_CONTINUE = INITIAL_ACTION_LOAD, +#endif }; enum FinalAction { - FINAL_ACTION_READ, // Store the texture for reading and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). - FINAL_ACTION_DISCARD, // Discard the texture data and make it read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit (only applies to color, depth and stencil attachments). - FINAL_ACTION_CONTINUE, // Store the texture and continue for further processing. Similar to `FINAL_ACTION_READ`, but does not make the texture read-only if it has the `TEXTURE_USAGE_SAMPLING_BIT` bit. - FINAL_ACTION_MAX + FINAL_ACTION_STORE, + FINAL_ACTION_DISCARD, + FINAL_ACTION_MAX, +#ifndef DISABLE_DEPRECATED + FINAL_ACTION_READ = FINAL_ACTION_STORE, + FINAL_ACTION_CONTINUE = FINAL_ACTION_STORE, +#endif }; /*********************/ @@ -668,7 +677,9 @@ private: uint32_t max_instances_allowed = 0; Vector<RDD::BufferID> buffers; // Not owned, just referenced. + Vector<RDG::ResourceTracker *> draw_trackers; // Not owned, just referenced. Vector<uint64_t> offsets; + HashSet<RID> untracked_buffers; }; RID_Owner<VertexArray> vertex_array_owner; @@ -685,6 +696,7 @@ private: struct IndexArray { uint32_t max_index = 0; // Remember the maximum index here too, for validation. RDD::BufferID driver_id; // Not owned, inherited from index buffer. + RDG::ResourceTracker *draw_tracker = nullptr; // Not owned, inherited from index buffer. uint32_t offset = 0; uint32_t indices = 0; IndexBufferFormat format = INDEX_BUFFER_FORMAT_UINT16; @@ -762,6 +774,7 @@ private: String name; // Used for debug. RDD::ShaderID driver_id; uint32_t layout_hash = 0; + BitField<RDD::PipelineStageBits> stage_bits; Vector<uint32_t> set_formats; }; @@ -770,10 +783,42 @@ private: RID_Owner<Shader> shader_owner; #ifndef DISABLE_DEPRECATED - BitField<BarrierMask> _convert_barrier_mask_81356(BitField<BarrierMask> p_old_barrier); +public: + enum BarrierMask{ + BARRIER_MASK_VERTEX = 1, + BARRIER_MASK_FRAGMENT = 8, + BARRIER_MASK_COMPUTE = 2, + BARRIER_MASK_TRANSFER = 4, + + BARRIER_MASK_RASTER = BARRIER_MASK_VERTEX | BARRIER_MASK_FRAGMENT, // 9, + BARRIER_MASK_ALL_BARRIERS = 0x7FFF, // all flags set + BARRIER_MASK_NO_BARRIER = 0x8000, + }; + + void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS); + void full_barrier(); + void draw_command_insert_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); + Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); + Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); + Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>()); + Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); + +private: void _draw_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); void _compute_list_end_bind_compat_81356(BitField<BarrierMask> p_post_barrier); void _barrier_bind_compat_81356(BitField<BarrierMask> p_from, BitField<BarrierMask> p_to); + void _draw_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier); + void _compute_list_end_bind_compat_84976(BitField<BarrierMask> p_post_barrier); + InitialAction _convert_initial_action_84976(InitialAction p_old_initial_action); + FinalAction _convert_final_action_84976(FinalAction p_old_final_action); + DrawListID _draw_list_begin_bind_compat_84976(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values, float p_clear_depth, uint32_t p_clear_stencil, const Rect2 &p_region, const TypedArray<RID> &p_storage_textures); + ComputeListID _compute_list_begin_bind_compat_84976(bool p_allow_draw_overlap); + Error _buffer_update_bind_compat_84976(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier); + Error _buffer_clear_bind_compat_84976(RID p_buffer, uint32_t p_offset, uint32_t p_size, BitField<BarrierMask> p_post_barrier); + Error _texture_update_bind_compat_84976(RID p_texture, uint32_t p_layer, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier); + Error _texture_copy_bind_compat_84976(RID p_from_texture, RID p_to_texture, const Vector3 &p_from, const Vector3 &p_to, const Vector3 &p_size, uint32_t p_src_mipmap, uint32_t p_dst_mipmap, uint32_t p_src_layer, uint32_t p_dst_layer, BitField<BarrierMask> p_post_barrier); + Error _texture_clear_bind_compat_84976(RID p_texture, const Color &p_color, uint32_t p_base_mipmap, uint32_t p_mipmaps, uint32_t p_base_layer, uint32_t p_layers, BitField<BarrierMask> p_post_barrier); + Error _texture_resolve_multisample_bind_compat_84976(RID p_from_texture, RID p_to_texture, BitField<BarrierMask> p_post_barrier); #endif public: @@ -875,6 +920,9 @@ public: }; private: + static const uint32_t MAX_UNIFORM_SETS = 16; + static const uint32_t MAX_PUSH_CONSTANT_SIZE = 128; + // This structure contains the descriptor set. They _need_ to be allocated // for a shader (and will be erased when this shader is erased), but should // work for other shaders as long as the hash matches. This covers using @@ -894,8 +942,9 @@ private: }; LocalVector<AttachableTexture> attachable_textures; // Used for validation. - Vector<Texture *> mutable_sampled_textures; // Used for layout change. - Vector<Texture *> mutable_storage_textures; // Used for layout change. + Vector<RDG::ResourceTracker *> draw_trackers; + Vector<RDG::ResourceUsage> draw_trackers_usage; + HashMap<RID, RDG::ResourceUsage> untracked_usage; InvalidationCallback invalidated_callback = nullptr; void *invalidated_callback_userdata = nullptr; }; @@ -941,6 +990,7 @@ private: uint32_t shader_layout_hash = 0; Vector<uint32_t> set_formats; RDD::PipelineID driver_id; + BitField<RDD::PipelineStageBits> stage_bits; uint32_t push_constant_size = 0; }; @@ -986,8 +1036,6 @@ public: /**** DRAW LISTS (II) ****/ /*************************/ - typedef int64_t DrawListID; - private: // Draw list contains both the command buffer // used for drawing as well as a LOT of @@ -995,20 +1043,7 @@ private: // validation is cheap so most of it can // also run in release builds. - // When using split command lists, this is - // implemented internally using secondary command - // buffers. As they can be created in threads, - // each needs its own command pool. - - struct SplitDrawListAllocator { - RDD::CommandPoolID command_pool; - Vector<RDD::CommandBufferID> command_buffers; // One for each frame. - }; - - Vector<SplitDrawListAllocator> split_draw_list_allocators; - struct DrawList { - RDD::CommandBufferID command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. Rect2i viewport; bool viewport_set = false; @@ -1040,9 +1075,8 @@ private: uint32_t vertex_array_size = 0; uint32_t vertex_max_instances_allowed = 0xFFFFFFFF; bool index_buffer_uses_restart_indices = false; - uint32_t index_array_size = 0; + uint32_t index_array_count = 0; uint32_t index_array_max_index = 0; - uint32_t index_array_offset = 0; Vector<uint32_t> set_formats; Vector<bool> set_bound; Vector<RID> set_rids; @@ -1060,13 +1094,12 @@ private: #else struct Validation { uint32_t vertex_array_size = 0; - uint32_t index_array_size = 0; - uint32_t index_array_offset; + uint32_t index_array_count = 0; } validation; #endif }; - DrawList *draw_list = nullptr; // One for regular draw lists, multiple for split. + DrawList *draw_list = nullptr; uint32_t draw_list_subpass_count = 0; uint32_t draw_list_count = 0; RDD::RenderPassID draw_list_render_pass; @@ -1076,23 +1109,20 @@ private: #endif uint32_t draw_list_current_subpass = 0; - bool draw_list_split = false; Vector<RID> draw_list_bound_textures; - Vector<RID> draw_list_storage_textures; - bool draw_list_unbind_color_textures = false; - bool draw_list_unbind_depth_textures = false; void _draw_list_insert_clear_region(DrawList *p_draw_list, Framebuffer *p_framebuffer, Point2i p_viewport_offset, Point2i p_viewport_size, bool p_clear_color, const Vector<Color> &p_clear_colors, bool p_clear_depth, float p_depth, uint32_t p_stencil); Error _draw_list_setup_framebuffer(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, RDD::FramebufferID *r_framebuffer, RDD::RenderPassID *r_render_pass, uint32_t *r_subpass_count); - Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass, RDD::CommandBufferID p_command_buffer, RDD::CommandBufferType p_cmd_buffer_mode, const Vector<RID> &p_storage_textures, bool p_constrained_to_region); + Error _draw_list_render_pass_begin(Framebuffer *p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_colors, float p_clear_depth, uint32_t p_clear_stencil, Point2i p_viewport_offset, Point2i p_viewport_size, RDD::FramebufferID p_framebuffer_driver_id, RDD::RenderPassID p_render_pass); + void _draw_list_set_viewport(Rect2i p_rect); + void _draw_list_set_scissor(Rect2i p_rect); _FORCE_INLINE_ DrawList *_get_draw_list_ptr(DrawListID p_id); - Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_splits, uint32_t p_subpass); + Error _draw_list_allocate(const Rect2i &p_viewport, uint32_t p_subpass); void _draw_list_free(Rect2i *r_last_viewport = nullptr); public: DrawListID draw_list_begin_for_screen(DisplayServer::WindowID p_screen = 0, const Color &p_clear_color = Color()); - DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); - Error draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, DrawListID *r_split_ids, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const Vector<RID> &p_storage_textures = Vector<RID>()); + DrawListID draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2()); void draw_list_set_blend_constants(DrawListID p_list, const Color &p_color); void draw_list_bind_render_pipeline(DrawListID p_list, RID p_render_pipeline); @@ -1109,20 +1139,15 @@ public: uint32_t draw_list_get_current_pass(); DrawListID draw_list_switch_to_next_pass(); - Error draw_list_switch_to_next_pass_split(uint32_t p_splits, DrawListID *r_split_ids); - void draw_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + void draw_list_end(); +private: /***********************/ /**** COMPUTE LISTS ****/ /***********************/ - typedef int64_t ComputeListID; - -private: struct ComputeList { - RDD::CommandBufferID command_buffer; // If persistent, this is owned, otherwise it's shared with the ringbuffer. - struct SetState { uint32_t pipeline_expected_format = 0; uint32_t uniform_set_format = 0; @@ -1132,7 +1157,6 @@ private: }; struct State { - HashSet<Texture *> textures_to_sampled_layout; SetState sets[MAX_UNIFORM_SETS]; uint32_t set_count = 0; RID pipeline; @@ -1140,7 +1164,8 @@ private: RDD::ShaderID pipeline_shader_driver_id; uint32_t pipeline_shader_layout_hash = 0; uint32_t local_group_size[3] = { 0, 0, 0 }; - bool allow_draw_overlap; + uint8_t push_constant_data[MAX_PUSH_CONSTANT_SIZE] = {}; + uint32_t push_constant_size = 0; } state; #ifdef DEBUG_ENABLED @@ -1160,11 +1185,10 @@ private: }; ComputeList *compute_list = nullptr; - - void _compute_list_add_barrier(BitField<BarrierMask> p_post_barrier, BitField<RDD::PipelineStageBits> p_stages, BitField<RDD::BarrierAccessBits> p_access); + ComputeList::State compute_list_barrier_state; public: - ComputeListID compute_list_begin(bool p_allow_draw_overlap = false); + ComputeListID compute_list_begin(); void compute_list_bind_compute_pipeline(ComputeListID p_list, RID p_compute_pipeline); void compute_list_bind_uniform_set(ComputeListID p_list, RID p_uniform_set, uint32_t p_index); void compute_list_set_push_constant(ComputeListID p_list, const void *p_data, uint32_t p_data_size); @@ -1173,10 +1197,22 @@ public: void compute_list_dispatch_indirect(ComputeListID p_list, RID p_buffer, uint32_t p_offset); void compute_list_add_barrier(ComputeListID p_list); - void compute_list_end(BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + void compute_list_end(); - void barrier(BitField<BarrierMask> p_from = BARRIER_MASK_ALL_BARRIERS, BitField<BarrierMask> p_to = BARRIER_MASK_ALL_BARRIERS); - void full_barrier(); +private: + /***********************/ + /**** COMMAND GRAPH ****/ + /***********************/ + + bool _texture_make_mutable(Texture *p_texture, RID p_texture_id); + bool _buffer_make_mutable(Buffer *p_buffer, RID p_buffer_id); + bool _vertex_array_make_mutable(VertexArray *p_vertex_array, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker); + bool _index_array_make_mutable(IndexArray *p_index_array, RDG::ResourceTracker *p_resource_tracker); + bool _uniform_set_make_mutable(UniformSet *p_uniform_set, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker); + bool _dependency_make_mutable(RID p_id, RID p_resource_id, RDG::ResourceTracker *p_resource_tracker); + bool _dependencies_make_mutable(RID p_id, RDG::ResourceTracker *p_resource_tracker); + + RenderingDeviceGraph draw_graph; /**************************/ /**** FRAME MANAGEMENT ****/ @@ -1258,7 +1294,7 @@ private: template <class T> void _free_rids(T &p_owner, const char *p_type); - void _finalize_command_bufers(); + void _finalize_command_buffers(bool p_postpare); void _begin_frame(); #ifdef DEV_ENABLED @@ -1311,7 +1347,6 @@ public: void set_resource_name(RID p_id, const String &p_name); void draw_command_begin_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); - void draw_command_insert_label(String p_label_name, const Color &p_color = Color(1, 1, 1, 1)); void draw_command_end_label(); String get_device_vendor_name() const; @@ -1353,16 +1388,13 @@ private: RID _uniform_set_create(const TypedArray<RDUniform> &p_uniforms, RID p_shader, uint32_t p_shader_set); - Error _buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data, BitField<BarrierMask> p_post_barrier = BARRIER_MASK_ALL_BARRIERS); + Error _buffer_update_bind(RID p_buffer, uint32_t p_offset, uint32_t p_size, const Vector<uint8_t> &p_data); RID _render_pipeline_create(RID p_shader, FramebufferFormatID p_framebuffer_format, VertexFormatID p_vertex_format, RenderPrimitive p_render_primitive, const Ref<RDPipelineRasterizationState> &p_rasterization_state, const Ref<RDPipelineMultisampleState> &p_multisample_state, const Ref<RDPipelineDepthStencilState> &p_depth_stencil_state, const Ref<RDPipelineColorBlendState> &p_blend_state, BitField<PipelineDynamicStateFlags> p_dynamic_state_flags, uint32_t p_for_render_pass, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants); RID _compute_pipeline_create(RID p_shader, const TypedArray<RDPipelineSpecializationConstant> &p_specialization_constants); - DrawListID _draw_list_begin(RID p_framebuffer, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>()); - Vector<int64_t> _draw_list_begin_split(RID p_framebuffer, uint32_t p_splits, InitialAction p_initial_color_action, FinalAction p_final_color_action, InitialAction p_initial_depth_action, FinalAction p_final_depth_action, const Vector<Color> &p_clear_color_values = Vector<Color>(), float p_clear_depth = 1.0, uint32_t p_clear_stencil = 0, const Rect2 &p_region = Rect2(), const TypedArray<RID> &p_storage_textures = TypedArray<RID>()); void _draw_list_set_push_constant(DrawListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); void _compute_list_set_push_constant(ComputeListID p_list, const Vector<uint8_t> &p_data, uint32_t p_data_size); - Vector<int64_t> _draw_list_switch_to_next_pass_split(uint32_t p_splits); }; VARIANT_ENUM_CAST(RenderingDevice::DeviceType) @@ -1371,7 +1403,6 @@ VARIANT_ENUM_CAST(RenderingDevice::ShaderStage) VARIANT_ENUM_CAST(RenderingDevice::ShaderLanguage) VARIANT_ENUM_CAST(RenderingDevice::CompareOperator) VARIANT_ENUM_CAST(RenderingDevice::DataFormat) -VARIANT_BITFIELD_CAST(RenderingDevice::BarrierMask); VARIANT_ENUM_CAST(RenderingDevice::TextureType) VARIANT_ENUM_CAST(RenderingDevice::TextureSamples) VARIANT_BITFIELD_CAST(RenderingDevice::TextureUsageBits) @@ -1399,6 +1430,10 @@ VARIANT_ENUM_CAST(RenderingDevice::Limit) VARIANT_ENUM_CAST(RenderingDevice::MemoryType) VARIANT_ENUM_CAST(RenderingDevice::Features) +#ifndef DISABLE_DEPRECATED +VARIANT_BITFIELD_CAST(RenderingDevice::BarrierMask); +#endif + typedef RenderingDevice RD; #endif // RENDERING_DEVICE_H diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h index dabd0c0867..a8936f8cca 100644 --- a/servers/rendering/rendering_device_commons.h +++ b/servers/rendering/rendering_device_commons.h @@ -379,6 +379,7 @@ public: TEXTURE_SLICE_CUBEMAP, TEXTURE_SLICE_3D, TEXTURE_SLICE_2D_ARRAY, + TEXTURE_SLICE_MAX }; /*****************/ @@ -910,6 +911,7 @@ protected: Vector<Vector<ShaderUniform>> uniform_sets; Vector<ShaderSpecializationConstant> specialization_constants; + Vector<ShaderStage> stages; }; struct ShaderReflection : public ShaderDescription { diff --git a/servers/rendering/rendering_device_driver.h b/servers/rendering/rendering_device_driver.h index bb71a29bbc..663222e69d 100644 --- a/servers/rendering/rendering_device_driver.h +++ b/servers/rendering/rendering_device_driver.h @@ -181,6 +181,10 @@ public: BUFFER_USAGE_INDIRECT_BIT = (1 << 8), }; + enum { + BUFFER_WHOLE_SIZE = ~0ULL + }; + virtual BufferID buffer_create(uint64_t p_size, BitField<BufferUsageBits> p_usage, MemoryAllocationType p_allocation_type) = 0; // Only for a buffer with BUFFER_USAGE_TEXEL_BIT. virtual bool buffer_set_texel_format(BufferID p_buffer, DataFormat p_format) = 0; @@ -535,6 +539,8 @@ public: float depth; uint32_t stencil; }; + + RenderPassClearValue() {} }; struct AttachmentClear { @@ -621,6 +627,13 @@ public: virtual void command_timestamp_write(CommandBufferID p_cmd_buffer, QueryPoolID p_pool_id, uint32_t p_index) = 0; /****************/ + /**** LABELS ****/ + /****************/ + + virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) = 0; + virtual void command_end_label(CommandBufferID p_cmd_buffer) = 0; + + /****************/ /**** SCREEN ****/ /****************/ diff --git a/servers/rendering/rendering_device_graph.cpp b/servers/rendering/rendering_device_graph.cpp new file mode 100644 index 0000000000..bfacd38065 --- /dev/null +++ b/servers/rendering/rendering_device_graph.cpp @@ -0,0 +1,1930 @@ +/**************************************************************************/ +/* rendering_device_graph.cpp */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#include "rendering_device_graph.h" + +#define PRINT_RENDER_GRAPH 0 +#define FORCE_FULL_ACCESS_BITS 0 +#define PRINT_RESOURCE_TRACKER_TOTAL 0 + +RenderingDeviceGraph::RenderingDeviceGraph() { + // Default initialization. +} + +RenderingDeviceGraph::~RenderingDeviceGraph() { + _wait_for_secondary_command_buffer_tasks(); + + for (Frame &f : frames) { + for (SecondaryCommandBuffer &secondary : f.secondary_command_buffers) { + if (secondary.command_pool.id != 0) { + driver->command_pool_free(secondary.command_pool); + } + } + } +} + +bool RenderingDeviceGraph::_is_write_usage(ResourceUsage p_usage) { + switch (p_usage) { + case RESOURCE_USAGE_TRANSFER_FROM: + case RESOURCE_USAGE_UNIFORM_BUFFER_READ: + case RESOURCE_USAGE_INDIRECT_BUFFER_READ: + case RESOURCE_USAGE_TEXTURE_BUFFER_READ: + case RESOURCE_USAGE_STORAGE_BUFFER_READ: + case RESOURCE_USAGE_VERTEX_BUFFER_READ: + case RESOURCE_USAGE_INDEX_BUFFER_READ: + case RESOURCE_USAGE_TEXTURE_SAMPLE: + case RESOURCE_USAGE_STORAGE_IMAGE_READ: + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ: + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ: + return false; + case RESOURCE_USAGE_TRANSFER_TO: + case RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE: + case RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE: + case RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE: + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE: + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE: + return true; + default: + DEV_ASSERT(false && "Invalid resource tracker usage."); + return false; + } +} + +RDD::TextureLayout RenderingDeviceGraph::_usage_to_image_layout(ResourceUsage p_usage) { + switch (p_usage) { + case RESOURCE_USAGE_TRANSFER_FROM: + return RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL; + case RESOURCE_USAGE_TRANSFER_TO: + return RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL; + case RESOURCE_USAGE_TEXTURE_SAMPLE: + return RDD::TEXTURE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; + case RESOURCE_USAGE_STORAGE_IMAGE_READ: + case RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE: + return RDD::TEXTURE_LAYOUT_GENERAL; + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ: + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE: + return RDD::TEXTURE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ: + return RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_READ_ONLY_OPTIMAL; + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE: + return RDD::TEXTURE_LAYOUT_DEPTH_STENCIL_ATTACHMENT_OPTIMAL; + case RESOURCE_USAGE_NONE: + return RDD::TEXTURE_LAYOUT_UNDEFINED; + default: + DEV_ASSERT(false && "Invalid resource tracker usage or not an image usage."); + return RDD::TEXTURE_LAYOUT_UNDEFINED; + } +} + +RDD::BarrierAccessBits RenderingDeviceGraph::_usage_to_access_bits(ResourceUsage p_usage) { +#if FORCE_FULL_ACCESS_BITS + return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_MEMORY_READ_BIT | RDD::BARRIER_ACCESS_MEMORY_WRITE_BIT); +#else + switch (p_usage) { + case RESOURCE_USAGE_NONE: + return RDD::BarrierAccessBits(0); + case RESOURCE_USAGE_TRANSFER_FROM: + return RDD::BARRIER_ACCESS_TRANSFER_READ_BIT; + case RESOURCE_USAGE_TRANSFER_TO: + return RDD::BARRIER_ACCESS_TRANSFER_WRITE_BIT; + case RESOURCE_USAGE_UNIFORM_BUFFER_READ: + return RDD::BARRIER_ACCESS_UNIFORM_READ_BIT; + case RESOURCE_USAGE_INDIRECT_BUFFER_READ: + return RDD::BARRIER_ACCESS_INDIRECT_COMMAND_READ_BIT; + case RESOURCE_USAGE_STORAGE_BUFFER_READ: + case RESOURCE_USAGE_STORAGE_IMAGE_READ: + case RESOURCE_USAGE_TEXTURE_BUFFER_READ: + case RESOURCE_USAGE_TEXTURE_SAMPLE: + return RDD::BARRIER_ACCESS_SHADER_READ_BIT; + case RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE: + case RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE: + case RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE: + return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_SHADER_READ_BIT | RDD::BARRIER_ACCESS_SHADER_WRITE_BIT); + case RESOURCE_USAGE_VERTEX_BUFFER_READ: + return RDD::BARRIER_ACCESS_VERTEX_ATTRIBUTE_READ_BIT; + case RESOURCE_USAGE_INDEX_BUFFER_READ: + return RDD::BARRIER_ACCESS_INDEX_READ_BIT; + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ: + return RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT; + case RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE: + return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_COLOR_ATTACHMENT_WRITE_BIT); + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ: + return RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT; + case RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE: + return RDD::BarrierAccessBits(RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_READ_BIT | RDD::BARRIER_ACCESS_DEPTH_STENCIL_ATTACHMENT_WRITE_BIT); + default: + DEV_ASSERT(false && "Invalid usage."); + return RDD::BarrierAccessBits(0); + } +#endif +} + +int32_t RenderingDeviceGraph::_add_to_command_list(int32_t p_command_index, int32_t p_list_index) { + DEV_ASSERT(p_command_index < int32_t(command_count)); + DEV_ASSERT(p_list_index < int32_t(command_list_nodes.size())); + + int32_t next_index = int32_t(command_list_nodes.size()); + command_list_nodes.resize(next_index + 1); + + RecordedCommandListNode &new_node = command_list_nodes[next_index]; + new_node.command_index = p_command_index; + new_node.next_list_index = p_list_index; + return next_index; +} + +void RenderingDeviceGraph::_add_adjacent_command(int32_t p_previous_command_index, int32_t p_command_index, RecordedCommand *r_command) { + const uint32_t previous_command_data_offset = command_data_offsets[p_previous_command_index]; + RecordedCommand &previous_command = *reinterpret_cast<RecordedCommand *>(&command_data[previous_command_data_offset]); + previous_command.adjacent_command_list_index = _add_to_command_list(p_command_index, previous_command.adjacent_command_list_index); + r_command->src_stages = r_command->src_stages | previous_command.dst_stages; +} + +int32_t RenderingDeviceGraph::_add_to_write_list(int32_t p_command_index, Rect2i suberesources, int32_t p_list_index) { + DEV_ASSERT(p_command_index < int32_t(command_count)); + DEV_ASSERT(p_list_index < int32_t(write_list_nodes.size())); + + int32_t next_index = int32_t(write_list_nodes.size()); + write_list_nodes.resize(next_index + 1); + + RecordedWriteListNode &new_node = write_list_nodes[next_index]; + new_node.command_index = p_command_index; + new_node.next_list_index = p_list_index; + new_node.subresources = suberesources; + return next_index; +} + +RenderingDeviceGraph::RecordedCommand *RenderingDeviceGraph::_allocate_command(uint32_t p_command_size, int32_t &r_command_index) { + uint32_t command_data_offset = command_data.size(); + command_data_offsets.push_back(command_data_offset); + command_data.resize(command_data_offset + p_command_size); + r_command_index = command_count++; + RecordedCommand *new_command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); + *new_command = RecordedCommand(); + return new_command; +} + +RenderingDeviceGraph::DrawListInstruction *RenderingDeviceGraph::_allocate_draw_list_instruction(uint32_t p_instruction_size) { + uint32_t draw_list_data_offset = draw_instruction_list.data.size(); + draw_instruction_list.data.resize(draw_list_data_offset + p_instruction_size); + return reinterpret_cast<DrawListInstruction *>(&draw_instruction_list.data[draw_list_data_offset]); +} + +RenderingDeviceGraph::ComputeListInstruction *RenderingDeviceGraph::_allocate_compute_list_instruction(uint32_t p_instruction_size) { + uint32_t compute_list_data_offset = compute_instruction_list.data.size(); + compute_instruction_list.data.resize(compute_list_data_offset + p_instruction_size); + return reinterpret_cast<ComputeListInstruction *>(&compute_instruction_list.data[compute_list_data_offset]); +} + +void RenderingDeviceGraph::_add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command) { + if (command_label_index >= 0) { + // If a label is active, tag the command with the label. + r_command->label_index = command_label_index; + } + + if (r_command->type == RecordedCommand::TYPE_CAPTURE_TIMESTAMP) { + // All previous commands starting from the previous timestamp should be adjacent to this command. + int32_t start_command_index = uint32_t(MAX(command_timestamp_index, 0)); + for (int32_t i = start_command_index; i < p_command_index; i++) { + _add_adjacent_command(i, p_command_index, r_command); + } + + // Make this command the new active timestamp command. + command_timestamp_index = p_command_index; + } else if (command_timestamp_index >= 0) { + // Timestamp command should be adjacent to this command. + _add_adjacent_command(command_timestamp_index, p_command_index, r_command); + } + + if (command_synchronization_pending) { + // All previous commands should be adjacent to this command. + int32_t start_command_index = uint32_t(MAX(command_synchronization_index, 0)); + for (int32_t i = start_command_index; i < p_command_index; i++) { + _add_adjacent_command(i, p_command_index, r_command); + } + + command_synchronization_index = p_command_index; + command_synchronization_pending = false; + } else if (command_synchronization_index >= 0) { + // Synchronization command should be adjacent to this command. + _add_adjacent_command(command_synchronization_index, p_command_index, r_command); + } + + for (uint32_t i = 0; i < p_resource_count; i++) { + ResourceTracker *resource_tracker = p_resource_trackers[i]; + DEV_ASSERT(resource_tracker != nullptr); + + resource_tracker->reset_if_outdated(tracking_frame); + + ResourceUsage new_resource_usage = p_resource_usages[i]; + bool write_usage = _is_write_usage(new_resource_usage); + BitField<RDD::BarrierAccessBits> new_usage_access = _usage_to_access_bits(new_resource_usage); + bool is_resource_a_slice = resource_tracker->parent != nullptr; + if (is_resource_a_slice) { + // This resource depends on a parent resource. + resource_tracker->parent->reset_if_outdated(tracking_frame); + + if (resource_tracker->texture_slice_command_index != p_command_index) { + // Indicate this slice has been used by this command. + resource_tracker->texture_slice_command_index = p_command_index; + } + + if (resource_tracker->parent->usage == RESOURCE_USAGE_NONE) { + if (resource_tracker->parent->texture_driver_id != 0) { + // If the resource is a texture, we transition it entirely to the layout determined by the first slice that uses it. + _add_texture_barrier_to_command(resource_tracker->parent->texture_driver_id, RDD::BarrierAccessBits(0), new_usage_access, RDG::RESOURCE_USAGE_NONE, new_resource_usage, resource_tracker->parent->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); + } + + // If the parent hasn't been used yet, we assign the usage of the slice to the entire resource. + resource_tracker->parent->usage = new_resource_usage; + + // Also assign the usage to the slice and consider it a write operation. + resource_tracker->usage = new_resource_usage; + write_usage = true; + } else if (resource_tracker->in_parent_dirty_list) { + if (resource_tracker->parent->usage == new_resource_usage) { + // The slice will be transitioned to the resource of the parent and can be deleted from the dirty list. + ResourceTracker *previous_tracker = nullptr; + ResourceTracker *current_tracker = resource_tracker->parent->dirty_shared_list; + bool initialized_dirty_rect = false; + while (current_tracker != nullptr) { + if (current_tracker == resource_tracker) { + current_tracker->in_parent_dirty_list = false; + + if (previous_tracker != nullptr) { + previous_tracker->next_shared = current_tracker->next_shared; + } else { + resource_tracker->parent->dirty_shared_list = current_tracker->next_shared; + } + + current_tracker = current_tracker->next_shared; + } else { + if (initialized_dirty_rect) { + resource_tracker->parent->texture_slice_or_dirty_rect = resource_tracker->parent->texture_slice_or_dirty_rect.merge(current_tracker->texture_slice_or_dirty_rect); + } else { + resource_tracker->parent->texture_slice_or_dirty_rect = current_tracker->texture_slice_or_dirty_rect; + initialized_dirty_rect = true; + } + + previous_tracker = current_tracker; + current_tracker = current_tracker->next_shared; + } + } + } + } else { + if (resource_tracker->parent->dirty_shared_list != nullptr && resource_tracker->parent->texture_slice_or_dirty_rect.intersects(resource_tracker->texture_slice_or_dirty_rect)) { + // There's an intersection with the current dirty area of the parent and the slice. We must verify if the intersection is against a slice + // that was used in this command or not. Any slice we can find that wasn't used by this command must be reverted to the layout of the parent. + ResourceTracker *previous_tracker = nullptr; + ResourceTracker *current_tracker = resource_tracker->parent->dirty_shared_list; + bool initialized_dirty_rect = false; + while (current_tracker != nullptr) { + if (current_tracker->texture_slice_or_dirty_rect.intersects(resource_tracker->texture_slice_or_dirty_rect)) { + if (current_tracker->command_frame == tracking_frame && current_tracker->texture_slice_command_index == p_command_index) { + ERR_FAIL_MSG("Texture slices that overlap can't be used in the same command."); + } else { + // Delete the slice from the dirty list and revert it to the usage of the parent. + if (current_tracker->texture_driver_id != 0) { + _add_texture_barrier_to_command(current_tracker->texture_driver_id, current_tracker->usage_access, new_usage_access, current_tracker->usage, resource_tracker->parent->usage, current_tracker->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); + } + + current_tracker->in_parent_dirty_list = false; + + if (previous_tracker != nullptr) { + previous_tracker->next_shared = current_tracker->next_shared; + } else { + resource_tracker->parent->dirty_shared_list = current_tracker->next_shared; + } + + current_tracker = current_tracker->next_shared; + } + } else { + // Recalculate the dirty rect of the parent so the deleted slices are excluded. + if (initialized_dirty_rect) { + resource_tracker->parent->texture_slice_or_dirty_rect = resource_tracker->parent->texture_slice_or_dirty_rect.merge(current_tracker->texture_slice_or_dirty_rect); + } else { + resource_tracker->parent->texture_slice_or_dirty_rect = current_tracker->texture_slice_or_dirty_rect; + initialized_dirty_rect = true; + } + + previous_tracker = current_tracker; + current_tracker = current_tracker->next_shared; + } + } + } + + // If it wasn't in the list, assume the usage is the same as the parent. + resource_tracker->usage = resource_tracker->parent->usage; + + if (resource_tracker->usage != new_resource_usage) { + // Insert to the dirty list if the requested usage is different. + resource_tracker->next_shared = resource_tracker->parent->dirty_shared_list; + resource_tracker->parent->dirty_shared_list = resource_tracker; + resource_tracker->in_parent_dirty_list = true; + if (resource_tracker->parent->dirty_shared_list != nullptr) { + resource_tracker->parent->texture_slice_or_dirty_rect = resource_tracker->parent->texture_slice_or_dirty_rect.merge(resource_tracker->texture_slice_or_dirty_rect); + } else { + resource_tracker->parent->texture_slice_or_dirty_rect = resource_tracker->texture_slice_or_dirty_rect; + } + } + } + } else { + if (resource_tracker->dirty_shared_list != nullptr) { + // Consider the usage as write if we must transition any of the slices. + write_usage = true; + } + + while (resource_tracker->dirty_shared_list != nullptr) { + if (resource_tracker->dirty_shared_list->texture_driver_id != 0) { + // Transition all slices to the layout of the parent resource. + _add_texture_barrier_to_command(resource_tracker->dirty_shared_list->texture_driver_id, resource_tracker->dirty_shared_list->usage_access, new_usage_access, resource_tracker->dirty_shared_list->usage, resource_tracker->usage, resource_tracker->dirty_shared_list->texture_subresources, command_normalization_barriers, r_command->normalization_barrier_index, r_command->normalization_barrier_count); + } + + resource_tracker->dirty_shared_list->in_parent_dirty_list = false; + resource_tracker->dirty_shared_list = resource_tracker->dirty_shared_list->next_shared; + } + } + + // Use the resource's parent tracker directly for all search operations. + bool resource_has_parent = resource_tracker->parent != nullptr; + ResourceTracker *search_tracker = resource_has_parent ? resource_tracker->parent : resource_tracker; + const RDD::TextureSubresourceRange &subresources = resource_tracker->texture_subresources; + Rect2i resource_tracker_rect(subresources.base_mipmap, subresources.base_layer, subresources.mipmap_count, subresources.layer_count); + bool different_usage = resource_tracker->usage != new_resource_usage; + bool write_usage_after_write = (write_usage && search_tracker->write_command_or_list_index >= 0); + if (different_usage || write_usage_after_write) { + // A barrier must be pushed if the usage is different of it's a write usage and there was already a command that wrote to this resource previously. + if (resource_tracker->texture_driver_id.id != 0) { + if (resource_tracker->usage_access.is_empty()) { + // FIXME: If the tracker does not know the previous type of usage, assume the generic memory write one. + // Tracking access bits across texture slices can be tricky, so this failsafe can be removed once that's improved. + resource_tracker->usage_access = RDD::BARRIER_ACCESS_MEMORY_WRITE_BIT; + } + + _add_texture_barrier_to_command(resource_tracker->texture_driver_id, resource_tracker->usage_access, new_usage_access, resource_tracker->usage, new_resource_usage, resource_tracker->texture_subresources, command_transition_barriers, r_command->transition_barrier_index, r_command->transition_barrier_count); + } else if (resource_tracker->buffer_driver_id.id != 0) { +#if USE_BUFFER_BARRIERS + _add_buffer_barrier_to_command(resource_tracker->buffer_driver_id, resource_tracker->usage_access, new_usage_access, r_command->buffer_barrier_index, r_command->buffer_barrier_count); +#endif + // FIXME: Memory barriers are currently pushed regardless of whether buffer barriers are being used or not. Refer to the comment on the + // definition of USE_BUFFER_BARRIERS for the reason behind this. This can be fixed to be one case or the other once it's been confirmed + // the buffer and memory barrier behavior discrepancy has been solved. + r_command->memory_barrier.src_access = resource_tracker->usage_access; + r_command->memory_barrier.dst_access = new_usage_access; + } else { + DEV_ASSERT(false && "Resource tracker does not contain a valid buffer or texture ID."); + } + } + + // Always update the access of the tracker according to the latest usage. + resource_tracker->usage_access = new_usage_access; + + if (different_usage) { + // Even if the usage of the resource isn't a write usage explicitly, a different usage implies a transition and it should therefore be considered a write. + write_usage = true; + resource_tracker->usage = new_resource_usage; + } + + if (search_tracker->write_command_or_list_index >= 0) { + if (search_tracker->write_command_list_enabled) { + // Make this command adjacent to any commands that wrote to this resource and intersect with the slice if it applies. + // For buffers or textures that never use slices, this list will only be one element long at most. + int32_t previous_write_list_index = -1; + int32_t write_list_index = search_tracker->write_command_or_list_index; + while (write_list_index >= 0) { + const RecordedWriteListNode &write_list_node = write_list_nodes[write_list_index]; + if (!resource_has_parent || resource_tracker_rect.intersects(write_list_node.subresources)) { + if (write_list_node.command_index == p_command_index) { + ERR_FAIL_COND_MSG(!resource_has_parent, "Command can't have itself as a dependency."); + } else { + // Command is dependent on this command. Add this command to the adjacency list of the write command. + _add_adjacent_command(write_list_node.command_index, p_command_index, r_command); + + if (resource_has_parent && write_usage && resource_tracker_rect.encloses(write_list_node.subresources)) { + // Eliminate redundant writes from the list. + if (previous_write_list_index >= 0) { + RecordedWriteListNode &previous_list_node = write_list_nodes[previous_write_list_index]; + previous_list_node.next_list_index = write_list_node.next_list_index; + } else { + search_tracker->write_command_or_list_index = write_list_node.next_list_index; + } + + write_list_index = write_list_node.next_list_index; + continue; + } + } + } + + previous_write_list_index = write_list_index; + write_list_index = write_list_node.next_list_index; + } + } else { + // The index is just the latest command index that wrote to the resource. + if (search_tracker->write_command_or_list_index == p_command_index) { + ERR_FAIL_MSG("Command can't have itself as a dependency."); + } else { + _add_adjacent_command(search_tracker->write_command_or_list_index, p_command_index, r_command); + } + } + } + + if (write_usage) { + if (resource_has_parent) { + if (!search_tracker->write_command_list_enabled && search_tracker->write_command_or_list_index >= 0) { + // Write command list was not being used but there was a write command recorded. Add a new node with the entire parent resource's subresources and the recorded command index to the list. + const RDD::TextureSubresourceRange &tracker_subresources = search_tracker->texture_subresources; + Rect2i tracker_rect(tracker_subresources.base_mipmap, tracker_subresources.base_layer, tracker_subresources.mipmap_count, tracker_subresources.layer_count); + search_tracker->write_command_or_list_index = _add_to_write_list(search_tracker->write_command_or_list_index, tracker_rect, -1); + } + + search_tracker->write_command_or_list_index = _add_to_write_list(p_command_index, resource_tracker_rect, search_tracker->write_command_or_list_index); + search_tracker->write_command_list_enabled = true; + } else { + search_tracker->write_command_or_list_index = p_command_index; + search_tracker->write_command_list_enabled = false; + } + + // We add this command to the adjacency list of all commands that were reading from this resource. We clear the list in the process. + int32_t previous_command_list_index = -1; + int32_t read_command_list_index = search_tracker->read_command_list_index; + while (read_command_list_index >= 0) { + const RecordedCommandListNode &command_list_node = command_list_nodes[read_command_list_index]; + if (command_list_node.command_index == p_command_index) { + if (!resource_has_parent) { + // Slices are allowed to be in different usages in the same command as they are guaranteed to have no overlap in the same command. + ERR_FAIL_MSG("Command can't have itself as a dependency."); + } else { + // Advance to the next element. + read_command_list_index = command_list_node.next_list_index; + previous_command_list_index = read_command_list_index; + } + } else { + if (previous_command_list_index >= 0) { + // Erase this element and connect the previous one to the next element. + command_list_nodes[previous_command_list_index].next_list_index = command_list_node.next_list_index; + read_command_list_index = command_list_node.next_list_index; + previous_command_list_index = read_command_list_index; + } else { + // Erase this element from the head of the list. + DEV_ASSERT(search_tracker->read_command_list_index == read_command_list_index); + read_command_list_index = command_list_node.next_list_index; + search_tracker->read_command_list_index = read_command_list_index; + } + + // Add this command to the adjacency list of each command that was reading this resource. + _add_adjacent_command(command_list_node.command_index, p_command_index, r_command); + } + } + } else { + // We add a read dependency to the tracker to indicate this command reads from the resource. + search_tracker->read_command_list_index = _add_to_command_list(p_command_index, search_tracker->read_command_list_index); + } + } +} + +void RenderingDeviceGraph::_add_texture_barrier_to_command(RDD::TextureID p_texture_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, ResourceUsage p_prev_usage, ResourceUsage p_next_usage, RDD::TextureSubresourceRange p_subresources, LocalVector<RDD::TextureBarrier> &r_barrier_vector, int32_t &r_barrier_index, int32_t &r_barrier_count) { + if (!driver_honors_barriers) { + return; + } + + if (r_barrier_index < 0) { + r_barrier_index = r_barrier_vector.size(); + } + + RDD::TextureBarrier texture_barrier; + texture_barrier.texture = p_texture_id; + texture_barrier.src_access = p_src_access; + texture_barrier.dst_access = p_dst_access; + texture_barrier.prev_layout = _usage_to_image_layout(p_prev_usage); + texture_barrier.next_layout = _usage_to_image_layout(p_next_usage); + texture_barrier.subresources = p_subresources; + r_barrier_vector.push_back(texture_barrier); + r_barrier_count++; +} + +#if USE_BUFFER_BARRIERS +void RenderingDeviceGraph::_add_buffer_barrier_to_command(RDD::BufferID p_buffer_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, int32_t &r_barrier_index, int32_t &r_barrier_count) { + if (!driver_honors_barriers) { + return; + } + + if (r_barrier_index < 0) { + r_barrier_index = command_buffer_barriers.size(); + } + + RDD::BufferBarrier buffer_barrier; + buffer_barrier.buffer = p_buffer_id; + buffer_barrier.src_access = p_src_access; + buffer_barrier.dst_access = p_dst_access; + buffer_barrier.offset = 0; + buffer_barrier.size = RDD::BUFFER_WHOLE_SIZE; + command_buffer_barriers.push_back(buffer_barrier); + r_barrier_count++; +} +#endif + +void RenderingDeviceGraph::_run_compute_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) { + uint32_t instruction_data_cursor = 0; + while (instruction_data_cursor < p_instruction_data_size) { + DEV_ASSERT((instruction_data_cursor + sizeof(ComputeListInstruction)) <= p_instruction_data_size); + + const ComputeListInstruction *instruction = reinterpret_cast<const ComputeListInstruction *>(&p_instruction_data[instruction_data_cursor]); + switch (instruction->type) { + case ComputeListInstruction::TYPE_BIND_PIPELINE: { + const ComputeListBindPipelineInstruction *bind_pipeline_instruction = reinterpret_cast<const ComputeListBindPipelineInstruction *>(instruction); + driver->command_bind_compute_pipeline(p_command_buffer, bind_pipeline_instruction->pipeline); + instruction_data_cursor += sizeof(ComputeListBindPipelineInstruction); + } break; + case ComputeListInstruction::TYPE_BIND_UNIFORM_SET: { + const ComputeListBindUniformSetInstruction *bind_uniform_set_instruction = reinterpret_cast<const ComputeListBindUniformSetInstruction *>(instruction); + driver->command_bind_compute_uniform_set(p_command_buffer, bind_uniform_set_instruction->uniform_set, bind_uniform_set_instruction->shader, bind_uniform_set_instruction->set_index); + instruction_data_cursor += sizeof(ComputeListBindUniformSetInstruction); + } break; + case ComputeListInstruction::TYPE_DISPATCH: { + const ComputeListDispatchInstruction *dispatch_instruction = reinterpret_cast<const ComputeListDispatchInstruction *>(instruction); + driver->command_compute_dispatch(p_command_buffer, dispatch_instruction->x_groups, dispatch_instruction->y_groups, dispatch_instruction->z_groups); + instruction_data_cursor += sizeof(ComputeListDispatchInstruction); + } break; + case ComputeListInstruction::TYPE_DISPATCH_INDIRECT: { + const ComputeListDispatchIndirectInstruction *dispatch_indirect_instruction = reinterpret_cast<const ComputeListDispatchIndirectInstruction *>(instruction); + driver->command_compute_dispatch_indirect(p_command_buffer, dispatch_indirect_instruction->buffer, dispatch_indirect_instruction->offset); + instruction_data_cursor += sizeof(ComputeListDispatchIndirectInstruction); + } break; + case ComputeListInstruction::TYPE_SET_PUSH_CONSTANT: { + const ComputeListSetPushConstantInstruction *set_push_constant_instruction = reinterpret_cast<const ComputeListSetPushConstantInstruction *>(instruction); + const VectorView push_constant_data_view(reinterpret_cast<const uint32_t *>(set_push_constant_instruction->data()), set_push_constant_instruction->size / sizeof(uint32_t)); + driver->command_bind_push_constants(p_command_buffer, set_push_constant_instruction->shader, 0, push_constant_data_view); + instruction_data_cursor += sizeof(ComputeListSetPushConstantInstruction); + instruction_data_cursor += set_push_constant_instruction->size; + } break; + case ComputeListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE: { + const ComputeListUniformSetPrepareForUseInstruction *uniform_set_prepare_for_use_instruction = reinterpret_cast<const ComputeListUniformSetPrepareForUseInstruction *>(instruction); + driver->command_uniform_set_prepare_for_use(p_command_buffer, uniform_set_prepare_for_use_instruction->uniform_set, uniform_set_prepare_for_use_instruction->shader, uniform_set_prepare_for_use_instruction->set_index); + instruction_data_cursor += sizeof(ComputeListUniformSetPrepareForUseInstruction); + } break; + default: + DEV_ASSERT(false && "Unknown compute list instruction type."); + return; + } + } +} + +void RenderingDeviceGraph::_run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) { + uint32_t instruction_data_cursor = 0; + while (instruction_data_cursor < p_instruction_data_size) { + DEV_ASSERT((instruction_data_cursor + sizeof(DrawListInstruction)) <= p_instruction_data_size); + + const DrawListInstruction *instruction = reinterpret_cast<const DrawListInstruction *>(&p_instruction_data[instruction_data_cursor]); + switch (instruction->type) { + case DrawListInstruction::TYPE_BIND_INDEX_BUFFER: { + const DrawListBindIndexBufferInstruction *bind_index_buffer_instruction = reinterpret_cast<const DrawListBindIndexBufferInstruction *>(instruction); + driver->command_render_bind_index_buffer(p_command_buffer, bind_index_buffer_instruction->buffer, bind_index_buffer_instruction->format, bind_index_buffer_instruction->offset); + instruction_data_cursor += sizeof(DrawListBindIndexBufferInstruction); + } break; + case DrawListInstruction::TYPE_BIND_PIPELINE: { + const DrawListBindPipelineInstruction *bind_pipeline_instruction = reinterpret_cast<const DrawListBindPipelineInstruction *>(instruction); + driver->command_bind_render_pipeline(p_command_buffer, bind_pipeline_instruction->pipeline); + instruction_data_cursor += sizeof(DrawListBindPipelineInstruction); + } break; + case DrawListInstruction::TYPE_BIND_UNIFORM_SET: { + const DrawListBindUniformSetInstruction *bind_uniform_set_instruction = reinterpret_cast<const DrawListBindUniformSetInstruction *>(instruction); + driver->command_bind_render_uniform_set(p_command_buffer, bind_uniform_set_instruction->uniform_set, bind_uniform_set_instruction->shader, bind_uniform_set_instruction->set_index); + instruction_data_cursor += sizeof(DrawListBindUniformSetInstruction); + } break; + case DrawListInstruction::TYPE_BIND_VERTEX_BUFFERS: { + const DrawListBindVertexBuffersInstruction *bind_vertex_buffers_instruction = reinterpret_cast<const DrawListBindVertexBuffersInstruction *>(instruction); + driver->command_render_bind_vertex_buffers(p_command_buffer, bind_vertex_buffers_instruction->vertex_buffers_count, bind_vertex_buffers_instruction->vertex_buffers(), bind_vertex_buffers_instruction->vertex_buffer_offsets()); + instruction_data_cursor += sizeof(DrawListBindVertexBuffersInstruction); + instruction_data_cursor += sizeof(RDD::BufferID) * bind_vertex_buffers_instruction->vertex_buffers_count; + instruction_data_cursor += sizeof(uint64_t) * bind_vertex_buffers_instruction->vertex_buffers_count; + } break; + case DrawListInstruction::TYPE_CLEAR_ATTACHMENTS: { + const DrawListClearAttachmentsInstruction *clear_attachments_instruction = reinterpret_cast<const DrawListClearAttachmentsInstruction *>(instruction); + const VectorView attachments_clear_view(clear_attachments_instruction->attachments_clear(), clear_attachments_instruction->attachments_clear_count); + const VectorView attachments_clear_rect_view(clear_attachments_instruction->attachments_clear_rect(), clear_attachments_instruction->attachments_clear_rect_count); + driver->command_render_clear_attachments(p_command_buffer, attachments_clear_view, attachments_clear_rect_view); + instruction_data_cursor += sizeof(DrawListClearAttachmentsInstruction); + instruction_data_cursor += sizeof(RDD::AttachmentClear) * clear_attachments_instruction->attachments_clear_count; + instruction_data_cursor += sizeof(Rect2i) * clear_attachments_instruction->attachments_clear_rect_count; + } break; + case DrawListInstruction::TYPE_DRAW: { + const DrawListDrawInstruction *draw_instruction = reinterpret_cast<const DrawListDrawInstruction *>(instruction); + driver->command_render_draw(p_command_buffer, draw_instruction->vertex_count, draw_instruction->instance_count, 0, 0); + instruction_data_cursor += sizeof(DrawListDrawInstruction); + } break; + case DrawListInstruction::TYPE_DRAW_INDEXED: { + const DrawListDrawIndexedInstruction *draw_indexed_instruction = reinterpret_cast<const DrawListDrawIndexedInstruction *>(instruction); + driver->command_render_draw_indexed(p_command_buffer, draw_indexed_instruction->index_count, draw_indexed_instruction->instance_count, draw_indexed_instruction->first_index, 0, 0); + instruction_data_cursor += sizeof(DrawListDrawIndexedInstruction); + } break; + case DrawListInstruction::TYPE_EXECUTE_COMMANDS: { + const DrawListExecuteCommandsInstruction *execute_commands_instruction = reinterpret_cast<const DrawListExecuteCommandsInstruction *>(instruction); + driver->command_buffer_execute_secondary(p_command_buffer, execute_commands_instruction->command_buffer); + instruction_data_cursor += sizeof(DrawListExecuteCommandsInstruction); + } break; + case DrawListInstruction::TYPE_NEXT_SUBPASS: { + const DrawListNextSubpassInstruction *next_subpass_instruction = reinterpret_cast<const DrawListNextSubpassInstruction *>(instruction); + driver->command_next_render_subpass(p_command_buffer, next_subpass_instruction->command_buffer_type); + instruction_data_cursor += sizeof(DrawListNextSubpassInstruction); + } break; + case DrawListInstruction::TYPE_SET_BLEND_CONSTANTS: { + const DrawListSetBlendConstantsInstruction *set_blend_constants_instruction = reinterpret_cast<const DrawListSetBlendConstantsInstruction *>(instruction); + driver->command_render_set_blend_constants(p_command_buffer, set_blend_constants_instruction->color); + instruction_data_cursor += sizeof(DrawListSetBlendConstantsInstruction); + } break; + case DrawListInstruction::TYPE_SET_LINE_WIDTH: { + const DrawListSetLineWidthInstruction *set_line_width_instruction = reinterpret_cast<const DrawListSetLineWidthInstruction *>(instruction); + driver->command_render_set_line_width(p_command_buffer, set_line_width_instruction->width); + instruction_data_cursor += sizeof(DrawListSetLineWidthInstruction); + } break; + case DrawListInstruction::TYPE_SET_PUSH_CONSTANT: { + const DrawListSetPushConstantInstruction *set_push_constant_instruction = reinterpret_cast<const DrawListSetPushConstantInstruction *>(instruction); + const VectorView push_constant_data_view(reinterpret_cast<const uint32_t *>(set_push_constant_instruction->data()), set_push_constant_instruction->size / sizeof(uint32_t)); + driver->command_bind_push_constants(p_command_buffer, set_push_constant_instruction->shader, 0, push_constant_data_view); + instruction_data_cursor += sizeof(DrawListSetPushConstantInstruction); + instruction_data_cursor += set_push_constant_instruction->size; + } break; + case DrawListInstruction::TYPE_SET_SCISSOR: { + const DrawListSetScissorInstruction *set_scissor_instruction = reinterpret_cast<const DrawListSetScissorInstruction *>(instruction); + driver->command_render_set_scissor(p_command_buffer, set_scissor_instruction->rect); + instruction_data_cursor += sizeof(DrawListSetScissorInstruction); + } break; + case DrawListInstruction::TYPE_SET_VIEWPORT: { + const DrawListSetViewportInstruction *set_viewport_instruction = reinterpret_cast<const DrawListSetViewportInstruction *>(instruction); + driver->command_render_set_viewport(p_command_buffer, set_viewport_instruction->rect); + instruction_data_cursor += sizeof(DrawListSetViewportInstruction); + } break; + case DrawListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE: { + const DrawListUniformSetPrepareForUseInstruction *uniform_set_prepare_for_use_instruction = reinterpret_cast<const DrawListUniformSetPrepareForUseInstruction *>(instruction); + driver->command_uniform_set_prepare_for_use(p_command_buffer, uniform_set_prepare_for_use_instruction->uniform_set, uniform_set_prepare_for_use_instruction->shader, uniform_set_prepare_for_use_instruction->set_index); + instruction_data_cursor += sizeof(DrawListUniformSetPrepareForUseInstruction); + } break; + default: + DEV_ASSERT(false && "Unknown draw list instruction type."); + return; + } + } +} + +void RenderingDeviceGraph::_run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary) { + driver->command_buffer_begin_secondary(p_secondary->command_buffer, p_secondary->render_pass, 0, p_secondary->framebuffer); + _run_draw_list_command(p_secondary->command_buffer, p_secondary->instruction_data.ptr(), p_secondary->instruction_data.size()); + driver->command_buffer_end(p_secondary->command_buffer); +} + +void RenderingDeviceGraph::_wait_for_secondary_command_buffer_tasks() { + for (uint32_t i = 0; i < frames[frame].secondary_command_buffers_used; i++) { + WorkerThreadPool::TaskID &task = frames[frame].secondary_command_buffers[i].task; + if (task != WorkerThreadPool::INVALID_TASK_ID) { + WorkerThreadPool::get_singleton()->wait_for_task_completion(task); + task = WorkerThreadPool::INVALID_TASK_ID; + } + } +} + +void RenderingDeviceGraph::_run_render_commands(RDD::CommandBufferID p_command_buffer, int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level) { + for (uint32_t i = 0; i < p_sorted_commands_count; i++) { + const uint32_t command_index = p_sorted_commands[i].index; + const uint32_t command_data_offset = command_data_offsets[command_index]; + const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); + _run_label_command_change(p_command_buffer, command->label_index, p_level, false, true, &p_sorted_commands[i], p_sorted_commands_count - i, r_current_label_index, r_current_label_level); + + switch (command->type) { + case RecordedCommand::TYPE_BUFFER_CLEAR: { + const RecordedBufferClearCommand *buffer_clear_command = reinterpret_cast<const RecordedBufferClearCommand *>(command); + driver->command_clear_buffer(p_command_buffer, buffer_clear_command->buffer, buffer_clear_command->offset, buffer_clear_command->size); + } break; + case RecordedCommand::TYPE_BUFFER_COPY: { + const RecordedBufferCopyCommand *buffer_copy_command = reinterpret_cast<const RecordedBufferCopyCommand *>(command); + driver->command_copy_buffer(p_command_buffer, buffer_copy_command->source, buffer_copy_command->destination, buffer_copy_command->region); + } break; + case RecordedCommand::TYPE_BUFFER_GET_DATA: { + const RecordedBufferGetDataCommand *buffer_get_data_command = reinterpret_cast<const RecordedBufferGetDataCommand *>(command); + driver->command_copy_buffer(p_command_buffer, buffer_get_data_command->source, buffer_get_data_command->destination, buffer_get_data_command->region); + } break; + case RecordedCommand::TYPE_BUFFER_UPDATE: { + const RecordedBufferUpdateCommand *buffer_update_command = reinterpret_cast<const RecordedBufferUpdateCommand *>(command); + const RecordedBufferCopy *command_buffer_copies = buffer_update_command->buffer_copies(); + for (uint32_t j = 0; j < buffer_update_command->buffer_copies_count; j++) { + driver->command_copy_buffer(p_command_buffer, command_buffer_copies[j].source, buffer_update_command->destination, command_buffer_copies[j].region); + } + } break; + case RecordedCommand::TYPE_COMPUTE_LIST: { + const RecordedComputeListCommand *compute_list_command = reinterpret_cast<const RecordedComputeListCommand *>(command); + _run_compute_list_command(p_command_buffer, compute_list_command->instruction_data(), compute_list_command->instruction_data_size); + } break; + case RecordedCommand::TYPE_DRAW_LIST: { + const RecordedDrawListCommand *draw_list_command = reinterpret_cast<const RecordedDrawListCommand *>(command); + const VectorView clear_values(draw_list_command->clear_values(), draw_list_command->clear_values_count); + driver->command_begin_render_pass(p_command_buffer, draw_list_command->render_pass, draw_list_command->framebuffer, draw_list_command->command_buffer_type, draw_list_command->region, clear_values); + _run_draw_list_command(p_command_buffer, draw_list_command->instruction_data(), draw_list_command->instruction_data_size); + driver->command_end_render_pass(p_command_buffer); + } break; + case RecordedCommand::TYPE_TEXTURE_CLEAR: { + const RecordedTextureClearCommand *texture_clear_command = reinterpret_cast<const RecordedTextureClearCommand *>(command); + driver->command_clear_color_texture(p_command_buffer, texture_clear_command->texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_clear_command->color, texture_clear_command->range); + } break; + case RecordedCommand::TYPE_TEXTURE_COPY: { + const RecordedTextureCopyCommand *texture_copy_command = reinterpret_cast<const RecordedTextureCopyCommand *>(command); + driver->command_copy_texture(p_command_buffer, texture_copy_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_copy_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_copy_command->region); + } break; + case RecordedCommand::TYPE_TEXTURE_GET_DATA: { + const RecordedTextureGetDataCommand *texture_get_data_command = reinterpret_cast<const RecordedTextureGetDataCommand *>(command); + const VectorView<RDD::BufferTextureCopyRegion> command_buffer_texture_copy_regions_view(texture_get_data_command->buffer_texture_copy_regions(), texture_get_data_command->buffer_texture_copy_regions_count); + driver->command_copy_texture_to_buffer(p_command_buffer, texture_get_data_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_get_data_command->to_buffer, command_buffer_texture_copy_regions_view); + } break; + case RecordedCommand::TYPE_TEXTURE_RESOLVE: { + const RecordedTextureResolveCommand *texture_resolve_command = reinterpret_cast<const RecordedTextureResolveCommand *>(command); + driver->command_resolve_texture(p_command_buffer, texture_resolve_command->from_texture, RDD::TEXTURE_LAYOUT_TRANSFER_SRC_OPTIMAL, texture_resolve_command->src_layer, texture_resolve_command->src_mipmap, texture_resolve_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, texture_resolve_command->dst_layer, texture_resolve_command->dst_mipmap); + } break; + case RecordedCommand::TYPE_TEXTURE_UPDATE: { + const RecordedTextureUpdateCommand *texture_update_command = reinterpret_cast<const RecordedTextureUpdateCommand *>(command); + const RecordedBufferToTextureCopy *command_buffer_to_texture_copies = texture_update_command->buffer_to_texture_copies(); + for (uint32_t j = 0; j < texture_update_command->buffer_to_texture_copies_count; j++) { + driver->command_copy_buffer_to_texture(p_command_buffer, command_buffer_to_texture_copies[j].from_buffer, texture_update_command->to_texture, RDD::TEXTURE_LAYOUT_TRANSFER_DST_OPTIMAL, command_buffer_to_texture_copies[j].region); + } + } break; + case RecordedCommand::TYPE_CAPTURE_TIMESTAMP: { + const RecordedCaptureTimestampCommand *texture_capture_timestamp_command = reinterpret_cast<const RecordedCaptureTimestampCommand *>(command); + driver->command_timestamp_write(p_command_buffer, texture_capture_timestamp_command->pool, texture_capture_timestamp_command->index); + } break; + default: { + DEV_ASSERT(false && "Unknown recorded command type."); + return; + } + } + } +} + +void RenderingDeviceGraph::_run_label_command_change(RDD::CommandBufferID p_command_buffer, int32_t p_new_label_index, int32_t p_new_level, bool p_ignore_previous_value, bool p_use_label_for_empty, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level) { + if (command_label_count == 0) { + // Ignore any label operations if no labels were pushed. + return; + } + + if (p_ignore_previous_value || p_new_label_index != r_current_label_index || p_new_level != r_current_label_level) { + if (!p_ignore_previous_value && (p_use_label_for_empty || r_current_label_index >= 0)) { + // End the current label. + driver->command_end_label(p_command_buffer); + } + + String label_name; + Color label_color; + if (p_new_label_index >= 0) { + const char *label_chars = &command_label_chars[command_label_offsets[p_new_label_index]]; + label_name.parse_utf8(label_chars); + label_color = command_label_colors[p_new_label_index]; + } else if (p_use_label_for_empty) { + label_name = "Command graph"; + label_color = Color(1, 1, 1, 1); + } + + // Add the level to the name. + label_name += " (L" + itos(p_new_level) + ")"; + + if (p_sorted_commands != nullptr && p_sorted_commands_count > 0) { + // Analyze the commands in the level that have the same label to detect what type of operations are performed. + bool copy_commands = false; + bool compute_commands = false; + bool draw_commands = false; + for (uint32_t i = 0; i < p_sorted_commands_count; i++) { + const uint32_t command_index = p_sorted_commands[i].index; + const uint32_t command_data_offset = command_data_offsets[command_index]; + const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); + if (command->label_index != p_new_label_index) { + break; + } + + switch (command->type) { + case RecordedCommand::TYPE_BUFFER_CLEAR: + case RecordedCommand::TYPE_BUFFER_COPY: + case RecordedCommand::TYPE_BUFFER_GET_DATA: + case RecordedCommand::TYPE_BUFFER_UPDATE: + case RecordedCommand::TYPE_TEXTURE_CLEAR: + case RecordedCommand::TYPE_TEXTURE_COPY: + case RecordedCommand::TYPE_TEXTURE_GET_DATA: + case RecordedCommand::TYPE_TEXTURE_RESOLVE: + case RecordedCommand::TYPE_TEXTURE_UPDATE: { + copy_commands = true; + } break; + case RecordedCommand::TYPE_COMPUTE_LIST: { + compute_commands = true; + } break; + case RecordedCommand::TYPE_DRAW_LIST: { + draw_commands = true; + } break; + default: { + // Ignore command. + } break; + } + + if (copy_commands && compute_commands && draw_commands) { + // There's no more command types to find. + break; + } + } + + if (copy_commands || compute_commands || draw_commands) { + // Add the operations to the name. + bool plus_after_copy = copy_commands && (compute_commands || draw_commands); + bool plus_after_compute = compute_commands && draw_commands; + label_name += " ("; + label_name += copy_commands ? "Copy" : ""; + label_name += plus_after_copy ? "+" : ""; + label_name += compute_commands ? "Compute" : ""; + label_name += plus_after_compute ? "+" : ""; + label_name += draw_commands ? "Draw" : ""; + label_name += ")"; + } + } + + // Start the new label. + CharString label_name_utf8 = label_name.utf8(); + driver->command_begin_label(p_command_buffer, label_name_utf8.get_data(), label_color); + + r_current_label_index = p_new_label_index; + r_current_label_level = p_new_level; + } +} + +void RenderingDeviceGraph::_boost_priority_for_render_commands(RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, uint32_t &r_boosted_priority) { + if (p_sorted_commands_count == 0) { + return; + } + + const uint32_t boosted_priority_value = 0; + if (r_boosted_priority > 0) { + bool perform_sort = false; + for (uint32_t j = 0; j < p_sorted_commands_count; j++) { + if (p_sorted_commands[j].priority == r_boosted_priority) { + p_sorted_commands[j].priority = boosted_priority_value; + perform_sort = true; + } + } + + if (perform_sort) { + SortArray<RecordedCommandSort> command_sorter; + command_sorter.sort(p_sorted_commands, p_sorted_commands_count); + } + } + + if (p_sorted_commands[p_sorted_commands_count - 1].priority != boosted_priority_value) { + r_boosted_priority = p_sorted_commands[p_sorted_commands_count - 1].priority; + } +} + +void RenderingDeviceGraph::_group_barriers_for_render_commands(RDD::CommandBufferID p_command_buffer, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, bool p_full_memory_barrier) { + if (!driver_honors_barriers) { + return; + } + + barrier_group.clear(); + barrier_group.src_stages = RDD::PIPELINE_STAGE_TOP_OF_PIPE_BIT; + barrier_group.dst_stages = RDD::PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT; + + for (uint32_t i = 0; i < p_sorted_commands_count; i++) { + const uint32_t command_index = p_sorted_commands[i].index; + const uint32_t command_data_offset = command_data_offsets[command_index]; + const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); + + // Merge command's stage bits with the barrier group. + barrier_group.src_stages = barrier_group.src_stages | command->src_stages; + barrier_group.dst_stages = barrier_group.dst_stages | command->dst_stages; + + // Merge command's memory barrier bits with the barrier group. + barrier_group.memory_barrier.src_access = barrier_group.memory_barrier.src_access | command->memory_barrier.src_access; + barrier_group.memory_barrier.dst_access = barrier_group.memory_barrier.dst_access | command->memory_barrier.dst_access; + + // Gather texture barriers. + for (int32_t j = 0; j < command->normalization_barrier_count; j++) { + const RDD::TextureBarrier &recorded_barrier = command_normalization_barriers[command->normalization_barrier_index + j]; + barrier_group.normalization_barriers.push_back(recorded_barrier); + } + + for (int32_t j = 0; j < command->transition_barrier_count; j++) { + const RDD::TextureBarrier &recorded_barrier = command_transition_barriers[command->transition_barrier_index + j]; + barrier_group.transition_barriers.push_back(recorded_barrier); + } + +#if USE_BUFFER_BARRIERS + // Gather buffer barriers. + for (int32_t j = 0; j < command->buffer_barrier_count; j++) { + const RDD::BufferBarrier &recorded_barrier = command_buffer_barriers[command->buffer_barrier_index + j]; + barrier_group.buffer_barriers.push_back(recorded_barrier); + } +#endif + } + + if (p_full_memory_barrier) { + barrier_group.src_stages = RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT; + barrier_group.dst_stages = RDD::PIPELINE_STAGE_ALL_COMMANDS_BIT; + barrier_group.memory_barrier.src_access = RDD::BARRIER_ACCESS_MEMORY_READ_BIT | RDD::BARRIER_ACCESS_MEMORY_WRITE_BIT; + barrier_group.memory_barrier.dst_access = RDD::BARRIER_ACCESS_MEMORY_READ_BIT | RDD::BARRIER_ACCESS_MEMORY_WRITE_BIT; + } + + const bool is_memory_barrier_empty = barrier_group.memory_barrier.src_access.is_empty() && barrier_group.memory_barrier.dst_access.is_empty(); + const bool are_texture_barriers_empty = barrier_group.normalization_barriers.is_empty() && barrier_group.transition_barriers.is_empty(); +#if USE_BUFFER_BARRIERS + const bool are_buffer_barriers_empty = barrier_group.buffer_barriers.is_empty(); +#else + const bool are_buffer_barriers_empty = true; +#endif + if (is_memory_barrier_empty && are_texture_barriers_empty && are_buffer_barriers_empty) { + // Commands don't require synchronization. + return; + } + + const VectorView<RDD::MemoryBarrier> memory_barriers = !is_memory_barrier_empty ? barrier_group.memory_barrier : VectorView<RDD::MemoryBarrier>(); + const VectorView<RDD::TextureBarrier> texture_barriers = barrier_group.normalization_barriers.is_empty() ? barrier_group.transition_barriers : barrier_group.normalization_barriers; +#if USE_BUFFER_BARRIERS + const VectorView<RDD::BufferBarrier> buffer_barriers = !are_buffer_barriers_empty ? barrier_group.buffer_barriers : VectorView<RDD::BufferBarrier>(); +#else + const VectorView<RDD::BufferBarrier> buffer_barriers = VectorView<RDD::BufferBarrier>(); +#endif + + driver->command_pipeline_barrier(p_command_buffer, barrier_group.src_stages, barrier_group.dst_stages, memory_barriers, buffer_barriers, texture_barriers); + + bool separate_texture_barriers = !barrier_group.normalization_barriers.is_empty() && !barrier_group.transition_barriers.is_empty(); + if (separate_texture_barriers) { + driver->command_pipeline_barrier(p_command_buffer, barrier_group.src_stages, barrier_group.dst_stages, VectorView<RDD::MemoryBarrier>(), VectorView<RDD::BufferBarrier>(), barrier_group.transition_barriers); + } +} + +void RenderingDeviceGraph::_print_render_commands(const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count) { + for (uint32_t i = 0; i < p_sorted_commands_count; i++) { + const uint32_t command_index = p_sorted_commands[i].index; + const uint32_t command_level = p_sorted_commands[i].level; + const uint32_t command_data_offset = command_data_offsets[command_index]; + const RecordedCommand *command = reinterpret_cast<RecordedCommand *>(&command_data[command_data_offset]); + switch (command->type) { + case RecordedCommand::TYPE_BUFFER_CLEAR: { + const RecordedBufferClearCommand *buffer_clear_command = reinterpret_cast<const RecordedBufferClearCommand *>(command); + print_line(command_index, "LEVEL", command_level, "BUFFER CLEAR DESTINATION", itos(buffer_clear_command->buffer.id)); + } break; + case RecordedCommand::TYPE_BUFFER_COPY: { + const RecordedBufferCopyCommand *buffer_copy_command = reinterpret_cast<const RecordedBufferCopyCommand *>(command); + print_line(command_index, "LEVEL", command_level, "BUFFER COPY SOURCE", itos(buffer_copy_command->source.id), "DESTINATION", itos(buffer_copy_command->destination.id)); + } break; + case RecordedCommand::TYPE_BUFFER_GET_DATA: { + const RecordedBufferGetDataCommand *buffer_get_data_command = reinterpret_cast<const RecordedBufferGetDataCommand *>(command); + print_line(command_index, "LEVEL", command_level, "BUFFER GET DATA DESTINATION", itos(buffer_get_data_command->destination.id)); + } break; + case RecordedCommand::TYPE_BUFFER_UPDATE: { + const RecordedBufferUpdateCommand *buffer_update_command = reinterpret_cast<const RecordedBufferUpdateCommand *>(command); + print_line(command_index, "LEVEL", command_level, "BUFFER UPDATE DESTINATION", itos(buffer_update_command->destination.id), "COPIES", buffer_update_command->buffer_copies_count); + } break; + case RecordedCommand::TYPE_COMPUTE_LIST: { + const RecordedComputeListCommand *compute_list_command = reinterpret_cast<const RecordedComputeListCommand *>(command); + print_line(command_index, "LEVEL", command_level, "COMPUTE LIST SIZE", compute_list_command->instruction_data_size); + } break; + case RecordedCommand::TYPE_DRAW_LIST: { + const RecordedDrawListCommand *draw_list_command = reinterpret_cast<const RecordedDrawListCommand *>(command); + print_line(command_index, "LEVEL", command_level, "DRAW LIST SIZE", draw_list_command->instruction_data_size); + } break; + case RecordedCommand::TYPE_TEXTURE_CLEAR: { + const RecordedTextureClearCommand *texture_clear_command = reinterpret_cast<const RecordedTextureClearCommand *>(command); + print_line(command_index, "LEVEL", command_level, "TEXTURE CLEAR", itos(texture_clear_command->texture.id), "COLOR", texture_clear_command->color); + } break; + case RecordedCommand::TYPE_TEXTURE_COPY: { + const RecordedTextureCopyCommand *texture_copy_command = reinterpret_cast<const RecordedTextureCopyCommand *>(command); + print_line(command_index, "LEVEL", command_level, "TEXTURE COPY FROM", itos(texture_copy_command->from_texture.id), "TO", itos(texture_copy_command->to_texture.id)); + } break; + case RecordedCommand::TYPE_TEXTURE_GET_DATA: { + print_line(command_index, "LEVEL", command_level, "TEXTURE GET DATA"); + } break; + case RecordedCommand::TYPE_TEXTURE_RESOLVE: { + const RecordedTextureResolveCommand *texture_resolve_command = reinterpret_cast<const RecordedTextureResolveCommand *>(command); + print_line(command_index, "LEVEL", command_level, "TEXTURE RESOLVE FROM", itos(texture_resolve_command->from_texture.id), "TO", itos(texture_resolve_command->to_texture.id)); + } break; + case RecordedCommand::TYPE_TEXTURE_UPDATE: { + const RecordedTextureUpdateCommand *texture_update_command = reinterpret_cast<const RecordedTextureUpdateCommand *>(command); + print_line(command_index, "LEVEL", command_level, "TEXTURE UPDATE TO", itos(texture_update_command->to_texture.id)); + } break; + case RecordedCommand::TYPE_CAPTURE_TIMESTAMP: { + const RecordedCaptureTimestampCommand *texture_capture_timestamp_command = reinterpret_cast<const RecordedCaptureTimestampCommand *>(command); + print_line(command_index, "LEVEL", command_level, "CAPTURE TIMESTAMP POOL", itos(texture_capture_timestamp_command->pool.id), "INDEX", texture_capture_timestamp_command->index); + } break; + default: + DEV_ASSERT(false && "Unknown recorded command type."); + return; + } + } +} + +void RenderingDeviceGraph::_print_draw_list(const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) { + uint32_t instruction_data_cursor = 0; + while (instruction_data_cursor < p_instruction_data_size) { + DEV_ASSERT((instruction_data_cursor + sizeof(DrawListInstruction)) <= p_instruction_data_size); + + const DrawListInstruction *instruction = reinterpret_cast<const DrawListInstruction *>(&p_instruction_data[instruction_data_cursor]); + switch (instruction->type) { + case DrawListInstruction::TYPE_BIND_INDEX_BUFFER: { + const DrawListBindIndexBufferInstruction *bind_index_buffer_instruction = reinterpret_cast<const DrawListBindIndexBufferInstruction *>(instruction); + print_line("\tBIND INDEX BUFFER ID", itos(bind_index_buffer_instruction->buffer.id), "FORMAT", bind_index_buffer_instruction->format, "OFFSET", bind_index_buffer_instruction->offset); + instruction_data_cursor += sizeof(DrawListBindIndexBufferInstruction); + } break; + case DrawListInstruction::TYPE_BIND_PIPELINE: { + const DrawListBindPipelineInstruction *bind_pipeline_instruction = reinterpret_cast<const DrawListBindPipelineInstruction *>(instruction); + print_line("\tBIND PIPELINE ID", itos(bind_pipeline_instruction->pipeline.id)); + instruction_data_cursor += sizeof(DrawListBindPipelineInstruction); + } break; + case DrawListInstruction::TYPE_BIND_UNIFORM_SET: { + const DrawListBindUniformSetInstruction *bind_uniform_set_instruction = reinterpret_cast<const DrawListBindUniformSetInstruction *>(instruction); + print_line("\tBIND UNIFORM SET ID", itos(bind_uniform_set_instruction->uniform_set.id), "SET INDEX", bind_uniform_set_instruction->set_index); + instruction_data_cursor += sizeof(DrawListBindUniformSetInstruction); + } break; + case DrawListInstruction::TYPE_BIND_VERTEX_BUFFERS: { + const DrawListBindVertexBuffersInstruction *bind_vertex_buffers_instruction = reinterpret_cast<const DrawListBindVertexBuffersInstruction *>(instruction); + print_line("\tBIND VERTEX BUFFERS COUNT", bind_vertex_buffers_instruction->vertex_buffers_count); + instruction_data_cursor += sizeof(DrawListBindVertexBuffersInstruction); + instruction_data_cursor += sizeof(RDD::BufferID) * bind_vertex_buffers_instruction->vertex_buffers_count; + instruction_data_cursor += sizeof(uint64_t) * bind_vertex_buffers_instruction->vertex_buffers_count; + } break; + case DrawListInstruction::TYPE_CLEAR_ATTACHMENTS: { + const DrawListClearAttachmentsInstruction *clear_attachments_instruction = reinterpret_cast<const DrawListClearAttachmentsInstruction *>(instruction); + print_line("\tATTACHMENTS CLEAR COUNT", clear_attachments_instruction->attachments_clear_count, "RECT COUNT", clear_attachments_instruction->attachments_clear_rect_count); + instruction_data_cursor += sizeof(DrawListClearAttachmentsInstruction); + instruction_data_cursor += sizeof(RDD::AttachmentClear) * clear_attachments_instruction->attachments_clear_count; + instruction_data_cursor += sizeof(Rect2i) * clear_attachments_instruction->attachments_clear_rect_count; + } break; + case DrawListInstruction::TYPE_DRAW: { + const DrawListDrawInstruction *draw_instruction = reinterpret_cast<const DrawListDrawInstruction *>(instruction); + print_line("\tDRAW VERTICES", draw_instruction->vertex_count, "INSTANCES", draw_instruction->instance_count); + instruction_data_cursor += sizeof(DrawListDrawInstruction); + } break; + case DrawListInstruction::TYPE_DRAW_INDEXED: { + const DrawListDrawIndexedInstruction *draw_indexed_instruction = reinterpret_cast<const DrawListDrawIndexedInstruction *>(instruction); + print_line("\tDRAW INDICES", draw_indexed_instruction->index_count, "INSTANCES", draw_indexed_instruction->instance_count, "FIRST INDEX", draw_indexed_instruction->first_index); + instruction_data_cursor += sizeof(DrawListDrawIndexedInstruction); + } break; + case DrawListInstruction::TYPE_EXECUTE_COMMANDS: { + print_line("\tEXECUTE COMMANDS"); + instruction_data_cursor += sizeof(DrawListExecuteCommandsInstruction); + } break; + case DrawListInstruction::TYPE_NEXT_SUBPASS: { + print_line("\tNEXT SUBPASS"); + instruction_data_cursor += sizeof(DrawListNextSubpassInstruction); + } break; + case DrawListInstruction::TYPE_SET_BLEND_CONSTANTS: { + const DrawListSetBlendConstantsInstruction *set_blend_constants_instruction = reinterpret_cast<const DrawListSetBlendConstantsInstruction *>(instruction); + print_line("\tSET BLEND CONSTANTS COLOR", set_blend_constants_instruction->color); + instruction_data_cursor += sizeof(DrawListSetBlendConstantsInstruction); + } break; + case DrawListInstruction::TYPE_SET_LINE_WIDTH: { + const DrawListSetLineWidthInstruction *set_line_width_instruction = reinterpret_cast<const DrawListSetLineWidthInstruction *>(instruction); + print_line("\tSET LINE WIDTH", set_line_width_instruction->width); + instruction_data_cursor += sizeof(DrawListSetLineWidthInstruction); + } break; + case DrawListInstruction::TYPE_SET_PUSH_CONSTANT: { + const DrawListSetPushConstantInstruction *set_push_constant_instruction = reinterpret_cast<const DrawListSetPushConstantInstruction *>(instruction); + print_line("\tSET PUSH CONSTANT SIZE", set_push_constant_instruction->size); + instruction_data_cursor += sizeof(DrawListSetPushConstantInstruction); + instruction_data_cursor += set_push_constant_instruction->size; + } break; + case DrawListInstruction::TYPE_SET_SCISSOR: { + const DrawListSetScissorInstruction *set_scissor_instruction = reinterpret_cast<const DrawListSetScissorInstruction *>(instruction); + print_line("\tSET SCISSOR", set_scissor_instruction->rect); + instruction_data_cursor += sizeof(DrawListSetScissorInstruction); + } break; + case DrawListInstruction::TYPE_SET_VIEWPORT: { + const DrawListSetViewportInstruction *set_viewport_instruction = reinterpret_cast<const DrawListSetViewportInstruction *>(instruction); + print_line("\tSET VIEWPORT", set_viewport_instruction->rect); + instruction_data_cursor += sizeof(DrawListSetViewportInstruction); + } break; + case DrawListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE: { + const DrawListUniformSetPrepareForUseInstruction *uniform_set_prepare_for_use_instruction = reinterpret_cast<const DrawListUniformSetPrepareForUseInstruction *>(instruction); + print_line("\tUNIFORM SET PREPARE FOR USE ID", itos(uniform_set_prepare_for_use_instruction->uniform_set.id), "SHADER ID", itos(uniform_set_prepare_for_use_instruction->shader.id), "INDEX", uniform_set_prepare_for_use_instruction->set_index); + instruction_data_cursor += sizeof(DrawListUniformSetPrepareForUseInstruction); + } break; + default: + DEV_ASSERT(false && "Unknown draw list instruction type."); + return; + } + } +} + +void RenderingDeviceGraph::_print_compute_list(const uint8_t *p_instruction_data, uint32_t p_instruction_data_size) { + uint32_t instruction_data_cursor = 0; + while (instruction_data_cursor < p_instruction_data_size) { + DEV_ASSERT((instruction_data_cursor + sizeof(ComputeListInstruction)) <= p_instruction_data_size); + + const ComputeListInstruction *instruction = reinterpret_cast<const ComputeListInstruction *>(&p_instruction_data[instruction_data_cursor]); + switch (instruction->type) { + case ComputeListInstruction::TYPE_BIND_PIPELINE: { + const ComputeListBindPipelineInstruction *bind_pipeline_instruction = reinterpret_cast<const ComputeListBindPipelineInstruction *>(instruction); + print_line("\tBIND PIPELINE ID", itos(bind_pipeline_instruction->pipeline.id)); + instruction_data_cursor += sizeof(ComputeListBindPipelineInstruction); + } break; + case ComputeListInstruction::TYPE_BIND_UNIFORM_SET: { + const ComputeListBindUniformSetInstruction *bind_uniform_set_instruction = reinterpret_cast<const ComputeListBindUniformSetInstruction *>(instruction); + print_line("\tBIND UNIFORM SET ID", itos(bind_uniform_set_instruction->uniform_set.id), "SHADER ID", itos(bind_uniform_set_instruction->shader.id)); + instruction_data_cursor += sizeof(ComputeListBindUniformSetInstruction); + } break; + case ComputeListInstruction::TYPE_DISPATCH: { + const ComputeListDispatchInstruction *dispatch_instruction = reinterpret_cast<const ComputeListDispatchInstruction *>(instruction); + print_line("\tDISPATCH", dispatch_instruction->x_groups, dispatch_instruction->y_groups, dispatch_instruction->z_groups); + instruction_data_cursor += sizeof(ComputeListDispatchInstruction); + } break; + case ComputeListInstruction::TYPE_DISPATCH_INDIRECT: { + const ComputeListDispatchIndirectInstruction *dispatch_indirect_instruction = reinterpret_cast<const ComputeListDispatchIndirectInstruction *>(instruction); + print_line("\tDISPATCH INDIRECT BUFFER ID", itos(dispatch_indirect_instruction->buffer.id), "OFFSET", dispatch_indirect_instruction->offset); + instruction_data_cursor += sizeof(ComputeListDispatchIndirectInstruction); + } break; + case ComputeListInstruction::TYPE_SET_PUSH_CONSTANT: { + const ComputeListSetPushConstantInstruction *set_push_constant_instruction = reinterpret_cast<const ComputeListSetPushConstantInstruction *>(instruction); + print_line("\tSET PUSH CONSTANT SIZE", set_push_constant_instruction->size); + instruction_data_cursor += sizeof(ComputeListSetPushConstantInstruction); + instruction_data_cursor += set_push_constant_instruction->size; + } break; + case ComputeListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE: { + const ComputeListUniformSetPrepareForUseInstruction *uniform_set_prepare_for_use_instruction = reinterpret_cast<const ComputeListUniformSetPrepareForUseInstruction *>(instruction); + print_line("\tUNIFORM SET PREPARE FOR USE ID", itos(uniform_set_prepare_for_use_instruction->uniform_set.id), "SHADER ID", itos(uniform_set_prepare_for_use_instruction->shader.id), "INDEX", itos(uniform_set_prepare_for_use_instruction->set_index)); + instruction_data_cursor += sizeof(ComputeListUniformSetPrepareForUseInstruction); + } break; + default: + DEV_ASSERT(false && "Unknown compute list instruction type."); + return; + } + } +} + +void RenderingDeviceGraph::initialize(RDD *p_driver, uint32_t p_frame_count, uint32_t p_secondary_command_buffers_per_frame) { + driver = p_driver; + frames.resize(p_frame_count); + + for (uint32_t i = 0; i < p_frame_count; i++) { + frames[i].secondary_command_buffers.resize(p_secondary_command_buffers_per_frame); + + for (uint32_t j = 0; j < p_secondary_command_buffers_per_frame; j++) { + SecondaryCommandBuffer &secondary = frames[i].secondary_command_buffers[j]; + secondary.command_pool = driver->command_pool_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY); + secondary.command_buffer = driver->command_buffer_create(RDD::COMMAND_BUFFER_TYPE_SECONDARY, secondary.command_pool); + secondary.task = WorkerThreadPool::INVALID_TASK_ID; + } + } + + driver_honors_barriers = driver->api_trait_get(RDD::API_TRAIT_HONORS_PIPELINE_BARRIERS); +} + +void RenderingDeviceGraph::begin() { + command_data.clear(); + command_data_offsets.clear(); + command_normalization_barriers.clear(); + command_transition_barriers.clear(); + command_label_chars.clear(); + command_label_colors.clear(); + command_label_offsets.clear(); + command_list_nodes.clear(); + write_list_nodes.clear(); + command_count = 0; + command_label_count = 0; + command_timestamp_index = -1; + command_synchronization_index = -1; + command_synchronization_pending = false; + command_label_index = -1; + frames[frame].secondary_command_buffers_used = 0; + draw_instruction_list.index = 0; + compute_instruction_list.index = 0; + tracking_frame++; + +#ifdef DEV_ENABLED + write_dependency_counters.clear(); +#endif +} + +void RenderingDeviceGraph::add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size) { + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + RecordedBufferClearCommand *command = static_cast<RecordedBufferClearCommand *>(_allocate_command(sizeof(RecordedBufferClearCommand), command_index)); + command->type = RecordedCommand::TYPE_BUFFER_CLEAR; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->buffer = p_dst; + command->offset = p_offset; + command->size = p_size; + + ResourceUsage usage = RESOURCE_USAGE_TRANSFER_TO; + _add_command_to_graph(&p_dst_tracker, &usage, 1, command_index, command); +} + +void RenderingDeviceGraph::add_buffer_copy(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, RDD::BufferCopyRegion p_region) { + // Source tracker is allowed to be null as it could be a read-only buffer. + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + RecordedBufferCopyCommand *command = static_cast<RecordedBufferCopyCommand *>(_allocate_command(sizeof(RecordedBufferCopyCommand), command_index)); + command->type = RecordedCommand::TYPE_BUFFER_COPY; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->source = p_src; + command->destination = p_dst; + command->region = p_region; + + ResourceTracker *trackers[2] = { p_dst_tracker, p_src_tracker }; + ResourceUsage usages[2] = { RESOURCE_USAGE_TRANSFER_TO, RESOURCE_USAGE_TRANSFER_FROM }; + _add_command_to_graph(trackers, usages, p_src_tracker != nullptr ? 2 : 1, command_index, command); +} + +void RenderingDeviceGraph::add_buffer_get_data(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, RDD::BufferCopyRegion p_region) { + // Source tracker is allowed to be null as it could be a read-only buffer. + int32_t command_index; + RecordedBufferGetDataCommand *command = static_cast<RecordedBufferGetDataCommand *>(_allocate_command(sizeof(RecordedBufferGetDataCommand), command_index)); + command->type = RecordedCommand::TYPE_BUFFER_GET_DATA; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->source = p_src; + command->destination = p_dst; + command->region = p_region; + + if (p_src_tracker != nullptr) { + ResourceUsage usage = RESOURCE_USAGE_TRANSFER_FROM; + _add_command_to_graph(&p_src_tracker, &usage, 1, command_index, command); + } else { + _add_command_to_graph(nullptr, nullptr, 0, command_index, command); + } +} + +void RenderingDeviceGraph::add_buffer_update(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, VectorView<RecordedBufferCopy> p_buffer_copies) { + DEV_ASSERT(p_dst_tracker != nullptr); + + size_t buffer_copies_size = p_buffer_copies.size() * sizeof(RecordedBufferCopy); + uint64_t command_size = sizeof(RecordedBufferUpdateCommand) + buffer_copies_size; + int32_t command_index; + RecordedBufferUpdateCommand *command = static_cast<RecordedBufferUpdateCommand *>(_allocate_command(command_size, command_index)); + command->type = RecordedCommand::TYPE_BUFFER_UPDATE; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->destination = p_dst; + command->buffer_copies_count = p_buffer_copies.size(); + + RecordedBufferCopy *buffer_copies = command->buffer_copies(); + for (uint32_t i = 0; i < command->buffer_copies_count; i++) { + buffer_copies[i] = p_buffer_copies[i]; + } + + ResourceUsage buffer_usage = RESOURCE_USAGE_TRANSFER_TO; + _add_command_to_graph(&p_dst_tracker, &buffer_usage, 1, command_index, command); +} + +void RenderingDeviceGraph::add_compute_list_begin() { + compute_instruction_list.clear(); + compute_instruction_list.index++; +} + +void RenderingDeviceGraph::add_compute_list_bind_pipeline(RDD::PipelineID p_pipeline) { + ComputeListBindPipelineInstruction *instruction = reinterpret_cast<ComputeListBindPipelineInstruction *>(_allocate_compute_list_instruction(sizeof(ComputeListBindPipelineInstruction))); + instruction->type = ComputeListInstruction::TYPE_BIND_PIPELINE; + instruction->pipeline = p_pipeline; + compute_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COMPUTE_SHADER_BIT); +} + +void RenderingDeviceGraph::add_compute_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index) { + ComputeListBindUniformSetInstruction *instruction = reinterpret_cast<ComputeListBindUniformSetInstruction *>(_allocate_compute_list_instruction(sizeof(ComputeListBindUniformSetInstruction))); + instruction->type = ComputeListInstruction::TYPE_BIND_UNIFORM_SET; + instruction->shader = p_shader; + instruction->uniform_set = p_uniform_set; + instruction->set_index = set_index; +} + +void RenderingDeviceGraph::add_compute_list_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups) { + ComputeListDispatchInstruction *instruction = reinterpret_cast<ComputeListDispatchInstruction *>(_allocate_compute_list_instruction(sizeof(ComputeListDispatchInstruction))); + instruction->type = ComputeListInstruction::TYPE_DISPATCH; + instruction->x_groups = p_x_groups; + instruction->y_groups = p_y_groups; + instruction->z_groups = p_z_groups; +} + +void RenderingDeviceGraph::add_compute_list_dispatch_indirect(RDD::BufferID p_buffer, uint32_t p_offset) { + ComputeListDispatchIndirectInstruction *instruction = reinterpret_cast<ComputeListDispatchIndirectInstruction *>(_allocate_compute_list_instruction(sizeof(ComputeListDispatchIndirectInstruction))); + instruction->type = ComputeListInstruction::TYPE_DISPATCH_INDIRECT; + instruction->buffer = p_buffer; + instruction->offset = p_offset; + compute_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_DRAW_INDIRECT_BIT); +} + +void RenderingDeviceGraph::add_compute_list_set_push_constant(RDD::ShaderID p_shader, const void *p_data, uint32_t p_data_size) { + uint32_t instruction_size = sizeof(ComputeListSetPushConstantInstruction) + p_data_size; + ComputeListSetPushConstantInstruction *instruction = reinterpret_cast<ComputeListSetPushConstantInstruction *>(_allocate_compute_list_instruction(instruction_size)); + instruction->type = ComputeListInstruction::TYPE_SET_PUSH_CONSTANT; + instruction->size = p_data_size; + instruction->shader = p_shader; + memcpy(instruction->data(), p_data, p_data_size); +} + +void RenderingDeviceGraph::add_compute_list_uniform_set_prepare_for_use(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index) { + ComputeListUniformSetPrepareForUseInstruction *instruction = reinterpret_cast<ComputeListUniformSetPrepareForUseInstruction *>(_allocate_compute_list_instruction(sizeof(ComputeListUniformSetPrepareForUseInstruction))); + instruction->type = ComputeListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE; + instruction->shader = p_shader; + instruction->uniform_set = p_uniform_set; + instruction->set_index = set_index; +} + +void RenderingDeviceGraph::add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage) { + DEV_ASSERT(p_tracker != nullptr); + + p_tracker->reset_if_outdated(tracking_frame); + + if (p_tracker->compute_list_index != compute_instruction_list.index) { + compute_instruction_list.command_trackers.push_back(p_tracker); + compute_instruction_list.command_tracker_usages.push_back(p_usage); + p_tracker->compute_list_index = compute_instruction_list.index; + } +} + +void RenderingDeviceGraph::add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages) { + DEV_ASSERT(p_trackers.size() == p_usages.size()); + + for (uint32_t i = 0; i < p_trackers.size(); i++) { + add_compute_list_usage(p_trackers[i], p_usages[i]); + } +} + +void RenderingDeviceGraph::add_compute_list_end() { + int32_t command_index; + uint32_t instruction_data_size = compute_instruction_list.data.size(); + uint32_t command_size = sizeof(RecordedComputeListCommand) + instruction_data_size; + RecordedComputeListCommand *command = static_cast<RecordedComputeListCommand *>(_allocate_command(command_size, command_index)); + command->type = RecordedCommand::TYPE_COMPUTE_LIST; + command->dst_stages = compute_instruction_list.stages; + command->instruction_data_size = instruction_data_size; + memcpy(command->instruction_data(), compute_instruction_list.data.ptr(), instruction_data_size); + _add_command_to_graph(compute_instruction_list.command_trackers.ptr(), compute_instruction_list.command_tracker_usages.ptr(), compute_instruction_list.command_trackers.size(), command_index, command); +} + +void RenderingDeviceGraph::add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth) { + draw_instruction_list.clear(); + draw_instruction_list.index++; + draw_instruction_list.render_pass = p_render_pass; + draw_instruction_list.framebuffer = p_framebuffer; + draw_instruction_list.region = p_region; + draw_instruction_list.clear_values.resize(p_clear_values.size()); + for (uint32_t i = 0; i < p_clear_values.size(); i++) { + draw_instruction_list.clear_values[i] = p_clear_values[i]; + } + + if (p_uses_color) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT); + } + + if (p_uses_depth) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_EARLY_FRAGMENT_TESTS_BIT); + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_LATE_FRAGMENT_TESTS_BIT); + } +} + +void RenderingDeviceGraph::add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset) { + DrawListBindIndexBufferInstruction *instruction = reinterpret_cast<DrawListBindIndexBufferInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListBindIndexBufferInstruction))); + instruction->type = DrawListInstruction::TYPE_BIND_INDEX_BUFFER; + instruction->buffer = p_buffer; + instruction->format = p_format; + instruction->offset = p_offset; + + if (instruction->buffer.id != 0) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); + } +} + +void RenderingDeviceGraph::add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField<RDD::PipelineStageBits> p_pipeline_stage_bits) { + DrawListBindPipelineInstruction *instruction = reinterpret_cast<DrawListBindPipelineInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListBindPipelineInstruction))); + instruction->type = DrawListInstruction::TYPE_BIND_PIPELINE; + instruction->pipeline = p_pipeline; + draw_instruction_list.stages = draw_instruction_list.stages | p_pipeline_stage_bits; +} + +void RenderingDeviceGraph::add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index) { + DrawListBindUniformSetInstruction *instruction = reinterpret_cast<DrawListBindUniformSetInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListBindUniformSetInstruction))); + instruction->type = DrawListInstruction::TYPE_BIND_UNIFORM_SET; + instruction->shader = p_shader; + instruction->uniform_set = p_uniform_set; + instruction->set_index = set_index; +} + +void RenderingDeviceGraph::add_draw_list_bind_vertex_buffers(VectorView<RDD::BufferID> p_vertex_buffers, VectorView<uint64_t> p_vertex_buffer_offsets) { + DEV_ASSERT(p_vertex_buffers.size() == p_vertex_buffer_offsets.size()); + + uint32_t instruction_size = sizeof(DrawListBindVertexBuffersInstruction) + sizeof(RDD::BufferID) * p_vertex_buffers.size() + sizeof(uint64_t) * p_vertex_buffer_offsets.size(); + DrawListBindVertexBuffersInstruction *instruction = reinterpret_cast<DrawListBindVertexBuffersInstruction *>(_allocate_draw_list_instruction(instruction_size)); + instruction->type = DrawListInstruction::TYPE_BIND_VERTEX_BUFFERS; + instruction->vertex_buffers_count = p_vertex_buffers.size(); + + RDD::BufferID *vertex_buffers = instruction->vertex_buffers(); + uint64_t *vertex_buffer_offsets = instruction->vertex_buffer_offsets(); + for (uint32_t i = 0; i < instruction->vertex_buffers_count; i++) { + vertex_buffers[i] = p_vertex_buffers[i]; + vertex_buffer_offsets[i] = p_vertex_buffer_offsets[i]; + } + + if (instruction->vertex_buffers_count > 0) { + draw_instruction_list.stages.set_flag(RDD::PIPELINE_STAGE_VERTEX_INPUT_BIT); + } +} + +void RenderingDeviceGraph::add_draw_list_clear_attachments(VectorView<RDD::AttachmentClear> p_attachments_clear, VectorView<Rect2i> p_attachments_clear_rect) { + uint32_t instruction_size = sizeof(DrawListClearAttachmentsInstruction) + sizeof(RDD::AttachmentClear) * p_attachments_clear.size() + sizeof(Rect2i) * p_attachments_clear_rect.size(); + DrawListClearAttachmentsInstruction *instruction = reinterpret_cast<DrawListClearAttachmentsInstruction *>(_allocate_draw_list_instruction(instruction_size)); + instruction->type = DrawListInstruction::TYPE_CLEAR_ATTACHMENTS; + instruction->attachments_clear_count = p_attachments_clear.size(); + instruction->attachments_clear_rect_count = p_attachments_clear_rect.size(); + + RDD::AttachmentClear *attachments_clear = instruction->attachments_clear(); + Rect2i *attachments_clear_rect = instruction->attachments_clear_rect(); + for (uint32_t i = 0; i < instruction->attachments_clear_count; i++) { + attachments_clear[i] = p_attachments_clear[i]; + } + + for (uint32_t i = 0; i < instruction->attachments_clear_rect_count; i++) { + attachments_clear_rect[i] = p_attachments_clear_rect[i]; + } +} + +void RenderingDeviceGraph::add_draw_list_draw(uint32_t p_vertex_count, uint32_t p_instance_count) { + DrawListDrawInstruction *instruction = reinterpret_cast<DrawListDrawInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListDrawInstruction))); + instruction->type = DrawListInstruction::TYPE_DRAW; + instruction->vertex_count = p_vertex_count; + instruction->instance_count = p_instance_count; +} + +void RenderingDeviceGraph::add_draw_list_draw_indexed(uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index) { + DrawListDrawIndexedInstruction *instruction = reinterpret_cast<DrawListDrawIndexedInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListDrawIndexedInstruction))); + instruction->type = DrawListInstruction::TYPE_DRAW_INDEXED; + instruction->index_count = p_index_count; + instruction->instance_count = p_instance_count; + instruction->first_index = p_first_index; +} + +void RenderingDeviceGraph::add_draw_list_execute_commands(RDD::CommandBufferID p_command_buffer) { + DrawListExecuteCommandsInstruction *instruction = reinterpret_cast<DrawListExecuteCommandsInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListExecuteCommandsInstruction))); + instruction->type = DrawListInstruction::TYPE_EXECUTE_COMMANDS; + instruction->command_buffer = p_command_buffer; +} + +void RenderingDeviceGraph::add_draw_list_next_subpass(RDD::CommandBufferType p_command_buffer_type) { + DrawListNextSubpassInstruction *instruction = reinterpret_cast<DrawListNextSubpassInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListNextSubpassInstruction))); + instruction->type = DrawListInstruction::TYPE_NEXT_SUBPASS; + instruction->command_buffer_type = p_command_buffer_type; +} + +void RenderingDeviceGraph::add_draw_list_set_blend_constants(const Color &p_color) { + DrawListSetBlendConstantsInstruction *instruction = reinterpret_cast<DrawListSetBlendConstantsInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListSetBlendConstantsInstruction))); + instruction->type = DrawListInstruction::TYPE_SET_BLEND_CONSTANTS; + instruction->color = p_color; +} + +void RenderingDeviceGraph::add_draw_list_set_line_width(float p_width) { + DrawListSetLineWidthInstruction *instruction = reinterpret_cast<DrawListSetLineWidthInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListSetLineWidthInstruction))); + instruction->type = DrawListInstruction::TYPE_SET_LINE_WIDTH; + instruction->width = p_width; +} + +void RenderingDeviceGraph::add_draw_list_set_push_constant(RDD::ShaderID p_shader, const void *p_data, uint32_t p_data_size) { + uint32_t instruction_size = sizeof(DrawListSetPushConstantInstruction) + p_data_size; + DrawListSetPushConstantInstruction *instruction = reinterpret_cast<DrawListSetPushConstantInstruction *>(_allocate_draw_list_instruction(instruction_size)); + instruction->type = DrawListInstruction::TYPE_SET_PUSH_CONSTANT; + instruction->size = p_data_size; + instruction->shader = p_shader; + memcpy(instruction->data(), p_data, p_data_size); +} + +void RenderingDeviceGraph::add_draw_list_set_scissor(Rect2i p_rect) { + DrawListSetScissorInstruction *instruction = reinterpret_cast<DrawListSetScissorInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListSetScissorInstruction))); + instruction->type = DrawListInstruction::TYPE_SET_SCISSOR; + instruction->rect = p_rect; +} + +void RenderingDeviceGraph::add_draw_list_set_viewport(Rect2i p_rect) { + DrawListSetViewportInstruction *instruction = reinterpret_cast<DrawListSetViewportInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListSetViewportInstruction))); + instruction->type = DrawListInstruction::TYPE_SET_VIEWPORT; + instruction->rect = p_rect; +} + +void RenderingDeviceGraph::add_draw_list_uniform_set_prepare_for_use(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index) { + DrawListUniformSetPrepareForUseInstruction *instruction = reinterpret_cast<DrawListUniformSetPrepareForUseInstruction *>(_allocate_draw_list_instruction(sizeof(DrawListUniformSetPrepareForUseInstruction))); + instruction->type = DrawListInstruction::TYPE_UNIFORM_SET_PREPARE_FOR_USE; + instruction->shader = p_shader; + instruction->uniform_set = p_uniform_set; + instruction->set_index = set_index; +} + +void RenderingDeviceGraph::add_draw_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage) { + p_tracker->reset_if_outdated(tracking_frame); + + if (p_tracker->draw_list_index != draw_instruction_list.index) { + draw_instruction_list.command_trackers.push_back(p_tracker); + draw_instruction_list.command_tracker_usages.push_back(p_usage); + p_tracker->draw_list_index = draw_instruction_list.index; + } +} + +void RenderingDeviceGraph::add_draw_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages) { + DEV_ASSERT(p_trackers.size() == p_usages.size()); + + for (uint32_t i = 0; i < p_trackers.size(); i++) { + add_draw_list_usage(p_trackers[i], p_usages[i]); + } +} + +void RenderingDeviceGraph::add_draw_list_end() { + // Arbitrary size threshold to evaluate if it'd be best to record the draw list on the background as a secondary buffer. + const uint32_t instruction_data_threshold_for_secondary = 16384; + RDD::CommandBufferType command_buffer_type; + uint32_t &secondary_buffers_used = frames[frame].secondary_command_buffers_used; + if (draw_instruction_list.data.size() > instruction_data_threshold_for_secondary && secondary_buffers_used < frames[frame].secondary_command_buffers.size()) { + // Copy the current instruction list data into another array that will be used by the secondary command buffer worker. + SecondaryCommandBuffer &secondary = frames[frame].secondary_command_buffers[secondary_buffers_used]; + secondary.render_pass = draw_instruction_list.render_pass; + secondary.framebuffer = draw_instruction_list.framebuffer; + secondary.instruction_data.resize(draw_instruction_list.data.size()); + memcpy(secondary.instruction_data.ptr(), draw_instruction_list.data.ptr(), draw_instruction_list.data.size()); + + // Run a background task for recording the secondary command buffer. + secondary.task = WorkerThreadPool::get_singleton()->add_template_task(this, &RenderingDeviceGraph::_run_secondary_command_buffer_task, &secondary, true); + + // Clear the instruction list and add a single command for executing the secondary command buffer instead. + draw_instruction_list.data.clear(); + add_draw_list_execute_commands(secondary.command_buffer); + secondary_buffers_used++; + + command_buffer_type = RDD::COMMAND_BUFFER_TYPE_SECONDARY; + } else { + command_buffer_type = RDD::COMMAND_BUFFER_TYPE_PRIMARY; + } + + int32_t command_index; + uint32_t clear_values_size = sizeof(RDD::RenderPassClearValue) * draw_instruction_list.clear_values.size(); + uint32_t instruction_data_size = draw_instruction_list.data.size(); + uint32_t command_size = sizeof(RecordedDrawListCommand) + clear_values_size + instruction_data_size; + RecordedDrawListCommand *command = static_cast<RecordedDrawListCommand *>(_allocate_command(command_size, command_index)); + command->type = RecordedCommand::TYPE_DRAW_LIST; + command->dst_stages = draw_instruction_list.stages; + command->instruction_data_size = instruction_data_size; + command->render_pass = draw_instruction_list.render_pass; + command->framebuffer = draw_instruction_list.framebuffer; + command->command_buffer_type = command_buffer_type; + command->region = draw_instruction_list.region; + command->clear_values_count = draw_instruction_list.clear_values.size(); + + RDD::RenderPassClearValue *clear_values = command->clear_values(); + for (uint32_t i = 0; i < command->clear_values_count; i++) { + clear_values[i] = draw_instruction_list.clear_values[i]; + } + + memcpy(command->instruction_data(), draw_instruction_list.data.ptr(), instruction_data_size); + _add_command_to_graph(draw_instruction_list.command_trackers.ptr(), draw_instruction_list.command_tracker_usages.ptr(), draw_instruction_list.command_trackers.size(), command_index, command); +} + +void RenderingDeviceGraph::add_texture_clear(RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, const Color &p_color, const RDD::TextureSubresourceRange &p_range) { + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + RecordedTextureClearCommand *command = static_cast<RecordedTextureClearCommand *>(_allocate_command(sizeof(RecordedTextureClearCommand), command_index)); + command->type = RecordedCommand::TYPE_TEXTURE_CLEAR; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->texture = p_dst; + command->color = p_color; + command->range = p_range; + + ResourceUsage usage = RESOURCE_USAGE_TRANSFER_TO; + _add_command_to_graph(&p_dst_tracker, &usage, 1, command_index, command); +} + +void RenderingDeviceGraph::add_texture_copy(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, RDD::TextureCopyRegion p_region) { + DEV_ASSERT(p_src_tracker != nullptr); + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + RecordedTextureCopyCommand *command = static_cast<RecordedTextureCopyCommand *>(_allocate_command(sizeof(RecordedTextureCopyCommand), command_index)); + command->type = RecordedCommand::TYPE_TEXTURE_COPY; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->from_texture = p_src; + command->to_texture = p_dst; + command->region = p_region; + + ResourceTracker *trackers[2] = { p_dst_tracker, p_src_tracker }; + ResourceUsage usages[2] = { RESOURCE_USAGE_TRANSFER_TO, RESOURCE_USAGE_TRANSFER_FROM }; + _add_command_to_graph(trackers, usages, 2, command_index, command); +} + +void RenderingDeviceGraph::add_texture_get_data(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, VectorView<RDD::BufferTextureCopyRegion> p_buffer_texture_copy_regions) { + DEV_ASSERT(p_src_tracker != nullptr); + + int32_t command_index; + uint64_t command_size = sizeof(RecordedTextureGetDataCommand) + p_buffer_texture_copy_regions.size() * sizeof(RDD::BufferTextureCopyRegion); + RecordedTextureGetDataCommand *command = static_cast<RecordedTextureGetDataCommand *>(_allocate_command(command_size, command_index)); + command->type = RecordedCommand::TYPE_TEXTURE_GET_DATA; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->from_texture = p_src; + command->to_buffer = p_dst; + command->buffer_texture_copy_regions_count = p_buffer_texture_copy_regions.size(); + + RDD::BufferTextureCopyRegion *buffer_texture_copy_regions = command->buffer_texture_copy_regions(); + for (uint32_t i = 0; i < command->buffer_texture_copy_regions_count; i++) { + buffer_texture_copy_regions[i] = p_buffer_texture_copy_regions[i]; + } + + ResourceUsage usage = RESOURCE_USAGE_TRANSFER_FROM; + _add_command_to_graph(&p_src_tracker, &usage, 1, command_index, command); +} + +void RenderingDeviceGraph::add_texture_resolve(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_src_layer, uint32_t p_src_mipmap, uint32_t p_dst_layer, uint32_t p_dst_mipmap) { + DEV_ASSERT(p_src_tracker != nullptr); + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + RecordedTextureResolveCommand *command = static_cast<RecordedTextureResolveCommand *>(_allocate_command(sizeof(RecordedTextureResolveCommand), command_index)); + command->type = RecordedCommand::TYPE_TEXTURE_RESOLVE; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->from_texture = p_src; + command->to_texture = p_dst; + command->src_layer = p_src_layer; + command->src_mipmap = p_src_mipmap; + command->dst_layer = p_dst_layer; + command->dst_mipmap = p_dst_mipmap; + + ResourceTracker *trackers[2] = { p_dst_tracker, p_src_tracker }; + ResourceUsage usages[2] = { RESOURCE_USAGE_TRANSFER_TO, RESOURCE_USAGE_TRANSFER_FROM }; + _add_command_to_graph(trackers, usages, 2, command_index, command); +} + +void RenderingDeviceGraph::add_texture_update(RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, VectorView<RecordedBufferToTextureCopy> p_buffer_copies) { + DEV_ASSERT(p_dst_tracker != nullptr); + + int32_t command_index; + uint64_t command_size = sizeof(RecordedTextureUpdateCommand) + p_buffer_copies.size() * sizeof(RecordedBufferToTextureCopy); + RecordedTextureUpdateCommand *command = static_cast<RecordedTextureUpdateCommand *>(_allocate_command(command_size, command_index)); + command->type = RecordedCommand::TYPE_TEXTURE_UPDATE; + command->dst_stages = RDD::PIPELINE_STAGE_TRANSFER_BIT; + command->to_texture = p_dst; + command->buffer_to_texture_copies_count = p_buffer_copies.size(); + + RecordedBufferToTextureCopy *buffer_to_texture_copies = command->buffer_to_texture_copies(); + for (uint32_t i = 0; i < command->buffer_to_texture_copies_count; i++) { + buffer_to_texture_copies[i] = p_buffer_copies[i]; + } + + ResourceUsage usage = RESOURCE_USAGE_TRANSFER_TO; + _add_command_to_graph(&p_dst_tracker, &usage, 1, command_index, command); +} + +void RenderingDeviceGraph::add_capture_timestamp(RDD::QueryPoolID p_query_pool, uint32_t p_index) { + int32_t command_index; + RecordedCaptureTimestampCommand *command = static_cast<RecordedCaptureTimestampCommand *>(_allocate_command(sizeof(RecordedCaptureTimestampCommand), command_index)); + command->type = RecordedCommand::TYPE_CAPTURE_TIMESTAMP; + command->dst_stages = 0; + command->pool = p_query_pool; + command->index = p_index; + _add_command_to_graph(nullptr, nullptr, 0, command_index, command); +} + +void RenderingDeviceGraph::add_synchronization() { + // Synchronization is only acknowledged if commands have been recorded on the graph already. + if (command_count > 0) { + command_synchronization_pending = true; + } +} + +void RenderingDeviceGraph::begin_label(const String &p_label_name, const Color &p_color) { + uint32_t command_label_offset = command_label_chars.size(); + PackedByteArray command_label_utf8 = p_label_name.to_utf8_buffer(); + int command_label_utf8_size = command_label_utf8.size(); + command_label_chars.resize(command_label_offset + command_label_utf8_size + 1); + memcpy(&command_label_chars[command_label_offset], command_label_utf8.ptr(), command_label_utf8.size()); + command_label_chars[command_label_offset + command_label_utf8_size] = '\0'; + command_label_colors.push_back(p_color); + command_label_offsets.push_back(command_label_offset); + command_label_index = command_label_count; + command_label_count++; +} + +void RenderingDeviceGraph::end_label() { + command_label_index = -1; +} + +void RenderingDeviceGraph::end(RDD::CommandBufferID p_command_buffer, bool p_reorder_commands, bool p_full_barriers) { + if (command_count == 0) { + // No commands have been logged, do nothing. + return; + } + + thread_local LocalVector<RecordedCommandSort> commands_sorted; + if (p_reorder_commands) { + thread_local LocalVector<int64_t> command_stack; + thread_local LocalVector<int32_t> sorted_command_indices; + thread_local LocalVector<uint32_t> command_degrees; + int32_t adjacency_list_index = 0; + int32_t command_index; + + // Count all the incoming connections to every node by traversing their adjacency list. + command_degrees.resize(command_count); + memset(command_degrees.ptr(), 0, sizeof(uint32_t) * command_degrees.size()); + for (uint32_t i = 0; i < command_count; i++) { + const RecordedCommand &recorded_command = *reinterpret_cast<const RecordedCommand *>(&command_data[command_data_offsets[i]]); + adjacency_list_index = recorded_command.adjacent_command_list_index; + while (adjacency_list_index >= 0) { + const RecordedCommandListNode &command_list_node = command_list_nodes[adjacency_list_index]; + DEV_ASSERT((command_list_node.command_index != int32_t(i)) && "Command can't have itself as a dependency."); + command_degrees[command_list_node.command_index] += 1; + adjacency_list_index = command_list_node.next_list_index; + } + } + + // Push to the stack all nodes that have no incoming connections. + command_stack.clear(); + for (uint32_t i = 0; i < command_count; i++) { + if (command_degrees[i] == 0) { + command_stack.push_back(i); + } + } + + sorted_command_indices.clear(); + while (!command_stack.is_empty()) { + // Pop command from the stack. + command_index = command_stack[command_stack.size() - 1]; + command_stack.resize(command_stack.size() - 1); + + // Add it to the sorted commands. + sorted_command_indices.push_back(command_index); + + // Search for its adjacents and lower their degree for every visit. If the degree reaches zero, we push the command to the stack. + const uint32_t command_data_offset = command_data_offsets[command_index]; + const RecordedCommand &recorded_command = *reinterpret_cast<const RecordedCommand *>(&command_data[command_data_offset]); + adjacency_list_index = recorded_command.adjacent_command_list_index; + while (adjacency_list_index >= 0) { + const RecordedCommandListNode &command_list_node = command_list_nodes[adjacency_list_index]; + uint32_t &command_degree = command_degrees[command_list_node.command_index]; + DEV_ASSERT(command_degree > 0); + command_degree--; + if (command_degree == 0) { + command_stack.push_back(command_list_node.command_index); + } + + adjacency_list_index = command_list_node.next_list_index; + } + } + + // Batch buffer, texture, draw lists and compute operations together. + const uint32_t PriorityTable[RecordedCommand::TYPE_MAX] = { + 0, // TYPE_NONE + 1, // TYPE_BUFFER_CLEAR + 1, // TYPE_BUFFER_COPY + 1, // TYPE_BUFFER_GET_DATA + 1, // TYPE_BUFFER_UPDATE + 4, // TYPE_COMPUTE_LIST + 3, // TYPE_DRAW_LIST + 2, // TYPE_TEXTURE_CLEAR + 2, // TYPE_TEXTURE_COPY + 2, // TYPE_TEXTURE_GET_DATA + 2, // TYPE_TEXTURE_RESOLVE + 2, // TYPE_TEXTURE_UPDATE + }; + + commands_sorted.clear(); + commands_sorted.resize(command_count); + + for (uint32_t i = 0; i < command_count; i++) { + const int32_t sorted_command_index = sorted_command_indices[i]; + const uint32_t command_data_offset = command_data_offsets[sorted_command_index]; + const RecordedCommand recorded_command = *reinterpret_cast<const RecordedCommand *>(&command_data[command_data_offset]); + const uint32_t next_command_level = commands_sorted[sorted_command_index].level + 1; + adjacency_list_index = recorded_command.adjacent_command_list_index; + while (adjacency_list_index >= 0) { + const RecordedCommandListNode &command_list_node = command_list_nodes[adjacency_list_index]; + uint32_t &adjacent_command_level = commands_sorted[command_list_node.command_index].level; + if (adjacent_command_level < next_command_level) { + adjacent_command_level = next_command_level; + } + + adjacency_list_index = command_list_node.next_list_index; + } + + commands_sorted[sorted_command_index].index = sorted_command_index; + commands_sorted[sorted_command_index].priority = PriorityTable[recorded_command.type]; + } + } else { + commands_sorted.clear(); + commands_sorted.resize(command_count); + + for (uint32_t i = 0; i < command_count; i++) { + commands_sorted[i].index = i; + } + } + + _wait_for_secondary_command_buffer_tasks(); + + if (command_count > 0) { + int32_t current_label_index = -1; + int32_t current_label_level = -1; + _run_label_command_change(p_command_buffer, -1, -1, true, true, nullptr, 0, current_label_index, current_label_level); + + if (p_reorder_commands) { +#if PRINT_RENDER_GRAPH + print_line("BEFORE SORT"); + _print_render_commands(commands_sorted.ptr(), command_count); +#endif + + commands_sorted.sort(); + +#if PRINT_RENDER_GRAPH + print_line("AFTER SORT"); + _print_render_commands(commands_sorted.ptr(), command_count); +#endif + + uint32_t boosted_priority = 0; + uint32_t current_level = commands_sorted[0].level; + uint32_t current_level_start = 0; + for (uint32_t i = 0; i < command_count; i++) { + if (current_level != commands_sorted[i].level) { + RecordedCommandSort *level_command_ptr = &commands_sorted[current_level_start]; + uint32_t level_command_count = i - current_level_start; + _boost_priority_for_render_commands(level_command_ptr, level_command_count, boosted_priority); + _group_barriers_for_render_commands(p_command_buffer, level_command_ptr, level_command_count, p_full_barriers); + _run_render_commands(p_command_buffer, current_level, level_command_ptr, level_command_count, current_label_index, current_label_level); + current_level = commands_sorted[i].level; + current_level_start = i; + } + } + + RecordedCommandSort *level_command_ptr = &commands_sorted[current_level_start]; + uint32_t level_command_count = command_count - current_level_start; + _boost_priority_for_render_commands(level_command_ptr, level_command_count, boosted_priority); + _group_barriers_for_render_commands(p_command_buffer, level_command_ptr, level_command_count, p_full_barriers); + _run_render_commands(p_command_buffer, current_level, level_command_ptr, level_command_count, current_label_index, current_label_level); + +#if PRINT_RENDER_GRAPH + print_line("COMMANDS", command_count, "LEVELS", current_level + 1); +#endif + } else { + for (uint32_t i = 0; i < command_count; i++) { + _group_barriers_for_render_commands(p_command_buffer, &commands_sorted[i], 1, p_full_barriers); + _run_render_commands(p_command_buffer, i, &commands_sorted[i], 1, current_label_index, current_label_level); + } + } + + _run_label_command_change(p_command_buffer, -1, -1, true, false, nullptr, 0, current_label_index, current_label_level); + } + + // Advance the frame counter. It's not necessary to do this if no commands are recorded because that means no secondary command buffers were used. + frame = (frame + 1) % frames.size(); +} + +#if PRINT_RESOURCE_TRACKER_TOTAL +static uint32_t resource_tracker_total = 0; +#endif + +RenderingDeviceGraph::ResourceTracker *RenderingDeviceGraph::resource_tracker_create() { +#if PRINT_RESOURCE_TRACKER_TOTAL + print_line("Resource trackers:", ++resource_tracker_total); +#endif + return memnew(ResourceTracker); +} + +void RenderingDeviceGraph::resource_tracker_free(ResourceTracker *tracker) { + if (tracker == nullptr) { + return; + } + + if (tracker->in_parent_dirty_list) { + // Delete the tracker from the parent's dirty linked list. + if (tracker->parent->dirty_shared_list == tracker) { + tracker->parent->dirty_shared_list = tracker->next_shared; + } else { + ResourceTracker *node = tracker->parent->dirty_shared_list; + while (node != nullptr) { + if (node->next_shared == tracker) { + node->next_shared = tracker->next_shared; + node = nullptr; + } else { + node = node->next_shared; + } + } + } + } + + memdelete(tracker); + +#if PRINT_RESOURCE_TRACKER_TOTAL + print_line("Resource trackers:", --resource_tracker_total); +#endif +} diff --git a/servers/rendering/rendering_device_graph.h b/servers/rendering/rendering_device_graph.h new file mode 100644 index 0000000000..84fbe02095 --- /dev/null +++ b/servers/rendering/rendering_device_graph.h @@ -0,0 +1,668 @@ +/**************************************************************************/ +/* rendering_device_graph.h */ +/**************************************************************************/ +/* This file is part of: */ +/* GODOT ENGINE */ +/* https://godotengine.org */ +/**************************************************************************/ +/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */ +/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur. */ +/* */ +/* Permission is hereby granted, free of charge, to any person obtaining */ +/* a copy of this software and associated documentation files (the */ +/* "Software"), to deal in the Software without restriction, including */ +/* without limitation the rights to use, copy, modify, merge, publish, */ +/* distribute, sublicense, and/or sell copies of the Software, and to */ +/* permit persons to whom the Software is furnished to do so, subject to */ +/* the following conditions: */ +/* */ +/* The above copyright notice and this permission notice shall be */ +/* included in all copies or substantial portions of the Software. */ +/* */ +/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, */ +/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF */ +/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */ +/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY */ +/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, */ +/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE */ +/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ +/**************************************************************************/ + +#ifndef RENDERING_DEVICE_GRAPH_H +#define RENDERING_DEVICE_GRAPH_H + +#include "core/object/worker_thread_pool.h" +#include "rendering_device_commons.h" +#include "rendering_device_driver.h" + +// Buffer barriers have not shown any significant improvement or shown to be +// even detrimental to performance. However, there are currently some known +// cases where using them can solve problems that using singular memory +// barriers does not, probably due to driver issues (see comment on PR #84976 +// https://github.com/godotengine/godot/pull/84976#issuecomment-1878566830). + +#define USE_BUFFER_BARRIERS 1 + +class RenderingDeviceGraph { +public: + struct ComputeListInstruction { + enum Type { + TYPE_NONE, + TYPE_BIND_PIPELINE, + TYPE_BIND_UNIFORM_SET, + TYPE_DISPATCH, + TYPE_DISPATCH_INDIRECT, + TYPE_SET_PUSH_CONSTANT, + TYPE_UNIFORM_SET_PREPARE_FOR_USE + }; + + Type type = TYPE_NONE; + }; + + struct DrawListInstruction { + enum Type { + TYPE_NONE, + TYPE_BIND_INDEX_BUFFER, + TYPE_BIND_PIPELINE, + TYPE_BIND_UNIFORM_SET, + TYPE_BIND_VERTEX_BUFFERS, + TYPE_CLEAR_ATTACHMENTS, + TYPE_DRAW, + TYPE_DRAW_INDEXED, + TYPE_EXECUTE_COMMANDS, + TYPE_NEXT_SUBPASS, + TYPE_SET_BLEND_CONSTANTS, + TYPE_SET_LINE_WIDTH, + TYPE_SET_PUSH_CONSTANT, + TYPE_SET_SCISSOR, + TYPE_SET_VIEWPORT, + TYPE_UNIFORM_SET_PREPARE_FOR_USE + }; + + Type type = TYPE_NONE; + }; + + struct RecordedCommand { + enum Type { + TYPE_NONE, + TYPE_BUFFER_CLEAR, + TYPE_BUFFER_COPY, + TYPE_BUFFER_GET_DATA, + TYPE_BUFFER_UPDATE, + TYPE_COMPUTE_LIST, + TYPE_DRAW_LIST, + TYPE_TEXTURE_CLEAR, + TYPE_TEXTURE_COPY, + TYPE_TEXTURE_GET_DATA, + TYPE_TEXTURE_RESOLVE, + TYPE_TEXTURE_UPDATE, + TYPE_CAPTURE_TIMESTAMP, + TYPE_MAX + }; + + Type type = TYPE_NONE; + int32_t adjacent_command_list_index = -1; + RDD::MemoryBarrier memory_barrier; + int32_t normalization_barrier_index = -1; + int normalization_barrier_count = 0; + int32_t transition_barrier_index = -1; + int32_t transition_barrier_count = 0; +#if USE_BUFFER_BARRIERS + int32_t buffer_barrier_index = -1; + int32_t buffer_barrier_count = 0; +#endif + int32_t label_index = -1; + BitField<RDD::PipelineStageBits> src_stages; + BitField<RDD::PipelineStageBits> dst_stages; + }; + + struct RecordedBufferCopy { + RDD::BufferID source; + RDD::BufferCopyRegion region; + }; + + struct RecordedBufferToTextureCopy { + RDD::BufferID from_buffer; + RDD::BufferTextureCopyRegion region; + }; + + enum ResourceUsage { + RESOURCE_USAGE_NONE, + RESOURCE_USAGE_TRANSFER_FROM, + RESOURCE_USAGE_TRANSFER_TO, + RESOURCE_USAGE_UNIFORM_BUFFER_READ, + RESOURCE_USAGE_INDIRECT_BUFFER_READ, + RESOURCE_USAGE_TEXTURE_BUFFER_READ, + RESOURCE_USAGE_TEXTURE_BUFFER_READ_WRITE, + RESOURCE_USAGE_STORAGE_BUFFER_READ, + RESOURCE_USAGE_STORAGE_BUFFER_READ_WRITE, + RESOURCE_USAGE_VERTEX_BUFFER_READ, + RESOURCE_USAGE_INDEX_BUFFER_READ, + RESOURCE_USAGE_TEXTURE_SAMPLE, + RESOURCE_USAGE_STORAGE_IMAGE_READ, + RESOURCE_USAGE_STORAGE_IMAGE_READ_WRITE, + RESOURCE_USAGE_ATTACHMENT_COLOR_READ, + RESOURCE_USAGE_ATTACHMENT_COLOR_READ_WRITE, + RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ, + RESOURCE_USAGE_ATTACHMENT_DEPTH_STENCIL_READ_WRITE + }; + + struct ResourceTracker { + uint32_t reference_count = 0; + int64_t command_frame = -1; + int32_t read_command_list_index = -1; + int32_t write_command_or_list_index = -1; + int32_t draw_list_index = -1; + int32_t compute_list_index = -1; + ResourceUsage usage = RESOURCE_USAGE_NONE; + BitField<RDD::BarrierAccessBits> usage_access; + RDD::BufferID buffer_driver_id; + RDD::TextureID texture_driver_id; + RDD::TextureSubresourceRange texture_subresources; + int32_t texture_slice_command_index = -1; + ResourceTracker *parent = nullptr; + ResourceTracker *dirty_shared_list = nullptr; + ResourceTracker *next_shared = nullptr; + Rect2i texture_slice_or_dirty_rect; + bool in_parent_dirty_list = false; + bool write_command_list_enabled = false; + + _FORCE_INLINE_ void reset_if_outdated(int64_t new_command_frame) { + if (new_command_frame != command_frame) { + usage_access.clear(); + command_frame = new_command_frame; + read_command_list_index = -1; + write_command_or_list_index = -1; + draw_list_index = -1; + compute_list_index = -1; + texture_slice_command_index = -1; + write_command_list_enabled = false; + } + } + }; + +private: + struct InstructionList { + LocalVector<uint8_t> data; + LocalVector<ResourceTracker *> command_trackers; + LocalVector<ResourceUsage> command_tracker_usages; + BitField<RDD::PipelineStageBits> stages; + int32_t index = 0; + + void clear() { + data.clear(); + command_trackers.clear(); + command_tracker_usages.clear(); + stages.clear(); + } + }; + + struct ComputeInstructionList : InstructionList { + // No extra contents. + }; + + struct DrawInstructionList : InstructionList { + RDD::RenderPassID render_pass; + RDD::FramebufferID framebuffer; + Rect2i region; + LocalVector<RDD::RenderPassClearValue> clear_values; + }; + + struct RecordedCommandSort { + uint32_t level = 0; + uint32_t priority = 0; + int32_t index = -1; + + RecordedCommandSort() = default; + + bool operator<(const RecordedCommandSort &p_other) const { + if (level < p_other.level) { + return true; + } else if (level > p_other.level) { + return false; + } + + if (priority < p_other.priority) { + return true; + } else if (priority > p_other.priority) { + return false; + } + + return index < p_other.index; + } + }; + + struct RecordedCommandListNode { + int32_t command_index = -1; + int32_t next_list_index = -1; + }; + + struct RecordedWriteListNode { + int32_t command_index = -1; + int32_t next_list_index = -1; + Rect2i subresources; + }; + + struct RecordedBufferClearCommand : RecordedCommand { + RDD::BufferID buffer; + uint32_t offset = 0; + uint32_t size = 0; + }; + + struct RecordedBufferCopyCommand : RecordedCommand { + RDD::BufferID source; + RDD::BufferID destination; + RDD::BufferCopyRegion region; + }; + + struct RecordedBufferGetDataCommand : RecordedCommand { + RDD::BufferID source; + RDD::BufferID destination; + RDD::BufferCopyRegion region; + }; + + struct RecordedBufferUpdateCommand : RecordedCommand { + RDD::BufferID destination; + uint32_t buffer_copies_count = 0; + + _FORCE_INLINE_ RecordedBufferCopy *buffer_copies() { + return reinterpret_cast<RecordedBufferCopy *>(&this[1]); + } + + _FORCE_INLINE_ const RecordedBufferCopy *buffer_copies() const { + return reinterpret_cast<const RecordedBufferCopy *>(&this[1]); + } + }; + + struct RecordedComputeListCommand : RecordedCommand { + uint32_t instruction_data_size = 0; + + _FORCE_INLINE_ uint8_t *instruction_data() { + return reinterpret_cast<uint8_t *>(&this[1]); + } + + _FORCE_INLINE_ const uint8_t *instruction_data() const { + return reinterpret_cast<const uint8_t *>(&this[1]); + } + }; + + struct RecordedDrawListCommand : RecordedCommand { + uint32_t instruction_data_size = 0; + RDD::RenderPassID render_pass; + RDD::FramebufferID framebuffer; + RDD::CommandBufferType command_buffer_type; + Rect2i region; + uint32_t clear_values_count = 0; + + _FORCE_INLINE_ RDD::RenderPassClearValue *clear_values() { + return reinterpret_cast<RDD::RenderPassClearValue *>(&this[1]); + } + + _FORCE_INLINE_ const RDD::RenderPassClearValue *clear_values() const { + return reinterpret_cast<const RDD::RenderPassClearValue *>(&this[1]); + } + + _FORCE_INLINE_ uint8_t *instruction_data() { + return reinterpret_cast<uint8_t *>(&clear_values()[clear_values_count]); + } + + _FORCE_INLINE_ const uint8_t *instruction_data() const { + return reinterpret_cast<const uint8_t *>(&clear_values()[clear_values_count]); + } + }; + + struct RecordedTextureClearCommand : RecordedCommand { + RDD::TextureID texture; + RDD::TextureSubresourceRange range; + Color color; + }; + + struct RecordedTextureCopyCommand : RecordedCommand { + RDD::TextureID from_texture; + RDD::TextureID to_texture; + RDD::TextureCopyRegion region; + }; + + struct RecordedTextureGetDataCommand : RecordedCommand { + RDD::TextureID from_texture; + RDD::BufferID to_buffer; + uint32_t buffer_texture_copy_regions_count = 0; + + _FORCE_INLINE_ RDD::BufferTextureCopyRegion *buffer_texture_copy_regions() { + return reinterpret_cast<RDD::BufferTextureCopyRegion *>(&this[1]); + } + + _FORCE_INLINE_ const RDD::BufferTextureCopyRegion *buffer_texture_copy_regions() const { + return reinterpret_cast<const RDD::BufferTextureCopyRegion *>(&this[1]); + } + }; + + struct RecordedTextureResolveCommand : RecordedCommand { + RDD::TextureID from_texture; + RDD::TextureID to_texture; + uint32_t src_layer = 0; + uint32_t src_mipmap = 0; + uint32_t dst_layer = 0; + uint32_t dst_mipmap = 0; + }; + + struct RecordedTextureUpdateCommand : RecordedCommand { + RDD::TextureID to_texture; + uint32_t buffer_to_texture_copies_count = 0; + + _FORCE_INLINE_ RecordedBufferToTextureCopy *buffer_to_texture_copies() { + return reinterpret_cast<RecordedBufferToTextureCopy *>(&this[1]); + } + + _FORCE_INLINE_ const RecordedBufferToTextureCopy *buffer_to_texture_copies() const { + return reinterpret_cast<const RecordedBufferToTextureCopy *>(&this[1]); + } + }; + + struct RecordedCaptureTimestampCommand : RecordedCommand { + RDD::QueryPoolID pool; + uint32_t index = 0; + }; + + struct DrawListBindIndexBufferInstruction : DrawListInstruction { + RDD::BufferID buffer; + RenderingDeviceCommons::IndexBufferFormat format; + uint32_t offset = 0; + }; + + struct DrawListBindPipelineInstruction : DrawListInstruction { + RDD::PipelineID pipeline; + }; + + struct DrawListBindUniformSetInstruction : DrawListInstruction { + RDD::UniformSetID uniform_set; + RDD::ShaderID shader; + uint32_t set_index = 0; + }; + + struct DrawListBindVertexBuffersInstruction : DrawListInstruction { + uint32_t vertex_buffers_count = 0; + + _FORCE_INLINE_ RDD::BufferID *vertex_buffers() { + return reinterpret_cast<RDD::BufferID *>(&this[1]); + } + + _FORCE_INLINE_ const RDD::BufferID *vertex_buffers() const { + return reinterpret_cast<const RDD::BufferID *>(&this[1]); + } + + _FORCE_INLINE_ uint64_t *vertex_buffer_offsets() { + return reinterpret_cast<uint64_t *>(&vertex_buffers()[vertex_buffers_count]); + } + + _FORCE_INLINE_ const uint64_t *vertex_buffer_offsets() const { + return reinterpret_cast<const uint64_t *>(&vertex_buffers()[vertex_buffers_count]); + } + }; + + struct DrawListClearAttachmentsInstruction : DrawListInstruction { + uint32_t attachments_clear_count = 0; + uint32_t attachments_clear_rect_count = 0; + + _FORCE_INLINE_ RDD::AttachmentClear *attachments_clear() { + return reinterpret_cast<RDD::AttachmentClear *>(&this[1]); + } + + _FORCE_INLINE_ const RDD::AttachmentClear *attachments_clear() const { + return reinterpret_cast<const RDD::AttachmentClear *>(&this[1]); + } + + _FORCE_INLINE_ Rect2i *attachments_clear_rect() { + return reinterpret_cast<Rect2i *>(&attachments_clear()[attachments_clear_count]); + } + + _FORCE_INLINE_ const Rect2i *attachments_clear_rect() const { + return reinterpret_cast<const Rect2i *>(&attachments_clear()[attachments_clear_count]); + } + }; + + struct DrawListDrawInstruction : DrawListInstruction { + uint32_t vertex_count = 0; + uint32_t instance_count = 0; + }; + + struct DrawListDrawIndexedInstruction : DrawListInstruction { + uint32_t index_count = 0; + uint32_t instance_count = 0; + uint32_t first_index = 0; + }; + + struct DrawListEndRenderPassInstruction : DrawListInstruction { + // No contents. + }; + + struct DrawListExecuteCommandsInstruction : DrawListInstruction { + RDD::CommandBufferID command_buffer; + }; + + struct DrawListSetPushConstantInstruction : DrawListInstruction { + uint32_t size = 0; + RDD::ShaderID shader; + + _FORCE_INLINE_ uint8_t *data() { + return reinterpret_cast<uint8_t *>(&this[1]); + } + + _FORCE_INLINE_ const uint8_t *data() const { + return reinterpret_cast<const uint8_t *>(&this[1]); + } + }; + + struct DrawListNextSubpassInstruction : DrawListInstruction { + RDD::CommandBufferType command_buffer_type; + }; + + struct DrawListSetBlendConstantsInstruction : DrawListInstruction { + Color color; + }; + + struct DrawListSetLineWidthInstruction : DrawListInstruction { + float width; + }; + + struct DrawListSetScissorInstruction : DrawListInstruction { + Rect2i rect; + }; + + struct DrawListSetViewportInstruction : DrawListInstruction { + Rect2i rect; + }; + + struct DrawListUniformSetPrepareForUseInstruction : DrawListInstruction { + RDD::UniformSetID uniform_set; + RDD::ShaderID shader; + uint32_t set_index = 0; + }; + + struct ComputeListBindPipelineInstruction : ComputeListInstruction { + RDD::PipelineID pipeline; + }; + + struct ComputeListBindUniformSetInstruction : ComputeListInstruction { + RDD::UniformSetID uniform_set; + RDD::ShaderID shader; + uint32_t set_index = 0; + }; + + struct ComputeListDispatchInstruction : ComputeListInstruction { + uint32_t x_groups = 0; + uint32_t y_groups = 0; + uint32_t z_groups = 0; + }; + + struct ComputeListDispatchIndirectInstruction : ComputeListInstruction { + RDD::BufferID buffer; + uint32_t offset = 0; + }; + + struct ComputeListSetPushConstantInstruction : ComputeListInstruction { + uint32_t size = 0; + RDD::ShaderID shader; + + _FORCE_INLINE_ uint8_t *data() { + return reinterpret_cast<uint8_t *>(&this[1]); + } + + _FORCE_INLINE_ const uint8_t *data() const { + return reinterpret_cast<const uint8_t *>(&this[1]); + } + }; + + struct ComputeListUniformSetPrepareForUseInstruction : ComputeListInstruction { + RDD::UniformSetID uniform_set; + RDD::ShaderID shader; + uint32_t set_index = 0; + }; + + struct BarrierGroup { + BitField<RDD::PipelineStageBits> src_stages; + BitField<RDD::PipelineStageBits> dst_stages; + RDD::MemoryBarrier memory_barrier; + LocalVector<RDD::TextureBarrier> normalization_barriers; + LocalVector<RDD::TextureBarrier> transition_barriers; +#if USE_BUFFER_BARRIERS + LocalVector<RDD::BufferBarrier> buffer_barriers; +#endif + + void clear() { + src_stages.clear(); + dst_stages.clear(); + memory_barrier.src_access.clear(); + memory_barrier.dst_access.clear(); + normalization_barriers.clear(); + transition_barriers.clear(); +#if USE_BUFFER_BARRIERS + buffer_barriers.clear(); +#endif + } + }; + + struct SecondaryCommandBuffer { + LocalVector<uint8_t> instruction_data; + RDD::CommandBufferID command_buffer; + RDD::CommandPoolID command_pool; + RDD::RenderPassID render_pass; + RDD::FramebufferID framebuffer; + WorkerThreadPool::TaskID task; + }; + + struct Frame { + TightLocalVector<SecondaryCommandBuffer> secondary_command_buffers; + uint32_t secondary_command_buffers_used = 0; + }; + + RDD *driver = nullptr; + int64_t tracking_frame = 0; + LocalVector<uint8_t> command_data; + LocalVector<uint32_t> command_data_offsets; + LocalVector<RDD::TextureBarrier> command_normalization_barriers; + LocalVector<RDD::TextureBarrier> command_transition_barriers; + LocalVector<RDD::BufferBarrier> command_buffer_barriers; + LocalVector<char> command_label_chars; + LocalVector<Color> command_label_colors; + LocalVector<uint32_t> command_label_offsets; + int32_t command_label_index = -1; + DrawInstructionList draw_instruction_list; + ComputeInstructionList compute_instruction_list; + uint32_t command_count = 0; + uint32_t command_label_count = 0; + LocalVector<RecordedCommandListNode> command_list_nodes; + LocalVector<RecordedWriteListNode> write_list_nodes; + int32_t command_timestamp_index = -1; + int32_t command_synchronization_index = -1; + bool command_synchronization_pending = false; + BarrierGroup barrier_group; + bool driver_honors_barriers = false; + TightLocalVector<Frame> frames; + uint32_t frame = 0; + +#ifdef DEV_ENABLED + RBMap<ResourceTracker *, uint32_t> write_dependency_counters; +#endif + + static bool _is_write_usage(ResourceUsage p_usage); + static RDD::TextureLayout _usage_to_image_layout(ResourceUsage p_usage); + static RDD::BarrierAccessBits _usage_to_access_bits(ResourceUsage p_usage); + int32_t _add_to_command_list(int32_t p_command_index, int32_t p_list_index); + void _add_adjacent_command(int32_t p_previous_command_index, int32_t p_command_index, RecordedCommand *r_command); + int32_t _add_to_write_list(int32_t p_command_index, Rect2i suberesources, int32_t p_list_index); + RecordedCommand *_allocate_command(uint32_t p_command_size, int32_t &r_command_index); + DrawListInstruction *_allocate_draw_list_instruction(uint32_t p_instruction_size); + ComputeListInstruction *_allocate_compute_list_instruction(uint32_t p_instruction_size); + void _add_command_to_graph(ResourceTracker **p_resource_trackers, ResourceUsage *p_resource_usages, uint32_t p_resource_count, int32_t p_command_index, RecordedCommand *r_command); + void _add_texture_barrier_to_command(RDD::TextureID p_texture_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, ResourceUsage p_prev_usage, ResourceUsage p_next_usage, RDD::TextureSubresourceRange p_subresources, LocalVector<RDD::TextureBarrier> &r_barrier_vector, int32_t &r_barrier_index, int32_t &r_barrier_count); +#if USE_BUFFER_BARRIERS + void _add_buffer_barrier_to_command(RDD::BufferID p_buffer_id, BitField<RDD::BarrierAccessBits> p_src_access, BitField<RDD::BarrierAccessBits> p_dst_access, int32_t &r_barrier_index, int32_t &r_barrier_count); +#endif + void _run_compute_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + void _run_draw_list_command(RDD::CommandBufferID p_command_buffer, const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + void _run_secondary_command_buffer_task(const SecondaryCommandBuffer *p_secondary); + void _wait_for_secondary_command_buffer_tasks(); + void _run_render_commands(RDD::CommandBufferID p_command_buffer, int32_t p_level, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level); + void _run_label_command_change(RDD::CommandBufferID p_command_buffer, int32_t p_new_label_index, int32_t p_new_level, bool p_ignore_previous_value, bool p_use_label_for_empty, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, int32_t &r_current_label_index, int32_t &r_current_label_level); + void _boost_priority_for_render_commands(RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, uint32_t &r_boosted_priority); + void _group_barriers_for_render_commands(RDD::CommandBufferID p_command_buffer, const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count, bool p_full_memory_barrier); + void _print_render_commands(const RecordedCommandSort *p_sorted_commands, uint32_t p_sorted_commands_count); + void _print_draw_list(const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + void _print_compute_list(const uint8_t *p_instruction_data, uint32_t p_instruction_data_size); + +public: + RenderingDeviceGraph(); + ~RenderingDeviceGraph(); + void initialize(RDD *p_driver, uint32_t p_frame_count, uint32_t p_secondary_command_buffers_per_frame); + void begin(); + void add_buffer_clear(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_offset, uint32_t p_size); + void add_buffer_copy(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, RDD::BufferCopyRegion p_region); + void add_buffer_get_data(RDD::BufferID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, RDD::BufferCopyRegion p_region); + void add_buffer_update(RDD::BufferID p_dst, ResourceTracker *p_dst_tracker, VectorView<RecordedBufferCopy> p_buffer_copies); + void add_compute_list_begin(); + void add_compute_list_bind_pipeline(RDD::PipelineID p_pipeline); + void add_compute_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); + void add_compute_list_dispatch(uint32_t p_x_groups, uint32_t p_y_groups, uint32_t p_z_groups); + void add_compute_list_dispatch_indirect(RDD::BufferID p_buffer, uint32_t p_offset); + void add_compute_list_set_push_constant(RDD::ShaderID p_shader, const void *p_data, uint32_t p_data_size); + void add_compute_list_uniform_set_prepare_for_use(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); + void add_compute_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage); + void add_compute_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages); + void add_compute_list_end(); + void add_draw_list_begin(RDD::RenderPassID p_render_pass, RDD::FramebufferID p_framebuffer, Rect2i p_region, VectorView<RDD::RenderPassClearValue> p_clear_values, bool p_uses_color, bool p_uses_depth); + void add_draw_list_bind_index_buffer(RDD::BufferID p_buffer, RDD::IndexBufferFormat p_format, uint32_t p_offset); + void add_draw_list_bind_pipeline(RDD::PipelineID p_pipeline, BitField<RDD::PipelineStageBits> p_pipeline_stage_bits); + void add_draw_list_bind_uniform_set(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); + void add_draw_list_bind_vertex_buffers(VectorView<RDD::BufferID> p_vertex_buffers, VectorView<uint64_t> p_vertex_buffer_offsets); + void add_draw_list_clear_attachments(VectorView<RDD::AttachmentClear> p_attachments_clear, VectorView<Rect2i> p_attachments_clear_rect); + void add_draw_list_draw(uint32_t p_vertex_count, uint32_t p_instance_count); + void add_draw_list_draw_indexed(uint32_t p_index_count, uint32_t p_instance_count, uint32_t p_first_index); + void add_draw_list_execute_commands(RDD::CommandBufferID p_command_buffer); + void add_draw_list_next_subpass(RDD::CommandBufferType p_command_buffer_type); + void add_draw_list_set_blend_constants(const Color &p_color); + void add_draw_list_set_line_width(float p_width); + void add_draw_list_set_push_constant(RDD::ShaderID p_shader, const void *p_data, uint32_t p_data_size); + void add_draw_list_set_scissor(Rect2i p_rect); + void add_draw_list_set_viewport(Rect2i p_rect); + void add_draw_list_uniform_set_prepare_for_use(RDD::ShaderID p_shader, RDD::UniformSetID p_uniform_set, uint32_t set_index); + void add_draw_list_usage(ResourceTracker *p_tracker, ResourceUsage p_usage); + void add_draw_list_usages(VectorView<ResourceTracker *> p_trackers, VectorView<ResourceUsage> p_usages); + void add_draw_list_end(); + void add_texture_clear(RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, const Color &p_color, const RDD::TextureSubresourceRange &p_range); + void add_texture_copy(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, RDD::TextureCopyRegion p_region); + void add_texture_get_data(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::BufferID p_dst, VectorView<RDD::BufferTextureCopyRegion> p_buffer_texture_copy_regions); + void add_texture_resolve(RDD::TextureID p_src, ResourceTracker *p_src_tracker, RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, uint32_t p_src_layer, uint32_t p_src_mipmap, uint32_t p_dst_layer, uint32_t p_dst_mipmap); + void add_texture_update(RDD::TextureID p_dst, ResourceTracker *p_dst_tracker, VectorView<RecordedBufferToTextureCopy> p_buffer_copies); + void add_capture_timestamp(RDD::QueryPoolID p_query_pool, uint32_t p_index); + void add_synchronization(); + void begin_label(const String &p_label_name, const Color &p_color); + void end_label(); + void end(RDD::CommandBufferID p_command_buffer, bool p_reorder_commands, bool p_full_barriers); + static ResourceTracker *resource_tracker_create(); + static void resource_tracker_free(ResourceTracker *tracker); +}; + +using RDG = RenderingDeviceGraph; + +#endif // RENDERING_DEVICE_GRAPH_H diff --git a/servers/rendering/rendering_server_default.cpp b/servers/rendering/rendering_server_default.cpp index 77fe91e4c9..1e6a311cc2 100644 --- a/servers/rendering/rendering_server_default.cpp +++ b/servers/rendering/rendering_server_default.cpp @@ -91,10 +91,7 @@ void RenderingServerDefault::_draw(bool p_swap_buffers, double frame_step) { RSG::viewport->draw_viewports(p_swap_buffers); RSG::canvas_render->update(); - if (!OS::get_singleton()->get_current_rendering_driver_name().begins_with("opengl3")) { - // Already called for gl_compatibility renderer. - RSG::rasterizer->end_frame(p_swap_buffers); - } + RSG::rasterizer->end_frame(p_swap_buffers); XRServer *xr_server = XRServer::get_singleton(); if (xr_server != nullptr) { @@ -214,6 +211,7 @@ void RenderingServerDefault::_finish() { free(test_cube); } + RSG::canvas->finalize(); RSG::rasterizer->finalize(); } diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp index a2da26eb65..fef1a205d6 100644 --- a/servers/rendering/shader_language.cpp +++ b/servers/rendering/shader_language.cpp @@ -4665,7 +4665,7 @@ bool ShaderLanguage::_validate_assign(Node *p_node, const FunctionInfo &p_functi return false; } -bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringName p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { +bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat) { for (int i = 0; i < shader->vfunctions.size(); i++) { if (shader->vfunctions[i].name == p_name) { ERR_FAIL_INDEX_V(p_argument, shader->vfunctions[i].function->arguments.size(), false); @@ -4699,7 +4699,7 @@ bool ShaderLanguage::_propagate_function_call_sampler_uniform_settings(StringNam 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) { +bool ShaderLanguage::_propagate_function_call_sampler_builtin_reference(const StringName &p_name, int p_argument, const StringName &p_builtin) { for (int i = 0; i < shader->vfunctions.size(); i++) { if (shader->vfunctions[i].name == p_name) { ERR_FAIL_INDEX_V(p_argument, shader->vfunctions[i].function->arguments.size(), false); diff --git a/servers/rendering/shader_language.h b/servers/rendering/shader_language.h index c707f6aad7..737545b8ca 100644 --- a/servers/rendering/shader_language.h +++ b/servers/rendering/shader_language.h @@ -1092,8 +1092,8 @@ private: bool _validate_function_call(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, DataType *r_ret_type, StringName *r_ret_type_str, bool *r_is_custom_function = nullptr); bool _parse_function_arguments(BlockNode *p_block, const FunctionInfo &p_function_info, OperatorNode *p_func, int *r_complete_arg = nullptr); - 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); + bool _propagate_function_call_sampler_uniform_settings(const StringName &p_name, int p_argument, TextureFilter p_filter, TextureRepeat p_repeat); + bool _propagate_function_call_sampler_builtin_reference(const StringName &p_name, int p_argument, const StringName &p_builtin); bool _validate_varying_assign(ShaderNode::Varying &p_varying, String *r_message); bool _check_node_constness(const Node *p_node) const; diff --git a/servers/rendering_server.cpp b/servers/rendering_server.cpp index 00015b74a1..2d61e47062 100644 --- a/servers/rendering_server.cpp +++ b/servers/rendering_server.cpp @@ -2204,6 +2204,25 @@ void RenderingServer::fix_surface_compatibility(SurfaceData &p_surface, const St } #endif +#ifdef TOOLS_ENABLED +void RenderingServer::get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const { + String pf = p_function; + if (p_idx == 0) { + if (pf == "global_shader_parameter_set" || pf == "global_shader_parameter_set_override" || + pf == "global_shader_parameter_get" || pf == "global_shader_parameter_get_type" || pf == "global_shader_parameter_remove") { + for (StringName E : global_shader_parameter_get_list()) { + r_options->push_back(E.operator String().quote()); + } + } else if (pf == "has_os_feature") { + for (String E : { "\"rgtc\"", "\"s3tc\"", "\"bptc\"", "\"etc\"", "\"etc2\"", "\"astc\"" }) { + r_options->push_back(E); + } + } + } + Object::get_argument_options(p_function, p_idx, r_options); +} +#endif + void RenderingServer::_bind_methods() { BIND_CONSTANT(NO_INDEX_ARRAY); BIND_CONSTANT(ARRAY_WEIGHTS_SIZE); @@ -3552,7 +3571,6 @@ void RenderingServer::init() { GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/limits/spatial_indexer/update_iterations_per_frame", PROPERTY_HINT_RANGE, "0,1024,1"), 10); GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/limits/spatial_indexer/threaded_cull_minimum_instances", PROPERTY_HINT_RANGE, "32,65536,1"), 1000); - GLOBAL_DEF_RST(PropertyInfo(Variant::INT, "rendering/limits/forward_renderer/threaded_render_minimum_instances", PROPERTY_HINT_RANGE, "32,65536,1"), 500); GLOBAL_DEF(PropertyInfo(Variant::FLOAT, "rendering/limits/cluster_builder/max_clustered_elements", PROPERTY_HINT_RANGE, "32,8192,1"), 512); diff --git a/servers/rendering_server.h b/servers/rendering_server.h index 016801fa6e..8b17463b4d 100644 --- a/servers/rendering_server.h +++ b/servers/rendering_server.h @@ -1639,6 +1639,10 @@ public: virtual void call_on_render_thread(const Callable &p_callable) = 0; +#ifdef TOOLS_ENABLED + virtual void get_argument_options(const StringName &p_function, int p_idx, List<String> *r_options) const override; +#endif + RenderingServer(); virtual ~RenderingServer(); diff --git a/tests/core/string/test_node_path.h b/tests/core/string/test_node_path.h index b0c810bb54..031a33c570 100644 --- a/tests/core/string/test_node_path.h +++ b/tests/core/string/test_node_path.h @@ -98,44 +98,44 @@ TEST_CASE("[NodePath] Relative path") { } TEST_CASE("[NodePath] Absolute path") { - const NodePath node_path_aboslute = NodePath("/root/Sprite2D"); + const NodePath node_path_absolute = NodePath("/root/Sprite2D"); CHECK_MESSAGE( - node_path_aboslute.get_as_property_path() == NodePath(":root/Sprite2D"), + node_path_absolute.get_as_property_path() == NodePath(":root/Sprite2D"), "The returned property path should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_concatenated_subnames() == "", + node_path_absolute.get_concatenated_subnames() == "", "The returned concatenated subnames should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(0) == "root", + node_path_absolute.get_name(0) == "root", "The returned name at index 0 should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(1) == "Sprite2D", + node_path_absolute.get_name(1) == "Sprite2D", "The returned name at index 1 should match the expected value."); ERR_PRINT_OFF; CHECK_MESSAGE( - node_path_aboslute.get_name(2) == "", + node_path_absolute.get_name(2) == "", "The returned name at invalid index 2 should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_name(-1) == "", + node_path_absolute.get_name(-1) == "", "The returned name at invalid index -1 should match the expected value."); ERR_PRINT_ON; CHECK_MESSAGE( - node_path_aboslute.get_name_count() == 2, + node_path_absolute.get_name_count() == 2, "The returned number of names should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.get_subname_count() == 0, + node_path_absolute.get_subname_count() == 0, "The returned number of subnames should match the expected value."); CHECK_MESSAGE( - node_path_aboslute.is_absolute(), + node_path_absolute.is_absolute(), "The node path should be considered absolute."); CHECK_MESSAGE( - !node_path_aboslute.is_empty(), + !node_path_absolute.is_empty(), "The node path shouldn't be considered empty."); } diff --git a/tests/core/variant/test_array.h b/tests/core/variant/test_array.h index 228d77b3b5..ea61ae2a02 100644 --- a/tests/core/variant/test_array.h +++ b/tests/core/variant/test_array.h @@ -367,7 +367,7 @@ TEST_CASE("[Array] Duplicate recursive array") { Array a_shallow = a.duplicate(false); CHECK_EQ(a, a_shallow); - // Deep copy of recursive array endup with recursion limit and return + // Deep copy of recursive array ends up with recursion limit and return // an invalid result (multiple nested arrays), the point is we should // not end up with a segfault and an error log should be printed ERR_PRINT_OFF; diff --git a/tests/test_main.cpp b/tests/test_main.cpp index 5187ebd00f..7f49805274 100644 --- a/tests/test_main.cpp +++ b/tests/test_main.cpp @@ -30,6 +30,8 @@ #include "test_main.h" +#include "editor/editor_paths.h" +#include "editor/editor_settings.h" #include "tests/core/config/test_project_settings.h" #include "tests/core/input/test_input_event.h" #include "tests/core/input/test_input_event_key.h" @@ -94,7 +96,6 @@ #include "tests/scene/test_arraymesh.h" #include "tests/scene/test_audio_stream_wav.h" #include "tests/scene/test_bit_map.h" -#include "tests/scene/test_camera_3d.h" #include "tests/scene/test_code_edit.h" #include "tests/scene/test_color_picker.h" #include "tests/scene/test_control.h" @@ -103,16 +104,12 @@ #include "tests/scene/test_curve_3d.h" #include "tests/scene/test_gradient.h" #include "tests/scene/test_navigation_agent_2d.h" -#include "tests/scene/test_navigation_agent_3d.h" #include "tests/scene/test_navigation_obstacle_2d.h" -#include "tests/scene/test_navigation_obstacle_3d.h" #include "tests/scene/test_navigation_region_2d.h" -#include "tests/scene/test_navigation_region_3d.h" #include "tests/scene/test_node.h" #include "tests/scene/test_node_2d.h" #include "tests/scene/test_packed_scene.h" #include "tests/scene/test_path_2d.h" -#include "tests/scene/test_path_3d.h" #include "tests/scene/test_primitives.h" #include "tests/scene/test_sprite_frames.h" #include "tests/scene/test_text_edit.h" @@ -122,10 +119,18 @@ #include "tests/scene/test_window.h" #include "tests/servers/rendering/test_shader_preprocessor.h" #include "tests/servers/test_navigation_server_2d.h" -#include "tests/servers/test_navigation_server_3d.h" #include "tests/servers/test_text_server.h" #include "tests/test_validate_testing.h" +#ifndef _3D_DISABLED +#include "tests/scene/test_camera_3d.h" +#include "tests/scene/test_navigation_agent_3d.h" +#include "tests/scene/test_navigation_obstacle_3d.h" +#include "tests/scene/test_navigation_region_3d.h" +#include "tests/scene/test_path_3d.h" +#include "tests/servers/test_navigation_server_3d.h" +#endif // _3D_DISABLED + #include "modules/modules_tests.gen.h" #include "tests/display_server_mock.h" @@ -221,7 +226,7 @@ struct GodotTestCaseListener : public doctest::IReporter { String name = String(p_in.m_name); String suite_name = String(p_in.m_test_suite); - if (name.find("[SceneTree]") != -1) { + if (name.find("[SceneTree]") != -1 || name.find("[Editor]") != -1) { memnew(MessageQueue); memnew(Input); @@ -264,6 +269,13 @@ struct GodotTestCaseListener : public doctest::IReporter { if (!DisplayServer::get_singleton()->has_feature(DisplayServer::Feature::FEATURE_SUBWINDOWS)) { SceneTree::get_singleton()->get_root()->set_embedding_subwindows(true); } + + if (name.find("[Editor]") != -1) { + Engine::get_singleton()->set_editor_hint(true); + EditorPaths::create(); + EditorSettings::create(); + } + return; } @@ -286,6 +298,12 @@ struct GodotTestCaseListener : public doctest::IReporter { } void test_case_end(const doctest::CurrentTestCaseStats &) override { + if (EditorSettings::get_singleton()) { + EditorSettings::destroy(); + } + + Engine::get_singleton()->set_editor_hint(false); + if (SceneTree::get_singleton()) { SceneTree::get_singleton()->finalize(); } diff --git a/thirdparty/README.md b/thirdparty/README.md index 510219194b..51ba9f1df4 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -727,7 +727,7 @@ with the provided patch. ## openxr - Upstream: https://github.com/KhronosGroup/OpenXR-SDK -- Version: 1.0.31 (95fe35ffb383710a6e0567e958ead9a3b66e930c, 2023) +- Version: 1.0.33 (dc1e23937fe45eabcce80f6588cf47449edb29d1, 2024) - License: Apache 2.0 Files extracted from upstream source: @@ -861,7 +861,7 @@ instead of `miniz.h` as an external dependency. ## thorvg - Upstream: https://github.com/thorvg/thorvg -- Version: 0.11.6 (3dba4f12f8f05f86acbc51096ca3a15f5d96bc06, 2023) +- Version: 0.12.0 (25ea242d3867ed66807714f5a52d080984d3c8cc, 2024) - License: MIT Files extracted from upstream source: diff --git a/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl b/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl index 65cc8b67ef..d989416eb3 100644 --- a/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl +++ b/thirdparty/amd-fsr2/shaders/ffx_fsr2_depth_clip_pass.glsl @@ -29,7 +29,6 @@ #define FSR2_BIND_SRV_DILATED_DEPTH 2 #define FSR2_BIND_SRV_REACTIVE_MASK 3 #define FSR2_BIND_SRV_TRANSPARENCY_AND_COMPOSITION_MASK 4 -#define FSR2_BIND_SRV_PREPARED_INPUT_COLOR 5 #define FSR2_BIND_SRV_PREVIOUS_DILATED_MOTION_VECTORS 6 #define FSR2_BIND_SRV_INPUT_MOTION_VECTORS 7 #define FSR2_BIND_SRV_INPUT_COLOR 8 diff --git a/thirdparty/openxr/COPYING.adoc b/thirdparty/openxr/COPYING.adoc index 473e7fdc5d..18c85fa6c7 100644 --- a/thirdparty/openxr/COPYING.adoc +++ b/thirdparty/openxr/COPYING.adoc @@ -1,6 +1,6 @@ = COPYING.adoc for the Khronos Group OpenXR projects -// Copyright (c) 2020-2023, The Khronos Group Inc. +// Copyright (c) 2020-2024, The Khronos Group Inc. // // SPDX-License-Identifier: CC-BY-4.0 diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h index 9ba17cbe06..3e9d6599bb 100644 --- a/thirdparty/openxr/include/openxr/openxr.h +++ b/thirdparty/openxr/include/openxr/openxr.h @@ -2,7 +2,7 @@ #define OPENXR_H_ 1 /* -** Copyright 2017-2023 The Khronos Group Inc. +** Copyright 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -25,7 +25,7 @@ extern "C" { ((((major) & 0xffffULL) << 48) | (((minor) & 0xffffULL) << 32) | ((patch) & 0xffffffffULL)) // OpenXR current version number. -#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 31) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 33) #define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL) #define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL) @@ -218,6 +218,18 @@ typedef enum XrResult { XR_RENDER_MODEL_UNAVAILABLE_FB = 1000119020, XR_ERROR_MARKER_NOT_TRACKED_VARJO = -1000124000, XR_ERROR_MARKER_ID_INVALID_VARJO = -1000124001, + XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML = -1000138000, + XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML = -1000138001, + XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML = -1000138002, + XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML = -1000138003, + XR_ERROR_MARKER_INVALID_ML = -1000138004, + XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML = -1000139000, + XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML = -1000139001, + XR_ERROR_LOCALIZATION_MAP_FAIL_ML = -1000139002, + XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML = -1000139003, + XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML = -1000139004, + XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML = -1000139005, + XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML = -1000139006, XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT = -1000142001, XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT = -1000142002, XR_SCENE_MARKER_DATA_NOT_STRING_MSFT = 1000147000, @@ -228,6 +240,7 @@ typedef enum XrResult { XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB = -1000169004, XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META = -1000266000, XR_ERROR_HINT_ALREADY_SET_QCOM = -1000306000, + XR_ERROR_NOT_AN_ANCHOR_HTC = -1000319000, XR_ERROR_SPACE_NOT_LOCATABLE_EXT = -1000429000, XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT = -1000429001, XR_RESULT_MAX_ENUM = 0x7FFFFFFF @@ -445,6 +458,20 @@ typedef enum XrStructureType { XR_TYPE_FRAME_END_INFO_ML = 1000135000, XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML = 1000136000, XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML = 1000137000, + XR_TYPE_SYSTEM_MARKER_UNDERSTANDING_PROPERTIES_ML = 1000138000, + XR_TYPE_MARKER_DETECTOR_CREATE_INFO_ML = 1000138001, + XR_TYPE_MARKER_DETECTOR_ARUCO_INFO_ML = 1000138002, + XR_TYPE_MARKER_DETECTOR_SIZE_INFO_ML = 1000138003, + XR_TYPE_MARKER_DETECTOR_APRIL_TAG_INFO_ML = 1000138004, + XR_TYPE_MARKER_DETECTOR_CUSTOM_PROFILE_INFO_ML = 1000138005, + XR_TYPE_MARKER_DETECTOR_SNAPSHOT_INFO_ML = 1000138006, + XR_TYPE_MARKER_DETECTOR_STATE_ML = 1000138007, + XR_TYPE_MARKER_SPACE_CREATE_INFO_ML = 1000138008, + XR_TYPE_LOCALIZATION_MAP_ML = 1000139000, + XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML = 1000139001, + XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML = 1000139002, + XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML = 1000139003, + XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML = 1000139004, XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML = 1000472000, XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML = 1000472001, XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML = 1000472002, @@ -534,6 +561,8 @@ typedef enum XrStructureType { XR_TYPE_FOVEATION_APPLY_INFO_HTC = 1000318000, XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC = 1000318001, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC = 1000318002, + XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC = 1000319000, + XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC = 1000319001, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT = 1000373000, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX = 1000375000, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX = 1000375001, @@ -580,6 +609,7 @@ typedef enum XrReferenceSpaceType { XR_REFERENCE_SPACE_TYPE_STAGE = 3, XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT = 1000038000, XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO = 1000121000, + XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML = 1000139000, XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT = 1000426000, XR_REFERENCE_SPACE_TYPE_MAX_ENUM = 0x7FFFFFFF } XrReferenceSpaceType; @@ -634,6 +664,8 @@ typedef enum XrObjectType { XR_OBJECT_TYPE_PASSTHROUGH_FB = 1000118000, XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB = 1000118002, XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB = 1000118004, + XR_OBJECT_TYPE_MARKER_DETECTOR_ML = 1000138000, + XR_OBJECT_TYPE_EXPORTED_LOCALIZATION_MAP_ML = 1000139000, XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT = 1000142000, XR_OBJECT_TYPE_FACE_TRACKER_FB = 1000201000, XR_OBJECT_TYPE_EYE_TRACKER_FB = 1000202000, @@ -1677,7 +1709,7 @@ typedef struct XrCompositionLayerColorScaleBiasKHR { #define XR_KHR_loader_init 1 -#define XR_KHR_loader_init_SPEC_VERSION 1 +#define XR_KHR_loader_init_SPEC_VERSION 2 #define XR_KHR_LOADER_INIT_EXTENSION_NAME "XR_KHR_loader_init" typedef struct XR_MAY_ALIAS XrLoaderInitInfoBaseHeaderKHR { XrStructureType type; @@ -4342,6 +4374,360 @@ typedef struct XrGlobalDimmerFrameEndInfoML { +#define XR_ML_marker_understanding 1 +XR_DEFINE_ATOM(XrMarkerML) +XR_DEFINE_HANDLE(XrMarkerDetectorML) +#define XR_ML_marker_understanding_SPEC_VERSION 1 +#define XR_ML_MARKER_UNDERSTANDING_EXTENSION_NAME "XR_ML_marker_understanding" + +typedef enum XrMarkerDetectorProfileML { + XR_MARKER_DETECTOR_PROFILE_DEFAULT_ML = 0, + XR_MARKER_DETECTOR_PROFILE_SPEED_ML = 1, + XR_MARKER_DETECTOR_PROFILE_ACCURACY_ML = 2, + XR_MARKER_DETECTOR_PROFILE_SMALL_TARGETS_ML = 3, + XR_MARKER_DETECTOR_PROFILE_LARGE_FOV_ML = 4, + XR_MARKER_DETECTOR_PROFILE_CUSTOM_ML = 5, + XR_MARKER_DETECTOR_PROFILE_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorProfileML; + +typedef enum XrMarkerTypeML { + XR_MARKER_TYPE_ARUCO_ML = 0, + XR_MARKER_TYPE_APRIL_TAG_ML = 1, + XR_MARKER_TYPE_QR_ML = 2, + XR_MARKER_TYPE_EAN_13_ML = 3, + XR_MARKER_TYPE_UPC_A_ML = 4, + XR_MARKER_TYPE_CODE_128_ML = 5, + XR_MARKER_TYPE_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerTypeML; + +typedef enum XrMarkerArucoDictML { + XR_MARKER_ARUCO_DICT_4X4_50_ML = 0, + XR_MARKER_ARUCO_DICT_4X4_100_ML = 1, + XR_MARKER_ARUCO_DICT_4X4_250_ML = 2, + XR_MARKER_ARUCO_DICT_4X4_1000_ML = 3, + XR_MARKER_ARUCO_DICT_5X5_50_ML = 4, + XR_MARKER_ARUCO_DICT_5X5_100_ML = 5, + XR_MARKER_ARUCO_DICT_5X5_250_ML = 6, + XR_MARKER_ARUCO_DICT_5X5_1000_ML = 7, + XR_MARKER_ARUCO_DICT_6X6_50_ML = 8, + XR_MARKER_ARUCO_DICT_6X6_100_ML = 9, + XR_MARKER_ARUCO_DICT_6X6_250_ML = 10, + XR_MARKER_ARUCO_DICT_6X6_1000_ML = 11, + XR_MARKER_ARUCO_DICT_7X7_50_ML = 12, + XR_MARKER_ARUCO_DICT_7X7_100_ML = 13, + XR_MARKER_ARUCO_DICT_7X7_250_ML = 14, + XR_MARKER_ARUCO_DICT_7X7_1000_ML = 15, + XR_MARKER_ARUCO_DICT_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerArucoDictML; + +typedef enum XrMarkerAprilTagDictML { + XR_MARKER_APRIL_TAG_DICT_16H5_ML = 0, + XR_MARKER_APRIL_TAG_DICT_25H9_ML = 1, + XR_MARKER_APRIL_TAG_DICT_36H10_ML = 2, + XR_MARKER_APRIL_TAG_DICT_36H11_ML = 3, + XR_MARKER_APRIL_TAG_DICT_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerAprilTagDictML; + +typedef enum XrMarkerDetectorFpsML { + XR_MARKER_DETECTOR_FPS_LOW_ML = 0, + XR_MARKER_DETECTOR_FPS_MEDIUM_ML = 1, + XR_MARKER_DETECTOR_FPS_HIGH_ML = 2, + XR_MARKER_DETECTOR_FPS_MAX_ML = 3, + XR_MARKER_DETECTOR_FPS_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorFpsML; + +typedef enum XrMarkerDetectorResolutionML { + XR_MARKER_DETECTOR_RESOLUTION_LOW_ML = 0, + XR_MARKER_DETECTOR_RESOLUTION_MEDIUM_ML = 1, + XR_MARKER_DETECTOR_RESOLUTION_HIGH_ML = 2, + XR_MARKER_DETECTOR_RESOLUTION_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorResolutionML; + +typedef enum XrMarkerDetectorCameraML { + XR_MARKER_DETECTOR_CAMERA_RGB_CAMERA_ML = 0, + XR_MARKER_DETECTOR_CAMERA_WORLD_CAMERAS_ML = 1, + XR_MARKER_DETECTOR_CAMERA_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorCameraML; + +typedef enum XrMarkerDetectorCornerRefineMethodML { + XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_NONE_ML = 0, + XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_SUBPIX_ML = 1, + XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_CONTOUR_ML = 2, + XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_APRIL_TAG_ML = 3, + XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorCornerRefineMethodML; + +typedef enum XrMarkerDetectorFullAnalysisIntervalML { + XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ML = 0, + XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_FAST_ML = 1, + XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MEDIUM_ML = 2, + XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_SLOW_ML = 3, + XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorFullAnalysisIntervalML; + +typedef enum XrMarkerDetectorStatusML { + XR_MARKER_DETECTOR_STATUS_PENDING_ML = 0, + XR_MARKER_DETECTOR_STATUS_READY_ML = 1, + XR_MARKER_DETECTOR_STATUS_ERROR_ML = 2, + XR_MARKER_DETECTOR_STATUS_MAX_ENUM_ML = 0x7FFFFFFF +} XrMarkerDetectorStatusML; +// XrSystemMarkerUnderstandingPropertiesML extends XrSystemProperties +typedef struct XrSystemMarkerUnderstandingPropertiesML { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsMarkerUnderstanding; +} XrSystemMarkerUnderstandingPropertiesML; + +typedef struct XrMarkerDetectorCreateInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrMarkerDetectorProfileML profile; + XrMarkerTypeML markerType; +} XrMarkerDetectorCreateInfoML; + +// XrMarkerDetectorArucoInfoML extends XrMarkerDetectorCreateInfoML +typedef struct XrMarkerDetectorArucoInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrMarkerArucoDictML arucoDict; +} XrMarkerDetectorArucoInfoML; + +// XrMarkerDetectorSizeInfoML extends XrMarkerDetectorCreateInfoML +typedef struct XrMarkerDetectorSizeInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + float markerLength; +} XrMarkerDetectorSizeInfoML; + +// XrMarkerDetectorAprilTagInfoML extends XrMarkerDetectorCreateInfoML +typedef struct XrMarkerDetectorAprilTagInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrMarkerAprilTagDictML aprilTagDict; +} XrMarkerDetectorAprilTagInfoML; + +// XrMarkerDetectorCustomProfileInfoML extends XrMarkerDetectorCreateInfoML +typedef struct XrMarkerDetectorCustomProfileInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrMarkerDetectorFpsML fpsHint; + XrMarkerDetectorResolutionML resolutionHint; + XrMarkerDetectorCameraML cameraHint; + XrMarkerDetectorCornerRefineMethodML cornerRefineMethod; + XrBool32 useEdgeRefinement; + XrMarkerDetectorFullAnalysisIntervalML fullAnalysisIntervalHint; +} XrMarkerDetectorCustomProfileInfoML; + +typedef struct XrMarkerDetectorSnapshotInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrMarkerDetectorSnapshotInfoML; + +typedef struct XrMarkerDetectorStateML { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrMarkerDetectorStatusML state; +} XrMarkerDetectorStateML; + +typedef struct XrMarkerSpaceCreateInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrMarkerDetectorML markerDetector; + XrMarkerML marker; + XrPosef poseInMarkerSpace; +} XrMarkerSpaceCreateInfoML; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerDetectorML)(XrSession session, const XrMarkerDetectorCreateInfoML* createInfo, XrMarkerDetectorML* markerDetector); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyMarkerDetectorML)(XrMarkerDetectorML markerDetector); +typedef XrResult (XRAPI_PTR *PFN_xrSnapshotMarkerDetectorML)(XrMarkerDetectorML markerDetector, XrMarkerDetectorSnapshotInfoML* snapshotInfo); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerDetectorStateML)(XrMarkerDetectorML markerDetector, XrMarkerDetectorStateML* state); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkersML)(XrMarkerDetectorML markerDetector, uint32_t markerCapacityInput, uint32_t* markerCountOutput, XrMarkerML* markers); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerReprojectionErrorML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, float* reprojectionErrorMeters); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerLengthML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, float* meters); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerNumberML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, uint64_t* number); +typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerStringML)(XrMarkerDetectorML markerDetector, XrMarkerML marker, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer); +typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerSpaceML)(XrSession session, const XrMarkerSpaceCreateInfoML* createInfo, XrSpace* space); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerDetectorML( + XrSession session, + const XrMarkerDetectorCreateInfoML* createInfo, + XrMarkerDetectorML* markerDetector); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyMarkerDetectorML( + XrMarkerDetectorML markerDetector); + +XRAPI_ATTR XrResult XRAPI_CALL xrSnapshotMarkerDetectorML( + XrMarkerDetectorML markerDetector, + XrMarkerDetectorSnapshotInfoML* snapshotInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerDetectorStateML( + XrMarkerDetectorML markerDetector, + XrMarkerDetectorStateML* state); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkersML( + XrMarkerDetectorML markerDetector, + uint32_t markerCapacityInput, + uint32_t* markerCountOutput, + XrMarkerML* markers); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerReprojectionErrorML( + XrMarkerDetectorML markerDetector, + XrMarkerML marker, + float* reprojectionErrorMeters); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerLengthML( + XrMarkerDetectorML markerDetector, + XrMarkerML marker, + float* meters); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerNumberML( + XrMarkerDetectorML markerDetector, + XrMarkerML marker, + uint64_t* number); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerStringML( + XrMarkerDetectorML markerDetector, + XrMarkerML marker, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + char* buffer); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateMarkerSpaceML( + XrSession session, + const XrMarkerSpaceCreateInfoML* createInfo, + XrSpace* space); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + +#define XR_ML_localization_map 1 +XR_DEFINE_HANDLE(XrExportedLocalizationMapML) +#define XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML 64 +#define XR_ML_localization_map_SPEC_VERSION 1 +#define XR_ML_LOCALIZATION_MAP_EXTENSION_NAME "XR_ML_localization_map" + +typedef enum XrLocalizationMapStateML { + XR_LOCALIZATION_MAP_STATE_NOT_LOCALIZED_ML = 0, + XR_LOCALIZATION_MAP_STATE_LOCALIZED_ML = 1, + XR_LOCALIZATION_MAP_STATE_LOCALIZATION_PENDING_ML = 2, + XR_LOCALIZATION_MAP_STATE_LOCALIZATION_SLEEPING_BEFORE_RETRY_ML = 3, + XR_LOCALIZATION_MAP_STATE_MAX_ENUM_ML = 0x7FFFFFFF +} XrLocalizationMapStateML; + +typedef enum XrLocalizationMapTypeML { + XR_LOCALIZATION_MAP_TYPE_ON_DEVICE_ML = 0, + XR_LOCALIZATION_MAP_TYPE_CLOUD_ML = 1, + XR_LOCALIZATION_MAP_TYPE_MAX_ENUM_ML = 0x7FFFFFFF +} XrLocalizationMapTypeML; + +typedef enum XrLocalizationMapConfidenceML { + XR_LOCALIZATION_MAP_CONFIDENCE_POOR_ML = 0, + XR_LOCALIZATION_MAP_CONFIDENCE_FAIR_ML = 1, + XR_LOCALIZATION_MAP_CONFIDENCE_GOOD_ML = 2, + XR_LOCALIZATION_MAP_CONFIDENCE_EXCELLENT_ML = 3, + XR_LOCALIZATION_MAP_CONFIDENCE_MAX_ENUM_ML = 0x7FFFFFFF +} XrLocalizationMapConfidenceML; +typedef XrFlags64 XrLocalizationMapErrorFlagsML; + +// Flag bits for XrLocalizationMapErrorFlagsML +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_UNKNOWN_BIT_ML = 0x00000001; +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_OUT_OF_MAPPED_AREA_BIT_ML = 0x00000002; +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_LOW_FEATURE_COUNT_BIT_ML = 0x00000004; +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_EXCESSIVE_MOTION_BIT_ML = 0x00000008; +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_LOW_LIGHT_BIT_ML = 0x00000010; +static const XrLocalizationMapErrorFlagsML XR_LOCALIZATION_MAP_ERROR_HEADPOSE_BIT_ML = 0x00000020; + +typedef struct XrLocalizationMapML { + XrStructureType type; + void* XR_MAY_ALIAS next; + char name[XR_MAX_LOCALIZATION_MAP_NAME_LENGTH_ML]; + XrUuidEXT mapUuid; + XrLocalizationMapTypeML mapType; +} XrLocalizationMapML; + +typedef struct XrEventDataLocalizationChangedML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSession session; + XrLocalizationMapStateML state; + XrLocalizationMapML map; + XrLocalizationMapConfidenceML confidence; + XrLocalizationMapErrorFlagsML errorFlags; +} XrEventDataLocalizationChangedML; + +typedef struct XrLocalizationMapQueryInfoBaseHeaderML { + XrStructureType type; + const void* XR_MAY_ALIAS next; +} XrLocalizationMapQueryInfoBaseHeaderML; + +typedef struct XrMapLocalizationRequestInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrUuidEXT mapUuid; +} XrMapLocalizationRequestInfoML; + +typedef struct XrLocalizationMapImportInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + uint32_t size; + char* data; +} XrLocalizationMapImportInfoML; + +typedef struct XrLocalizationEnableEventsInfoML { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrBool32 enabled; +} XrLocalizationEnableEventsInfoML; + +typedef XrResult (XRAPI_PTR *PFN_xrEnableLocalizationEventsML)(XrSession session, const XrLocalizationEnableEventsInfoML * info); +typedef XrResult (XRAPI_PTR *PFN_xrQueryLocalizationMapsML)(XrSession session, const XrLocalizationMapQueryInfoBaseHeaderML* queryInfo, uint32_t mapCapacityInput, uint32_t * mapCountOutput, XrLocalizationMapML* maps); +typedef XrResult (XRAPI_PTR *PFN_xrRequestMapLocalizationML)(XrSession session, const XrMapLocalizationRequestInfoML* requestInfo); +typedef XrResult (XRAPI_PTR *PFN_xrImportLocalizationMapML)(XrSession session, const XrLocalizationMapImportInfoML* importInfo, XrUuidEXT* mapUuid); +typedef XrResult (XRAPI_PTR *PFN_xrCreateExportedLocalizationMapML)(XrSession session, const XrUuidEXT* mapUuid, XrExportedLocalizationMapML* map); +typedef XrResult (XRAPI_PTR *PFN_xrDestroyExportedLocalizationMapML)(XrExportedLocalizationMapML map); +typedef XrResult (XRAPI_PTR *PFN_xrGetExportedLocalizationMapDataML)(XrExportedLocalizationMapML map, uint32_t bufferCapacityInput, uint32_t* bufferCountOutput, char* buffer); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrEnableLocalizationEventsML( + XrSession session, + const XrLocalizationEnableEventsInfoML * info); + +XRAPI_ATTR XrResult XRAPI_CALL xrQueryLocalizationMapsML( + XrSession session, + const XrLocalizationMapQueryInfoBaseHeaderML* queryInfo, + uint32_t mapCapacityInput, + uint32_t * mapCountOutput, + XrLocalizationMapML* maps); + +XRAPI_ATTR XrResult XRAPI_CALL xrRequestMapLocalizationML( + XrSession session, + const XrMapLocalizationRequestInfoML* requestInfo); + +XRAPI_ATTR XrResult XRAPI_CALL xrImportLocalizationMapML( + XrSession session, + const XrLocalizationMapImportInfoML* importInfo, + XrUuidEXT* mapUuid); + +XRAPI_ATTR XrResult XRAPI_CALL xrCreateExportedLocalizationMapML( + XrSession session, + const XrUuidEXT* mapUuid, + XrExportedLocalizationMapML* map); + +XRAPI_ATTR XrResult XRAPI_CALL xrDestroyExportedLocalizationMapML( + XrExportedLocalizationMapML map); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetExportedLocalizationMapDataML( + XrExportedLocalizationMapML map, + uint32_t bufferCapacityInput, + uint32_t* bufferCountOutput, + char* buffer); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + #define XR_MSFT_spatial_anchor_persistence 1 XR_DEFINE_HANDLE(XrSpatialAnchorStoreConnectionMSFT) #define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_MSFT 256 @@ -5814,6 +6200,11 @@ XRAPI_ATTR XrResult XRAPI_CALL xrUpdatePassthroughColorLutMETA( #endif /* !XR_NO_PROTOTYPES */ +#define XR_META_touch_controller_plus 1 +#define XR_META_touch_controller_plus_SPEC_VERSION 1 +#define XR_META_TOUCH_CONTROLLER_PLUS_EXTENSION_NAME "XR_META_touch_controller_plus" + + #define XR_EXT_uuid 1 #define XR_EXT_uuid_SPEC_VERSION 1 #define XR_EXT_UUID_EXTENSION_NAME "XR_EXT_uuid" @@ -5980,6 +6371,46 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyFoveationHTC( #endif /* !XR_NO_PROTOTYPES */ +#define XR_HTC_anchor 1 +#define XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC 256 +#define XR_HTC_anchor_SPEC_VERSION 1 +#define XR_HTC_ANCHOR_EXTENSION_NAME "XR_HTC_anchor" +// XrSystemAnchorPropertiesHTC extends XrSystemProperties +typedef struct XrSystemAnchorPropertiesHTC { + XrStructureType type; + void* XR_MAY_ALIAS next; + XrBool32 supportsAnchor; +} XrSystemAnchorPropertiesHTC; + +typedef struct XrSpatialAnchorNameHTC { + char name[XR_MAX_SPATIAL_ANCHOR_NAME_SIZE_HTC]; +} XrSpatialAnchorNameHTC; + +typedef struct XrSpatialAnchorCreateInfoHTC { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrSpace space; + XrPosef poseInSpace; + XrSpatialAnchorNameHTC name; +} XrSpatialAnchorCreateInfoHTC; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateSpatialAnchorHTC)(XrSession session, const XrSpatialAnchorCreateInfoHTC* createInfo, XrSpace* anchor); +typedef XrResult (XRAPI_PTR *PFN_xrGetSpatialAnchorNameHTC)(XrSpace anchor, XrSpatialAnchorNameHTC* name); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateSpatialAnchorHTC( + XrSession session, + const XrSpatialAnchorCreateInfoHTC* createInfo, + XrSpace* anchor); + +XRAPI_ATTR XrResult XRAPI_CALL xrGetSpatialAnchorNameHTC( + XrSpace anchor, + XrSpatialAnchorNameHTC* name); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + #define XR_EXT_active_action_set_priority 1 #define XR_EXT_active_action_set_priority_SPEC_VERSION 1 #define XR_EXT_ACTIVE_ACTION_SET_PRIORITY_EXTENSION_NAME "XR_EXT_active_action_set_priority" @@ -6274,7 +6705,7 @@ typedef struct XrUserCalibrationEnableEventsInfoML { XrBool32 enabled; } XrUserCalibrationEnableEventsInfoML; -typedef XrResult (XRAPI_PTR *PFN_xrEnableUserCalibrationEventsML)(XrInstance instance, const XrUserCalibrationEnableEventsInfoML* enableInfo); +typedef XrResult (XRAPI_PTR *PFN_xrEnableUserCalibrationEventsML)(XrInstance instance, const XrUserCalibrationEnableEventsInfoML* enableInfo); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES diff --git a/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h new file mode 100644 index 0000000000..619b226760 --- /dev/null +++ b/thirdparty/openxr/include/openxr/openxr_loader_negotiation.h @@ -0,0 +1,140 @@ +#ifndef OPENXR_LOADER_NEGOTIATION_H_ +#define OPENXR_LOADER_NEGOTIATION_H_ 1 + +/* +** Copyright 2017-2024, The Khronos Group Inc. +** +** SPDX-License-Identifier: Apache-2.0 OR MIT +*/ + +/* +** This header is generated from the Khronos OpenXR XML API Registry. +** +*/ + +#include "openxr.h" + +#ifdef __cplusplus +extern "C" { +#endif + + + +#define XR_LOADER_VERSION_1_0 1 + +#define XR_CURRENT_LOADER_API_LAYER_VERSION 1 + + +#define XR_CURRENT_LOADER_RUNTIME_VERSION 1 + + +#define XR_LOADER_INFO_STRUCT_VERSION 1 + + +#define XR_API_LAYER_INFO_STRUCT_VERSION 1 + + +#define XR_RUNTIME_INFO_STRUCT_VERSION 1 + + +#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1 + + +#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1 + + +#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512 + + +typedef enum XrLoaderInterfaceStructs { + XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0, + XR_LOADER_INTERFACE_STRUCT_LOADER_INFO = 1, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST = 2, + XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST = 3, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO = 4, + XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO = 5, + XR_LOADER_INTERFACE_STRUCTS_MAX_ENUM = 0x7FFFFFFF +} XrLoaderInterfaceStructs; +typedef XrResult (XRAPI_PTR *PFN_xrGetInstanceProcAddr)(XrInstance instance, const char* name, PFN_xrVoidFunction* function); + +typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo; +typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)( + const XrInstanceCreateInfo* info, + const XrApiLayerCreateInfo* apiLayerInfo, + XrInstance* instance); + +typedef struct XrApiLayerNextInfo { + XrLoaderInterfaceStructs structType; + uint32_t structVersion; + size_t structSize; + char layerName[XR_MAX_API_LAYER_NAME_SIZE]; + PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; + PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; + struct XrApiLayerNextInfo* next; +} XrApiLayerNextInfo; + +typedef struct XrApiLayerCreateInfo { + XrLoaderInterfaceStructs structType; + uint32_t structVersion; + size_t structSize; + void* XR_MAY_ALIAS loaderInstance; + char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; + XrApiLayerNextInfo* nextInfo; +} XrApiLayerCreateInfo; + +typedef struct XrNegotiateLoaderInfo { + XrLoaderInterfaceStructs structType; + uint32_t structVersion; + size_t structSize; + uint32_t minInterfaceVersion; + uint32_t maxInterfaceVersion; + XrVersion minApiVersion; + XrVersion maxApiVersion; +} XrNegotiateLoaderInfo; + +typedef struct XrNegotiateRuntimeRequest { + XrLoaderInterfaceStructs structType; + uint32_t structVersion; + size_t structSize; + uint32_t runtimeInterfaceVersion; + XrVersion runtimeApiVersion; + PFN_xrGetInstanceProcAddr getInstanceProcAddr; +} XrNegotiateRuntimeRequest; + +typedef struct XrNegotiateApiLayerRequest { + XrLoaderInterfaceStructs structType; + uint32_t structVersion; + size_t structSize; + uint32_t layerInterfaceVersion; + XrVersion layerApiVersion; + PFN_xrGetInstanceProcAddr getInstanceProcAddr; + PFN_xrCreateApiLayerInstance createApiLayerInstance; +} XrNegotiateApiLayerRequest; + +typedef XrResult (XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo* info, const XrApiLayerCreateInfo* layerInfo, XrInstance* instance); +typedef XrResult (XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo* loaderInfo, XrNegotiateRuntimeRequest* runtimeRequest); +typedef XrResult (XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo* loaderInfo, const char* layerName, XrNegotiateApiLayerRequest* apiLayerRequest); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrCreateApiLayerInstance( + const XrInstanceCreateInfo* info, + const XrApiLayerCreateInfo* layerInfo, + XrInstance* instance); + +XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderRuntimeInterface( + const XrNegotiateLoaderInfo* loaderInfo, + XrNegotiateRuntimeRequest* runtimeRequest); + +XRAPI_ATTR XrResult XRAPI_CALL xrNegotiateLoaderApiLayerInterface( + const XrNegotiateLoaderInfo* loaderInfo, + const char* layerName, + XrNegotiateApiLayerRequest* apiLayerRequest); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + +#ifdef __cplusplus +} +#endif + +#endif diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h index e4ddfcbb7d..1f47428676 100644 --- a/thirdparty/openxr/include/openxr/openxr_platform.h +++ b/thirdparty/openxr/include/openxr/openxr_platform.h @@ -2,7 +2,7 @@ #define OPENXR_PLATFORM_H_ 1 /* -** Copyright 2017-2023 The Khronos Group Inc. +** Copyright 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -22,7 +22,7 @@ extern "C" { #ifdef XR_USE_PLATFORM_ANDROID #define XR_KHR_android_thread_settings 1 -#define XR_KHR_android_thread_settings_SPEC_VERSION 5 +#define XR_KHR_android_thread_settings_SPEC_VERSION 6 #define XR_KHR_ANDROID_THREAD_SETTINGS_EXTENSION_NAME "XR_KHR_android_thread_settings" typedef enum XrAndroidThreadTypeKHR { @@ -489,7 +489,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( #ifdef XR_USE_PLATFORM_EGL #define XR_MNDX_egl_enable 1 -#define XR_MNDX_egl_enable_SPEC_VERSION 1 +#define XR_MNDX_egl_enable_SPEC_VERSION 2 #define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable" typedef PFN_xrVoidFunction (*PFN_xrEglGetProcAddressMNDX)(const char *name); // XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo diff --git a/thirdparty/openxr/include/openxr/openxr_platform_defines.h b/thirdparty/openxr/include/openxr/openxr_platform_defines.h index 820b7b3e1e..cd07c91360 100644 --- a/thirdparty/openxr/include/openxr/openxr_platform_defines.h +++ b/thirdparty/openxr/include/openxr/openxr_platform_defines.h @@ -1,5 +1,5 @@ /* -** Copyright (c) 2017-2023, The Khronos Group Inc. +** Copyright (c) 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h index 8f49323417..f6d66363bf 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection.h @@ -2,7 +2,7 @@ #define OPENXR_REFLECTION_H_ 1 /* -** Copyright (c) 2017-2023, The Khronos Group Inc. +** Copyright (c) 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -114,6 +114,18 @@ XR_ENUM_STR(XrResult); _(XR_RENDER_MODEL_UNAVAILABLE_FB, 1000119020) \ _(XR_ERROR_MARKER_NOT_TRACKED_VARJO, -1000124000) \ _(XR_ERROR_MARKER_ID_INVALID_VARJO, -1000124001) \ + _(XR_ERROR_MARKER_DETECTOR_PERMISSION_DENIED_ML, -1000138000) \ + _(XR_ERROR_MARKER_DETECTOR_LOCATE_FAILED_ML, -1000138001) \ + _(XR_ERROR_MARKER_DETECTOR_INVALID_DATA_QUERY_ML, -1000138002) \ + _(XR_ERROR_MARKER_DETECTOR_INVALID_CREATE_INFO_ML, -1000138003) \ + _(XR_ERROR_MARKER_INVALID_ML, -1000138004) \ + _(XR_ERROR_LOCALIZATION_MAP_INCOMPATIBLE_ML, -1000139000) \ + _(XR_ERROR_LOCALIZATION_MAP_UNAVAILABLE_ML, -1000139001) \ + _(XR_ERROR_LOCALIZATION_MAP_FAIL_ML, -1000139002) \ + _(XR_ERROR_LOCALIZATION_MAP_IMPORT_EXPORT_PERMISSION_DENIED_ML, -1000139003) \ + _(XR_ERROR_LOCALIZATION_MAP_PERMISSION_DENIED_ML, -1000139004) \ + _(XR_ERROR_LOCALIZATION_MAP_ALREADY_EXISTS_ML, -1000139005) \ + _(XR_ERROR_LOCALIZATION_MAP_CANNOT_EXPORT_CLOUD_MAP_ML, -1000139006) \ _(XR_ERROR_SPATIAL_ANCHOR_NAME_NOT_FOUND_MSFT, -1000142001) \ _(XR_ERROR_SPATIAL_ANCHOR_NAME_INVALID_MSFT, -1000142002) \ _(XR_SCENE_MARKER_DATA_NOT_STRING_MSFT, 1000147000) \ @@ -124,6 +136,7 @@ XR_ENUM_STR(XrResult); _(XR_ERROR_SPACE_CLOUD_STORAGE_DISABLED_FB, -1000169004) \ _(XR_ERROR_PASSTHROUGH_COLOR_LUT_BUFFER_SIZE_MISMATCH_META, -1000266000) \ _(XR_ERROR_HINT_ALREADY_SET_QCOM, -1000306000) \ + _(XR_ERROR_NOT_AN_ANCHOR_HTC, -1000319000) \ _(XR_ERROR_SPACE_NOT_LOCATABLE_EXT, -1000429000) \ _(XR_ERROR_PLANE_DETECTION_PERMISSION_DENIED_EXT, -1000429001) \ _(XR_RESULT_MAX_ENUM, 0x7FFFFFFF) @@ -340,6 +353,20 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_FRAME_END_INFO_ML, 1000135000) \ _(XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML, 1000136000) \ _(XR_TYPE_COORDINATE_SPACE_CREATE_INFO_ML, 1000137000) \ + _(XR_TYPE_SYSTEM_MARKER_UNDERSTANDING_PROPERTIES_ML, 1000138000) \ + _(XR_TYPE_MARKER_DETECTOR_CREATE_INFO_ML, 1000138001) \ + _(XR_TYPE_MARKER_DETECTOR_ARUCO_INFO_ML, 1000138002) \ + _(XR_TYPE_MARKER_DETECTOR_SIZE_INFO_ML, 1000138003) \ + _(XR_TYPE_MARKER_DETECTOR_APRIL_TAG_INFO_ML, 1000138004) \ + _(XR_TYPE_MARKER_DETECTOR_CUSTOM_PROFILE_INFO_ML, 1000138005) \ + _(XR_TYPE_MARKER_DETECTOR_SNAPSHOT_INFO_ML, 1000138006) \ + _(XR_TYPE_MARKER_DETECTOR_STATE_ML, 1000138007) \ + _(XR_TYPE_MARKER_SPACE_CREATE_INFO_ML, 1000138008) \ + _(XR_TYPE_LOCALIZATION_MAP_ML, 1000139000) \ + _(XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML, 1000139001) \ + _(XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML, 1000139002) \ + _(XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML, 1000139003) \ + _(XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML, 1000139004) \ _(XR_TYPE_EVENT_DATA_HEADSET_FIT_CHANGED_ML, 1000472000) \ _(XR_TYPE_EVENT_DATA_EYE_CALIBRATION_CHANGED_ML, 1000472001) \ _(XR_TYPE_USER_CALIBRATION_ENABLE_EVENTS_INFO_ML, 1000472002) \ @@ -429,6 +456,8 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_FOVEATION_APPLY_INFO_HTC, 1000318000) \ _(XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC, 1000318001) \ _(XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC, 1000318002) \ + _(XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC, 1000319000) \ + _(XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC, 1000319001) \ _(XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT, 1000373000) \ _(XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX, 1000375000) \ _(XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX, 1000375001) \ @@ -467,6 +496,7 @@ XR_ENUM_STR(XrResult); _(XR_REFERENCE_SPACE_TYPE_STAGE, 3) \ _(XR_REFERENCE_SPACE_TYPE_UNBOUNDED_MSFT, 1000038000) \ _(XR_REFERENCE_SPACE_TYPE_COMBINED_EYE_VARJO, 1000121000) \ + _(XR_REFERENCE_SPACE_TYPE_LOCALIZATION_MAP_ML, 1000139000) \ _(XR_REFERENCE_SPACE_TYPE_LOCAL_FLOOR_EXT, 1000426000) \ _(XR_REFERENCE_SPACE_TYPE_MAX_ENUM, 0x7FFFFFFF) @@ -517,6 +547,8 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_PASSTHROUGH_FB, 1000118000) \ _(XR_OBJECT_TYPE_PASSTHROUGH_LAYER_FB, 1000118002) \ _(XR_OBJECT_TYPE_GEOMETRY_INSTANCE_FB, 1000118004) \ + _(XR_OBJECT_TYPE_MARKER_DETECTOR_ML, 1000138000) \ + _(XR_OBJECT_TYPE_EXPORTED_LOCALIZATION_MAP_ML, 1000139000) \ _(XR_OBJECT_TYPE_SPATIAL_ANCHOR_STORE_CONNECTION_MSFT, 1000142000) \ _(XR_OBJECT_TYPE_FACE_TRACKER_FB, 1000201000) \ _(XR_OBJECT_TYPE_EYE_TRACKER_FB, 1000202000) \ @@ -527,6 +559,15 @@ XR_ENUM_STR(XrResult); _(XR_OBJECT_TYPE_PLANE_DETECTOR_EXT, 1000429000) \ _(XR_OBJECT_TYPE_MAX_ENUM, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrLoaderInterfaceStructs(_) \ + _(XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED, 0) \ + _(XR_LOADER_INTERFACE_STRUCT_LOADER_INFO, 1) \ + _(XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST, 2) \ + _(XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST, 3) \ + _(XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO, 4) \ + _(XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO, 5) \ + _(XR_LOADER_INTERFACE_STRUCTS_MAX_ENUM, 0x7FFFFFFF) + #define XR_LIST_ENUM_XrAndroidThreadTypeKHR(_) \ _(XR_ANDROID_THREAD_TYPE_APPLICATION_MAIN_KHR, 1) \ _(XR_ANDROID_THREAD_TYPE_APPLICATION_WORKER_KHR, 2) \ @@ -877,6 +918,107 @@ XR_ENUM_STR(XrResult); _(XR_PASSTHROUGH_LAYER_PURPOSE_TRACKED_KEYBOARD_MASKED_HANDS_FB, 1000203002) \ _(XR_PASSTHROUGH_LAYER_PURPOSE_MAX_ENUM_FB, 0x7FFFFFFF) +#define XR_LIST_ENUM_XrMarkerDetectorProfileML(_) \ + _(XR_MARKER_DETECTOR_PROFILE_DEFAULT_ML, 0) \ + _(XR_MARKER_DETECTOR_PROFILE_SPEED_ML, 1) \ + _(XR_MARKER_DETECTOR_PROFILE_ACCURACY_ML, 2) \ + _(XR_MARKER_DETECTOR_PROFILE_SMALL_TARGETS_ML, 3) \ + _(XR_MARKER_DETECTOR_PROFILE_LARGE_FOV_ML, 4) \ + _(XR_MARKER_DETECTOR_PROFILE_CUSTOM_ML, 5) \ + _(XR_MARKER_DETECTOR_PROFILE_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerTypeML(_) \ + _(XR_MARKER_TYPE_ARUCO_ML, 0) \ + _(XR_MARKER_TYPE_APRIL_TAG_ML, 1) \ + _(XR_MARKER_TYPE_QR_ML, 2) \ + _(XR_MARKER_TYPE_EAN_13_ML, 3) \ + _(XR_MARKER_TYPE_UPC_A_ML, 4) \ + _(XR_MARKER_TYPE_CODE_128_ML, 5) \ + _(XR_MARKER_TYPE_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerArucoDictML(_) \ + _(XR_MARKER_ARUCO_DICT_4X4_50_ML, 0) \ + _(XR_MARKER_ARUCO_DICT_4X4_100_ML, 1) \ + _(XR_MARKER_ARUCO_DICT_4X4_250_ML, 2) \ + _(XR_MARKER_ARUCO_DICT_4X4_1000_ML, 3) \ + _(XR_MARKER_ARUCO_DICT_5X5_50_ML, 4) \ + _(XR_MARKER_ARUCO_DICT_5X5_100_ML, 5) \ + _(XR_MARKER_ARUCO_DICT_5X5_250_ML, 6) \ + _(XR_MARKER_ARUCO_DICT_5X5_1000_ML, 7) \ + _(XR_MARKER_ARUCO_DICT_6X6_50_ML, 8) \ + _(XR_MARKER_ARUCO_DICT_6X6_100_ML, 9) \ + _(XR_MARKER_ARUCO_DICT_6X6_250_ML, 10) \ + _(XR_MARKER_ARUCO_DICT_6X6_1000_ML, 11) \ + _(XR_MARKER_ARUCO_DICT_7X7_50_ML, 12) \ + _(XR_MARKER_ARUCO_DICT_7X7_100_ML, 13) \ + _(XR_MARKER_ARUCO_DICT_7X7_250_ML, 14) \ + _(XR_MARKER_ARUCO_DICT_7X7_1000_ML, 15) \ + _(XR_MARKER_ARUCO_DICT_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerAprilTagDictML(_) \ + _(XR_MARKER_APRIL_TAG_DICT_16H5_ML, 0) \ + _(XR_MARKER_APRIL_TAG_DICT_25H9_ML, 1) \ + _(XR_MARKER_APRIL_TAG_DICT_36H10_ML, 2) \ + _(XR_MARKER_APRIL_TAG_DICT_36H11_ML, 3) \ + _(XR_MARKER_APRIL_TAG_DICT_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorFpsML(_) \ + _(XR_MARKER_DETECTOR_FPS_LOW_ML, 0) \ + _(XR_MARKER_DETECTOR_FPS_MEDIUM_ML, 1) \ + _(XR_MARKER_DETECTOR_FPS_HIGH_ML, 2) \ + _(XR_MARKER_DETECTOR_FPS_MAX_ML, 3) \ + _(XR_MARKER_DETECTOR_FPS_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorResolutionML(_) \ + _(XR_MARKER_DETECTOR_RESOLUTION_LOW_ML, 0) \ + _(XR_MARKER_DETECTOR_RESOLUTION_MEDIUM_ML, 1) \ + _(XR_MARKER_DETECTOR_RESOLUTION_HIGH_ML, 2) \ + _(XR_MARKER_DETECTOR_RESOLUTION_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorCameraML(_) \ + _(XR_MARKER_DETECTOR_CAMERA_RGB_CAMERA_ML, 0) \ + _(XR_MARKER_DETECTOR_CAMERA_WORLD_CAMERAS_ML, 1) \ + _(XR_MARKER_DETECTOR_CAMERA_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorCornerRefineMethodML(_) \ + _(XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_NONE_ML, 0) \ + _(XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_SUBPIX_ML, 1) \ + _(XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_CONTOUR_ML, 2) \ + _(XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_APRIL_TAG_ML, 3) \ + _(XR_MARKER_DETECTOR_CORNER_REFINE_METHOD_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorFullAnalysisIntervalML(_) \ + _(XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ML, 0) \ + _(XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_FAST_ML, 1) \ + _(XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MEDIUM_ML, 2) \ + _(XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_SLOW_ML, 3) \ + _(XR_MARKER_DETECTOR_FULL_ANALYSIS_INTERVAL_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrMarkerDetectorStatusML(_) \ + _(XR_MARKER_DETECTOR_STATUS_PENDING_ML, 0) \ + _(XR_MARKER_DETECTOR_STATUS_READY_ML, 1) \ + _(XR_MARKER_DETECTOR_STATUS_ERROR_ML, 2) \ + _(XR_MARKER_DETECTOR_STATUS_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrLocalizationMapStateML(_) \ + _(XR_LOCALIZATION_MAP_STATE_NOT_LOCALIZED_ML, 0) \ + _(XR_LOCALIZATION_MAP_STATE_LOCALIZED_ML, 1) \ + _(XR_LOCALIZATION_MAP_STATE_LOCALIZATION_PENDING_ML, 2) \ + _(XR_LOCALIZATION_MAP_STATE_LOCALIZATION_SLEEPING_BEFORE_RETRY_ML, 3) \ + _(XR_LOCALIZATION_MAP_STATE_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrLocalizationMapTypeML(_) \ + _(XR_LOCALIZATION_MAP_TYPE_ON_DEVICE_ML, 0) \ + _(XR_LOCALIZATION_MAP_TYPE_CLOUD_ML, 1) \ + _(XR_LOCALIZATION_MAP_TYPE_MAX_ENUM_ML, 0x7FFFFFFF) + +#define XR_LIST_ENUM_XrLocalizationMapConfidenceML(_) \ + _(XR_LOCALIZATION_MAP_CONFIDENCE_POOR_ML, 0) \ + _(XR_LOCALIZATION_MAP_CONFIDENCE_FAIR_ML, 1) \ + _(XR_LOCALIZATION_MAP_CONFIDENCE_GOOD_ML, 2) \ + _(XR_LOCALIZATION_MAP_CONFIDENCE_EXCELLENT_ML, 3) \ + _(XR_LOCALIZATION_MAP_CONFIDENCE_MAX_ENUM_ML, 0x7FFFFFFF) + #define XR_LIST_ENUM_XrSceneMarkerTypeMSFT(_) \ _(XR_SCENE_MARKER_TYPE_QR_CODE_MSFT, 1) \ _(XR_SCENE_MARKER_TYPE_MAX_ENUM_MSFT, 0x7FFFFFFF) @@ -1281,6 +1423,14 @@ XR_ENUM_STR(XrResult); #define XR_LIST_BITS_XrGlobalDimmerFrameEndInfoFlagsML(_) \ _(XR_GLOBAL_DIMMER_FRAME_END_INFO_ENABLED_BIT_ML, 0x00000001) \ +#define XR_LIST_BITS_XrLocalizationMapErrorFlagsML(_) \ + _(XR_LOCALIZATION_MAP_ERROR_UNKNOWN_BIT_ML, 0x00000001) \ + _(XR_LOCALIZATION_MAP_ERROR_OUT_OF_MAPPED_AREA_BIT_ML, 0x00000002) \ + _(XR_LOCALIZATION_MAP_ERROR_LOW_FEATURE_COUNT_BIT_ML, 0x00000004) \ + _(XR_LOCALIZATION_MAP_ERROR_EXCESSIVE_MOTION_BIT_ML, 0x00000008) \ + _(XR_LOCALIZATION_MAP_ERROR_LOW_LIGHT_BIT_ML, 0x00000010) \ + _(XR_LOCALIZATION_MAP_ERROR_HEADPOSE_BIT_ML, 0x00000020) \ + #define XR_LIST_BITS_XrCompositionLayerSpaceWarpInfoFlagsFB(_) \ _(XR_COMPOSITION_LAYER_SPACE_WARP_INFO_FRAME_SKIP_BIT_FB, 0x00000001) \ @@ -1851,6 +2001,54 @@ XR_ENUM_STR(XrResult); _(b) \ _(a) \ +/// Calls your macro with the name of each member of XrApiLayerNextInfo, in order. +#define XR_LIST_STRUCT_XrApiLayerNextInfo(_) \ + _(structType) \ + _(structVersion) \ + _(structSize) \ + _(layerName) \ + _(nextGetInstanceProcAddr) \ + _(nextCreateApiLayerInstance) \ + _(next) \ + +/// Calls your macro with the name of each member of XrApiLayerCreateInfo, in order. +#define XR_LIST_STRUCT_XrApiLayerCreateInfo(_) \ + _(structType) \ + _(structVersion) \ + _(structSize) \ + _(loaderInstance) \ + _(settings_file_location) \ + _(nextInfo) \ + +/// Calls your macro with the name of each member of XrNegotiateLoaderInfo, in order. +#define XR_LIST_STRUCT_XrNegotiateLoaderInfo(_) \ + _(structType) \ + _(structVersion) \ + _(structSize) \ + _(minInterfaceVersion) \ + _(maxInterfaceVersion) \ + _(minApiVersion) \ + _(maxApiVersion) \ + +/// Calls your macro with the name of each member of XrNegotiateRuntimeRequest, in order. +#define XR_LIST_STRUCT_XrNegotiateRuntimeRequest(_) \ + _(structType) \ + _(structVersion) \ + _(structSize) \ + _(runtimeInterfaceVersion) \ + _(runtimeApiVersion) \ + _(getInstanceProcAddr) \ + +/// Calls your macro with the name of each member of XrNegotiateApiLayerRequest, in order. +#define XR_LIST_STRUCT_XrNegotiateApiLayerRequest(_) \ + _(structType) \ + _(structVersion) \ + _(structSize) \ + _(layerInterfaceVersion) \ + _(layerApiVersion) \ + _(getInstanceProcAddr) \ + _(createApiLayerInstance) \ + /// Calls your macro with the name of each member of XrCompositionLayerCubeKHR, in order. #define XR_LIST_STRUCT_XrCompositionLayerCubeKHR(_) \ _(type) \ @@ -3212,6 +3410,109 @@ XR_ENUM_STR(XrResult); _(cfuid) \ _(poseInCoordinateSpace) \ +/// Calls your macro with the name of each member of XrSystemMarkerUnderstandingPropertiesML, in order. +#define XR_LIST_STRUCT_XrSystemMarkerUnderstandingPropertiesML(_) \ + _(type) \ + _(next) \ + _(supportsMarkerUnderstanding) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorCreateInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorCreateInfoML(_) \ + _(type) \ + _(next) \ + _(profile) \ + _(markerType) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorArucoInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorArucoInfoML(_) \ + _(type) \ + _(next) \ + _(arucoDict) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorSizeInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorSizeInfoML(_) \ + _(type) \ + _(next) \ + _(markerLength) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorAprilTagInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorAprilTagInfoML(_) \ + _(type) \ + _(next) \ + _(aprilTagDict) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorCustomProfileInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorCustomProfileInfoML(_) \ + _(type) \ + _(next) \ + _(fpsHint) \ + _(resolutionHint) \ + _(cameraHint) \ + _(cornerRefineMethod) \ + _(useEdgeRefinement) \ + _(fullAnalysisIntervalHint) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorSnapshotInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorSnapshotInfoML(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrMarkerDetectorStateML, in order. +#define XR_LIST_STRUCT_XrMarkerDetectorStateML(_) \ + _(type) \ + _(next) \ + _(state) \ + +/// Calls your macro with the name of each member of XrMarkerSpaceCreateInfoML, in order. +#define XR_LIST_STRUCT_XrMarkerSpaceCreateInfoML(_) \ + _(type) \ + _(next) \ + _(markerDetector) \ + _(marker) \ + _(poseInMarkerSpace) \ + +/// Calls your macro with the name of each member of XrLocalizationMapML, in order. +#define XR_LIST_STRUCT_XrLocalizationMapML(_) \ + _(type) \ + _(next) \ + _(name) \ + _(mapUuid) \ + _(mapType) \ + +/// Calls your macro with the name of each member of XrEventDataLocalizationChangedML, in order. +#define XR_LIST_STRUCT_XrEventDataLocalizationChangedML(_) \ + _(type) \ + _(next) \ + _(session) \ + _(state) \ + _(map) \ + _(confidence) \ + _(errorFlags) \ + +/// Calls your macro with the name of each member of XrLocalizationMapQueryInfoBaseHeaderML, in order. +#define XR_LIST_STRUCT_XrLocalizationMapQueryInfoBaseHeaderML(_) \ + _(type) \ + _(next) \ + +/// Calls your macro with the name of each member of XrMapLocalizationRequestInfoML, in order. +#define XR_LIST_STRUCT_XrMapLocalizationRequestInfoML(_) \ + _(type) \ + _(next) \ + _(mapUuid) \ + +/// Calls your macro with the name of each member of XrLocalizationMapImportInfoML, in order. +#define XR_LIST_STRUCT_XrLocalizationMapImportInfoML(_) \ + _(type) \ + _(next) \ + _(size) \ + _(data) \ + +/// Calls your macro with the name of each member of XrLocalizationEnableEventsInfoML, in order. +#define XR_LIST_STRUCT_XrLocalizationEnableEventsInfoML(_) \ + _(type) \ + _(next) \ + _(enabled) \ + /// Calls your macro with the name of each member of XrSpatialAnchorPersistenceNameMSFT, in order. #define XR_LIST_STRUCT_XrSpatialAnchorPersistenceNameMSFT(_) \ _(name) \ @@ -3941,6 +4242,24 @@ XR_ENUM_STR(XrResult); _(configCount) \ _(configs) \ +/// Calls your macro with the name of each member of XrSystemAnchorPropertiesHTC, in order. +#define XR_LIST_STRUCT_XrSystemAnchorPropertiesHTC(_) \ + _(type) \ + _(next) \ + _(supportsAnchor) \ + +/// Calls your macro with the name of each member of XrSpatialAnchorNameHTC, in order. +#define XR_LIST_STRUCT_XrSpatialAnchorNameHTC(_) \ + _(name) \ + +/// Calls your macro with the name of each member of XrSpatialAnchorCreateInfoHTC, in order. +#define XR_LIST_STRUCT_XrSpatialAnchorCreateInfoHTC(_) \ + _(type) \ + _(next) \ + _(space) \ + _(poseInSpace) \ + _(name) \ + /// Calls your macro with the name of each member of XrActiveActionSetPriorityEXT, in order. #define XR_LIST_STRUCT_XrActiveActionSetPriorityEXT(_) \ _(actionSet) \ @@ -4278,6 +4597,20 @@ XR_ENUM_STR(XrResult); _(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \ _(XrFrameEndInfoML, XR_TYPE_FRAME_END_INFO_ML) \ _(XrGlobalDimmerFrameEndInfoML, XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML) \ + _(XrSystemMarkerUnderstandingPropertiesML, XR_TYPE_SYSTEM_MARKER_UNDERSTANDING_PROPERTIES_ML) \ + _(XrMarkerDetectorCreateInfoML, XR_TYPE_MARKER_DETECTOR_CREATE_INFO_ML) \ + _(XrMarkerDetectorArucoInfoML, XR_TYPE_MARKER_DETECTOR_ARUCO_INFO_ML) \ + _(XrMarkerDetectorSizeInfoML, XR_TYPE_MARKER_DETECTOR_SIZE_INFO_ML) \ + _(XrMarkerDetectorAprilTagInfoML, XR_TYPE_MARKER_DETECTOR_APRIL_TAG_INFO_ML) \ + _(XrMarkerDetectorCustomProfileInfoML, XR_TYPE_MARKER_DETECTOR_CUSTOM_PROFILE_INFO_ML) \ + _(XrMarkerDetectorSnapshotInfoML, XR_TYPE_MARKER_DETECTOR_SNAPSHOT_INFO_ML) \ + _(XrMarkerDetectorStateML, XR_TYPE_MARKER_DETECTOR_STATE_ML) \ + _(XrMarkerSpaceCreateInfoML, XR_TYPE_MARKER_SPACE_CREATE_INFO_ML) \ + _(XrLocalizationMapML, XR_TYPE_LOCALIZATION_MAP_ML) \ + _(XrEventDataLocalizationChangedML, XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML) \ + _(XrMapLocalizationRequestInfoML, XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML) \ + _(XrLocalizationMapImportInfoML, XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML) \ + _(XrLocalizationEnableEventsInfoML, XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML) \ _(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \ _(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \ _(XrSceneMarkersMSFT, XR_TYPE_SCENE_MARKERS_MSFT) \ @@ -4359,6 +4692,8 @@ XR_ENUM_STR(XrResult); _(XrFoveationApplyInfoHTC, XR_TYPE_FOVEATION_APPLY_INFO_HTC) \ _(XrFoveationDynamicModeInfoHTC, XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC) \ _(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \ + _(XrSystemAnchorPropertiesHTC, XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC) \ + _(XrSpatialAnchorCreateInfoHTC, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC) \ _(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \ _(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \ _(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \ @@ -4629,6 +4964,8 @@ XR_ENUM_STR(XrResult); _(XR_ML_frame_end_info, 136) \ _(XR_ML_global_dimmer, 137) \ _(XR_ML_compat, 138) \ + _(XR_ML_marker_understanding, 139) \ + _(XR_ML_localization_map, 140) \ _(XR_MSFT_spatial_anchor_persistence, 143) \ _(XR_MSFT_scene_marker, 148) \ _(XR_ULTRALEAP_hand_tracking_forearm, 150) \ @@ -4667,11 +5004,13 @@ XR_ENUM_STR(XrResult); _(XR_FB_spatial_entity_user, 242) \ _(XR_META_headset_id, 246) \ _(XR_META_passthrough_color_lut, 267) \ + _(XR_META_touch_controller_plus, 280) \ _(XR_EXT_uuid, 300) \ _(XR_EXT_hand_interaction, 303) \ _(XR_QCOM_tracking_optimization_settings, 307) \ _(XR_HTC_passthrough, 318) \ _(XR_HTC_foveation, 319) \ + _(XR_HTC_anchor, 320) \ _(XR_EXT_active_action_set_priority, 374) \ _(XR_MNDX_force_feedback_curl, 376) \ _(XR_BD_controller_interaction, 385) \ diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h index f410ddcf79..4241682e0b 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_parent_structs.h @@ -2,7 +2,7 @@ #define OPENXR_REFLECTION_PARENT_STRUCTS_H_ 1 /* -** Copyright (c) 2017-2023, The Khronos Group Inc. +** Copyright (c) 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -58,6 +58,7 @@ This file contains expansion macros (X Macros) for OpenXR structures that have a _avail(XrEventDataSpatialAnchorCreateCompleteFB, XR_TYPE_EVENT_DATA_SPATIAL_ANCHOR_CREATE_COMPLETE_FB) \ _avail(XrEventDataSpaceSetStatusCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SET_STATUS_COMPLETE_FB) \ _avail(XrEventDataMarkerTrackingUpdateVARJO, XR_TYPE_EVENT_DATA_MARKER_TRACKING_UPDATE_VARJO) \ + _avail(XrEventDataLocalizationChangedML, XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML) \ _avail(XrEventDataSpaceQueryResultsAvailableFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_RESULTS_AVAILABLE_FB) \ _avail(XrEventDataSpaceQueryCompleteFB, XR_TYPE_EVENT_DATA_SPACE_QUERY_COMPLETE_FB) \ _avail(XrEventDataSpaceSaveCompleteFB, XR_TYPE_EVENT_DATA_SPACE_SAVE_COMPLETE_FB) \ diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h index 2d23ac15ca..bcc1333e29 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h @@ -2,7 +2,7 @@ #define OPENXR_REFLECTION_STRUCTS_H_ 1 /* -** Copyright (c) 2017-2023, The Khronos Group Inc. +** Copyright (c) 2017-2024, The Khronos Group Inc. ** ** SPDX-License-Identifier: Apache-2.0 OR MIT */ @@ -225,6 +225,20 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrMarkerSpaceCreateInfoVARJO, XR_TYPE_MARKER_SPACE_CREATE_INFO_VARJO) \ _avail(XrFrameEndInfoML, XR_TYPE_FRAME_END_INFO_ML) \ _avail(XrGlobalDimmerFrameEndInfoML, XR_TYPE_GLOBAL_DIMMER_FRAME_END_INFO_ML) \ + _avail(XrSystemMarkerUnderstandingPropertiesML, XR_TYPE_SYSTEM_MARKER_UNDERSTANDING_PROPERTIES_ML) \ + _avail(XrMarkerDetectorCreateInfoML, XR_TYPE_MARKER_DETECTOR_CREATE_INFO_ML) \ + _avail(XrMarkerDetectorArucoInfoML, XR_TYPE_MARKER_DETECTOR_ARUCO_INFO_ML) \ + _avail(XrMarkerDetectorSizeInfoML, XR_TYPE_MARKER_DETECTOR_SIZE_INFO_ML) \ + _avail(XrMarkerDetectorAprilTagInfoML, XR_TYPE_MARKER_DETECTOR_APRIL_TAG_INFO_ML) \ + _avail(XrMarkerDetectorCustomProfileInfoML, XR_TYPE_MARKER_DETECTOR_CUSTOM_PROFILE_INFO_ML) \ + _avail(XrMarkerDetectorSnapshotInfoML, XR_TYPE_MARKER_DETECTOR_SNAPSHOT_INFO_ML) \ + _avail(XrMarkerDetectorStateML, XR_TYPE_MARKER_DETECTOR_STATE_ML) \ + _avail(XrMarkerSpaceCreateInfoML, XR_TYPE_MARKER_SPACE_CREATE_INFO_ML) \ + _avail(XrLocalizationMapML, XR_TYPE_LOCALIZATION_MAP_ML) \ + _avail(XrEventDataLocalizationChangedML, XR_TYPE_EVENT_DATA_LOCALIZATION_CHANGED_ML) \ + _avail(XrMapLocalizationRequestInfoML, XR_TYPE_MAP_LOCALIZATION_REQUEST_INFO_ML) \ + _avail(XrLocalizationMapImportInfoML, XR_TYPE_LOCALIZATION_MAP_IMPORT_INFO_ML) \ + _avail(XrLocalizationEnableEventsInfoML, XR_TYPE_LOCALIZATION_ENABLE_EVENTS_INFO_ML) \ _avail(XrSpatialAnchorPersistenceInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_PERSISTENCE_INFO_MSFT) \ _avail(XrSpatialAnchorFromPersistedAnchorCreateInfoMSFT, XR_TYPE_SPATIAL_ANCHOR_FROM_PERSISTED_ANCHOR_CREATE_INFO_MSFT) \ _avail(XrSceneMarkersMSFT, XR_TYPE_SCENE_MARKERS_MSFT) \ @@ -306,6 +320,8 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrFoveationApplyInfoHTC, XR_TYPE_FOVEATION_APPLY_INFO_HTC) \ _avail(XrFoveationDynamicModeInfoHTC, XR_TYPE_FOVEATION_DYNAMIC_MODE_INFO_HTC) \ _avail(XrFoveationCustomModeInfoHTC, XR_TYPE_FOVEATION_CUSTOM_MODE_INFO_HTC) \ + _avail(XrSystemAnchorPropertiesHTC, XR_TYPE_SYSTEM_ANCHOR_PROPERTIES_HTC) \ + _avail(XrSpatialAnchorCreateInfoHTC, XR_TYPE_SPATIAL_ANCHOR_CREATE_INFO_HTC) \ _avail(XrActiveActionSetPrioritiesEXT, XR_TYPE_ACTIVE_ACTION_SET_PRIORITIES_EXT) \ _avail(XrSystemForceFeedbackCurlPropertiesMNDX, XR_TYPE_SYSTEM_FORCE_FEEDBACK_CURL_PROPERTIES_MNDX) \ _avail(XrForceFeedbackCurlApplyLocationsMNDX, XR_TYPE_FORCE_FEEDBACK_CURL_APPLY_LOCATIONS_MNDX) \ diff --git a/thirdparty/openxr/src/common/extra_algorithms.h b/thirdparty/openxr/src/common/extra_algorithms.h index eec429e12a..bdaa420e9f 100644 --- a/thirdparty/openxr/src/common/extra_algorithms.h +++ b/thirdparty/openxr/src/common/extra_algorithms.h @@ -1,11 +1,11 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // Copyright (c) 2019 Collabora, Ltd. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> // /*! diff --git a/thirdparty/openxr/src/common/filesystem_utils.cpp b/thirdparty/openxr/src/common/filesystem_utils.cpp index 16e6ff3292..063c4c4cee 100644 --- a/thirdparty/openxr/src/common/filesystem_utils.cpp +++ b/thirdparty/openxr/src/common/filesystem_utils.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG, Inc. // diff --git a/thirdparty/openxr/src/common/filesystem_utils.hpp b/thirdparty/openxr/src/common/filesystem_utils.hpp index 3dea1b2c3e..2a923fac4c 100644 --- a/thirdparty/openxr/src/common/filesystem_utils.hpp +++ b/thirdparty/openxr/src/common/filesystem_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG, Inc. // diff --git a/thirdparty/openxr/src/common/hex_and_handles.h b/thirdparty/openxr/src/common/hex_and_handles.h index 300669033f..12bdb3670c 100644 --- a/thirdparty/openxr/src/common/hex_and_handles.h +++ b/thirdparty/openxr/src/common/hex_and_handles.h @@ -1,11 +1,11 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // Copyright (c) 2019 Collabora, Ltd. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> // /*! diff --git a/thirdparty/openxr/src/common/loader_interfaces.h b/thirdparty/openxr/src/common/loader_interfaces.h deleted file mode 100644 index 020c3456ea..0000000000 --- a/thirdparty/openxr/src/common/loader_interfaces.h +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. -// Copyright (c) 2017 Valve Corporation -// Copyright (c) 2017 LunarG, Inc. -// -// SPDX-License-Identifier: Apache-2.0 OR MIT -// -// Initial Author: Mark Young <marky@lunarg.com> -// - -#pragma once - -#include <openxr/openxr.h> - -#ifdef __cplusplus -extern "C" { -#endif - -// Forward declare. -typedef struct XrApiLayerCreateInfo XrApiLayerCreateInfo; - -// Function pointer prototype for the xrCreateApiLayerInstance function used in place of xrCreateInstance. -// This function allows us to pass special API layer information to each layer during the process of creating an Instance. -typedef XrResult(XRAPI_PTR *PFN_xrCreateApiLayerInstance)(const XrInstanceCreateInfo *info, - const XrApiLayerCreateInfo *apiLayerInfo, XrInstance *instance); - -// Loader/API Layer Interface versions -// 1 - First version, introduces negotiation structure and functions -#define XR_CURRENT_LOADER_API_LAYER_VERSION 1 - -// Loader/Runtime Interface versions -// 1 - First version, introduces negotiation structure and functions -#define XR_CURRENT_LOADER_RUNTIME_VERSION 1 - -// Version negotiation values -typedef enum XrLoaderInterfaceStructs { - XR_LOADER_INTERFACE_STRUCT_UNINTIALIZED = 0, - XR_LOADER_INTERFACE_STRUCT_LOADER_INFO, - XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST, - XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST, - XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO, - XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO, -} XrLoaderInterfaceStructs; - -#define XR_LOADER_INFO_STRUCT_VERSION 1 -typedef struct XrNegotiateLoaderInfo { - XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_LOADER_INFO - uint32_t structVersion; // XR_LOADER_INFO_STRUCT_VERSION - size_t structSize; // sizeof(XrNegotiateLoaderInfo) - uint32_t minInterfaceVersion; - uint32_t maxInterfaceVersion; - XrVersion minApiVersion; - XrVersion maxApiVersion; -} XrNegotiateLoaderInfo; - -#define XR_API_LAYER_INFO_STRUCT_VERSION 1 -typedef struct XrNegotiateApiLayerRequest { - XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_REQUEST - uint32_t structVersion; // XR_API_LAYER_INFO_STRUCT_VERSION - size_t structSize; // sizeof(XrNegotiateApiLayerRequest) - uint32_t layerInterfaceVersion; // CURRENT_LOADER_API_LAYER_VERSION - XrVersion layerApiVersion; - PFN_xrGetInstanceProcAddr getInstanceProcAddr; - PFN_xrCreateApiLayerInstance createApiLayerInstance; -} XrNegotiateApiLayerRequest; - -#define XR_RUNTIME_INFO_STRUCT_VERSION 1 -typedef struct XrNegotiateRuntimeRequest { - XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_RUNTIME_REQUEST - uint32_t structVersion; // XR_RUNTIME_INFO_STRUCT_VERSION - size_t structSize; // sizeof(XrNegotiateRuntimeRequest) - uint32_t runtimeInterfaceVersion; // CURRENT_LOADER_RUNTIME_VERSION - XrVersion runtimeApiVersion; - PFN_xrGetInstanceProcAddr getInstanceProcAddr; -} XrNegotiateRuntimeRequest; - -// Function used to negotiate an interface betewen the loader and an API layer. Each library exposing one or -// more API layers needs to expose at least this function. -typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderApiLayerInterface)(const XrNegotiateLoaderInfo *loaderInfo, - const char *apiLayerName, - XrNegotiateApiLayerRequest *apiLayerRequest); - -// Function used to negotiate an interface betewen the loader and a runtime. Each runtime should expose -// at least this function. -typedef XrResult(XRAPI_PTR *PFN_xrNegotiateLoaderRuntimeInterface)(const XrNegotiateLoaderInfo *loaderInfo, - XrNegotiateRuntimeRequest *runtimeRequest); - -// Forward declare. -typedef struct XrApiLayerNextInfo XrApiLayerNextInfo; - -#define XR_API_LAYER_NEXT_INFO_STRUCT_VERSION 1 -struct XrApiLayerNextInfo { - XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_NEXT_INFO - uint32_t structVersion; // XR_API_LAYER_NEXT_INFO_STRUCT_VERSION - size_t structSize; // sizeof(XrApiLayerNextInfo) - char layerName[XR_MAX_API_LAYER_NAME_SIZE]; // Name of API layer which should receive this info - PFN_xrGetInstanceProcAddr nextGetInstanceProcAddr; // Pointer to next API layer's xrGetInstanceProcAddr - PFN_xrCreateApiLayerInstance nextCreateApiLayerInstance; // Pointer to next API layer's xrCreateApiLayerInstance - XrApiLayerNextInfo *next; // Pointer to the next API layer info in the sequence -}; - -#define XR_API_LAYER_MAX_SETTINGS_PATH_SIZE 512 -#define XR_API_LAYER_CREATE_INFO_STRUCT_VERSION 1 -typedef struct XrApiLayerCreateInfo { - XrLoaderInterfaceStructs structType; // XR_LOADER_INTERFACE_STRUCT_API_LAYER_CREATE_INFO - uint32_t structVersion; // XR_API_LAYER_CREATE_INFO_STRUCT_VERSION - size_t structSize; // sizeof(XrApiLayerCreateInfo) - void *loaderInstance; // Pointer to the LoaderInstance class - char settings_file_location[XR_API_LAYER_MAX_SETTINGS_PATH_SIZE]; // Location to the found settings file (or empty '\0') - XrApiLayerNextInfo *nextInfo; // Pointer to the next API layer's Info -} XrApiLayerCreateInfo; - -#ifdef __cplusplus -} // extern "C" -#endif diff --git a/thirdparty/openxr/src/common/object_info.cpp b/thirdparty/openxr/src/common/object_info.cpp index 3f8f96bc6e..d45430a609 100644 --- a/thirdparty/openxr/src/common/object_info.cpp +++ b/thirdparty/openxr/src/common/object_info.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // Copyright (c) 2019 Collabora, Ltd. @@ -6,7 +6,7 @@ // SPDX-License-Identifier: Apache-2.0 OR MIT // // Initial Authors: Mark Young <marky@lunarg.com> -// Ryan Pavlik <ryan.pavlik@collabora.com> +// Rylie Pavlik <rylie.pavlik@collabora.com> // Dave Houlton <daveh@lunarg.com> // diff --git a/thirdparty/openxr/src/common/object_info.h b/thirdparty/openxr/src/common/object_info.h index 247ede0dcc..572f06ab15 100644 --- a/thirdparty/openxr/src/common/object_info.h +++ b/thirdparty/openxr/src/common/object_info.h @@ -1,11 +1,11 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // Copyright (c) 2019 Collabora, Ltd. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Authors: Mark Young <marky@lunarg.com>, Ryan Pavlik <ryan.pavlik@collabora.com +// Initial Authors: Mark Young <marky@lunarg.com>, Rylie Pavlik <rylie.pavlik@collabora.com // /*! * @file diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp index 0b295f5cc9..c4d75bf259 100644 --- a/thirdparty/openxr/src/common/platform_utils.hpp +++ b/thirdparty/openxr/src/common/platform_utils.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/common/stdfs_conditions.h b/thirdparty/openxr/src/common/stdfs_conditions.h index 0a551f08cd..9e2ba70d42 100644 --- a/thirdparty/openxr/src/common/stdfs_conditions.h +++ b/thirdparty/openxr/src/common/stdfs_conditions.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG, Inc. // diff --git a/thirdparty/openxr/src/common/unique_asset.h b/thirdparty/openxr/src/common/unique_asset.h index a8ae8077bc..fd5f8868e3 100644 --- a/thirdparty/openxr/src/common/unique_asset.h +++ b/thirdparty/openxr/src/common/unique_asset.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // // SPDX-License-Identifier: Apache-2.0 OR MIT #pragma once diff --git a/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp b/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp index 451219d20f..885389ce8e 100644 --- a/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp +++ b/thirdparty/openxr/src/common/vulkan_debug_object_namer.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // // SPDX-License-Identifier: Apache-2.0 diff --git a/thirdparty/openxr/src/common/xr_dependencies.h b/thirdparty/openxr/src/common/xr_dependencies.h index 6c9cf2d05f..55d93bfbac 100644 --- a/thirdparty/openxr/src/common/xr_dependencies.h +++ b/thirdparty/openxr/src/common/xr_dependencies.h @@ -1,4 +1,4 @@ -// Copyright (c) 2018-2023, The Khronos Group Inc. +// Copyright (c) 2018-2024, The Khronos Group Inc. // // SPDX-License-Identifier: Apache-2.0 OR MIT // diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h index ce65f8ddfb..2b295ed304 100644 --- a/thirdparty/openxr/src/common/xr_linear.h +++ b/thirdparty/openxr/src/common/xr_linear.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2016, Oculus VR, LLC. // // SPDX-License-Identifier: Apache-2.0 diff --git a/thirdparty/openxr/src/loader/android_utilities.cpp b/thirdparty/openxr/src/loader/android_utilities.cpp index 9a3ad76ce0..5c9b846b5a 100644 --- a/thirdparty/openxr/src/loader/android_utilities.cpp +++ b/thirdparty/openxr/src/loader/android_utilities.cpp @@ -1,9 +1,9 @@ -// Copyright (c) 2020-2023, The Khronos Group Inc. +// Copyright (c) 2020-2024, The Khronos Group Inc. // Copyright (c) 2020-2021, Collabora, Ltd. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> #include "android_utilities.h" @@ -245,18 +245,38 @@ static int populateFunctions(wrap::android::content::Context const &context, boo return 0; } +// The current file relies on android-jni-wrappers and jnipp, which may throw on failure. +// This is problematic when the loader is compiled with exception handling disabled - the consumers can reasonably +// expect that the compilation with -fno-exceptions will succeed, but the compiler will not accept the code that +// uses `try` & `catch` keywords. We cannot use the `exception_handling.hpp` here since we're not at an ABI boundary, +// so we define helper macros here. This is fine for now since the only occurrence of exception-handling code is in this file. +#ifdef XRLOADER_DISABLE_EXCEPTION_HANDLING + +#define ANDROID_UTILITIES_TRY +#define ANDROID_UTILITIES_CATCH_FALLBACK(...) + +#else + +#define ANDROID_UTILITIES_TRY try +#define ANDROID_UTILITIES_CATCH_FALLBACK(...) \ + catch (const std::exception &e) { \ + __VA_ARGS__ \ + } + +#endif // XRLOADER_DISABLE_EXCEPTION_HANDLING + /// Get cursor for active runtime, parameterized by whether or not we use the system broker static bool getActiveRuntimeCursor(wrap::android::content::Context const &context, jni::Array<std::string> const &projection, bool systemBroker, Cursor &cursor) { auto uri = active_runtime::makeContentUri(systemBroker, XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), ABI); ALOGI("getActiveRuntimeCursor: Querying URI: %s", uri.toString().c_str()); - try { - cursor = context.getContentResolver().query(uri, projection); - } catch (const std::exception &e) { + + ANDROID_UTILITIES_TRY { cursor = context.getContentResolver().query(uri, projection); } + ANDROID_UTILITIES_CATCH_FALLBACK({ ALOGW("Exception when querying %s content resolver: %s", getBrokerTypeName(systemBroker), e.what()); cursor = {}; return false; - } + }) if (cursor.isNull()) { ALOGW("Null cursor when querying %s content resolver.", getBrokerTypeName(systemBroker)); diff --git a/thirdparty/openxr/src/loader/android_utilities.h b/thirdparty/openxr/src/loader/android_utilities.h index f66c9bf1d0..582a65056e 100644 --- a/thirdparty/openxr/src/loader/android_utilities.h +++ b/thirdparty/openxr/src/loader/android_utilities.h @@ -1,9 +1,9 @@ -// Copyright (c) 2020-2023, The Khronos Group Inc. +// Copyright (c) 2020-2024, The Khronos Group Inc. // Copyright (c) 2020-2021, Collabora, Ltd. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> #pragma once #ifdef __ANDROID__ diff --git a/thirdparty/openxr/src/loader/api_layer_interface.cpp b/thirdparty/openxr/src/loader/api_layer_interface.cpp index c9e24ec40b..fb509de270 100644 --- a/thirdparty/openxr/src/loader/api_layer_interface.cpp +++ b/thirdparty/openxr/src/loader/api_layer_interface.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -9,13 +9,14 @@ #include "api_layer_interface.hpp" -#include "loader_interfaces.h" +#include "loader_init_data.hpp" #include "loader_logger.hpp" #include "loader_platform.hpp" #include "manifest_file.hpp" #include "platform_utils.hpp" #include <openxr/openxr.h> +#include <openxr/openxr_loader_negotiation.h> #include <cstring> #include <memory> @@ -282,6 +283,38 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin LoaderLogger::LogWarningMessage(openxr_command, warning_message); continue; } +#ifdef XR_KHR_LOADER_INIT_SUPPORT + if (!LoaderInitData::instance().initialized()) { + LoaderLogger::LogErrorMessage(openxr_command, "ApiLayerInterface::LoadApiLayers skipping manifest file " + + manifest_file->Filename() + + " because xrInitializeLoaderKHR was not yet called."); + + LoaderPlatformLibraryClose(layer_library); + return XR_ERROR_VALIDATION_FAILURE; + } + bool forwardedInitLoader = false; + { + // If we have xrInitializeLoaderKHR exposed as an export, forward call to it. + const auto function_name = manifest_file->GetFunctionName("xrInitializeLoaderKHR"); + auto initLoader = + reinterpret_cast<PFN_xrInitializeLoaderKHR>(LoaderPlatformLibraryGetProcAddr(layer_library, function_name)); + if (initLoader != nullptr) { + // we found the entry point one way or another. + LoaderLogger::LogInfoMessage(openxr_command, + "ApiLayerInterface::LoadApiLayers forwarding xrInitializeLoaderKHR call to API layer " + "before calling xrNegotiateLoaderApiLayerInterface."); + XrResult res = initLoader(LoaderInitData::instance().getParam()); + if (!XR_SUCCEEDED(res)) { + LoaderLogger::LogErrorMessage( + openxr_command, "ApiLayerInterface::LoadApiLayers forwarded call to xrInitializeLoaderKHR failed."); + + LoaderPlatformLibraryClose(layer_library); + return res; + } + forwardedInitLoader = true; + } + } +#endif // Get and settle on an layer interface version (using any provided name if required). std::string function_name = manifest_file->GetFunctionName("xrNegotiateLoaderApiLayerInterface"); @@ -324,6 +357,38 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin LoaderLogger::LogWarningMessage(openxr_command, warning_message); res = XR_ERROR_FILE_CONTENTS_INVALID; } + +#ifdef XR_KHR_LOADER_INIT_SUPPORT + if (XR_SUCCEEDED(res) && !forwardedInitLoader) { + // Forward initialize loader call, where possible and if we did not do so before. + PFN_xrVoidFunction initializeVoid = nullptr; + PFN_xrInitializeLoaderKHR initialize = nullptr; + + // Now we may try asking xrGetInstanceProcAddr on the API layer + if (XR_SUCCEEDED(api_layer_info.getInstanceProcAddr(XR_NULL_HANDLE, "xrInitializeLoaderKHR", &initializeVoid))) { + if (initializeVoid == nullptr) { + LoaderLogger::LogErrorMessage(openxr_command, + "ApiLayerInterface::LoadApiLayers got success from xrGetInstanceProcAddr " + "for xrInitializeLoaderKHR, but output a null pointer."); + res = XR_ERROR_RUNTIME_FAILURE; + } else { + initialize = reinterpret_cast<PFN_xrInitializeLoaderKHR>(initializeVoid); + } + } + if (initialize != nullptr) { + // we found the entry point one way or another. + LoaderLogger::LogInfoMessage(openxr_command, + "ApiLayerInterface::LoadApiLayers forwarding xrInitializeLoaderKHR call to API layer " + "after calling xrNegotiateLoaderApiLayerInterface."); + res = initialize(LoaderInitData::instance().getParam()); + if (!XR_SUCCEEDED(res)) { + LoaderLogger::LogErrorMessage( + openxr_command, "ApiLayerInterface::LoadApiLayers forwarded call to xrInitializeLoaderKHR failed."); + } + } + } +#endif + if (XR_FAILED(res)) { if (!any_loaded) { last_error = res; diff --git a/thirdparty/openxr/src/loader/api_layer_interface.hpp b/thirdparty/openxr/src/loader/api_layer_interface.hpp index 98685b0c32..43758b31a4 100644 --- a/thirdparty/openxr/src/loader/api_layer_interface.hpp +++ b/thirdparty/openxr/src/loader/api_layer_interface.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -14,9 +14,9 @@ #include <memory> #include <openxr/openxr.h> +#include <openxr/openxr_loader_negotiation.h> #include "loader_platform.hpp" -#include "loader_interfaces.h" struct XrGeneratedDispatchTable; diff --git a/thirdparty/openxr/src/loader/exception_handling.hpp b/thirdparty/openxr/src/loader/exception_handling.hpp index bc0d9b65e3..62395fdf69 100644 --- a/thirdparty/openxr/src/loader/exception_handling.hpp +++ b/thirdparty/openxr/src/loader/exception_handling.hpp @@ -1,8 +1,8 @@ -// Copyright (c) 2019-2023, The Khronos Group Inc. +// Copyright (c) 2019-2024, The Khronos Group Inc. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> // // Provides protection for C ABI functions if standard library functions may throw. diff --git a/thirdparty/openxr/src/loader/loader_core.cpp b/thirdparty/openxr/src/loader/loader_core.cpp index 06e6870053..8bf2609a08 100644 --- a/thirdparty/openxr/src/loader/loader_core.cpp +++ b/thirdparty/openxr/src/loader/loader_core.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -14,6 +14,7 @@ #include "api_layer_interface.hpp" #include "exception_handling.hpp" #include "hex_and_handles.h" +#include "loader_init_data.hpp" #include "loader_instance.hpp" #include "loader_logger_recorders.hpp" #include "loader_logger.hpp" @@ -77,7 +78,7 @@ inline bool IsMissingNullTerminator(const char (&str)[max_length]) { #ifdef XR_KHR_LOADER_INIT_SUPPORT // platforms that support XR_KHR_loader_init. XRAPI_ATTR XrResult XRAPI_CALL LoaderXrInitializeLoaderKHR(const XrLoaderInitInfoBaseHeaderKHR *loaderInitInfo) XRLOADER_ABI_TRY { LoaderLogger::LogVerboseMessage("xrInitializeLoaderKHR", "Entering loader trampoline"); - return InitializeLoader(loaderInitInfo); + return InitializeLoaderInitData(loaderInitInfo); } XRLOADER_ABI_CATCH_FALLBACK #endif diff --git a/thirdparty/openxr/src/loader/loader_init_data.cpp b/thirdparty/openxr/src/loader/loader_init_data.cpp new file mode 100644 index 0000000000..11d3c4e77b --- /dev/null +++ b/thirdparty/openxr/src/loader/loader_init_data.cpp @@ -0,0 +1,59 @@ +// Copyright (c) 2017-2024, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Mark Young <marky@lunarg.com> +// + +#include "loader_init_data.hpp" + +#ifdef XR_KHR_LOADER_INIT_SUPPORT + +#ifdef XR_USE_PLATFORM_ANDROID +// Check and copy the Android-specific init data. +XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) { + if (info->type != XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) { + return XR_ERROR_VALIDATION_FAILURE; + } + auto cast_info = reinterpret_cast<XrLoaderInitInfoAndroidKHR const*>(info); + + if (cast_info->applicationVM == nullptr) { + return XR_ERROR_VALIDATION_FAILURE; + } + if (cast_info->applicationContext == nullptr) { + return XR_ERROR_VALIDATION_FAILURE; + } + + // Copy and store the JVM pointer and Android Context, ensuring the JVM is initialised. + _data = *cast_info; + _data.next = nullptr; + jni::init(static_cast<jni::JavaVM*>(_data.applicationVM)); + const jni::Object context = jni::Object{static_cast<jni::jobject>(_data.applicationContext)}; + + // Retrieve a reference to the Android AssetManager. + const auto assetManager = context.call<jni::Object>("getAssets()Landroid/content/res/AssetManager;"); + _android_asset_manager = AAssetManager_fromJava(jni::env(), assetManager.getHandle()); + + // Retrieve the path to the native libraries. + const auto applicationContext = context.call<jni::Object>("getApplicationContext()Landroid/content/Context;"); + const auto applicationInfo = context.call<jni::Object>("getApplicationInfo()Landroid/content/pm/ApplicationInfo;"); + _native_library_path = applicationInfo.get<std::string>("nativeLibraryDir"); + + _initialized = true; + return XR_SUCCESS; +} +#endif // XR_USE_PLATFORM_ANDROID + +XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) { + return LoaderInitData::instance().initialize(loaderInitInfo); +} + +#ifdef XR_USE_PLATFORM_ANDROID +std::string GetAndroidNativeLibraryDir() { return LoaderInitData::instance()._native_library_path; } + +void* Android_Get_Asset_Manager() { return LoaderInitData::instance()._android_asset_manager; } +#endif // XR_USE_PLATFORM_ANDROID + +#endif // XR_KHR_LOADER_INIT_SUPPORT diff --git a/thirdparty/openxr/src/loader/loader_init_data.hpp b/thirdparty/openxr/src/loader/loader_init_data.hpp new file mode 100644 index 0000000000..fe6bc134d3 --- /dev/null +++ b/thirdparty/openxr/src/loader/loader_init_data.hpp @@ -0,0 +1,92 @@ +// Copyright (c) 2017-2024, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 OR MIT +// +// Initial Author: Mark Young <marky@lunarg.com> +// + +#pragma once + +#include <xr_dependencies.h> +#include <openxr/openxr_platform.h> + +#ifdef XR_USE_PLATFORM_ANDROID +#include <json/value.h> +#include <android/asset_manager_jni.h> +#include "android_utilities.h" +#endif // XR_USE_PLATFORM_ANDROID + +#ifdef XR_KHR_LOADER_INIT_SUPPORT + +/*! + * Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton. + */ +class LoaderInitData { + public: + /*! + * Singleton accessor. + */ + static LoaderInitData& instance() { + static LoaderInitData obj; + return obj; + } + +#ifdef XR_USE_PLATFORM_ANDROID + /*! + * Type alias for the platform-specific structure type. + */ + using StructType = XrLoaderInitInfoAndroidKHR; + /*! + * Native library path. + */ + std::string _native_library_path; + /*! + * Android asset manager. + */ + AAssetManager* _android_asset_manager; +#else +#error "Platform specific XR_KHR_loader_init structure is not defined for this platform." +#endif + + /*! + * Get our copy of the data, casted to pass to the runtime's matching method. + */ + const XrLoaderInitInfoBaseHeaderKHR* getParam() const { return reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&_data); } + + /*! + * Get the data via its real structure type. + */ + const StructType& getData() const { return _data; } + + /*! + * Has this been correctly initialized? + */ + bool initialized() const noexcept { return _initialized; } + + /*! + * Initialize loader data - called by InitializeLoaderInitData() and thus ultimately by the loader's xrInitializeLoaderKHR + * implementation. Each platform that needs this extension will provide an implementation of this. + */ + XrResult initialize(const XrLoaderInitInfoBaseHeaderKHR* info); + + private: + //! Private constructor, forces use of singleton accessor. + LoaderInitData() = default; + //! Platform-specific init data + StructType _data = {}; + //! Flag for indicating whether _data is valid. + bool _initialized = false; +}; + +//! Initialize loader init data, where required. +XrResult InitializeLoaderInitData(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); + +#ifdef XR_USE_PLATFORM_ANDROID +XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest); +std::string GetAndroidNativeLibraryDir(); +void* Android_Get_Asset_Manager(); +#endif // XR_USE_PLATFORM_ANDROID + +#endif // XR_KHR_LOADER_INIT_SUPPORT diff --git a/thirdparty/openxr/src/loader/loader_instance.cpp b/thirdparty/openxr/src/loader/loader_instance.cpp index ce5c205505..f18230087f 100644 --- a/thirdparty/openxr/src/loader/loader_instance.cpp +++ b/thirdparty/openxr/src/loader/loader_instance.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -15,13 +15,13 @@ #include "api_layer_interface.hpp" #include "hex_and_handles.h" -#include "loader_interfaces.h" #include "loader_logger.hpp" #include "runtime_interface.hpp" #include "xr_generated_dispatch_table_core.h" #include "xr_generated_loader.hpp" #include <openxr/openxr.h> +#include <openxr/openxr_loader_negotiation.h> #include <cstring> #include <memory> @@ -60,7 +60,7 @@ XrResult Get(LoaderInstance** loader_instance, const char* log_function_name) { bool IsAvailable() { return GetSetCurrentLoaderInstance() != nullptr; } -void Remove() { GetSetCurrentLoaderInstance().release(); } +void Remove() { GetSetCurrentLoaderInstance().reset(nullptr); } } // namespace ActiveLoaderInstance // Extensions that are supported by the loader, but may not be supported diff --git a/thirdparty/openxr/src/loader/loader_instance.hpp b/thirdparty/openxr/src/loader/loader_instance.hpp index a0268a855c..b99e6b047f 100644 --- a/thirdparty/openxr/src/loader/loader_instance.hpp +++ b/thirdparty/openxr/src/loader/loader_instance.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -10,9 +10,9 @@ #pragma once #include "extra_algorithms.h" -#include "loader_interfaces.h" #include <openxr/openxr.h> +#include <openxr/openxr_loader_negotiation.h> #include <array> #include <cmath> diff --git a/thirdparty/openxr/src/loader/loader_logger.cpp b/thirdparty/openxr/src/loader/loader_logger.cpp index 1c8d64f394..593e19a3ba 100644 --- a/thirdparty/openxr/src/loader/loader_logger.cpp +++ b/thirdparty/openxr/src/loader/loader_logger.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/loader_logger.hpp b/thirdparty/openxr/src/loader/loader_logger.hpp index d31fac093a..964aa47962 100644 --- a/thirdparty/openxr/src/loader/loader_logger.hpp +++ b/thirdparty/openxr/src/loader/loader_logger.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/loader_logger_recorders.cpp b/thirdparty/openxr/src/loader/loader_logger_recorders.cpp index 32e4687b2f..00392c27bd 100644 --- a/thirdparty/openxr/src/loader/loader_logger_recorders.cpp +++ b/thirdparty/openxr/src/loader/loader_logger_recorders.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/loader_logger_recorders.hpp b/thirdparty/openxr/src/loader/loader_logger_recorders.hpp index 7b934202d5..b676c37e93 100644 --- a/thirdparty/openxr/src/loader/loader_logger_recorders.hpp +++ b/thirdparty/openxr/src/loader/loader_logger_recorders.hpp @@ -1,10 +1,10 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // // SPDX-License-Identifier: Apache-2.0 OR MIT // -// Initial Author: Ryan Pavlik <ryan.pavlik@collabora.com> +// Initial Author: Rylie Pavlik <rylie.pavlik@collabora.com> // #pragma once diff --git a/thirdparty/openxr/src/loader/loader_platform.hpp b/thirdparty/openxr/src/loader/loader_platform.hpp index 0ea80c05b8..964116caa4 100644 --- a/thirdparty/openxr/src/loader/loader_platform.hpp +++ b/thirdparty/openxr/src/loader/loader_platform.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/manifest_file.cpp b/thirdparty/openxr/src/loader/manifest_file.cpp index 0683bc166a..f9699ece40 100644 --- a/thirdparty/openxr/src/loader/manifest_file.cpp +++ b/thirdparty/openxr/src/loader/manifest_file.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -18,6 +18,7 @@ #endif // OPENXR_HAVE_COMMON_CONFIG #include "filesystem_utils.hpp" +#include "loader_init_data.hpp" #include "loader_platform.hpp" #include "platform_utils.hpp" #include "loader_logger.hpp" @@ -666,14 +667,14 @@ XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<Runt } #else -#if defined(XR_KHR_LOADER_INIT_SUPPORT) +#if defined(XR_USE_PLATFORM_ANDROID) Json::Value virtualManifest; result = GetPlatformRuntimeVirtualManifest(virtualManifest); if (XR_SUCCESS == result) { RuntimeManifestFile::CreateIfValid(virtualManifest, "", manifest_files); return result; } -#endif // defined(XR_KHR_LOADER_INIT_SUPPORT) +#endif // defined(XR_USE_PLATFORM_ANDROID) if (!PlatformGetGlobalRuntimeFileName(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) { LoaderLogger::LogErrorMessage( "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment"); diff --git a/thirdparty/openxr/src/loader/manifest_file.hpp b/thirdparty/openxr/src/loader/manifest_file.hpp index 46b842c663..52fe3134b0 100644 --- a/thirdparty/openxr/src/loader/manifest_file.hpp +++ b/thirdparty/openxr/src/loader/manifest_file.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017 Valve Corporation // Copyright (c) 2017 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/runtime_interface.cpp b/thirdparty/openxr/src/loader/runtime_interface.cpp index 8312b15ba4..7812aca987 100644 --- a/thirdparty/openxr/src/loader/runtime_interface.cpp +++ b/thirdparty/openxr/src/loader/runtime_interface.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -9,14 +9,15 @@ #include "runtime_interface.hpp" +#include <openxr/openxr.h> +#include <openxr/openxr_loader_negotiation.h> + #include "manifest_file.hpp" -#include "loader_interfaces.h" +#include "loader_init_data.hpp" #include "loader_logger.hpp" #include "loader_platform.hpp" #include "xr_generated_dispatch_table_core.h" -#include <openxr/openxr.h> - #include <cstring> #include <memory> #include <mutex> @@ -26,8 +27,6 @@ #include <vector> #ifdef XR_USE_PLATFORM_ANDROID -#include "android_utilities.h" -#include <android/asset_manager_jni.h> #include <json/value.h> // Needed for the loader init struct @@ -35,112 +34,6 @@ #include <openxr/openxr_platform.h> #endif // XR_USE_PLATFORM_ANDROID -#ifdef XR_KHR_LOADER_INIT_SUPPORT -namespace { -/*! - * Stores a copy of the data passed to the xrInitializeLoaderKHR function in a singleton. - */ -class LoaderInitData { - public: - /*! - * Singleton accessor. - */ - static LoaderInitData& instance() { - static LoaderInitData obj; - return obj; - } - -#ifdef XR_USE_PLATFORM_ANDROID - /*! - * Type alias for the platform-specific structure type. - */ - using StructType = XrLoaderInitInfoAndroidKHR; - /*! - * Native library path. - */ - std::string _native_library_path; - /*! - * Android asset manager. - */ - AAssetManager* _android_asset_manager; -#endif - - /*! - * Get our copy of the data, casted to pass to the runtime's matching method. - */ - const XrLoaderInitInfoBaseHeaderKHR* getParam() const { return reinterpret_cast<const XrLoaderInitInfoBaseHeaderKHR*>(&_data); } - - /*! - * Get the data via its real structure type. - */ - const StructType& getData() const { return _data; } - - /*! - * Has this been correctly initialized? - */ - bool initialized() const noexcept { return _initialized; } - - /*! - * Initialize loader data - called by InitializeLoader() and thus ultimately by the loader's xrInitializeLoaderKHR - * implementation. Each platform that needs this extension will provide an implementation of this. - */ - XrResult initialize(const XrLoaderInitInfoBaseHeaderKHR* info); - - private: - //! Private constructor, forces use of singleton accessor. - LoaderInitData() = default; - //! Platform-specific init data - StructType _data = {}; - //! Flag for indicating whether _data is valid. - bool _initialized = false; -}; - -#ifdef XR_USE_PLATFORM_ANDROID -// Check and copy the Android-specific init data. -XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) { - if (info->type != XR_TYPE_LOADER_INIT_INFO_ANDROID_KHR) { - return XR_ERROR_VALIDATION_FAILURE; - } - auto cast_info = reinterpret_cast<XrLoaderInitInfoAndroidKHR const*>(info); - - if (cast_info->applicationVM == nullptr) { - return XR_ERROR_VALIDATION_FAILURE; - } - if (cast_info->applicationContext == nullptr) { - return XR_ERROR_VALIDATION_FAILURE; - } - - // Copy and store the JVM pointer and Android Context, ensuring the JVM is initialised. - _data = *cast_info; - _data.next = nullptr; - jni::init(static_cast<jni::JavaVM*>(_data.applicationVM)); - const jni::Object context = jni::Object{static_cast<jni::jobject>(_data.applicationContext)}; - - // Retrieve a reference to the Android AssetManager. - const auto assetManager = context.call<jni::Object>("getAssets()Landroid/content/res/AssetManager;"); - _android_asset_manager = AAssetManager_fromJava(jni::env(), assetManager.getHandle()); - - // Retrieve the path to the native libraries. - const auto applicationContext = context.call<jni::Object>("getApplicationContext()Landroid/content/Context;"); - const auto applicationInfo = context.call<jni::Object>("getApplicationInfo()Landroid/content/pm/ApplicationInfo;"); - _native_library_path = applicationInfo.get<std::string>("nativeLibraryDir"); - - _initialized = true; - return XR_SUCCESS; -} -#endif // XR_USE_PLATFORM_ANDROID -} // namespace - -XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo) { - return LoaderInitData::instance().initialize(loaderInitInfo); -} - -std::string GetAndroidNativeLibraryDir() { return LoaderInitData::instance()._native_library_path; } - -void* Android_Get_Asset_Manager() { return LoaderInitData::instance()._android_asset_manager; } - -#endif // XR_KHR_LOADER_INIT_SUPPORT - #ifdef XR_USE_PLATFORM_ANDROID XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest) { using wrap::android::content::Context; diff --git a/thirdparty/openxr/src/loader/runtime_interface.hpp b/thirdparty/openxr/src/loader/runtime_interface.hpp index 8d55ec674a..093f8ba767 100644 --- a/thirdparty/openxr/src/loader/runtime_interface.hpp +++ b/thirdparty/openxr/src/loader/runtime_interface.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -19,22 +19,10 @@ #include <mutex> #include <memory> -#ifdef XR_USE_PLATFORM_ANDROID -#define XR_KHR_LOADER_INIT_SUPPORT -#endif - namespace Json { class Value; } -#ifdef XR_KHR_LOADER_INIT_SUPPORT -//! Initialize loader, where required. -XrResult InitializeLoader(const XrLoaderInitInfoBaseHeaderKHR* loaderInitInfo); -XrResult GetPlatformRuntimeVirtualManifest(Json::Value& out_manifest); -std::string GetAndroidNativeLibraryDir(); -void* Android_Get_Asset_Manager(); -#endif - class RuntimeManifestFile; struct XrGeneratedDispatchTable; diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.cpp b/thirdparty/openxr/src/loader/xr_generated_loader.cpp index 8c79afddc5..7bddbdc3ea 100644 --- a/thirdparty/openxr/src/loader/xr_generated_loader.cpp +++ b/thirdparty/openxr/src/loader/xr_generated_loader.cpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // SPDX-License-Identifier: Apache-2.0 OR MIT @@ -6,7 +6,7 @@ // See loader_source_generator.py for modifications // ************************************************************ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.hpp b/thirdparty/openxr/src/loader/xr_generated_loader.hpp index e28e35bbcf..68a6b9470d 100644 --- a/thirdparty/openxr/src/loader/xr_generated_loader.hpp +++ b/thirdparty/openxr/src/loader/xr_generated_loader.hpp @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // SPDX-License-Identifier: Apache-2.0 OR MIT @@ -6,7 +6,7 @@ // See loader_source_generator.py for modifications // ************************************************************ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // @@ -34,10 +34,9 @@ #include "xr_dependencies.h" #include "openxr/openxr.h" +#include "openxr/openxr_loader_negotiation.h" #include "openxr/openxr_platform.h" -#include "loader_interfaces.h" - #include "loader_instance.hpp" #include "loader_platform.hpp" diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.c b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c index de88ef7e6c..e73e8b2b95 100644 --- a/thirdparty/openxr/src/xr_generated_dispatch_table_core.c +++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019, Valve Corporation // Copyright (c) 2017-2019, LunarG, Inc. @@ -8,7 +8,7 @@ // See utility_source_generator.py for modifications // ************************************************************ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.h b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h index 0f3e7e0502..5871231267 100644 --- a/thirdparty/openxr/src/xr_generated_dispatch_table_core.h +++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h @@ -1,4 +1,4 @@ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019, Valve Corporation // Copyright (c) 2017-2019, LunarG, Inc. @@ -8,7 +8,7 @@ // See utility_source_generator.py for modifications // ************************************************************ -// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2024, The Khronos Group Inc. // Copyright (c) 2017-2019 Valve Corporation // Copyright (c) 2017-2019 LunarG, Inc. // diff --git a/thirdparty/thorvg/AUTHORS b/thirdparty/thorvg/AUTHORS index 3a459e9bb4..d5d5e9be97 100644 --- a/thirdparty/thorvg/AUTHORS +++ b/thirdparty/thorvg/AUTHORS @@ -23,3 +23,5 @@ Rafał Mikrut <mikrutrafal@protonmail.com> Martin Capitanio <capnm@capitanio.org> RuiwenTang <tangruiwen1989@gmail.com> YouJin Lee <ol-of@naver.com> +SergeyLebedkin <sergii@lottiefiles.com> +Jinny You <jinny@lottiefiles.com> diff --git a/thirdparty/thorvg/LICENSE b/thirdparty/thorvg/LICENSE index d056ff6cbf..f97be65005 100644 --- a/thirdparty/thorvg/LICENSE +++ b/thirdparty/thorvg/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2020 - 2023 notice for the ThorVG Project (see AUTHORS) +Copyright (c) 2020 - 2024 notice for the ThorVG Project (see AUTHORS) Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: diff --git a/thirdparty/thorvg/inc/config.h b/thirdparty/thorvg/inc/config.h index 98f35a6e45..d1abc5a290 100644 --- a/thirdparty/thorvg/inc/config.h +++ b/thirdparty/thorvg/inc/config.h @@ -9,5 +9,5 @@ // For internal debugging: //#define THORVG_LOG_ENABLED -#define THORVG_VERSION_STRING "0.11.6" +#define THORVG_VERSION_STRING "0.12.0" #endif diff --git a/thirdparty/thorvg/inc/thorvg.h b/thirdparty/thorvg/inc/thorvg.h index 20acda7667..6aee53f7e0 100644 --- a/thirdparty/thorvg/inc/thorvg.h +++ b/thirdparty/thorvg/inc/thorvg.h @@ -1,17 +1,3 @@ -/*! - * @file thorvg.h - * - * The main APIs enabling the TVG initialization, preparation of the canvas and provisioning of its content: - * - drawing shapes: line, arc, curve, path, polygon... - * - drawing pictures: tvg, svg, png, jpg, bitmap... - * - drawing fillings: solid, linear and radial gradient... - * - drawing stroking: continuous stroking with arbitrary width, join, cap, dash styles. - * - drawing composition: blending, masking, path clipping... - * - drawing scene graph & affine transformation (translation, rotation, scale, ...) - * and finally drawing the canvas and TVG termination. - */ - - #ifndef _THORVG_H_ #define _THORVG_H_ @@ -172,10 +158,10 @@ enum class CompositeMethod InvAlphaMask, ///< Alpha Masking using the complement to the compositing target's pixels as an alpha value. LumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the compositing target's pixels. @since 0.9 InvLumaMask, ///< Alpha Masking using the grayscale (0.2125R + 0.7154G + 0.0721*B) of the complement to the compositing target's pixels. - AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) @BETA_API - SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) @BETA_API - IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) @BETA_API - DifferenceMask ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) @BETA_API + AddMask, ///< Combines the target and source objects pixels using target alpha. (T * TA) + (S * (255 - TA)) (Experimental API) + SubtractMask, ///< Subtracts the source color from the target color while considering their respective target alpha. (T * TA) - (S * (255 - TA)) (Experimental API) + IntersectMask, ///< Computes the result by taking the minimum value between the target alpha and the source alpha and multiplies it with the target color. (T * min(TA, SA)) (Experimental API) + DifferenceMask ///< Calculates the absolute difference between the target color and the source color multiplied by the complement of the target alpha. abs(T - S * (255 - TA)) (Experimental API) }; @@ -186,7 +172,7 @@ enum class CompositeMethod * * @see Paint::blend() * - * @BETA_API + * @note Experimental API */ enum class BlendMethod : uint8_t { @@ -213,7 +199,8 @@ enum class BlendMethod : uint8_t enum class CanvasEngine { Sw = (1 << 1), ///< CPU rasterizer. - Gl = (1 << 2) ///< OpenGL rasterizer. + Gl = (1 << 2), ///< OpenGL rasterizer. + Wg = (1 << 3), ///< WebGPU rasterizer. (Experimental API) }; @@ -247,7 +234,7 @@ struct Matrix * @param pt The vertex coordinate * @param uv The normalized texture coordinate in the range (0.0..1.0, 0.0..1.0) * - * @BETA_API + * @note Experimental API */ struct Vertex { @@ -261,7 +248,7 @@ struct Vertex * * @param vertex The three vertices that make up the polygon * - * @BETA_API + * @note Experimental API */ struct Polygon { @@ -291,7 +278,7 @@ public: * * @param[in] degree The value of the angle in degrees. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result rotate(float degree) noexcept; @@ -300,7 +287,7 @@ public: * * @param[in] factor The value of the scaling factor. The default value is 1. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result scale(float factor) noexcept; @@ -313,7 +300,7 @@ public: * @param[in] x The value of the horizontal shift. * @param[in] y The value of the vertical shift. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result translate(float x, float y) noexcept; @@ -324,7 +311,7 @@ public: * * @param[in] m The 3x3 augmented matrix. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result transform(const Matrix& m) noexcept; @@ -345,7 +332,7 @@ public: * * @param[in] o The opacity value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note Setting the opacity with this API may require multiple render pass for composition. It is recommended to avoid changing the opacity if possible. * @note ClipPath won't use the opacity value. (see: enum class CompositeMethod::ClipPath) @@ -358,7 +345,7 @@ public: * @param[in] target The paint of the target object. * @param[in] method The method used to composite the source object with the target. * - * @return Result::Success when succeed, Result::InvalidArguments otherwise. + * @retval Result::Success when succeed, Result::InvalidArguments otherwise. */ Result composite(std::unique_ptr<Paint> target, CompositeMethod method) noexcept; @@ -371,9 +358,9 @@ public: * * @param[in] method The blending method to be set. * - * @return Result::Success when the blending method is successfully set. + * @retval Result::Success when the blending method is successfully set. * - * @BETA_API + * @note Experimental API */ Result blend(BlendMethod method) const noexcept; @@ -404,7 +391,7 @@ public: * @param[out] h The height of the object. * @param[in] transformed If @c true, the paint's transformations are taken into account, otherwise they aren't. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. * * @note The bounding box doesn't indicate the actual drawing region. It's the smallest rectangle that encloses the object. */ @@ -442,7 +429,7 @@ public: * * @return The blending method * - * @BETA_API + * @note Experimental API */ BlendMethod blend() const noexcept; @@ -493,7 +480,7 @@ public: * @param[in] colorStops An array of ColorStop data structure. * @param[in] cnt The count of the @p colorStops array equal to the colors number used in the gradient. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result colorStops(const ColorStop* colorStops, uint32_t cnt) noexcept; @@ -502,7 +489,7 @@ public: * * @param[in] s The FillSpread value. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result spread(FillSpread s) noexcept; @@ -513,7 +500,7 @@ public: * * @param[in] m The 3x3 augmented matrix. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result transform(const Matrix& m) noexcept; @@ -538,7 +525,7 @@ public: * * In case no transformation was applied, the identity matrix is returned. * - * @retval The augmented transformation matrix. + * @return The augmented transformation matrix. */ Matrix transform() const noexcept; @@ -600,7 +587,7 @@ public: * @warning Please avoid accessing the paints during Canvas update/draw. You can access them after calling sync(). * @see Canvas::sync() * - * @BETA_API + * @note Experimental API */ std::list<Paint*>& paints() noexcept; @@ -631,7 +618,7 @@ public: * * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. * * @see Canvas::push() * @see Canvas::paints() @@ -646,7 +633,7 @@ public: * * @param[in] paint A pointer to the Paint object or @c nullptr. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. * * @note The Update behavior can be asynchronous if the assigned thread number is greater than zero. */ @@ -655,7 +642,7 @@ public: /** * @brief Requests the canvas to draw the Paint objects. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. * * @note Drawing can be asynchronous if the assigned thread number is greater than zero. To guarantee the drawing is done, call sync() afterwards. * @see Canvas::sync() @@ -668,7 +655,7 @@ public: * The Canvas rendering can be performed asynchronously. To make sure that rendering is finished, * the sync() must be called after the draw() regardless of threading. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. * @see Canvas::draw() */ virtual Result sync() noexcept; @@ -702,7 +689,7 @@ public: * @param[in] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[in] y2 The vertical coordinate of the second point used to determine the gradient bounds. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note In case the first and the second points are equal, an object filled with such a gradient fill is not rendered. */ @@ -720,7 +707,7 @@ public: * @param[out] x2 The horizontal coordinate of the second point used to determine the gradient bounds. * @param[out] y2 The vertical coordinate of the second point used to determine the gradient bounds. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result linear(float* x1, float* y1, float* x2, float* y2) const noexcept; @@ -764,7 +751,7 @@ public: * @param[in] cy The vertical coordinate of the center of the bounding circle. * @param[in] radius The radius of the bounding circle. * - * @return Result::Success when succeed, Result::InvalidArguments in case the @p radius value is zero or less. + * @retval Result::Success when succeed, Result::InvalidArguments in case the @p radius value is zero or less. */ Result radial(float cx, float cy, float radius) noexcept; @@ -777,7 +764,7 @@ public: * @param[out] cy The vertical coordinate of the center of the bounding circle. * @param[out] radius The radius of the bounding circle. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result radial(float* cx, float* cy, float* radius) const noexcept; @@ -823,7 +810,7 @@ public: * * The transformation matrix, the color, the fill and the stroke properties are retained. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note The memory, where the path data is stored, is not deallocated at this stage for caching effect. */ @@ -837,7 +824,7 @@ public: * @param[in] x The horizontal coordinate of the initial point of the sub-path. * @param[in] y The vertical coordinate of the initial point of the sub-path. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result moveTo(float x, float y) noexcept; @@ -849,7 +836,7 @@ public: * @param[in] x The horizontal coordinate of the end-point of the line. * @param[in] y The vertical coordinate of the end-point of the line. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note In case this is the first command in the path, it corresponds to the moveTo() call. */ @@ -868,7 +855,7 @@ public: * @param[in] x The horizontal coordinate of the end-point of the curve. * @param[in] y The vertical coordinate of the end-point of the curve. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note In case this is the first command in the path, no data from the path are rendered. */ @@ -879,7 +866,7 @@ public: * * The value of the current point is set to the initial point of the closed sub-path. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note In case the sub-path does not contain any points, this function has no effect. */ @@ -905,7 +892,7 @@ public: * @param[in] rx The x-axis radius of the ellipse defining the rounded corners of the rectangle. * @param[in] ry The y-axis radius of the ellipse defining the rounded corners of the rectangle. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note For @p rx and @p ry greater than or equal to the half of @p w and the half of @p h, respectively, the shape become an ellipse. */ @@ -925,7 +912,7 @@ public: * @param[in] rx The x-axis radius of the ellipse. * @param[in] ry The y-axis radius of the ellipse. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result appendCircle(float cx, float cy, float rx, float ry) noexcept; @@ -942,7 +929,7 @@ public: * @param[in] sweep The central angle of the arc given in degrees, measured counter-clockwise from @p startAngle. * @param[in] pie Specifies whether to draw radii from the arc's center to both of its end-point - drawn if @c true. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note Setting @p sweep value greater than 360 degrees, is equivalent to calling appendCircle(cx, cy, radius, radius). */ @@ -960,7 +947,7 @@ public: * @param[in] pts The array of the two-dimensional points. * @param[in] ptsCnt The number of the points in the @p pts array. * - * @return Result::Success when succeed, Result::InvalidArguments otherwise. + * @retval Result::Success when succeed, Result::InvalidArguments otherwise. * * @note The interface is designed for optimal path setting if the caller has a completed path commands already. */ @@ -971,7 +958,7 @@ public: * * @param[in] width The width of the stroke. The default value is 0. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result stroke(float width) noexcept; @@ -983,7 +970,7 @@ public: * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result stroke(uint8_t r, uint8_t g, uint8_t b, uint8_t a = 255) noexcept; @@ -1018,7 +1005,7 @@ public: * * @param[in] cap The cap style value. The default value is @c StrokeCap::Square. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result stroke(StrokeCap cap) noexcept; @@ -1029,7 +1016,7 @@ public: * * @param[in] join The join style value. The default value is @c StrokeJoin::Bevel. * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. */ Result stroke(StrokeJoin join) noexcept; @@ -1039,7 +1026,7 @@ public: * * @param[in] miterlimit The miterlimit imposes a limit on the extent of the stroke join, when the @c StrokeJoin::Miter join style is set. The default value is 4. * - * @return Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::NonSupport unsupported value, Result::FailedAllocation otherwise. * * @since 0.11 */ @@ -1055,7 +1042,7 @@ public: * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. * @param[in] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. The default value is 0. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. * @note ClipPath won't use the fill values. (see: enum class CompositeMethod::ClipPath) @@ -1069,7 +1056,7 @@ public: * * @param[in] f The unique pointer to the gradient fill. * - * @return Result::Success when succeed, Result::MemoryCorruption otherwise. + * @retval Result::Success when succeed, Result::MemoryCorruption otherwise. * * @note Either a solid color or a gradient fill is applied, depending on what was set as last. */ @@ -1080,7 +1067,7 @@ public: * * @param[in] r The fill rule value. The default value is @c FillRule::Winding. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result fill(FillRule r) noexcept; @@ -1090,7 +1077,7 @@ public: * * @param[in] strokeFirst If @c true the stroke is rendered before the fill, otherwise the stroke is rendered as the second one (the default option). * - * @return Result::Success when succeed, Result::FailedAllocation otherwise. + * @retval Result::Success when succeed, Result::FailedAllocation otherwise. * * @since 0.10 */ @@ -1156,7 +1143,7 @@ public: * @param[out] b The blue color channel value in the range [0 ~ 255]. * @param[out] a The alpha channel value in the range [0 ~ 255], where 0 is completely transparent and 255 is opaque. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. */ Result strokeColor(uint8_t* r, uint8_t* g, uint8_t* b, uint8_t* a = nullptr) const noexcept; @@ -1295,7 +1282,7 @@ public: * @param[in] w A new width of the image in pixels. * @param[in] h A new height of the image in pixels. * - * @return Result::Success when succeed, Result::InsufficientCondition otherwise. + * @retval Result::Success when succeed, Result::InsufficientCondition otherwise. */ Result size(float w, float h) noexcept; @@ -1305,13 +1292,20 @@ public: * @param[out] w The width of the image in pixels. * @param[out] h The height of the image in pixels. * - * @return Result::Success when succeed. + * @retval Result::Success when succeed. */ Result size(float* w, float* h) const noexcept; /** * @brief Loads a raw data from a memory block with a given size. * + * @param[in] paint A Tvg_Paint pointer to the picture object. + * @param[in] data A pointer to a memory location where the content of the picture raw data is stored. + * @param[in] w The width of the image @p data in pixels. + * @param[in] h The height of the image @p data in pixels. + * @param[in] premultiplied If @c true, the given image data is alpha-premultiplied. + * @param[in] copy If @c true the data are copied into the engine local buffer, otherwise they are not. + * * @retval Result::Success When succeed, Result::InsufficientCondition otherwise. * @retval Result::FailedAllocation An internal error possibly with memory allocation. * @@ -1339,7 +1333,7 @@ public: * @note The Polygons are copied internally, so modifying them after calling Mesh::mesh has no affect. * @warning Please do not use it, this API is not official one. It could be modified in the next version. * - * @BETA_API + * @note Experimental API */ Result mesh(const Polygon* triangles, uint32_t triangleCnt) noexcept; @@ -1348,12 +1342,12 @@ public: * * @param[out] triangles Optional. A pointer to the array of Polygons used by this mesh. * - * @return uint32_t The number of polygons in the array. + * @return The number of polygons in the array. * * @note Modifying the triangles returned by this method will modify them directly within the mesh. * @warning Please do not use it, this API is not official one. It could be modified in the next version. * - * @BETA_API + * @note Experimental API */ uint32_t mesh(const Polygon** triangles) const noexcept; @@ -1402,7 +1396,7 @@ public: * * @param[in] paint A Paint object to be drawn. * - * @return Result::Success when succeed, Result::MemoryCorruption otherwise. + * @retval Result::Success when succeed, Result::MemoryCorruption otherwise. * * @note The rendering order of the paints is the same as the order as they were pushed. Consider sorting the paints before pushing them if you intend to use layering. * @see Scene::paints() @@ -1432,7 +1426,7 @@ public: * @see Scene::push() * @see Scene::clear() * - * @BETA_API + * @note Experimental API */ std::list<Paint*>& paints() noexcept; @@ -1442,7 +1436,7 @@ public: * * @param[in] free If @c true, the memory occupied by paints is deallocated, otherwise it is not. * - * @return Result::Success when succeed + * @retval Result::Success when succeed * * @warning If you don't free the paints they become dangled. They are supposed to be reused, otherwise you are responsible for their lives. Thus please use the @p free argument only when you know how it works, otherwise it's not recommended. * @@ -1471,6 +1465,138 @@ public: /** + * @class Text + * + * @brief A class to represent text objects in a graphical context, allowing for rendering and manipulation of unicode text. + * + * @note Experimental API + */ +class TVG_API Text final : public Paint +{ +public: + ~Text(); + + /** + * @brief Sets the font properties for the text. + * + * This function allows you to define the font characteristics used for text rendering. + * It sets the font name, size and optionally the style. + * + * @param[in] name The name of the font. This should correspond to a font available in the canvas. + * @param[in] size The size of the font in points. This determines how large the text will appear. + * @param[in] style The style of the font. It can be used to set the font to 'italic'. + * If not specified, the default style is used. Only 'italic' style is supported currently. + * + * @retval Result::Success when the font properties are set successfully. + * @retval Result::InsufficientCondition when the specified @p name cannot be found. + * + * @note Experimental API + */ + Result font(const char* name, float size, const char* style = nullptr) noexcept; + + /** + * @brief Assigns the given unicode text to be rendered. + * + * This function sets the unicode string that will be displayed by the rendering system. + * The text is set according to the specified UTF encoding method, which defaults to UTF-8. + * + * @param[in] text The multi-byte text encoded with utf8 string to be rendered. + * + * @retval Result::Success when succeed. + * + * @note Experimental API + */ + Result text(const char* text) noexcept; + + /** + * @brief Sets the text color. + * + * @param[in] r The red color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] g The green color channel value in the range [0 ~ 255]. The default value is 0. + * @param[in] b The blue color channel value in the range [0 ~ 255]. The default value is 0. + * + * @retval Result::Success when succeed. + * @retval Result::InsufficientCondition when the font has not been set up prior to this operation. + * + * @see Text::font() + * + * @note Experimental API + */ + Result fill(uint8_t r, uint8_t g, uint8_t b) noexcept; + + /** + * @brief Sets the gradient fill for all of the figures from the text. + * + * The parts of the text defined as inner are filled. + * + * @param[in] f The unique pointer to the gradient fill. + * + * @retval Result::Success when succeed, Result::MemoryCorruption otherwise. + * @retval Result::InsufficientCondition when the font has not been set up prior to this operation. + * + * @note Either a solid color or a gradient fill is applied, depending on what was set as last. + * @note Experimental API + * + * @see Text::font() + */ + Result fill(std::unique_ptr<Fill> f) noexcept; + + /** + * @brief Loads a scalable font data(ttf) from a file. + * + * @param[in] path The path to the font file. + * + * @retval Result::Success When succeed. + * @retval Result::InvalidArguments In case the @p path is invalid. + * @retval Result::NonSupport When trying to load a file with an unknown extension. + * @retval Result::Unknown If an error occurs at a later stage. + * + * @note Experimental API + * + * @see Text::unload(const std::string& path) + */ + static Result load(const std::string& path) noexcept; + + /** + * @brief Unloads the specified scalable font data (TTF) that was previously loaded. + * + * This function is used to release resources associated with a font file that has been loaded into memory. + * + * @param[in] path The file path of the loaded font. + * + * @retval Result::Success Successfully unloads the font data. + * @retval Result::InsufficientCondition Fails if the loader is not initialized. + * + * @note If the font data is currently in use, it will not be immediately unloaded. + * @note Experimental API + * + * @see Text::load(const std::string& path) + */ + static Result unload(const std::string& path) noexcept; + + /** + * @brief Creates a new Text object. + * + * @return A new Text object. + * + * @note Experimental API + */ + static std::unique_ptr<Text> gen() noexcept; + + /** + * @brief Return the unique id value of this class. + * + * This method can be referred for identifying the Text class type. + * + * @return The type id of the Text class. + */ + static uint32_t identifier() noexcept; + + _TVG_DECLARE_PRIVATE(Text); +}; + + +/** * @class SwCanvas * * @brief A class for the rendering graphical elements with a software raster engine. @@ -1487,8 +1613,8 @@ public: { ABGR8888 = 0, ///< The channels are joined in the order: alpha, blue, green, red. Colors are alpha-premultiplied. (a << 24 | b << 16 | g << 8 | r) ARGB8888, ///< The channels are joined in the order: alpha, red, green, blue. Colors are alpha-premultiplied. (a << 24 | r << 16 | g << 8 | b) - ABGR8888S, ///< @BETA_API The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. - ARGB8888S, ///< @BETA_API The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. + ABGR8888S, ///< The channels are joined in the order: alpha, blue, green, red. Colors are un-alpha-premultiplied. @since 0.12 + ARGB8888S, ///< The channels are joined in the order: alpha, red, green, blue. Colors are un-alpha-premultiplied. @since 0.12 }; /** @@ -1564,7 +1690,7 @@ public: * * @warning Please do not use it. This class is not fully supported yet. * - * @BETA_API + * @note Experimental API */ class TVG_API GlCanvas final : public Canvas { @@ -1576,7 +1702,7 @@ public: * * @warning Please do not use it, this API is not official one. It could be modified in the next version. * - * @BETA_API + * @note Experimental API */ Result target(uint32_t* buffer, uint32_t stride, uint32_t w, uint32_t h) noexcept; @@ -1585,7 +1711,7 @@ public: * * @return A new GlCanvas object. * - * @BETA_API + * @note Experimental API */ static std::unique_ptr<GlCanvas> gen() noexcept; @@ -1594,6 +1720,42 @@ public: /** + * @class WgCanvas + * + * @brief A class for the rendering graphic elements with a WebGPU raster engine. + * + * @warning Please do not use it. This class is not fully supported yet. + * + * @note Experimental API + */ +class TVG_API WgCanvas final : public Canvas +{ +public: + ~WgCanvas(); + + /** + * @brief Sets the target window for the rasterization. + * + * @warning Please do not use it, this API is not official one. It could be modified in the next version. + * + * @note Experimental API + */ + Result target(void* window, uint32_t w, uint32_t h) noexcept; + + /** + * @brief Creates a new WgCanvas object. + * + * @return A new WgCanvas object. + * + * @note Experimental API + */ + static std::unique_ptr<WgCanvas> gen() noexcept; + + _TVG_DECLARE_PRIVATE(WgCanvas); +}; + + +/** * @class Initializer * * @brief A class that enables initialization and termination of the TVG engines. @@ -1650,7 +1812,7 @@ public: * * This class supports the display and control of animation frames. * - * @BETA_API + * @note Experimental API */ class TVG_API Animation @@ -1669,7 +1831,7 @@ public: * * @see totalFrame() * - * @BETA_API + * @note Experimental API */ Result frame(float no) noexcept; @@ -1684,7 +1846,7 @@ public: * * @warning The picture instance is owned by Animation. It should not be deleted manually. * - * @BETA_API + * @note Experimental API */ Picture* picture() const noexcept; @@ -1698,7 +1860,7 @@ public: * @see Animation::frame(float no) * @see Animation::totalFrame() * - * @BETA_API + * @note Experimental API */ float curFrame() const noexcept; @@ -1710,7 +1872,7 @@ public: * @note Frame numbering starts from 0. * @note If the Picture is not properly configured, this function will return 0. * - * @BETA_API + * @note Experimental API */ float totalFrame() const noexcept; @@ -1721,7 +1883,7 @@ public: * * @note If the Picture is not properly configured, this function will return 0. * - * @BETA_API + * @% Experimental API */ float duration() const noexcept; @@ -1730,7 +1892,7 @@ public: * * @return A new Animation object. * - * @BETA_API + * @note Experimental API */ static std::unique_ptr<Animation> gen() noexcept; @@ -1761,6 +1923,15 @@ public: ~Saver(); /** + * @brief Sets the base background content for the saved image. + * + * @param[in] paint The paint to be drawn as the background image for the saving paint. + * + * @note Experimental API + */ + Result background(std::unique_ptr<Paint> paint) noexcept; + + /** * @brief Exports the given @p paint data to the given @p path * * If the saver module supports any compression mechanism, it will optimize the data size. @@ -1794,18 +1965,18 @@ public: * @param[in] quality The encoded quality level. @c 0 is the minimum, @c 100 is the maximum value(recommended). * @param[in] fps The desired frames per second (FPS). For example, to encode data at 60 FPS, pass 60. Pass 0 to keep the original frame data. * - * @return Result::Success if the export succeeds. - * @return Result::InsufficientCondition if there are ongoing resource-saving operations. - * @return Result::NonSupport if an attempt is made to save the file with an unknown extension or in an unsupported format. - * @return Result::MemoryCorruption in case of an internal error. - * @return Result::Unknown if attempting to save an empty paint. + * @retval Result::Success if the export succeeds. + * @retval Result::InsufficientCondition if there are ongoing resource-saving operations. + * @retval Result::NonSupport if an attempt is made to save the file with an unknown extension or in an unsupported format. + * @retval Result::MemoryCorruption in case of an internal error. + * @retval Result::Unknown if attempting to save an empty paint. * * @note A higher frames per second (FPS) would result in a larger file size. It is recommended to use the default value. * @note Saving can be asynchronous if the assigned thread number is greater than zero. To guarantee the saving is done, call sync() afterwards. * * @see Saver::sync() * - * @note: Experimental API + * @note Experimental API */ Result save(std::unique_ptr<Animation> animation, const std::string& path, uint32_t quality = 100, uint32_t fps = 0) noexcept; @@ -1882,7 +2053,7 @@ public: * @brief The cast() function is a utility function used to cast a 'Paint' to type 'T'. * @since 0.11 */ -template<typename T> +template<typename T = tvg::Paint> std::unique_ptr<T> cast(Paint* paint) { return std::unique_ptr<T>(static_cast<T*>(paint)); @@ -1893,7 +2064,7 @@ std::unique_ptr<T> cast(Paint* paint) * @brief The cast() function is a utility function used to cast a 'Fill' to type 'T'. * @since 0.11 */ -template<typename T> +template<typename T = tvg::Fill> std::unique_ptr<T> cast(Fill* fill) { return std::unique_ptr<T>(static_cast<T*>(fill)); diff --git a/thirdparty/thorvg/src/common/tvgArray.h b/thirdparty/thorvg/src/common/tvgArray.h index 1afc647b68..acb3a41b97 100644 --- a/thirdparty/thorvg/src/common/tvgArray.h +++ b/thirdparty/thorvg/src/common/tvgArray.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -39,6 +39,11 @@ struct Array Array(){} + Array(int32_t size) + { + reserve(size); + } + Array(const Array& rhs) { reset(); diff --git a/thirdparty/thorvg/src/common/tvgBezier.cpp b/thirdparty/thorvg/src/common/tvgBezier.cpp index f9daf07b84..5fb501721e 100644 --- a/thirdparty/thorvg/src/common/tvgBezier.cpp +++ b/thirdparty/thorvg/src/common/tvgBezier.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgBezier.h b/thirdparty/thorvg/src/common/tvgBezier.h index 539a63bdd3..cb2766c505 100644 --- a/thirdparty/thorvg/src/common/tvgBezier.h +++ b/thirdparty/thorvg/src/common/tvgBezier.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgCompressor.cpp b/thirdparty/thorvg/src/common/tvgCompressor.cpp index e38940f3d0..778fc4d0a2 100644 --- a/thirdparty/thorvg/src/common/tvgCompressor.cpp +++ b/thirdparty/thorvg/src/common/tvgCompressor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgCompressor.h b/thirdparty/thorvg/src/common/tvgCompressor.h index 05d23f4293..0756127ec6 100644 --- a/thirdparty/thorvg/src/common/tvgCompressor.h +++ b/thirdparty/thorvg/src/common/tvgCompressor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgInlist.h b/thirdparty/thorvg/src/common/tvgInlist.h new file mode 100644 index 0000000000..ff28cfd48e --- /dev/null +++ b/thirdparty/thorvg/src/common/tvgInlist.h @@ -0,0 +1,111 @@ +/* + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TVG_INLIST_H_ +#define _TVG_INLIST_H_ + +namespace tvg { + +//NOTE: declare this in your list item +#define INLIST_ITEM(T) \ + T* prev; \ + T* next + +template<typename T> +struct Inlist +{ + T* head = nullptr; + T* tail = nullptr; + + void free() + { + while (head) { + auto t = head; + head = t->next; + delete(t); + } + head = tail = nullptr; + } + + void back(T* element) + { + if (tail) { + tail->next = element; + element->prev = tail; + element->next = nullptr; + tail = element; + } else { + head = tail = element; + element->prev = nullptr; + element->next = nullptr; + } + } + + void front(T* element) + { + if (head) { + head->prev = element; + element->prev = nullptr; + element->next = head; + head = element; + } else { + head = tail = element; + element->prev = nullptr; + element->next = nullptr; + } + } + + T* back() + { + if (!tail) return nullptr; + auto t = tail; + tail = t->prev; + if (!tail) head = nullptr; + return t; + } + + T* front() + { + if (!head) return nullptr; + auto t = head; + head = t->next; + if (!head) tail = nullptr; + return t; + } + + void remove(T* element) + { + if (element->prev) element->prev->next = element->next; + if (element->next) element->next->prev = element->prev; + if (element == head) head = element->next; + if (element == tail) tail = element->prev; + } + + bool empty() + { + return head ? false : true; + } +}; + +} + +#endif // _TVG_INLIST_H_ diff --git a/thirdparty/thorvg/src/common/tvgList.h b/thirdparty/thorvg/src/common/tvgList.h deleted file mode 100644 index 01e87a840f..0000000000 --- a/thirdparty/thorvg/src/common/tvgList.h +++ /dev/null @@ -1,90 +0,0 @@ -/* - * Copyright (c) 2023 the ThorVG project. All rights reserved. - - * Permission is hereby granted, free of charge, to any person obtaining a copy - * of this software and associated documentation files (the "Software"), to deal - * in the Software without restriction, including without limitation the rights - * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell - * copies of the Software, and to permit persons to whom the Software is - * furnished to do so, subject to the following conditions: - - * The above copyright notice and this permission notice shall be included in all - * copies or substantial portions of the Software. - - * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE - * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER - * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, - * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE - * SOFTWARE. - */ - -#ifndef _TVG_LIST_H_ -#define _TVG_LIST_H_ - -namespace tvg { - -template<typename T> -struct LinkedList -{ - T *head = nullptr; - T *tail = nullptr; - - LinkedList() = default; - LinkedList(T *head, T *tail) : head(head), tail(tail) - { - } - - template<T *T::*Prev, T *T::*Next> - static void insert(T *t, T *prev, T *next, T **head, T **tail) - { - t->*Prev = prev; - t->*Next = next; - - if (prev) { - prev->*Next = t; - } else if (head) { - *head = t; - } - - if (next) { - next->*Prev = t; - } else if (tail) { - *tail = t; - } - } - - template<T *T::*Prev, T *T::*Next> - static void remove(T *t, T **head, T **tail) - { - if (t->*Prev) { - t->*Prev->*Next = t->*Next; - } else if (head) { - *head = t->*Next; - } - - if (t->*Next) { - t->*Next->*Prev = t->*Prev; - } else if (tail) { - *tail = t->*Prev; - } - - t->*Prev = t->*Next = nullptr; - } - - template <T* T::*Next> - static bool contains(T *t, T **head, T **tail) { - for (T *it = *head; it; it = it->*Next) { - if (it == t) { - return true; - } - } - - return false; - } -}; - -} - -#endif // _TVG_LIST_H_ diff --git a/thirdparty/thorvg/src/common/tvgMath.cpp b/thirdparty/thorvg/src/common/tvgMath.cpp index a9463c8077..42bc2cf4aa 100644 --- a/thirdparty/thorvg/src/common/tvgMath.cpp +++ b/thirdparty/thorvg/src/common/tvgMath.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgMath.h b/thirdparty/thorvg/src/common/tvgMath.h index 004fff1e7b..50c3458efc 100644 --- a/thirdparty/thorvg/src/common/tvgMath.h +++ b/thirdparty/thorvg/src/common/tvgMath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -174,6 +174,18 @@ static inline Point operator*(const Point& lhs, float rhs) } +static inline Point operator*(const float& lhs, const Point& rhs) +{ + return {lhs * rhs.x, lhs * rhs.y}; +} + + +static inline Point operator/(const Point& lhs, const float rhs) +{ + return {lhs.x / rhs, lhs.y / rhs}; +} + + template <typename T> static inline T mathLerp(const T &start, const T &end, float t) { diff --git a/thirdparty/thorvg/src/common/tvgStr.cpp b/thirdparty/thorvg/src/common/tvgStr.cpp index eeed4fc404..311b986511 100644 --- a/thirdparty/thorvg/src/common/tvgStr.cpp +++ b/thirdparty/thorvg/src/common/tvgStr.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/common/tvgStr.h b/thirdparty/thorvg/src/common/tvgStr.h index 448cc69336..9e5f9ba9b5 100644 --- a/thirdparty/thorvg/src/common/tvgStr.h +++ b/thirdparty/thorvg/src/common/tvgStr.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp index 30a66f9cad..10277f846d 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp +++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,19 +20,25 @@ * SOFTWARE. */ -#include "tvgLoader.h" #include "tvgPngLoader.h" /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +void PngLoader::clear() +{ + png_image_free(image); + free(image); + image = nullptr; +} + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ -PngLoader::PngLoader() +PngLoader::PngLoader() : ImageLoader(FileType::Png) { image = static_cast<png_imagep>(calloc(1, sizeof(png_image))); image->version = PNG_IMAGE_VERSION; @@ -41,13 +47,11 @@ PngLoader::PngLoader() PngLoader::~PngLoader() { - if (content) { - free((void*)content); - content = nullptr; - } - free(image); + clear(); + free((void*)surface.buf32); } + bool PngLoader::open(const string& path) { image->opaque = NULL; @@ -56,11 +60,11 @@ bool PngLoader::open(const string& path) w = (float)image->width; h = (float)image->height; - cs = ColorSpace::ARGB8888; return true; } + bool PngLoader::open(const char* data, uint32_t size, bool copy) { image->opaque = NULL; @@ -69,7 +73,6 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) w = (float)image->width; h = (float)image->height; - cs = ColorSpace::ARGB8888; return true; } @@ -77,6 +80,10 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) bool PngLoader::read() { + if (!LoadModule::read()) return true; + + if (w == 0 || h == 0) return false; + png_bytep buffer; image->format = PNG_FORMAT_BGRA; buffer = static_cast<png_bytep>(malloc(PNG_IMAGE_SIZE((*image)))); @@ -89,32 +96,17 @@ bool PngLoader::read() free(buffer); return false; } - content = reinterpret_cast<uint32_t*>(buffer); - return true; -} + //setup the surface + surface.buf32 = reinterpret_cast<uint32_t*>(buffer); + surface.stride = (uint32_t)w; + surface.w = (uint32_t)w; + surface.h = (uint32_t)h; + surface.channelSize = sizeof(uint32_t); + surface.cs = ColorSpace::ARGB8888; + surface.premultiplied = false; -bool PngLoader::close() -{ - png_image_free(image); - return true; -} + clear(); -unique_ptr<Surface> PngLoader::bitmap() -{ - if (!content) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf32 = content; - surface->stride = (uint32_t)w; - surface->w = (uint32_t)w; - surface->h = (uint32_t)h; - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->owner = true; - surface->premultiplied = false; - - return unique_ptr<Surface>(surface); + return true; } - diff --git a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h index 5354e1bdd6..c45544eb07 100644 --- a/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h +++ b/thirdparty/thorvg/src/loaders/external_png/tvgPngLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,24 +24,22 @@ #define _TVG_PNG_LOADER_H_ #include <png.h> +#include "tvgLoader.h" -class PngLoader : public LoadModule +class PngLoader : public ImageLoader { public: PngLoader(); ~PngLoader(); - using LoadModule::open; bool open(const string& path) override; bool open(const char* data, uint32_t size, bool copy) override; bool read() override; - bool close() override; - - unique_ptr<Surface> bitmap() override; private: + void clear(); + png_imagep image = nullptr; - uint32_t* content = nullptr; }; #endif //_TVG_PNG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp index 6edda86cc1..86a46245d9 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -21,7 +21,6 @@ */ #include <memory.h> -#include "tvgLoader.h" #include "tvgJpgLoader.h" /************************************************************************/ @@ -38,30 +37,45 @@ void JpgLoader::clear() } +void JpgLoader::run(unsigned tid) +{ + surface.buf8 = jpgdDecompress(decoder); + surface.stride = static_cast<uint32_t>(w); + surface.w = static_cast<uint32_t>(w); + surface.h = static_cast<uint32_t>(h); + surface.cs = ColorSpace::ARGB8888; + surface.channelSize = sizeof(uint32_t); + surface.premultiplied = true; + + clear(); +} + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ +JpgLoader::JpgLoader() : ImageLoader(FileType::Jpg) +{ + +} + JpgLoader::~JpgLoader() { - jpgdDelete(decoder); - if (freeData) free(data); - free(image); + clear(); + free(surface.buf8); } bool JpgLoader::open(const string& path) { - clear(); - int width, height; decoder = jpgdHeader(path.c_str(), &width, &height); if (!decoder) return false; w = static_cast<float>(width); h = static_cast<float>(height); - cs = ColorSpace::ARGB8888; return true; } @@ -69,8 +83,6 @@ bool JpgLoader::open(const string& path) bool JpgLoader::open(const char* data, uint32_t size, bool copy) { - clear(); - if (copy) { this->data = (char *) malloc(size); if (!this->data) return false; @@ -87,7 +99,6 @@ bool JpgLoader::open(const char* data, uint32_t size, bool copy) w = static_cast<float>(width); h = static_cast<float>(height); - cs = ColorSpace::ARGB8888; return true; } @@ -96,7 +107,9 @@ bool JpgLoader::open(const char* data, uint32_t size, bool copy) bool JpgLoader::read() { - if (!decoder || w <= 0 || h <= 0) return false; + if (!LoadModule::read()) return true; + + if (!decoder || w == 0 || h == 0) return false; TaskScheduler::request(this); @@ -106,38 +119,14 @@ bool JpgLoader::read() bool JpgLoader::close() { + if (!LoadModule::close()) return false; this->done(); - clear(); return true; } -unique_ptr<Surface> JpgLoader::bitmap() +Surface* JpgLoader::bitmap() { this->done(); - - if (!image) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf8 = image; - surface->stride = static_cast<uint32_t>(w); - surface->w = static_cast<uint32_t>(w); - surface->h = static_cast<uint32_t>(h); - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->premultiplied = true; - surface->owner = true; - - return unique_ptr<Surface>(surface); -} - - -void JpgLoader::run(unsigned tid) -{ - if (image) { - free(image); - image = nullptr; - } - image = jpgdDecompress(decoder); + return ImageLoader::bitmap(); } diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h index 6d2febe94f..05cbb54c85 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,30 +23,30 @@ #ifndef _TVG_JPG_LOADER_H_ #define _TVG_JPG_LOADER_H_ +#include "tvgLoader.h" #include "tvgTaskScheduler.h" #include "tvgJpgd.h" -class JpgLoader : public LoadModule, public Task +class JpgLoader : public ImageLoader, public Task { private: jpeg_decoder* decoder = nullptr; char* data = nullptr; - unsigned char *image = nullptr; bool freeData = false; void clear(); + void run(unsigned tid) override; public: + JpgLoader(); ~JpgLoader(); - using LoadModule::open; bool open(const string& path) override; bool open(const char* data, uint32_t size, bool copy) override; bool read() override; bool close() override; - unique_ptr<Surface> bitmap() override; - void run(unsigned tid) override; + Surface* bitmap() override; }; #endif //_TVG_JPG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp index 3cd852a4bb..6f2bd916d5 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h index 030fdc2946..e1fe35f488 100644 --- a/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h +++ b/thirdparty/thorvg/src/loaders/jpg/tvgJpgd.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp index fc4cce4061..fb7527f045 100644 --- a/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp +++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h index 7254a55477..cd3efacf98 100644 --- a/thirdparty/thorvg/src/loaders/png/tvgLodePng.h +++ b/thirdparty/thorvg/src/loaders/png/tvgLodePng.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp index 32ff57c5c3..111180b657 100644 --- a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp +++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,14 +29,22 @@ /* Internal Class Implementation */ /************************************************************************/ -void PngLoader::clear() + +void PngLoader::run(unsigned tid) { - lodepng_state_cleanup(&state); + auto width = static_cast<unsigned>(w); + auto height = static_cast<unsigned>(h); - if (freeData) free(data); - data = nullptr; - size = 0; - freeData = false; + if (lodepng_decode(&surface.buf8, &width, &height, &state, data, size)) { + TVGERR("PNG", "Failed to decode image"); + } + + //setup the surface + surface.stride = width; + surface.w = width; + surface.h = height; + surface.channelSize = sizeof(uint32_t); + surface.premultiplied = false; } @@ -44,7 +52,7 @@ void PngLoader::clear() /* External Class Implementation */ /************************************************************************/ -PngLoader::PngLoader() +PngLoader::PngLoader() : ImageLoader(FileType::Png) { lodepng_state_init(&state); } @@ -53,14 +61,13 @@ PngLoader::PngLoader() PngLoader::~PngLoader() { if (freeData) free(data); - free(image); + free(surface.buf8); + lodepng_state_cleanup(&state); } bool PngLoader::open(const string& path) { - clear(); - auto pngFile = fopen(path.c_str(), "rb"); if (!pngFile) return false; @@ -76,26 +83,23 @@ bool PngLoader::open(const string& path) freeData = true; - if (fread(data, size, 1, pngFile) < 1) goto failure; + if (fread(data, size, 1, pngFile) < 1) goto finalize; lodepng_state_init(&state); unsigned int width, height; - if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto failure; + if (lodepng_inspect(&width, &height, &state, data, size) > 0) goto finalize; w = static_cast<float>(width); h = static_cast<float>(height); - if (state.info_png.color.colortype == LCT_RGBA) cs = ColorSpace::ABGR8888; - else cs = ColorSpace::ARGB8888; + if (state.info_png.color.colortype == LCT_RGBA) surface.cs = ColorSpace::ABGR8888; + else surface.cs = ColorSpace::ARGB8888; ret = true; goto finalize; -failure: - clear(); - finalize: fclose(pngFile); return ret; @@ -104,10 +108,6 @@ finalize: bool PngLoader::open(const char* data, uint32_t size, bool copy) { - clear(); - - lodepng_state_init(&state); - unsigned int width, height; if (lodepng_inspect(&width, &height, &state, (unsigned char*)(data), size) > 0) return false; @@ -125,7 +125,7 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) h = static_cast<float>(height); this->size = size; - cs = ColorSpace::ABGR8888; + surface.cs = ColorSpace::ABGR8888; return true; } @@ -133,53 +133,18 @@ bool PngLoader::open(const char* data, uint32_t size, bool copy) bool PngLoader::read() { - if (!data || w <= 0 || h <= 0) return false; - - TaskScheduler::request(this); + if (!data || w == 0 || h == 0) return false; - return true; -} + if (!LoadModule::read()) return true; + TaskScheduler::request(this); -bool PngLoader::close() -{ - this->done(); - clear(); return true; } -unique_ptr<Surface> PngLoader::bitmap() +Surface* PngLoader::bitmap() { this->done(); - - if (!image) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf8 = image; - surface->stride = static_cast<uint32_t>(w); - surface->w = static_cast<uint32_t>(w); - surface->h = static_cast<uint32_t>(h); - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->premultiplied = false; - surface->owner = true; - - return unique_ptr<Surface>(surface); -} - - -void PngLoader::run(unsigned tid) -{ - if (image) { - free(image); - image = nullptr; - } - auto width = static_cast<unsigned>(w); - auto height = static_cast<unsigned>(h); - - if (lodepng_decode(&image, &width, &height, &state, data, size)) { - TVGERR("PNG", "Failed to decode image"); - } + return ImageLoader::bitmap(); } diff --git a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h index 579d197ad6..06fbbf7349 100644 --- a/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h +++ b/thirdparty/thorvg/src/loaders/png/tvgPngLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -27,29 +27,25 @@ #include "tvgTaskScheduler.h" -class PngLoader : public LoadModule, public Task +class PngLoader : public ImageLoader, public Task { private: LodePNGState state; unsigned char* data = nullptr; - unsigned char *image = nullptr; unsigned long size = 0; bool freeData = false; - void clear(); + void run(unsigned tid) override; public: PngLoader(); ~PngLoader(); - using LoadModule::open; bool open(const string& path) override; bool open(const char* data, uint32_t size, bool copy) override; bool read() override; - bool close() override; - unique_ptr<Surface> bitmap() override; - void run(unsigned tid) override; + Surface* bitmap() override; }; #endif //_TVG_PNG_LOADER_H_ diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp index 5f5e72b0dd..b797f98218 100644 --- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp +++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,17 +34,21 @@ /* External Class Implementation */ /************************************************************************/ +RawLoader::RawLoader() : ImageLoader(FileType::Raw) +{ +} + + RawLoader::~RawLoader() { - if (copy && content) { - free((void*)content); - content = nullptr; - } + if (copy) free(surface.buf32); } bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { + if (!LoadModule::read()) return true; + if (!data || w == 0 || h == 0) return false; this->w = (float)w; @@ -52,13 +56,19 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) this->copy = copy; if (copy) { - content = (uint32_t*)malloc(sizeof(uint32_t) * w * h); - if (!content) return false; - memcpy((void*)content, data, sizeof(uint32_t) * w * h); + surface.buf32 = (uint32_t*)malloc(sizeof(uint32_t) * w * h); + if (!surface.buf32) return false; + memcpy((void*)surface.buf32, data, sizeof(uint32_t) * w * h); } - else content = const_cast<uint32_t*>(data); + else surface.buf32 = const_cast<uint32_t*>(data); - cs = ColorSpace::ARGB8888; + //setup the surface + surface.stride = w; + surface.w = w; + surface.h = h; + surface.cs = ColorSpace::ARGB8888; + surface.channelSize = sizeof(uint32_t); + surface.premultiplied = true; return true; } @@ -66,30 +76,7 @@ bool RawLoader::open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) bool RawLoader::read() { - return true; -} - + LoadModule::read(); -bool RawLoader::close() -{ return true; } - - -unique_ptr<Surface> RawLoader::bitmap() -{ - if (!content) return nullptr; - - //TODO: It's better to keep this surface instance in the loader side - auto surface = new Surface; - surface->buf32 = content; - surface->stride = static_cast<uint32_t>(w); - surface->w = static_cast<uint32_t>(w); - surface->h = static_cast<uint32_t>(h); - surface->cs = cs; - surface->channelSize = sizeof(uint32_t); - surface->premultiplied = true; - surface->owner = true; - - return unique_ptr<Surface>(surface); -} diff --git a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h index 69f9bdc47a..970cdd9c83 100644 --- a/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h +++ b/thirdparty/thorvg/src/loaders/raw/tvgRawLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,20 +23,17 @@ #ifndef _TVG_RAW_LOADER_H_ #define _TVG_RAW_LOADER_H_ -class RawLoader : public LoadModule +class RawLoader : public ImageLoader { public: - uint32_t* content = nullptr; bool copy = false; + RawLoader(); ~RawLoader(); using LoadModule::open; - bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) override; + bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy); bool read() override; - bool close() override; - - unique_ptr<Surface> bitmap() override; }; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp index c3c477a263..2826dc9134 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2022 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h index 228c5996da..7d8dcfa94a 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgCssStyle.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2022 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2022 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp index be99ec085c..6e52476625 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -3528,6 +3528,10 @@ void SvgLoader::clear(bool all) loaderData.images.reset(); if (copy) free((char*)content); + + delete(root); + root = nullptr; + size = 0; content = nullptr; copy = false; @@ -3538,14 +3542,15 @@ void SvgLoader::clear(bool all) /* External Class Implementation */ /************************************************************************/ -SvgLoader::SvgLoader() +SvgLoader::SvgLoader() : ImageLoader(FileType::Svg) { } SvgLoader::~SvgLoader() { - close(); + this->done(); + clear(); } @@ -3554,7 +3559,7 @@ void SvgLoader::run(unsigned tid) //According to the SVG standard the value of the width/height of the viewbox set to 0 disables rendering if ((viewFlag & SvgViewFlag::Viewbox) && (fabsf(vw) <= FLT_EPSILON || fabsf(vh) <= FLT_EPSILON)) { TVGLOG("SVG", "The <viewBox> width and/or height set to 0 - rendering disabled."); - root = Scene::gen(); + root = Scene::gen().release(); return; } @@ -3727,6 +3732,8 @@ bool SvgLoader::read() { if (!content || size == 0) return false; + if (!LoadModule::read()) return true; + //the loading has been already completed in header() if (root) return true; @@ -3738,16 +3745,17 @@ bool SvgLoader::read() bool SvgLoader::close() { + if (!LoadModule::close()) return false; this->done(); - clear(); - return true; } -unique_ptr<Paint> SvgLoader::paint() +Paint* SvgLoader::paint() { this->done(); - return std::move(root); + auto ret = root; + root = nullptr; + return ret; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h index 4bac52e0b9..e0cba8b11e 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,7 +26,7 @@ #include "tvgTaskScheduler.h" #include "tvgSvgLoaderCommon.h" -class SvgLoader : public LoadModule, public Task +class SvgLoader : public ImageLoader, public Task { public: string filePath; @@ -35,21 +35,20 @@ public: uint32_t size = 0; SvgLoaderData loaderData; - unique_ptr<Scene> root; + Scene* root = nullptr; bool copy = false; SvgLoader(); ~SvgLoader(); - using LoadModule::open; bool open(const string& path) override; bool open(const char* data, uint32_t size, bool copy) override; bool resize(Paint* paint, float w, float h) override; bool read() override; bool close() override; - unique_ptr<Paint> paint() override; + Paint* paint() override; private: SvgViewFlag viewFlag = SvgViewFlag::None; diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h index b4ee3e8e0d..d6febd6825 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgLoaderCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp index 79a9c0771d..f618a3c827 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -53,6 +53,7 @@ #include <cstring> #include <math.h> #include <ctype.h> +#include "tvgShape.h" #include "tvgSvgLoaderCommon.h" #include "tvgSvgPath.h" #include "tvgStr.h" @@ -534,7 +535,7 @@ static char* _nextCommand(char* path, char* cmd, float* arr, int* count) /************************************************************************/ -bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts) +bool svgPathToShape(const char* svgPath, Shape* shape) { float numberArray[7]; int numberCount = 0; @@ -545,11 +546,16 @@ bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point bool isQuadratic = false; char* path = (char*)svgPath; + auto& pts = P(shape)->rs.path.pts; + auto& cmds = P(shape)->rs.path.cmds; + auto lastCmds = cmds.count; + while ((path[0] != '\0')) { path = _nextCommand(path, &cmd, numberArray, &numberCount); if (!path) break; if (!_processCommand(&cmds, &pts, cmd, numberArray, numberCount, &cur, &curCtl, &startPoint, &isQuadratic)) break; } + if (cmds.count > lastCmds && cmds[lastCmds] != PathCommand::MoveTo) return false; return true; } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h index 4199088dc1..b8641dd165 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgPath.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,6 @@ #include <tvgCommon.h> -bool svgPathToTvgPath(const char* svgPath, Array<PathCommand>& cmds, Array<Point>& pts); +bool svgPathToShape(const char* svgPath, Shape* shape); #endif //_TVG_SVG_PATH_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp index 1791df57f0..674c30f5bd 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -396,10 +396,9 @@ static bool _recognizeShape(SvgNode* node, Shape* shape) switch (node->type) { case SvgNodeType::Path: { if (node->node.path.path) { - Array<PathCommand> cmds; - Array<Point> pts; - if (svgPathToTvgPath(node->node.path.path, cmds, pts)) { - shape->appendPath(cmds.data, cmds.count, pts.data, pts.count); + if (!svgPathToShape(node->node.path.path, shape)) { + TVGERR("SVG", "Invalid path information."); + return false; } } break; @@ -845,7 +844,7 @@ static void _updateInvalidViewSize(const Scene* scene, Box& vBox, float& w, floa /* External Class Implementation */ /************************************************************************/ -unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) +Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag) { //TODO: aspect ratio is valid only if viewBox was set @@ -863,8 +862,7 @@ unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, fl } auto viewBoxClip = Shape::gen(); - viewBoxClip->appendRect(0, 0, w, h, 0, 0); - viewBoxClip->fill(0, 0, 0); + viewBoxClip->appendRect(0, 0, w, h); auto compositeLayer = Scene::gen(); compositeLayer->composite(std::move(viewBoxClip), CompositeMethod::ClipPath); @@ -880,5 +878,5 @@ unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, fl loaderData.doc->node.doc.w = w; loaderData.doc->node.doc.h = h; - return root; + return root.release(); } diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h index f6a60f850d..0663a37f05 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgSceneBuilder.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,6 @@ #include "tvgCommon.h" -unique_ptr<Scene> svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); +Scene* svgSceneBuild(SvgLoaderData& loaderData, Box vBox, float w, float h, AspectRatioAlign align, AspectRatioMeetOrSlice meetOrSlice, const string& svgPath, SvgViewFlag viewFlag); #endif //_TVG_SVG_SCENE_BUILDER_H_ diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp index 7940188ade..32affd04c6 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h index ee0e3f8861..220a4b3e55 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgSvgUtil.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp index dbc3b17b70..aec30d2384 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h index 7333bb09fb..30ca23b565 100644 --- a/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h +++ b/thirdparty/thorvg/src/loaders/svg/tvgXmlParser.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h index 8fe7b77edd..3d73075a4a 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -261,13 +261,26 @@ struct SwSurface : Surface SwAlpha alphas[4]; //Alpha:2, InvAlpha:3, Luma:4, InvLuma:5 SwBlender blender = nullptr; //blender (optional) SwCompositor* compositor = nullptr; //compositor (optional) - BlendMethod blendMethod; //blending method (uint8_t) + BlendMethod blendMethod; //blending method (uint8_t) SwAlpha alpha(CompositeMethod method) { auto idx = (int)(method) - 2; //0: None, 1: ClipPath return alphas[idx > 3 ? 0 : idx]; //CompositeMethod has only four Matting methods. } + + SwSurface() + { + } + + SwSurface(const SwSurface* rhs) : Surface(rhs) + { + join = rhs->join; + memcpy(alphas, rhs->alphas, sizeof(alphas)); + blender = rhs->blender; + compositor = rhs->compositor; + blendMethod = rhs->blendMethod; + } }; struct SwCompositor : Compositor diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp index cede9e6eb7..8956cd9f24 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwFill.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -167,7 +167,6 @@ bool _prepareLinear(SwFill* fill, const LinearGradient* linear, const Matrix* tr fill->linear.dy = dx * invTransform.e12 + fill->linear.dy * invTransform.e22; fill->linear.len = fill->linear.dx * fill->linear.dx + fill->linear.dy * fill->linear.dy; - if (fill->linear.len < FLT_EPSILON) return true; } return true; diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp index b1624037bc..c162945501 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwImage.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp index d58dd9e3c5..42e405195e 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMath.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp index 54ae594bff..68eb7a5a6f 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwMemPool.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp index 96a0ed35ad..9a6dc45950 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRaster.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -1714,7 +1714,7 @@ static bool _rasterSolidGradientRle(SwSurface* surface, const SwRleData* rle, co static bool _rasterLinearGradientRle(SwSurface* surface, const SwRleData* rle, const SwFill* fill) { - if (!rle || fill->linear.len < FLT_EPSILON) return false; + if (!rle) return false; if (_compositing(surface)) { if (_matting(surface)) return _rasterGradientMattedRle<FillLinear>(surface, rle, fill); @@ -1855,7 +1855,9 @@ void rasterUnpremultiply(Surface* surface) void rasterPremultiply(Surface* surface) { - if (surface->channelSize != sizeof(uint32_t)) return; + unique_lock<mutex> lock{surface->mtx}; + if (surface->premultiplied || (surface->channelSize != sizeof(uint32_t))) return; + surface->premultiplied = true; TVGLOG("SW_ENGINE", "Premultiply [Size: %d x %d]", surface->w, surface->h); @@ -1869,7 +1871,6 @@ void rasterPremultiply(Surface* surface) *dst = (c & 0xff000000) + ((((c >> 8) & 0xff) * a) & 0xff00) + ((((c & 0x00ff00ff) * a) >> 8) & 0x00ff00ff); } } - surface->premultiplied = true; } @@ -1935,6 +1936,9 @@ bool rasterImage(SwSurface* surface, SwImage* image, const RenderMesh* mesh, con bool rasterConvertCS(Surface* surface, ColorSpace to) { + unique_lock<mutex> lock{surface->mtx}; + if (surface->cs == to) return true; + //TOOD: Support SIMD accelerations auto from = surface->cs; @@ -1946,6 +1950,5 @@ bool rasterConvertCS(Surface* surface, ColorSpace to) surface->cs = to; return cRasterARGBtoABGR(surface); } - return false; } diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h index 090fa29a7a..177c7b847f 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterAvx.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h index da3c7077e8..6d0bd9383d 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterC.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h index ba77ed53cf..cfbeb0caca 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterNeon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h index 698ab37da2..04e382b842 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRasterTexmap.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp index 18815e69a9..c9c543fbb5 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -45,8 +45,11 @@ struct SwTask : Task bool pushed = false; //Pushed into task list? bool disposed = false; //Disposed task? - RenderRegion bounds() const + RenderRegion bounds() { + //Can we skip the synchronization? + done(); + RenderRegion region; //Range over? @@ -278,10 +281,8 @@ struct SwImageTask : SwTask auto clipRegion = bbox; //Convert colorspace if it's not aligned. - if (source->owner) { - if (source->cs != surface->cs) rasterConvertCS(source, surface->cs); - if (!source->premultiplied) rasterPremultiply(source); - } + rasterConvertCS(source, surface->cs); + rasterPremultiply(source); image.data = source->data; image.w = source->w; @@ -433,7 +434,6 @@ bool SwRenderer::target(pixel_t* data, uint32_t stride, uint32_t w, uint32_t h, surface->cs = cs; surface->channelSize = CHANNEL_SIZE(cs); surface->premultiplied = true; - surface->owner = true; vport.x = vport.y = 0; vport.w = surface->w; @@ -635,11 +635,8 @@ Compositor* SwRenderer::target(const RenderRegion& region, ColorSpace cs) //New Composition if (!cmp) { - cmp = new SwSurface; - //Inherits attributes from main surface - *cmp = *surface; - + cmp = new SwSurface(surface); cmp->compositor = new SwCompositor; //TODO: We can optimize compositor surface size from (surface->stride x surface->h) to Parameter(w x h) diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h index 4393740bd9..83d942388f 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRenderer.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp index a4a7fabdee..33c94e1063 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwRle.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp index ae2ddd2af7..e001ced2a8 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwShape.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp index 75c5fa5667..8f44cf3616 100644 --- a/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp +++ b/thirdparty/thorvg/src/renderer/sw_engine/tvgSwStroke.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgAccessor.cpp b/thirdparty/thorvg/src/renderer/tvgAccessor.cpp index 0c636979b5..903437f29d 100644 --- a/thirdparty/thorvg/src/renderer/tvgAccessor.cpp +++ b/thirdparty/thorvg/src/renderer/tvgAccessor.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp b/thirdparty/thorvg/src/renderer/tvgAnimation.cpp index b4ea534b91..995eca7f41 100644 --- a/thirdparty/thorvg/src/renderer/tvgAnimation.cpp +++ b/thirdparty/thorvg/src/renderer/tvgAnimation.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -64,7 +64,7 @@ Animation::Animation() : pImpl(new Impl) Result Animation::frame(float no) noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return Result::InsufficientCondition; if (!loader->animatable()) return Result::NonSupport; @@ -82,7 +82,7 @@ Picture* Animation::picture() const noexcept float Animation::curFrame() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; @@ -93,7 +93,7 @@ float Animation::curFrame() const noexcept float Animation::totalFrame() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; @@ -104,7 +104,7 @@ float Animation::totalFrame() const noexcept float Animation::duration() const noexcept { - auto loader = pImpl->picture->pImpl->loader.get(); + auto loader = pImpl->picture->pImpl->loader; if (!loader) return 0; if (!loader->animatable()) return 0; diff --git a/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h b/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h index b1468148d6..29f84eb82a 100644 --- a/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h +++ b/thirdparty/thorvg/src/renderer/tvgBinaryDesc.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,7 +36,7 @@ using TvgBinFlag = TvgBinByte; #define TVG_HEADER_SIZE 33 //TVG_HEADER_SIGNATURE_LENGTH + TVG_HEADER_VERSION_LENGTH + 2*SIZE(float) + TVG_HEADER_RESERVED_LENGTH + TVG_HEADER_COMPRESS_SIZE #define TVG_HEADER_SIGNATURE "ThorVG" #define TVG_HEADER_SIGNATURE_LENGTH 6 -#define TVG_HEADER_VERSION "001100" //Major 00, Minor 11, Micro 00 +#define TVG_HEADER_VERSION "001200" //Major 00, Minor 12, Micro 00 #define TVG_HEADER_VERSION_LENGTH 6 #define TVG_HEADER_RESERVED_LENGTH 1 //Storing flags for extensions #define TVG_HEADER_COMPRESS_SIZE 12 //TVG_HEADER_UNCOMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE + TVG_HEADER_COMPRESSED_SIZE_BITS diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgCanvas.cpp index 25741f611d..11d6b778f7 100644 --- a/thirdparty/thorvg/src/renderer/tvgCanvas.cpp +++ b/thirdparty/thorvg/src/renderer/tvgCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgCanvas.h b/thirdparty/thorvg/src/renderer/tvgCanvas.h index f893f9f7c0..25181de47e 100644 --- a/thirdparty/thorvg/src/renderer/tvgCanvas.h +++ b/thirdparty/thorvg/src/renderer/tvgCanvas.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,14 +20,11 @@ * SOFTWARE. */ -#ifndef _TVG_CANVAS_IMPL_H_ -#define _TVG_CANVAS_IMPL_H_ +#ifndef _TVG_CANVAS_H_ +#define _TVG_CANVAS_H_ #include "tvgPaint.h" -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ struct Canvas::Impl { @@ -143,4 +140,4 @@ struct Canvas::Impl } }; -#endif /* _TVG_CANVAS_IMPL_H_ */ +#endif /* _TVG_CANVAS_H_ */ diff --git a/thirdparty/thorvg/src/renderer/tvgCommon.h b/thirdparty/thorvg/src/renderer/tvgCommon.h index 2b67681a40..deb419bc65 100644 --- a/thirdparty/thorvg/src/renderer/tvgCommon.h +++ b/thirdparty/thorvg/src/renderer/tvgCommon.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -61,8 +61,9 @@ using namespace tvg; #define TVG_CLASS_ID_PICTURE 3 #define TVG_CLASS_ID_LINEAR 4 #define TVG_CLASS_ID_RADIAL 5 +#define TVG_CLASS_ID_TEXT 6 -enum class FileType { Tvg = 0, Svg, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown }; +enum class FileType { Tvg = 0, Svg, Ttf, Lottie, Raw, Png, Jpg, Webp, Gif, Unknown }; using Size = Point; diff --git a/thirdparty/thorvg/src/renderer/tvgFill.cpp b/thirdparty/thorvg/src/renderer/tvgFill.cpp index 9215882c8d..ea1010051e 100644 --- a/thirdparty/thorvg/src/renderer/tvgFill.cpp +++ b/thirdparty/thorvg/src/renderer/tvgFill.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgFill.h b/thirdparty/thorvg/src/renderer/tvgFill.h index ff3dd48c49..47f0c051c0 100644 --- a/thirdparty/thorvg/src/renderer/tvgFill.h +++ b/thirdparty/thorvg/src/renderer/tvgFill.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgFrameModule.h b/thirdparty/thorvg/src/renderer/tvgFrameModule.h index 332cca3090..df97ccb894 100644 --- a/thirdparty/thorvg/src/renderer/tvgFrameModule.h +++ b/thirdparty/thorvg/src/renderer/tvgFrameModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -28,9 +28,10 @@ namespace tvg { -class FrameModule: public LoadModule +class FrameModule: public ImageLoader { public: + FrameModule(FileType type) : ImageLoader(type) {} virtual ~FrameModule() {} virtual bool frame(float no) = 0; //set the current frame number diff --git a/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp index aa7f8dbe2a..f6f0d354d1 100644 --- a/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp +++ b/thirdparty/thorvg/src/renderer/tvgGlCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgInitializer.cpp b/thirdparty/thorvg/src/renderer/tvgInitializer.cpp index b7326a9fbc..76d89b40ed 100644 --- a/thirdparty/thorvg/src/renderer/tvgInitializer.cpp +++ b/thirdparty/thorvg/src/renderer/tvgInitializer.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -36,6 +36,10 @@ #include "tvgGlRenderer.h" #endif +#ifdef THORVG_WG_RASTER_SUPPORT + #include "tvgWgRenderer.h" +#endif + /************************************************************************/ /* Internal Class Implementation */ @@ -91,19 +95,27 @@ static bool _buildVersionInfo() Result Initializer::init(CanvasEngine engine, uint32_t threads) noexcept { auto nonSupport = true; + if (static_cast<int>(engine) == 0) return Result::InvalidArguments; if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; #endif - } else if (engine & CanvasEngine::Gl) { + } + + if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::init(threads)) return Result::FailedAllocation; nonSupport = false; #endif - } else { - return Result::InvalidArguments; + } + + if (engine & CanvasEngine::Wg) { + #ifdef THORVG_WG_RASTER_SUPPORT + if (!WgRenderer::init(threads)) return Result::FailedAllocation; + nonSupport = false; + #endif } if (nonSupport) return Result::NonSupport; @@ -125,19 +137,27 @@ Result Initializer::term(CanvasEngine engine) noexcept if (_initCnt == 0) return Result::InsufficientCondition; auto nonSupport = true; + if (static_cast<int>(engine) == 0) return Result::InvalidArguments; if (engine & CanvasEngine::Sw) { #ifdef THORVG_SW_RASTER_SUPPORT if (!SwRenderer::term()) return Result::InsufficientCondition; nonSupport = false; #endif - } else if (engine & CanvasEngine::Gl) { + } + + if (engine & CanvasEngine::Gl) { #ifdef THORVG_GL_RASTER_SUPPORT if (!GlRenderer::term()) return Result::InsufficientCondition; nonSupport = false; #endif - } else { - return Result::InvalidArguments; + } + + if (engine & CanvasEngine::Wg) { + #ifdef THORVG_WG_RASTER_SUPPORT + if (!WgRenderer::term()) return Result::InsufficientCondition; + nonSupport = false; + #endif } if (nonSupport) return Result::NonSupport; @@ -156,3 +176,4 @@ uint16_t THORVG_VERSION_NUMBER() { return _version; } + diff --git a/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h b/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h index 2347613ed8..46e900a582 100644 --- a/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h +++ b/thirdparty/thorvg/src/renderer/tvgIteratorAccessor.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgLoadModule.h b/thirdparty/thorvg/src/renderer/tvgLoadModule.h index 29ceba3fcc..0ea766395e 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoadModule.h +++ b/thirdparty/thorvg/src/renderer/tvgLoadModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -24,35 +24,73 @@ #define _TVG_LOAD_MODULE_H_ #include "tvgRender.h" +#include "tvgInlist.h" -namespace tvg -{ -class LoadModule +struct LoadModule { -public: - float w = 0, h = 0; //default image size - ColorSpace cs = ColorSpace::Unsupported; //must be clarified at open() + INLIST_ITEM(LoadModule); + + //Use either hashkey(data) or hashpath(path) + uint64_t hashkey; + char* hashpath = nullptr; - virtual ~LoadModule() {} + FileType type; //current loader file type + uint16_t sharing = 0; //reference count + bool readied = false; //read done already. + + LoadModule(FileType type) : type(type) {} + virtual ~LoadModule() + { + free(hashpath); + } virtual bool open(const string& path) { return false; } virtual bool open(const char* data, uint32_t size, bool copy) { return false; } - virtual bool open(const uint32_t* data, uint32_t w, uint32_t h, bool copy) { return false; } - - //Override this if the vector-format has own resizing policy. virtual bool resize(Paint* paint, float w, float h) { return false; } - - virtual bool animatable() { return false; } //true if this loader supports animation. virtual void sync() {}; //finish immediately if any async update jobs. - virtual bool read() = 0; - virtual bool close() = 0; + virtual bool read() + { + if (readied) return false; + readied = true; + return true; + } - virtual unique_ptr<Surface> bitmap() { return nullptr; } - virtual unique_ptr<Paint> paint() { return nullptr; } + virtual bool close() + { + if (sharing == 0) return true; + --sharing; + return false; + } }; -} + +struct ImageLoader : LoadModule +{ + float w = 0, h = 0; //default image size + Surface surface; + + ImageLoader(FileType type) : LoadModule(type) {} + + virtual bool animatable() { return false; } //true if this loader supports animation. + virtual Paint* paint() { return nullptr; } + + virtual Surface* bitmap() + { + if (surface.data) return &surface; + return nullptr; + } +}; + + +struct FontLoader : LoadModule +{ + float scale = 1.0f; + + FontLoader(FileType type) : LoadModule(type) {} + + virtual bool request(Shape* shape, char* text, bool italic = false) = 0; +}; #endif //_TVG_LOAD_MODULE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.cpp b/thirdparty/thorvg/src/renderer/tvgLoader.cpp index 65330e9e77..628b0fa17f 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoader.cpp +++ b/thirdparty/thorvg/src/renderer/tvgLoader.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,6 +20,9 @@ * SOFTWARE. */ +#include <string.h> + +#include "tvgInlist.h" #include "tvgLoader.h" #ifdef THORVG_SVG_LOADER_SUPPORT @@ -42,16 +45,30 @@ #include "tvgWebpLoader.h" #endif +#ifdef THORVG_TTF_LOADER_SUPPORT + #include "tvgTtfLoader.h" +#endif + #ifdef THORVG_LOTTIE_LOADER_SUPPORT #include "tvgLottieLoader.h" #endif #include "tvgRawLoader.h" + +uint64_t HASH_KEY(const char* data, uint64_t size) +{ + return (((uint64_t) data) << 32) | size; +} + /************************************************************************/ /* Internal Class Implementation */ /************************************************************************/ +static mutex mtx; +static Inlist<LoadModule> _activeLoaders; + + static LoadModule* _find(FileType type) { switch(type) { @@ -67,6 +84,12 @@ static LoadModule* _find(FileType type) #endif break; } + case FileType::Ttf: { +#ifdef THORVG_TTF_LOADER_SUPPORT + return new TtfLoader; +#endif + break; + } case FileType::Lottie: { #ifdef THORVG_LOTTIE_LOADER_SUPPORT return new LottieLoader; @@ -111,6 +134,10 @@ static LoadModule* _find(FileType type) format = "SVG"; break; } + case FileType::Ttf: { + format = "TTF"; + break; + } case FileType::Lottie: { format = "lottie(json)"; break; @@ -152,29 +179,71 @@ static LoadModule* _findByPath(const string& path) if (!ext.compare("png")) return _find(FileType::Png); if (!ext.compare("jpg")) return _find(FileType::Jpg); if (!ext.compare("webp")) return _find(FileType::Webp); + if (!ext.compare("ttf") || !ext.compare("ttc")) return _find(FileType::Ttf); + if (!ext.compare("otf") || !ext.compare("otc")) return _find(FileType::Ttf); return nullptr; } -static LoadModule* _findByType(const string& mimeType) +static FileType _convert(const string& mimeType) { - if (mimeType.empty()) return nullptr; - auto type = FileType::Unknown; if (mimeType == "tvg") type = FileType::Tvg; else if (mimeType == "svg" || mimeType == "svg+xml") type = FileType::Svg; + else if (mimeType == "ttf" || mimeType == "otf") type = FileType::Ttf; else if (mimeType == "lottie") type = FileType::Lottie; else if (mimeType == "raw") type = FileType::Raw; else if (mimeType == "png") type = FileType::Png; else if (mimeType == "jpg" || mimeType == "jpeg") type = FileType::Jpg; else if (mimeType == "webp") type = FileType::Webp; - else { - TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str()); - return nullptr; + else TVGLOG("RENDERER", "Given mimetype is unknown = \"%s\".", mimeType.c_str()); + + return type; +} + + +static LoadModule* _findByType(const string& mimeType) +{ + return _find(_convert(mimeType)); +} + + +static LoadModule* _findFromCache(const string& path) +{ + unique_lock<mutex> lock{mtx}; + + auto loader = _activeLoaders.head; + + while (loader) { + if (loader->hashpath && !strcmp(loader->hashpath, path.c_str())) { + ++loader->sharing; + return loader; + } + loader = loader->next; } + return nullptr; +} + + +static LoadModule* _findFromCache(const char* data, uint32_t size, const string& mimeType) +{ + auto type = _convert(mimeType); + if (type == FileType::Unknown) return nullptr; - return _find(type); + unique_lock<mutex> lock{mtx}; + auto loader = _activeLoaders.head; + + auto key = HASH_KEY(data, size); + + while (loader) { + if (loader->type == type && loader->hashkey == key) { + ++loader->sharing; + return loader; + } + loader = loader->next; + } + return nullptr; } @@ -185,40 +254,95 @@ static LoadModule* _findByType(const string& mimeType) bool LoaderMgr::init() { - //TODO: - return true; } bool LoaderMgr::term() { - //TODO: + auto loader = _activeLoaders.head; + + //clean up the remained font loaders which is globally used. + while (loader && loader->type == FileType::Ttf) { + auto ret = loader->close(); + auto tmp = loader; + loader = loader->next; + _activeLoaders.remove(tmp); + if (ret) delete(loader); + } + return true; +} + +bool LoaderMgr::retrieve(LoadModule* loader) +{ + if (!loader) return false; + if (loader->close()) { + { + unique_lock<mutex> lock{mtx}; + _activeLoaders.remove(loader); + } + delete(loader); + } return true; } -shared_ptr<LoadModule> LoaderMgr::loader(const string& path, bool* invalid) +LoadModule* LoaderMgr::loader(const string& path, bool* invalid) { *invalid = false; + if (auto loader = _findFromCache(path)) return loader; + if (auto loader = _findByPath(path)) { - if (loader->open(path)) return shared_ptr<LoadModule>(loader); - else delete(loader); + if (loader->open(path)) { + loader->hashpath = strdup(path.c_str()); + { + unique_lock<mutex> lock{mtx}; + _activeLoaders.back(loader); + } + return loader; + } + delete(loader); *invalid = true; } return nullptr; } -shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy) +bool LoaderMgr::retrieve(const string& path) +{ + return retrieve(_findFromCache(path)); +} + + +LoadModule* LoaderMgr::loader(const char* key) +{ + auto loader = _activeLoaders.head; + + while (loader) { + if (loader->hashpath && strstr(loader->hashpath, key)) { + ++loader->sharing; + return loader; + } + loader = loader->next; + } + return nullptr; +} + + +LoadModule* LoaderMgr::loader(const char* data, uint32_t size, const string& mimeType, bool copy) { + if (auto loader = _findFromCache(data, size, mimeType)) return loader; + //Try with the given MimeType if (!mimeType.empty()) { if (auto loader = _findByType(mimeType)) { if (loader->open(data, size, copy)) { - return shared_ptr<LoadModule>(loader); + loader->hashkey = HASH_KEY(data, size); + unique_lock<mutex> lock{mtx}; + _activeLoaders.back(loader); + return loader; } else { TVGLOG("LOADER", "Given mimetype \"%s\" seems incorrect or not supported.", mimeType.c_str()); delete(loader); @@ -229,8 +353,15 @@ shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const for (int i = 0; i < static_cast<int>(FileType::Unknown); i++) { auto loader = _find(static_cast<FileType>(i)); if (loader) { - if (loader->open(data, size, copy)) return shared_ptr<LoadModule>(loader); - else delete(loader); + if (loader->open(data, size, copy)) { + loader->hashkey = HASH_KEY(data, size); + { + unique_lock<mutex> lock{mtx}; + _activeLoaders.back(loader); + } + return loader; + } + delete(loader); } } } @@ -238,12 +369,21 @@ shared_ptr<LoadModule> LoaderMgr::loader(const char* data, uint32_t size, const } -shared_ptr<LoadModule> LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy) +LoadModule* LoaderMgr::loader(const uint32_t *data, uint32_t w, uint32_t h, bool copy) { + //TODO: should we check premultiplied?? + if (auto loader = _findFromCache((const char*)(data), w * h, "raw")) return loader; + //function is dedicated for raw images only auto loader = new RawLoader; - if (loader->open(data, w, h, copy)) return shared_ptr<LoadModule>(loader); - else delete(loader); - + if (loader->open(data, w, h, copy)) { + loader->hashkey = HASH_KEY((const char*)data, w * h); + { + unique_lock<mutex> lock{mtx}; + _activeLoaders.back(loader); + } + return loader; + } + delete(loader); return nullptr; } diff --git a/thirdparty/thorvg/src/renderer/tvgLoader.h b/thirdparty/thorvg/src/renderer/tvgLoader.h index 17ff9e2637..b15032df27 100644 --- a/thirdparty/thorvg/src/renderer/tvgLoader.h +++ b/thirdparty/thorvg/src/renderer/tvgLoader.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -29,9 +29,12 @@ struct LoaderMgr { static bool init(); static bool term(); - static shared_ptr<LoadModule> loader(const string& path, bool* invalid); - static shared_ptr<LoadModule> loader(const char* data, uint32_t size, const string& mimeType, bool copy); - static shared_ptr<LoadModule> loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy); + static LoadModule* loader(const string& path, bool* invalid); + static LoadModule* loader(const char* data, uint32_t size, const string& mimeType, bool copy); + static LoadModule* loader(const uint32_t* data, uint32_t w, uint32_t h, bool copy); + static LoadModule* loader(const char* key); + static bool retrieve(const string& path); + static bool retrieve(LoadModule* loader); }; #endif //_TVG_LOADER_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.cpp b/thirdparty/thorvg/src/renderer/tvgPaint.cpp index 008e6589b5..563db3b44a 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPaint.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -25,6 +25,7 @@ #include "tvgShape.h" #include "tvgPicture.h" #include "tvgScene.h" +#include "tvgText.h" /************************************************************************/ /* Internal Class Implementation */ @@ -35,6 +36,7 @@ case TVG_CLASS_ID_SHAPE: ret = P((Shape*)paint)->METHOD; break; \ case TVG_CLASS_ID_SCENE: ret = P((Scene*)paint)->METHOD; break; \ case TVG_CLASS_ID_PICTURE: ret = P((Picture*)paint)->METHOD; break; \ + case TVG_CLASS_ID_TEXT: ret = P((Text*)paint)->METHOD; break; \ default: ret = {}; \ } diff --git a/thirdparty/thorvg/src/renderer/tvgPaint.h b/thirdparty/thorvg/src/renderer/tvgPaint.h index f4721f8e15..4ec9fa7f7e 100644 --- a/thirdparty/thorvg/src/renderer/tvgPaint.h +++ b/thirdparty/thorvg/src/renderer/tvgPaint.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.cpp b/thirdparty/thorvg/src/renderer/tvgPicture.cpp index e382795698..7f120593f4 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.cpp +++ b/thirdparty/thorvg/src/renderer/tvgPicture.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -30,9 +30,8 @@ RenderUpdateFlag Picture::Impl::load() { if (loader) { if (!paint) { - if (auto p = loader->paint()) { - paint = p.release(); - loader->close(); + paint = loader->paint(); + if (paint) { if (w != loader->w || h != loader->h) { if (!resizing) { w = loader->w; @@ -41,13 +40,12 @@ RenderUpdateFlag Picture::Impl::load() loader->resize(paint, w, h); resizing = false; } - if (paint) return RenderUpdateFlag::None; + return RenderUpdateFlag::None; } } else loader->sync(); if (!surface) { - if ((surface = loader->bitmap().release())) { - loader->close(); + if ((surface = loader->bitmap())) { return RenderUpdateFlag::Image; } } @@ -55,6 +53,93 @@ RenderUpdateFlag Picture::Impl::load() return RenderUpdateFlag::None; } + +bool Picture::Impl::needComposition(uint8_t opacity) +{ + //In this case, paint(scene) would try composition itself. + if (opacity < 255) return false; + + //Composition test + const Paint* target; + auto method = picture->composite(&target); + if (!target || method == tvg::CompositeMethod::ClipPath) return false; + if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; + + return true; +} + + +bool Picture::Impl::render(RenderMethod &renderer) +{ + bool ret = false; + if (surface) return renderer.renderImage(rd); + else if (paint) { + Compositor* cmp = nullptr; + if (needComp) { + cmp = renderer.target(bounds(renderer), renderer.colorSpace()); + renderer.beginComposite(cmp, CompositeMethod::None, 255); + } + ret = paint->pImpl->render(renderer); + if (cmp) renderer.endComposite(cmp); + } + return ret; +} + + +bool Picture::Impl::size(float w, float h) +{ + this->w = w; + this->h = h; + resizing = true; + return true; +} + + +RenderRegion Picture::Impl::bounds(RenderMethod& renderer) +{ + if (rd) return renderer.region(rd); + if (paint) return paint->pImpl->bounds(renderer); + return {0, 0, 0, 0}; +} + + +RenderTransform Picture::Impl::resizeTransform(const RenderTransform* pTransform) +{ + //Overriding Transformation by the desired image size + auto sx = w / loader->w; + auto sy = h / loader->h; + auto scale = sx < sy ? sx : sy; + + RenderTransform tmp; + tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1}; + + if (!pTransform) return tmp; + else return RenderTransform(pTransform, &tmp); +} + + +Result Picture::Impl::load(ImageLoader* loader) +{ + //Same resource has been loaded. + if (this->loader == loader) { + this->loader->sharing--; //make it sure the reference counting. + return Result::Success; + } else if (this->loader) { + LoaderMgr::retrieve(this->loader); + } + + this->loader = loader; + + if (!loader->read()) return Result::Unknown; + + this->w = loader->w; + this->h = loader->h; + + return Result::Success; +} + + + /************************************************************************/ /* External Class Implementation */ /************************************************************************/ diff --git a/thirdparty/thorvg/src/renderer/tvgPicture.h b/thirdparty/thorvg/src/renderer/tvgPicture.h index 447f56ecb7..26a171ba66 100644 --- a/thirdparty/thorvg/src/renderer/tvgPicture.h +++ b/thirdparty/thorvg/src/renderer/tvgPicture.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,16 +20,13 @@ * SOFTWARE. */ -#ifndef _TVG_PICTURE_IMPL_H_ -#define _TVG_PICTURE_IMPL_H_ +#ifndef _TVG_PICTURE_H_ +#define _TVG_PICTURE_H_ #include <string> #include "tvgPaint.h" #include "tvgLoader.h" -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ struct PictureIterator : Iterator { @@ -60,7 +57,7 @@ struct PictureIterator : Iterator struct Picture::Impl { - shared_ptr<LoadModule> loader = nullptr; + ImageLoader* loader = nullptr; Paint* paint = nullptr; //vector picture uses Surface* surface = nullptr; //bitmap picture uses @@ -71,14 +68,21 @@ struct Picture::Impl bool resizing = false; bool needComp = false; //need composition + RenderTransform resizeTransform(const RenderTransform* pTransform); + bool needComposition(uint8_t opacity); + bool render(RenderMethod &renderer); + bool size(float w, float h); + RenderRegion bounds(RenderMethod& renderer); + Result load(ImageLoader* ploader); + Impl(Picture* p) : picture(p) { } ~Impl() { + LoaderMgr::retrieve(loader); delete(paint); - delete(surface); } bool dispose(RenderMethod& renderer) @@ -89,34 +93,6 @@ struct Picture::Impl return true; } - RenderTransform resizeTransform(const RenderTransform* pTransform) - { - //Overriding Transformation by the desired image size - auto sx = w / loader->w; - auto sy = h / loader->h; - auto scale = sx < sy ? sx : sy; - - RenderTransform tmp; - tmp.m = {scale, 0, 0, 0, scale, 0, 0, 0, 1}; - - if (!pTransform) return tmp; - else return RenderTransform(pTransform, &tmp); - } - - bool needComposition(uint8_t opacity) - { - //In this case, paint(scene) would try composition itself. - if (opacity < 255) return false; - - //Composition test - const Paint* target; - auto method = picture->composite(&target); - if (!target || method == tvg::CompositeMethod::ClipPath) return false; - if (target->pImpl->opacity == 255 || target->pImpl->opacity == 0) return false; - - return true; - } - RenderData update(RenderMethod &renderer, const RenderTransform* pTransform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) { auto flag = load(); @@ -135,30 +111,6 @@ struct Picture::Impl return rd; } - bool render(RenderMethod &renderer) - { - bool ret = false; - if (surface) return renderer.renderImage(rd); - else if (paint) { - Compositor* cmp = nullptr; - if (needComp) { - cmp = renderer.target(bounds(renderer), renderer.colorSpace()); - renderer.beginComposite(cmp, CompositeMethod::None, 255); - } - ret = paint->pImpl->render(renderer); - if (cmp) renderer.endComposite(cmp); - } - return ret; - } - - bool size(float w, float h) - { - this->w = w; - this->h = h; - resizing = true; - return true; - } - bool bounds(float* x, float* y, float* w, float* h, bool stroking) { if (rm.triangleCnt > 0) { @@ -195,50 +147,35 @@ struct Picture::Impl return true; } - RenderRegion bounds(RenderMethod& renderer) - { - if (rd) return renderer.region(rd); - if (paint) return paint->pImpl->bounds(renderer); - return {0, 0, 0, 0}; - } - Result load(const string& path) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); + bool invalid; //Invalid Path - loader = LoaderMgr::loader(path, &invalid); + auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(path, &invalid)); if (!loader) { if (invalid) return Result::InvalidArguments; return Result::NonSupport; } - if (!loader->read()) return Result::Unknown; - w = loader->w; - h = loader->h; - return Result::Success; + return load(loader); } Result load(const char* data, uint32_t size, const string& mimeType, bool copy) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); - loader = LoaderMgr::loader(data, size, mimeType, copy); + auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, size, mimeType, copy)); if (!loader) return Result::NonSupport; - if (!loader->read()) return Result::Unknown; - w = loader->w; - h = loader->h; - return Result::Success; + return load(loader); } Result load(uint32_t* data, uint32_t w, uint32_t h, bool copy) { if (paint || surface) return Result::InsufficientCondition; - if (loader) loader->close(); - loader = LoaderMgr::loader(data, w, h, copy); + + auto loader = static_cast<ImageLoader*>(LoaderMgr::loader(data, w, h, copy)); if (!loader) return Result::FailedAllocation; - this->w = loader->w; - this->h = loader->h; - return Result::Success; + + return load(loader); } void mesh(const Polygon* triangles, const uint32_t triangleCnt) @@ -258,18 +195,17 @@ struct Picture::Impl { load(); - auto ret = Picture::gen(); + auto ret = Picture::gen().release(); + auto dup = ret->pImpl; - auto dup = ret.get()->pImpl; if (paint) dup->paint = paint->duplicate(); - dup->loader = loader; - if (surface) { - dup->surface = new Surface; - *dup->surface = *surface; - //TODO: A dupilcation is not a proxy... it needs copy of the pixel data? - dup->surface->owner = false; + if (loader) { + dup->loader = loader; + ++dup->loader->sharing; } + + dup->surface = surface; dup->w = w; dup->h = h; dup->resizing = resizing; @@ -280,7 +216,7 @@ struct Picture::Impl memcpy(dup->rm.triangles, rm.triangles, sizeof(Polygon) * rm.triangleCnt); } - return ret.release(); + return ret; } Iterator* iterator() @@ -308,4 +244,4 @@ struct Picture::Impl RenderUpdateFlag load(); }; -#endif //_TVG_PICTURE_IMPL_H_ +#endif //_TVG_PICTURE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgRender.cpp b/thirdparty/thorvg/src/renderer/tvgRender.cpp index 64d76d3562..bdfb9f322e 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.cpp +++ b/thirdparty/thorvg/src/renderer/tvgRender.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgRender.h b/thirdparty/thorvg/src/renderer/tvgRender.h index 6c0a7d5f13..1e70b53494 100644 --- a/thirdparty/thorvg/src/renderer/tvgRender.h +++ b/thirdparty/thorvg/src/renderer/tvgRender.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,7 @@ #ifndef _TVG_RENDER_H_ #define _TVG_RENDER_H_ +#include <mutex> #include "tvgCommon.h" #include "tvgArray.h" @@ -49,17 +50,33 @@ enum ColorSpace struct Surface { union { - pixel_t* data; //system based data pointer - uint32_t* buf32; //for explicit 32bits channels - uint8_t* buf8; //for explicit 8bits grayscale + pixel_t* data = nullptr; //system based data pointer + uint32_t* buf32; //for explicit 32bits channels + uint8_t* buf8; //for explicit 8bits grayscale }; - uint32_t stride; - uint32_t w, h; - ColorSpace cs; - uint8_t channelSize; + mutex mtx; //used for thread safety + uint32_t stride = 0; + uint32_t w = 0, h = 0; + ColorSpace cs = ColorSpace::Unsupported; + uint8_t channelSize = 0; + bool premultiplied = 0; //Alpha-premultiplied + + Surface() + { + } + + Surface(const Surface* rhs) + { + data = rhs->data; + stride = rhs->stride; + w = rhs->w; + h = rhs->h; + cs = rhs->cs; + channelSize = rhs->channelSize; + premultiplied = rhs->premultiplied; + } + - bool premultiplied; //Alpha-premultiplied - bool owner; //Only owner could modify the buffer }; struct Compositor diff --git a/thirdparty/thorvg/src/renderer/tvgSaveModule.h b/thirdparty/thorvg/src/renderer/tvgSaveModule.h index 09e7bde72b..fed05c52df 100644 --- a/thirdparty/thorvg/src/renderer/tvgSaveModule.h +++ b/thirdparty/thorvg/src/renderer/tvgSaveModule.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -34,7 +34,7 @@ public: virtual ~SaveModule() {} virtual bool save(Paint* paint, const string& path, bool compress) = 0; - virtual bool save(Animation* animation, const string& path, uint32_t quality, uint32_t fps) = 0; + virtual bool save(Animation* animation, Paint* bg, const string& path, uint32_t quality, uint32_t fps) = 0; virtual bool close() = 0; }; diff --git a/thirdparty/thorvg/src/renderer/tvgSaver.cpp b/thirdparty/thorvg/src/renderer/tvgSaver.cpp index 038d1ad097..11eb24d437 100644 --- a/thirdparty/thorvg/src/renderer/tvgSaver.cpp +++ b/thirdparty/thorvg/src/renderer/tvgSaver.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2021 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2021 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -37,9 +37,12 @@ struct Saver::Impl { SaveModule* saveModule = nullptr; + Paint* bg = nullptr; + ~Impl() { delete(saveModule); + delete(bg); } }; @@ -139,7 +142,16 @@ Result Saver::save(std::unique_ptr<Paint> paint, const string& path, bool compre } -Result Saver::save(std::unique_ptr<Animation> animation, const string& path, uint32_t quality, uint32_t fps) noexcept +Result Saver::background(unique_ptr<Paint> paint) noexcept +{ + delete(pImpl->bg); + pImpl->bg = paint.release(); + + return Result::Success; +} + + +Result Saver::save(unique_ptr<Animation> animation, const string& path, uint32_t quality, uint32_t fps) noexcept { auto a = animation.release(); if (!a) return Result::MemoryCorruption; @@ -156,7 +168,7 @@ Result Saver::save(std::unique_ptr<Animation> animation, const string& path, uin } if (auto saveModule = _find(path)) { - if (saveModule->save(a, path, quality, fps)) { + if (saveModule->save(a, pImpl->bg, path, quality, fps)) { pImpl->saveModule = saveModule; return Result::Success; } else { diff --git a/thirdparty/thorvg/src/renderer/tvgScene.cpp b/thirdparty/thorvg/src/renderer/tvgScene.cpp index cd73913b30..f5809cf93b 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.cpp +++ b/thirdparty/thorvg/src/renderer/tvgScene.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgScene.h b/thirdparty/thorvg/src/renderer/tvgScene.h index b558e95a2f..1a5600c231 100644 --- a/thirdparty/thorvg/src/renderer/tvgScene.h +++ b/thirdparty/thorvg/src/renderer/tvgScene.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,15 +20,12 @@ * SOFTWARE. */ -#ifndef _TVG_SCENE_IMPL_H_ -#define _TVG_SCENE_IMPL_H_ +#ifndef _TVG_SCENE_H_ +#define _TVG_SCENE_H_ #include <float.h> #include "tvgPaint.h" -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ struct SceneIterator : Iterator { @@ -126,8 +123,7 @@ struct Scene::Impl this->renderer = &renderer; if (clipper) { - Array<RenderData> rds; - rds.reserve(paints.size()); + Array<RenderData> rds(paints.size()); for (auto paint : paints) { rds.push(paint->pImpl->update(renderer, transform, clips, opacity, flag, true)); } @@ -216,9 +212,8 @@ struct Scene::Impl Paint* duplicate() { - auto ret = Scene::gen(); - - auto dup = ret.get()->pImpl; + auto ret = Scene::gen().release(); + auto dup = ret->pImpl; for (auto paint : paints) { auto cdup = paint->duplicate(); @@ -226,7 +221,7 @@ struct Scene::Impl dup->paints.push_back(cdup); } - return ret.release(); + return ret; } void clear(bool free) @@ -247,4 +242,4 @@ struct Scene::Impl } }; -#endif //_TVG_SCENE_IMPL_H_ +#endif //_TVG_SCENE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgShape.cpp b/thirdparty/thorvg/src/renderer/tvgShape.cpp index 9a64c7df9f..23b8006dd9 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.cpp +++ b/thirdparty/thorvg/src/renderer/tvgShape.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgShape.h b/thirdparty/thorvg/src/renderer/tvgShape.h index 46b2d7d0db..a7f1226690 100644 --- a/thirdparty/thorvg/src/renderer/tvgShape.h +++ b/thirdparty/thorvg/src/renderer/tvgShape.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,16 +20,13 @@ * SOFTWARE. */ -#ifndef _TVG_SHAPE_IMPL_H_ -#define _TVG_SHAPE_IMPL_H_ +#ifndef _TVG_SHAPE_H_ +#define _TVG_SHAPE_H_ #include <memory.h> #include "tvgMath.h" #include "tvgPaint.h" -/************************************************************************/ -/* Internal Class Implementation */ -/************************************************************************/ struct Shape::Impl { @@ -341,9 +338,9 @@ struct Shape::Impl Paint* duplicate() { - auto ret = Shape::gen(); + auto ret = Shape::gen().release(); + auto dup = ret->pImpl; - auto dup = ret.get()->pImpl; dup->rs.rule = rs.rule; //Color @@ -379,7 +376,7 @@ struct Shape::Impl dup->flag |= RenderUpdateFlag::Gradient; } - return ret.release(); + return ret; } Iterator* iterator() @@ -388,4 +385,4 @@ struct Shape::Impl } }; -#endif //_TVG_SHAPE_IMPL_H_ +#endif //_TVG_SHAPE_H_ diff --git a/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp index 95df6b486d..4404057049 100644 --- a/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp +++ b/thirdparty/thorvg/src/renderer/tvgSwCanvas.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal diff --git a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp index e3887c60fc..36d1ce1f8b 100644 --- a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp +++ b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.cpp @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -20,11 +20,11 @@ * SOFTWARE. */ -#include <deque> #include <thread> -#include <vector> #include <atomic> #include <condition_variable> +#include "tvgArray.h" +#include "tvgInlist.h" #include "tvgTaskScheduler.h" /************************************************************************/ @@ -34,7 +34,7 @@ namespace tvg { struct TaskQueue { - deque<Task*> taskDeque; + Inlist<Task> taskDeque; mutex mtx; condition_variable ready; bool done = false; @@ -44,8 +44,6 @@ struct TaskQueue { unique_lock<mutex> lock{mtx, try_to_lock}; if (!lock || taskDeque.empty()) return false; *task = taskDeque.front(); - taskDeque.pop_front(); - return true; } @@ -54,11 +52,9 @@ struct TaskQueue { { unique_lock<mutex> lock{mtx, try_to_lock}; if (!lock) return false; - taskDeque.push_back(task); + taskDeque.back(task); } - ready.notify_one(); - return true; } @@ -82,8 +78,6 @@ struct TaskQueue { if (taskDeque.empty()) return false; *task = taskDeque.front(); - taskDeque.pop_front(); - return true; } @@ -91,12 +85,10 @@ struct TaskQueue { { { unique_lock<mutex> lock{mtx}; - taskDeque.push_back(task); + taskDeque.back(task); } - ready.notify_one(); } - }; @@ -105,24 +97,36 @@ static thread_local bool _async = true; //toggle async tasking for each thread struct TaskSchedulerImpl { - uint32_t threadCnt; - vector<thread> threads; - vector<TaskQueue> taskQueues; + Array<thread*> threads; + Array<TaskQueue*> taskQueues; atomic<uint32_t> idx{0}; - TaskSchedulerImpl(unsigned threadCnt) : threadCnt(threadCnt), taskQueues(threadCnt) + TaskSchedulerImpl(unsigned threadCnt) { threads.reserve(threadCnt); + taskQueues.reserve(threadCnt); for (unsigned i = 0; i < threadCnt; ++i) { - threads.emplace_back([&, i] { run(i); }); + taskQueues.push(new TaskQueue); + threads.push(new thread); + } + for (unsigned i = 0; i < threadCnt; ++i) { + *threads.data[i] = thread([&, i] { run(i); }); } } ~TaskSchedulerImpl() { - for (auto& queue : taskQueues) queue.complete(); - for (auto& thread : threads) thread.join(); + for (auto tq = taskQueues.data; tq < taskQueues.end(); ++tq) { + (*tq)->complete(); + } + for (auto thread = threads.data; thread < threads.end(); ++thread) { + (*thread)->join(); + delete(*thread); + } + for (auto tq = taskQueues.data; tq < taskQueues.end(); ++tq) { + delete(*tq); + } } void run(unsigned i) @@ -132,14 +136,14 @@ struct TaskSchedulerImpl //Thread Loop while (true) { auto success = false; - for (unsigned x = 0; x < threadCnt * 2; ++x) { - if (taskQueues[(i + x) % threadCnt].tryPop(&task)) { + for (unsigned x = 0; x < threads.count * 2; ++x) { + if (taskQueues[(i + x) % threads.count]->tryPop(&task)) { success = true; break; } } - if (!success && !taskQueues[i].pop(&task)) break; + if (!success && !taskQueues[i]->pop(&task)) break; (*task)(i + 1); } } @@ -147,13 +151,13 @@ struct TaskSchedulerImpl void request(Task* task) { //Async - if (threadCnt > 0 && _async) { + if (threads.count > 0 && _async) { task->prepare(); auto i = idx++; - for (unsigned n = 0; n < threadCnt; ++n) { - if (taskQueues[(i + n) % threadCnt].tryPush(task)) return; + for (unsigned n = 0; n < threads.count; ++n) { + if (taskQueues[(i + n) % threads.count]->tryPush(task)) return; } - taskQueues[i % threadCnt].push(task); + taskQueues[i % threads.count]->push(task); //Sync } else { task->run(0); @@ -192,7 +196,7 @@ void TaskScheduler::request(Task* task) unsigned TaskScheduler::threads() { - if (inst) return inst->threadCnt; + if (inst) return inst->threads.count; return 0; } diff --git a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h index 2dad80f5d0..483e084880 100644 --- a/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h +++ b/thirdparty/thorvg/src/renderer/tvgTaskScheduler.h @@ -1,5 +1,5 @@ /* - * Copyright (c) 2020 - 2023 the ThorVG project. All rights reserved. + * Copyright (c) 2020 - 2024 the ThorVG project. All rights reserved. * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal @@ -26,6 +26,7 @@ #include <mutex> #include <condition_variable> #include "tvgCommon.h" +#include "tvgInlist.h" namespace tvg { @@ -50,6 +51,8 @@ private: bool pending = false; public: + INLIST_ITEM(Task); + virtual ~Task() = default; void done() diff --git a/thirdparty/thorvg/src/renderer/tvgText.cpp b/thirdparty/thorvg/src/renderer/tvgText.cpp new file mode 100644 index 0000000000..1fe244c11d --- /dev/null +++ b/thirdparty/thorvg/src/renderer/tvgText.cpp @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + + +#include "tvgText.h" + + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + + +Text::Text() : pImpl(new Impl) +{ + Paint::pImpl->id = TVG_CLASS_ID_TEXT; +} + + +Text::~Text() +{ + delete(pImpl); +} + + +Result Text::text(const char* text) noexcept +{ + return pImpl->text(text); +} + + +Result Text::font(const char* name, float size, const char* style) noexcept +{ + return pImpl->font(name, size, style); +} + + +Result Text::load(const std::string& path) noexcept +{ + bool invalid; //invalid path + if (!LoaderMgr::loader(path, &invalid)) { + if (invalid) return Result::InvalidArguments; + else return Result::NonSupport; + } + + return Result::Success; +} + + +Result Text::unload(const std::string& path) noexcept +{ + if (LoaderMgr::retrieve(path)) return Result::Success; + return Result::InsufficientCondition; +} + + +Result Text::fill(uint8_t r, uint8_t g, uint8_t b) noexcept +{ + if (!pImpl->paint) return Result::InsufficientCondition; + + return pImpl->fill(r, g, b); +} + + +Result Text::fill(unique_ptr<Fill> f) noexcept +{ + if (!pImpl->paint) return Result::InsufficientCondition; + + auto p = f.release(); + if (!p) return Result::MemoryCorruption; + + return pImpl->fill(p); +} + + +unique_ptr<Text> Text::gen() noexcept +{ + return unique_ptr<Text>(new Text); +} + + +uint32_t Text::identifier() noexcept +{ + return TVG_CLASS_ID_TEXT; +} diff --git a/thirdparty/thorvg/src/renderer/tvgText.h b/thirdparty/thorvg/src/renderer/tvgText.h new file mode 100644 index 0000000000..b9f7ef6079 --- /dev/null +++ b/thirdparty/thorvg/src/renderer/tvgText.h @@ -0,0 +1,191 @@ +/* + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#ifndef _TVG_TEXT_H +#define _TVG_TEXT_H + +#include <cstring> +#include "tvgShape.h" +#include "tvgFill.h" + +#ifdef THORVG_TTF_LOADER_SUPPORT + #include "tvgTtfLoader.h" +#else + #include "tvgLoader.h" +#endif + +struct Text::Impl +{ + RenderData rd = nullptr; + FontLoader* loader = nullptr; + Shape* paint = nullptr; + char* utf8 = nullptr; + float fontSize; + bool italic = false; + bool changed = false; + + ~Impl() + { + free(utf8); + LoaderMgr::retrieve(loader); + delete(paint); + } + + Result fill(uint8_t r, uint8_t g, uint8_t b) + { + return paint->fill(r, g, b); + } + + Result fill(Fill* f) + { + return paint->fill(cast<Fill>(f)); + } + + Result text(const char* utf8) + { + free(this->utf8); + if (utf8) this->utf8 = strdup(utf8); + else this->utf8 = nullptr; + changed = true; + + return Result::Success; + } + + Result font(const char* name, float size, const char* style) + { + auto loader = LoaderMgr::loader(name); + if (!loader) return Result::InsufficientCondition; + + //Same resource has been loaded. + if (this->loader == loader) { + this->loader->sharing--; //make it sure the reference counting. + return Result::Success; + } else if (this->loader) { + LoaderMgr::retrieve(this->loader); + } + this->loader = static_cast<FontLoader*>(loader); + + if (!paint) paint = Shape::gen().release(); + + fontSize = size; + if (style && strstr(style, "italic")) italic = true; + changed = true; + return Result::Success; + } + + RenderRegion bounds(RenderMethod& renderer) + { + return renderer.region(rd); + } + + bool render(RenderMethod& renderer) + { + if (paint) return PP(paint)->render(renderer); + return false; + } + + bool load() + { + if (!loader) return false; + + //reload + if (changed) { + loader->request(paint, utf8, italic); + loader->read(); + changed = false; + } + if (paint) { + loader->resize(paint, fontSize, fontSize); + return true; + } + return false; + } + + RenderData update(RenderMethod& renderer, const RenderTransform* transform, Array<RenderData>& clips, uint8_t opacity, RenderUpdateFlag pFlag, bool clipper) + { + if (!load()) return nullptr; + + //transform the gradient coordinates based on the final scaled font. + if (P(paint)->flag & RenderUpdateFlag::Gradient) { + auto fill = P(paint)->rs.fill; + auto scale = 1.0f / loader->scale; + if (fill->identifier() == TVG_CLASS_ID_LINEAR) { + P(static_cast<LinearGradient*>(fill))->x1 *= scale; + P(static_cast<LinearGradient*>(fill))->y1 *= scale; + P(static_cast<LinearGradient*>(fill))->x2 *= scale; + P(static_cast<LinearGradient*>(fill))->y2 *= scale; + } else { + P(static_cast<RadialGradient*>(fill))->cx *= scale; + P(static_cast<RadialGradient*>(fill))->cy *= scale; + P(static_cast<RadialGradient*>(fill))->r *= scale; + P(static_cast<RadialGradient*>(fill))->fx *= scale; + P(static_cast<RadialGradient*>(fill))->fy *= scale; + P(static_cast<RadialGradient*>(fill))->fr *= scale; + } + } + rd = PP(paint)->update(renderer, transform, clips, opacity, pFlag, clipper); + return rd; + } + + bool bounds(float* x, float* y, float* w, float* h, TVG_UNUSED bool stroking) + { + if (!load() || !paint) return false; + paint->bounds(x, y, w, h, true); + return true; + } + + bool dispose(RenderMethod& renderer) + { + renderer.dispose(rd); + this->rd = nullptr; + return true; + } + + Paint* duplicate() + { + load(); + + auto ret = Text::gen().release(); + auto dup = ret->pImpl; + if (paint) dup->paint = static_cast<Shape*>(paint->duplicate()); + + if (loader) { + dup->loader = loader; + ++dup->loader->sharing; + } + + dup->utf8 = strdup(utf8); + dup->italic = italic; + dup->fontSize = fontSize; + + return ret; + } + + Iterator* iterator() + { + return nullptr; + } +}; + + + +#endif //_TVG_TEXT_H diff --git a/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp b/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp new file mode 100644 index 0000000000..5205df1737 --- /dev/null +++ b/thirdparty/thorvg/src/renderer/tvgWgCanvas.cpp @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2023 - 2024 the ThorVG project. All rights reserved. + + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +#include "tvgCanvas.h" + +#ifdef THORVG_WG_RASTER_SUPPORT + #include "tvgWgRenderer.h" +#endif + +/************************************************************************/ +/* Internal Class Implementation */ +/************************************************************************/ + +struct WgCanvas::Impl +{ +}; + + +/************************************************************************/ +/* External Class Implementation */ +/************************************************************************/ + +#ifdef THORVG_WG_RASTER_SUPPORT +WgCanvas::WgCanvas() : Canvas(WgRenderer::gen()), pImpl(new Impl) +#else +WgCanvas::WgCanvas() : Canvas(nullptr), pImpl(nullptr) +#endif +{ +} + +WgCanvas::~WgCanvas() +{ + delete pImpl; +} + +Result WgCanvas::target(void* window, uint32_t w, uint32_t h) noexcept +{ +#ifdef THORVG_WG_RASTER_SUPPORT + if (!window) return Result::InvalidArguments; + if ((w == 0) || (h == 0)) return Result::InvalidArguments; + + //We know renderer type, avoid dynamic_cast for performance. + auto renderer = static_cast<WgRenderer*>(Canvas::pImpl->renderer); + if (!renderer) return Result::MemoryCorruption; + + if (!renderer->target(window, w, h)) return Result::Unknown; + + //Paints must be updated again with this new target. + Canvas::pImpl->needRefresh(); + + return Result::Success; +#endif + return Result::NonSupport; +} + +unique_ptr<WgCanvas> WgCanvas::gen() noexcept +{ +#ifdef THORVG_WG_RASTER_SUPPORT + return unique_ptr<WgCanvas>(new WgCanvas); +#endif + return nullptr; +} diff --git a/thirdparty/thorvg/update-thorvg.sh b/thirdparty/thorvg/update-thorvg.sh index d4323848da..a1732d5157 100755 --- a/thirdparty/thorvg/update-thorvg.sh +++ b/thirdparty/thorvg/update-thorvg.sh @@ -1,6 +1,6 @@ #!/bin/bash -e -VERSION=0.11.6 +VERSION=0.12.0 cd thirdparty/thorvg/ || true rm -rf AUTHORS LICENSE inc/ src/ *.zip *.tar.gz tmp/ @@ -52,6 +52,7 @@ cp -rv src/renderer ../src/ # Only sw_engine is enabled. rm -rfv ../src/renderer/gl_engine +rm -rfv ../src/renderer/wg_engine # Enabled embedded loaders: raw, JPEG, PNG. mkdir ../src/loaders |