Browse Source

Merge pull request #104019 from akien-mga/4.4-cherrypicks

[4.4] Cherry-picks for the 4.4 branch (future 4.4.1) - 1st batch
Rémi Verschelde 6 months ago
parent
commit
cc77c02d47
100 changed files with 1139 additions and 359 deletions
  1. 5 4
      .github/actions/godot-cpp-build/action.yml
  2. 2 2
      .github/workflows/android_builds.yml
  3. 2 0
      .github/workflows/linux_builds.yml
  4. 5 0
      .gitignore
  5. 50 18
      core/object/worker_thread_pool.cpp
  6. 9 3
      core/object/worker_thread_pool.h
  7. 3 3
      core/os/os.h
  8. 1 1
      core/variant/callable.cpp
  9. 1 1
      doc/classes/LineEdit.xml
  10. 1 1
      doc/classes/NativeMenu.xml
  11. 1 1
      doc/classes/RDTextureView.xml
  12. 1 1
      doc/classes/RDVertexAttribute.xml
  13. 29 1
      doc/classes/RenderingDevice.xml
  14. 1 0
      drivers/apple/joypad_apple.h
  15. 18 4
      drivers/apple/joypad_apple.mm
  16. 14 0
      drivers/d3d12/rendering_device_driver_d3d12.cpp
  17. 1 1
      drivers/gles3/effects/copy_effects.cpp
  18. 6 0
      drivers/gles3/rasterizer_canvas_gles3.cpp
  19. 8 20
      drivers/gles3/storage/particles_storage.cpp
  20. 14 0
      drivers/metal/pixel_formats.mm
  21. 23 0
      drivers/vulkan/rendering_device_driver_vulkan.cpp
  22. 1 1
      drivers/windows/file_access_windows.cpp
  23. 2 1
      editor/animation_track_editor.cpp
  24. 8 3
      editor/connections_dialog.cpp
  25. 1 0
      editor/debugger/debug_adapter/debug_adapter_protocol.cpp
  26. 2 0
      editor/editor_inspector.cpp
  27. 19 7
      editor/editor_node.cpp
  28. 1 1
      editor/editor_node.h
  29. 3 0
      editor/export/project_export.cpp
  30. 2 0
      editor/plugins/polygon_3d_editor_plugin.cpp
  31. 10 0
      editor/plugins/script_editor_plugin.cpp
  32. 1 0
      editor/plugins/script_editor_plugin.h
  33. 22 6
      editor/plugins/visual_shader_editor_plugin.cpp
  34. 0 2
      editor/project_manager.cpp
  35. 4 6
      editor/project_manager/project_list.cpp
  36. 1 1
      editor/scene_tree_dock.cpp
  37. 7 5
      main/main.cpp
  38. 2 0
      modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd
  39. 2 0
      modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd
  40. 2 0
      modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd
  41. 1 1
      modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
  42. 1 1
      modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
  43. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.cfg
  44. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.gd
  45. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.cfg
  46. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.gd
  47. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.cfg
  48. 0 0
      modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.gd
  49. 0 0
      modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.cfg
  50. 0 0
      modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.gd
  51. 0 0
      modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.cfg
  52. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.gd
  53. 0 0
      modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.cfg
  54. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.gd
  55. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd
  56. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd
  57. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd
  58. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd
  59. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd
  60. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd
  61. 1 1
      modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd
  62. 0 0
      modules/gdscript/tests/scripts/completion/types/local/inferred.cfg
  63. 0 0
      modules/gdscript/tests/scripts/completion/types/local/inferred.gd
  64. 0 0
      modules/gdscript/tests/scripts/completion/types/member/inferred.cfg
  65. 0 0
      modules/gdscript/tests/scripts/completion/types/member/inferred.gd
  66. 4 0
      modules/gdscript/tests/test_completion.h
  67. 70 0
      modules/jolt_physics/misc/jolt_math_funcs.cpp
  68. 59 0
      modules/jolt_physics/misc/jolt_math_funcs.h
  69. 3 3
      modules/jolt_physics/objects/jolt_area_3d.cpp
  70. 3 3
      modules/jolt_physics/objects/jolt_body_3d.cpp
  71. 7 3
      modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
  72. 13 17
      modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp
  73. 7 0
      modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
  74. 6 0
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
  75. 8 1
      modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
  76. 17 9
      modules/multiplayer/editor/editor_network_profiler.cpp
  77. 6 1
      modules/openxr/extensions/platform/openxr_opengl_extension.cpp
  78. 6 0
      modules/openxr/register_types.cpp
  79. 17 3
      modules/text_server_adv/text_server_adv.cpp
  80. 1 0
      modules/text_server_adv/text_server_adv.h
  81. 17 3
      modules/text_server_fb/text_server_fb.cpp
  82. 1 0
      modules/text_server_fb/text_server_fb.h
  83. 1 1
      platform/android/README.md
  84. 1 1
      platform/android/android_input_handler.cpp
  85. 1 1
      platform/android/detect.py
  86. 1 1
      platform/android/java/app/AndroidManifest.xml
  87. 4 4
      platform/android/java/editor/src/main/AndroidManifest.xml
  88. 6 4
      platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
  89. 379 142
      platform/android/java_class_wrapper.cpp
  90. 1 0
      platform/android/java_godot_lib_jni.cpp
  91. 37 1
      platform/ios/export/export_plugin.cpp
  92. 1 1
      platform/linuxbsd/README.md
  93. 85 0
      platform/linuxbsd/os_linuxbsd.cpp
  94. 3 0
      platform/linuxbsd/os_linuxbsd.h
  95. 1 1
      platform/linuxbsd/wayland/detect_prime_egl.h
  96. 17 33
      platform/linuxbsd/x11/detect_prime_x11.cpp
  97. 24 1
      platform/linuxbsd/x11/detect_prime_x11.h
  98. 13 5
      platform/linuxbsd/x11/display_server_x11.cpp
  99. 1 1
      platform/web/README.md
  100. 22 14
      platform/windows/os_windows.cpp

+ 5 - 4
.github/actions/godot-cpp-build/action.yml

@@ -1,9 +1,6 @@
 name: Build godot-cpp
 description: Build godot-cpp with the provided options.
 
-env:
-  GODOT_CPP_BRANCH: 4.4
-
 inputs:
   bin:
     description: Path to the Godot binary.
@@ -16,6 +13,10 @@ inputs:
     description: The SCons cache path.
     default: ${{ github.workspace }}/.scons_cache/
     type: string
+  godot-cpp-branch:
+    description: The godot-cpp branch.
+    default: master
+    type: string
 
 runs:
   using: composite
@@ -25,7 +26,7 @@ runs:
       with:
         submodules: recursive
         repository: godotengine/godot-cpp
-        ref: ${{ env.GODOT_CPP_BRANCH }}
+        ref: ${{ inputs.godot-cpp-branch }}
         path: godot-cpp
 
     - name: Extract API

+ 2 - 2
.github/workflows/android_builds.yml

@@ -62,8 +62,8 @@ jobs:
       - name: Download pre-built Android Swappy Frame Pacing Library
         uses: dsaltares/[email protected]
         with:
-          repo: darksylinc/godot-swappy
-          version: tags/v2023.3.0.0
+          repo: godotengine/godot-swappy
+          version: tags/from-source-2025-01-31
           file: godot-swappy.7z
           target: swappy/godot-swappy.7z
 

+ 2 - 0
.github/workflows/linux_builds.yml

@@ -6,6 +6,7 @@ on:
 env:
   # Used for the cache key. Add version suffix to force clean build.
   GODOT_BASE_BRANCH: 4.4
+  GODOT_CPP_BRANCH: 4.4
   SCONSFLAGS: verbose=yes warnings=extra werror=yes module_text_server_fb_enabled=yes strict_checks=yes
   DOTNET_NOLOGO: true
   DOTNET_CLI_TELEMETRY_OPTOUT: true
@@ -174,6 +175,7 @@ jobs:
         with:
           bin: ${{ matrix.bin }}
           scons-flags: target=template_debug dev_build=yes verbose=yes
+          godot-cpp-branch: ${{ env.GODOT_CPP_BRANCH }}
 
       - name: Save Godot build cache
         uses: ./.github/actions/godot-cache-save

+ 5 - 0
.gitignore

@@ -263,6 +263,11 @@ bld/
 !thirdparty/**/arm/
 !thirdparty/**/arm64/
 
+thirdparty/swappy-frame-pacing/arm64-v8a/abi.json
+thirdparty/swappy-frame-pacing/armeabi-v7a/abi.json
+thirdparty/swappy-frame-pacing/x86/abi.json
+thirdparty/swappy-frame-pacing/x86_64/abi.json
+
 # Visual Studio 2015/2017 cache/options directory
 .vs/
 

+ 50 - 18
core/object/worker_thread_pool.cpp

@@ -37,6 +37,8 @@
 
 WorkerThreadPool::Task *const WorkerThreadPool::ThreadData::YIELDING = (Task *)1;
 
+HashMap<StringName, WorkerThreadPool *> WorkerThreadPool::named_pools;
+
 void WorkerThreadPool::Task::free_template_userdata() {
 	ERR_FAIL_NULL(template_userdata);
 	ERR_FAIL_NULL(native_func_userdata);
@@ -184,25 +186,25 @@ void WorkerThreadPool::_thread_function(void *p_user) {
 	while (true) {
 		Task *task_to_process = nullptr;
 		{
-			MutexLock lock(singleton->task_mutex);
+			MutexLock lock(thread_data->pool->task_mutex);
 
-			bool exit = singleton->_handle_runlevel(thread_data, lock);
+			bool exit = thread_data->pool->_handle_runlevel(thread_data, lock);
 			if (unlikely(exit)) {
 				break;
 			}
 
 			thread_data->signaled = false;
 
-			if (singleton->task_queue.first()) {
-				task_to_process = singleton->task_queue.first()->self();
-				singleton->task_queue.remove(singleton->task_queue.first());
+			if (thread_data->pool->task_queue.first()) {
+				task_to_process = thread_data->pool->task_queue.first()->self();
+				thread_data->pool->task_queue.remove(thread_data->pool->task_queue.first());
 			} else {
 				thread_data->cond_var.wait(lock);
 			}
 		}
 
 		if (task_to_process) {
-			singleton->_process_task(task_to_process);
+			thread_data->pool->_process_task(task_to_process);
 		}
 	}
 }
@@ -497,7 +499,7 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
 				}
 			}
 
