diff options
118 files changed, 1975 insertions, 481 deletions
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml index ea97ef023d..495f643e56 100644 --- a/.github/workflows/android_builds.yml +++ b/.github/workflows/android_builds.yml @@ -20,11 +20,11 @@ jobs: steps: - uses: actions/checkout@v4 - - name: Set up Java 11 + - name: Set up Java 17 uses: actions/setup-java@v3 with: distribution: temurin - java-version: 11 + java-version: 17 - name: Setup Godot build cache uses: ./.github/actions/godot-cache diff --git a/core/config/project_settings.cpp b/core/config/project_settings.cpp index 3049994240..cbbfe3de75 100644 --- a/core/config/project_settings.cpp +++ b/core/config/project_settings.cpp @@ -1343,6 +1343,9 @@ ProjectSettings::ProjectSettings() { GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/2d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f); GLOBAL_DEF_RST(PropertyInfo(Variant::FLOAT, "audio/general/3d_panning_strength", PROPERTY_HINT_RANGE, "0,2,0.01"), 0.5f); + GLOBAL_DEF(PropertyInfo(Variant::INT, "audio/general/ios/session_category", PROPERTY_HINT_ENUM, "Ambient,Multi Route,Play and Record,Playback,Record,Solo Ambient"), 0); + GLOBAL_DEF("audio/general/ios/mix_with_others", false); + PackedStringArray extensions; extensions.push_back("gd"); if (Engine::get_singleton()->has_singleton("GodotSharp")) { diff --git a/core/core_constants.cpp b/core/core_constants.cpp index 33b3271495..2f70fdf219 100644 --- a/core/core_constants.cpp +++ b/core/core_constants.cpp @@ -794,6 +794,8 @@ void register_global_constants() { void unregister_global_constants() { _global_constants.clear(); + _global_constants_map.clear(); + _global_enums.clear(); } int CoreConstants::get_global_constant_count() { diff --git a/core/extension/gdextension.cpp b/core/extension/gdextension.cpp index 8fb1aab6dc..26512d0c56 100644 --- a/core/extension/gdextension.cpp +++ b/core/extension/gdextension.cpp @@ -664,7 +664,7 @@ void GDExtension::_get_library_path(GDExtensionClassLibraryPtr p_library, GDExte memnew_placement(r_path, String(self->library_path)); } -HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions; +HashMap<StringName, GDExtensionInterfaceFunctionPtr> GDExtension::gdextension_interface_functions; void GDExtension::register_interface_function(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."); @@ -836,6 +836,10 @@ void GDExtension::initialize_gdextensions() { register_interface_function("get_library_path", (GDExtensionInterfaceFunctionPtr)&GDExtension::_get_library_path); } +void GDExtension::finalize_gdextensions() { + gdextension_interface_functions.clear(); +} + Error GDExtensionResourceLoader::load_gdextension_resource(const String &p_path, Ref<GDExtension> &p_extension) { ERR_FAIL_COND_V_MSG(p_extension.is_valid() && p_extension->is_library_open(), ERR_ALREADY_IN_USE, "Cannot load GDExtension resource into already opened library."); diff --git a/core/extension/gdextension.h b/core/extension/gdextension.h index d71b1f9704..bab3bcd198 100644 --- a/core/extension/gdextension.h +++ b/core/extension/gdextension.h @@ -106,6 +106,8 @@ class GDExtension : public Resource { void clear_instance_bindings(); #endif + static HashMap<StringName, GDExtensionInterfaceFunctionPtr> gdextension_interface_functions; + protected: static void _bind_methods(); @@ -153,6 +155,7 @@ public: static void register_interface_function(StringName p_function_name, GDExtensionInterfaceFunctionPtr p_function_pointer); static GDExtensionInterfaceFunctionPtr get_interface_function(StringName p_function_name); static void initialize_gdextensions(); + static void finalize_gdextensions(); GDExtension(); ~GDExtension(); diff --git a/core/extension/gdextension_compat_hashes.cpp b/core/extension/gdextension_compat_hashes.cpp index 9c8d6b3e7f..2dac4a3a5d 100644 --- a/core/extension/gdextension_compat_hashes.cpp +++ b/core/extension/gdextension_compat_hashes.cpp @@ -840,4 +840,8 @@ void GDExtensionCompatHashes::initialize() { // clang-format on } +void GDExtensionCompatHashes::finalize() { + mappings.clear(); +} + #endif // DISABLE_DEPRECATED diff --git a/core/extension/gdextension_compat_hashes.h b/core/extension/gdextension_compat_hashes.h index 3a66ef0b97..29393dcb2d 100644 --- a/core/extension/gdextension_compat_hashes.h +++ b/core/extension/gdextension_compat_hashes.h @@ -48,6 +48,7 @@ class GDExtensionCompatHashes { public: static void initialize(); + static void finalize(); static bool lookup_current_hash(const StringName &p_class, const StringName &p_method, uint32_t p_legacy_hash, uint32_t *r_current_hash); static bool get_legacy_hashes(const StringName &p_class, const StringName &p_method, Array &r_hashes); }; diff --git a/core/extension/gdextension_interface.h b/core/extension/gdextension_interface.h index ae7c5e0d2a..240da6e073 100644 --- a/core/extension/gdextension_interface.h +++ b/core/extension/gdextension_interface.h @@ -590,7 +590,10 @@ typedef GDExtensionInterfaceFunctionPtr (*GDExtensionInterfaceGetProcAddress)(co * * For example: * - * GDExtensionInterfaceGetGodotVersion *get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version"); + * GDExtensionInterfaceGetGodotVersion get_godot_version = (GDExtensionInterfaceGetGodotVersion)p_get_proc_address("get_godot_version"); + * + * (Note that snippet may cause "cast between incompatible function types" on some compilers, you can + * silence this by adding an intermediary `void*` cast.) * * You can then call it like a normal function: * diff --git a/core/extension/gdextension_manager.cpp b/core/extension/gdextension_manager.cpp index 0dc84f685f..a4d032f22f 100644 --- a/core/extension/gdextension_manager.cpp +++ b/core/extension/gdextension_manager.cpp @@ -293,3 +293,9 @@ GDExtensionManager::GDExtensionManager() { GDExtensionCompatHashes::initialize(); #endif } + +GDExtensionManager::~GDExtensionManager() { +#ifndef DISABLE_DEPRECATED + GDExtensionCompatHashes::finalize(); +#endif +} diff --git a/core/extension/gdextension_manager.h b/core/extension/gdextension_manager.h index 8cd6d5a3e2..9386e356bb 100644 --- a/core/extension/gdextension_manager.h +++ b/core/extension/gdextension_manager.h @@ -86,6 +86,7 @@ public: void reload_extensions(); GDExtensionManager(); + ~GDExtensionManager(); }; VARIANT_ENUM_CAST(GDExtensionManager::LoadStatus) diff --git a/core/object/make_virtuals.py b/core/object/make_virtuals.py index 0f3cf3916a..79a8df6c8a 100644 --- a/core/object/make_virtuals.py +++ b/core/object/make_virtuals.py @@ -47,8 +47,8 @@ _FORCE_INLINE_ bool _gdvirtual_##m_name##_call($CALLARGS) $CONST { \\ }\\ _FORCE_INLINE_ bool _gdvirtual_##m_name##_overridden() const { \\ ScriptInstance *_script_instance = ((Object*)(this))->get_script_instance();\\ - if (_script_instance) {\\ - return _script_instance->has_method(_gdvirtual_##m_name##_sn);\\ + if (_script_instance && _script_instance->has_method(_gdvirtual_##m_name##_sn)) {\\ + return true;\\ }\\ if (unlikely(_get_extension() && !_gdvirtual_##m_name##_initialized)) {\\ _gdvirtual_##m_name = nullptr;\\ diff --git a/core/register_core_types.cpp b/core/register_core_types.cpp index b4ac533779..4ad9dd43c4 100644 --- a/core/register_core_types.cpp +++ b/core/register_core_types.cpp @@ -360,6 +360,7 @@ void unregister_core_extensions() { if (_is_core_extensions_registered) { gdextension_manager->deinitialize_extensions(GDExtension::INITIALIZATION_LEVEL_CORE); } + GDExtension::finalize_gdextensions(); } void unregister_core_types() { diff --git a/doc/classes/AABB.xml b/doc/classes/AABB.xml index a803e4f106..c1c637d2d6 100644 --- a/doc/classes/AABB.xml +++ b/doc/classes/AABB.xml @@ -245,7 +245,9 @@ <return type="AABB" /> <param index="0" name="right" type="Transform3D" /> <description> - Inversely transforms (multiplies) the [AABB] by the given [Transform3D] transformation matrix. + Inversely transforms (multiplies) the [AABB] by the given [Transform3D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]aabb * transform[/code] is equivalent to [code]transform.inverse() * aabb[/code]. See [method Transform3D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * aabb[/code] can be used instead. See [method Transform3D.affine_inverse]. </description> </operator> <operator name="operator =="> diff --git a/doc/classes/Button.xml b/doc/classes/Button.xml index bf0534ab1e..d7782a816d 100644 --- a/doc/classes/Button.xml +++ b/doc/classes/Button.xml @@ -132,6 +132,7 @@ Font size of the [Button]'s text. </theme_item> <theme_item name="icon" data_type="icon" type="Texture2D"> + Default icon for the [Button]. Appears only if [member icon] is not assigned. </theme_item> <theme_item name="disabled" data_type="style" type="StyleBox"> [StyleBox] used when the [Button] is disabled. diff --git a/doc/classes/GraphEdit.xml b/doc/classes/GraphEdit.xml index c59b2944bb..410efd6389 100644 --- a/doc/classes/GraphEdit.xml +++ b/doc/classes/GraphEdit.xml @@ -214,7 +214,7 @@ <param index="3" name="to_port" type="int" /> <param index="4" name="amount" type="float" /> <description> - Sets the coloration of the connection between [param from_node]'s [param from_port] and [param to_node]'s [param to_port] with the color provided in the [theme_item activity] theme property. + Sets the coloration of the connection between [param from_node]'s [param from_port] and [param to_node]'s [param to_port] with the color provided in the [theme_item activity] theme property. The color is linearly interpolated between the connection color and the activity color using [param amount] as weight. </description> </method> <method name="set_selected"> @@ -408,6 +408,7 @@ </constants> <theme_items> <theme_item name="activity" data_type="color" type="Color" default="Color(1, 1, 1, 1)"> + Color of the connection's activity (see [method set_connection_activity]). </theme_item> <theme_item name="grid_major" data_type="color" type="Color" default="Color(1, 1, 1, 0.2)"> Color of major grid lines. diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml index e2aa13403a..e706e3d6e0 100644 --- a/doc/classes/LineEdit.xml +++ b/doc/classes/LineEdit.xml @@ -269,7 +269,7 @@ If [code]true[/code], every character is replaced with the secret character (see [member secret_character]). </member> <member name="secret_character" type="String" setter="set_secret_character" getter="get_secret_character" default=""•""> - The character to use to mask secret input (defaults to "•"). Only a single character can be used as the secret character. + The character to use to mask secret input. Only a single character can be used as the secret character. If it is longer than one character, only the first one will be used. If it is empty, a space will be used instead. </member> <member name="select_all_on_focus" type="bool" setter="set_select_all_on_focus" getter="is_select_all_on_focus" default="false"> If [code]true[/code], the [LineEdit] will select the whole text when it gains focus. diff --git a/doc/classes/Object.xml b/doc/classes/Object.xml index 88465b2159..2ffb02096d 100644 --- a/doc/classes/Object.xml +++ b/doc/classes/Object.xml @@ -219,6 +219,7 @@ # Storing the value in the fake property. internal_data["fake_property"] = value return true + return false func _get_property_list(): return [ @@ -228,7 +229,7 @@ [csharp] private Godot.Collections.Dictionary _internalData = new Godot.Collections.Dictionary(); - public override void _Set(StringName property, Variant value) + public override bool _Set(StringName property, Variant value) { if (property == "FakeProperty") { diff --git a/doc/classes/PackedVector2Array.xml b/doc/classes/PackedVector2Array.xml index 9f8671f076..0e5bb12d02 100644 --- a/doc/classes/PackedVector2Array.xml +++ b/doc/classes/PackedVector2Array.xml @@ -204,7 +204,9 @@ <return type="PackedVector2Array" /> <param index="0" name="right" type="Transform2D" /> <description> - Transforms (multiplies) all vectors in the array by the [Transform2D] matrix. + Returns a new [PackedVector2Array] with all vectors in this array inversely transformed (multiplied) by the given [Transform2D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]array * transform[/code] is equivalent to [code]transform.inverse() * array[/code]. See [method Transform2D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * array[/code] can be used instead. See [method Transform2D.affine_inverse]. </description> </operator> <operator name="operator +"> diff --git a/doc/classes/PackedVector3Array.xml b/doc/classes/PackedVector3Array.xml index 54f65a619b..ce7ae4f396 100644 --- a/doc/classes/PackedVector3Array.xml +++ b/doc/classes/PackedVector3Array.xml @@ -203,7 +203,9 @@ <return type="PackedVector3Array" /> <param index="0" name="right" type="Transform3D" /> <description> - Transforms (multiplies) all vectors in the array by the [Transform3D] matrix. + Returns a new [PackedVector3Array] with all vectors in this array inversely transformed (multiplied) by the given [Transform3D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]array * transform[/code] is equivalent to [code]transform.inverse() * array[/code]. See [method Transform3D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * array[/code] can be used instead. See [method Transform3D.affine_inverse]. </description> </operator> <operator name="operator +"> diff --git a/doc/classes/Plane.xml b/doc/classes/Plane.xml index a0da82d1d6..254557b986 100644 --- a/doc/classes/Plane.xml +++ b/doc/classes/Plane.xml @@ -193,6 +193,7 @@ <param index="0" name="right" type="Transform3D" /> <description> Inversely transforms (multiplies) the [Plane] by the given [Transform3D] transformation matrix. + [code]plane * transform[/code] is equivalent to [code]transform.affine_inverse() * plane[/code]. See [method Transform3D.affine_inverse]. </description> </operator> <operator name="operator =="> diff --git a/doc/classes/ProjectSettings.xml b/doc/classes/ProjectSettings.xml index 126f3a88d7..519061e398 100644 --- a/doc/classes/ProjectSettings.xml +++ b/doc/classes/ProjectSettings.xml @@ -393,6 +393,13 @@ The base strength of the panning effect for all [AudioStreamPlayer3D] nodes. The panning strength can be further scaled on each Node using [member AudioStreamPlayer3D.panning_strength]. A value of [code]0.0[/code] disables stereo panning entirely, leaving only volume attenuation in place. A value of [code]1.0[/code] completely mutes one of the channels if the sound is located exactly to the left (or right) of the listener. The default value of [code]0.5[/code] is tuned for headphones. When using speakers, you may find lower values to sound better as speakers have a lower stereo separation compared to headphones. </member> + <member name="audio/general/ios/mix_with_others" type="bool" setter="" getter="" default="false"> + Sets the [url=https://developer.apple.com/documentation/avfaudio/avaudiosession/categoryoptions/1616611-mixwithothers]mixWithOthers[/url] option for the AVAudioSession on iOS. This will override the mix behavior, if the category is set to [code]Play and Record[/code], [code]Playback[/code], or [code]Multi Route[/code]. + [code]Ambient[/code] always has this set per default. + </member> + <member name="audio/general/ios/session_category" type="int" setter="" getter="" default="0"> + Sets the [url=https://developer.apple.com/documentation/avfaudio/avaudiosessioncategory]AVAudioSessionCategory[/url] on iOS. Use the [code]Playback[/code] category to get sound output, even if the phone is in silent mode. + </member> <member name="audio/general/text_to_speech" type="bool" setter="" getter="" default="false"> If [code]true[/code], text-to-speech support is enabled, see [method DisplayServer.tts_get_voices] and [method DisplayServer.tts_speak]. [b]Note:[/b] Enabling TTS can cause addition idle CPU usage and interfere with the sleep mode, so consider disabling it if TTS is not used. diff --git a/doc/classes/Rect2.xml b/doc/classes/Rect2.xml index 4f17e3cf2d..0471d73749 100644 --- a/doc/classes/Rect2.xml +++ b/doc/classes/Rect2.xml @@ -237,7 +237,9 @@ <return type="Rect2" /> <param index="0" name="right" type="Transform2D" /> <description> - Inversely transforms (multiplies) the [Rect2] by the given [Transform2D] transformation matrix. + Inversely transforms (multiplies) the [Rect2] by the given [Transform2D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]rect * transform[/code] is equivalent to [code]transform.inverse() * rect[/code]. See [method Transform2D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * rect[/code] can be used instead. See [method Transform2D.affine_inverse]. </description> </operator> <operator name="operator =="> diff --git a/doc/classes/ScrollBar.xml b/doc/classes/ScrollBar.xml index e8d2753a9a..4ee05632a5 100644 --- a/doc/classes/ScrollBar.xml +++ b/doc/classes/ScrollBar.xml @@ -49,8 +49,6 @@ <theme_item name="grabber_pressed" data_type="style" type="StyleBox"> Used when the grabber is being dragged. </theme_item> - <theme_item name="hscroll" data_type="style" type="StyleBox"> - </theme_item> <theme_item name="scroll" data_type="style" type="StyleBox"> Used as background of this [ScrollBar]. </theme_item> diff --git a/doc/classes/Transform2D.xml b/doc/classes/Transform2D.xml index abb81b95cb..aee70f6b59 100644 --- a/doc/classes/Transform2D.xml +++ b/doc/classes/Transform2D.xml @@ -59,7 +59,7 @@ <method name="affine_inverse" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the inverse of the transform, under the assumption that the transformation is composed of rotation, scaling and translation. + Returns the inverse of the transform, under the assumption that the basis is invertible (must have non-zero determinant). </description> </method> <method name="basis_xform" qualifiers="const"> @@ -67,15 +67,17 @@ <param index="0" name="v" type="Vector2" /> <description> Returns a vector transformed (multiplied) by the basis matrix. - This method does not account for translation (the origin vector). + This method does not account for translation (the [member origin] vector). </description> </method> <method name="basis_xform_inv" qualifiers="const"> <return type="Vector2" /> <param index="0" name="v" type="Vector2" /> <description> - Returns a vector transformed (multiplied) by the inverse basis matrix. - This method does not account for translation (the origin vector). + Returns a vector transformed (multiplied) by the inverse basis matrix, under the assumption that the basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + This method does not account for translation (the [member origin] vector). + [code]transform.basis_xform_inv(vector)[/code] is equivalent to [code]transform.inverse().basis_xform(vector)[/code]. See [method inverse]. + For non-orthonormal transforms (e.g. with scaling) [code]transform.affine_inverse().basis_xform(vector)[/code] can be used instead. See [method affine_inverse]. </description> </method> <method name="determinant" qualifiers="const"> @@ -120,7 +122,7 @@ <method name="inverse" qualifiers="const"> <return type="Transform2D" /> <description> - Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use [method affine_inverse] for transforms with scaling). + Returns the inverse of the transform, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). Use [method affine_inverse] for non-orthonormal transforms (e.g. with scaling). </description> </method> <method name="is_conformal" qualifiers="const"> @@ -274,14 +276,14 @@ <return type="Transform2D" /> <param index="0" name="right" type="float" /> <description> - This operator multiplies all components of the [Transform2D], including the origin vector, which scales it uniformly. + This operator multiplies all components of the [Transform2D], including the [member origin] vector, which scales it uniformly. </description> </operator> <operator name="operator *"> <return type="Transform2D" /> <param index="0" name="right" type="int" /> <description> - This operator multiplies all components of the [Transform2D], including the origin vector, which scales it uniformly. + This operator multiplies all components of the [Transform2D], including the [member origin] vector, which scales it uniformly. </description> </operator> <operator name="operator =="> diff --git a/doc/classes/Transform3D.xml b/doc/classes/Transform3D.xml index c96be5124e..85da629d70 100644 --- a/doc/classes/Transform3D.xml +++ b/doc/classes/Transform3D.xml @@ -59,7 +59,7 @@ <method name="affine_inverse" qualifiers="const"> <return type="Transform3D" /> <description> - Returns the inverse of the transform, under the assumption that the transformation is composed of rotation, scaling and translation. + Returns the inverse of the transform, under the assumption that the basis is invertible (must have non-zero determinant). </description> </method> <method name="interpolate_with" qualifiers="const"> @@ -73,7 +73,7 @@ <method name="inverse" qualifiers="const"> <return type="Transform3D" /> <description> - Returns the inverse of the transform, under the assumption that the transformation is composed of rotation and translation (no scaling, use [method affine_inverse] for transforms with scaling). + Returns the inverse of the transform, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). Use [method affine_inverse] for non-orthonormal transforms (e.g. with scaling). </description> </method> <method name="is_equal_approx" qualifiers="const"> @@ -235,14 +235,14 @@ <return type="Transform3D" /> <param index="0" name="right" type="float" /> <description> - This operator multiplies all components of the [Transform3D], including the origin vector, which scales it uniformly. + This operator multiplies all components of the [Transform3D], including the [member origin] vector, which scales it uniformly. </description> </operator> <operator name="operator *"> <return type="Transform3D" /> <param index="0" name="right" type="int" /> <description> - This operator multiplies all components of the [Transform3D], including the origin vector, which scales it uniformly. + This operator multiplies all components of the [Transform3D], including the [member origin] vector, which scales it uniformly. </description> </operator> <operator name="operator =="> diff --git a/doc/classes/Vector2.xml b/doc/classes/Vector2.xml index fdaaf239f8..3fa7bb46fc 100644 --- a/doc/classes/Vector2.xml +++ b/doc/classes/Vector2.xml @@ -419,7 +419,9 @@ <return type="Vector2" /> <param index="0" name="right" type="Transform2D" /> <description> - Inversely transforms (multiplies) the [Vector2] by the given [Transform2D] transformation matrix. + Inversely transforms (multiplies) the [Vector2] by the given [Transform2D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]vector * transform[/code] is equivalent to [code]transform.inverse() * vector[/code]. See [method Transform2D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * vector[/code] can be used instead. See [method Transform2D.affine_inverse]. </description> </operator> <operator name="operator *"> diff --git a/doc/classes/Vector3.xml b/doc/classes/Vector3.xml index 92fbdf6850..83a8c6af73 100644 --- a/doc/classes/Vector3.xml +++ b/doc/classes/Vector3.xml @@ -442,7 +442,9 @@ <return type="Vector3" /> <param index="0" name="right" type="Basis" /> <description> - Inversely transforms (multiplies) the [Vector3] by the given [Basis] matrix. + Inversely transforms (multiplies) the [Vector3] by the given [Basis] matrix, under the assumption that the basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]vector * basis[/code] is equivalent to [code]basis.transposed() * vector[/code]. See [method Basis.transposed]. + For transforming by inverse of a non-orthonormal basis (e.g. with scaling) [code]basis.inverse() * vector[/code] can be used instead. See [method Basis.inverse]. </description> </operator> <operator name="operator *"> @@ -450,13 +452,16 @@ <param index="0" name="right" type="Quaternion" /> <description> Inversely transforms (multiplies) the [Vector3] by the given [Quaternion]. + [code]vector * quaternion[/code] is equivalent to [code]quaternion.inverse() * vector[/code]. See [method Quaternion.inverse]. </description> </operator> <operator name="operator *"> <return type="Vector3" /> <param index="0" name="right" type="Transform3D" /> <description> - Inversely transforms (multiplies) the [Vector3] by the given [Transform3D] transformation matrix. + Inversely transforms (multiplies) the [Vector3] by the given [Transform3D] transformation matrix, under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection is fine, scaling/skew is not). + [code]vector * transform[/code] is equivalent to [code]transform.inverse() * vector[/code]. See [method Transform3D.inverse]. + For transforming by inverse of an affine transformation (e.g. with scaling) [code]transform.affine_inverse() * vector[/code] can be used instead. See [method Transform3D.affine_inverse]. </description> </operator> <operator name="operator *"> diff --git a/doc/classes/Vector4.xml b/doc/classes/Vector4.xml index e5ad5631e8..c5503a9357 100644 --- a/doc/classes/Vector4.xml +++ b/doc/classes/Vector4.xml @@ -280,7 +280,8 @@ <return type="Vector4" /> <param index="0" name="right" type="Projection" /> <description> - Inversely transforms (multiplies) the [Vector4] by the given [Projection] matrix. + Transforms (multiplies) the [Vector4] by the transpose of the given [Projection] matrix. + For transforming by inverse of a projection [code]projection.inverse() * vector[/code] can be used instead. See [method Projection.inverse]. </description> </operator> <operator name="operator *"> diff --git a/doc/classes/VisibleOnScreenEnabler2D.xml b/doc/classes/VisibleOnScreenEnabler2D.xml index 9f8b07261e..e9017a389b 100644 --- a/doc/classes/VisibleOnScreenEnabler2D.xml +++ b/doc/classes/VisibleOnScreenEnabler2D.xml @@ -1,19 +1,21 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisibleOnScreenEnabler2D" inherits="VisibleOnScreenNotifier2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Automatically disables another node if not visible on screen. + A rectangular region of 2D space that, when visible on screen, enables a target node. </brief_description> <description> - VisibleOnScreenEnabler2D detects when it is visible on screen (just like [VisibleOnScreenNotifier2D]) and automatically enables or disables the target node. The target node is disabled when [VisibleOnScreenEnabler2D] is not visible on screen (including when [member CanvasItem.visible] is [code]false[/code]), and enabled when the enabler is visible. The disabling is achieved by changing [member Node.process_mode]. + [VisibleOnScreenEnabler2D] contains a rectangular region of 2D space and a target node. The target node will be automatically enabled (via its [member Node.process_mode] property) when any part of this region becomes visible on the screen, and automatically disabled otherwise. This can for example be used to activate enemies only when the player approaches them. + See [VisibleOnScreenNotifier2D] if you only want to be notified when the region is visible on screen. + [b]Note:[/b] [VisibleOnScreenEnabler2D] uses the render culling code to determine whether it's visible on screen, so it won't function unless [member CanvasItem.visible] is set to [code]true[/code]. </description> <tutorials> </tutorials> <members> <member name="enable_mode" type="int" setter="set_enable_mode" getter="get_enable_mode" enum="VisibleOnScreenEnabler2D.EnableMode" default="0"> - Determines how the node is enabled. Corresponds to [enum Node.ProcessMode]. Disabled node uses [constant Node.PROCESS_MODE_DISABLED]. + Determines how the target node is enabled. Corresponds to [enum Node.ProcessMode]. When the node is disabled, it always uses [constant Node.PROCESS_MODE_DISABLED]. </member> <member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath("..")"> - The path to the target node, relative to the [VisibleOnScreenEnabler2D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler2D] is inside scene tree) and every time the [VisibleOnScreenEnabler2D] enters the scene tree. If the path is invalid, nothing will happen. + The path to the target node, relative to the [VisibleOnScreenEnabler2D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler2D] is inside the scene tree) and every time the [VisibleOnScreenEnabler2D] enters the scene tree. If the path is invalid, an error will be printed in the editor and no node will be affected. </member> </members> <constants> diff --git a/doc/classes/VisibleOnScreenEnabler3D.xml b/doc/classes/VisibleOnScreenEnabler3D.xml index 1808953fcc..a26a47ac92 100644 --- a/doc/classes/VisibleOnScreenEnabler3D.xml +++ b/doc/classes/VisibleOnScreenEnabler3D.xml @@ -1,28 +1,32 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisibleOnScreenEnabler3D" inherits="VisibleOnScreenNotifier3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Enables certain nodes only when approximately visible. + A box-shaped region of 3D space that, when visible on screen, enables a target node. </brief_description> <description> - The VisibleOnScreenEnabler3D will disable [RigidBody3D] and [AnimationPlayer] nodes when they are not visible. It will only affect other nodes within the same scene as the VisibleOnScreenEnabler3D itself. - If you just want to receive notifications, use [VisibleOnScreenNotifier3D] instead. - [b]Note:[/b] VisibleOnScreenEnabler3D uses an approximate heuristic for performance reasons. It doesn't take walls and other occlusion into account. The heuristic is an implementation detail and may change in future versions. If you need precise visibility checking, use another method such as adding an [Area3D] node as a child of a [Camera3D] node and/or [method Vector3.dot]. - [b]Note:[/b] VisibleOnScreenEnabler3D will not affect nodes added after scene initialization. + [VisibleOnScreenEnabler3D] contains a box-shaped region of 3D space and a target node. The target node will be automatically enabled (via its [member Node.process_mode] property) when any part of this region becomes visible on the screen, and automatically disabled otherwise. This can for example be used to activate enemies only when the player approaches them. + See [VisibleOnScreenNotifier3D] if you only want to be notified when the region is visible on screen. + [b]Note:[/b] [VisibleOnScreenEnabler3D] uses an approximate heuristic that doesn't take walls and other occlusion into account, unless occlusion culling is used. It also won't function unless [member Node3D.visible] is set to [code]true[/code]. </description> <tutorials> </tutorials> <members> <member name="enable_mode" type="int" setter="set_enable_mode" getter="get_enable_mode" enum="VisibleOnScreenEnabler3D.EnableMode" default="0"> + Determines how the target node is enabled. Corresponds to [enum Node.ProcessMode]. When the node is disabled, it always uses [constant Node.PROCESS_MODE_DISABLED]. </member> <member name="enable_node_path" type="NodePath" setter="set_enable_node_path" getter="get_enable_node_path" default="NodePath("..")"> + The path to the target node, relative to the [VisibleOnScreenEnabler3D]. The target node is cached; it's only assigned when setting this property (if the [VisibleOnScreenEnabler3D] is inside the scene tree) and every time the [VisibleOnScreenEnabler3D] enters the scene tree. If the path is invalid, an error will be printed in the editor and no node will be affected. </member> </members> <constants> <constant name="ENABLE_MODE_INHERIT" value="0" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_INHERIT]. </constant> <constant name="ENABLE_MODE_ALWAYS" value="1" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_ALWAYS]. </constant> <constant name="ENABLE_MODE_WHEN_PAUSED" value="2" enum="EnableMode"> + Corresponds to [constant Node.PROCESS_MODE_WHEN_PAUSED]. </constant> </constants> </class> diff --git a/doc/classes/VisibleOnScreenNotifier2D.xml b/doc/classes/VisibleOnScreenNotifier2D.xml index dc36e25003..9d22bf6cff 100644 --- a/doc/classes/VisibleOnScreenNotifier2D.xml +++ b/doc/classes/VisibleOnScreenNotifier2D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisibleOnScreenNotifier2D" inherits="Node2D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Detects when the node extents are visible on screen. + A rectangular region of 2D space that detects whether it is visible on screen. </brief_description> <description> - The VisibleOnScreenNotifier2D detects when it is visible on the screen. It also notifies when its bounding rectangle enters or exits the screen or a viewport. - If you want nodes to be disabled automatically when they exit the screen, use [VisibleOnScreenEnabler2D] instead. - [b]Note:[/b] VisibleOnScreenNotifier2D uses the render culling code to determine whether it's visible on screen, which also means that its [member CanvasItem.visible] must be [code]true[/code] to work correctly. + [VisibleOnScreenEnabler2D] represents a rectangular region of 2D space. When any part of this region becomes visible on screen or in a viewport, it will emit a [signal screen_entered] signal, and likewise it will emit a [signal screen_exited] signal when no part of it remains visible. + If you want a node to be enabled automatically when this region is visible on screen, use [VisibleOnScreenEnabler2D]. + [b]Note:[/b] [VisibleOnScreenNotifier2D] uses the render culling code to determine whether it's visible on screen, so it won't function unless [member CanvasItem.visible] is set to [code]true[/code]. </description> <tutorials> <link title="2D Dodge The Creeps Demo">https://godotengine.org/asset-library/asset/515</link> @@ -16,7 +16,7 @@ <return type="bool" /> <description> If [code]true[/code], the bounding rectangle is on the screen. - [b]Note:[/b] It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass. + [b]Note:[/b] It takes one frame for the [VisibleOnScreenNotifier2D]'s visibility to be determined once added to the scene tree, so this method will always return [code]false[/code] right after it is instantiated, before the draw pass. </description> </method> </methods> diff --git a/doc/classes/VisibleOnScreenNotifier3D.xml b/doc/classes/VisibleOnScreenNotifier3D.xml index ba1fa763d3..38d55cd331 100644 --- a/doc/classes/VisibleOnScreenNotifier3D.xml +++ b/doc/classes/VisibleOnScreenNotifier3D.xml @@ -1,12 +1,12 @@ <?xml version="1.0" encoding="UTF-8" ?> <class name="VisibleOnScreenNotifier3D" inherits="VisualInstance3D" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:noNamespaceSchemaLocation="../class.xsd"> <brief_description> - Detects approximately when the node is visible on screen. + A box-shaped region of 3D space that detects whether it is visible on screen. </brief_description> <description> - The VisibleOnScreenNotifier3D detects when it is visible on the screen. It also notifies when its bounding rectangle enters or exits the screen or a [Camera3D]'s view. - If you want nodes to be disabled automatically when they exit the screen, use [VisibleOnScreenEnabler3D] instead. - [b]Note:[/b] VisibleOnScreenNotifier3D uses the render culling code to determine whether it's visible on screen, which also means that its [member Node3D.visible] must be [code]true[/code] to work correctly. + [VisibleOnScreenEnabler3D] represents a box-shaped region of 3D space. When any part of this region becomes visible on screen or in a [Camera3D]'s view, it will emit a [signal screen_entered] signal, and likewise it will emit a [signal screen_exited] signal when no part of it remains visible. + If you want a node to be enabled automatically when this region is visible on screen, use [VisibleOnScreenEnabler3D]. + [b]Note:[/b] [VisibleOnScreenNotifier3D] uses an approximate heuristic that doesn't take walls and other occlusion into account, unless occlusion culling is used. It also won't function unless [member Node3D.visible] is set to [code]true[/code]. </description> <tutorials> </tutorials> @@ -14,25 +14,25 @@ <method name="is_on_screen" qualifiers="const"> <return type="bool" /> <description> - If [code]true[/code], the bounding box is on the screen. - [b]Note:[/b] It takes one frame for the node's visibility to be assessed once added to the scene tree, so this method will return [code]false[/code] right after it is instantiated, even if it will be on screen in the draw pass. + Returns [code]true[/code] if the bounding box is on the screen. + [b]Note:[/b] It takes one frame for the [VisibleOnScreenNotifier3D]'s visibility to be assessed once added to the scene tree, so this method will always return [code]false[/code] right after it is instantiated. </description> </method> </methods> <members> <member name="aabb" type="AABB" setter="set_aabb" getter="get_aabb" default="AABB(-1, -1, -1, 2, 2, 2)"> - The VisibleOnScreenNotifier3D's bounding box. + The [VisibleOnScreenNotifier3D]'s bounding box. </member> </members> <signals> <signal name="screen_entered"> <description> - Emitted when the VisibleOnScreenNotifier3D enters the screen. + Emitted when the [VisibleOnScreenNotifier3D] enters the screen. </description> </signal> <signal name="screen_exited"> <description> - Emitted when the VisibleOnScreenNotifier3D exits the screen. + Emitted when the [VisibleOnScreenNotifier3D] exits the screen. </description> </signal> </signals> diff --git a/drivers/vulkan/rendering_device_vulkan.cpp b/drivers/vulkan/rendering_device_vulkan.cpp index 5c68149a5f..a9f77c9072 100644 --- a/drivers/vulkan/rendering_device_vulkan.cpp +++ b/drivers/vulkan/rendering_device_vulkan.cpp @@ -4639,8 +4639,9 @@ String RenderingDeviceVulkan::_shader_uniform_debug(RID p_shader, int p_set) { // Version 1: initial. // Version 2: Added shader name. // Version 3: Added writable. +// Version 4: 64-bit vertex input mask. -#define SHADER_BINARY_VERSION 3 +#define SHADER_BINARY_VERSION 4 String RenderingDeviceVulkan::shader_get_binary_cache_key() const { return "Vulkan-SV" + itos(SHADER_BINARY_VERSION); diff --git a/editor/code_editor.cpp b/editor/code_editor.cpp index b6d90a5bd2..014fe8a530 100644 --- a/editor/code_editor.cpp +++ b/editor/code_editor.cpp @@ -761,7 +761,7 @@ FindReplaceBar::FindReplaceBar() { hide_button = memnew(TextureButton); add_child(hide_button); hide_button->set_focus_mode(FOCUS_NONE); - hide_button->connect("pressed", callable_mp(this, &FindReplaceBar::_hide_bar)); + hide_button->connect("pressed", callable_mp(this, &FindReplaceBar::_hide_bar).bind(false)); hide_button->set_v_size_flags(SIZE_SHRINK_CENTER); } diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp index dd3198954d..fa8810c539 100644 --- a/editor/editor_node.cpp +++ b/editor/editor_node.cpp @@ -3664,6 +3664,8 @@ void EditorNode::fix_dependencies(const String &p_for_file) { int EditorNode::new_scene() { int idx = editor_data.add_edited_scene(-1); + _set_current_scene(idx); // Before trying to remove an empty scene, set the current tab index to the newly added tab index. + // Remove placeholder empty scene. if (editor_data.get_edited_scene_count() > 1) { for (int i = 0; i < editor_data.get_edited_scene_count() - 1; i++) { @@ -3674,9 +3676,7 @@ int EditorNode::new_scene() { } } } - idx = MAX(idx, 0); - _set_current_scene(idx); editor_data.clear_editor_states(); scene_tabs->update_scene_tabs(); return idx; @@ -3834,6 +3834,12 @@ Error EditorNode::load_scene(const String &p_scene, bool p_ignore_broken_deps, b // If we are, we'll edit it after the restoration is done. if (!restoring_scenes) { push_item(new_scene); + } else { + // Initialize history for restored scenes. + ObjectID id = new_scene->get_instance_id(); + if (id != editor_history.get_current()) { + editor_history.add_object(id); + } } // Load the selected nodes. @@ -5850,6 +5856,14 @@ void EditorNode::add_control_to_dock(DockSlot p_slot, Control *p_control) { } void EditorNode::remove_control_from_dock(Control *p_control) { + // If the dock is floating, close it first. + for (WindowWrapper *wrapper : floating_docks) { + if (p_control == wrapper->get_wrapped_control()) { + wrapper->set_window_enabled(false); + break; + } + } + Control *dock = nullptr; for (int i = 0; i < DOCK_SLOT_MAX; i++) { if (p_control->get_parent() == dock_slot[i]) { @@ -6945,6 +6959,7 @@ EditorNode::EditorNode() { // Exporters might need the theme. EditorColorMap::create(); + EditorTheme::initialize(); theme = create_custom_theme(); DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor))); @@ -8032,6 +8047,8 @@ EditorNode::~EditorNode() { memdelete(progress_hb); EditorSettings::destroy(); + EditorColorMap::finish(); + EditorTheme::finalize(); GDExtensionEditorPlugins::editor_node_add_plugin = nullptr; GDExtensionEditorPlugins::editor_node_remove_plugin = nullptr; diff --git a/editor/editor_themes.cpp b/editor/editor_themes.cpp index f5be91c6de..bf0c5392c1 100644 --- a/editor/editor_themes.cpp +++ b/editor/editor_themes.cpp @@ -55,7 +55,7 @@ void EditorColorMap::add_conversion_color_pair(const String p_from_color, const color_conversion_map[Color::html(p_from_color)] = Color::html(p_to_color); } -void EditorColorMap::add_conversion_exception(const StringName p_icon_name) { +void EditorColorMap::add_conversion_exception(const StringName &p_icon_name) { color_conversion_exceptions.insert(p_icon_name); } @@ -63,7 +63,7 @@ void EditorColorMap::create() { // Some of the colors below are listed for completeness sake. // This can be a basis for proper palette validation later. - // Convert: FROM TO + // Convert: FROM TO add_conversion_color_pair("#478cbf", "#478cbf"); // Godot Blue add_conversion_color_pair("#414042", "#414042"); // Godot Gray @@ -215,6 +215,11 @@ void EditorColorMap::create() { add_conversion_exception("Breakpoint"); } +void EditorColorMap::finish() { + color_conversion_map.clear(); + color_conversion_exceptions.clear(); +} + Vector<StringName> EditorTheme::editor_theme_types; // TODO: Refactor these and corresponding Theme methods to use the bool get_xxx(r_value) pattern internally. @@ -301,13 +306,15 @@ Ref<StyleBox> EditorTheme::get_stylebox(const StringName &p_name, const StringNa } } -EditorTheme::EditorTheme() { - if (editor_theme_types.is_empty()) { - editor_theme_types.append(EditorStringName(Editor)); - editor_theme_types.append(EditorStringName(EditorFonts)); - editor_theme_types.append(EditorStringName(EditorIcons)); - editor_theme_types.append(EditorStringName(EditorStyles)); - } +void EditorTheme::initialize() { + editor_theme_types.append(EditorStringName(Editor)); + editor_theme_types.append(EditorStringName(EditorFonts)); + editor_theme_types.append(EditorStringName(EditorIcons)); + editor_theme_types.append(EditorStringName(EditorStyles)); +} + +void EditorTheme::finalize() { + editor_theme_types.clear(); } // Editor theme generatior. diff --git a/editor/editor_themes.h b/editor/editor_themes.h index 7ca0050103..7d913ccc40 100644 --- a/editor/editor_themes.h +++ b/editor/editor_themes.h @@ -45,12 +45,14 @@ class EditorColorMap { static HashSet<StringName> color_conversion_exceptions; public: - static void create(); static void add_conversion_color_pair(const String p_from_color, const String p_to_color); - static void add_conversion_exception(const StringName p_icon_name); + static void add_conversion_exception(const StringName &p_icon_name); static HashMap<Color, Color> &get_color_conversion_map() { return color_conversion_map; }; static HashSet<StringName> &get_color_conversion_exceptions() { return color_conversion_exceptions; }; + + static void create(); + static void finish(); }; class EditorTheme : public Theme { @@ -66,7 +68,8 @@ public: virtual Ref<Texture2D> get_icon(const StringName &p_name, const StringName &p_theme_type) const override; virtual Ref<StyleBox> get_stylebox(const StringName &p_name, const StringName &p_theme_type) const override; - EditorTheme(); + static void initialize(); + static void finalize(); }; Ref<Theme> create_editor_theme(Ref<Theme> p_theme = nullptr); diff --git a/editor/filesystem_dock.cpp b/editor/filesystem_dock.cpp index 655f8ee95c..c5feb71c9e 100644 --- a/editor/filesystem_dock.cpp +++ b/editor/filesystem_dock.cpp @@ -2975,7 +2975,7 @@ void FileSystemDock::_file_and_folders_fill_popup(PopupMenu *p_popup, Vector<Str folder_colors_menu->add_separator(); for (const KeyValue<String, Color> &E : folder_colors) { - folder_colors_menu->add_icon_item(get_editor_theme_icon(SNAME("Folder")), TTR(E.key.capitalize())); + folder_colors_menu->add_icon_item(get_editor_theme_icon(SNAME("Folder")), E.key.capitalize()); folder_colors_menu->set_item_icon_modulate(-1, editor_is_dark_theme ? E.value : E.value * 2); folder_colors_menu->set_item_metadata(-1, E.key); @@ -3535,16 +3535,16 @@ FileSystemDock::FileSystemDock() { ED_SHORTCUT("filesystem_dock/open_in_external_program", TTR("Open in External Program")); #endif - folder_colors = HashMap<String, Color>(); - folder_colors["red"] = Color(1.0, 0.271, 0.271); - folder_colors["orange"] = Color(1.0, 0.561, 0.271); - folder_colors["yellow"] = Color(1.0, 0.890, 0.271); - folder_colors["green"] = Color(0.502, 1.0, 0.271); - folder_colors["teal"] = Color(0.271, 1.0, 0.635); - folder_colors["blue"] = Color(0.271, 0.843, 1.0); - folder_colors["purple"] = Color(0.502, 0.271, 1.0); - folder_colors["pink"] = Color(1.0, 0.271, 0.588); - folder_colors["gray"] = Color(0.616, 0.616, 0.616); + // Properly translating color names would require a separate HashMap, so for simplicity they are provided as comments. + folder_colors["red"] = Color(1.0, 0.271, 0.271); // TTR("Red") + folder_colors["orange"] = Color(1.0, 0.561, 0.271); // TTR("Orange") + folder_colors["yellow"] = Color(1.0, 0.890, 0.271); // TTR("Yellow") + folder_colors["green"] = Color(0.502, 1.0, 0.271); // TTR("Green") + folder_colors["teal"] = Color(0.271, 1.0, 0.635); // TTR("Teal") + folder_colors["blue"] = Color(0.271, 0.843, 1.0); // TTR("Blue") + folder_colors["purple"] = Color(0.502, 0.271, 1.0); // TTR("Purple") + folder_colors["pink"] = Color(1.0, 0.271, 0.588); // TTR("Pink") + folder_colors["gray"] = Color(0.616, 0.616, 0.616); // TTR("Gray") assigned_folder_colors = ProjectSettings::get_singleton()->get_setting("file_customization/folder_colors"); diff --git a/editor/gui/editor_file_dialog.cpp b/editor/gui/editor_file_dialog.cpp index 14d57cbd10..22761d3773 100644 --- a/editor/gui/editor_file_dialog.cpp +++ b/editor/gui/editor_file_dialog.cpp @@ -1393,12 +1393,14 @@ void EditorFileDialog::_update_favorites() { bool fav_changed = false; int current_favorite = -1; for (int i = 0; i < favorited.size(); i++) { - bool cres = favorited[i].begins_with("res://"); - if (cres != res) { + String name = favorited[i]; + + bool cres = name.begins_with("res://"); + if (cres != res || !name.ends_with("/")) { continue; } - if (!dir_access->dir_exists(favorited[i])) { + if (!dir_access->dir_exists(name)) { // Remove invalid directory from the list of Favorited directories. favorited.remove_at(i--); fav_changed = true; @@ -1406,7 +1408,6 @@ void EditorFileDialog::_update_favorites() { } // Compute favorite display text. - String name = favorited[i]; if (res && name == "res://") { if (name == current) { current_favorite = favorited_paths.size(); @@ -1414,7 +1415,7 @@ void EditorFileDialog::_update_favorites() { name = "/"; favorited_paths.append(favorited[i]); favorited_names.append(name); - } else if (name.ends_with("/")) { + } else { if (name == current || name == current + "/") { current_favorite = favorited_paths.size(); } @@ -1422,8 +1423,6 @@ void EditorFileDialog::_update_favorites() { name = name.get_file(); favorited_paths.append(favorited[i]); favorited_names.append(name); - } else { - // Ignore favorited files. } } diff --git a/editor/gui/editor_scene_tabs.cpp b/editor/gui/editor_scene_tabs.cpp index a11d3e7b6a..1fe8eee6d7 100644 --- a/editor/gui/editor_scene_tabs.cpp +++ b/editor/gui/editor_scene_tabs.cpp @@ -341,6 +341,8 @@ void EditorSceneTabs::_bind_methods() { EditorSceneTabs::EditorSceneTabs() { singleton = this; + set_process_shortcut_input(true); + tabbar_panel = memnew(PanelContainer); add_child(tabbar_panel); tabbar_container = memnew(HBoxContainer); diff --git a/editor/plugins/animation_blend_tree_editor_plugin.cpp b/editor/plugins/animation_blend_tree_editor_plugin.cpp index 673f135a54..772957bc55 100644 --- a/editor/plugins/animation_blend_tree_editor_plugin.cpp +++ b/editor/plugins/animation_blend_tree_editor_plugin.cpp @@ -447,6 +447,11 @@ void AnimationNodeBlendTreeEditor::_connection_request(const String &p_from, int AnimationNodeBlendTree::ConnectionError err = blend_tree->can_connect_node(p_to, p_to_index, p_from); + if (err == AnimationNodeBlendTree::CONNECTION_ERROR_CONNECTION_EXISTS) { + blend_tree->disconnect_node(p_to, p_to_index); + err = blend_tree->can_connect_node(p_to, p_to_index, p_from); + } + if (err != AnimationNodeBlendTree::CONNECTION_OK) { EditorNode::get_singleton()->show_warning(TTR("Unable to connect, port may be in use or connection may be invalid.")); return; diff --git a/editor/plugins/sprite_frames_editor_plugin.cpp b/editor/plugins/sprite_frames_editor_plugin.cpp index f41b1d30d7..01e9eb7a49 100644 --- a/editor/plugins/sprite_frames_editor_plugin.cpp +++ b/editor/plugins/sprite_frames_editor_plugin.cpp @@ -870,13 +870,10 @@ void SpriteFramesEditor::_sync_animation() { } void SpriteFramesEditor::_select_animation(const String &p_name, bool p_update_node) { - TreeItem *selected = nullptr; - selected = animations->get_item_with_text(p_name); - if (!selected) { + if (!frames->has_animation(p_name)) { return; - }; - - edited_anim = selected->get_text(0); + } + edited_anim = p_name; if (animated_sprite) { if (p_update_node) { @@ -951,6 +948,7 @@ void SpriteFramesEditor::_animation_name_edited() { counter++; name = new_name + "_" + itos(counter); } + edited->set_text(0, name); EditorUndoRedoManager *undo_redo = EditorUndoRedoManager::get_singleton(); undo_redo->create_action(TTR("Rename Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); @@ -958,11 +956,12 @@ void SpriteFramesEditor::_animation_name_edited() { undo_redo->add_undo_method(frames.ptr(), "rename_animation", name, edited_anim); _rename_node_animation(undo_redo, false, edited_anim, name, name); _rename_node_animation(undo_redo, true, edited_anim, edited_anim, edited_anim); + undo_redo->add_do_method(this, "_select_animation", name); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - _select_animation(name); animations->grab_focus(); } @@ -1007,11 +1006,12 @@ void SpriteFramesEditor::_animation_add() { undo_redo->create_action(TTR("Add Animation"), UndoRedo::MERGE_DISABLE, EditorNode::get_singleton()->get_edited_scene()); undo_redo->add_do_method(frames.ptr(), "add_animation", name); undo_redo->add_undo_method(frames.ptr(), "remove_animation", name); + undo_redo->add_do_method(this, "_select_animation", name); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - _select_animation(name); animations->grab_focus(); } @@ -1057,11 +1057,11 @@ void SpriteFramesEditor::_animation_remove_confirmed() { float duration = frames->get_frame_duration(edited_anim, i); undo_redo->add_undo_method(frames.ptr(), "add_frame", edited_anim, texture, duration); } + undo_redo->add_do_method(this, "_select_animation", new_edited); + undo_redo->add_undo_method(this, "_select_animation", edited_anim); undo_redo->add_do_method(this, "_update_library"); undo_redo->add_undo_method(this, "_update_library"); undo_redo->commit_action(); - - _select_animation(new_edited); } void SpriteFramesEditor::_animation_search_text_changed(const String &p_text) { @@ -1179,6 +1179,20 @@ void SpriteFramesEditor::_zoom_reset() { } void SpriteFramesEditor::_update_library(bool p_skip_selector) { + if (!p_skip_selector) { + animations_dirty = true; + } + + if (pending_update) { + return; + } + pending_update = true; + callable_mp(this, &SpriteFramesEditor::_update_library_impl).call_deferred(); +} + +void SpriteFramesEditor::_update_library_impl() { + pending_update = false; + if (frames.is_null()) { return; } @@ -1187,7 +1201,8 @@ void SpriteFramesEditor::_update_library(bool p_skip_selector) { frame_duration->set_value_no_signal(1.0); // Default. - if (!p_skip_selector) { + if (animations_dirty) { + animations_dirty = false; animations->clear(); TreeItem *anim_root = animations->create_item(); @@ -1624,6 +1639,7 @@ void SpriteFramesEditor::_autoplay_pressed() { void SpriteFramesEditor::_bind_methods() { ClassDB::bind_method(D_METHOD("_update_library", "skipsel"), &SpriteFramesEditor::_update_library, DEFVAL(false)); + ClassDB::bind_method(D_METHOD("_select_animation", "name", "update_node"), &SpriteFramesEditor::_select_animation, DEFVAL(true)); } void SpriteFramesEditor::_node_removed(Node *p_node) { diff --git a/editor/plugins/sprite_frames_editor_plugin.h b/editor/plugins/sprite_frames_editor_plugin.h index ed75be9061..f14c2203df 100644 --- a/editor/plugins/sprite_frames_editor_plugin.h +++ b/editor/plugins/sprite_frames_editor_plugin.h @@ -189,6 +189,7 @@ class SpriteFramesEditor : public HSplitContainer { void _down_pressed(); void _frame_duration_changed(double p_value); void _update_library(bool p_skip_selector = false); + void _update_library_impl(); void _update_stop_icon(); void _play_pressed(); @@ -214,6 +215,9 @@ class SpriteFramesEditor : public HSplitContainer { void _zoom_out(); void _zoom_reset(); + bool animations_dirty = false; + bool pending_update = false; + bool updating; bool updating_split_settings = false; // Skip SpinBox/Range callback when setting value by code. diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp index c9555554e1..de807c79e0 100644 --- a/editor/project_manager.cpp +++ b/editor/project_manager.cpp @@ -2845,6 +2845,7 @@ ProjectManager::ProjectManager() { } EditorColorMap::create(); + EditorTheme::initialize(); Ref<Theme> theme = create_custom_theme(); DisplayServer::set_early_window_clear_color_override(true, theme->get_color(SNAME("background"), EditorStringName(Editor))); @@ -3297,6 +3298,9 @@ ProjectManager::~ProjectManager() { if (EditorSettings::get_singleton()) { EditorSettings::destroy(); } + + EditorColorMap::finish(); + EditorTheme::finalize(); } void ProjectTag::_notification(int p_what) { diff --git a/modules/gdscript/gdscript.cpp b/modules/gdscript/gdscript.cpp index 58aa7a08d4..6245cc85a0 100644 --- a/modules/gdscript/gdscript.cpp +++ b/modules/gdscript/gdscript.cpp @@ -1386,6 +1386,36 @@ String GDScript::debug_get_script_name(const Ref<Script> &p_script) { } #endif +thread_local GDScript::UpdatableFuncPtr GDScript::func_ptrs_to_update_thread_local; + +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.mutex = &func_ptrs_to_update_thread_local.mutex; + + if (likely(func_ptrs_to_update_thread_local.initialized)) { + return result; + } + + func_ptrs_to_update_thread_local.initialized = true; + } + + MutexLock lock(func_ptrs_to_update_mutex); + func_ptrs_to_update.push_back(&func_ptrs_to_update_thread_local); + + return result; +} + +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.mutex); + MutexLock lock(*p_func_ptr_element.mutex); + p_func_ptr_element.element->erase(); +} + void GDScript::clear(ClearData *p_clear_data) { if (clearing) { return; @@ -1403,6 +1433,16 @@ void GDScript::clear(ClearData *p_clear_data) { is_root = true; } + { + MutexLock outer_lock(func_ptrs_to_update_mutex); + for (UpdatableFuncPtr *updatable : func_ptrs_to_update) { + MutexLock inner_lock(updatable->mutex); + for (GDScriptFunction **func_ptr_ptr : updatable->ptrs) { + *func_ptr_ptr = nullptr; + } + } + } + RBSet<GDScript *> must_clear_dependencies = get_must_clear_dependencies(); for (GDScript *E : must_clear_dependencies) { clear_data->scripts.insert(E); diff --git a/modules/gdscript/gdscript.h b/modules/gdscript/gdscript.h index d335ec85ee..04b0a1d786 100644 --- a/modules/gdscript/gdscript.h +++ b/modules/gdscript/gdscript.h @@ -86,6 +86,8 @@ class GDScript : public Script { friend class GDScriptAnalyzer; friend class GDScriptCompiler; friend class GDScriptDocGen; + friend class GDScriptLambdaCallable; + friend class GDScriptLambdaSelfCallable; friend class GDScriptLanguage; friend struct GDScriptUtilityFunctionsDefinitions; @@ -108,6 +110,30 @@ class GDScript : public Script { HashMap<StringName, MethodInfo> _signals; Dictionary rpc_config; + struct LambdaInfo { + int capture_count; + bool use_self; + }; + + 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 = false; + }; + struct UpdatableFuncPtrElement { + List<GDScriptFunction **>::Element *element = nullptr; + Mutex *mutex = nullptr; + }; + static thread_local UpdatableFuncPtr func_ptrs_to_update_thread_local; + 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); + #ifdef TOOLS_ENABLED // For static data storage during hot-reloading. HashMap<StringName, MemberInfo> old_static_variables_indices; diff --git a/modules/gdscript/gdscript_analyzer.cpp b/modules/gdscript/gdscript_analyzer.cpp index f3f95ba50b..2439e5760c 100644 --- a/modules/gdscript/gdscript_analyzer.cpp +++ b/modules/gdscript/gdscript_analyzer.cpp @@ -4343,7 +4343,7 @@ void GDScriptAnalyzer::reduce_subscript(GDScriptParser::SubscriptNode *p_subscri } } else if (base_type.kind != GDScriptParser::DataType::BUILTIN && !index_type.is_variant()) { if (index_type.builtin_type != Variant::STRING && index_type.builtin_type != Variant::STRING_NAME) { - push_error(vformat(R"(Only String or StringName can be used as index for type "%s", but received a "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index); + push_error(vformat(R"(Only "String" or "StringName" can be used as index for type "%s", but received "%s".)", base_type.to_string(), index_type.to_string()), p_subscript->index); } } diff --git a/modules/gdscript/gdscript_cache.cpp b/modules/gdscript/gdscript_cache.cpp index 18609d0b80..26f01ec218 100644 --- a/modules/gdscript/gdscript_cache.cpp +++ b/modules/gdscript/gdscript_cache.cpp @@ -287,7 +287,8 @@ Ref<GDScript> GDScriptCache::get_full_script(const String &p_path, Error &r_erro if (script.is_null()) { script = get_shallow_script(p_path, r_error); - if (r_error) { + // Only exit early if script failed to load, otherwise let reload report errors. + if (script.is_null()) { return script; } } diff --git a/modules/gdscript/gdscript_compiler.cpp b/modules/gdscript/gdscript_compiler.cpp index bf648abc9e..7980f020b8 100644 --- a/modules/gdscript/gdscript_compiler.cpp +++ b/modules/gdscript/gdscript_compiler.cpp @@ -1371,6 +1371,7 @@ GDScriptCodeGenerator::Address GDScriptCompiler::_parse_expression(CodeGen &code return GDScriptCodeGenerator::Address(); } + main_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++) { @@ -2631,6 +2632,7 @@ Error GDScriptCompiler::_prepare_compilation(GDScript *p_script, const GDScriptP p_script->implicit_ready = nullptr; p_script->static_initializer = nullptr; p_script->rpc_config.clear(); + p_script->lambda_info.clear(); p_script->clearing = false; @@ -3040,6 +3042,128 @@ void GDScriptCompiler::make_scripts(GDScript *p_script, const GDScriptParser::Cl } } +GDScriptCompiler::FunctionLambdaInfo GDScriptCompiler::_get_function_replacement_info(GDScriptFunction *p_func, int p_index, int p_depth, GDScriptFunction *p_parent_func) { + FunctionLambdaInfo info; + info.function = p_func; + info.parent = p_parent_func; + info.script = p_parent_func; + info.name = p_func->get_name(); + info.line = p_func->_initial_line; + info.index = p_index; + info.depth = p_depth; + info.capture_count = 0; + info.use_self = false; + info.arg_count = p_func->_argument_count; + 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); + if (extra_info != nullptr) { + info.capture_count = extra_info->capture_count; + info.use_self = extra_info->use_self; + } + + return info; +} + +Vector<GDScriptCompiler::FunctionLambdaInfo> GDScriptCompiler::_get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth, GDScriptFunction *p_parent_func) { + Vector<FunctionLambdaInfo> result; + // Only scrape the lambdas inside p_func. + for (int i = 0; i < p_func->lambdas.size(); ++i) { + result.push_back(_get_function_replacement_info(p_func->lambdas[i], i, p_depth + 1, p_func)); + } + return result; +} + +GDScriptCompiler::ScriptLambdaInfo GDScriptCompiler::_get_script_lambda_replacement_info(GDScript *p_script) { + ScriptLambdaInfo info; + + if (p_script->implicit_initializer) { + info.implicit_initializer_info = _get_function_lambda_replacement_info(p_script->implicit_initializer); + } + if (p_script->implicit_ready) { + info.implicit_ready_info = _get_function_lambda_replacement_info(p_script->implicit_ready); + } + if (p_script->static_initializer) { + info.static_initializer_info = _get_function_lambda_replacement_info(p_script->static_initializer); + } + + for (const KeyValue<StringName, GDScriptFunction *> &E : p_script->member_functions) { + info.member_function_infos.insert(E.key, _get_function_lambda_replacement_info(E.value)); + } + + for (const KeyValue<StringName, Ref<GDScript>> &KV : p_script->get_subclasses()) { + info.subclass_info.insert(KV.key, _get_script_lambda_replacement_info(KV.value.ptr())); + } + + return info; +} + +bool GDScriptCompiler::_do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) { + if (p_new_info == nullptr) { + return false; + } + + if (p_new_info->capture_count != p_old_info.capture_count || p_new_info->use_self != p_old_info.use_self) { + return false; + } + + int old_required_arg_count = p_old_info.arg_count - p_old_info.default_arg_count; + int new_required_arg_count = p_new_info->arg_count - p_new_info->default_arg_count; + if (new_required_arg_count > old_required_arg_count || p_new_info->arg_count < old_required_arg_count) { + return false; + } + + return true; +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info) { + ERR_FAIL_COND(r_replacements.has(p_old_info.function)); + if (!_do_function_infos_match(p_old_info, p_new_info)) { + p_new_info = nullptr; + } + + r_replacements.insert(p_old_info.function, p_new_info != nullptr ? p_new_info->function : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.sublambdas, p_new_info != nullptr ? &p_new_info->sublambdas : nullptr); +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos) { + for (int i = 0; i < p_old_infos.size(); ++i) { + const FunctionLambdaInfo &old_info = p_old_infos[i]; + const FunctionLambdaInfo *new_info = nullptr; + if (p_new_infos != nullptr && p_new_infos->size() == p_old_infos.size()) { + // For now only attempt if the size is the same. + new_info = &p_new_infos->get(i); + } + _get_function_ptr_replacements(r_replacements, old_info, new_info); + } +} + +void GDScriptCompiler::_get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info) { + _get_function_ptr_replacements(r_replacements, p_old_info.implicit_initializer_info, p_new_info != nullptr ? &p_new_info->implicit_initializer_info : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.implicit_ready_info, p_new_info != nullptr ? &p_new_info->implicit_ready_info : nullptr); + _get_function_ptr_replacements(r_replacements, p_old_info.static_initializer_info, p_new_info != nullptr ? &p_new_info->static_initializer_info : nullptr); + + for (const KeyValue<StringName, Vector<FunctionLambdaInfo>> &old_kv : p_old_info.member_function_infos) { + _get_function_ptr_replacements(r_replacements, old_kv.value, p_new_info != nullptr ? p_new_info->member_function_infos.getptr(old_kv.key) : nullptr); + } + for (int i = 0; i < p_old_info.other_function_infos.size(); ++i) { + const FunctionLambdaInfo &old_other_info = p_old_info.other_function_infos[i]; + const FunctionLambdaInfo *new_other_info = nullptr; + if (p_new_info != nullptr && p_new_info->other_function_infos.size() == p_old_info.other_function_infos.size()) { + // For now only attempt if the size is the same. + new_other_info = &p_new_info->other_function_infos[i]; + } + // Needs to be called on all old lambdas, even if there's no replacement. + _get_function_ptr_replacements(r_replacements, old_other_info, new_other_info); + } + for (const KeyValue<StringName, ScriptLambdaInfo> &old_kv : p_old_info.subclass_info) { + const ScriptLambdaInfo &old_subinfo = old_kv.value; + const ScriptLambdaInfo *new_subinfo = p_new_info != nullptr ? p_new_info->subclass_info.getptr(old_kv.key) : nullptr; + _get_function_ptr_replacements(r_replacements, old_subinfo, new_subinfo); + } +} + Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_script, bool p_keep_state) { err_line = -1; err_column = -1; @@ -3050,6 +3174,8 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri source = p_script->get_path(); + ScriptLambdaInfo old_lambda_info = _get_script_lambda_replacement_info(p_script); + // Create scripts for subclasses beforehand so they can be referenced make_scripts(p_script, root, p_keep_state); @@ -3065,6 +3191,27 @@ Error GDScriptCompiler::compile(const GDScriptParser *p_parser, GDScript *p_scri return err; } + ScriptLambdaInfo new_lambda_info = _get_script_lambda_replacement_info(p_script); + + 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; + } + } + } + } + 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 099bd00a2e..fd6b22f527 100644 --- a/modules/gdscript/gdscript_compiler.h +++ b/modules/gdscript/gdscript_compiler.h @@ -44,6 +44,34 @@ class GDScriptCompiler { HashSet<GDScript *> parsing_classes; GDScript *main_script = nullptr; + struct FunctionLambdaInfo { + GDScriptFunction *function; + GDScriptFunction *parent; + Ref<GDScript> script; + StringName name; + int line; + int index; + int depth; + //uint64_t code_hash; + //int code_size; + int capture_count; + int use_self; + int arg_count; + int default_arg_count; + //Vector<GDScriptDataType> argument_types; + //GDScriptDataType return_type; + Vector<FunctionLambdaInfo> sublambdas; + }; + + struct ScriptLambdaInfo { + Vector<FunctionLambdaInfo> implicit_initializer_info; + Vector<FunctionLambdaInfo> implicit_ready_info; + Vector<FunctionLambdaInfo> static_initializer_info; + HashMap<StringName, Vector<FunctionLambdaInfo>> member_function_infos; + Vector<FunctionLambdaInfo> other_function_infos; + HashMap<StringName, ScriptLambdaInfo> subclass_info; + }; + struct CodeGen { GDScript *script = nullptr; const GDScriptParser::ClassNode *class_node = nullptr; @@ -137,6 +165,13 @@ class GDScriptCompiler { Error _parse_setter_getter(GDScript *p_script, const GDScriptParser::ClassNode *p_class, const GDScriptParser::VariableNode *p_variable, bool p_is_setter); Error _prepare_compilation(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); Error _compile_class(GDScript *p_script, const GDScriptParser::ClassNode *p_class, bool p_keep_state); + FunctionLambdaInfo _get_function_replacement_info(GDScriptFunction *p_func, int p_index = -1, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr); + Vector<FunctionLambdaInfo> _get_function_lambda_replacement_info(GDScriptFunction *p_func, int p_depth = 0, GDScriptFunction *p_parent_func = nullptr); + ScriptLambdaInfo _get_script_lambda_replacement_info(GDScript *p_script); + bool _do_function_infos_match(const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info); + void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const FunctionLambdaInfo &p_old_info, const FunctionLambdaInfo *p_new_info); + void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const Vector<FunctionLambdaInfo> &p_old_infos, const Vector<FunctionLambdaInfo> *p_new_infos); + void _get_function_ptr_replacements(HashMap<GDScriptFunction *, GDScriptFunction *> &r_replacements, const ScriptLambdaInfo &p_old_info, const ScriptLambdaInfo *p_new_info); int err_line = 0; int err_column = 0; StringName source; diff --git a/modules/gdscript/gdscript_lambda_callable.cpp b/modules/gdscript/gdscript_lambda_callable.cpp index 9d0fce0928..547f5607d3 100644 --- a/modules/gdscript/gdscript_lambda_callable.cpp +++ b/modules/gdscript/gdscript_lambda_callable.cpp @@ -44,11 +44,18 @@ bool GDScriptLambdaCallable::compare_less(const CallableCustom *p_a, const Calla return p_a < p_b; } +bool GDScriptLambdaCallable::is_valid() const { + return CallableCustom::is_valid() && function != nullptr; +} + uint32_t GDScriptLambdaCallable::hash() const { return h; } String GDScriptLambdaCallable::get_as_text() const { + if (function == nullptr) { + return "<invalid lambda>"; + } if (function->get_name() != StringName()) { return function->get_name().operator String() + "(lambda)"; } @@ -74,6 +81,12 @@ StringName GDScriptLambdaCallable::get_method() const { void GDScriptLambdaCallable::call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const { int captures_amount = captures.size(); + if (function == nullptr) { + r_return_value = Variant(); + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } + if (captures_amount > 0) { Vector<const Variant *> args; args.resize(p_argcount + captures_amount); @@ -127,11 +140,19 @@ 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) { + 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) { @@ -144,11 +165,18 @@ bool GDScriptLambdaSelfCallable::compare_less(const CallableCustom *p_a, const C return p_a < p_b; } +bool GDScriptLambdaSelfCallable::is_valid() const { + return CallableCustom::is_valid() && function != nullptr; +} + uint32_t GDScriptLambdaSelfCallable::hash() const { return h; } String GDScriptLambdaSelfCallable::get_as_text() const { + if (function == nullptr) { + return "<invalid lambda>"; + } if (function->get_name() != StringName()) { return function->get_name().operator String() + "(lambda)"; } @@ -178,6 +206,12 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun int captures_amount = captures.size(); + if (function == nullptr) { + r_return_value = Variant(); + r_call_error.error = Callable::CallError::CALL_ERROR_INSTANCE_IS_NULL; + return; + } + if (captures_amount > 0) { Vector<const Variant *> args; args.resize(p_argcount + captures_amount); @@ -231,18 +265,36 @@ void GDScriptLambdaSelfCallable::call(const Variant **p_arguments, int p_argcoun } GDScriptLambdaSelfCallable::GDScriptLambdaSelfCallable(Ref<RefCounted> p_self, GDScriptFunction *p_function, const Vector<Variant> &p_captures) { + 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) { + 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 1c7a18fb9d..ee7d547544 100644 --- a/modules/gdscript/gdscript_lambda_callable.h +++ b/modules/gdscript/gdscript_lambda_callable.h @@ -31,12 +31,13 @@ #ifndef GDSCRIPT_LAMBDA_CALLABLE_H #define GDSCRIPT_LAMBDA_CALLABLE_H +#include "gdscript.h" + #include "core/object/ref_counted.h" #include "core/templates/vector.h" #include "core/variant/callable.h" #include "core/variant/variant.h" -class GDScript; class GDScriptFunction; class GDScriptInstance; @@ -44,6 +45,7 @@ class GDScriptLambdaCallable : public CallableCustom { GDScriptFunction *function = nullptr; Ref<GDScript> script; uint32_t h; + GDScript::UpdatableFuncPtrElement updatable_func_ptr_element; Vector<Variant> captures; @@ -51,6 +53,7 @@ class GDScriptLambdaCallable : public CallableCustom { static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); public: + bool is_valid() const override; uint32_t hash() const override; String get_as_text() const override; CompareEqualFunc get_compare_equal_func() const override; @@ -60,7 +63,7 @@ public: void call(const Variant **p_arguments, int p_argcount, Variant &r_return_value, Callable::CallError &r_call_error) const override; GDScriptLambdaCallable(Ref<GDScript> p_script, GDScriptFunction *p_function, const Vector<Variant> &p_captures); - virtual ~GDScriptLambdaCallable() = default; + virtual ~GDScriptLambdaCallable(); }; // Lambda callable that references a particular object, so it can use `self` in the body. @@ -69,6 +72,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom { 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; @@ -76,6 +80,7 @@ class GDScriptLambdaSelfCallable : public CallableCustom { static bool compare_less(const CallableCustom *p_a, const CallableCustom *p_b); public: + bool is_valid() const override; uint32_t hash() const override; String get_as_text() const override; CompareEqualFunc get_compare_equal_func() const override; @@ -85,7 +90,7 @@ public: 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() = default; + virtual ~GDScriptLambdaSelfCallable(); }; #endif // GDSCRIPT_LAMBDA_CALLABLE_H diff --git a/modules/gdscript/gdscript_rpc_callable.cpp b/modules/gdscript/gdscript_rpc_callable.cpp index a6d2388a91..df014d3cfe 100644 --- a/modules/gdscript/gdscript_rpc_callable.cpp +++ b/modules/gdscript/gdscript_rpc_callable.cpp @@ -73,6 +73,7 @@ void GDScriptRPCCallable::call(const Variant **p_arguments, int p_argcount, Vari } GDScriptRPCCallable::GDScriptRPCCallable(Object *p_object, const StringName &p_method) { + ERR_FAIL_NULL(p_object); object = p_object; method = p_method; h = method.hash(); diff --git a/modules/lightmapper_rd/lightmapper_rd.cpp b/modules/lightmapper_rd/lightmapper_rd.cpp index feb9a2274e..fe919953c1 100644 --- a/modules/lightmapper_rd/lightmapper_rd.cpp +++ b/modules/lightmapper_rd/lightmapper_rd.cpp @@ -124,7 +124,7 @@ void LightmapperRD::add_probe(const Vector3 &p_position) { probe_positions.push_back(probe); } -void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size) { +void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &p_triangles_sort, uint32_t p_grid_size) { int half_size = p_size / 2; for (int i = 0; i < 8; i++) { @@ -159,13 +159,69 @@ void LightmapperRD::_plot_triangle_into_triangle_index_list(int p_size, const Ve TriangleSort ts; ts.cell_index = n.x + (n.y * p_grid_size) + (n.z * p_grid_size * p_grid_size); ts.triangle_index = p_triangle_index; - triangles.push_back(ts); + ts.triangle_aabb.position = p_points[0]; + ts.triangle_aabb.size = Vector3(); + ts.triangle_aabb.expand_to(p_points[1]); + ts.triangle_aabb.expand_to(p_points[2]); + p_triangles_sort.push_back(ts); } else { - _plot_triangle_into_triangle_index_list(half_size, n, aabb, p_points, p_triangle_index, triangles, p_grid_size); + _plot_triangle_into_triangle_index_list(half_size, n, aabb, p_points, p_triangle_index, p_triangles_sort, p_grid_size); } } } +void LightmapperRD::_sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_cluster_index, uint32_t p_index_start, uint32_t p_count, LocalVector<TriangleSort> &p_triangle_sort, LocalVector<ClusterAABB> &p_cluster_aabb) { + if (p_count == 0) { + return; + } + + // Compute AABB for all triangles in the range. + SortArray<TriangleSort, TriangleSortAxis<0>> triangle_sorter_x; + SortArray<TriangleSort, TriangleSortAxis<1>> triangle_sorter_y; + SortArray<TriangleSort, TriangleSortAxis<2>> triangle_sorter_z; + AABB cluster_aabb = p_triangle_sort[p_index_start].triangle_aabb; + for (uint32_t i = 1; i < p_count; i++) { + cluster_aabb.merge_with(p_triangle_sort[p_index_start + i].triangle_aabb); + } + + if (p_count > p_cluster_size) { + int longest_axis_index = cluster_aabb.get_longest_axis_index(); + switch (longest_axis_index) { + case 0: + triangle_sorter_x.sort(&p_triangle_sort[p_index_start], p_count); + break; + case 1: + triangle_sorter_y.sort(&p_triangle_sort[p_index_start], p_count); + break; + case 2: + triangle_sorter_z.sort(&p_triangle_sort[p_index_start], p_count); + break; + default: + DEV_ASSERT(false && "Invalid axis returned by AABB."); + break; + } + + uint32_t left_cluster_count = next_power_of_2(p_count / 2); + left_cluster_count = MAX(left_cluster_count, p_cluster_size); + left_cluster_count = MIN(left_cluster_count, p_count); + _sort_triangle_clusters(p_cluster_size, p_cluster_index, p_index_start, left_cluster_count, p_triangle_sort, p_cluster_aabb); + + if (left_cluster_count < p_count) { + uint32_t cluster_index_right = p_cluster_index + (left_cluster_count / p_cluster_size); + _sort_triangle_clusters(p_cluster_size, cluster_index_right, p_index_start + left_cluster_count, p_count - left_cluster_count, p_triangle_sort, p_cluster_aabb); + } + } else { + ClusterAABB &aabb = p_cluster_aabb[p_cluster_index]; + Vector3 aabb_end = cluster_aabb.get_end(); + aabb.min_bounds[0] = cluster_aabb.position.x; + aabb.min_bounds[1] = cluster_aabb.position.y; + aabb.min_bounds[2] = cluster_aabb.position.z; + aabb.max_bounds[0] = aabb_end.x; + aabb.max_bounds[1] = aabb_end.y; + aabb.max_bounds[2] = aabb_end.z; + } +} + Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata) { Vector<Size2i> sizes; @@ -281,7 +337,7 @@ Lightmapper::BakeError LightmapperRD::_blit_meshes_into_atlas(int p_max_texture_ return BAKE_OK; } -void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &p_probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) { +void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &p_probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata) { HashMap<Vertex, uint32_t, VertexHash> vertex_map; //fill triangles array and vertex array @@ -433,31 +489,70 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i //sort it triangle_sort.sort(); + LocalVector<uint32_t> cluster_indices; + LocalVector<ClusterAABB> cluster_aabbs; Vector<uint32_t> triangle_indices; triangle_indices.resize(triangle_sort.size()); Vector<uint32_t> grid_indices; grid_indices.resize(grid_size * grid_size * grid_size * 2); memset(grid_indices.ptrw(), 0, grid_indices.size() * sizeof(uint32_t)); - Vector<bool> solid; - solid.resize(grid_size * grid_size * grid_size); - memset(solid.ptrw(), 0, solid.size() * sizeof(bool)); { - uint32_t *tiw = triangle_indices.ptrw(); + // Fill grid with cell indices. uint32_t last_cell = 0xFFFFFFFF; uint32_t *giw = grid_indices.ptrw(); - bool *solidw = solid.ptrw(); + uint32_t cluster_count = 0; + uint32_t solid_cell_count = 0; for (uint32_t i = 0; i < triangle_sort.size(); i++) { uint32_t cell = triangle_sort[i].cell_index; if (cell != last_cell) { - //cell changed, update pointer to indices - giw[cell * 2 + 1] = i; - solidw[cell] = true; + giw[cell * 2 + 1] = solid_cell_count; + solid_cell_count++; } - tiw[i] = triangle_sort[i].triangle_index; - giw[cell * 2]++; //update counter + + if ((giw[cell * 2] % p_cluster_size) == 0) { + // Add an extra cluster every time the triangle counter reaches a multiple of the cluster size. + cluster_count++; + } + + giw[cell * 2]++; last_cell = cell; } + + // Build fixed-size triangle clusters for all the cells to speed up the traversal. A cell can hold multiple clusters that each contain a fixed + // amount of triangles and an AABB. The tracer will check against the AABBs first to know whether it needs to visit the cell's triangles. + // + // The building algorithm will divide the triangles recursively contained inside each cell, sorting by the longest axis of the AABB on each step. + // + // - If the amount of triangles is less or equal to the cluster size, the AABB will be stored and the algorithm stops. + // + // - The division by two is increased to the next power of two of half the amount of triangles (with cluster size as the minimum value) to + // ensure the first half always fills the cluster. + + cluster_indices.resize(solid_cell_count * 2); + cluster_aabbs.resize(cluster_count); + + uint32_t i = 0; + uint32_t cluster_index = 0; + uint32_t solid_cell_index = 0; + uint32_t *tiw = triangle_indices.ptrw(); + while (i < triangle_sort.size()) { + cluster_indices[solid_cell_index * 2] = cluster_index; + cluster_indices[solid_cell_index * 2 + 1] = i; + + uint32_t cell = triangle_sort[i].cell_index; + uint32_t triangle_count = giw[cell * 2]; + uint32_t cell_cluster_count = (triangle_count + p_cluster_size - 1) / p_cluster_size; + _sort_triangle_clusters(p_cluster_size, cluster_index, i, triangle_count, triangle_sort, cluster_aabbs); + + for (uint32_t j = 0; j < triangle_count; j++) { + tiw[i + j] = triangle_sort[i + j].triangle_index; + } + + i += triangle_count; + cluster_index += cell_cluster_count; + solid_cell_index++; + } } #if 0 for (int i = 0; i < grid_size; i++) { @@ -507,7 +602,13 @@ void LightmapperRD::_create_acceleration_structures(RenderingDevice *rd, Size2i triangle_buffer = rd->storage_buffer_create(tb.size(), tb); Vector<uint8_t> tib = triangle_indices.to_byte_array(); - triangle_cell_indices_buffer = rd->storage_buffer_create(tib.size(), tib); + r_triangle_indices_buffer = rd->storage_buffer_create(tib.size(), tib); + + Vector<uint8_t> cib = cluster_indices.to_byte_array(); + r_cluster_indices_buffer = rd->storage_buffer_create(cib.size(), cib); + + Vector<uint8_t> cab = cluster_aabbs.to_byte_array(); + r_cluster_aabbs_buffer = rd->storage_buffer_create(cab.size(), cab); Vector<uint8_t> lb = lights.to_byte_array(); if (lb.size() == 0) { @@ -1020,24 +1121,29 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d RID vertex_buffer; RID triangle_buffer; RID lights_buffer; - RID triangle_cell_indices_buffer; + RID triangle_indices_buffer; + RID cluster_indices_buffer; + RID cluster_aabbs_buffer; RID grid_texture; RID seams_buffer; RID probe_positions_buffer; Vector<int> slice_seam_count; -#define FREE_BUFFERS \ - rd->free(bake_parameters_buffer); \ - rd->free(vertex_buffer); \ - rd->free(triangle_buffer); \ - rd->free(lights_buffer); \ - rd->free(triangle_cell_indices_buffer); \ - rd->free(grid_texture); \ - rd->free(seams_buffer); \ +#define FREE_BUFFERS \ + rd->free(bake_parameters_buffer); \ + rd->free(vertex_buffer); \ + rd->free(triangle_buffer); \ + rd->free(lights_buffer); \ + rd->free(triangle_indices_buffer); \ + rd->free(cluster_indices_buffer); \ + rd->free(cluster_aabbs_buffer); \ + rd->free(grid_texture); \ + rd->free(seams_buffer); \ rd->free(probe_positions_buffer); - _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_cell_indices_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata); + const uint32_t cluster_size = 16; + _create_acceleration_structures(rd, atlas_size, atlas_slices, bounds, grid_size, cluster_size, probe_positions, p_generate_probes, slice_triangle_count, slice_seam_count, vertex_buffer, triangle_buffer, lights_buffer, triangle_indices_buffer, cluster_indices_buffer, cluster_aabbs_buffer, probe_positions_buffer, grid_texture, seams_buffer, p_step_function, p_bake_userdata); // Create global bake parameters buffer. BakeParameters bake_parameters; @@ -1133,7 +1239,7 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d RD::Uniform u; u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; u.binding = 3; - u.append_id(triangle_cell_indices_buffer); + u.append_id(triangle_indices_buffer); base_uniforms.push_back(u); } { @@ -1185,6 +1291,20 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d u.append_id(sampler); base_uniforms.push_back(u); } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 11; + u.append_id(cluster_indices_buffer); + base_uniforms.push_back(u); + } + { + RD::Uniform u; + u.uniform_type = RD::UNIFORM_TYPE_STORAGE_BUFFER; + u.binding = 12; + u.append_id(cluster_aabbs_buffer); + base_uniforms.push_back(u); + } } RID raster_base_uniform = rd->uniform_set_create(base_uniforms, rasterize_shader, 0); @@ -1230,6 +1350,8 @@ LightmapperRD::BakeError LightmapperRD::bake(BakeQuality p_quality, bool p_use_d Ref<RDShaderFile> compute_shader; String defines = ""; + defines += "\n#define CLUSTER_SIZE " + uitos(cluster_size) + "\n"; + if (p_bake_sh) { defines += "\n#define USE_SH_LIGHTMAPS\n"; } diff --git a/modules/lightmapper_rd/lightmapper_rd.h b/modules/lightmapper_rd/lightmapper_rd.h index 8c1c4deba6..5414048ddc 100644 --- a/modules/lightmapper_rd/lightmapper_rd.h +++ b/modules/lightmapper_rd/lightmapper_rd.h @@ -192,6 +192,13 @@ class LightmapperRD : public Lightmapper { } }; + struct ClusterAABB { + float min_bounds[3]; + float pad0 = 0.0f; + float max_bounds[3]; + float pad1 = 0.0f; + }; + Vector<MeshInstance> mesh_instances; Vector<Light> lights; @@ -199,12 +206,22 @@ class LightmapperRD : public Lightmapper { struct TriangleSort { uint32_t cell_index = 0; uint32_t triangle_index = 0; + AABB triangle_aabb; + bool operator<(const TriangleSort &p_triangle_sort) const { return cell_index < p_triangle_sort.cell_index; //sorting by triangle index in this case makes no sense } }; + template <int T> + struct TriangleSortAxis { + bool operator()(const TriangleSort &p_a, const TriangleSort &p_b) const { + return p_a.triangle_aabb.get_center()[T] < p_b.triangle_aabb.get_center()[T]; + } + }; + void _plot_triangle_into_triangle_index_list(int p_size, const Vector3i &p_ofs, const AABB &p_bounds, const Vector3 p_points[3], uint32_t p_triangle_index, LocalVector<TriangleSort> &triangles, uint32_t p_grid_size); + void _sort_triangle_clusters(uint32_t p_cluster_size, uint32_t p_cluster_index, uint32_t p_index_start, uint32_t p_count, LocalVector<TriangleSort> &p_triangle_sort, LocalVector<ClusterAABB> &p_cluster_aabb); struct RasterPushConstant { float atlas_size[2] = {}; @@ -250,7 +267,7 @@ class LightmapperRD : public Lightmapper { }; BakeError _blit_meshes_into_atlas(int p_max_texture_size, Vector<Ref<Image>> &albedo_images, Vector<Ref<Image>> &emission_images, AABB &bounds, Size2i &atlas_size, int &atlas_slices, BakeStepFunc p_step_function, void *p_bake_userdata); - void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &triangle_cell_indices_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata); + void _create_acceleration_structures(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, AABB &bounds, int grid_size, uint32_t p_cluster_size, Vector<Probe> &probe_positions, GenerateProbes p_generate_probes, Vector<int> &slice_triangle_count, Vector<int> &slice_seam_count, RID &vertex_buffer, RID &triangle_buffer, RID &lights_buffer, RID &r_triangle_indices_buffer, RID &r_cluster_indices_buffer, RID &r_cluster_aabbs_buffer, RID &probe_positions_buffer, RID &grid_texture, RID &seams_buffer, BakeStepFunc p_step_function, void *p_bake_userdata); void _raster_geometry(RenderingDevice *rd, Size2i atlas_size, int atlas_slices, int grid_size, AABB bounds, float p_bias, Vector<int> slice_triangle_count, RID position_tex, RID unocclude_tex, RID normal_tex, RID raster_depth_buffer, RID rasterize_shader, RID raster_base_uniform); BakeError _dilate(RenderingDevice *rd, Ref<RDShaderFile> &compute_shader, RID &compute_base_uniform_set, PushConstant &push_constant, RID &source_light_tex, RID &dest_light_tex, const Size2i &atlas_size, int atlas_slices); diff --git a/modules/lightmapper_rd/lm_common_inc.glsl b/modules/lightmapper_rd/lm_common_inc.glsl index c91f06d0f3..98d11b9e69 100644 --- a/modules/lightmapper_rd/lm_common_inc.glsl +++ b/modules/lightmapper_rd/lm_common_inc.glsl @@ -42,15 +42,22 @@ struct Triangle { uint pad1; }; +struct ClusterAABB { + vec3 min_bounds; + uint pad0; + vec3 max_bounds; + uint pad1; +}; + layout(set = 0, binding = 2, std430) restrict readonly buffer Triangles { Triangle data[]; } triangles; -layout(set = 0, binding = 3, std430) restrict readonly buffer GridIndices { +layout(set = 0, binding = 3, std430) restrict readonly buffer TriangleIndices { uint data[]; } -grid_indices; +triangle_indices; #define LIGHT_TYPE_DIRECTIONAL 0 #define LIGHT_TYPE_OMNI 1 @@ -104,6 +111,16 @@ layout(set = 0, binding = 9) uniform texture2DArray emission_tex; layout(set = 0, binding = 10) uniform sampler linear_sampler; +layout(set = 0, binding = 11, std430) restrict readonly buffer ClusterIndices { + uint data[]; +} +cluster_indices; + +layout(set = 0, binding = 12, std430) restrict readonly buffer ClusterAABBs { + ClusterAABB data[]; +} +cluster_aabbs; + // Fragment action constants const uint FA_NONE = 0; const uint FA_SMOOTHEN_POSITION = 1; diff --git a/modules/lightmapper_rd/lm_compute.glsl b/modules/lightmapper_rd/lm_compute.glsl index 572e6d55d8..a2a480043a 100644 --- a/modules/lightmapper_rd/lm_compute.glsl +++ b/modules/lightmapper_rd/lm_compute.glsl @@ -119,6 +119,17 @@ const uint RAY_FRONT = 1; const uint RAY_BACK = 2; const uint RAY_ANY = 3; +bool ray_box_test(vec3 p_from, vec3 p_inv_dir, vec3 p_box_min, vec3 p_box_max) { + vec3 t0 = (p_box_min - p_from) * p_inv_dir; + vec3 t1 = (p_box_max - p_from) * p_inv_dir; + vec3 tmin = min(t0, t1), tmax = max(t0, t1); + return max(tmin.x, max(tmin.y, tmin.z)) <= min(tmax.x, min(tmax.y, tmax.z)); +} + +#if CLUSTER_SIZE > 32 +#define CLUSTER_TRIANGLE_ITERATION +#endif + uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out vec3 r_normal, out uint r_triangle, out vec3 r_barycentric) { // World coordinates. vec3 rel = p_to - p_from; @@ -142,60 +153,106 @@ uint trace_ray(vec3 p_from, vec3 p_to, bool p_any_hit, out float r_distance, out uint iters = 0; while (all(greaterThanEqual(icell, ivec3(0))) && all(lessThan(icell, ivec3(bake_params.grid_size))) && (iters < 1000)) { uvec2 cell_data = texelFetch(usampler3D(grid, linear_sampler), icell, 0).xy; - if (cell_data.x > 0) { //triangles here + uint triangle_count = cell_data.x; + if (triangle_count > 0) { uint hit = RAY_MISS; float best_distance = 1e20; - - for (uint i = 0; i < cell_data.x; i++) { - uint tidx = grid_indices.data[cell_data.y + i]; - - // Ray-Box test. - Triangle triangle = triangles.data[tidx]; - vec3 t0 = (triangle.min_bounds - p_from) * inv_dir; - vec3 t1 = (triangle.max_bounds - p_from) * inv_dir; - vec3 tmin = min(t0, t1), tmax = max(t0, t1); - - if (max(tmin.x, max(tmin.y, tmin.z)) > min(tmax.x, min(tmax.y, tmax.z))) { - continue; // Ray-Box test failed. - } - - // Prepare triangle vertices. - vec3 vtx0 = vertices.data[triangle.indices.x].position; - vec3 vtx1 = vertices.data[triangle.indices.y].position; - vec3 vtx2 = vertices.data[triangle.indices.z].position; - vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2))); - bool backface = dot(normal, dir) >= 0.0; - float distance; - vec3 barycentric; - if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) { - if (p_any_hit) { - // Return early if any hit was requested. - return RAY_ANY; + uint cluster_start = cluster_indices.data[cell_data.y * 2]; + uint cell_triangle_start = cluster_indices.data[cell_data.y * 2 + 1]; + uint cluster_count = (triangle_count + CLUSTER_SIZE - 1) / CLUSTER_SIZE; + uint cluster_base_index = 0; + while (cluster_base_index < cluster_count) { + // To minimize divergence, all Ray-AABB tests on the clusters contained in the cell are performed + // before checking against the triangles. We do this 32 clusters at a time and store the intersected + // clusters on each bit of the 32-bit integer. + uint cluster_test_count = min(32, cluster_count - cluster_base_index); + uint cluster_hits = 0; + for (uint i = 0; i < cluster_test_count; i++) { + uint cluster_index = cluster_start + cluster_base_index + i; + ClusterAABB cluster_aabb = cluster_aabbs.data[cluster_index]; + if (ray_box_test(p_from, inv_dir, cluster_aabb.min_bounds, cluster_aabb.max_bounds)) { + cluster_hits |= (1 << i); } + } - vec3 position = p_from + dir * distance; - vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size; - if (icell != ivec3(hit_cell)) { - // It's possible for the ray to hit a triangle in a position outside the bounds of the cell - // if it's large enough to cover multiple ones. The hit must be ignored if this is the case. - continue; - } + // Check the triangles in any of the clusters that were intersected by toggling off the bits in the + // 32-bit integer counter until no bits are left. + while (cluster_hits > 0) { + uint cluster_index = findLSB(cluster_hits); + cluster_hits &= ~(1 << cluster_index); + cluster_index += cluster_base_index; + + // Do the same divergence execution trick with triangles as well. + uint triangle_base_index = 0; +#ifdef CLUSTER_TRIANGLE_ITERATION + while (triangle_base_index < triangle_count) +#endif + { + uint triangle_start_index = cell_triangle_start + cluster_index * CLUSTER_SIZE + triangle_base_index; + uint triangle_test_count = min(CLUSTER_SIZE, triangle_count - triangle_base_index); + uint triangle_hits = 0; + for (uint i = 0; i < triangle_test_count; i++) { + uint triangle_index = triangle_indices.data[triangle_start_index + i]; + if (ray_box_test(p_from, inv_dir, triangles.data[triangle_index].min_bounds, triangles.data[triangle_index].max_bounds)) { + triangle_hits |= (1 << i); + } + } - if (!backface) { - // The case of meshes having both a front and back face in the same plane is more common than expected. - // If this is a front-face, bias it closer to the ray origin, so it always wins over the back-face. - distance = max(bake_params.bias, distance - bake_params.bias); - } + while (triangle_hits > 0) { + uint cluster_triangle_index = findLSB(triangle_hits); + triangle_hits &= ~(1 << cluster_triangle_index); + cluster_triangle_index += triangle_start_index; + + uint triangle_index = triangle_indices.data[cluster_triangle_index]; + Triangle triangle = triangles.data[triangle_index]; + + // Gather the triangle vertex positions. + vec3 vtx0 = vertices.data[triangle.indices.x].position; + vec3 vtx1 = vertices.data[triangle.indices.y].position; + vec3 vtx2 = vertices.data[triangle.indices.z].position; + vec3 normal = -normalize(cross((vtx0 - vtx1), (vtx0 - vtx2))); + bool backface = dot(normal, dir) >= 0.0; + float distance; + vec3 barycentric; + if (ray_hits_triangle(p_from, dir, rel_len, vtx0, vtx1, vtx2, distance, barycentric)) { + if (p_any_hit) { + // Return early if any hit was requested. + return RAY_ANY; + } + + vec3 position = p_from + dir * distance; + vec3 hit_cell = (position - bake_params.to_cell_offset) * bake_params.to_cell_size; + if (icell != ivec3(hit_cell)) { + // It's possible for the ray to hit a triangle in a position outside the bounds of the cell + // if it's large enough to cover multiple ones. The hit must be ignored if this is the case. + continue; + } + + if (!backface) { + // The case of meshes having both a front and back face in the same plane is more common than + // expected, so if this is a front-face, bias it closer to the ray origin, so it always wins + // over the back-face. + distance = max(bake_params.bias, distance - bake_params.bias); + } + + if (distance < best_distance) { + hit = backface ? RAY_BACK : RAY_FRONT; + best_distance = distance; + r_distance = distance; + r_normal = normal; + r_triangle = triangle_index; + r_barycentric = barycentric; + } + } + } - if (distance < best_distance) { - hit = backface ? RAY_BACK : RAY_FRONT; - best_distance = distance; - r_distance = distance; - r_normal = normal; - r_triangle = tidx; - r_barycentric = barycentric; +#ifdef CLUSTER_TRIANGLE_ITERATION + triangle_base_index += CLUSTER_SIZE; +#endif } } + + cluster_base_index += 32; } if (hit != RAY_MISS) { diff --git a/modules/mono/build_scripts/build_assemblies.py b/modules/mono/build_scripts/build_assemblies.py index 580f51c973..90a517cc40 100755 --- a/modules/mono/build_scripts/build_assemblies.py +++ b/modules/mono/build_scripts/build_assemblies.py @@ -316,9 +316,35 @@ def generate_sdk_package_versions(): f.write(props) f.close() + # Also write the versioned docs URL to a constant for the Source Generators. + + constants = """namespace Godot.SourceGenerators +{{ + partial class Common + {{ + public const string VersionDocsUrl = "https://docs.godotengine.org/en/{docs_branch}"; + }} +}} +""".format( + **version_info + ) + + generators_dir = os.path.join( + dirname(script_path), + "editor", + "Godot.NET.Sdk", + "Godot.SourceGenerators", + "Generated", + ) + os.makedirs(generators_dir, exist_ok=True) + + with open(os.path.join(generators_dir, "Common.Constants.cs"), "w") as f: + f.write(constants) + f.close() + def build_all(msbuild_tool, module_dir, output_dir, godot_platform, dev_debug, push_nupkgs_local, precision): - # Generate SdkPackageVersions.props + # Generate SdkPackageVersions.props and VersionDocsUrl constant generate_sdk_package_versions() # Godot API diff --git a/modules/mono/editor/Godot.NET.Sdk/.gitignore b/modules/mono/editor/Godot.NET.Sdk/.gitignore new file mode 100644 index 0000000000..55ec4bcc64 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/.gitignore @@ -0,0 +1,2 @@ +# Generated sources directories +Godot.SourceGenerators/Generated diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs new file mode 100644 index 0000000000..3021f57115 --- /dev/null +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators.Sample/NestedClass.cs @@ -0,0 +1,22 @@ +using System; + +namespace Godot.SourceGenerators.Sample; + +public partial class NestedClass : GodotObject +{ + public partial class NestedClass2 : GodotObject + { + public partial class NestedClass3 : GodotObject + { + [Signal] + public delegate void MySignalEventHandler(string str, int num); + + [Export] private String field_String = "foo"; + [Export] private String property_String { get; set; } = "foo"; + + private void Method() + { + } + } + } +} diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs index 72614dd7e0..df35091596 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/Common.cs @@ -5,8 +5,10 @@ using Microsoft.CodeAnalysis.Diagnostics; namespace Godot.SourceGenerators { - public static class Common + public static partial class Common { + private static readonly string _helpLinkFormat = $"{VersionDocsUrl}/tutorials/scripting/c_sharp/diagnostics/{{0}}.html"; + public static void ReportNonPartialGodotScriptClass( GeneratorExecutionContext context, ClassDeclarationSyntax cds, INamedTypeSymbol symbol @@ -14,9 +16,9 @@ namespace Godot.SourceGenerators { string message = "Missing partial modifier on declaration of type '" + - $"{symbol.FullQualifiedNameOmitGlobal()}' which is a subclass of '{GodotClasses.GodotObject}'"; + $"{symbol.FullQualifiedNameOmitGlobal()}' that derives from '{GodotClasses.GodotObject}'"; - string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' " + + string description = $"{message}. Classes that derive from '{GodotClasses.GodotObject}' " + "must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( @@ -26,7 +28,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0001")), cds.GetLocation(), cds.SyntaxTree.FilePath)); } @@ -46,9 +49,9 @@ namespace Godot.SourceGenerators string message = $"Missing partial modifier on declaration of type '{fullQualifiedName}', " + - $"which contains one or more subclasses of '{GodotClasses.GodotObject}'"; + $"which contains nested classes that derive from '{GodotClasses.GodotObject}'"; - string description = $"{message}. Subclasses of '{GodotClasses.GodotObject}' and their " + + string description = $"{message}. Classes that derive from '{GodotClasses.GodotObject}' and their " + "containing types must be declared with the partial modifier."; context.ReportDiagnostic(Diagnostic.Create( @@ -58,7 +61,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0002")), outerTypeDeclSyntax.GetLocation(), outerTypeDeclSyntax.SyntaxTree.FilePath)); } @@ -85,7 +89,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0101")), location, location?.SourceTree?.FilePath)); } @@ -111,7 +116,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0102")), location, location?.SourceTree?.FilePath)); } @@ -139,7 +145,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD1003")), location, location?.SourceTree?.FilePath)); } @@ -163,7 +170,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0104")), location, location?.SourceTree?.FilePath)); } @@ -189,7 +197,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0105")), location, location?.SourceTree?.FilePath)); } @@ -215,7 +224,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0106")), location, location?.SourceTree?.FilePath)); } @@ -240,7 +250,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0201")), location, location?.SourceTree?.FilePath)); } @@ -264,7 +275,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0202")), location, location?.SourceTree?.FilePath)); } @@ -288,7 +300,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0203")), location, location?.SourceTree?.FilePath)); } @@ -300,7 +313,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument."); + "The generic type argument must be a Variant compatible type. Use a Variant compatible type as the generic type argument.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0301")); public static void ReportGenericTypeArgumentMustBeVariant( SyntaxNodeAnalysisContext context, @@ -319,7 +333,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0301")), typeArgumentSyntax.GetLocation(), typeArgumentSyntax.SyntaxTree.FilePath)); } @@ -331,7 +346,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - "The generic type argument must be a Variant type. Use a Variant type as the generic type argument."); + "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0302")); public static void ReportGenericTypeParameterMustBeVariantAnnotated( SyntaxNodeAnalysisContext context, @@ -349,7 +365,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0302")), typeArgumentSyntax.GetLocation(), typeArgumentSyntax.SyntaxTree.FilePath)); } @@ -361,7 +378,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - "The generic type argument must be a Variant type. Use a Variant type as the generic type argument."); + "The generic type argument must be a Variant type. Use a Variant type as the generic type argument.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0303")); public static void ReportTypeArgumentParentSymbolUnhandled( SyntaxNodeAnalysisContext context, @@ -380,7 +398,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0303")), typeArgumentSyntax.GetLocation(), typeArgumentSyntax.SyntaxTree.FilePath)); } @@ -388,11 +407,12 @@ namespace Godot.SourceGenerators public static readonly DiagnosticDescriptor GlobalClassMustDeriveFromGodotObjectRule = new DiagnosticDescriptor(id: "GD0401", title: "The class must derive from GodotObject or a derived class", - messageFormat: "The class '{0}' must derive from GodotObject or a derived class.", + messageFormat: "The class '{0}' must derive from GodotObject or a derived class", category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute."); + "The class must derive from GodotObject or a derived class. Change the base class or remove the '[GlobalClass]' attribute.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0401")); public static void ReportGlobalClassMustDeriveFromGodotObject( SyntaxNodeAnalysisContext context, @@ -410,7 +430,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0401")), classSyntax.GetLocation(), classSyntax.SyntaxTree.FilePath)); } @@ -422,7 +443,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute."); + "The class must be a non-generic type. Remove the generic arguments or the '[GlobalClass]' attribute.", + helpLinkUri: string.Format(_helpLinkFormat, "GD0401")); public static void ReportGlobalClassMustNotBeGeneric( SyntaxNodeAnalysisContext context, @@ -440,7 +462,8 @@ namespace Godot.SourceGenerators category: "Usage", DiagnosticSeverity.Error, isEnabledByDefault: true, - description), + description, + helpLinkUri: string.Format(_helpLinkFormat, "GD0402")), classSyntax.GetLocation(), classSyntax.SyntaxTree.FilePath)); } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs index 7b643914bb..7232e4d7d7 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptMethodsGenerator.cs @@ -105,16 +105,20 @@ namespace Godot.SourceGenerators if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs index 219ab7aa44..de44ada6de 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertiesGenerator.cs @@ -91,16 +91,20 @@ namespace Godot.SourceGenerators if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs index 4df16d05f0..fc0bfbf084 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptPropertyDefValGenerator.cs @@ -88,16 +88,20 @@ namespace Godot.SourceGenerators if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs index 9de99414b6..0bc58c2b47 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSerializationGenerator.cs @@ -91,16 +91,20 @@ namespace Godot.SourceGenerators if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs index 8f2774d5ae..5409d1a961 100644 --- a/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs +++ b/modules/mono/editor/Godot.NET.Sdk/Godot.SourceGenerators/ScriptSignalsGenerator.cs @@ -100,16 +100,20 @@ namespace Godot.SourceGenerators if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs index 6b000cc89b..750e11777d 100644 --- a/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs +++ b/modules/mono/glue/GodotSharp/Godot.SourceGenerators.Internal/UnmanagedCallbacksGenerator.cs @@ -128,16 +128,20 @@ using Godot.NativeInterop; if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } @@ -303,16 +307,20 @@ using Godot.NativeInterop; if (isInnerClass) { var containingType = symbol.ContainingType; + AppendPartialContainingTypeDeclarations(containingType); - while (containingType != null) + void AppendPartialContainingTypeDeclarations(INamedTypeSymbol? containingType) { + if (containingType == null) + return; + + AppendPartialContainingTypeDeclarations(containingType.ContainingType); + source.Append("partial "); source.Append(containingType.GetDeclarationKeyword()); source.Append(" "); source.Append(containingType.NameWithTypeParameters()); source.Append("\n{\n"); - - containingType = containingType.ContainingType; } } diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs index a7712db737..b4f7b82f60 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Basis.cs @@ -239,7 +239,7 @@ namespace Godot /// <summary> /// Returns the basis's rotation in the form of Euler angles. - /// The Euler order depends on the [param order] parameter, + /// The Euler order depends on the <paramref name="order"/> parameter, /// by default it uses the YXZ convention: when decomposing, /// first Z, then X, and Y last. The returned vector contains /// the rotation angles in the format (X angle, Y angle, Z angle). @@ -1037,10 +1037,11 @@ namespace Godot } /// <summary> - /// Returns a Vector3 transformed (multiplied) by the transposed basis matrix. - /// - /// Note: This results in a multiplication by the inverse of the - /// basis matrix only if it represents a rotation-reflection. + /// Returns a Vector3 transformed (multiplied) by the inverse basis matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>vector * basis</c> is equivalent to <c>basis.Transposed() * vector</c>. See <see cref="Transposed"/>. + /// For transforming by inverse of a non-orthonormal basis (e.g. with scaling) <c>basis.Inverse() * vector</c> can be used instead. See <see cref="Inverse"/>. /// </summary> /// <param name="vector">A Vector3 to inversely transform.</param> /// <param name="basis">The basis matrix transformation to apply.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs index a80d202ef2..155e90ff24 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Projection.cs @@ -905,7 +905,8 @@ namespace Godot } /// <summary> - /// Returns a Vector4 transformed (multiplied) by the inverse projection. + /// Returns a Vector4 transformed (multiplied) by the transpose of the projection. + /// For transforming by inverse of a projection <c>projection.Inverse() * vector</c> can be used instead. See <see cref="Inverse"/>. /// </summary> /// <param name="proj">The projection to apply.</param> /// <param name="vector">A Vector4 to transform.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs index 39e1b7e4b8..3d45913586 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Quaternion.cs @@ -644,6 +644,7 @@ namespace Godot /// <summary> /// Returns a Vector3 rotated (multiplied) by the inverse quaternion. + /// <c>vector * quaternion</c> is equivalent to <c>quaternion.Inverse() * vector</c>. See <see cref="Inverse"/>. /// </summary> /// <param name="vector">A Vector3 to inversely rotate.</param> /// <param name="quaternion">The quaternion to rotate by.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs index 0e3e54a0c2..386f587464 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform2D.cs @@ -126,7 +126,7 @@ namespace Godot /// <summary> /// Returns the inverse of the transform, under the assumption that - /// the transformation is composed of rotation, scaling, and translation. + /// the basis is invertible (must have non-zero determinant). /// </summary> /// <seealso cref="Inverse"/> /// <returns>The inverse transformation matrix.</returns> @@ -180,11 +180,12 @@ namespace Godot } /// <summary> - /// Returns a vector transformed (multiplied) by the inverse basis matrix. + /// Returns a vector transformed (multiplied) by the inverse basis matrix, + /// under the assumption that the basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). /// This method does not account for translation (the <see cref="Origin"/> vector). - /// - /// Note: This results in a multiplication by the inverse of the - /// basis matrix only if it represents a rotation-reflection. + /// <c>transform.BasisXformInv(vector)</c> is equivalent to <c>transform.Inverse().BasisXform(vector)</c>. See <see cref="Inverse"/>. + /// For non-orthonormal transforms (e.g. with scaling) <c>transform.AffineInverse().BasisXform(vector)</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <seealso cref="BasisXform(Vector2)"/> /// <param name="v">A vector to inversely transform.</param> @@ -213,8 +214,9 @@ namespace Godot /// <summary> /// Returns the inverse of the transform, under the assumption that - /// the transformation is composed of rotation and translation - /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling). + /// the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for + /// non-orthonormal transforms (e.g. with scaling). /// </summary> /// <returns>The inverse matrix.</returns> public readonly Transform2D Inverse() @@ -480,7 +482,11 @@ namespace Godot } /// <summary> - /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix. + /// Returns a Vector2 transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="vector">A Vector2 to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> @@ -507,7 +513,11 @@ namespace Godot } /// <summary> - /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix. + /// Returns a Rect2 transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>rect * transform</c> is equivalent to <c>transform.Inverse() * rect</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * rect</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="rect">A Rect2 to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> @@ -541,7 +551,11 @@ namespace Godot } /// <summary> - /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix. + /// Returns a copy of the given Vector2[] transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="array">A Vector2[] to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs index 7b27071df1..2d09259dcb 100644 --- a/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs +++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/Transform3D.cs @@ -105,7 +105,7 @@ namespace Godot /// <summary> /// Returns the inverse of the transform, under the assumption that - /// the transformation is composed of rotation, scaling, and translation. + /// the basis is invertible (must have non-zero determinant). /// </summary> /// <seealso cref="Inverse"/> /// <returns>The inverse transformation matrix.</returns> @@ -144,8 +144,9 @@ namespace Godot /// <summary> /// Returns the inverse of the transform, under the assumption that - /// the transformation is composed of rotation and translation - /// (no scaling, use <see cref="AffineInverse"/> for transforms with scaling). + /// the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). Use <see cref="AffineInverse"/> for + /// non-orthonormal transforms (e.g. with scaling). /// </summary> /// <returns>The inverse matrix.</returns> public readonly Transform3D Inverse() @@ -426,10 +427,11 @@ namespace Godot } /// <summary> - /// Returns a Vector3 transformed (multiplied) by the transposed transformation matrix. - /// - /// Note: This results in a multiplication by the inverse of the - /// transformation matrix only if it represents a rotation-reflection. + /// Returns a Vector3 transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>vector * transform</c> is equivalent to <c>transform.Inverse() * vector</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * vector</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="vector">A Vector3 to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> @@ -482,7 +484,11 @@ namespace Godot } /// <summary> - /// Returns an AABB transformed (multiplied) by the inverse transformation matrix. + /// Returns an AABB transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>aabb * transform</c> is equivalent to <c>transform.Inverse() * aabb</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * aabb</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="aabb">An AABB to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> @@ -523,6 +529,7 @@ namespace Godot /// <summary> /// Returns a Plane transformed (multiplied) by the inverse transformation matrix. + /// <c>plane * transform</c> is equivalent to <c>transform.AffineInverse() * plane</c>. See <see cref="AffineInverse"/>. /// </summary> /// <param name="plane">A Plane to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> @@ -568,7 +575,11 @@ namespace Godot } /// <summary> - /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix. + /// Returns a copy of the given Vector3[] transformed (multiplied) by the inverse transformation matrix, + /// under the assumption that the transformation basis is orthonormal (i.e. rotation/reflection + /// is fine, scaling/skew is not). + /// <c>array * transform</c> is equivalent to <c>transform.Inverse() * array</c>. See <see cref="Inverse"/>. + /// For transforming by inverse of an affine transformation (e.g. with scaling) <c>transform.AffineInverse() * array</c> can be used instead. See <see cref="AffineInverse"/>. /// </summary> /// <param name="array">A Vector3[] to inversely transform.</param> /// <param name="transform">The transformation to apply.</param> diff --git a/modules/navigation/nav_region.cpp b/modules/navigation/nav_region.cpp index 3675aae518..09697c7be0 100644 --- a/modules/navigation/nav_region.cpp +++ b/modules/navigation/nav_region.cpp @@ -125,11 +125,11 @@ void NavRegion::update_polygons() { #ifdef DEBUG_ENABLED if (!Math::is_equal_approx(double(map->get_cell_size()), double(mesh->get_cell_size()))) { - ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_size()), double(mesh->get_cell_size()))); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_size` of %s while assigned to a navigation map set to a `cell_size` of %s. The cell size for navigation maps can be changed by using the NavigationServer map_set_cell_size() function. The cell size for default navigation maps can also be changed in the ProjectSettings.", double(mesh->get_cell_size()), double(map->get_cell_size()))); } if (!Math::is_equal_approx(double(map->get_cell_height()), double(mesh->get_cell_height()))) { - ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(map->get_cell_height()), double(mesh->get_cell_height()))); + ERR_PRINT_ONCE(vformat("Navigation map synchronization error. Attempted to update a navigation region with a navigation mesh that uses a `cell_height` of %s while assigned to a navigation map set to a `cell_height` of %s. The cell height for navigation maps can be changed by using the NavigationServer map_set_cell_height() function. The cell height for default navigation maps can also be changed in the ProjectSettings.", double(mesh->get_cell_height()), double(map->get_cell_height()))); } if (map && Math::rad_to_deg(map->get_up().angle_to(transform.basis.get_column(1))) >= 90.0f) { diff --git a/modules/openxr/SCsub b/modules/openxr/SCsub index 73a3723ea4..2ccfe46062 100644 --- a/modules/openxr/SCsub +++ b/modules/openxr/SCsub @@ -71,6 +71,7 @@ if env["builtin_openxr"]: # Build the engine using object files khrloader_obj = [] env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table.c") + env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/xr_generated_dispatch_table_core.c") env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/filesystem_utils.cpp") env_thirdparty.add_source_files(khrloader_obj, thirdparty_dir + "/src/common/object_info.cpp") diff --git a/platform/android/java/app/config.gradle b/platform/android/java/app/config.gradle index e7c06628c8..a91e7bc7ce 100644 --- a/platform/android/java/app/config.gradle +++ b/platform/android/java/app/config.gradle @@ -9,7 +9,7 @@ ext.versions = [ kotlinVersion : '1.7.0', fragmentVersion : '1.3.6', nexusPublishVersion: '1.1.0', - javaVersion : 11, + javaVersion : 17, // Also update 'platform/android/detect.py#get_ndk_version()' when this is updated. ndkVersion : '23.2.8568313' diff --git a/platform/ios/app_delegate.mm b/platform/ios/app_delegate.mm index 38846e7508..8a16f8fcc1 100644 --- a/platform/ios/app_delegate.mm +++ b/platform/ios/app_delegate.mm @@ -51,6 +51,15 @@ extern void ios_finish(); @implementation AppDelegate +enum { + SESSION_CATEGORY_AMBIENT, + SESSION_CATEGORY_MULTI_ROUTE, + SESSION_CATEGORY_PLAY_AND_RECORD, + SESSION_CATEGORY_PLAYBACK, + SESSION_CATEGORY_RECORD, + SESSION_CATEGORY_SOLO_AMBIENT +}; + static ViewController *mainViewController = nil; + (ViewController *)viewController { @@ -92,8 +101,28 @@ static ViewController *mainViewController = nil; mainViewController = viewController; - // prevent to stop music in another background app - [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryAmbient error:nil]; + int sessionCategorySetting = GLOBAL_GET("audio/general/ios/session_category"); + + // Initialize with default Ambient category. + AVAudioSessionCategory category = AVAudioSessionCategoryAmbient; + + if (sessionCategorySetting == SESSION_CATEGORY_MULTI_ROUTE) { + category = AVAudioSessionCategoryMultiRoute; + } else if (sessionCategorySetting == SESSION_CATEGORY_PLAY_AND_RECORD) { + category = AVAudioSessionCategoryPlayAndRecord; + } else if (sessionCategorySetting == SESSION_CATEGORY_PLAYBACK) { + category = AVAudioSessionCategoryPlayback; + } else if (sessionCategorySetting == SESSION_CATEGORY_RECORD) { + category = AVAudioSessionCategoryRecord; + } else if (sessionCategorySetting == SESSION_CATEGORY_SOLO_AMBIENT) { + category = AVAudioSessionCategorySoloAmbient; + } + + if (GLOBAL_GET("audio/general/ios/mix_with_others")) { + [[AVAudioSession sharedInstance] setCategory:category withOptions:AVAudioSessionCategoryOptionMixWithOthers error:nil]; + } else { + [[AVAudioSession sharedInstance] setCategory:category error:nil]; + } return YES; } diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp index fe81da76d3..1660101598 100644 --- a/platform/linuxbsd/x11/display_server_x11.cpp +++ b/platform/linuxbsd/x11/display_server_x11.cpp @@ -39,6 +39,7 @@ #include "core/math/math_funcs.h" #include "core/string/print_string.h" #include "core/string/ustring.h" +#include "drivers/png/png_driver_common.h" #include "main/main.h" #include "scene/resources/atlas_texture.h" @@ -519,7 +520,7 @@ Bool DisplayServerX11::_predicate_clipboard_selection(Display *display, XEvent * } Bool DisplayServerX11::_predicate_clipboard_incr(Display *display, XEvent *event, XPointer arg) { - if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue) { + if (event->type == PropertyNotify && event->xproperty.state == PropertyNewValue && event->xproperty.atom == *(Atom *)arg) { return True; } else { return False; @@ -593,7 +594,7 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A // Non-blocking wait for next event and remove it from the queue. XEvent ev; - while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, nullptr)) { + while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, (XPointer)&selection)) { result = XGetWindowProperty(x11_display, x11_window, selection, // selection type 0, LONG_MAX, // offset - len @@ -664,6 +665,74 @@ String DisplayServerX11::_clipboard_get_impl(Atom p_source, Window x11_window, A return ret; } +Atom DisplayServerX11::_clipboard_get_image_target(Atom p_source, Window x11_window) const { + Atom target = XInternAtom(x11_display, "TARGETS", 0); + Atom png = XInternAtom(x11_display, "image/png", 0); + Atom *valid_targets = nullptr; + unsigned long atom_count = 0; + + Window selection_owner = XGetSelectionOwner(x11_display, p_source); + if (selection_owner != None) { + // Block events polling while processing selection events. + MutexLock mutex_lock(events_mutex); + + Atom selection = XA_PRIMARY; + XConvertSelection(x11_display, p_source, target, selection, x11_window, CurrentTime); + + XFlush(x11_display); + + // Blocking wait for predicate to be True and remove the event from the queue. + XEvent event; + XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window); + // Do not get any data, see how much data is there. + Atom type; + int format, result; + unsigned long len, bytes_left, dummy; + XGetWindowProperty(x11_display, x11_window, + selection, // Tricky.. + 0, 0, // offset - len + 0, // Delete 0==FALSE + XA_ATOM, // flag + &type, // return type + &format, // return format + &len, &bytes_left, // data length + (unsigned char **)&valid_targets); + + if (valid_targets) { + XFree(valid_targets); + valid_targets = nullptr; + } + + if (type == XA_ATOM && bytes_left > 0) { + // Data is ready and can be processed all at once. + result = XGetWindowProperty(x11_display, x11_window, + selection, 0, bytes_left / 4, 0, + XA_ATOM, &type, &format, + &len, &dummy, (unsigned char **)&valid_targets); + if (result == Success) { + atom_count = len; + } else { + print_verbose("Failed to get selection data."); + return None; + } + } else { + return None; + } + } else { + return None; + } + for (unsigned long i = 0; i < atom_count; i++) { + Atom atom = valid_targets[i]; + if (atom == png) { + XFree(valid_targets); + return png; + } + } + + XFree(valid_targets); + return None; +} + String DisplayServerX11::_clipboard_get(Atom p_source, Window x11_window) const { String ret; Atom utf8_atom = XInternAtom(x11_display, "UTF8_STRING", True); @@ -702,6 +771,158 @@ String DisplayServerX11::clipboard_get_primary() const { return ret; } +Ref<Image> DisplayServerX11::clipboard_get_image() const { + _THREAD_SAFE_METHOD_ + Atom clipboard = XInternAtom(x11_display, "CLIPBOARD", 0); + Window x11_window = windows[MAIN_WINDOW_ID].x11_window; + Ref<Image> ret; + Atom target = _clipboard_get_image_target(clipboard, x11_window); + if (target == None) { + return ret; + } + + Window selection_owner = XGetSelectionOwner(x11_display, clipboard); + + if (selection_owner != None) { + // Block events polling while processing selection events. + MutexLock mutex_lock(events_mutex); + + // Identifier for the property the other window + // will send the converted data to. + Atom transfer_prop = XA_PRIMARY; + XConvertSelection(x11_display, + clipboard, // source selection + target, // format to convert to + transfer_prop, // output property + x11_window, CurrentTime); + + XFlush(x11_display); + + // Blocking wait for predicate to be True and remove the event from the queue. + XEvent event; + XIfEvent(x11_display, &event, _predicate_clipboard_selection, (XPointer)&x11_window); + + // Do not get any data, see how much data is there. + Atom type; + int format, result; + unsigned long len, bytes_left, dummy; + unsigned char *data; + XGetWindowProperty(x11_display, x11_window, + transfer_prop, // Property data is transferred through + 0, 1, // offset, len (4 so we can get the size if INCR is used) + 0, // Delete 0==FALSE + AnyPropertyType, // flag + &type, // return type + &format, // return format + &len, &bytes_left, // data length + &data); + + if (type == XInternAtom(x11_display, "INCR", 0)) { + ERR_FAIL_COND_V_MSG(len != 1, ret, "Incremental transfer initial value was not length."); + + // Data is going to be received incrementally. + DEBUG_LOG_X11("INCR selection started.\n"); + + LocalVector<uint8_t> incr_data; + uint32_t data_size = 0; + bool success = false; + + // Initial response is the lower bound of the length of the transferred data. + incr_data.resize(*(unsigned long *)data); + XFree(data); + data = nullptr; + + // Delete INCR property to notify the owner. + XDeleteProperty(x11_display, x11_window, transfer_prop); + + // Process events from the queue. + bool done = false; + while (!done) { + if (!_wait_for_events()) { + // Error or timeout, abort. + break; + } + // Non-blocking wait for next event and remove it from the queue. + XEvent ev; + while (XCheckIfEvent(x11_display, &ev, _predicate_clipboard_incr, (XPointer)&transfer_prop)) { + result = XGetWindowProperty(x11_display, x11_window, + transfer_prop, // output property + 0, LONG_MAX, // offset - len + True, // delete property to notify the owner + AnyPropertyType, // flag + &type, // return type + &format, // return format + &len, &bytes_left, // data length + &data); + + DEBUG_LOG_X11("PropertyNotify: len=%lu, format=%i\n", len, format); + + if (result == Success) { + if (data && (len > 0)) { + uint32_t prev_size = incr_data.size(); + // New chunk, resize to be safe and append data. + incr_data.resize(MAX(data_size + len, prev_size)); + memcpy(incr_data.ptr() + data_size, data, len); + data_size += len; + } else if (!(format == 0 && len == 0)) { + // For unclear reasons the first GetWindowProperty always returns a length and format of 0. + // Otherwise, last chunk, process finished. + done = true; + success = true; + } + } else { + print_verbose("Failed to get selection data chunk."); + done = true; + } + + if (data) { + XFree(data); + data = nullptr; + } + + if (done) { + break; + } + } + } + + if (success && (data_size > 0)) { + ret.instantiate(); + PNGDriverCommon::png_to_image(incr_data.ptr(), incr_data.size(), false, ret); + } + } else if (bytes_left > 0) { + if (data) { + XFree(data); + data = nullptr; + } + // Data is ready and can be processed all at once. + result = XGetWindowProperty(x11_display, x11_window, + transfer_prop, 0, bytes_left + 4, 0, + AnyPropertyType, &type, &format, + &len, &dummy, &data); + if (result == Success) { + ret.instantiate(); + PNGDriverCommon::png_to_image((uint8_t *)data, bytes_left, false, ret); + } else { + print_verbose("Failed to get selection data."); + } + + if (data) { + XFree(data); + } + } + } + + return ret; +} + +bool DisplayServerX11::clipboard_has_image() const { + Atom target = _clipboard_get_image_target( + XInternAtom(x11_display, "CLIPBOARD", 0), + windows[MAIN_WINDOW_ID].x11_window); + return target != None; +} + Bool DisplayServerX11::_predicate_clipboard_save_targets(Display *display, XEvent *event, XPointer arg) { if (event->xany.window == *(Window *)arg) { return (event->type == SelectionRequest) || diff --git a/platform/linuxbsd/x11/display_server_x11.h b/platform/linuxbsd/x11/display_server_x11.h index 9706a4aa11..a8d134a6c7 100644 --- a/platform/linuxbsd/x11/display_server_x11.h +++ b/platform/linuxbsd/x11/display_server_x11.h @@ -304,6 +304,7 @@ class DisplayServerX11 : public DisplayServer { String _clipboard_get_impl(Atom p_source, Window x11_window, Atom target) const; String _clipboard_get(Atom p_source, Window x11_window) const; + Atom _clipboard_get_image_target(Atom p_source, Window x11_window) const; void _clipboard_transfer_ownership(Atom p_source, Window x11_window) const; bool do_mouse_warp = false; @@ -408,6 +409,8 @@ public: virtual void clipboard_set(const String &p_text) override; virtual String clipboard_get() const override; + virtual Ref<Image> clipboard_get_image() const override; + virtual bool clipboard_has_image() const override; virtual void clipboard_set_primary(const String &p_text) override; virtual String clipboard_get_primary() const override; diff --git a/scene/2d/tile_map.cpp b/scene/2d/tile_map.cpp index c36af5401f..b6da4d5082 100644 --- a/scene/2d/tile_map.cpp +++ b/scene/2d/tile_map.cpp @@ -407,6 +407,7 @@ void TileMapLayer::_rendering_update() { // 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) { diff --git a/scene/3d/joint_3d.cpp b/scene/3d/joint_3d.cpp index e7a42857c9..57f242edc6 100644 --- a/scene/3d/joint_3d.cpp +++ b/scene/3d/joint_3d.cpp @@ -312,7 +312,7 @@ void HingeJoint3D::_bind_methods() { ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "angular_limit/relaxation", PROPERTY_HINT_RANGE, "0.01,16,0.01"), "set_param", "get_param", PARAM_LIMIT_RELAXATION); ADD_PROPERTYI(PropertyInfo(Variant::BOOL, "motor/enable"), "set_flag", "get_flag", FLAG_ENABLE_MOTOR); - ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, "-200,200,0.01,or_greater,or_less,suffix:m/s"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); + ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/target_velocity", PROPERTY_HINT_RANGE, U"-200,200,0.01,or_greater,or_less,radians_as_degrees,suffix:\u00B0/s"), "set_param", "get_param", PARAM_MOTOR_TARGET_VELOCITY); ADD_PROPERTYI(PropertyInfo(Variant::FLOAT, "motor/max_impulse", PROPERTY_HINT_RANGE, "0.01,1024,0.01"), "set_param", "get_param", PARAM_MOTOR_MAX_IMPULSE); BIND_ENUM_CONSTANT(PARAM_BIAS); diff --git a/scene/gui/graph_edit.cpp b/scene/gui/graph_edit.cpp index d824d569a9..1fc501e980 100644 --- a/scene/gui/graph_edit.cpp +++ b/scene/gui/graph_edit.cpp @@ -1409,7 +1409,7 @@ void GraphEdit::_zoom_callback(float p_zoom_factor, Vector2 p_origin, Ref<InputE void GraphEdit::set_connection_activity(const StringName &p_from, int p_from_port, const StringName &p_to, int p_to_port, float p_activity) { for (Connection &E : connections) { if (E.from_node == p_from && E.from_port == p_from_port && E.to_node == p_to && E.to_port == p_to_port) { - if (Math::is_equal_approx(E.activity, p_activity)) { + if (!Math::is_equal_approx(E.activity, p_activity)) { // Update only if changed. top_layer->queue_redraw(); minimap->queue_redraw(); diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp index 9de86d1877..12ffafadf7 100644 --- a/scene/gui/line_edit.cpp +++ b/scene/gui/line_edit.cpp @@ -1914,15 +1914,12 @@ bool LineEdit::is_secret() const { } void LineEdit::set_secret_character(const String &p_string) { - // An empty string as the secret character would crash the engine. - // It also wouldn't make sense to use multiple characters as the secret character. - ERR_FAIL_COND_MSG(p_string.length() != 1, "Secret character must be exactly one character long (" + itos(p_string.length()) + " characters given)."); - if (secret_character == p_string) { return; } secret_character = p_string; + update_configuration_warnings(); _shape(); queue_redraw(); } @@ -2266,6 +2263,13 @@ void LineEdit::_emit_text_change() { emit_signal(SNAME("text_changed"), text); text_changed_dirty = false; } +PackedStringArray LineEdit::get_configuration_warnings() const { + PackedStringArray warnings = Control::get_configuration_warnings(); + if (secret_character.length() > 1) { + warnings.push_back("Secret Character property supports only one character. Extra characters will be ignored."); + } + return warnings; +} void LineEdit::_shape() { const Ref<Font> &font = theme_cache.font; @@ -2281,7 +2285,14 @@ void LineEdit::_shape() { if (text.length() == 0 && ime_text.length() == 0) { t = placeholder_translated; } else if (pass) { - t = secret_character.repeat(text.length() + ime_text.length()); + // TODO: Integrate with text server to add support for non-latin scripts. + // Allow secret_character as empty strings, act like if a space was used as a secret character. + String secret = " "; + // Allow values longer than 1 character in the property, but trim characters after the first one. + if (!secret_character.is_empty()) { + secret = secret_character.left(1); + } + t = secret.repeat(text.length() + ime_text.length()); } else { if (ime_text.length() > 0) { t = text.substr(0, caret_column) + ime_text + text.substr(caret_column, text.length()); diff --git a/scene/gui/line_edit.h b/scene/gui/line_edit.h index 4a81f90166..993bc727e4 100644 --- a/scene/gui/line_edit.h +++ b/scene/gui/line_edit.h @@ -385,6 +385,8 @@ public: virtual bool is_text_field() const override; + PackedStringArray get_configuration_warnings() const override; + void show_virtual_keyboard(); LineEdit(const String &p_placeholder = String()); diff --git a/scene/gui/popup_menu.cpp b/scene/gui/popup_menu.cpp index 2e4a35e1d3..28f5ed7dfd 100644 --- a/scene/gui/popup_menu.cpp +++ b/scene/gui/popup_menu.cpp @@ -711,9 +711,9 @@ void PopupMenu::_draw_items() { // Separator item_ofs.x += items[i].indent * theme_cache.indent; if (items[i].separator) { - if (!text.is_empty() || !items[i].icon.is_null()) { + if (!text.is_empty() || items[i].icon.is_valid()) { int content_size = items[i].text_buf->get_size().width + theme_cache.h_separation * 2; - if (!items[i].icon.is_null()) { + if (items[i].icon.is_valid()) { content_size += icon_size.width + theme_cache.h_separation; } @@ -742,7 +742,9 @@ void PopupMenu::_draw_items() { icon_color *= items[i].icon_modulate; // For non-separator items, add some padding for the content. - item_ofs.x += theme_cache.item_start_padding; + if (!items[i].separator) { + item_ofs.x += theme_cache.item_start_padding; + } // Checkboxes if (items[i].checkable_type && !items[i].separator) { @@ -758,7 +760,7 @@ void PopupMenu::_draw_items() { int separator_ofs = (display_width - items[i].text_buf->get_size().width) / 2; // Icon - if (!items[i].icon.is_null()) { + if (items[i].icon.is_valid()) { const Point2 icon_offset = Point2(0, Math::floor((h - icon_size.height) / 2.0)); Point2 icon_pos; @@ -769,6 +771,7 @@ void PopupMenu::_draw_items() { icon_pos = Size2(control->get_size().width - item_ofs.x - separator_ofs - icon_size.width, item_ofs.y); } else { icon_pos = item_ofs + Size2(separator_ofs, 0); + separator_ofs += icon_size.width + theme_cache.h_separation; } } else { if (rtl) { @@ -794,9 +797,6 @@ void PopupMenu::_draw_items() { if (items[i].separator) { if (!text.is_empty()) { Vector2 text_pos = Point2(separator_ofs, item_ofs.y + Math::floor((h - items[i].text_buf->get_size().y) / 2.0)); - if (!rtl && !items[i].icon.is_null()) { - text_pos.x += icon_size.width + theme_cache.h_separation; - } if (theme_cache.font_separator_outline_size > 0 && theme_cache.font_separator_outline_color.a > 0) { items[i].text_buf->draw_outline(ci, text_pos, theme_cache.font_separator_outline_size, theme_cache.font_separator_outline_color); diff --git a/scene/gui/scroll_bar.cpp b/scene/gui/scroll_bar.cpp index 2931b5be91..1310cac2c7 100644 --- a/scene/gui/scroll_bar.cpp +++ b/scene/gui/scroll_bar.cpp @@ -468,22 +468,6 @@ double ScrollBar::get_area_size() const { } } -double ScrollBar::get_area_offset() const { - double ofs = 0.0; - - if (orientation == VERTICAL) { - ofs += theme_cache.scroll_offset_style->get_margin(SIDE_TOP); - ofs += theme_cache.decrement_icon->get_height(); - } - - if (orientation == HORIZONTAL) { - ofs += theme_cache.scroll_offset_style->get_margin(SIDE_LEFT); - ofs += theme_cache.decrement_icon->get_width(); - } - - return ofs; -} - double ScrollBar::get_grabber_offset() const { return (get_area_size()) * get_as_ratio(); } @@ -639,7 +623,6 @@ void ScrollBar::_bind_methods() { BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_style, "scroll"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_focus_style, "scroll_focus"); - BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, scroll_offset_style, "hscroll"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_style, "grabber"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_hl_style, "grabber_highlight"); BIND_THEME_ITEM_CUSTOM(Theme::DATA_TYPE_STYLEBOX, ScrollBar, grabber_pressed_style, "grabber_pressed"); diff --git a/scene/gui/scroll_bar.h b/scene/gui/scroll_bar.h index aacf2060b8..deadbb53d6 100644 --- a/scene/gui/scroll_bar.h +++ b/scene/gui/scroll_bar.h @@ -63,7 +63,6 @@ class ScrollBar : public Range { double get_grabber_size() const; double get_grabber_min_size() const; double get_area_size() const; - double get_area_offset() const; double get_grabber_offset() const; static void set_can_focus_by_default(bool p_can_focus); diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp index 26dbe1cb0c..bd549a6e4a 100644 --- a/scene/gui/spin_box.cpp +++ b/scene/gui/spin_box.cpp @@ -40,7 +40,7 @@ Size2 SpinBox::get_minimum_size() const { return ms; } -void SpinBox::_update_text() { +void SpinBox::_update_text(bool p_keep_line_edit) { String value = String::num(get_value(), Math::range_step_decimals(get_step())); if (is_localizing_numeral_system()) { value = TS->format_number(value); @@ -55,7 +55,12 @@ void SpinBox::_update_text() { } } + if (p_keep_line_edit && value == last_updated_text && value != line_edit->get_text()) { + return; + } + line_edit->set_text_with_selection(value); + last_updated_text = value; } void SpinBox::_text_submitted(const String &p_string) { @@ -245,7 +250,7 @@ inline void SpinBox::_adjust_width_for_icon(const Ref<Texture2D> &icon) { void SpinBox::_notification(int p_what) { switch (p_what) { case NOTIFICATION_DRAW: { - _update_text(); + _update_text(true); _adjust_width_for_icon(theme_cache.updown_icon); RID ci = get_canvas_item(); diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h index bbb1db637a..4d49626d71 100644 --- a/scene/gui/spin_box.h +++ b/scene/gui/spin_box.h @@ -46,12 +46,13 @@ class SpinBox : public Range { void _range_click_timeout(); void _release_mouse(); - void _update_text(); + void _update_text(bool p_keep_line_edit = false); void _text_submitted(const String &p_string); void _text_changed(const String &p_string); String prefix; String suffix; + String last_updated_text; double custom_arrow_step = 0.0; void _line_edit_input(const Ref<InputEvent> &p_event); diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp index 1cef81305b..479480011c 100644 --- a/scene/gui/text_edit.cpp +++ b/scene/gui/text_edit.cpp @@ -7340,7 +7340,7 @@ void TextEdit::_update_scrollbars() { } int visible_width = size.width - theme_cache.style_normal->get_minimum_size().width; - int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + vmin.x + gutters_width + gutter_padding; + int total_width = (draw_placeholder ? placeholder_max_width : text.get_max_width()) + gutters_width + gutter_padding; if (draw_minimap) { total_width += minimap_width; diff --git a/scene/main/canvas_item.cpp b/scene/main/canvas_item.cpp index 8f38a6f6c8..a350b97bc8 100644 --- a/scene/main/canvas_item.cpp +++ b/scene/main/canvas_item.cpp @@ -1496,6 +1496,9 @@ CanvasItem::~CanvasItem() { void CanvasTexture::set_diffuse_texture(const Ref<Texture2D> &p_diffuse) { ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_diffuse.ptr()) != nullptr, "Can't self-assign a CanvasTexture"); + if (diffuse_texture == p_diffuse) { + return; + } diffuse_texture = p_diffuse; RID tex_rid = diffuse_texture.is_valid() ? diffuse_texture->get_rid() : RID(); @@ -1508,9 +1511,13 @@ Ref<Texture2D> CanvasTexture::get_diffuse_texture() const { void CanvasTexture::set_normal_texture(const Ref<Texture2D> &p_normal) { ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_normal.ptr()) != nullptr, "Can't self-assign a CanvasTexture"); + if (normal_texture == p_normal) { + return; + } normal_texture = p_normal; RID tex_rid = normal_texture.is_valid() ? normal_texture->get_rid() : RID(); RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_NORMAL, tex_rid); + emit_changed(); } Ref<Texture2D> CanvasTexture::get_normal_texture() const { return normal_texture; @@ -1518,9 +1525,13 @@ Ref<Texture2D> CanvasTexture::get_normal_texture() const { void CanvasTexture::set_specular_texture(const Ref<Texture2D> &p_specular) { ERR_FAIL_COND_MSG(Object::cast_to<CanvasTexture>(p_specular.ptr()) != nullptr, "Can't self-assign a CanvasTexture"); + if (specular_texture == p_specular) { + return; + } specular_texture = p_specular; RID tex_rid = specular_texture.is_valid() ? specular_texture->get_rid() : RID(); RS::get_singleton()->canvas_texture_set_channel(canvas_texture, RS::CANVAS_TEXTURE_CHANNEL_SPECULAR, tex_rid); + emit_changed(); } Ref<Texture2D> CanvasTexture::get_specular_texture() const { @@ -1528,8 +1539,12 @@ Ref<Texture2D> CanvasTexture::get_specular_texture() const { } void CanvasTexture::set_specular_color(const Color &p_color) { + if (specular == p_color) { + return; + } specular = p_color; RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess); + emit_changed(); } Color CanvasTexture::get_specular_color() const { @@ -1537,8 +1552,12 @@ Color CanvasTexture::get_specular_color() const { } void CanvasTexture::set_specular_shininess(real_t p_shininess) { + if (shininess == p_shininess) { + return; + } shininess = p_shininess; RS::get_singleton()->canvas_texture_set_shading_parameters(canvas_texture, specular, shininess); + emit_changed(); } real_t CanvasTexture::get_specular_shininess() const { @@ -1546,16 +1565,24 @@ real_t CanvasTexture::get_specular_shininess() const { } void CanvasTexture::set_texture_filter(CanvasItem::TextureFilter p_filter) { + if (texture_filter == p_filter) { + return; + } texture_filter = p_filter; RS::get_singleton()->canvas_texture_set_texture_filter(canvas_texture, RS::CanvasItemTextureFilter(p_filter)); + emit_changed(); } CanvasItem::TextureFilter CanvasTexture::get_texture_filter() const { return texture_filter; } void CanvasTexture::set_texture_repeat(CanvasItem::TextureRepeat p_repeat) { + if (texture_repeat == p_repeat) { + return; + } texture_repeat = p_repeat; RS::get_singleton()->canvas_texture_set_texture_repeat(canvas_texture, RS::CanvasItemTextureRepeat(p_repeat)); + emit_changed(); } CanvasItem::TextureRepeat CanvasTexture::get_texture_repeat() const { return texture_repeat; diff --git a/scene/resources/navigation_polygon.cpp b/scene/resources/navigation_polygon.cpp index 6c0e1343ec..52840eaa65 100644 --- a/scene/resources/navigation_polygon.cpp +++ b/scene/resources/navigation_polygon.cpp @@ -503,6 +503,4 @@ void NavigationPolygon::_validate_property(PropertyInfo &p_property) const { } } -NavigationPolygon::NavigationPolygon() { - navigation_mesh.instantiate(); -} +NavigationPolygon::NavigationPolygon() {} diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp index a857434b2a..b80e258af9 100644 --- a/scene/resources/packed_scene.cpp +++ b/scene/resources/packed_scene.cpp @@ -450,7 +450,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const { } if (!old_parent_path.is_empty()) { - node->_set_name_nocheck(old_parent_path + "@" + node->get_name()); + node->set_name(old_parent_path + "#" + node->get_name()); } if (n.owner >= 0) { diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp index 3e710e1f27..ff0a6431bd 100644 --- a/scene/resources/particle_process_material.cpp +++ b/scene/resources/particle_process_material.cpp @@ -564,7 +564,7 @@ void ParticleProcessMaterial::_update_shader() { code += " params.lifetime = (1.0 - lifetime_randomness * rand_from_seed(alt_seed));\n"; code += " params.color = color_value;\n"; if (color_initial_ramp.is_valid()) { - code += " params.color = texture(color_initial_ramp, vec2(rand_from_seed(alt_seed)));\n"; + code += " params.color *= texture(color_initial_ramp, vec2(rand_from_seed(alt_seed)));\n"; } if (emission_color_texture.is_valid() && (emission_shape == EMISSION_SHAPE_POINTS || emission_shape == EMISSION_SHAPE_DIRECTED_POINTS)) { code += " int point = min(emission_texture_point_count - 1, int(rand_from_seed(alt_seed) * float(emission_texture_point_count)));\n"; @@ -663,7 +663,7 @@ void ParticleProcessMaterial::_update_shader() { // No reason to run all these expensive calculation below if we have no orbit velocity // HOWEVER // May be a bad idea for fps consistency? - code += "if(abs(param.orbit_velocity) < 0.01){ return vec3(0.0);}\n"; + code += "if(abs(param.orbit_velocity) < 0.01 || delta < 0.001){ return vec3(0.0);}\n"; code += "\n"; code += " vec3 displacement = vec3(0.);\n"; code += " float pi = 3.14159;\n"; @@ -678,7 +678,7 @@ void ParticleProcessMaterial::_update_shader() { code += " vec3 pos = transform[3].xyz;\n"; code += " vec3 org = emission_transform[3].xyz;\n"; code += " vec3 diff = pos - org;\n"; - code += " float ang = orbit_amount * delta * pi * 2.0;\n"; + code += " float ang = orbit_amount * pi * 2.0;\n"; code += " mat2 rot = mat2(vec2(cos(ang), -sin(ang)), vec2(sin(ang), cos(ang)));\n"; code += " displacement.xy -= diff.xy;\n"; code += " displacement.xy += rot * diff.xy;\n"; @@ -727,7 +727,7 @@ void ParticleProcessMaterial::_update_shader() { code += " displacement += new_pos - local_pos;\n"; code += "\n"; } - code += " return (emission_transform * vec4(displacement, 0.0)).xyz;\n"; + code += " return (emission_transform * vec4(displacement/delta, 0.0)).xyz;\n"; code += "}\n"; code += "\n"; code += "\n"; @@ -758,7 +758,7 @@ void ParticleProcessMaterial::_update_shader() { code += "}\n"; - code += "vec3 process_radial_displacement(DynamicsParameters param, float lifetime, inout uint alt_seed, mat4 transform, mat4 emission_transform, float delta){\n"; + code += "vec3 process_radial_displacement(DynamicsParameters param, float lifetime, inout uint alt_seed, mat4 transform, mat4 emission_transform){\n"; code += " vec3 radial_displacement = vec3(0.0);\n"; code += " float radial_displacement_multiplier = 1.0;\n"; if (tex_parameters[PARAM_RADIAL_VELOCITY].is_valid()) { @@ -773,10 +773,10 @@ void ParticleProcessMaterial::_update_shader() { code += " radial_displacement = normalize(radial_displacement) * min(abs((radial_displacement_multiplier * param.radial_velocity)), length(transform[3].xyz - global_pivot));\n"; code += " }\n"; code += " \n"; - code += " return radial_displacement * delta;\n"; + code += " return radial_displacement;\n"; code += "}\n"; if (tex_parameters[PARAM_DIRECTIONAL_VELOCITY].is_valid()) { - code += "vec3 process_directional_displacement(DynamicsParameters param, float lifetime_percent,mat4 transform, mat4 emission_transform, float delta){\n"; + code += "vec3 process_directional_displacement(DynamicsParameters param, float lifetime_percent,mat4 transform, mat4 emission_transform){\n"; code += " vec3 displacement = vec3(0.);\n"; if (directional_velocity_global) { code += " displacement = texture(directional_velocity_curve, vec2(lifetime_percent)).xyz * param.directional_velocity;\n"; @@ -784,7 +784,7 @@ void ParticleProcessMaterial::_update_shader() { } else { code += " displacement = texture(directional_velocity_curve, vec2(lifetime_percent)).xyz * param.directional_velocity;\n"; } - code += " return displacement * delta;\n"; + code += " return displacement;\n"; code += "}\n"; } @@ -809,9 +809,6 @@ void ParticleProcessMaterial::_update_shader() { code += "void start() {\n"; code += " uint base_number = NUMBER;\n"; code += " uint alt_seed = hash(base_number + uint(1) + RANDOM_SEED);\n"; - code += " if (rand_from_seed(alt_seed) > AMOUNT_RATIO) {\n"; - code += " ACTIVE = false;\n"; - code += " }\n"; code += " DisplayParameters params;\n"; code += " calculate_initial_display_params(params, alt_seed);\n"; code += " // reset alt seed?\n"; @@ -821,6 +818,9 @@ void ParticleProcessMaterial::_update_shader() { code += " PhysicalParameters physics_params;\n"; code += " calculate_initial_physical_params(physics_params, alt_seed);\n"; code += " process_display_param(params, 0.0);\n"; + code += " if (rand_from_seed(alt_seed) > AMOUNT_RATIO) {\n"; + code += " ACTIVE = false;\n"; + code += " }\n"; code += " \n"; code += " float pi = 3.14159;\n"; code += " float degree_to_rad = pi / 180.0;\n"; @@ -898,10 +898,10 @@ void ParticleProcessMaterial::_update_shader() { } code += " // calculate all velocity\n"; code += " \n"; - code += " controlled_displacement += process_radial_displacement(dynamic_params, lifetime_percent, alt_seed, TRANSFORM, EMISSION_TRANSFORM, DELTA);\n"; + code += " controlled_displacement += process_radial_displacement(dynamic_params, lifetime_percent, alt_seed, TRANSFORM, EMISSION_TRANSFORM);\n"; code += " \n"; if (tex_parameters[PARAM_DIRECTIONAL_VELOCITY].is_valid()) { - code += " controlled_displacement += process_directional_displacement(dynamic_params, lifetime_percent, TRANSFORM, EMISSION_TRANSFORM, DELTA);\n"; + code += " controlled_displacement += process_directional_displacement(dynamic_params, lifetime_percent, TRANSFORM, EMISSION_TRANSFORM);\n"; } code += " \n"; code += " process_physical_parameters(physics_params, lifetime_percent);\n"; @@ -973,7 +973,7 @@ void ParticleProcessMaterial::_update_shader() { code += " ACTIVE = false;\n"; code += " }\n"; } - code += " vec3 final_velocity = controlled_displacement/DELTA + VELOCITY;\n"; + code += " vec3 final_velocity = controlled_displacement + VELOCITY;\n"; code += " \n"; code += " // turbulence before limiting\n"; if (turbulence_enabled) { diff --git a/scene/resources/tile_set.cpp b/scene/resources/tile_set.cpp index dce0d87d10..121d29b728 100644 --- a/scene/resources/tile_set.cpp +++ b/scene/resources/tile_set.cpp @@ -4758,30 +4758,18 @@ void TileSetAtlasSource::_queue_update_padded_texture() { call_deferred(SNAME("_update_padded_texture")); } -void TileSetAtlasSource::_update_padded_texture() { - if (!padded_texture_needs_update) { - return; - } - padded_texture_needs_update = false; - padded_texture = Ref<ImageTexture>(); +Ref<ImageTexture> TileSetAtlasSource::_create_padded_image_texture(const Ref<Texture2D> &p_source) { + ERR_FAIL_COND_V(p_source.is_null(), Ref<ImageTexture>()); - if (!texture.is_valid()) { - return; - } - - if (!use_texture_padding) { - return; + Ref<Image> src_image = p_source->get_image(); + if (src_image.is_null()) { + Ref<ImageTexture> ret; + ret.instantiate(); + return ret; } Size2 size = get_atlas_grid_size() * (texture_region_size + Vector2i(2, 2)); - - Ref<Image> src = texture->get_image(); - - if (!src.is_valid()) { - return; - } - - Ref<Image> image = Image::create_empty(size.x, size.y, false, src->get_format()); + Ref<Image> image = Image::create_empty(size.x, size.y, false, src_image->get_format()); for (KeyValue<Vector2i, TileAlternativesData> kv : tiles) { for (int frame = 0; frame < (int)kv.value.animation_frames_durations.size(); frame++) { @@ -4797,24 +4785,89 @@ void TileSetAtlasSource::_update_padded_texture() { Vector2i frame_coords = kv.key + (kv.value.size_in_atlas + kv.value.animation_separation) * ((kv.value.animation_columns > 0) ? Vector2i(frame % kv.value.animation_columns, frame / kv.value.animation_columns) : Vector2i(frame, 0)); Vector2i base_pos = frame_coords * (texture_region_size + Vector2i(2, 2)) + Vector2i(1, 1); - image->blit_rect(*src, src_rect, base_pos); + image->blit_rect(*src_image, src_rect, base_pos); - image->blit_rect(*src, top_src_rect, base_pos + Vector2i(0, -1)); - image->blit_rect(*src, bottom_src_rect, base_pos + Vector2i(0, src_rect.size.y)); - image->blit_rect(*src, left_src_rect, base_pos + Vector2i(-1, 0)); - image->blit_rect(*src, right_src_rect, base_pos + Vector2i(src_rect.size.x, 0)); + image->blit_rect(*src_image, top_src_rect, base_pos + Vector2i(0, -1)); + image->blit_rect(*src_image, bottom_src_rect, base_pos + Vector2i(0, src_rect.size.y)); + image->blit_rect(*src_image, left_src_rect, base_pos + Vector2i(-1, 0)); + image->blit_rect(*src_image, right_src_rect, base_pos + Vector2i(src_rect.size.x, 0)); - image->set_pixelv(base_pos + Vector2i(-1, -1), src->get_pixelv(src_rect.position)); - image->set_pixelv(base_pos + Vector2i(src_rect.size.x, -1), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, 0))); - image->set_pixelv(base_pos + Vector2i(-1, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(0, src_rect.size.y - 1))); - image->set_pixelv(base_pos + Vector2i(src_rect.size.x, src_rect.size.y), src->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, src_rect.size.y - 1))); + image->set_pixelv(base_pos + Vector2i(-1, -1), src_image->get_pixelv(src_rect.position)); + image->set_pixelv(base_pos + Vector2i(src_rect.size.x, -1), src_image->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, 0))); + image->set_pixelv(base_pos + Vector2i(-1, src_rect.size.y), src_image->get_pixelv(src_rect.position + Vector2i(0, src_rect.size.y - 1))); + image->set_pixelv(base_pos + Vector2i(src_rect.size.x, src_rect.size.y), src_image->get_pixelv(src_rect.position + Vector2i(src_rect.size.x - 1, src_rect.size.y - 1))); } } - if (!padded_texture.is_valid()) { - padded_texture.instantiate(); + return ImageTexture::create_from_image(image); +} + +void TileSetAtlasSource::_update_padded_texture() { + if (!padded_texture_needs_update) { + return; + } + padded_texture_needs_update = false; + + if (padded_texture.is_valid()) { + padded_texture->disconnect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); + } + + padded_texture = Ref<CanvasTexture>(); + + if (texture.is_null()) { + return; + } + + if (!use_texture_padding) { + return; + } + + padded_texture.instantiate(); + + Ref<CanvasTexture> src_canvas_texture = texture; + if (src_canvas_texture.is_valid()) { + // Use all textures. + // Diffuse + Ref<Texture2D> src = src_canvas_texture->get_diffuse_texture(); + Ref<ImageTexture> image_texture; + if (src.is_valid()) { + image_texture = _create_padded_image_texture(src); + } else { + image_texture.instantiate(); + } + padded_texture->set_diffuse_texture(image_texture); + + // Normal + src = src_canvas_texture->get_normal_texture(); + image_texture.instantiate(); + if (src.is_valid()) { + image_texture = _create_padded_image_texture(src); + } else { + image_texture.instantiate(); + } + padded_texture->set_normal_texture(image_texture); + + // Specular + src = src_canvas_texture->get_specular_texture(); + image_texture.instantiate(); + if (src.is_valid()) { + image_texture = _create_padded_image_texture(src); + } else { + image_texture.instantiate(); + } + padded_texture->set_specular_texture(image_texture); + + // Other properties. + padded_texture->set_specular_color(src_canvas_texture->get_specular_color()); + padded_texture->set_specular_shininess(src_canvas_texture->get_specular_shininess()); + padded_texture->set_texture_filter(src_canvas_texture->get_texture_filter()); + padded_texture->set_texture_repeat(src_canvas_texture->get_texture_repeat()); + } else { + // Use only diffuse. + Ref<ImageTexture> image_texture = _create_padded_image_texture(texture); + padded_texture->set_diffuse_texture(image_texture); } - padded_texture->set_image(image); + padded_texture->connect_changed(callable_mp(this, &TileSetAtlasSource::_queue_update_padded_texture)); emit_changed(); } diff --git a/scene/resources/tile_set.h b/scene/resources/tile_set.h index 722d615b09..313c4df65d 100644 --- a/scene/resources/tile_set.h +++ b/scene/resources/tile_set.h @@ -641,9 +641,10 @@ private: void _create_coords_mapping_cache(Vector2i p_atlas_coords); bool use_texture_padding = true; - Ref<ImageTexture> padded_texture; + Ref<CanvasTexture> padded_texture; bool padded_texture_needs_update = false; void _queue_update_padded_texture(); + Ref<ImageTexture> _create_padded_image_texture(const Ref<Texture2D> &p_source); void _update_padded_texture(); protected: 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 efec3b5072..dce97808b1 100644 --- a/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp +++ b/servers/rendering/renderer_rd/forward_clustered/render_forward_clustered.cpp @@ -2137,6 +2137,12 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co RD::get_singleton()->draw_list_end(); } + if (rb_data.is_valid() && using_fsr2) { + // Make sure the upscaled texture is initialized, but not necessarily filled, before running screen copies + // so it properly detect if a dedicated copy texture should be used. + rb->ensure_upscaled(); + } + if (scene_state.used_screen_texture) { RENDER_TIMESTAMP("Copy Screen Texture"); @@ -2200,7 +2206,6 @@ void RenderForwardClustered::_render_scene(RenderDataRD *p_render_data, const Co if (rb_data.is_valid() && (using_fsr2 || using_taa)) { if (using_fsr2) { - rb->ensure_upscaled(); rb_data->ensure_fsr2(fsr2_effect); RID exposure; diff --git a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp index 07d56eae0c..d10443d6ad 100644 --- a/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp +++ b/servers/rendering/renderer_rd/renderer_scene_render_rd.cpp @@ -260,15 +260,29 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData RD::get_singleton()->draw_command_begin_label("Copy screen texture"); - rb->allocate_blur_textures(); - + StringName texture_name; bool can_use_storage = _render_buffers_can_be_storage(); Size2i size = rb->get_internal_size(); + // When upscaling, the blur texture needs to be at the target size for post-processing to work. We prefer to use a + // dedicated backbuffer copy texture instead if the blur texture is not an option so shader effects work correctly. + Size2i target_size = rb->get_target_size(); + bool internal_size_matches = (size.width == target_size.width) && (size.height == target_size.height); + bool reuse_blur_texture = !rb->has_upscaled_texture() || internal_size_matches; + if (reuse_blur_texture) { + rb->allocate_blur_textures(); + texture_name = RB_TEX_BLUR_0; + } else { + uint32_t usage_bits = RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT; + usage_bits |= can_use_storage ? RD::TEXTURE_USAGE_STORAGE_BIT : RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT; + rb->create_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR, rb->get_base_data_format(), usage_bits); + texture_name = RB_TEX_BACK_COLOR; + } + for (uint32_t v = 0; v < rb->get_view_count(); v++) { RID texture = rb->get_internal_texture(v); - int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0).mipmaps); - RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, 0); + int mipmaps = int(rb->get_texture_format(RB_SCOPE_BUFFERS, texture_name).mipmaps); + RID dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, texture_name, v, 0); if (can_use_storage) { copy_effects->copy_to_rect(texture, dest, Rect2i(0, 0, size.x, size.y)); @@ -279,8 +293,8 @@ void RendererSceneRenderRD::_render_buffers_copy_screen_texture(const RenderData for (int i = 1; i < mipmaps; i++) { RID source = dest; - dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, v, i); - Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0, i); + dest = rb->get_texture_slice(RB_SCOPE_BUFFERS, texture_name, v, i); + Size2i msize = rb->get_texture_slice_size(RB_SCOPE_BUFFERS, texture_name, i); if (can_use_storage) { copy_effects->make_mipmap(source, dest, msize); 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 43704119e7..b2946e6bbc 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 @@ -58,6 +58,7 @@ #define RB_TEX_BLUR_1 SNAME("blur_1") #define RB_TEX_HALF_BLUR SNAME("half_blur") // only for raster! +#define RB_TEX_BACK_COLOR SNAME("back_color") #define RB_TEX_BACK_DEPTH SNAME("back_depth") class RenderSceneBuffersRD : public RenderSceneBuffers { @@ -267,7 +268,16 @@ public: } // back buffer (color) - RID get_back_buffer_texture() const { return has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) ? get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0) : RID(); } // We (re)use our blur texture here. + RID get_back_buffer_texture() const { + // Prefer returning the dedicated backbuffer color texture if it was created. Return the reused blur texture otherwise. + if (has_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR)) { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_BACK_COLOR); + } else if (has_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0)) { + return get_texture(RB_SCOPE_BUFFERS, RB_TEX_BLUR_0); + } else { + return RID(); + } + } // Upscaled. void ensure_upscaled(); diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h index 840a04becf..9c9ade4445 100644 --- a/tests/scene/test_text_edit.h +++ b/tests/scene/test_text_edit.h @@ -3913,7 +3913,8 @@ TEST_CASE("[SceneTree][TextEdit] viewport") { CHECK(text_edit->get_h_scroll() == 0); text_edit->set_h_scroll(10000000); - CHECK(text_edit->get_h_scroll() == 314); + CHECK(text_edit->get_h_scroll() == 306); + CHECK(text_edit->get_h_scroll_bar()->get_combined_minimum_size().x == 8); text_edit->set_h_scroll(-100); CHECK(text_edit->get_h_scroll() == 0); diff --git a/thirdparty/README.md b/thirdparty/README.md index 4a369a045c..218bddbf54 100644 --- a/thirdparty/README.md +++ b/thirdparty/README.md @@ -672,7 +672,7 @@ with the provided patch. ## openxr - Upstream: https://github.com/KhronosGroup/OpenXR-SDK -- Version: 1.0.28 (f5beb0131f1bea8701ace744d1b50df9049bf331, 2023) +- Version: 1.0.30 (55224479ab13db8ebc8ab1e3d49197bce6201b0b, 2023) - License: Apache 2.0 Files extracted from upstream source: @@ -683,6 +683,7 @@ Files extracted from upstream source: - `src/*.{c,h}` - `src/external/jsoncpp/include/` - `src/external/jsoncpp/src/lib_json/` +- `src/external/jsoncpp/{AUTHORS,LICENSE}` - `LICENSE` and `COPYING.adoc` Exclude: diff --git a/thirdparty/openxr/include/openxr/openxr.h b/thirdparty/openxr/include/openxr/openxr.h index 5d953fb9ba..1faee71082 100644 --- a/thirdparty/openxr/include/openxr/openxr.h +++ b/thirdparty/openxr/include/openxr/openxr.h @@ -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, 28) +#define XR_CURRENT_API_VERSION XR_MAKE_VERSION(1, 0, 30) #define XR_VERSION_MAJOR(version) (uint16_t)(((uint64_t)(version) >> 48)& 0xffffULL) #define XR_VERSION_MINOR(version) (uint16_t)(((uint64_t)(version) >> 32) & 0xffffULL) @@ -491,6 +491,7 @@ typedef enum XrStructureType { XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB = 1000209002, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB = 1000212000, XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META = 1000216000, + XR_TYPE_PASSTHROUGH_PREFERENCES_META = 1000217000, XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META = 1000219001, XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META = 1000219002, XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META = 1000219003, @@ -1799,7 +1800,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrThermalGetTemperatureTrendEXT( #define XR_EXT_debug_utils 1 XR_DEFINE_HANDLE(XrDebugUtilsMessengerEXT) -#define XR_EXT_debug_utils_SPEC_VERSION 4 +#define XR_EXT_debug_utils_SPEC_VERSION 5 #define XR_EXT_DEBUG_UTILS_EXTENSION_NAME "XR_EXT_debug_utils" typedef XrFlags64 XrDebugUtilsMessageSeverityFlagsEXT; @@ -2115,9 +2116,9 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetInputDeviceLocationEXT( #define XR_MSFT_spatial_graph_bridge 1 XR_DEFINE_HANDLE(XrSpatialGraphNodeBindingMSFT) +#define XR_GUID_SIZE_MSFT 16 #define XR_MSFT_spatial_graph_bridge_SPEC_VERSION 2 #define XR_MSFT_SPATIAL_GRAPH_BRIDGE_EXTENSION_NAME "XR_MSFT_spatial_graph_bridge" -#define XR_GUID_SIZE_MSFT 16 typedef enum XrSpatialGraphNodeTypeMSFT { XR_SPATIAL_GRAPH_NODE_TYPE_STATIC_MSFT = 1, @@ -2453,9 +2454,9 @@ typedef struct XrSecondaryViewConfigurationSwapchainCreateInfoMSFT { #define XR_NULL_CONTROLLER_MODEL_KEY_MSFT 0 XR_DEFINE_ATOM(XrControllerModelKeyMSFT) +#define XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT 64 #define XR_MSFT_controller_model_SPEC_VERSION 2 #define XR_MSFT_CONTROLLER_MODEL_EXTENSION_NAME "XR_MSFT_controller_model" -#define XR_MAX_CONTROLLER_MODEL_NODE_NAME_SIZE_MSFT 64 typedef struct XrControllerModelKeyStateMSFT { XrStructureType type; void* XR_MAY_ALIAS next; @@ -3266,7 +3267,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrRequestDisplayRefreshRateFB( #define XR_HTCX_vive_tracker_interaction 1 -#define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 2 +#define XR_HTCX_vive_tracker_interaction_SPEC_VERSION 3 #define XR_HTCX_VIVE_TRACKER_INTERACTION_EXTENSION_NAME "XR_HTCX_vive_tracker_interaction" typedef struct XrViveTrackerPathsHTCX { XrStructureType type; @@ -3451,7 +3452,7 @@ typedef struct XrSystemColorSpacePropertiesFB { } XrSystemColorSpacePropertiesFB; typedef XrResult (XRAPI_PTR *PFN_xrEnumerateColorSpacesFB)(XrSession session, uint32_t colorSpaceCapacityInput, uint32_t* colorSpaceCountOutput, XrColorSpaceFB* colorSpaces); -typedef XrResult (XRAPI_PTR *PFN_xrSetColorSpaceFB)(XrSession session, const XrColorSpaceFB colorspace); +typedef XrResult (XRAPI_PTR *PFN_xrSetColorSpaceFB)(XrSession session, const XrColorSpaceFB colorSpace); #ifndef XR_NO_PROTOTYPES #ifdef XR_EXTENSION_PROTOTYPES @@ -3463,7 +3464,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrEnumerateColorSpacesFB( XRAPI_ATTR XrResult XRAPI_CALL xrSetColorSpaceFB( XrSession session, - const XrColorSpaceFB colorspace); + const XrColorSpaceFB colorSpace); #endif /* XR_EXTENSION_PROTOTYPES */ #endif /* !XR_NO_PROTOTYPES */ @@ -3756,9 +3757,9 @@ typedef struct XrFoveationLevelProfileCreateInfoFB { #define XR_FB_keyboard_tracking 1 +#define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128 #define XR_FB_keyboard_tracking_SPEC_VERSION 1 #define XR_FB_KEYBOARD_TRACKING_EXTENSION_NAME "XR_FB_keyboard_tracking" -#define XR_MAX_KEYBOARD_TRACKING_NAME_SIZE_FB 128 typedef XrFlags64 XrKeyboardTrackingFlagsFB; // Flag bits for XrKeyboardTrackingFlagsFB @@ -3893,9 +3894,9 @@ XRAPI_ATTR XrResult XRAPI_CALL xrTriangleMeshEndVertexBufferUpdateFB( XR_DEFINE_HANDLE(XrPassthroughFB) XR_DEFINE_HANDLE(XrPassthroughLayerFB) XR_DEFINE_HANDLE(XrGeometryInstanceFB) +#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 #define XR_FB_passthrough_SPEC_VERSION 3 #define XR_FB_PASSTHROUGH_EXTENSION_NAME "XR_FB_passthrough" -#define XR_PASSTHROUGH_COLOR_MAP_MONO_SIZE_FB 256 typedef enum XrPassthroughLayerPurposeFB { XR_PASSTHROUGH_LAYER_PURPOSE_RECONSTRUCTION_FB = 0, @@ -4084,9 +4085,9 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGeometryInstanceSetTransformFB( #define XR_NULL_RENDER_MODEL_KEY_FB 0 XR_DEFINE_ATOM(XrRenderModelKeyFB) +#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64 #define XR_FB_render_model_SPEC_VERSION 4 #define XR_FB_RENDER_MODEL_EXTENSION_NAME "XR_FB_render_model" -#define XR_MAX_RENDER_MODEL_NAME_SIZE_FB 64 typedef XrFlags64 XrRenderModelFlagsFB; // Flag bits for XrRenderModelFlagsFB @@ -4243,7 +4244,7 @@ typedef struct XrMarkerSpaceCreateInfoVARJO { typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingVARJO)(XrSession session, XrBool32 enabled); typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingTimeoutVARJO)(XrSession session, uint64_t markerId, XrDuration timeout); -typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingPredictionVARJO)(XrSession session, uint64_t markerId, XrBool32 enabled); +typedef XrResult (XRAPI_PTR *PFN_xrSetMarkerTrackingPredictionVARJO)(XrSession session, uint64_t markerId, XrBool32 enable); typedef XrResult (XRAPI_PTR *PFN_xrGetMarkerSizeVARJO)(XrSession session, uint64_t markerId, XrExtent2Df* size); typedef XrResult (XRAPI_PTR *PFN_xrCreateMarkerSpaceVARJO)(XrSession session, const XrMarkerSpaceCreateInfoVARJO* createInfo, XrSpace* space); @@ -4261,7 +4262,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingTimeoutVARJO( XRAPI_ATTR XrResult XRAPI_CALL xrSetMarkerTrackingPredictionVARJO( XrSession session, uint64_t markerId, - XrBool32 enabled); + XrBool32 enable); XRAPI_ATTR XrResult XRAPI_CALL xrGetMarkerSizeVARJO( XrSession session, @@ -5238,6 +5239,31 @@ typedef struct XrLocalDimmingFrameEndInfoMETA { +#define XR_META_passthrough_preferences 1 +#define XR_META_passthrough_preferences_SPEC_VERSION 1 +#define XR_META_PASSTHROUGH_PREFERENCES_EXTENSION_NAME "XR_META_passthrough_preferences" +typedef XrFlags64 XrPassthroughPreferenceFlagsMETA; + +// Flag bits for XrPassthroughPreferenceFlagsMETA +static const XrPassthroughPreferenceFlagsMETA XR_PASSTHROUGH_PREFERENCE_DEFAULT_TO_ACTIVE_BIT_META = 0x00000001; + +typedef struct XrPassthroughPreferencesMETA { + XrStructureType type; + const void* XR_MAY_ALIAS next; + XrPassthroughPreferenceFlagsMETA flags; +} XrPassthroughPreferencesMETA; + +typedef XrResult (XRAPI_PTR *PFN_xrGetPassthroughPreferencesMETA)(XrSession session, XrPassthroughPreferencesMETA* preferences); + +#ifndef XR_NO_PROTOTYPES +#ifdef XR_EXTENSION_PROTOTYPES +XRAPI_ATTR XrResult XRAPI_CALL xrGetPassthroughPreferencesMETA( + XrSession session, + XrPassthroughPreferencesMETA* preferences); +#endif /* XR_EXTENSION_PROTOTYPES */ +#endif /* !XR_NO_PROTOTYPES */ + + #define XR_META_virtual_keyboard 1 XR_DEFINE_HANDLE(XrVirtualKeyboardMETA) #define XR_MAX_VIRTUAL_KEYBOARD_COMMIT_TEXT_SIZE_META 3992 @@ -5934,7 +5960,7 @@ XRAPI_ATTR XrResult XRAPI_CALL xrApplyForceFeedbackCurlMNDX( #define XR_BD_controller_interaction 1 -#define XR_BD_controller_interaction_SPEC_VERSION 1 +#define XR_BD_controller_interaction_SPEC_VERSION 2 #define XR_BD_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_BD_controller_interaction" @@ -6128,6 +6154,11 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetPlanePolygonBufferEXT( #define XR_OPPO_controller_interaction_SPEC_VERSION 1 #define XR_OPPO_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_OPPO_controller_interaction" + +#define XR_YVR_controller_interaction 1 +#define XR_YVR_controller_interaction_SPEC_VERSION 1 +#define XR_YVR_CONTROLLER_INTERACTION_EXTENSION_NAME "XR_YVR_controller_interaction" + #ifdef __cplusplus } #endif diff --git a/thirdparty/openxr/include/openxr/openxr_platform.h b/thirdparty/openxr/include/openxr/openxr_platform.h index b0a5328f3e..e4ddfcbb7d 100644 --- a/thirdparty/openxr/include/openxr/openxr_platform.h +++ b/thirdparty/openxr/include/openxr/openxr_platform.h @@ -491,14 +491,15 @@ XRAPI_ATTR XrResult XRAPI_CALL xrGetVulkanGraphicsRequirements2KHR( #define XR_MNDX_egl_enable 1 #define XR_MNDX_egl_enable_SPEC_VERSION 1 #define XR_MNDX_EGL_ENABLE_EXTENSION_NAME "XR_MNDX_egl_enable" +typedef PFN_xrVoidFunction (*PFN_xrEglGetProcAddressMNDX)(const char *name); // XrGraphicsBindingEGLMNDX extends XrSessionCreateInfo typedef struct XrGraphicsBindingEGLMNDX { - XrStructureType type; - const void* XR_MAY_ALIAS next; - PFNEGLGETPROCADDRESSPROC getProcAddress; - EGLDisplay display; - EGLConfig config; - EGLContext context; + XrStructureType type; + const void* XR_MAY_ALIAS next; + PFN_xrEglGetProcAddressMNDX getProcAddress; + EGLDisplay display; + EGLConfig config; + EGLContext context; } XrGraphicsBindingEGLMNDX; #endif /* XR_USE_PLATFORM_EGL */ diff --git a/thirdparty/openxr/include/openxr/openxr_reflection.h b/thirdparty/openxr/include/openxr/openxr_reflection.h index c53d412365..8c9aabfab1 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection.h @@ -386,6 +386,7 @@ XR_ENUM_STR(XrResult); _(XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB, 1000209002) \ _(XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB, 1000212000) \ _(XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META, 1000216000) \ + _(XR_TYPE_PASSTHROUGH_PREFERENCES_META, 1000217000) \ _(XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META, 1000219001) \ _(XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META, 1000219002) \ _(XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META, 1000219003) \ @@ -1269,6 +1270,9 @@ XR_ENUM_STR(XrResult); _(XR_COMPOSITION_LAYER_SETTINGS_NORMAL_SHARPENING_BIT_FB, 0x00000004) \ _(XR_COMPOSITION_LAYER_SETTINGS_QUALITY_SHARPENING_BIT_FB, 0x00000008) \ +#define XR_LIST_BITS_XrPassthroughPreferenceFlagsMETA(_) \ + _(XR_PASSTHROUGH_PREFERENCE_DEFAULT_TO_ACTIVE_BIT_META, 0x00000001) \ + #define XR_LIST_BITS_XrVirtualKeyboardInputStateFlagsMETA(_) \ _(XR_VIRTUAL_KEYBOARD_INPUT_STATE_PRESSED_BIT_META, 0x00000001) \ @@ -3588,6 +3592,12 @@ XR_ENUM_STR(XrResult); _(next) \ _(localDimmingMode) \ +/// Calls your macro with the name of each member of XrPassthroughPreferencesMETA, in order. +#define XR_LIST_STRUCT_XrPassthroughPreferencesMETA(_) \ + _(type) \ + _(next) \ + _(flags) \ + /// Calls your macro with the name of each member of XrSystemVirtualKeyboardPropertiesMETA, in order. #define XR_LIST_STRUCT_XrSystemVirtualKeyboardPropertiesMETA(_) \ _(type) \ @@ -4227,6 +4237,7 @@ XR_ENUM_STR(XrResult); _(XrDevicePcmSampleRateStateFB, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB) \ _(XrCompositionLayerDepthTestFB, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB) \ _(XrLocalDimmingFrameEndInfoMETA, XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META) \ + _(XrPassthroughPreferencesMETA, XR_TYPE_PASSTHROUGH_PREFERENCES_META) \ _(XrSystemVirtualKeyboardPropertiesMETA, XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META) \ _(XrVirtualKeyboardCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META) \ _(XrVirtualKeyboardSpaceCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META) \ @@ -4556,6 +4567,7 @@ XR_ENUM_STR(XrResult); _(XR_FB_haptic_pcm, 210) \ _(XR_FB_composition_layer_depth_test, 213) \ _(XR_META_local_dimming, 217) \ + _(XR_META_passthrough_preferences, 218) \ _(XR_META_virtual_keyboard, 220) \ _(XR_OCULUS_external_camera, 227) \ _(XR_META_vulkan_swapchain_create_info, 228) \ @@ -4576,6 +4588,7 @@ XR_ENUM_STR(XrResult); _(XR_EXT_hand_tracking_data_source, 429) \ _(XR_EXT_plane_detection, 430) \ _(XR_OPPO_controller_interaction, 454) \ + _(XR_YVR_controller_interaction, 498) \ #endif diff --git a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h index ec186390a3..7028a6edeb 100644 --- a/thirdparty/openxr/include/openxr/openxr_reflection_structs.h +++ b/thirdparty/openxr/include/openxr/openxr_reflection_structs.h @@ -268,6 +268,7 @@ This file contains expansion macros (X Macros) for OpenXR structures. _avail(XrDevicePcmSampleRateStateFB, XR_TYPE_DEVICE_PCM_SAMPLE_RATE_STATE_FB) \ _avail(XrCompositionLayerDepthTestFB, XR_TYPE_COMPOSITION_LAYER_DEPTH_TEST_FB) \ _avail(XrLocalDimmingFrameEndInfoMETA, XR_TYPE_LOCAL_DIMMING_FRAME_END_INFO_META) \ + _avail(XrPassthroughPreferencesMETA, XR_TYPE_PASSTHROUGH_PREFERENCES_META) \ _avail(XrSystemVirtualKeyboardPropertiesMETA, XR_TYPE_SYSTEM_VIRTUAL_KEYBOARD_PROPERTIES_META) \ _avail(XrVirtualKeyboardCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_CREATE_INFO_META) \ _avail(XrVirtualKeyboardSpaceCreateInfoMETA, XR_TYPE_VIRTUAL_KEYBOARD_SPACE_CREATE_INFO_META) \ diff --git a/thirdparty/openxr/src/common/platform_utils.hpp b/thirdparty/openxr/src/common/platform_utils.hpp index 219d19789d..883baef82d 100644 --- a/thirdparty/openxr/src/common/platform_utils.hpp +++ b/thirdparty/openxr/src/common/platform_utils.hpp @@ -37,6 +37,44 @@ #include "common_config.h" #endif // OPENXR_HAVE_COMMON_CONFIG +#if defined(__x86_64__) && defined(__ILP32__) +#define XR_ARCH_ABI "x32" +#elif defined(_M_X64) || defined(__x86_64__) +#define XR_ARCH_ABI "x86_64" +#elif defined(_M_IX86) || defined(__i386__) || defined(_X86_) +#define XR_ARCH_ABI "i686" +#elif (defined(__aarch64__) && defined(__LP64__)) || defined(_M_ARM64) +#define XR_ARCH_ABI "aarch64" +#elif (defined(__ARM_ARCH) && __ARM_ARCH >= 7 && (defined(__ARM_PCS_VFP) || defined(__ANDROID__))) || defined(_M_ARM) +#define XR_ARCH_ABI "armv7a-vfp" +#elif defined(__ARM_ARCH_5TE__) +#define XR_ARCH_ABI "armv5te" +#elif defined(__mips64) +#define XR_ARCH_ABI "mips64" +#elif defined(__mips) +#define XR_ARCH_ABI "mips" +#elif defined(__powerpc64__) && __BYTE_ORDER__ == __ORDER_BIG_ENDIAN__ +#define XR_ARCH_ABI "ppc64" +#elif defined(__powerpc__) && __BYTE_ORDER__ == __ORDER_LITTLE_ENDIAN__ +#define XR_ARCH_ABI "ppc64el" +#elif defined(__s390x__) || defined(__zarch__) +#define XR_ARCH_ABI "s390x" +#elif defined(__hppa__) +#define XR_ARCH_ABI "hppa" +#elif defined(__alpha__) +#define XR_ARCH_ABI "alpha" +#elif defined(__ia64__) || defined(_M_IA64) +#define XR_ARCH_ABI "ia64" +#elif defined(__m68k__) +#define XR_ARCH_ABI "m68k" +#elif defined(__riscv_xlen) && (__riscv_xlen == 64) +#define XR_ARCH_ABI "riscv64" +#elif defined(__sparc__) && defined(__arch64__) +#define XR_ARCH_ABI "sparc64" +#else +#error "No architecture string known!" +#endif + // Consumers of this file must ensure this function is implemented. For example, the loader will implement this function so that it // can route messages through the loader's logging system. void LogPlatformUtilsError(const std::string& message); @@ -47,6 +85,7 @@ void LogPlatformUtilsError(const std::string& message); #include <unistd.h> #include <fcntl.h> #include <iostream> +#include <sys/stat.h> namespace detail { @@ -72,6 +111,31 @@ static inline char* ImplGetSecureEnv(const char* name) { } // namespace detail #endif // defined(XR_OS_LINUX) || defined(XR_OS_APPLE) + +#if defined(XR_OS_ANDROID) || defined(XR_OS_APPLE) + +#include <sys/stat.h> + +namespace detail { + +static inline bool ImplTryRuntimeFilename(const char* rt_dir_prefix, uint16_t major_version, std::string& file_name) { + auto decorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime." XR_ARCH_ABI ".json"; + auto undecorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime.json"; + + struct stat buf {}; + if (0 == stat(decorated_path.c_str(), &buf)) { + file_name = decorated_path; + return true; + } + if (0 == stat(undecorated_path.c_str(), &buf)) { + file_name = undecorated_path; + return true; + } + return false; +} + +} // namespace detail +#endif // defined(XR_OS_ANDROID) || defined(XR_OS_APPLE) #if defined(XR_OS_LINUX) static inline std::string PlatformUtilsGetEnv(const char* name) { @@ -130,15 +194,8 @@ static inline bool PlatformUtilsSetEnv(const char* name, const char* value) { return (result == 0); } -// Prefix for the Apple global runtime JSON file name -static const std::string rt_dir_prefix = "/usr/local/share/openxr/"; -static const std::string rt_filename = "/active_runtime.json"; - static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { - file_name = rt_dir_prefix; - file_name += std::to_string(major_version); - file_name += rt_filename; - return true; + return detail::ImplTryRuntimeFilename("/usr/local/share/openxr/", major_version, file_name); } #elif defined(XR_OS_WINDOWS) @@ -311,22 +368,19 @@ static inline bool PlatformUtilsSetEnv(const char* /* name */, const char* /* va return false; } -#include <sys/stat.h> - // Intended to be only used as a fallback on Android, with a more open, "native" technique used in most cases static inline bool PlatformGetGlobalRuntimeFileName(uint16_t major_version, std::string& file_name) { // Prefix for the runtime JSON file name static const char* rt_dir_prefixes[] = {"/product", "/odm", "/oem", "/vendor", "/system"}; - static const std::string rt_filename = "/active_runtime.json"; + static const std::string subdir = "/etc/openxr/"; for (const auto prefix : rt_dir_prefixes) { - auto path = prefix + subdir + std::to_string(major_version) + rt_filename; - struct stat buf; - if (0 == stat(path.c_str(), &buf)) { - file_name = path; + const std::string rt_dir_prefix = prefix + subdir; + if (detail::ImplTryRuntimeFilename(rt_dir_prefix.c_str(), major_version, file_name)) { return true; } } + return false; } #else // Not Linux, Apple, nor Windows diff --git a/thirdparty/openxr/src/common/xr_dependencies.h b/thirdparty/openxr/src/common/xr_dependencies.h index 5c7bd04774..6c9cf2d05f 100644 --- a/thirdparty/openxr/src/common/xr_dependencies.h +++ b/thirdparty/openxr/src/common/xr_dependencies.h @@ -76,7 +76,10 @@ #include "wayland-client.h" #endif // XR_USE_PLATFORM_WAYLAND -#ifdef XR_USE_GRAPHICS_API_OPENGL +#ifdef XR_USE_PLATFORM_EGL +#include <EGL/egl.h> +#endif // XR_USE_PLATFORM_EGL + #if defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB) #ifdef Success #undef Success @@ -90,4 +93,3 @@ #undef None #endif // None #endif // defined(XR_USE_PLATFORM_XLIB) || defined(XR_USE_PLATFORM_XCB) -#endif // XR_USE_GRAPHICS_API_OPENGL diff --git a/thirdparty/openxr/src/common/xr_linear.h b/thirdparty/openxr/src/common/xr_linear.h index 5b0da645ac..ce65f8ddfb 100644 --- a/thirdparty/openxr/src/common/xr_linear.h +++ b/thirdparty/openxr/src/common/xr_linear.h @@ -1,5 +1,5 @@ -// Copyright (c) 2017 The Khronos Group Inc. -// Copyright (c) 2016 Oculus VR, LLC. +// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2016, Oculus VR, LLC. // // SPDX-License-Identifier: Apache-2.0 // @@ -23,15 +23,17 @@ #include <openxr/openxr.h> +/* REUSE-IgnoreStart */ +/* The following has copyright notices that duplicate the header above */ + /* ================================================================================================ -Description : Vector, matrix and quaternion math. -Author : J.M.P. van Waveren -Date : 12/10/2016 -Language : C99 -Format : Indent 4 spaces - no tabs. -Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. +Description : Vector, matrix and quaternion math. +Orig. Author : J.M.P. van Waveren +Orig. Date : 12/10/2016 +Language : C99 +Copyright : Copyright (c) 2016 Oculus VR, LLC. All Rights reserved. DESCRIPTION @@ -145,6 +147,8 @@ inline static float XrRcpSqrt(const float x) { return rcp; } +inline static float XrVector2f_Length(const XrVector2f* v) { return sqrtf(v->x * v->x + v->y * v->y); } + inline static void XrVector3f_Set(XrVector3f* v, const float value) { v->x = value; v->y = value; diff --git a/thirdparty/openxr/src/loader/.gitignore b/thirdparty/openxr/src/loader/.gitignore deleted file mode 100644 index 5e8e0ba3a4..0000000000 --- a/thirdparty/openxr/src/loader/.gitignore +++ /dev/null @@ -1,5 +0,0 @@ -# Copyright (c) 2020 The Khronos Group Inc. -# -# SPDX-License-Identifier: Apache-2.0 - -!openxr_loader_for_android.pom diff --git a/thirdparty/openxr/src/loader/api_layer_interface.cpp b/thirdparty/openxr/src/loader/api_layer_interface.cpp index 5560c31a52..c9e24ec40b 100644 --- a/thirdparty/openxr/src/loader/api_layer_interface.cpp +++ b/thirdparty/openxr/src/loader/api_layer_interface.cpp @@ -237,21 +237,23 @@ XrResult ApiLayerInterface::LoadApiLayers(const std::string& openxr_command, uin for (const auto& layer_name : enabled_explicit_api_layer_names) { bool found_this_layer = false; - for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) { - bool erased_layer_manifest_file = false; - - if (layers_already_found.count(layer_name) > 0) { - found_this_layer = true; - } else if (layer_name == (*it)->LayerName()) { - found_this_layer = true; - layers_already_found.insert(layer_name); - enabled_layer_manifest_files_in_init_order.push_back(std::move(*it)); - it = explicit_layer_manifest_files.erase(it); - erased_layer_manifest_file = true; - } + if (layers_already_found.count(layer_name) > 0) { + found_this_layer = true; + } else { + for (auto it = explicit_layer_manifest_files.begin(); it != explicit_layer_manifest_files.end();) { + bool erased_layer_manifest_file = false; + + if (layer_name == (*it)->LayerName()) { + found_this_layer = true; + layers_already_found.insert(layer_name); + enabled_layer_manifest_files_in_init_order.push_back(std::move(*it)); + it = explicit_layer_manifest_files.erase(it); + erased_layer_manifest_file = true; + } - if (!erased_layer_manifest_file) { - it++; + if (!erased_layer_manifest_file) { + it++; + } } } diff --git a/thirdparty/openxr/src/loader/loader_core.cpp b/thirdparty/openxr/src/loader/loader_core.cpp index 98d3fa971a..06e6870053 100644 --- a/thirdparty/openxr/src/loader/loader_core.cpp +++ b/thirdparty/openxr/src/loader/loader_core.cpp @@ -19,7 +19,7 @@ #include "loader_logger.hpp" #include "loader_platform.hpp" #include "runtime_interface.hpp" -#include "xr_generated_dispatch_table.h" +#include "xr_generated_dispatch_table_core.h" #include "xr_generated_loader.hpp" #include <openxr/openxr.h> diff --git a/thirdparty/openxr/src/loader/loader_instance.cpp b/thirdparty/openxr/src/loader/loader_instance.cpp index badd39193c..ce5c205505 100644 --- a/thirdparty/openxr/src/loader/loader_instance.cpp +++ b/thirdparty/openxr/src/loader/loader_instance.cpp @@ -18,7 +18,7 @@ #include "loader_interfaces.h" #include "loader_logger.hpp" #include "runtime_interface.hpp" -#include "xr_generated_dispatch_table.h" +#include "xr_generated_dispatch_table_core.h" #include "xr_generated_loader.hpp" #include <openxr/openxr.h> diff --git a/thirdparty/openxr/src/loader/manifest_file.cpp b/thirdparty/openxr/src/loader/manifest_file.cpp index 99f4e84104..0683bc166a 100644 --- a/thirdparty/openxr/src/loader/manifest_file.cpp +++ b/thirdparty/openxr/src/loader/manifest_file.cpp @@ -274,16 +274,45 @@ static std::string GetXDGEnvAbsolute(const char *name, const char *fallback_path return fallback_paths; } +/// @param rt_dir_prefix Directory prefix with a trailing slash +static bool FindEitherActiveRuntimeFilename(const char *prefix_desc, const std::string &rt_dir_prefix, uint16_t major_version, + std::string &out) { + { + std::ostringstream oss; + oss << "Looking for active_runtime." XR_ARCH_ABI ".json or active_runtime.json in "; + oss << prefix_desc; + oss << ": "; + oss << rt_dir_prefix; + + LoaderLogger::LogInfoMessage("", oss.str()); + } + { + auto decorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime." XR_ARCH_ABI ".json"; + + if (FileSysUtilsPathExists(decorated_path)) { + out = decorated_path; + return true; + } + } + { + auto undecorated_path = rt_dir_prefix + std::to_string(major_version) + "/active_runtime.json"; + + if (FileSysUtilsPathExists(undecorated_path)) { + out = undecorated_path; + return true; + } + } + return false; +} // Return the first instance of relative_path occurring in an XDG config dir according to standard // precedence order. -static bool FindXDGConfigFile(const std::string &relative_path, std::string &out) { - out = GetXDGEnvHome("XDG_CONFIG_HOME", ".config"); - if (!out.empty()) { - out += "/"; - out += relative_path; - - LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in XDG_CONFIG_HOME: " + out); - if (FileSysUtilsPathExists(out)) { +static bool FindXDGConfigFile(const char *relative_dir, uint16_t major_version, std::string &out) { + const std::string message{"Looking for active_runtime." XR_ARCH_ABI ".json or active_runtime.json"}; + std::string dir_prefix = GetXDGEnvHome("XDG_CONFIG_HOME", ".config"); + if (!dir_prefix.empty()) { + dir_prefix += "/"; + dir_prefix += relative_dir; + if (FindEitherActiveRuntimeFilename("XDG_CONFIG_HOME", dir_prefix, major_version, out)) { return true; } } @@ -294,29 +323,26 @@ static bool FindXDGConfigFile(const std::string &relative_path, std::string &out if (path.empty()) { continue; } - out = path; - out += "/"; - out += relative_path; - LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in an entry of XDG_CONFIG_DIRS: " + out); - if (FileSysUtilsPathExists(out)) { + dir_prefix = std::move(path); + dir_prefix += "/"; + dir_prefix += relative_dir; + if (FindEitherActiveRuntimeFilename("an entry of XDG_CONFIG_DIRS", dir_prefix, major_version, out)) { return true; } } - out = SYSCONFDIR; - out += "/"; - out += relative_path; - LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in SYSCONFDIR: " + out); - if (FileSysUtilsPathExists(out)) { + dir_prefix = SYSCONFDIR; + dir_prefix += "/"; + dir_prefix += relative_dir; + if (FindEitherActiveRuntimeFilename("compiled-in SYSCONFDIR", dir_prefix, major_version, out)) { return true; } #if defined(EXTRASYSCONFDIR) - out = EXTRASYSCONFDIR; - out += "/"; - out += relative_path; - LoaderLogger::LogInfoMessage("", "Looking for " + relative_path + " in compiled-in EXTRASYSCONFDIR: " + out); - if (FileSysUtilsPathExists(out)) { + dir_prefix = EXTRASYSCONFDIR; + dir_prefix += "/"; + dir_prefix += relative_dir; + if (FindEitherActiveRuntimeFilename("compiled-in EXTRASYSCONFDIR", dir_prefix, major_version, out)) { return true; } #endif @@ -632,9 +658,8 @@ XrResult RuntimeManifestFile::FindManifestFiles(std::vector<std::unique_ptr<Runt LoaderLogger::LogInfoMessage("", "RuntimeManifestFile::FindManifestFiles - using registry-specified runtime file " + filename); #elif defined(XR_OS_LINUX) - const std::string relative_path = - "openxr/" + std::to_string(XR_VERSION_MAJOR(XR_CURRENT_API_VERSION)) + "/active_runtime.json"; - if (!FindXDGConfigFile(relative_path, filename)) { + + if (!FindXDGConfigFile("openxr/", XR_VERSION_MAJOR(XR_CURRENT_API_VERSION), filename)) { LoaderLogger::LogErrorMessage( "", "RuntimeManifestFile::FindManifestFiles - failed to determine active runtime file path for this environment"); return XR_ERROR_RUNTIME_UNAVAILABLE; diff --git a/thirdparty/openxr/src/loader/runtime_interface.cpp b/thirdparty/openxr/src/loader/runtime_interface.cpp index d9ab86bb58..8312b15ba4 100644 --- a/thirdparty/openxr/src/loader/runtime_interface.cpp +++ b/thirdparty/openxr/src/loader/runtime_interface.cpp @@ -13,7 +13,7 @@ #include "loader_interfaces.h" #include "loader_logger.hpp" #include "loader_platform.hpp" -#include "xr_generated_dispatch_table.h" +#include "xr_generated_dispatch_table_core.h" #include <openxr/openxr.h> @@ -29,6 +29,10 @@ #include "android_utilities.h" #include <android/asset_manager_jni.h> #include <json/value.h> + +// Needed for the loader init struct +#include <xr_dependencies.h> +#include <openxr/openxr_platform.h> #endif // XR_USE_PLATFORM_ANDROID #ifdef XR_KHR_LOADER_INIT_SUPPORT @@ -105,33 +109,22 @@ XrResult LoaderInitData::initialize(const XrLoaderInitInfoBaseHeaderKHR* info) { 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; - jni::init((jni::JavaVM*)_data.applicationVM); _data.next = nullptr; - JNIEnv* Env; - ((jni::JavaVM*)(cast_info->applicationVM))->AttachCurrentThread(&Env, nullptr); - const jclass contextClass = Env->GetObjectClass((jobject)_data.applicationContext); - - const jmethodID getAssetsMethod = Env->GetMethodID(contextClass, "getAssets", "()Landroid/content/res/AssetManager;"); - const jobject AssetManagerObject = Env->CallObjectMethod((jobject)_data.applicationContext, getAssetsMethod); - _android_asset_manager = AAssetManager_fromJava(Env, AssetManagerObject); - - const jmethodID getApplicationContextMethod = - Env->GetMethodID(contextClass, "getApplicationContext", "()Landroid/content/Context;"); - const jobject contextObject = Env->CallObjectMethod((jobject)_data.applicationContext, getApplicationContextMethod); - const jmethodID getApplicationInfoMethod = - Env->GetMethodID(contextClass, "getApplicationInfo", "()Landroid/content/pm/ApplicationInfo;"); - const jobject applicationInfoObject = Env->CallObjectMethod(contextObject, getApplicationInfoMethod); - const jfieldID nativeLibraryDirField = - Env->GetFieldID(Env->GetObjectClass(applicationInfoObject), "nativeLibraryDir", "Ljava/lang/String;"); - const jobject nativeLibraryDirObject = Env->GetObjectField(applicationInfoObject, nativeLibraryDirField); - const jmethodID getBytesMethod = - Env->GetMethodID(Env->GetObjectClass(nativeLibraryDirObject), "getBytes", "(Ljava/lang/String;)[B"); - const auto bytesObject = - static_cast<jbyteArray>(Env->CallObjectMethod(nativeLibraryDirObject, getBytesMethod, Env->NewStringUTF("UTF-8"))); - const size_t length = Env->GetArrayLength(bytesObject); - const jbyte* const bytes = Env->GetByteArrayElements(bytesObject, nullptr); - _native_library_path = std::string(reinterpret_cast<const char*>(bytes), length); + 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; } diff --git a/thirdparty/openxr/src/loader/xr_generated_loader.cpp b/thirdparty/openxr/src/loader/xr_generated_loader.cpp index e7767fd30a..8c79afddc5 100644 --- a/thirdparty/openxr/src/loader/xr_generated_loader.cpp +++ b/thirdparty/openxr/src/loader/xr_generated_loader.cpp @@ -36,7 +36,7 @@ #include "loader_logger.hpp" #include "loader_platform.hpp" #include "runtime_interface.hpp" -#include "xr_generated_dispatch_table.h" +#include "xr_generated_dispatch_table_core.h" #include "xr_dependencies.h" #include <openxr/openxr.h> diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.c b/thirdparty/openxr/src/xr_generated_dispatch_table.c index 302bed31f5..6e43a29724 100644 --- a/thirdparty/openxr/src/xr_generated_dispatch_table.c +++ b/thirdparty/openxr/src/xr_generated_dispatch_table.c @@ -1,7 +1,9 @@ // Copyright (c) 2017-2023, The Khronos Group Inc. -// Copyright (c) 2017-2019 Valve Corporation -// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2017-2019, Valve Corporation +// Copyright (c) 2017-2019, LunarG, Inc. + // SPDX-License-Identifier: Apache-2.0 OR MIT + // *********** THIS FILE IS GENERATED - DO NOT EDIT *********** // See utility_source_generator.py for modifications // ************************************************************ @@ -29,9 +31,6 @@ #include <time.h> #include "xr_generated_dispatch_table.h" -#include "xr_dependencies.h" -#include <openxr/openxr.h> -#include <openxr/openxr_platform.h> #ifdef __cplusplus @@ -404,6 +403,9 @@ void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table, // ---- XR_FB_haptic_pcm extension commands (get_inst_proc_addr(instance, "xrGetDeviceSampleRateFB", (PFN_xrVoidFunction*)&table->GetDeviceSampleRateFB)); + // ---- XR_META_passthrough_preferences extension commands + (get_inst_proc_addr(instance, "xrGetPassthroughPreferencesMETA", (PFN_xrVoidFunction*)&table->GetPassthroughPreferencesMETA)); + // ---- XR_META_virtual_keyboard extension commands (get_inst_proc_addr(instance, "xrCreateVirtualKeyboardMETA", (PFN_xrVoidFunction*)&table->CreateVirtualKeyboardMETA)); (get_inst_proc_addr(instance, "xrDestroyVirtualKeyboardMETA", (PFN_xrVoidFunction*)&table->DestroyVirtualKeyboardMETA)); diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table.h b/thirdparty/openxr/src/xr_generated_dispatch_table.h index b6e17f98d4..a385f81906 100644 --- a/thirdparty/openxr/src/xr_generated_dispatch_table.h +++ b/thirdparty/openxr/src/xr_generated_dispatch_table.h @@ -1,7 +1,9 @@ // Copyright (c) 2017-2023, The Khronos Group Inc. -// Copyright (c) 2017-2019 Valve Corporation -// Copyright (c) 2017-2019 LunarG, Inc. +// Copyright (c) 2017-2019, Valve Corporation +// Copyright (c) 2017-2019, LunarG, Inc. + // SPDX-License-Identifier: Apache-2.0 OR MIT + // *********** THIS FILE IS GENERATED - DO NOT EDIT *********** // See utility_source_generator.py for modifications // ************************************************************ @@ -28,6 +30,7 @@ // #pragma once + #include "xr_dependencies.h" #include <openxr/openxr.h> #include <openxr/openxr_platform.h> @@ -406,6 +409,9 @@ struct XrGeneratedDispatchTable { // ---- XR_FB_haptic_pcm extension commands PFN_xrGetDeviceSampleRateFB GetDeviceSampleRateFB; + // ---- XR_META_passthrough_preferences extension commands + PFN_xrGetPassthroughPreferencesMETA GetPassthroughPreferencesMETA; + // ---- XR_META_virtual_keyboard extension commands PFN_xrCreateVirtualKeyboardMETA CreateVirtualKeyboardMETA; PFN_xrDestroyVirtualKeyboardMETA DestroyVirtualKeyboardMETA; diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.c b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c new file mode 100644 index 0000000000..b3f34e68a9 --- /dev/null +++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.c @@ -0,0 +1,112 @@ +// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2019, Valve Corporation +// Copyright (c) 2017-2019, LunarG, Inc. + +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// *********** THIS FILE IS GENERATED - DO NOT EDIT *********** +// See utility_source_generator.py for modifications +// ************************************************************ + +// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Mark Young <marky@lunarg.com> +// + +#include "xr_generated_dispatch_table_core.h" + + +#ifdef __cplusplus +extern "C" { +#endif +// Helper function to populate an instance dispatch table +void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table, + XrInstance instance, + PFN_xrGetInstanceProcAddr get_inst_proc_addr) { + + // ---- Core 1.0 commands + table->GetInstanceProcAddr = get_inst_proc_addr; + (get_inst_proc_addr(instance, "xrCreateInstance", (PFN_xrVoidFunction*)&table->CreateInstance)); + (get_inst_proc_addr(instance, "xrDestroyInstance", (PFN_xrVoidFunction*)&table->DestroyInstance)); + (get_inst_proc_addr(instance, "xrGetInstanceProperties", (PFN_xrVoidFunction*)&table->GetInstanceProperties)); + (get_inst_proc_addr(instance, "xrPollEvent", (PFN_xrVoidFunction*)&table->PollEvent)); + (get_inst_proc_addr(instance, "xrResultToString", (PFN_xrVoidFunction*)&table->ResultToString)); + (get_inst_proc_addr(instance, "xrStructureTypeToString", (PFN_xrVoidFunction*)&table->StructureTypeToString)); + (get_inst_proc_addr(instance, "xrGetSystem", (PFN_xrVoidFunction*)&table->GetSystem)); + (get_inst_proc_addr(instance, "xrGetSystemProperties", (PFN_xrVoidFunction*)&table->GetSystemProperties)); + (get_inst_proc_addr(instance, "xrEnumerateEnvironmentBlendModes", (PFN_xrVoidFunction*)&table->EnumerateEnvironmentBlendModes)); + (get_inst_proc_addr(instance, "xrCreateSession", (PFN_xrVoidFunction*)&table->CreateSession)); + (get_inst_proc_addr(instance, "xrDestroySession", (PFN_xrVoidFunction*)&table->DestroySession)); + (get_inst_proc_addr(instance, "xrEnumerateReferenceSpaces", (PFN_xrVoidFunction*)&table->EnumerateReferenceSpaces)); + (get_inst_proc_addr(instance, "xrCreateReferenceSpace", (PFN_xrVoidFunction*)&table->CreateReferenceSpace)); + (get_inst_proc_addr(instance, "xrGetReferenceSpaceBoundsRect", (PFN_xrVoidFunction*)&table->GetReferenceSpaceBoundsRect)); + (get_inst_proc_addr(instance, "xrCreateActionSpace", (PFN_xrVoidFunction*)&table->CreateActionSpace)); + (get_inst_proc_addr(instance, "xrLocateSpace", (PFN_xrVoidFunction*)&table->LocateSpace)); + (get_inst_proc_addr(instance, "xrDestroySpace", (PFN_xrVoidFunction*)&table->DestroySpace)); + (get_inst_proc_addr(instance, "xrEnumerateViewConfigurations", (PFN_xrVoidFunction*)&table->EnumerateViewConfigurations)); + (get_inst_proc_addr(instance, "xrGetViewConfigurationProperties", (PFN_xrVoidFunction*)&table->GetViewConfigurationProperties)); + (get_inst_proc_addr(instance, "xrEnumerateViewConfigurationViews", (PFN_xrVoidFunction*)&table->EnumerateViewConfigurationViews)); + (get_inst_proc_addr(instance, "xrEnumerateSwapchainFormats", (PFN_xrVoidFunction*)&table->EnumerateSwapchainFormats)); + (get_inst_proc_addr(instance, "xrCreateSwapchain", (PFN_xrVoidFunction*)&table->CreateSwapchain)); + (get_inst_proc_addr(instance, "xrDestroySwapchain", (PFN_xrVoidFunction*)&table->DestroySwapchain)); + (get_inst_proc_addr(instance, "xrEnumerateSwapchainImages", (PFN_xrVoidFunction*)&table->EnumerateSwapchainImages)); + (get_inst_proc_addr(instance, "xrAcquireSwapchainImage", (PFN_xrVoidFunction*)&table->AcquireSwapchainImage)); + (get_inst_proc_addr(instance, "xrWaitSwapchainImage", (PFN_xrVoidFunction*)&table->WaitSwapchainImage)); + (get_inst_proc_addr(instance, "xrReleaseSwapchainImage", (PFN_xrVoidFunction*)&table->ReleaseSwapchainImage)); + (get_inst_proc_addr(instance, "xrBeginSession", (PFN_xrVoidFunction*)&table->BeginSession)); + (get_inst_proc_addr(instance, "xrEndSession", (PFN_xrVoidFunction*)&table->EndSession)); + (get_inst_proc_addr(instance, "xrRequestExitSession", (PFN_xrVoidFunction*)&table->RequestExitSession)); + (get_inst_proc_addr(instance, "xrWaitFrame", (PFN_xrVoidFunction*)&table->WaitFrame)); + (get_inst_proc_addr(instance, "xrBeginFrame", (PFN_xrVoidFunction*)&table->BeginFrame)); + (get_inst_proc_addr(instance, "xrEndFrame", (PFN_xrVoidFunction*)&table->EndFrame)); + (get_inst_proc_addr(instance, "xrLocateViews", (PFN_xrVoidFunction*)&table->LocateViews)); + (get_inst_proc_addr(instance, "xrStringToPath", (PFN_xrVoidFunction*)&table->StringToPath)); + (get_inst_proc_addr(instance, "xrPathToString", (PFN_xrVoidFunction*)&table->PathToString)); + (get_inst_proc_addr(instance, "xrCreateActionSet", (PFN_xrVoidFunction*)&table->CreateActionSet)); + (get_inst_proc_addr(instance, "xrDestroyActionSet", (PFN_xrVoidFunction*)&table->DestroyActionSet)); + (get_inst_proc_addr(instance, "xrCreateAction", (PFN_xrVoidFunction*)&table->CreateAction)); + (get_inst_proc_addr(instance, "xrDestroyAction", (PFN_xrVoidFunction*)&table->DestroyAction)); + (get_inst_proc_addr(instance, "xrSuggestInteractionProfileBindings", (PFN_xrVoidFunction*)&table->SuggestInteractionProfileBindings)); + (get_inst_proc_addr(instance, "xrAttachSessionActionSets", (PFN_xrVoidFunction*)&table->AttachSessionActionSets)); + (get_inst_proc_addr(instance, "xrGetCurrentInteractionProfile", (PFN_xrVoidFunction*)&table->GetCurrentInteractionProfile)); + (get_inst_proc_addr(instance, "xrGetActionStateBoolean", (PFN_xrVoidFunction*)&table->GetActionStateBoolean)); + (get_inst_proc_addr(instance, "xrGetActionStateFloat", (PFN_xrVoidFunction*)&table->GetActionStateFloat)); + (get_inst_proc_addr(instance, "xrGetActionStateVector2f", (PFN_xrVoidFunction*)&table->GetActionStateVector2f)); + (get_inst_proc_addr(instance, "xrGetActionStatePose", (PFN_xrVoidFunction*)&table->GetActionStatePose)); + (get_inst_proc_addr(instance, "xrSyncActions", (PFN_xrVoidFunction*)&table->SyncActions)); + (get_inst_proc_addr(instance, "xrEnumerateBoundSourcesForAction", (PFN_xrVoidFunction*)&table->EnumerateBoundSourcesForAction)); + (get_inst_proc_addr(instance, "xrGetInputSourceLocalizedName", (PFN_xrVoidFunction*)&table->GetInputSourceLocalizedName)); + (get_inst_proc_addr(instance, "xrApplyHapticFeedback", (PFN_xrVoidFunction*)&table->ApplyHapticFeedback)); + (get_inst_proc_addr(instance, "xrStopHapticFeedback", (PFN_xrVoidFunction*)&table->StopHapticFeedback)); + + // ---- XR_EXT_debug_utils extension commands + (get_inst_proc_addr(instance, "xrSetDebugUtilsObjectNameEXT", (PFN_xrVoidFunction*)&table->SetDebugUtilsObjectNameEXT)); + (get_inst_proc_addr(instance, "xrCreateDebugUtilsMessengerEXT", (PFN_xrVoidFunction*)&table->CreateDebugUtilsMessengerEXT)); + (get_inst_proc_addr(instance, "xrDestroyDebugUtilsMessengerEXT", (PFN_xrVoidFunction*)&table->DestroyDebugUtilsMessengerEXT)); + (get_inst_proc_addr(instance, "xrSubmitDebugUtilsMessageEXT", (PFN_xrVoidFunction*)&table->SubmitDebugUtilsMessageEXT)); + (get_inst_proc_addr(instance, "xrSessionBeginDebugUtilsLabelRegionEXT", (PFN_xrVoidFunction*)&table->SessionBeginDebugUtilsLabelRegionEXT)); + (get_inst_proc_addr(instance, "xrSessionEndDebugUtilsLabelRegionEXT", (PFN_xrVoidFunction*)&table->SessionEndDebugUtilsLabelRegionEXT)); + (get_inst_proc_addr(instance, "xrSessionInsertDebugUtilsLabelEXT", (PFN_xrVoidFunction*)&table->SessionInsertDebugUtilsLabelEXT)); +} + + +#ifdef __cplusplus +} // extern "C" +#endif + diff --git a/thirdparty/openxr/src/xr_generated_dispatch_table_core.h b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h new file mode 100644 index 0000000000..0f3e7e0502 --- /dev/null +++ b/thirdparty/openxr/src/xr_generated_dispatch_table_core.h @@ -0,0 +1,119 @@ +// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2019, Valve Corporation +// Copyright (c) 2017-2019, LunarG, Inc. + +// SPDX-License-Identifier: Apache-2.0 OR MIT + +// *********** THIS FILE IS GENERATED - DO NOT EDIT *********** +// See utility_source_generator.py for modifications +// ************************************************************ + +// Copyright (c) 2017-2023, The Khronos Group Inc. +// Copyright (c) 2017-2019 Valve Corporation +// Copyright (c) 2017-2019 LunarG, Inc. +// +// SPDX-License-Identifier: Apache-2.0 +// +// Licensed under the Apache License, Version 2.0 (the "License"); +// you may not use this file except in compliance with the License. +// You may obtain a copy of the License at +// +// http://www.apache.org/licenses/LICENSE-2.0 +// +// Unless required by applicable law or agreed to in writing, software +// distributed under the License is distributed on an "AS IS" BASIS, +// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +// See the License for the specific language governing permissions and +// limitations under the License. +// +// Author: Mark Young <marky@lunarg.com> +// + +#pragma once + +#include <openxr/openxr.h> + + +#ifdef __cplusplus +extern "C" { +#endif +// Generated dispatch table +struct XrGeneratedDispatchTable { + + // ---- Core 1.0 commands + PFN_xrGetInstanceProcAddr GetInstanceProcAddr; + PFN_xrEnumerateApiLayerProperties EnumerateApiLayerProperties; + PFN_xrEnumerateInstanceExtensionProperties EnumerateInstanceExtensionProperties; + PFN_xrCreateInstance CreateInstance; + PFN_xrDestroyInstance DestroyInstance; + PFN_xrGetInstanceProperties GetInstanceProperties; + PFN_xrPollEvent PollEvent; + PFN_xrResultToString ResultToString; + PFN_xrStructureTypeToString StructureTypeToString; + PFN_xrGetSystem GetSystem; + PFN_xrGetSystemProperties GetSystemProperties; + PFN_xrEnumerateEnvironmentBlendModes EnumerateEnvironmentBlendModes; + PFN_xrCreateSession CreateSession; + PFN_xrDestroySession DestroySession; + PFN_xrEnumerateReferenceSpaces EnumerateReferenceSpaces; + PFN_xrCreateReferenceSpace CreateReferenceSpace; + PFN_xrGetReferenceSpaceBoundsRect GetReferenceSpaceBoundsRect; + PFN_xrCreateActionSpace CreateActionSpace; + PFN_xrLocateSpace LocateSpace; + PFN_xrDestroySpace DestroySpace; + PFN_xrEnumerateViewConfigurations EnumerateViewConfigurations; + PFN_xrGetViewConfigurationProperties GetViewConfigurationProperties; + PFN_xrEnumerateViewConfigurationViews EnumerateViewConfigurationViews; + PFN_xrEnumerateSwapchainFormats EnumerateSwapchainFormats; + PFN_xrCreateSwapchain CreateSwapchain; + PFN_xrDestroySwapchain DestroySwapchain; + PFN_xrEnumerateSwapchainImages EnumerateSwapchainImages; + PFN_xrAcquireSwapchainImage AcquireSwapchainImage; + PFN_xrWaitSwapchainImage WaitSwapchainImage; + PFN_xrReleaseSwapchainImage ReleaseSwapchainImage; + PFN_xrBeginSession BeginSession; + PFN_xrEndSession EndSession; + PFN_xrRequestExitSession RequestExitSession; + PFN_xrWaitFrame WaitFrame; + PFN_xrBeginFrame BeginFrame; + PFN_xrEndFrame EndFrame; + PFN_xrLocateViews LocateViews; + PFN_xrStringToPath StringToPath; + PFN_xrPathToString PathToString; + PFN_xrCreateActionSet CreateActionSet; + PFN_xrDestroyActionSet DestroyActionSet; + PFN_xrCreateAction CreateAction; + PFN_xrDestroyAction DestroyAction; + PFN_xrSuggestInteractionProfileBindings SuggestInteractionProfileBindings; + PFN_xrAttachSessionActionSets AttachSessionActionSets; + PFN_xrGetCurrentInteractionProfile GetCurrentInteractionProfile; + PFN_xrGetActionStateBoolean GetActionStateBoolean; + PFN_xrGetActionStateFloat GetActionStateFloat; + PFN_xrGetActionStateVector2f GetActionStateVector2f; + PFN_xrGetActionStatePose GetActionStatePose; + PFN_xrSyncActions SyncActions; + PFN_xrEnumerateBoundSourcesForAction EnumerateBoundSourcesForAction; + PFN_xrGetInputSourceLocalizedName GetInputSourceLocalizedName; + PFN_xrApplyHapticFeedback ApplyHapticFeedback; + PFN_xrStopHapticFeedback StopHapticFeedback; + + // ---- XR_EXT_debug_utils extension commands + PFN_xrSetDebugUtilsObjectNameEXT SetDebugUtilsObjectNameEXT; + PFN_xrCreateDebugUtilsMessengerEXT CreateDebugUtilsMessengerEXT; + PFN_xrDestroyDebugUtilsMessengerEXT DestroyDebugUtilsMessengerEXT; + PFN_xrSubmitDebugUtilsMessageEXT SubmitDebugUtilsMessageEXT; + PFN_xrSessionBeginDebugUtilsLabelRegionEXT SessionBeginDebugUtilsLabelRegionEXT; + PFN_xrSessionEndDebugUtilsLabelRegionEXT SessionEndDebugUtilsLabelRegionEXT; + PFN_xrSessionInsertDebugUtilsLabelEXT SessionInsertDebugUtilsLabelEXT; +}; + + +// Prototype for dispatch table helper function +void GeneratedXrPopulateDispatchTable(struct XrGeneratedDispatchTable *table, + XrInstance instance, + PFN_xrGetInstanceProcAddr get_inst_proc_addr); + +#ifdef __cplusplus +} // extern "C" +#endif + |