-			if (singleton->task_queue.first()) {
+			if (p_caller_pool_thread->pool->task_queue.first()) {
 				task_to_process = task_queue.first()->self();
 				task_queue.remove(task_queue.first());
 			}
@@ -505,7 +507,9 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
 			if (!task_to_process) {
 				p_caller_pool_thread->awaited_task = p_task;
 
-				_unlock_unlockable_mutexes();
+				if (this == singleton) {
+					_unlock_unlockable_mutexes();
+				}
 				relock_unlockables = true;
 
 				p_caller_pool_thread->cond_var.wait(lock);
@@ -514,7 +518,7 @@ void WorkerThreadPool::_wait_collaboratively(ThreadData *p_caller_pool_thread, T
 			}
 		}
 
-		if (relock_unlockables) {
+		if (relock_unlockables && this == singleton) {
 			_lock_unlockable_mutexes();
 		}
 
@@ -690,9 +694,13 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
 	{
 		Group *group = *groupp;
 
-		_unlock_unlockable_mutexes();
+		if (this == singleton) {
+			_unlock_unlockable_mutexes();
+		}
 		group->done_semaphore.wait();
-		_lock_unlockable_mutexes();
+		if (this == singleton) {
+			_lock_unlockable_mutexes();
+		}
 
 		uint32_t max_users = group->tasks_used + 1; // Add 1 because the thread waiting for it is also user. Read before to avoid another thread freeing task after increment.
 		uint32_t finished_users = group->finished.increment(); // fetch happens before inc, so increment later.
@@ -709,15 +717,15 @@ void WorkerThreadPool::wait_for_group_task_completion(GroupID p_group) {
 #endif
 }
 
-int WorkerThreadPool::get_thread_index() {
+int WorkerThreadPool::get_thread_index() const {
 	Thread::ID tid = Thread::get_caller_id();
-	return singleton->thread_ids.has(tid) ? singleton->thread_ids[tid] : -1;
+	return thread_ids.has(tid) ? thread_ids[tid] : -1;
 }
 
-WorkerThreadPool::TaskID WorkerThreadPool::get_caller_task_id() {
+WorkerThreadPool::TaskID WorkerThreadPool::get_caller_task_id() const {
 	int th_index = get_thread_index();
-	if (th_index != -1 && singleton->threads[th_index].current_task) {
-		return singleton->threads[th_index].current_task->self;
+	if (th_index != -1 && threads[th_index].current_task) {
+		return threads[th_index].current_task->self;
 	} else {
 		return INVALID_TASK_ID;
 	}
@@ -766,6 +774,7 @@ void WorkerThreadPool::init(int p_thread_count, float p_low_priority_task_ratio)
 
 	for (uint32_t i = 0; i < threads.size(); i++) {
 		threads[i].index = i;
+		threads[i].pool = this;
 		threads[i].thread.start(&WorkerThreadPool::_thread_function, &threads[i]);
 		thread_ids.insert(threads[i].thread.get_id(), i);
 	}
@@ -832,10 +841,33 @@ void WorkerThreadPool::_bind_methods() {
 	ClassDB::bind_method(D_METHOD("wait_for_group_task_completion", "group_id"), &WorkerThreadPool::wait_for_group_task_completion);
 }
 
-WorkerThreadPool::WorkerThreadPool() {
-	singleton = this;
+WorkerThreadPool *WorkerThreadPool::get_named_pool(const StringName &p_name) {
+	WorkerThreadPool **pool_ptr = named_pools.getptr(p_name);
+	if (pool_ptr) {
+		return *pool_ptr;
+	} else {
+		WorkerThreadPool *pool = memnew(WorkerThreadPool(false));
+		pool->init();
+		named_pools[p_name] = pool;
+		return pool;
+	}
+}
+
+WorkerThreadPool::WorkerThreadPool(bool p_singleton) {
+	if (p_singleton) {
+		singleton = this;
+	}
 }
 
 WorkerThreadPool::~WorkerThreadPool() {
 	finish();
+
+	if (this == singleton) {
+		singleton = nullptr;
+		for (KeyValue<StringName, WorkerThreadPool *> &E : named_pools) {
+			E.value->finish();
+			memdelete(E.value);
+		}
+		named_pools.clear();
+	}
 }

+ 9 - 3
core/object/worker_thread_pool.h

@@ -119,6 +119,7 @@ private:
 		Task *current_task = nullptr;
 		Task *awaited_task = nullptr; // Null if not awaiting the condition variable, or special value (YIELDING).
 		ConditionVariable cond_var;
+		WorkerThreadPool *pool = nullptr;
 
 		ThreadData() :
 				signaled(false),
@@ -166,6 +167,8 @@ private:
 
 	uint64_t last_task = 1;
 
+	static HashMap<StringName, WorkerThreadPool *> named_pools;
+
 	static void _thread_function(void *p_user);
 
 	void _process_task(Task *task);
@@ -266,9 +269,12 @@ public:
 #endif
 	}
 
+	// Note: Do not use this unless you know what you are doing, and it is absolutely necessary. Main thread pool (`get_singleton()`) should be preferred instead.
+	static WorkerThreadPool *get_named_pool(const StringName &p_name);
+
 	static WorkerThreadPool *get_singleton() { return singleton; }
-	static int get_thread_index();
-	static TaskID get_caller_task_id();
+	int get_thread_index() const;
+	TaskID get_caller_task_id() const;
 
 #ifdef THREADS_ENABLED
 	_ALWAYS_INLINE_ static uint32_t thread_enter_unlock_allowance_zone(const MutexLock<BinaryMutex> &p_lock) { return _thread_enter_unlock_allowance_zone(p_lock._get_lock()); }
@@ -285,7 +291,7 @@ public:
 	void init(int p_thread_count = -1, float p_low_priority_task_ratio = 0.3);
 	void exit_languages_threads();
 	void finish();
-	WorkerThreadPool();
+	WorkerThreadPool(bool p_singleton = true);
 	~WorkerThreadPool();
 };
 

+ 3 - 3
core/os/os.h

@@ -362,9 +362,9 @@ public:
 	// This is invoked by the GDExtensionManager after loading GDExtensions specified by the project.
 	virtual void load_platform_gdextensions() const {}
 
-	// Windows only. Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
-	virtual bool _test_create_rendering_device_and_gl() const { return true; }
-	virtual bool _test_create_rendering_device() const { return true; }
+	// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
+	virtual bool _test_create_rendering_device_and_gl(const String &p_display_driver) const { return true; }
+	virtual bool _test_create_rendering_device(const String &p_display_driver) const { return true; }
 
 	OS();
 	virtual ~OS();

+ 1 - 1
core/variant/callable.cpp

@@ -188,7 +188,7 @@ int Callable::get_argument_count(bool *r_is_valid) const {
 	if (is_custom()) {
 		bool valid = false;
 		return custom->get_argument_count(r_is_valid ? *r_is_valid : valid);
-	} else if (!is_null()) {
+	} else if (is_valid()) {
 		return get_object()->get_method_argument_count(method, r_is_valid);
 	} else {
 		if (r_is_valid) {

+ 1 - 1
doc/classes/LineEdit.xml

@@ -252,7 +252,7 @@
 			The caret's column position inside the [LineEdit]. When set, the text may scroll to accommodate it.
 		</member>
 		<member name="caret_force_displayed" type="bool" setter="set_caret_force_displayed" getter="is_caret_force_displayed" default="false">
-			If [code]true[/code], the [LineEdit] will always show the caret, even if focus is lost.
+			If [code]true[/code], the [LineEdit] will always show the caret, even if not editing or focus is lost.
 		</member>
 		<member name="caret_mid_grapheme" type="bool" setter="set_caret_mid_grapheme_enabled" getter="is_caret_mid_grapheme_enabled" default="false">
 			Allow moving caret, selecting and removing the individual composite character components.

+ 1 - 1
doc/classes/NativeMenu.xml

@@ -374,7 +374,7 @@
 			<param index="0" name="rid" type="RID" />
 			<description>
 				Returns global menu open callback.
-				b]Note:[/b] This method is implemented only on macOS.
+				[b]Note:[/b] This method is implemented only on macOS.
 			</description>
 		</method>
 		<method name="get_size" qualifiers="const">

+ 1 - 1
doc/classes/RDTextureView.xml

@@ -9,7 +9,7 @@
 	<tutorials>
 	</tutorials>
 	<members>
-		<member name="format_override" type="int" setter="set_format_override" getter="get_format_override" enum="RenderingDevice.DataFormat" default="218">
+		<member name="format_override" type="int" setter="set_format_override" getter="get_format_override" enum="RenderingDevice.DataFormat" default="232">
 			Optional override for the data format to return sampled values in. The corresponding [RDTextureFormat] must have had this added as a shareable format. The default value of [constant RenderingDevice.DATA_FORMAT_MAX] does not override the format.
 		</member>
 		<member name="swizzle_a" type="int" setter="set_swizzle_a" getter="get_swizzle_a" enum="RenderingDevice.TextureSwizzle" default="6">

+ 1 - 1
doc/classes/RDVertexAttribute.xml

@@ -9,7 +9,7 @@
 	<tutorials>
 	</tutorials>
 	<members>
-		<member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="218">
+		<member name="format" type="int" setter="set_format" getter="get_format" enum="RenderingDevice.DataFormat" default="232">
 			The way that this attribute's data is interpreted when sent to a shader.
 		</member>
 		<member name="frequency" type="int" setter="set_frequency" getter="get_frequency" enum="RenderingDevice.VertexFrequency" default="0">

+ 29 - 1
doc/classes/RenderingDevice.xml

@@ -1871,7 +1871,35 @@
 		<constant name="DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM" value="217" enum="DataFormat">
 			16-bit-per-channel unsigned floating-point green/blue/red channel data with normalized value, plus 6 unused bits after each channel. Stored across 3 separate planes (green + blue + red). Values are in the [code][0.0, 1.0][/code] range.
 		</constant>
-		<constant name="DATA_FORMAT_MAX" value="218" enum="DataFormat">
+		<constant name="DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK" value="218" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK" value="219" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK" value="220" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK" value="221" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK" value="222" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK" value="223" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK" value="224" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK" value="225" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK" value="226" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK" value="227" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK" value="228" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK" value="229" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK" value="230" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK" value="231" enum="DataFormat">
+		</constant>
+		<constant name="DATA_FORMAT_MAX" value="232" enum="DataFormat">
 			Represents the size of the [enum DataFormat] enum.
 		</constant>
 		<constant name="BARRIER_MASK_VERTEX" value="1" enum="BarrierMask" is_bitfield="true">

+ 1 - 0
drivers/apple/joypad_apple.h

@@ -43,6 +43,7 @@ struct GameController {
 	RumbleContext *rumble_context API_AVAILABLE(macos(11.0), ios(14.0), tvos(14.0)) = nil;
 	NSInteger ff_effect_timestamp = 0;
 	bool force_feedback = false;
+	bool nintendo_button_layout = false;
 
 	GameController(int p_joy_id, GCController *p_controller);
 	~GameController();

+ 18 - 4
drivers/apple/joypad_apple.mm

@@ -144,6 +144,12 @@ GameController::GameController(int p_joy_id, GCController *p_controller) :
 		}
 	}
 
+	if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) {
+		if ([controller.productCategory isEqualToString:@"Switch Pro Controller"] || [controller.productCategory isEqualToString:@"Nintendo Switch Joy-Con (L/R)"]) {
+			nintendo_button_layout = true;
+		}
+	}
+
 	int l_joy_id = joy_id;
 
 	auto BUTTON = [l_joy_id](JoyButton p_button) {
@@ -155,10 +161,18 @@ GameController::GameController(int p_joy_id, GCController *p_controller) :
 	if (controller.extendedGamepad != nil) {
 		GCExtendedGamepad *gamepad = controller.extendedGamepad;
 
-		gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
-		gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::B);
-		gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
-		gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::Y);
+		if (nintendo_button_layout) {
+			gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::B);
+			gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::A);
+			gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::Y);
+			gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::X);
+		} else {
+			gamepad.buttonA.pressedChangedHandler = BUTTON(JoyButton::A);
+			gamepad.buttonB.pressedChangedHandler = BUTTON(JoyButton::B);
+			gamepad.buttonX.pressedChangedHandler = BUTTON(JoyButton::X);
+			gamepad.buttonY.pressedChangedHandler = BUTTON(JoyButton::Y);
+		}
+
 		gamepad.leftShoulder.pressedChangedHandler = BUTTON(JoyButton::LEFT_SHOULDER);
 		gamepad.rightShoulder.pressedChangedHandler = BUTTON(JoyButton::RIGHT_SHOULDER);
 		gamepad.dpad.up.pressedChangedHandler = BUTTON(JoyButton::DPAD_UP);

+ 14 - 0
drivers/d3d12/rendering_device_driver_d3d12.cpp

@@ -332,6 +332,20 @@ const RenderingDeviceDriverD3D12::D3D12Format RenderingDeviceDriverD3D12::RD_TO_
 	/* DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM */ {},
 	/* DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM */ {},
 	/* DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM */ {},
+	/* DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK*/ {},
+	/* DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK */ {},
+	/* DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK */ {},
 };
 
 Error RenderingDeviceDriverD3D12::DescriptorsHeap::allocate(ID3D12Device *p_device, D3D12_DESCRIPTOR_HEAP_TYPE p_type, uint32_t p_descriptor_count, bool p_for_gpu) {

+ 1 - 1
drivers/gles3/effects/copy_effects.cpp

@@ -243,7 +243,7 @@ void CopyEffects::gaussian_blur(GLuint p_source_texture, int p_mipmap_count, con
 
 		glBindTexture(GL_TEXTURE_2D, p_source_texture);
 		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_BASE_LEVEL, i - 1);
-		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i - 1);
+		glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAX_LEVEL, i);
 		glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, p_source_texture, i);
 #ifdef DEV_ENABLED
 		GLenum status = glCheckFramebufferStatus(GL_FRAMEBUFFER);

+ 6 - 0
drivers/gles3/rasterizer_canvas_gles3.cpp

@@ -1152,6 +1152,12 @@ void RasterizerCanvasGLES3::_record_item_commands(const Item *p_item, RID p_rend
 					// Reset base data.
 					_update_transform_2d_to_mat2x3(base_transform * draw_transform, state.instance_data_array[r_index].world);
 					_prepare_canvas_texture(state.canvas_instance_batches[state.current_batch_index].tex, state.canvas_instance_batches[state.current_batch_index].filter, state.canvas_instance_batches[state.current_batch_index].repeat, r_index, texpixel_size);
+					state.instance_data_array[r_index].lights[0] = lights[0];
+					state.instance_data_array[r_index].lights[1] = lights[1];
+					state.instance_data_array[r_index].lights[2] = lights[2];
+					state.instance_data_array[r_index].lights[3] = lights[3];
+					state.instance_data_array[r_index].flags = base_flags;
+					state.instance_data_array[r_index].instance_uniforms_ofs = p_item->instance_allocated_shader_uniforms_offset;
 
 					for (uint32_t j = 0; j < 3; j++) {
 						int offset = j == 0 ? 0 : 1;

+ 8 - 20
drivers/gles3/storage/particles_storage.cpp

@@ -513,7 +513,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
 	GLES3::TextureStorage *texture_storage = GLES3::TextureStorage::get_singleton();
 	GLES3::MaterialStorage *material_storage = GLES3::MaterialStorage::get_singleton();
 
-	double new_phase = Math::fmod(p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0);
+	double new_phase = Math::fmod(p_particles->phase + (p_delta / p_particles->lifetime), 1.0);
 
 	//update current frame
 	ParticlesFrameParams frame_params;
@@ -534,7 +534,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
 	p_particles->phase = new_phase;
 
 	frame_params.time = RSG::rasterizer->get_total_time();
-	frame_params.delta = p_delta * p_particles->speed_scale;
+	frame_params.delta = p_delta;
 	frame_params.random_seed = p_particles->random_seed;
 	frame_params.explosiveness = p_particles->explosiveness;
 	frame_params.randomness = p_particles->randomness;
@@ -1097,8 +1097,6 @@ void ParticlesStorage::update_particles() {
 			fixed_fps = particles->fixed_fps;
 		}
 
-		bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
-
 		if (particles->clear && particles->pre_process_time > 0.0) {
 			double frame_time;
 			if (fixed_fps > 0) {
@@ -1115,37 +1113,27 @@ void ParticlesStorage::update_particles() {
 			}
 		}
 
+		double time_scale = MAX(particles->speed_scale, 0.0);
+
 		if (fixed_fps > 0) {
-			double frame_time;
-			double decr;
-			if (zero_time_scale) {
-				frame_time = 0.0;
-				decr = 1.0 / fixed_fps;
-			} else {
-				frame_time = 1.0 / fixed_fps;
-				decr = frame_time;
-			}
+			double frame_time = 1.0 / fixed_fps;
 			double delta = RSG::rasterizer->get_frame_delta_time();
 			if (delta > 0.1) { //avoid recursive stalls if fps goes below 10
 				delta = 0.1;
 			} else if (delta <= 0.0) { //unlikely but..
 				delta = 0.001;
 			}
-			double todo = particles->frame_remainder + delta;
+			double todo = particles->frame_remainder + delta * time_scale;
 
 			while (todo >= frame_time) {
 				_particles_process(particles, frame_time);
-				todo -= decr;
+				todo -= frame_time;
 			}
 
 			particles->frame_remainder = todo;
 
 		} else {
-			if (zero_time_scale) {
-				_particles_process(particles, 0.0);
-			} else {
-				_particles_process(particles, RSG::rasterizer->get_frame_delta_time());
-			}
+			_particles_process(particles, RSG::rasterizer->get_frame_delta_time() * time_scale);
 		}
 
 		if (particles->request_process_time > 0.0) {

+ 14 - 0
drivers/metal/pixel_formats.mm

@@ -493,32 +493,46 @@ void PixelFormats::initDataFormatCapabilities() {
 	addDataFormatDesc(EAC_R11G11_SNORM_BLOCK, EAC_RG11Snorm, Invalid, Invalid, Invalid, 4, 4, 16, Compressed);
 
 	addDataFormatDesc(ASTC_4x4_UNORM_BLOCK, ASTC_4x4_LDR, Invalid, Invalid, Invalid, 4, 4, 16, Compressed);
+	addDataFormatDesc(ASTC_4x4_SFLOAT_BLOCK, ASTC_4x4_HDR, Invalid, Invalid, Invalid, 4, 4, 16, Compressed);
 	addDataFormatDesc(ASTC_4x4_SRGB_BLOCK, ASTC_4x4_sRGB, Invalid, Invalid, Invalid, 4, 4, 16, Compressed);
 	addDataFormatDesc(ASTC_5x4_UNORM_BLOCK, ASTC_5x4_LDR, Invalid, Invalid, Invalid, 5, 4, 16, Compressed);
+	addDataFormatDesc(ASTC_5x4_SFLOAT_BLOCK, ASTC_5x4_HDR, Invalid, Invalid, Invalid, 5, 4, 16, Compressed);
 	addDataFormatDesc(ASTC_5x4_SRGB_BLOCK, ASTC_5x4_sRGB, Invalid, Invalid, Invalid, 5, 4, 16, Compressed);
 	addDataFormatDesc(ASTC_5x5_UNORM_BLOCK, ASTC_5x5_LDR, Invalid, Invalid, Invalid, 5, 5, 16, Compressed);
+	addDataFormatDesc(ASTC_5x5_SFLOAT_BLOCK, ASTC_5x5_HDR, Invalid, Invalid, Invalid, 5, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_5x5_SRGB_BLOCK, ASTC_5x5_sRGB, Invalid, Invalid, Invalid, 5, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_6x5_UNORM_BLOCK, ASTC_6x5_LDR, Invalid, Invalid, Invalid, 6, 5, 16, Compressed);
+	addDataFormatDesc(ASTC_6x5_SFLOAT_BLOCK, ASTC_6x5_HDR, Invalid, Invalid, Invalid, 6, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_6x5_SRGB_BLOCK, ASTC_6x5_sRGB, Invalid, Invalid, Invalid, 6, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_6x6_UNORM_BLOCK, ASTC_6x6_LDR, Invalid, Invalid, Invalid, 6, 6, 16, Compressed);
+	addDataFormatDesc(ASTC_6x6_SFLOAT_BLOCK, ASTC_6x6_HDR, Invalid, Invalid, Invalid, 6, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_6x6_SRGB_BLOCK, ASTC_6x6_sRGB, Invalid, Invalid, Invalid, 6, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_8x5_UNORM_BLOCK, ASTC_8x5_LDR, Invalid, Invalid, Invalid, 8, 5, 16, Compressed);
+	addDataFormatDesc(ASTC_8x5_SFLOAT_BLOCK, ASTC_8x5_HDR, Invalid, Invalid, Invalid, 8, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_8x5_SRGB_BLOCK, ASTC_8x5_sRGB, Invalid, Invalid, Invalid, 8, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_8x6_UNORM_BLOCK, ASTC_8x6_LDR, Invalid, Invalid, Invalid, 8, 6, 16, Compressed);
+	addDataFormatDesc(ASTC_8x6_SFLOAT_BLOCK, ASTC_8x6_HDR, Invalid, Invalid, Invalid, 8, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_8x6_SRGB_BLOCK, ASTC_8x6_sRGB, Invalid, Invalid, Invalid, 8, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_8x8_UNORM_BLOCK, ASTC_8x8_LDR, Invalid, Invalid, Invalid, 8, 8, 16, Compressed);
+	addDataFormatDesc(ASTC_8x8_SFLOAT_BLOCK, ASTC_8x8_HDR, Invalid, Invalid, Invalid, 8, 8, 16, Compressed);
 	addDataFormatDesc(ASTC_8x8_SRGB_BLOCK, ASTC_8x8_sRGB, Invalid, Invalid, Invalid, 8, 8, 16, Compressed);
 	addDataFormatDesc(ASTC_10x5_UNORM_BLOCK, ASTC_10x5_LDR, Invalid, Invalid, Invalid, 10, 5, 16, Compressed);
+	addDataFormatDesc(ASTC_10x5_SFLOAT_BLOCK, ASTC_10x5_HDR, Invalid, Invalid, Invalid, 10, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_10x5_SRGB_BLOCK, ASTC_10x5_sRGB, Invalid, Invalid, Invalid, 10, 5, 16, Compressed);
 	addDataFormatDesc(ASTC_10x6_UNORM_BLOCK, ASTC_10x6_LDR, Invalid, Invalid, Invalid, 10, 6, 16, Compressed);
+	addDataFormatDesc(ASTC_10x6_SFLOAT_BLOCK, ASTC_10x6_HDR, Invalid, Invalid, Invalid, 10, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_10x6_SRGB_BLOCK, ASTC_10x6_sRGB, Invalid, Invalid, Invalid, 10, 6, 16, Compressed);
 	addDataFormatDesc(ASTC_10x8_UNORM_BLOCK, ASTC_10x8_LDR, Invalid, Invalid, Invalid, 10, 8, 16, Compressed);
+	addDataFormatDesc(ASTC_10x8_SFLOAT_BLOCK, ASTC_10x8_HDR, Invalid, Invalid, Invalid, 10, 8, 16, Compressed);
 	addDataFormatDesc(ASTC_10x8_SRGB_BLOCK, ASTC_10x8_sRGB, Invalid, Invalid, Invalid, 10, 8, 16, Compressed);
 	addDataFormatDesc(ASTC_10x10_UNORM_BLOCK, ASTC_10x10_LDR, Invalid, Invalid, Invalid, 10, 10, 16, Compressed);
+	addDataFormatDesc(ASTC_10x10_SFLOAT_BLOCK, ASTC_10x10_HDR, Invalid, Invalid, Invalid, 10, 10, 16, Compressed);
 	addDataFormatDesc(ASTC_10x10_SRGB_BLOCK, ASTC_10x10_sRGB, Invalid, Invalid, Invalid, 10, 10, 16, Compressed);
 	addDataFormatDesc(ASTC_12x10_UNORM_BLOCK, ASTC_12x10_LDR, Invalid, Invalid, Invalid, 12, 10, 16, Compressed);
+	addDataFormatDesc(ASTC_12x10_SFLOAT_BLOCK, ASTC_12x10_HDR, Invalid, Invalid, Invalid, 12, 10, 16, Compressed);
 	addDataFormatDesc(ASTC_12x10_SRGB_BLOCK, ASTC_12x10_sRGB, Invalid, Invalid, Invalid, 12, 10, 16, Compressed);
 	addDataFormatDesc(ASTC_12x12_UNORM_BLOCK, ASTC_12x12_LDR, Invalid, Invalid, Invalid, 12, 12, 16, Compressed);
+	addDataFormatDesc(ASTC_12x12_SFLOAT_BLOCK, ASTC_12x12_HDR, Invalid, Invalid, Invalid, 12, 12, 16, Compressed);
 	addDataFormatDesc(ASTC_12x12_SRGB_BLOCK, ASTC_12x12_sRGB, Invalid, Invalid, Invalid, 12, 12, 16, Compressed);
 
 	addDfFormatDescChromaSubsampling(G8B8G8R8_422_UNORM, GBGR422, 1, 8, 2, 1, 4);

+ 23 - 0
drivers/vulkan/rendering_device_driver_vulkan.cpp

@@ -276,6 +276,20 @@ static const VkFormat RD_TO_VK_FORMAT[RDD::DATA_FORMAT_MAX] = {
 	VK_FORMAT_G16_B16_R16_3PLANE_422_UNORM,
 	VK_FORMAT_G16_B16R16_2PLANE_422_UNORM,
 	VK_FORMAT_G16_B16_R16_3PLANE_444_UNORM,
+	VK_FORMAT_ASTC_4x4_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_5x4_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_5x5_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_6x5_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_6x6_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_8x5_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_8x6_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_8x8_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_10x5_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_10x6_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_10x8_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_10x10_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_12x10_SFLOAT_BLOCK,
+	VK_FORMAT_ASTC_12x12_SFLOAT_BLOCK,
 };
 
 static VkImageLayout RD_TO_VK_LAYOUT[RDD::TEXTURE_LAYOUT_MAX] = {
@@ -514,6 +528,7 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
 	_register_requested_device_extension(VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, false);
 	_register_requested_device_extension(VK_EXT_ASTC_DECODE_MODE_EXTENSION_NAME, false);
 	_register_requested_device_extension(VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, false);
+	_register_requested_device_extension(VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME, false);
 
 	if (Engine::get_singleton()->is_generate_spirv_debug_info_enabled()) {
 		_register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true);
@@ -1547,6 +1562,10 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
 		} break;
 		case MEMORY_ALLOCATION_TYPE_GPU: {
 			vma_usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
+			if (!Engine::get_singleton()->is_extra_gpu_memory_tracking_enabled()) {
+				// We must set it right now or else vmaFindMemoryTypeIndexForBufferInfo will use wrong parameters.
+				alloc_create_info.usage = vma_usage;
+			}
 			alloc_create_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 			if (p_size <= SMALL_ALLOCATION_MAX_SIZE) {
 				uint32_t mem_type_index = 0;
@@ -2140,6 +2159,10 @@ void RenderingDeviceDriverVulkan::texture_unmap(TextureID p_texture) {
 }
 
 BitField<RDD::TextureUsageBits> RenderingDeviceDriverVulkan::texture_get_usages_supported_by_format(DataFormat p_format, bool p_cpu_readable) {
+	if (p_format >= DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK && p_format <= DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK && !enabled_device_extension_names.has(VK_EXT_TEXTURE_COMPRESSION_ASTC_HDR_EXTENSION_NAME)) {
+		// Formats that were introduced later with extensions must not reach vkGetPhysicalDeviceFormatProperties if the extension isn't available. This means it's not supported.
+		return 0;
+	}
 	VkFormatProperties properties = {};
 	vkGetPhysicalDeviceFormatProperties(physical_device, RD_TO_VK_FORMAT[p_format], &properties);
 

+ 1 - 1
drivers/windows/file_access_windows.cpp

@@ -418,7 +418,7 @@ uint64_t FileAccessWindows::_get_modified_time(const String &p_file) {
 		file = file.substr(0, file.length() - 1);
 	}
 
-	HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), GENERIC_READ, FILE_SHARE_READ, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
+	HANDLE handle = CreateFileW((LPCWSTR)(file.utf16().get_data()), FILE_READ_ATTRIBUTES, FILE_SHARE_READ | FILE_SHARE_WRITE | FILE_SHARE_DELETE, nullptr, OPEN_EXISTING, FILE_FLAG_BACKUP_SEMANTICS, nullptr);
 
 	if (handle != INVALID_HANDLE_VALUE) {
 		FILETIME ft_create, ft_write;

+ 2 - 1
editor/animation_track_editor.cpp

@@ -5088,7 +5088,7 @@ void AnimationTrackEditor::_update_nearest_fps_label() {
 		nearest_fps_label->hide();
 	} else {
 		nearest_fps_label->show();
-		nearest_fps_label->set_text("Nearest FPS: " + itos(nearest_fps));
+		nearest_fps_label->set_text(vformat(TTR("Nearest FPS: %d"), nearest_fps));
 	}
 }
 
@@ -7688,6 +7688,7 @@ AnimationTrackEditor::AnimationTrackEditor() {
 	fps_compat->connect(SceneStringName(toggled), callable_mp(this, &AnimationTrackEditor::_update_fps_compat_mode));
 
 	nearest_fps_label = memnew(Label);
+	nearest_fps_label->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
 	bottom_hf->add_child(nearest_fps_label);
 
 	step = memnew(EditorSpinSlider);

+ 8 - 3
editor/connections_dialog.cpp

@@ -453,7 +453,13 @@ void ConnectDialog::_update_ok_enabled() {
 }
 
 void ConnectDialog::_update_warning_label() {
-	Ref<Script> scr = source->get_node(dst_path)->get_script();
+	Node *dst = source->get_node(dst_path);
+	if (dst == nullptr) {
+		warning_label->set_visible(false);
+		return;
+	}
+
+	Ref<Script> scr = dst->get_script();
 	if (scr.is_null()) {
 		warning_label->set_visible(false);
 		return;
@@ -865,9 +871,8 @@ ConnectDialog::ConnectDialog() {
 	hbc_method->add_child(dst_method);
 	register_text_enter(dst_method);
 
-	open_method_tree = memnew(Button);
+	open_method_tree = memnew(Button(TTRC("Pick")));
 	hbc_method->add_child(open_method_tree);
-	open_method_tree->set_text("Pick");
 	open_method_tree->connect(SceneStringName(pressed), callable_mp(this, &ConnectDialog::_open_method_popup));
 
 	advanced = memnew(CheckButton(TTR("Advanced")));

+ 1 - 0
editor/debugger/debug_adapter/debug_adapter_protocol.cpp

@@ -1032,6 +1032,7 @@ void DebugAdapterProtocol::on_debug_paused() {
 void DebugAdapterProtocol::on_debug_stopped() {
 	notify_exited();
 	notify_terminated();
+	reset_ids();
 }
 
 void DebugAdapterProtocol::on_debug_output(const String &p_message, int p_type) {

+ 2 - 0
editor/editor_inspector.cpp

@@ -3440,6 +3440,8 @@ void EditorInspector::update_tree() {
 		// Recreate the category vbox if it was reset.
 		if (category_vbox == nullptr) {
 			category_vbox = memnew(VBoxContainer);
+			int separation = get_theme_constant(SNAME("v_separation"), SNAME("EditorInspector"));
+			category_vbox->add_theme_constant_override(SNAME("separation"), separation);
 			category_vbox->hide();
 			main_vbox->add_child(category_vbox);
 		}

+ 19 - 7
editor/editor_node.cpp

@@ -1207,7 +1207,7 @@ void EditorNode::_sources_changed(bool p_exist) {
 		}
 
 		// Start preview thread now that it's safe.
-		if (!singleton->cmdline_export_mode) {
+		if (!singleton->cmdline_mode) {
 			EditorResourcePreview::get_singleton()->start();
 		}
 
@@ -1807,7 +1807,7 @@ void EditorNode::_save_scene_with_preview(String p_file, int p_idx) {
 	save_scene_progress->step(TTR("Saving Scene"), 4);
 	_save_scene(p_file, p_idx);
 
-	if (!singleton->cmdline_export_mode) {
+	if (!singleton->cmdline_mode) {
 		EditorResourcePreview::get_singleton()->check_for_invalidation(p_file);
 	}
 
@@ -1867,6 +1867,7 @@ int EditorNode::_save_external_resources(bool p_also_save_external_data) {
 		res->set_edited(false);
 	}
 
+	bool script_was_saved = false;
 	for (const String &E : edited_resources) {
 		Ref<Resource> res = ResourceCache::get_ref(E);
 		if (res.is_null()) {
@@ -1876,10 +1877,18 @@ int EditorNode::_save_external_resources(bool p_also_save_external_data) {
 		if (ps.is_valid()) {
 			continue; // Do not save PackedScenes, this will mess up the editor.
 		}
+		if (!script_was_saved) {
+			Ref<Script> scr = res;
+			script_was_saved = scr.is_valid();
+		}
 		ResourceSaver::save(res, res->get_path(), flg);
 		saved++;
 	}
 
+	if (script_was_saved) {
+		ScriptEditor::get_singleton()->update_script_times();
+	}
+
 	if (p_also_save_external_data) {
 		for (int i = 0; i < editor_data.get_editor_plugin_count(); i++) {
 			EditorPlugin *plugin = editor_data.get_editor_plugin(i);
@@ -4944,7 +4953,7 @@ bool EditorNode::is_object_of_custom_type(const Object *p_object, const StringNa
 void EditorNode::progress_add_task(const String &p_task, const String &p_label, int p_steps, bool p_can_cancel) {
 	if (!singleton) {
 		return;
-	} else if (singleton->cmdline_export_mode) {
+	} else if (singleton->cmdline_mode) {
 		print_line(p_task + ": begin: " + p_label + " steps: " + itos(p_steps));
 	} else if (singleton->progress_dialog) {
 		singleton->progress_dialog->add_task(p_task, p_label, p_steps, p_can_cancel);
@@ -4954,7 +4963,7 @@ void EditorNode::progress_add_task(const String &p_task, const String &p_label,
 bool EditorNode::progress_task_step(const String &p_task, const String &p_state, int p_step, bool p_force_refresh) {
 	if (!singleton) {
 		return false;
-	} else if (singleton->cmdline_export_mode) {
+	} else if (singleton->cmdline_mode) {
 		print_line("\t" + p_task + ": step " + itos(p_step) + ": " + p_state);
 		return false;
 	} else if (singleton->progress_dialog) {
@@ -4967,7 +4976,7 @@ bool EditorNode::progress_task_step(const String &p_task, const String &p_state,
 void EditorNode::progress_end_task(const String &p_task) {
 	if (!singleton) {
 		return;
-	} else if (singleton->cmdline_export_mode) {
+	} else if (singleton->cmdline_mode) {
 		print_line(p_task + ": end");
 	} else if (singleton->progress_dialog) {
 		singleton->progress_dialog->end_task(p_task);
@@ -5220,7 +5229,7 @@ Error EditorNode::export_preset(const String &p_preset, const String &p_path, bo
 	export_defer.android_build_template = p_android_build_template;
 	export_defer.patch = p_patch;
 	export_defer.patches = p_patches;
-	cmdline_export_mode = true;
+	cmdline_mode = true;
 	return OK;
 }
 
@@ -6848,7 +6857,10 @@ EditorNode::EditorNode() {
 	DEV_ASSERT(!singleton);
 	singleton = this;
 
-	set_translation_domain("godot.editor");
+	// Detecting headless mode, that means the editor is running in command line.
+	if (!DisplayServer::get_singleton()->window_can_draw()) {
+		cmdline_mode = true;
+	}
 
 	Resource::_get_local_scene_func = _resource_get_edited_scene;
 

+ 1 - 1
editor/editor_node.h

@@ -419,7 +419,7 @@ private:
 	bool script_distraction_free = false;
 
 	bool changing_scene = false;
-	bool cmdline_export_mode = false;
+	bool cmdline_mode = false;
 	bool convert_old = false;
 	bool immediate_dialog_confirmed = false;
 	bool opening_prev = false;

+ 3 - 0
editor/export/project_export.cpp

@@ -1746,11 +1746,13 @@ ProjectExportDialog::ProjectExportDialog() {
 	export_error = memnew(Label);
 	main_vb->add_child(export_error);
 	export_error->hide();
+	export_error->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
 	export_error->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
 
 	export_warning = memnew(Label);
 	main_vb->add_child(export_warning);
 	export_warning->hide();
+	export_warning->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
 	export_warning->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("warning_color"), EditorStringName(Editor)));
 
 	export_templates_error = memnew(HBoxContainer);
@@ -1759,6 +1761,7 @@ ProjectExportDialog::ProjectExportDialog() {
 
 	Label *export_error2 = memnew(Label);
 	export_templates_error->add_child(export_error2);
+	export_error2->set_text_overrun_behavior(TextServer::OVERRUN_TRIM_WORD_ELLIPSIS);
 	export_error2->add_theme_color_override(SceneStringName(font_color), EditorNode::get_singleton()->get_editor_theme()->get_color(SNAME("error_color"), EditorStringName(Editor)));
 	export_error2->set_text(String::utf8("•  ") + TTR("Export templates for this platform are missing:") + " ");
 

+ 2 - 0
editor/plugins/polygon_3d_editor_plugin.cpp

@@ -560,6 +560,7 @@ Polygon3DEditor::Polygon3DEditor() {
 	line_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
 	line_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
 	line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+	line_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
 	line_material->set_albedo(Color(1, 1, 1));
 
 	handle_material.instantiate();
@@ -569,6 +570,7 @@ Polygon3DEditor::Polygon3DEditor() {
 	handle_material->set_flag(StandardMaterial3D::FLAG_ALBEDO_FROM_VERTEX_COLOR, true);
 	handle_material->set_flag(StandardMaterial3D::FLAG_SRGB_VERTEX_COLOR, true);
 	handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_FOG, true);
+	handle_material->set_flag(StandardMaterial3D::FLAG_DISABLE_DEPTH_TEST, true);
 	Ref<Texture2D> handle = EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("Editor3DHandle"), EditorStringName(EditorIcons));
 	handle_material->set_point_size(handle->get_width());
 	handle_material->set_texture(StandardMaterial3D::TEXTURE_ALBEDO, handle);

+ 10 - 0
editor/plugins/script_editor_plugin.cpp

@@ -2817,6 +2817,15 @@ void ScriptEditor::save_all_scripts() {
 	_update_script_names();
 }
 
+void ScriptEditor::update_script_times() {
+	for (int i = 0; i < tab_container->get_tab_count(); i++) {
+		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
+		if (se) {
+			se->edited_file_data.last_modified_time = FileAccess::get_modified_time(se->edited_file_data.path);
+		}
+	}
+}
+
 void ScriptEditor::apply_scripts() const {
 	for (int i = 0; i < tab_container->get_tab_count(); i++) {
 		ScriptEditorBase *se = Object::cast_to<ScriptEditorBase>(tab_container->get_tab_control(i));
@@ -4205,6 +4214,7 @@ ScriptEditor::ScriptEditor(WindowWrapper *p_wrapper) {
 	overview_vbox->add_child(buttons_hbox);
 
 	filename = memnew(Label);
+	filename->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
 	filename->set_clip_text(true);
 	filename->set_h_size_flags(SIZE_EXPAND_FILL);
 	filename->set_vertical_alignment(VERTICAL_ALIGNMENT_CENTER);

+ 1 - 0
editor/plugins/script_editor_plugin.h

@@ -569,6 +569,7 @@ public:
 	PackedStringArray get_unsaved_scripts() const;
 	void save_current_script();
 	void save_all_scripts();
+	void update_script_times();
 
 	void set_window_layout(Ref<ConfigFile> p_layout);
 	void get_window_layout(Ref<ConfigFile> p_layout);

+ 22 - 6
editor/plugins/visual_shader_editor_plugin.cpp

@@ -7623,12 +7623,15 @@ public:
 	}
 
 	void _item_selected(int p_item) {
-		editor->call_deferred(SNAME("_input_select_item"), input, get_item_text(p_item));
+		editor->call_deferred(SNAME("_input_select_item"), input, get_item_metadata(p_item));
 	}
 
 	void setup(VisualShaderEditor *p_editor, const Ref<VisualShaderNodeInput> &p_input) {
+		set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+
 		editor = p_editor;
 		input = p_input;
+
 		Ref<Texture2D> type_icon[] = {
 			EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("float"), EditorStringName(EditorIcons)),
 			EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("int"), EditorStringName(EditorIcons)),
@@ -7641,13 +7644,16 @@ public:
 			EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("ImageTexture"), EditorStringName(EditorIcons)),
 		};
 
-		add_item("[None]");
+		add_item(TTR("[None]"));
+		set_item_metadata(-1, "[None]");
+
 		int to_select = -1;
 		for (int i = 0; i < input->get_input_index_count(); i++) {
 			if (input->get_input_name() == input->get_input_index_name(i)) {
 				to_select = i + 1;
 			}
 			add_icon_item(type_icon[input->get_input_index_type(i)], input->get_input_index_name(i));
+			set_item_metadata(-1, input->get_input_index_name(i));
 		}
 
 		if (to_select >= 0) {
@@ -7672,10 +7678,12 @@ public:
 	}
 
 	void _item_selected(int p_item) {
-		editor->call_deferred(SNAME("_varying_select_item"), varying, get_item_text(p_item));
+		editor->call_deferred(SNAME("_varying_select_item"), varying, get_item_metadata(p_item));
 	}
 
 	void setup(VisualShaderEditor *p_editor, const Ref<VisualShaderNodeVarying> &p_varying, VisualShader::Type p_type) {
+		set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+
 		editor = p_editor;
 		varying = p_varying;
 
@@ -7692,7 +7700,8 @@ public:
 
 		bool is_getter = Ref<VisualShaderNodeVaryingGetter>(p_varying.ptr()).is_valid();
 
-		add_item("[None]");
+		add_item(TTR("[None]"));
+		set_item_metadata(-1, "[None]");
 
 		int to_select = -1;
 		for (int i = 0, j = 0; i < varying->get_varyings_count(); i++) {
@@ -7726,6 +7735,7 @@ public:
 				to_select = i - j + 1;
 			}
 			add_icon_item(type_icon[varying->get_varying_type_by_index(i)], varying->get_varying_name_by_index(i));
+			set_item_metadata(-1, varying->get_varying_name_by_index(i));
 		}
 
 		if (to_select >= 0) {
@@ -7752,10 +7762,12 @@ public:
 	}
 
 	void _item_selected(int p_item) {
-		editor->call_deferred(SNAME("_parameter_ref_select_item"), parameter_ref, get_item_text(p_item));
+		editor->call_deferred(SNAME("_parameter_ref_select_item"), parameter_ref, get_item_metadata(p_item));
 	}
 
 	void setup(VisualShaderEditor *p_editor, const Ref<VisualShaderNodeParameterRef> &p_parameter_ref) {
+		set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
+
 		editor = p_editor;
 		parameter_ref = p_parameter_ref;
 
@@ -7772,13 +7784,16 @@ public:
 			EditorNode::get_singleton()->get_editor_theme()->get_icon(SNAME("ImageTexture"), EditorStringName(EditorIcons)),
 		};
 
-		add_item("[None]");
+		add_item(TTR("[None]"));
+		set_item_metadata(-1, "[None]");
+
 		int to_select = -1;
 		for (int i = 0; i < p_parameter_ref->get_parameters_count(); i++) {
 			if (p_parameter_ref->get_parameter_name() == p_parameter_ref->get_parameter_name_by_index(i)) {
 				to_select = i + 1;
 			}
 			add_icon_item(type_icon[p_parameter_ref->get_parameter_type_by_index(i)], p_parameter_ref->get_parameter_name_by_index(i));
+			set_item_metadata(-1, p_parameter_ref->get_parameter_name_by_index(i));
 		}
 
 		if (to_select >= 0) {
@@ -8126,6 +8141,7 @@ void EditorPropertyVisualShaderMode::set_option_button_clip(bool p_enable) {
 
 EditorPropertyVisualShaderMode::EditorPropertyVisualShaderMode() {
 	options = memnew(OptionButton);
+	options->set_auto_translate_mode(AUTO_TRANSLATE_MODE_DISABLED);
 	options->set_clip_text(true);
 	add_child(options);
 	add_focusable(options);

+ 0 - 2
editor/project_manager.cpp

@@ -1162,8 +1162,6 @@ void ProjectManager::_titlebar_resized() {
 ProjectManager::ProjectManager(bool p_custom_res) {
 	singleton = this;
 
-	set_translation_domain("godot.editor");
-
 	// Turn off some servers we aren't going to be using in the Project Manager.
 	NavigationServer3D::get_singleton()->set_active(false);
 	PhysicsServer3D::get_singleton()->set_active(false);

+ 4 - 6
editor/project_manager/project_list.cpp

@@ -68,10 +68,13 @@ void ProjectListItemControl::_notification(int p_what) {
 			project_unsupported_features->set_texture(get_editor_theme_icon(SNAME("NodeWarning")));
 
 			favorite_button->set_texture_normal(get_editor_theme_icon(SNAME("Favorites")));
+
 			if (project_is_missing) {
 				explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
+#if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
 			} else {
 				explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
+#endif
 			}
 		} break;
 
@@ -190,9 +193,6 @@ void ProjectListItemControl::set_is_favorite(bool p_favorite) {
 }
 
 void ProjectListItemControl::set_is_missing(bool p_missing) {
-	if (project_is_missing == p_missing) {
-		return;
-	}
 	project_is_missing = p_missing;
 
 	if (project_is_missing) {
@@ -201,10 +201,8 @@ void ProjectListItemControl::set_is_missing(bool p_missing) {
 		explore_button->set_button_icon(get_editor_theme_icon(SNAME("FileBroken")));
 		explore_button->set_tooltip_text(TTR("Error: Project is missing on the filesystem."));
 	} else {
-		project_icon->set_modulate(Color(1, 1, 1, 1.0));
-
-		explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
 #if !defined(ANDROID_ENABLED) && !defined(WEB_ENABLED)
+		explore_button->set_button_icon(get_editor_theme_icon(SNAME("Load")));
 		explore_button->set_tooltip_text(TTR("Show in File Manager"));
 #else
 		// Opening the system file manager is not supported on the Android and web editors.

+ 1 - 1
editor/scene_tree_dock.cpp

@@ -4268,7 +4268,7 @@ List<Node *> SceneTreeDock::paste_nodes(bool p_paste_as_sibling) {
 			// and added to the node_clipboard_edited_scene_owned list.
 			if (d != dup && E2.key->get_owner() == nullptr) {
 				if (node_clipboard_edited_scene_owned.find(const_cast<Node *>(E2.key))) {
-					ur->add_do_method(d, "set_owner", edited_scene);
+					ur->add_do_method(d, "set_owner", owner);
 				}
 			}
 		}

+ 7 - 5
main/main.cpp

@@ -979,7 +979,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 	String project_path = ".";
 	bool upwards = false;
 	String debug_uri = "";
-#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
 	bool test_rd_creation = false;
 	bool test_rd_support = false;
 #endif
@@ -1671,7 +1671,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		} else if (arg == "--debug-stringnames") {
 			StringName::set_debug_stringnames(true);
 #endif
-#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
 		} else if (arg == "--test-rd-support") {
 			test_rd_support = true;
 		} else if (arg == "--test-rd-creation") {
@@ -1880,12 +1880,12 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 #endif
 	}
 
-#if defined(TOOLS_ENABLED) && defined(WINDOWS_ENABLED)
+#if defined(TOOLS_ENABLED) && (defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED))
 	if (test_rd_support) {
 		// Test Rendering Device creation and exit.
 
 		OS::get_singleton()->set_crash_handler_silent();
-		if (OS::get_singleton()->_test_create_rendering_device()) {
+		if (OS::get_singleton()->_test_create_rendering_device(display_driver)) {
 			exit_err = ERR_HELP;
 		} else {
 			exit_err = ERR_UNAVAILABLE;
@@ -1895,7 +1895,7 @@ Error Main::setup(const char *execpath, int argc, char *argv[], bool p_second_ph
 		// Test OpenGL context and Rendering Device simultaneous creation and exit.
 
 		OS::get_singleton()->set_crash_handler_silent();
-		if (OS::get_singleton()->_test_create_rendering_device_and_gl()) {
+		if (OS::get_singleton()->_test_create_rendering_device_and_gl(display_driver)) {
 			exit_err = ERR_HELP;
 		} else {
 			exit_err = ERR_UNAVAILABLE;
@@ -4115,6 +4115,7 @@ int Main::start() {
 		if (editor) {
 			OS::get_singleton()->benchmark_begin_measure("Startup", "Editor");
 
+			sml->get_root()->set_translation_domain("godot.editor");
 			if (editor_pseudolocalization) {
 				translation_server->get_editor_domain()->set_pseudolocalization_enabled(true);
 			}
@@ -4312,6 +4313,7 @@ int Main::start() {
 			OS::get_singleton()->benchmark_begin_measure("Startup", "Project Manager");
 			Engine::get_singleton()->set_editor_hint(true);
 
+			sml->get_root()->set_translation_domain("godot.editor");
 			if (editor_pseudolocalization) {
 				translation_server->get_editor_domain()->set_pseudolocalization_enabled(true);
 			}

+ 2 - 0
modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd

@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim := $AnimationPlayer
 
 func test():

+ 2 - 0
modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd

@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim: AnimationPlayer = $AnimationPlayer
 
 func test():

+ 2 - 0
modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd

@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim = $AnimationPlayer
 
 func test():

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd

@@ -1,5 +1,5 @@
 extends Node
 
 func a():
-    %AnimationPlayer.➡
+    $UniqueAnimationPlayer.➡
     pass

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd

@@ -1,5 +1,5 @@
 extends Node
 
 func a():
-    $UniqueAnimationPlayer.➡
+    %AnimationPlayer.➡
     pass

+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg → modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd → modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.gd


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg → modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd → modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.gd


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg → modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd → modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.gd


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg → modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd → modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.gd


+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg → modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.cfg


+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd → modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test := $A
+@onready var test := $A
 
 func a():
     test.➡

+ 0 - 0
modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg → modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.cfg


+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd → modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test := $AnimationPlayer
+@onready var test := $AnimationPlayer
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test = $A
+@onready var test = $A
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test = $AnimationPlayer
+@onready var test = $AnimationPlayer
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test: AnimationPlayer = $AnimationPlayer
+@onready var test: AnimationPlayer = $AnimationPlayer
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test: Node = $A
+@onready var test: Node = $A
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test: Node = $AnimationPlayer
+@onready var test: Node = $AnimationPlayer
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test: Area2D = $A
+@onready var test: Area2D = $A
 
 func a():
     test.➡

+ 1 - 1
modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd

@@ -1,6 +1,6 @@
 extends Node
 
-var test: Area2D = $AnimationPlayer
+@onready var test: Area2D = $AnimationPlayer
 
 func a():
     test.➡

+ 0 - 0
modules/gdscript/tests/scripts/completion/types/local/infered.cfg → modules/gdscript/tests/scripts/completion/types/local/inferred.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/types/local/infered.gd → modules/gdscript/tests/scripts/completion/types/local/inferred.gd


+ 0 - 0
modules/gdscript/tests/scripts/completion/types/member/infered.cfg → modules/gdscript/tests/scripts/completion/types/member/inferred.cfg


+ 0 - 0
modules/gdscript/tests/scripts/completion/types/member/infered.gd → modules/gdscript/tests/scripts/completion/types/member/inferred.gd


+ 4 - 0
modules/gdscript/tests/test_completion.h

@@ -161,6 +161,8 @@ static void test_directory(const String &p_dir) {
 				owner = scene->get_node(conf.get_value("input", "node_path", "."));
 			}
 
+			// The only requirement is for the script to be parsable, warnings and errors from the analyzer might happen and completion should still work.
+			ERR_PRINT_OFF;
 			if (owner != nullptr) {
 				// Remove the line which contains the sentinel char, to get a valid script.
 				Ref<GDScript> scr;
@@ -184,6 +186,8 @@ static void test_directory(const String &p_dir) {
 			}
 
 			GDScriptLanguage::get_singleton()->complete_code(code, res_path, owner, &options, forced, call_hint);
+			ERR_PRINT_ON;
+
 			String contains_excluded;
 			for (ScriptLanguage::CodeCompletionOption &option : options) {
 				for (const Dictionary &E : exclude) {

+ 70 - 0
modules/jolt_physics/misc/jolt_math_funcs.cpp

@@ -0,0 +1,70 @@
+/**************************************************************************/
+/*  jolt_math_funcs.cpp                                                   */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+/*
+Adapted to Godot from the Jolt Physics library.
+*/
+
+/*
+Copyright 2021 Jorrit Rouwe
+
+Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in
+the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
+the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR
+A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
+ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
+*/
+
+#include "jolt_math_funcs.h"
+
+void JoltMath::decompose(Basis &p_basis, Vector3 &r_scale) {
+	Vector3 x = p_basis.get_column(Vector3::AXIS_X);
+	Vector3 y = p_basis.get_column(Vector3::AXIS_Y);
+	Vector3 z = p_basis.get_column(Vector3::AXIS_Z);
+
+	const real_t x_dot_x = x.dot(x);
+	y -= x * (y.dot(x) / x_dot_x);
+	z -= x * (z.dot(x) / x_dot_x);
+	const real_t y_dot_y = y.dot(y);
+	z -= y * (z.dot(y) / y_dot_y);
+	const real_t z_dot_z = z.dot(z);
+
+	const real_t det = x.cross(y).dot(z);
+
+	r_scale = SIGN(det) * Vector3(Math::sqrt(x_dot_x), Math::sqrt(y_dot_y), Math::sqrt(z_dot_z));
+
+	p_basis.set_column(Vector3::AXIS_X, x / r_scale.x);
+	p_basis.set_column(Vector3::AXIS_Y, y / r_scale.y);
+	p_basis.set_column(Vector3::AXIS_Z, z / r_scale.z);
+}

+ 59 - 0
modules/jolt_physics/misc/jolt_math_funcs.h

@@ -0,0 +1,59 @@
+/**************************************************************************/
+/*  jolt_math_funcs.h                                                     */
+/**************************************************************************/
+/*                         This file is part of:                          */
+/*                             GODOT ENGINE                               */
+/*                        https://godotengine.org                         */
+/**************************************************************************/
+/* Copyright (c) 2014-present Godot Engine contributors (see AUTHORS.md). */
+/* Copyright (c) 2007-2014 Juan Linietsky, Ariel Manzur.                  */
+/*                                                                        */
+/* Permission is hereby granted, free of charge, to any person obtaining  */
+/* a copy of this software and associated documentation files (the        */
+/* "Software"), to deal in the Software without restriction, including    */
+/* without limitation the rights to use, copy, modify, merge, publish,    */
+/* distribute, sublicense, and/or sell copies of the Software, and to     */
+/* permit persons to whom the Software is furnished to do so, subject to  */
+/* the following conditions:                                              */
+/*                                                                        */
+/* The above copyright notice and this permission notice shall be         */
+/* included in all copies or substantial portions of the Software.        */
+/*                                                                        */
+/* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,        */
+/* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF     */
+/* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. */
+/* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY   */
+/* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,   */
+/* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE      */
+/* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.                 */
+/**************************************************************************/
+
+#ifndef JOLT_MATH_FUNCS_H
+#define JOLT_MATH_FUNCS_H
+
+#include "core/math/transform_3d.h"
+
+class JoltMath {
+public:
+	// Note that `p_basis` is mutated to be right-handed orthonormal.
+	static void decompose(Basis &p_basis, Vector3 &r_scale);
+
+	// Note that `p_transform` is mutated to be right-handed orthonormal.
+	static _FORCE_INLINE_ void decompose(Transform3D &p_transform, Vector3 &r_scale) {
+		decompose(p_transform.basis, r_scale);
+	}
+
+	static _FORCE_INLINE_ void decomposed(const Basis &p_basis, Basis &r_new_basis, Vector3 &r_scale) {
+		Basis new_basis = p_basis;
+		decompose(new_basis, r_scale);
+		r_new_basis = new_basis;
+	}
+
+	static _FORCE_INLINE_ void decomposed(const Transform3D &p_transform, Transform3D &r_new_transform, Vector3 &r_scale) {
+		Transform3D new_transform;
+		decompose(new_transform, r_scale);
+		r_new_transform = new_transform;
+	}
+};
+
+#endif // JOLT_MATH_FUNCS_H

+ 3 - 3
modules/jolt_physics/objects/jolt_area_3d.cpp

@@ -31,6 +31,7 @@
 #include "jolt_area_3d.h"
 
 #include "../jolt_project_settings.h"
+#include "../misc/jolt_math_funcs.h"
 #include "../misc/jolt_type_conversions.h"
 #include "../shapes/jolt_shape_3d.h"
 #include "../spaces/jolt_broad_phase_layer.h"
@@ -370,7 +371,8 @@ void JoltArea3D::set_default_area(bool p_value) {
 void JoltArea3D::set_transform(Transform3D p_transform) {
 	JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed to area '%s'.", to_string()));
 
-	const Vector3 new_scale = p_transform.basis.get_scale();
+	Vector3 new_scale;
+	JoltMath::decompose(p_transform, new_scale);
 
 	// Ideally we would do an exact comparison here, but due to floating-point precision this would be invalidated very often.
 	if (!scale.is_equal_approx(new_scale)) {
@@ -378,8 +380,6 @@ void JoltArea3D::set_transform(Transform3D p_transform) {
 		_shapes_changed();
 	}
 
-	p_transform.basis.orthonormalize();
-
 	if (!in_space()) {
 		jolt_settings->mPosition = to_jolt_r(p_transform.origin);
 		jolt_settings->mRotation = to_jolt(p_transform.basis);

+ 3 - 3
modules/jolt_physics/objects/jolt_body_3d.cpp

@@ -32,6 +32,7 @@
 
 #include "../joints/jolt_joint_3d.h"
 #include "../jolt_project_settings.h"
+#include "../misc/jolt_math_funcs.h"
 #include "../misc/jolt_type_conversions.h"
 #include "../shapes/jolt_shape_3d.h"
 #include "../spaces/jolt_broad_phase_layer.h"
@@ -543,7 +544,8 @@ JoltBody3D::~JoltBody3D() {
 void JoltBody3D::set_transform(Transform3D p_transform) {
 	JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed to physics body '%s'.", to_string()));
 
-	const Vector3 new_scale = p_transform.basis.get_scale();
+	Vector3 new_scale;
+	JoltMath::decompose(p_transform, new_scale);
 
 	// Ideally we would do an exact comparison here, but due to floating-point precision this would be invalidated very often.
 	if (!scale.is_equal_approx(new_scale)) {
@@ -551,8 +553,6 @@ void JoltBody3D::set_transform(Transform3D p_transform) {
 		_shapes_changed();
 	}
 
-	p_transform.basis.orthonormalize();
-
 	if (!in_space()) {
 		jolt_settings->mPosition = to_jolt_r(p_transform.origin);
 		jolt_settings->mRotation = to_jolt(p_transform.basis);

+ 7 - 3
modules/jolt_physics/objects/jolt_shaped_object_3d.cpp

@@ -30,6 +30,7 @@
 
 #include "jolt_shaped_object_3d.h"
 
+#include "../misc/jolt_math_funcs.h"
 #include "../misc/jolt_type_conversions.h"
 #include "../shapes/jolt_custom_double_sided_shape.h"
 #include "../shapes/jolt_shape_3d.h"
@@ -347,7 +348,10 @@ void JoltShapedObject3D::commit_shapes(bool p_optimize_compound) {
 void JoltShapedObject3D::add_shape(JoltShape3D *p_shape, Transform3D p_transform, bool p_disabled) {
 	JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, vformat("An invalid transform was passed when adding shape at index %d to physics body '%s'.", shapes.size(), to_string()));
 
-	shapes.push_back(JoltShapeInstance3D(this, p_shape, p_transform.orthonormalized(), p_transform.basis.get_scale(), p_disabled));
+	Vector3 shape_scale;
+	JoltMath::decompose(p_transform, shape_scale);
+
+	shapes.push_back(JoltShapeInstance3D(this, p_shape, p_transform, shape_scale, p_disabled));
 
 	_shapes_changed();
 }
@@ -430,8 +434,8 @@ void JoltShapedObject3D::set_shape_transform(int p_index, Transform3D p_transfor
 	ERR_FAIL_INDEX(p_index, (int)shapes.size());
 	JOLT_ENSURE_SCALE_NOT_ZERO(p_transform, "Failed to correctly set transform for shape at index %d in body '%s'.");
 
-	Vector3 new_scale = p_transform.basis.get_scale();
-	p_transform.basis.orthonormalize();
+	Vector3 new_scale;
+	JoltMath::decompose(p_transform, new_scale);
 
 	JoltShapeInstance3D &shape = shapes[p_index];
 

+ 13 - 17
modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp

@@ -32,6 +32,7 @@
 
 #include "../jolt_physics_server_3d.h"
 #include "../jolt_project_settings.h"
+#include "../misc/jolt_math_funcs.h"
 #include "../misc/jolt_type_conversions.h"
 #include "../objects/jolt_area_3d.h"
 #include "../objects/jolt_body_3d.h"
@@ -288,11 +289,10 @@ bool JoltPhysicsDirectSpaceState3D::_body_motion_cast(const JoltBody3D &p_body,
 		const Transform3D transform_com_local = transform_local.translated_local(com_scaled);
 		Transform3D transform_com = body_transform * transform_com_local;
 
-		Vector3 scale = transform_com.basis.get_scale();
+		Vector3 scale;
+		JoltMath::decompose(transform_com, scale);
 		JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "body_test_motion was passed an invalid transform along with body '%s'. This results in invalid scaling for shape at index %d.");
 
-		transform_com.basis.orthonormalize();
-
 		real_t shape_safe_fraction = 1.0;
 		real_t shape_unsafe_fraction = 1.0;
 
@@ -590,11 +590,10 @@ int JoltPhysicsDirectSpaceState3D::intersect_shape(const ShapeParameters &p_para
 	Transform3D transform = p_parameters.transform;
 	JOLT_ENSURE_SCALE_NOT_ZERO(transform, "intersect_shape was passed an invalid transform.");
 
-	Vector3 scale = transform.basis.get_scale();
+	Vector3 scale;
+	JoltMath::decompose(transform, scale);
 	JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "intersect_shape was passed an invalid transform.");
 
-	transform.basis.orthonormalize();
-
 	const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
 	const Transform3D transform_com = transform.translated_local(com_scaled);
 
@@ -647,11 +646,10 @@ bool JoltPhysicsDirectSpaceState3D::cast_motion(const ShapeParameters &p_paramet
 	Transform3D transform = p_parameters.transform;
 	JOLT_ENSURE_SCALE_NOT_ZERO(transform, "cast_motion (maybe from ShapeCast3D?) was passed an invalid transform.");
 
-	Vector3 scale = transform.basis.get_scale();
+	Vector3 scale;
+	JoltMath::decompose(transform, scale);
 	JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "cast_motion (maybe from ShapeCast3D?) was passed an invalid transform.");
 
-	transform.basis.orthonormalize();
-
 	const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
 	Transform3D transform_com = transform.translated_local(com_scaled);
 
@@ -684,11 +682,10 @@ bool JoltPhysicsDirectSpaceState3D::collide_shape(const ShapeParameters &p_param
 	Transform3D transform = p_parameters.transform;
 	JOLT_ENSURE_SCALE_NOT_ZERO(transform, "collide_shape was passed an invalid transform.");
 
-	Vector3 scale = transform.basis.get_scale();
+	Vector3 scale;
+	JoltMath::decompose(transform, scale);
 	JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "collide_shape was passed an invalid transform.");
 
-	transform.basis.orthonormalize();
-
 	const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
 	const Transform3D transform_com = transform.translated_local(com_scaled);
 
@@ -754,11 +751,10 @@ bool JoltPhysicsDirectSpaceState3D::rest_info(const ShapeParameters &p_parameter
 	Transform3D transform = p_parameters.transform;
 	JOLT_ENSURE_SCALE_NOT_ZERO(transform, "get_rest_info (maybe from ShapeCast3D?) was passed an invalid transform.");
 
-	Vector3 scale = transform.basis.get_scale();
+	Vector3 scale;
+	JoltMath::decompose(transform, scale);
 	JOLT_ENSURE_SCALE_VALID(jolt_shape, scale, "get_rest_info (maybe from ShapeCast3D?) was passed an invalid transform.");
 
-	transform.basis.orthonormalize();
-
 	const Vector3 com_scaled = to_godot(jolt_shape->GetCenterOfMass());
 	const Transform3D transform_com = transform.translated_local(com_scaled);
 
@@ -890,8 +886,8 @@ bool JoltPhysicsDirectSpaceState3D::body_test_motion(const JoltBody3D &p_body, c
 	Transform3D transform = p_parameters.from;
 	JOLT_ENSURE_SCALE_NOT_ZERO(transform, vformat("body_test_motion (maybe from move_and_slide?) was passed an invalid transform along with body '%s'.", p_body.to_string()));
 
-	Vector3 scale = transform.basis.get_scale();
-	transform.basis.orthonormalize();
+	Vector3 scale;
+	JoltMath::decompose(transform, scale);
 
 	space->try_optimize();
 

+ 7 - 0
modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs

@@ -191,6 +191,13 @@ namespace GodotTools.ProjectEditor
                 // Otherwise, it can be removed.
                 if (mainTfmVersion > minTfmVersion)
                 {
+                    var propertyTfmVersion = NuGetFramework.Parse(property.Value).Version;
+                    if (propertyTfmVersion == minTfmVersion)
+                    {
+                        // The 'TargetFramework' property already matches the minimum version.
+                        continue;
+                    }
+
                     property.Value = minTfmValue;
                 }
                 else

+ 6 - 0
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs

@@ -455,6 +455,12 @@ namespace Godot.NativeInterop
             get => _data._m_obj_data.obj;
         }
 
+        public readonly ulong ObjectId
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _data._m_obj_data.id;
+        }
+
         public void Dispose()
         {
             switch (Type)

+ 8 - 1
modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs

@@ -485,7 +485,14 @@ namespace Godot.NativeInterop
                 NativeFuncs.godotsharp_variant_as_rid(p_var);
 
         public static IntPtr ConvertToGodotObjectPtr(in godot_variant p_var)
-            => p_var.Type == Variant.Type.Object ? p_var.Object : IntPtr.Zero;
+        {
+            if (p_var.Type != Variant.Type.Object || p_var.ObjectId == 0)
+            {
+                return IntPtr.Zero;
+            }
+
+            return NativeFuncs.godotsharp_instance_from_id(p_var.ObjectId);
+        }
 
         [MethodImpl(MethodImplOptions.AggressiveInlining)]
         public static GodotObject ConvertToGodotObject(in godot_variant p_var)

+ 17 - 9
modules/multiplayer/editor/editor_network_profiler.cpp

@@ -35,6 +35,7 @@
 #include "editor/gui/editor_run_bar.h"
 #include "editor/themes/editor_scale.h"
 #include "scene/gui/check_box.h"
+#include "scene/gui/flow_container.h"
 
 void EditorNetworkProfiler::_bind_methods() {
 	ADD_SIGNAL(MethodInfo("enable_profiling", PropertyInfo(Variant::BOOL, "enable")));
@@ -297,30 +298,37 @@ bool EditorNetworkProfiler::is_profiling() {
 }
 
 EditorNetworkProfiler::EditorNetworkProfiler() {
-	HBoxContainer *hb = memnew(HBoxContainer);
-	hb->add_theme_constant_override("separation", 8 * EDSCALE);
-	add_child(hb);
+	FlowContainer *container = memnew(FlowContainer);
+	container->add_theme_constant_override(SNAME("h_separation"), 8 * EDSCALE);
+	container->add_theme_constant_override(SNAME("v_separation"), 2 * EDSCALE);
+	add_child(container);
 
 	activate = memnew(Button);
 	activate->set_toggle_mode(true);
 	activate->set_text(TTR("Start"));
 	activate->set_disabled(true);
 	activate->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_activate_pressed));
-	hb->add_child(activate);
+	container->add_child(activate);
 
 	clear_button = memnew(Button);
 	clear_button->set_text(TTR("Clear"));
 	clear_button->set_disabled(true);
 	clear_button->connect(SceneStringName(pressed), callable_mp(this, &EditorNetworkProfiler::_clear_pressed));
-	hb->add_child(clear_button);
+	container->add_child(clear_button);
 
 	CheckBox *autostart_checkbox = memnew(CheckBox);
 	autostart_checkbox->set_text(TTR("Autostart"));
 	autostart_checkbox->set_pressed(EditorSettings::get_singleton()->get_project_metadata("debug_options", "autostart_network_profiler", false));
 	autostart_checkbox->connect(SceneStringName(toggled), callable_mp(this, &EditorNetworkProfiler::_autostart_toggled));
-	hb->add_child(autostart_checkbox);
+	container->add_child(autostart_checkbox);
+
+	Control *c = memnew(Control);
+	c->set_h_size_flags(SIZE_EXPAND_FILL);
+	container->add_child(c);
 
-	hb->add_spacer();
+	HBoxContainer *hb = memnew(HBoxContainer);
+	hb->add_theme_constant_override(SNAME("separation"), 8 * EDSCALE);
+	container->add_child(hb);
 
 	Label *lb = memnew(Label);
 	// TRANSLATORS: This is the label for the network profiler's incoming bandwidth.
@@ -359,7 +367,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
 
 	// RPC
 	counters_display = memnew(Tree);
-	counters_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
+	counters_display->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
 	counters_display->set_v_size_flags(SIZE_EXPAND_FILL);
 	counters_display->set_h_size_flags(SIZE_EXPAND_FILL);
 	counters_display->set_hide_folding(true);
@@ -382,7 +390,7 @@ EditorNetworkProfiler::EditorNetworkProfiler() {
 
 	// Replication
 	replication_display = memnew(Tree);
-	replication_display->set_custom_minimum_size(Size2(320, 0) * EDSCALE);
+	replication_display->set_custom_minimum_size(Size2(280, 0) * EDSCALE);
 	replication_display->set_v_size_flags(SIZE_EXPAND_FILL);
 	replication_display->set_h_size_flags(SIZE_EXPAND_FILL);
 	replication_display->set_hide_folding(true);

+ 6 - 1
modules/openxr/extensions/platform/openxr_opengl_extension.cpp

@@ -141,7 +141,12 @@ XrGraphicsBindingEGLMNDX OpenXROpenGLExtension::graphics_binding_egl;
 #endif
 
 void *OpenXROpenGLExtension::set_session_create_and_get_next_pointer(void *p_next_pointer) {
-	XrVersion desired_version = XR_MAKE_VERSION(3, 3, 0);
+	GLint gl_version_major = 0;
+	GLint gl_version_minor = 0;
+	glGetIntegerv(GL_MAJOR_VERSION, &gl_version_major);
+	glGetIntegerv(GL_MINOR_VERSION, &gl_version_minor);
+
+	XrVersion desired_version = XR_MAKE_VERSION(gl_version_major, gl_version_minor, 0);
 
 	if (!check_graphics_api_support(desired_version)) {
 		print_line("OpenXR: Trying to initialize with OpenGL anyway...");

+ 6 - 0
modules/openxr/register_types.cpp

@@ -225,10 +225,16 @@ void initialize_openxr_module(ModuleInitializationLevel p_level) {
 		}
 
 #ifdef TOOLS_ENABLED
+		// Register as "editor", not "core".
+		ClassDB::APIType prev_api = ClassDB::get_current_api();
+		ClassDB::set_current_api(ClassDB::API_EDITOR);
+
 		GDREGISTER_ABSTRACT_CLASS(OpenXRInteractionProfileEditorBase);
 		GDREGISTER_CLASS(OpenXRInteractionProfileEditor);
 		GDREGISTER_CLASS(OpenXRBindingModifierEditor);
 
+		ClassDB::set_current_api(prev_api);
+
 		EditorNode::add_init_callback(_editor_init);
 #endif
 	}

+ 17 - 3
modules/text_server_adv/text_server_adv.cpp

@@ -1318,11 +1318,12 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data,
 			} break;
 		}
 
+		FT_GlyphSlot slot = fd->face->glyph;
+		bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
 		if (!outline) {
 			if (!p_font_data->msdf) {
-				error = FT_Render_Glyph(fd->face->glyph, aa_mode);
+				error = FT_Render_Glyph(slot, aa_mode);
 			}
-			FT_GlyphSlot slot = fd->face->glyph;
 			if (!error) {
 				if (p_font_data->msdf) {
 #ifdef MODULE_MSDFGEN_ENABLED
@@ -1363,6 +1364,7 @@ _FORCE_INLINE_ bool TextServerAdvanced::_ensure_glyph(FontAdvanced *p_font_data,
 		cleanup_stroker:
 			FT_Stroker_Done(stroker);
 		}
+		gl.from_svg = from_svg;
 		E = fd->glyph_map.insert(p_glyph, gl);
 		r_glyph = E->value;
 		return gl.found;
@@ -3312,6 +3314,10 @@ RID TextServerAdvanced::_font_get_glyph_texture_rid(const RID &p_font_rid, const
 			if (ffsd->textures[fgl.texture_idx].dirty) {
 				ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 				Ref<Image> img = tex.image;
+				if (fgl.from_svg) {
+					// Same as the "fix alpha border" process option when importing SVGs
+					img->fix_alpha_edges();
+				}
 				if (fd->mipmaps && !img->has_mipmaps()) {
 					img = tex.image->duplicate();
 					img->generate_mipmaps();
@@ -3360,6 +3366,10 @@ Size2 TextServerAdvanced::_font_get_glyph_texture_size(const RID &p_font_rid, co
 			if (ffsd->textures[fgl.texture_idx].dirty) {
 				ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 				Ref<Image> img = tex.image;
+				if (fgl.from_svg) {
+					// Same as the "fix alpha border" process option when importing SVGs
+					img->fix_alpha_edges();
+				}
 				if (fd->mipmaps && !img->has_mipmaps()) {
 					img = tex.image->duplicate();
 					img->generate_mipmaps();
@@ -3798,7 +3808,7 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 		if (fgl.texture_idx != -1) {
 			Color modulate = p_color;
 #ifdef MODULE_FREETYPE_ENABLED
-			if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
+			if (!fgl.from_svg && ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
 				modulate.r = modulate.g = modulate.b = 1.0;
 			}
 #endif
@@ -3806,6 +3816,10 @@ void TextServerAdvanced::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 				if (ffsd->textures[fgl.texture_idx].dirty) {
 					ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 					Ref<Image> img = tex.image;
+					if (fgl.from_svg) {
+						// Same as the "fix alpha border" process option when importing SVGs
+						img->fix_alpha_edges();
+					}
 					if (fd->mipmaps && !img->has_mipmaps()) {
 						img = tex.image->duplicate();
 						img->generate_mipmaps();

+ 1 - 0
modules/text_server_adv/text_server_adv.h

@@ -272,6 +272,7 @@ class TextServerAdvanced : public TextServerExtension {
 		Rect2 rect;
 		Rect2 uv_rect;
 		Vector2 advance;
+		bool from_svg = false;
 	};
 
 	struct FontForSizeAdvanced {

+ 17 - 3
modules/text_server_fb/text_server_fb.cpp

@@ -741,11 +741,12 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data,
 			} break;
 		}
 
+		FT_GlyphSlot slot = fd->face->glyph;
+		bool from_svg = (slot->format == FT_GLYPH_FORMAT_SVG); // Need to check before FT_Render_Glyph as it will change format to bitmap.
 		if (!outline) {
 			if (!p_font_data->msdf) {
-				error = FT_Render_Glyph(fd->face->glyph, aa_mode);
+				error = FT_Render_Glyph(slot, aa_mode);
 			}
-			FT_GlyphSlot slot = fd->face->glyph;
 			if (!error) {
 				if (p_font_data->msdf) {
 #ifdef MODULE_MSDFGEN_ENABLED
@@ -786,6 +787,7 @@ _FORCE_INLINE_ bool TextServerFallback::_ensure_glyph(FontFallback *p_font_data,
 		cleanup_stroker:
 			FT_Stroker_Done(stroker);
 		}
+		gl.from_svg = from_svg;
 		E = fd->glyph_map.insert(p_glyph, gl);
 		r_glyph = E->value;
 		return gl.found;
@@ -2290,6 +2292,10 @@ RID TextServerFallback::_font_get_glyph_texture_rid(const RID &p_font_rid, const
 			if (ffsd->textures[fgl.texture_idx].dirty) {
 				ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 				Ref<Image> img = tex.image;
+				if (fgl.from_svg) {
+					// Same as the "fix alpha border" process option when importing SVGs
+					img->fix_alpha_edges();
+				}
 				if (fd->mipmaps && !img->has_mipmaps()) {
 					img = tex.image->duplicate();
 					img->generate_mipmaps();
@@ -2338,6 +2344,10 @@ Size2 TextServerFallback::_font_get_glyph_texture_size(const RID &p_font_rid, co
 			if (ffsd->textures[fgl.texture_idx].dirty) {
 				ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 				Ref<Image> img = tex.image;
+				if (fgl.from_svg) {
+					// Same as the "fix alpha border" process option when importing SVGs
+					img->fix_alpha_edges();
+				}
 				if (fd->mipmaps && !img->has_mipmaps()) {
 					img = tex.image->duplicate();
 					img->generate_mipmaps();
@@ -2729,7 +2739,7 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 		if (fgl.texture_idx != -1) {
 			Color modulate = p_color;
 #ifdef MODULE_FREETYPE_ENABLED
-			if (ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
+			if (!fgl.from_svg && ffsd->face && ffsd->textures[fgl.texture_idx].image.is_valid() && (ffsd->textures[fgl.texture_idx].image->get_format() == Image::FORMAT_RGBA8) && !lcd_aa && !fd->msdf) {
 				modulate.r = modulate.g = modulate.b = 1.0;
 			}
 #endif
@@ -2737,6 +2747,10 @@ void TextServerFallback::_font_draw_glyph(const RID &p_font_rid, const RID &p_ca
 				if (ffsd->textures[fgl.texture_idx].dirty) {
 					ShelfPackTexture &tex = ffsd->textures.write[fgl.texture_idx];
 					Ref<Image> img = tex.image;
+					if (fgl.from_svg) {
+						// Same as the "fix alpha border" process option when importing SVGs
+						img->fix_alpha_edges();
+					}
 					if (fd->mipmaps && !img->has_mipmaps()) {
 						img = tex.image->duplicate();
 						img->generate_mipmaps();

+ 1 - 0
modules/text_server_fb/text_server_fb.h

@@ -219,6 +219,7 @@ class TextServerFallback : public TextServerExtension {
 		Rect2 rect;
 		Rect2 uv_rect;
 		Vector2 advance;
+		bool from_svg = false;
 	};
 
 	struct FontForSizeFallback {

+ 1 - 1
platform/android/README.md

@@ -12,7 +12,7 @@ using [Gradle](https://gradle.org/) as a build system.
 
 ## Artwork license
 
-[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under
+[`logo.svg`](export/logo.svg) and [`run_icon.svg`](export/run_icon.svg) are licensed under
 [Creative Commons Attribution 3.0 Unported](https://developer.android.com/distribute/marketing-tools/brand-guidelines#android_robot)
 per the Android logo usage guidelines:
 

+ 1 - 1
platform/android/android_input_handler.cpp

@@ -337,7 +337,7 @@ void AndroidInputHandler::process_mouse_event(int p_event_action, int p_event_an
 		} break;
 
 		case AMOTION_EVENT_ACTION_MOVE: {
-			if (!mouse_event_info.valid) {
+			if (!p_source_mouse_relative && !mouse_event_info.valid) {
 				return;
 			}
 

+ 1 - 1
platform/android/detect.py

@@ -187,7 +187,7 @@ def configure(env: "SConsEnvironment"):
     has_swappy = detect_swappy()
     if not has_swappy:
         print_warning(
-            "Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/darksylinc/godot-swappy/releases and extract it so that the following files can be found:\n"
+            "Swappy Frame Pacing not detected! It is strongly recommended you download it from https://github.com/godotengine/godot-swappy/releases and extract it so that the following files can be found:\n"
             + " thirdparty/swappy-frame-pacing/arm64-v8a/libswappy_static.a\n"
             + " thirdparty/swappy-frame-pacing/armeabi-v7a/libswappy_static.a\n"
             + " thirdparty/swappy-frame-pacing/x86/libswappy_static.a\n"

+ 1 - 1
platform/android/java/app/AndroidManifest.xml

@@ -46,7 +46,7 @@
             android:excludeFromRecents="false"
             android:exported="true"
             android:screenOrientation="landscape"
-            android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+            android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
             android:resizeableActivity="false"
             tools:ignore="UnusedAttribute" >
 

+ 4 - 4
platform/android/java/editor/src/main/AndroidManifest.xml

@@ -41,7 +41,7 @@
 
         <activity
             android:name=".GodotEditor"
-            android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+            android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
             android:exported="true"
             android:icon="@mipmap/themed_icon"
             android:launchMode="singleTask"
@@ -59,7 +59,7 @@
         </activity>
         <activity
             android:name=".GodotGame"
-            android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+            android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
             android:exported="false"
             android:icon="@mipmap/ic_play_window"
             android:label="@string/godot_game_activity_name"
@@ -74,7 +74,7 @@
         </activity>
         <activity
             android:name=".embed.EmbeddedGodotGame"
-            android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+            android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
             android:exported="false"
             android:icon="@mipmap/ic_play_window"
             android:label="@string/godot_game_activity_name"
@@ -87,7 +87,7 @@
             android:screenOrientation="userLandscape" />
         <activity
             android:name=".GodotXRGame"
-            android:configChanges="orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
+            android:configChanges="layoutDirection|locale|orientation|keyboardHidden|screenSize|smallestScreenSize|density|keyboard|navigation|screenLayout|uiMode"
             android:process=":GodotXRGame"
             android:launchMode="singleTask"
             android:icon="@mipmap/ic_play_window"

+ 6 - 4
platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java

@@ -59,6 +59,7 @@ import androidx.annotation.NonNull;
 import java.util.Collections;
 import java.util.HashSet;
 import java.util.Set;
+import java.util.concurrent.atomic.AtomicInteger;
 
 /**
  * Handles input related events for the {@link GodotRenderView} view.
@@ -83,7 +84,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
 	/**
 	 * Used to decide whether mouse capture can be enabled.
 	 */
-	private int lastSeenToolType = MotionEvent.TOOL_TYPE_UNKNOWN;
+	private AtomicInteger lastSeenToolType = new AtomicInteger(MotionEvent.TOOL_TYPE_UNKNOWN);
 
 	private int rotaryInputAxis = ROTARY_INPUT_VERTICAL_AXIS;
 
@@ -149,7 +150,8 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
 	}
 
 	public boolean canCapturePointer() {
-		return lastSeenToolType == MotionEvent.TOOL_TYPE_MOUSE;
+		return lastSeenToolType.get() == MotionEvent.TOOL_TYPE_MOUSE ||
+				lastSeenToolType.get() == MotionEvent.TOOL_TYPE_UNKNOWN;
 	}
 
 	public void onPointerCaptureChange(boolean hasCapture) {
@@ -210,7 +212,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
 	}
 
 	public boolean onTouchEvent(final MotionEvent event) {
-		lastSeenToolType = getEventToolType(event);
+		lastSeenToolType.set(getEventToolType(event));
 
 		this.scaleGestureDetector.onTouchEvent(event);
 		if (this.gestureDetector.onTouchEvent(event)) {
@@ -236,7 +238,7 @@ public class GodotInputHandler implements InputManager.InputDeviceListener, Sens
 	}
 
 	public boolean onGenericMotionEvent(MotionEvent event) {
-		lastSeenToolType = getEventToolType(event);
+		lastSeenToolType.set(getEventToolType(event));
 
 		if (event.isFromSource(InputDevice.SOURCE_JOYSTICK) && event.getActionMasked() == MotionEvent.ACTION_MOVE) {
 			// Check if the device exists

+ 379 - 142
platform/android/java_class_wrapper.cpp

@@ -108,18 +108,21 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 					}
 				} break;
 				case ARG_TYPE_CLASS: {
-					if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
+					String cn = E.param_sigs[i].operator String();
+					if (cn.begins_with("L") && cn.ends_with(";")) {
+						cn = cn.substr(1, cn.length() - 2);
+					}
+					if (cn == "org/godotengine/godot/Dictionary") {
+						if (p_args[i]->get_type() != Variant::DICTIONARY) {
+							arg_expected = Variant::DICTIONARY;
+						}
+					} else if (p_args[i]->get_type() != Variant::OBJECT && p_args[i]->get_type() != Variant::NIL) {
 						arg_expected = Variant::OBJECT;
 					} else {
 						Ref<RefCounted> ref = *p_args[i];
 						if (ref.is_valid()) {
 							if (Object::cast_to<JavaObject>(ref.ptr())) {
 								Ref<JavaObject> jo = ref;
-								//could be faster
-								String cn = E.param_sigs[i].operator String();
-								if (cn.begins_with("L") && cn.ends_with(";")) {
-									cn = cn.substr(1, cn.length() - 2);
-								}
 								jclass c = env->FindClass(cn.utf8().get_data());
 								if (!c || !env->IsInstanceOf(jo->instance, c)) {
 									arg_expected = Variant::OBJECT;
@@ -132,6 +135,116 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 						}
 					}
 				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::BOOL) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_BYTE:
+				case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
+					if (p_args[i]->get_type() != Variant::PACKED_BYTE_ARRAY) {
+						arg_expected = Variant::PACKED_BYTE_ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_SHORT:
+				case ARG_ARRAY_BIT | ARG_TYPE_INT: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else if (p_args[i]->get_type() != Variant::PACKED_INT32_ARRAY) {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::INT) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else if (p_args[i]->get_type() != Variant::PACKED_INT64_ARRAY) {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT32_ARRAY) {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::FLOAT) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else if (p_args[i]->get_type() != Variant::PACKED_FLOAT64_ARRAY) {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_STRING:
+				case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::STRING) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else if (p_args[i]->get_type() != Variant::PACKED_STRING_ARRAY) {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_CALLABLE: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::CALLABLE) {
+							arg_expected = Variant::ARRAY;
+						}
+					} else {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
+				case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
+					if (p_args[i]->get_type() == Variant::ARRAY) {
+						Array arr = *p_args[i];
+						if (arr.is_typed() && arr.get_typed_builtin() != Variant::OBJECT) {
+							arg_expected = Variant::ARRAY;
+						} else {
+							String cn = E.param_sigs[i].operator String();
+							if (cn.begins_with("[L") && cn.ends_with(";")) {
+								cn = cn.substr(2, cn.length() - 3);
+							}
+							jclass c = env->FindClass(cn.utf8().get_data());
+							if (c) {
+								for (int j = 0; j < arr.size(); j++) {
+									Ref<JavaObject> jo = arr[j];
+									if (jo.is_valid()) {
+										if (!env->IsInstanceOf(jo->instance, c)) {
+											arg_expected = Variant::ARRAY;
+											break;
+										}
+									} else {
+										arg_expected = Variant::ARRAY;
+										break;
+									}
+								}
+							} else {
+								arg_expected = Variant::ARRAY;
+							}
+						}
+					} else {
+						arg_expected = Variant::ARRAY;
+					}
+				} break;
 				default: {
 					if (p_args[i]->get_type() != Variant::ARRAY) {
 						arg_expected = Variant::ARRAY;
@@ -287,13 +400,16 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				to_free.push_back(jcallable);
 			} break;
 			case ARG_TYPE_CLASS: {
-				Ref<JavaObject> jo = *p_args[i];
-				if (jo.is_valid()) {
-					argv[i].l = jo->instance;
+				if (p_args[i]->get_type() == Variant::DICTIONARY) {
+					argv[i].l = _variant_to_jvalue(env, Variant::DICTIONARY, p_args[i]).obj;
 				} else {
-					argv[i].l = nullptr; //I hope this works
+					Ref<JavaObject> jo = *p_args[i];
+					if (jo.is_valid()) {
+						argv[i].l = jo->instance;
+					} else {
+						argv[i].l = nullptr; //I hope this works
+					}
 				}
-
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_BOOLEAN: {
 				Array arr = *p_args[i];
@@ -307,90 +423,171 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-				Array arr = *p_args[i];
-				jbyteArray a = env->NewByteArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jbyte val = arr[j];
-					env->SetByteArrayRegion(a, j, 1, &val);
+				jbyteArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewByteArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jbyte val = arr[j];
+						env->SetByteArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
+					PackedByteArray arr = *p_args[i];
+					a = env->NewByteArray(arr.size());
+					env->SetByteArrayRegion(a, 0, arr.size(), (const jbyte *)arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
-				Array arr = *p_args[i];
-				jcharArray a = env->NewCharArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jchar val = arr[j];
-					env->SetCharArrayRegion(a, j, 1, &val);
+				jcharArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewCharArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jchar val = arr[j];
+						env->SetCharArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_BYTE_ARRAY) {
+					PackedByteArray arr = *p_args[i];
+					// The data is expected to be UTF-16 encoded, so the length is half the size of the byte array.
+					int size = arr.size() / 2;
+					a = env->NewCharArray(size);
+					env->SetCharArrayRegion(a, 0, size, (const jchar *)arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
-				Array arr = *p_args[i];
-				jshortArray a = env->NewShortArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jshort val = arr[j];
-					env->SetShortArrayRegion(a, j, 1, &val);
+				jshortArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewShortArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jshort val = arr[j];
+						env->SetShortArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
+					PackedInt32Array arr = *p_args[i];
+					a = env->NewShortArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jshort val = arr[j];
+						env->SetShortArrayRegion(a, j, 1, &val);
+					}
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_INT: {
-				Array arr = *p_args[i];
-				jintArray a = env->NewIntArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jint val = arr[j];
-					env->SetIntArrayRegion(a, j, 1, &val);
+				jintArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewIntArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jint val = arr[j];
+						env->SetIntArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_INT32_ARRAY) {
+					PackedInt32Array arr = *p_args[i];
+					a = env->NewIntArray(arr.size());
+					env->SetIntArrayRegion(a, 0, arr.size(), arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
-				Array arr = *p_args[i];
-				jlongArray a = env->NewLongArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jlong val = (int64_t)arr[j];
-					env->SetLongArrayRegion(a, j, 1, &val);
+				jlongArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewLongArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jlong val = (int64_t)arr[j];
+						env->SetLongArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_INT64_ARRAY) {
+					PackedInt64Array arr = *p_args[i];
+					a = env->NewLongArray(arr.size());
+					env->SetLongArrayRegion(a, 0, arr.size(), arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
-				Array arr = *p_args[i];
-				jfloatArray a = env->NewFloatArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jfloat val = arr[j];
-					env->SetFloatArrayRegion(a, j, 1, &val);
+				jfloatArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewFloatArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jfloat val = arr[j];
+						env->SetFloatArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT32_ARRAY) {
+					PackedFloat32Array arr = *p_args[i];
+					a = env->NewFloatArray(arr.size());
+					env->SetFloatArrayRegion(a, 0, arr.size(), arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
-				Array arr = *p_args[i];
-				jdoubleArray a = env->NewDoubleArray(arr.size());
-				for (int j = 0; j < arr.size(); j++) {
-					jdouble val = arr[j];
-					env->SetDoubleArrayRegion(a, j, 1, &val);
+				jdoubleArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewDoubleArray(arr.size());
+					for (int j = 0; j < arr.size(); j++) {
+						jdouble val = arr[j];
+						env->SetDoubleArrayRegion(a, j, 1, &val);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_FLOAT64_ARRAY) {
+					PackedFloat64Array arr = *p_args[i];
+					a = env->NewDoubleArray(arr.size());
+					env->SetDoubleArrayRegion(a, 0, arr.size(), arr.ptr());
 				}
+
 				argv[i].l = a;
 				to_free.push_back(a);
 
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_STRING:
 			case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
-				Array arr = *p_args[i];
-				jobjectArray a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
-				for (int j = 0; j < arr.size(); j++) {
-					String s = arr[j];
-					jstring jStr = env->NewStringUTF(s.utf8().get_data());
-					env->SetObjectArrayElement(a, j, jStr);
-					to_free.push_back(jStr);
+				jobjectArray a = nullptr;
+
+				if (p_args[i]->get_type() == Variant::ARRAY) {
+					Array arr = *p_args[i];
+					a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
+					for (int j = 0; j < arr.size(); j++) {
+						String s = arr[j];
+						jstring jStr = env->NewStringUTF(s.utf8().get_data());
+						env->SetObjectArrayElement(a, j, jStr);
+						to_free.push_back(jStr);
+					}
+				} else if (p_args[i]->get_type() == Variant::PACKED_STRING_ARRAY) {
+					PackedStringArray arr = *p_args[i];
+					a = env->NewObjectArray(arr.size(), env->FindClass("java/lang/String"), nullptr);
+					for (int j = 0; j < arr.size(); j++) {
+						String s = arr[j];
+						jstring jStr = env->NewStringUTF(s.utf8().get_data());
+						env->SetObjectArrayElement(a, j, jStr);
+						to_free.push_back(jStr);
+					}
 				}
 
 				argv[i].l = a;
@@ -410,7 +607,22 @@ bool JavaClass::_call_method(JavaObject *p_instance, const StringName &p_method,
 				to_free.push_back(jarr);
 			} break;
 			case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
-				argv[i].l = nullptr;
+				String cn = method->param_sigs[i].operator String();
+				if (cn.begins_with("[L") && cn.ends_with(";")) {
+					cn = cn.substr(2, cn.length() - 3);
+				}
+				jclass c = env->FindClass(cn.utf8().get_data());
+				if (c) {
+					Array arr = *p_args[i];
+					jobjectArray jarr = env->NewObjectArray(arr.size(), c, nullptr);
+					for (int j = 0; j < arr.size(); j++) {
+						Ref<JavaObject> jo = arr[j];
+						env->SetObjectArrayElement(jarr, j, jo->instance);
+					}
+
+					argv[i].l = jarr;
+					to_free.push_back(jarr);
+				}
 			} break;
 		}
 	}
@@ -865,8 +1077,13 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			env->DeleteLocalRef(java_class);
 
 			if (java_class_wrapped.is_valid()) {
-				Ref<JavaObject> ret = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
-				var = ret;
+				String cn = java_class_wrapped->get_java_class_name();
+				if (cn == "org/godotengine/godot/Dictionary" || cn == "java.util.HashMap") {
+					var = _jobject_to_variant(env, obj);
+				} else {
+					Ref<JavaObject> ret = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
+					var = ret;
+				}
 				return true;
 			}
 
@@ -881,11 +1098,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
 			for (int i = 0; i < count; i++) {
 				jboolean val;
-				env->GetBooleanArrayRegion((jbooleanArray)arr, 0, 1, &val);
-				ret.push_back(val);
+				env->GetBooleanArrayRegion((jbooleanArray)arr, i, 1, &val);
+				ret[i] = (bool)val;
 			}
 
 			var = ret;
@@ -893,106 +1111,85 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-			Array ret;
+			PackedByteArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jbyte val;
-				env->GetByteArrayRegion((jbyteArray)arr, 0, 1, &val);
-				ret.push_back(val);
-			}
+			ret.resize(count);
+			env->GetByteArrayRegion((jbyteArray)arr, 0, count, (int8_t *)ret.ptrw());
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
-			Array ret;
+			PackedByteArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jchar val;
-				env->GetCharArrayRegion((jcharArray)arr, 0, 1, &val);
-				ret.push_back(val);
-			}
+			// Char arrays are UTF-16 encoded, so it's double the length.
+			ret.resize(count * 2);
+			env->GetCharArrayRegion((jcharArray)arr, 0, count, (jchar *)ret.ptrw());
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
-			Array ret;
+			PackedInt32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			int32_t *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jshort val;
-				env->GetShortArrayRegion((jshortArray)arr, 0, 1, &val);
-				ret.push_back(val);
+				env->GetShortArrayRegion((jshortArray)arr, i, 1, &val);
+				ptr[i] = val;
 			}
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_INT: {
-			Array ret;
+			PackedInt32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jint val;
-				env->GetIntArrayRegion((jintArray)arr, 0, 1, &val);
-				ret.push_back(val);
-			}
+			ret.resize(count);
+			env->GetIntArrayRegion((jintArray)arr, 0, count, ret.ptrw());
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_LONG: {
-			Array ret;
+			PackedInt64Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jlong val;
-				env->GetLongArrayRegion((jlongArray)arr, 0, 1, &val);
-				ret.push_back((int64_t)val);
-			}
+			ret.resize(count);
+			env->GetLongArrayRegion((jlongArray)arr, 0, count, ret.ptrw());
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
-			Array ret;
+			PackedFloat32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jfloat val;
-				env->GetFloatArrayRegion((jfloatArray)arr, 0, 1, &val);
-				ret.push_back(val);
-			}
+			ret.resize(count);
+			env->GetFloatArrayRegion((jfloatArray)arr, 0, count, ret.ptrw());
 
 			var = ret;
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
-			Array ret;
+			PackedFloat64Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
-
-			for (int i = 0; i < count; i++) {
-				jdouble val;
-				env->GetDoubleArrayRegion((jdoubleArray)arr, 0, 1, &val);
-				ret.push_back(val);
-			}
+			ret.resize(count);
+			env->GetDoubleArrayRegion((jdoubleArray)arr, 0, count, ret.ptrw());
 
 			var = ret;
 			return true;
@@ -1002,14 +1199,13 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
-				if (!o) {
-					ret.push_back(Variant());
-				} else {
+				if (o) {
 					bool val = env->CallBooleanMethod(o, JavaClassWrapper::singleton->Boolean_booleanValue);
-					ret.push_back(val);
+					ret[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1019,18 +1215,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_BYTE: {
-			Array ret;
+			PackedByteArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			uint8_t *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0;
 				} else {
 					int val = env->CallByteMethod(o, JavaClassWrapper::singleton->Byte_byteValue);
-					ret.push_back(val);
+					ptr[i] = (uint8_t)val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1039,18 +1237,22 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_CHAR: {
-			Array ret;
+			PackedByteArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			// Char arrays are UTF-16 encoded, so it's double the length.
+			ret.resize(count * 2);
 
+			jchar *ptr = (jchar *)ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					count = i;
+					break;
 				} else {
 					int val = env->CallCharMethod(o, JavaClassWrapper::singleton->Character_characterValue);
-					ret.push_back(val);
+					ptr[i] = (jchar)val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1059,18 +1261,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_SHORT: {
-			Array ret;
+			PackedInt32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			int32_t *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0;
 				} else {
 					int val = env->CallShortMethod(o, JavaClassWrapper::singleton->Short_shortValue);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1079,18 +1283,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_INT: {
-			Array ret;
+			PackedInt32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			int32_t *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0;
 				} else {
 					int val = env->CallIntMethod(o, JavaClassWrapper::singleton->Integer_integerValue);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1099,18 +1305,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_LONG: {
-			Array ret;
+			PackedInt64Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			int64_t *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0;
 				} else {
 					int64_t val = env->CallLongMethod(o, JavaClassWrapper::singleton->Long_longValue);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1119,18 +1327,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_FLOAT: {
-			Array ret;
+			PackedFloat32Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			float *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0.0;
 				} else {
 					float val = env->CallFloatMethod(o, JavaClassWrapper::singleton->Float_floatValue);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1139,18 +1349,20 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_NUMBER_CLASS_BIT | ARG_ARRAY_BIT | ARG_TYPE_DOUBLE: {
-			Array ret;
+			PackedFloat64Array ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			double *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
 				if (!o) {
-					ret.push_back(Variant());
+					ptr[i] = 0.0;
 				} else {
 					double val = env->CallDoubleMethod(o, JavaClassWrapper::singleton->Double_doubleValue);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1160,18 +1372,18 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 		} break;
 
 		case ARG_ARRAY_BIT | ARG_TYPE_STRING: {
-			Array ret;
+			PackedStringArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			String *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
-				if (!o) {
-					ret.push_back(Variant());
-				} else {
+				if (o) {
 					String val = jstring_to_string((jstring)o, env);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1180,18 +1392,18 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_CHARSEQUENCE: {
-			Array ret;
+			PackedStringArray ret;
 			jobjectArray arr = (jobjectArray)obj;
 
 			int count = env->GetArrayLength(arr);
+			ret.resize(count);
 
+			String *ptr = ret.ptrw();
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(arr, i);
-				if (!o) {
-					ret.push_back(Variant());
-				} else {
+				if (o) {
 					String val = charsequence_to_string(env, o);
-					ret.push_back(val);
+					ptr[i] = val;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1203,13 +1415,12 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			Array ret;
 			jobjectArray jarr = (jobjectArray)obj;
 			int count = env->GetArrayLength(jarr);
+			ret.resize(count);
 			for (int i = 0; i < count; i++) {
 				jobject o = env->GetObjectArrayElement(jarr, i);
-				if (!o) {
-					ret.push_back(Variant());
-				} else {
+				if (o) {
 					Callable callable = jcallable_to_callable(env, o);
-					ret.push_back(callable);
+					ret[i] = callable;
 				}
 				env->DeleteLocalRef(o);
 			}
@@ -1218,6 +1429,32 @@ bool JavaClass::_convert_object_to_variant(JNIEnv *env, jobject obj, Variant &va
 			return true;
 		} break;
 		case ARG_ARRAY_BIT | ARG_TYPE_CLASS: {
+			Array ret;
+			jobjectArray jarr = (jobjectArray)obj;
+			int count = env->GetArrayLength(jarr);
+			ret.resize(count);
+			for (int i = 0; i < count; i++) {
+				jobject obj = env->GetObjectArrayElement(jarr, i);
+				if (obj) {
+					jclass java_class = env->GetObjectClass(obj);
+					Ref<JavaClass> java_class_wrapped = JavaClassWrapper::singleton->wrap_jclass(java_class);
+					env->DeleteLocalRef(java_class);
+
+					if (java_class_wrapped.is_valid()) {
+						String cn = java_class_wrapped->get_java_class_name();
+						if (cn == "org/godotengine/godot/Dictionary" || cn == "java.util.HashMap") {
+							ret[i] = _jobject_to_variant(env, obj);
+						} else {
+							Ref<JavaObject> java_obj_wrapped = Ref<JavaObject>(memnew(JavaObject(java_class_wrapped, obj)));
+							ret[i] = java_obj_wrapped;
+						}
+					}
+				}
+				env->DeleteLocalRef(obj);
+			}
+
+			var = ret;
+			return true;
 		} break;
 	}
 

+ 1 - 0
platform/android/java_godot_lib_jni.cpp

@@ -49,6 +49,7 @@
 #include "core/config/project_settings.h"
 #include "core/input/input.h"
 #include "main/main.h"
+#include "servers/rendering_server.h"
 
 #ifndef _3D_DISABLED
 #include "servers/xr_server.h"

+ 37 - 1
platform/ios/export/export_plugin.cpp

@@ -2735,6 +2735,42 @@ void EditorExportPlatformIOS::_check_for_changes_poll_thread(void *ud) {
 			}
 		}
 
+		// Enum devices (via Xcode).
+		if (ea->has_runnable_preset.is_set() && _check_xcode_install() && (FileAccess::exists("/usr/bin/xcrun") || FileAccess::exists("/bin/xcrun"))) {
+			String devices;
+			List<String> args;
+			args.push_back("devicectl");
+			args.push_back("list");
+			args.push_back("devices");
+			args.push_back("-j");
+			args.push_back("-");
+			args.push_back("-q");
+			int ec = 0;
+			Error err = OS::get_singleton()->execute("xcrun", args, &devices, &ec, true);
+			if (err == OK && ec == 0) {
+				Ref<JSON> json;
+				json.instantiate();
+				err = json->parse(devices);
+				if (err == OK) {
+					const Dictionary &data = json->get_data();
+					const Dictionary &result = data["result"];
+					const Array &devices = result["devices"];
+					for (int i = 0; i < devices.size(); i++) {
+						const Dictionary &device_info = devices[i];
+						const Dictionary &conn_props = device_info["connectionProperties"];
+						const Dictionary &dev_props = device_info["deviceProperties"];
+						if (conn_props["pairingState"] == "paired" && dev_props["developerModeStatus"] == "enabled") {
+							Device nd;
+							nd.id = device_info["identifier"];
+							nd.name = dev_props["name"].operator String() + " (devicectl, " + ((conn_props["transportType"] == "localNetwork") ? "network" : "wired") + ")";
+							nd.wifi = conn_props["transportType"] == "localNetwork";
+							ldevices.push_back(nd);
+						}
+					}
+				}
+			}
+		}
+
 		// Update device list.
 		{
 			MutexLock lock(ea->device_lock);
@@ -2922,7 +2958,7 @@ Error EditorExportPlatformIOS::run(const Ref<EditorExportPreset> &p_preset, int
 			}
 		}
 	} else {
-		// Deploy and run on real device.
+		// Deploy and run on real device (via Xcode).
 		if (ep.step("Installing to device...", 3)) {
 			CLEANUP_AND_RETURN(ERR_SKIP);
 		} else {

+ 1 - 1
platform/linuxbsd/README.md

@@ -14,7 +14,7 @@ used by this platform.
 
 ## Artwork license
 
-[`logo.png`](logo.png) is derived from the [Linux logo](https://isc.tamu.edu/~lewing/linux/):
+[`logo.svg`](export/logo.svg) is derived from the [Linux logo](https://isc.tamu.edu/~lewing/linux/):
 
 > Permission to use and/or modify this image is granted provided you acknowledge me
 > <[email protected]> and [The GIMP](https://isc.tamu.edu/~lewing/gimp/)

+ 85 - 0
platform/linuxbsd/os_linuxbsd.cpp

@@ -37,10 +37,12 @@
 #include "servers/rendering_server.h"
 
 #ifdef X11_ENABLED
+#include "x11/detect_prime_x11.h"
 #include "x11/display_server_x11.h"
 #endif
 
 #ifdef WAYLAND_ENABLED
+#include "wayland/detect_prime_egl.h"
 #include "wayland/display_server_wayland.h"
 #endif
 
@@ -49,6 +51,22 @@
 #include "modules/regex/regex.h"
 #endif
 
+#if defined(RD_ENABLED)
+#include "servers/rendering/rendering_device.h"
+#endif
+
+#if defined(VULKAN_ENABLED)
+#ifdef X11_ENABLED
+#include "x11/rendering_context_driver_vulkan_x11.h"
+#endif
+#ifdef WAYLAND_ENABLED
+#include "wayland/rendering_context_driver_vulkan_wayland.h"
+#endif
+#endif
+#if defined(GLES3_ENABLED)
+#include "drivers/gles3/rasterizer_gles3.h"
+#endif
+
 #include <dlfcn.h>
 #include <stdio.h>
 #include <stdlib.h>
@@ -1180,6 +1198,73 @@ String OS_LinuxBSD::get_system_ca_certificates() {
 	return f->get_as_text();
 }
 
+bool OS_LinuxBSD::_test_create_rendering_device(const String &p_display_driver) const {
+	// Tests Rendering Device creation.
+
+	bool ok = false;
+#if defined(RD_ENABLED)
+	Error err;
+	RenderingContextDriver *rcd = nullptr;
+
+#if defined(VULKAN_ENABLED)
+#ifdef X11_ENABLED
+	if (p_display_driver == "x11" || p_display_driver.is_empty()) {
+		rcd = memnew(RenderingContextDriverVulkanX11);
+	}
+#endif
+#ifdef WAYLAND_ENABLED
+	if (p_display_driver == "wayland") {
+		rcd = memnew(RenderingContextDriverVulkanWayland);
+	}
+#endif
+#endif
+	if (rcd != nullptr) {
+		err = rcd->initialize();
+		if (err == OK) {
+			RenderingDevice *rd = memnew(RenderingDevice);
+			err = rd->initialize(rcd);
+			memdelete(rd);
+			rd = nullptr;
+			if (err == OK) {
+				ok = true;
+			}
+		}
+		memdelete(rcd);
+		rcd = nullptr;
+	}
+#endif
+	return ok;
+}
+
+bool OS_LinuxBSD::_test_create_rendering_device_and_gl(const String &p_display_driver) const {
+	// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some drivers.
+
+#ifdef GLES3_ENABLED
+#ifdef X11_ENABLED
+	if (p_display_driver == "x11" || p_display_driver.is_empty()) {
+#ifdef SOWRAP_ENABLED
+		if (initialize_xlib(0) != 0) {
+			return false;
+		}
+#endif
+		DetectPrimeX11::create_context();
+	}
+#endif
+#ifdef WAYLAND_ENABLED
+	if (p_display_driver == "wayland") {
+#ifdef SOWRAP_ENABLED
+		if (initialize_wayland_egl(0) != 0) {
+			return false;
+		}
+#endif
+		DetectPrimeEGL::create_context(EGL_PLATFORM_WAYLAND_KHR);
+	}
+#endif
+	RasterizerGLES3::make_current(true);
+#endif
+	return _test_create_rendering_device(p_display_driver);
+}
+
 OS_LinuxBSD::OS_LinuxBSD() {
 	main_loop = nullptr;
 

+ 3 - 0
platform/linuxbsd/os_linuxbsd.h

@@ -138,6 +138,9 @@ public:
 
 	virtual String get_system_ca_certificates() override;
 
+	virtual bool _test_create_rendering_device_and_gl(const String &p_display_driver) const override;
+	virtual bool _test_create_rendering_device(const String &p_display_driver) const override;
+
 	OS_LinuxBSD();
 	~OS_LinuxBSD();
 };

+ 1 - 1
platform/linuxbsd/wayland/detect_prime_egl.h

@@ -77,9 +77,9 @@ private:
 		{ nullptr, 0 }
 	};
 
+public:
 	static void create_context(EGLenum p_platform_enum);
 
-public:
 	static int detect_prime(EGLenum p_platform_enum);
 };
 

+ 17 - 33
platform/linuxbsd/x11/detect_prime_x11.cpp

@@ -60,25 +60,23 @@ typedef GLXContext (*GLXCREATECONTEXTATTRIBSARBPROC)(Display *, GLXFBConfig, GLX
 // To prevent shadowing warnings
 #undef glGetString
 
-struct vendor {
-	const char *glxvendor = nullptr;
-	int priority = 0;
-};
-
-vendor vendormap[] = {
-	{ "Advanced Micro Devices, Inc.", 30 },
-	{ "AMD", 30 },
-	{ "NVIDIA Corporation", 30 },
-	{ "X.Org", 30 },
-	{ "Intel Open Source Technology Center", 20 },
-	{ "Intel", 20 },
-	{ "nouveau", 10 },
-	{ "Mesa Project", 0 },
-	{ nullptr, 0 }
-};
+int silent_error_handler(Display *display, XErrorEvent *error) {
+	static char message[1024];
+	XGetErrorText(display, error->error_code, message, sizeof(message));
+	print_verbose(vformat("XServer error: %s"
+						  "\n   Major opcode of failed request: %d"
+						  "\n   Serial number of failed request: %d"
+						  "\n   Current serial number in output stream: %d",
+			String::utf8(message), (uint64_t)error->request_code, (uint64_t)error->minor_code, (uint64_t)error->serial));
+
+	quick_exit(1);
+	return 0;
+}
 
 // Runs inside a child. Exiting will not quit the engine.
-void create_context() {
+void DetectPrimeX11::create_context() {
+	XSetErrorHandler(&silent_error_handler);
+
 	Display *x11_display = XOpenDisplay(nullptr);
 	Window x11_window;
 	GLXContext glx_context;
@@ -137,20 +135,7 @@ void create_context() {
 	XFree(vi);
 }
 
-int silent_error_handler(Display *display, XErrorEvent *error) {
-	static char message[1024];
-	XGetErrorText(display, error->error_code, message, sizeof(message));
-	print_verbose(vformat("XServer error: %s"
-						  "\n   Major opcode of failed request: %d"
-						  "\n   Serial number of failed request: %d"
-						  "\n   Current serial number in output stream: %d",
-			String::utf8(message), (uint64_t)error->request_code, (uint64_t)error->minor_code, (uint64_t)error->serial));
-
-	quick_exit(1);
-	return 0;
-}
-
-int detect_prime() {
+int DetectPrimeX11::detect_prime() {
 	pid_t p;
 	int priorities[2] = {};
 	String vendors[2];
@@ -202,7 +187,6 @@ int detect_prime() {
 			// cleaning up these processes, and fork() makes a copy
 			// of all globals.
 			CoreGlobals::leak_reporting_enabled = false;
-			XSetErrorHandler(&silent_error_handler);
 
 			char string[201];
 
@@ -253,7 +237,7 @@ int detect_prime() {
 	}
 
 	for (int i = 1; i >= 0; --i) {
-		vendor *v = vendormap;
+		const Vendor *v = vendor_map;
 		while (v->glxvendor) {
 			if (v->glxvendor == vendors[i]) {
 				priorities[i] = v->priority;

+ 24 - 1
platform/linuxbsd/x11/detect_prime_x11.h

@@ -33,7 +33,30 @@
 
 #if defined(X11_ENABLED) && defined(GLES3_ENABLED)
 
-int detect_prime();
+class DetectPrimeX11 {
+private:
+	struct Vendor {
+		const char *glxvendor = nullptr;
+		int priority = 0;
+	};
+
+	static constexpr Vendor vendor_map[] = {
+		{ "Advanced Micro Devices, Inc.", 30 },
+		{ "AMD", 30 },
+		{ "NVIDIA Corporation", 30 },
+		{ "X.Org", 30 },
+		{ "Intel Open Source Technology Center", 20 },
+		{ "Intel", 20 },
+		{ "nouveau", 10 },
+		{ "Mesa Project", 0 },
+		{ nullptr, 0 }
+	};
+
+public:
+	static void create_context();
+
+	static int detect_prime();
+};
 
 #endif // X11_ENABLED && GLES3_ENABLED
 

+ 13 - 5
platform/linuxbsd/x11/display_server_x11.cpp

@@ -2563,7 +2563,8 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
 		Atom *atoms = (Atom *)data;
 		Atom wm_act_max_horz;
 		Atom wm_act_max_vert;
-		if (strcmp(p_atom_name, "_NET_WM_STATE") == 0) {
+		bool checking_state = strcmp(p_atom_name, "_NET_WM_STATE") == 0;
+		if (checking_state) {
 			wm_act_max_horz = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_HORZ", False);
 			wm_act_max_vert = XInternAtom(x11_display, "_NET_WM_STATE_MAXIMIZED_VERT", False);
 		} else {
@@ -2581,9 +2582,16 @@ bool DisplayServerX11::_window_maximize_check(WindowID p_window, const char *p_a
 				found_wm_act_max_vert = true;
 			}
 
-			if (found_wm_act_max_horz || found_wm_act_max_vert) {
-				retval = true;
-				break;
+			if (checking_state) {
+				if (found_wm_act_max_horz && found_wm_act_max_vert) {
+					retval = true;
+					break;
+				}
+			} else {
+				if (found_wm_act_max_horz || found_wm_act_max_vert) {
+					retval = true;
+					break;
+				}
 			}
 		}
 
@@ -6844,7 +6852,7 @@ DisplayServerX11::DisplayServerX11(const String &p_rendering_driver, WindowMode
 
 			if (use_prime == -1) {
 				print_verbose("Detecting GPUs, set DRI_PRIME in the environment to override GPU detection logic.");
-				use_prime = detect_prime();
+				use_prime = DetectPrimeX11::detect_prime();
 			}
 
 			if (use_prime) {

+ 1 - 1
platform/web/README.md

@@ -17,6 +17,6 @@ this platform such as the html shell (web page).
 
 ## Artwork license
 
-[`logo.png`](logo.png) and [`run_icon.png`](run_icon.png) are licensed under
+[`logo.svg`](export/logo.svg) and [`run_icon.svg`](export/run_icon.svg) are licensed under
 [Creative Commons Attribution 3.0 Unported](https://www.w3.org/html/logo/faq.html#how-licenced)
 per the HTML5 logo usage guidelines.

+ 22 - 14
platform/windows/os_windows.cpp

@@ -2340,18 +2340,26 @@ void OS_Windows::add_frame_delay(bool p_can_draw) {
 		target_ticks += dynamic_delay;
 		uint64_t current_ticks = get_ticks_usec();
 
-		if (target_ticks > current_ticks + delay_resolution) {
-			uint64_t delay_time = target_ticks - current_ticks - delay_resolution;
-			// Make sure we always sleep for a multiple of delay_resolution to avoid overshooting.
-			// Refer to: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep#remarks
-			delay_time = (delay_time / delay_resolution) * delay_resolution;
-			if (delay_time > 0) {
-				delay_usec(delay_time);
+		if (!is_in_low_processor_usage_mode()) {
+			if (target_ticks > current_ticks + delay_resolution) {
+				uint64_t delay_time = target_ticks - current_ticks - delay_resolution;
+				// Make sure we always sleep for a multiple of delay_resolution to avoid overshooting.
+				// Refer to: https://learn.microsoft.com/en-us/windows/win32/api/synchapi/nf-synchapi-sleep#remarks
+				delay_time = (delay_time / delay_resolution) * delay_resolution;
+				if (delay_time > 0) {
+					delay_usec(delay_time);
+				}
+			}
+			// Busy wait for the remainder of time.
+			while (get_ticks_usec() < target_ticks) {
+				YieldProcessor();
+			}
+		} else {
+			// Use a more relaxed approach for low processor usage mode.
+			// This has worse frame pacing but is more power efficient.
+			if (current_ticks < target_ticks) {
+				delay_usec(target_ticks - current_ticks);
 			}
-		}
-		// Busy wait for the remainder of time.
-		while (get_ticks_usec() < target_ticks) {
-			YieldProcessor();
 		}
 
 		current_ticks = get_ticks_usec();
@@ -2359,7 +2367,7 @@ void OS_Windows::add_frame_delay(bool p_can_draw) {
 	}
 }
 
-bool OS_Windows::_test_create_rendering_device() const {
+bool OS_Windows::_test_create_rendering_device(const String &p_display_driver) const {
 	// Tests Rendering Device creation.
 
 	bool ok = false;
@@ -2394,7 +2402,7 @@ bool OS_Windows::_test_create_rendering_device() const {
 	return ok;
 }
 
-bool OS_Windows::_test_create_rendering_device_and_gl() const {
+bool OS_Windows::_test_create_rendering_device_and_gl(const String &p_display_driver) const {
 	// Tests OpenGL context and Rendering Device simultaneous creation. This function is expected to crash on some NVIDIA drivers.
 
 	WNDCLASSEXW wc_probe;
@@ -2438,7 +2446,7 @@ bool OS_Windows::_test_create_rendering_device_and_gl() const {
 	}
 
 	if (ok) {
-		ok = _test_create_rendering_device();
+		ok = _test_create_rendering_device(p_display_driver);
 	}
 
 #ifdef GLES3_ENABLED

Some files were not shown because too many files changed in this diff