Repository: godotengine/godot (language: cpp)

Issue:
Gray line at the bottom of scaled VideoStreamPlayer
### Tested versions

Godot Engine v4.2.1.stable.official.b09f793f5 - https://godotengine.org



### System information

Linux; Vulkan API 1.3.255 - Forward+ - Using Vulkan Device #0: AMD - AMD Radeon RX 5600 XT (RADV NAVI10)  

### Issue description

When have a VideoStreamPlayer that's scaled down (in my case, to 50%), when the video plays, it shows a gray line at the bottom.  The gray line is not visible when I play the video at 1:1 scale.

I don't think this is the same as #62752 -- first, I think my video doesn't have any overscan since its resolution is a multiple of 16, and second, it doesn't manifest as duplication but as a grey line.

![gray-line](https://github.com/godotengine/godot/assets/77003/13a89274-61ea-4d83-a44f-52dc807e4e7f)

Weirdly, it seems to depend on some tiny details of the video file.  I have one video file which experiences it, and one that doesn't even though both are the same resolution, frame rate, etc.  The one with the line is made by:
`ffmpeg -r 30  -pattern_type sequence -i out/cat-%06d.png    -map 0:v  -y  cat.ogv`
The one without is made by:
`theora_png2theora -k 30 -f 30 out/cat-%06d.png -o cat2.ogv`

Unfortunately, as you can see, the second one produces a video with the wrong color (and I have to run it through ffmpeg anyway to get the audio added; to save you from having to hear my awful voice acting looping over and over, I didn't add the audio to either of the clips in the repro project).  

### Steps to reproduce

The attached sample code reproduces this issue.  I have a VideoStreamPlayer which is scaled (in this case, the root node is scaled, because that pretty closely matches my actual project).   There are actually two players, to demonstrate that the effect depends on some weird detail of the actual video; the one on the left is the one with the problem (the color mismatch on the right is Theora's fault not Godot's).

### Minimal reproduction project (MRP)

[white-line.zip](https://github.com/godotengine/godot/files/14086142/white-line.zip)

MetalFX Temporal upscaling fallback on non-supported rendering drivers does not disable TAA jitter (or fallback to FSR2)
### Tested versions

- Reproducible in: 4.4.stable
- MetalFX is not available before 4.4.

### System information

Godot v4.4.stable (4c311cbee) - Fedora Linux 41 (KDE Plasma) on X11 - X11 display driver, Multi-window, 1 monitor - OpenGL 3 (Compatibility) - NVIDIA GeForce RTX 4090 (nvidia; 570.86.16) - 13th Gen Intel(R) Core(TM) i9-13900K (32 threads)

### Issue description

When attempting to use MetalFX Temporal upscaling on an unsupported rendering driver, the TAA jitter is still active despite not being needed with bilinear scaling. MetalFX Spatial upscaling doesn't have this issue, as it doesn't engage TAA jitter in the first place due to its purely spatial nature.

Alternatively (and this is the solution I prefer), we should fallback to FSR2 when MetalFX Temporal upscaling is not available (and FSR1 when MetalFX Spatial upscaling is not available). This will look better than falling back to bilinear scaling and means we won't have to disable TAA jitter, as it's still needed with FSR2.

cc @stuartcarnie 

https://github.com/user-attachments/assets/bde8cd82-63ba-4f82-ba59-0be8e46ea63c

### Steps to reproduce

- Create a project using the Forward+ rendering method.
- Set the root viewport's 3D scaling mode to MetalFX Temporal using a script on a platform/renderer that doesn't support MetalFX upscaling (Windows, Linux, or macOS when using MoltenVK):
```gdscript
func _ready() -> void:
    get_window().scaling_3d_mode = Viewport.SCALING_3D_MODE_METALFX_TEMPORAL
```

### Minimal reproduction project (MRP)

https://github.com/godotengine/tps-demo/pull/196
*Change the check in the demo's code from `== "metal"` to `!= "metal` to test this on unsupported drivers. I suggest using the Ultra Performance scaling mode so the issue is more noticeable.*


Discussion / hints:
I've fixed #62752 and this still happens so I don't think they're related at all.
It only happens when texture filter is set to linear. Setting the filter to nearest removes the gray line.

It's weird that it happens only in one of the videos but the background color is different so that might be the reason.
Video on the left with the gray line:

```
❯ ffprobe cat.ogv                                                                                  
Input #0, ogg, from 'cat.ogv':
  Duration: 00:00:05.73, start: 0.000000, bitrate: 216 kb/s
  Stream #0:0: Video: theora, yuv444p, 256x256 [SAR 1:1 DAR 1:1], 30 tbr, 30 tbn
      Metadata:
        encoder         : Lavc60.31.102 libtheora
```

Video on the right (which renders correctly in Godot):

```
❯ ffprobe cat2.ogv
[ogg @ 0x559083ead180] Broken file, keyframe not correctly marked.
Input #0, ogg, from 'cat2.ogv':
  Duration: 00:00:05.77, start: 0.000000, bitrate: 65 kb/s
  Stream #0:0: Video: theora, yuv420p, 256x256, 30 tbr, 30 tbn
```


Fix patch:
diff --git a/.github/actions/godot-cpp-build/action.yml b/.github/actions/godot-cpp-build/action.yml
index 7667527246ed..1046f09470e9 100644
--- a/.github/actions/godot-cpp-build/action.yml
+++ b/.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
diff --git a/.github/workflows/android_builds.yml b/.github/workflows/android_builds.yml
index f1c721fb6382..865f826bc0f3 100644
--- a/.github/workflows/android_builds.yml
+++ b/.github/workflows/android_builds.yml
@@ -62,8 +62,8 @@ jobs:
       - name: Download pre-built Android Swappy Frame Pacing Library
         uses: dsaltares/fetch-gh-release-asset@1.1.2
         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
 
diff --git a/.github/workflows/linux_builds.yml b/.github/workflows/linux_builds.yml
index 44aefc77a73b..1cb9637cee50 100644
--- a/.github/workflows/linux_builds.yml
+++ b/.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
diff --git a/.gitignore b/.gitignore
index 2c3bf742ba2e..8adc7b786c63 100644
--- a/.gitignore
+++ b/.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/
 
diff --git a/core/object/worker_thread_pool.cpp b/core/object/worker_thread_pool.cpp
index 08903d61964d..d9ff30576794 100644
--- a/core/object/worker_thread_pool.cpp
+++ b/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();
+	}
 }
diff --git a/core/object/worker_thread_pool.h b/core/object/worker_thread_pool.h
index 58e86e3e48eb..76332ec8a3d3 100644
--- a/core/object/worker_thread_pool.h
+++ b/core/object/worker_thread_pool.h
@@ -119,6 +119,7 @@ class WorkerThreadPool : public Object {
 		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 @@ class WorkerThreadPool : public Object {
 
 	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 @@ class WorkerThreadPool : public Object {
 #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 @@ class WorkerThreadPool : public Object {
 	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();
 };
 
diff --git a/core/os/os.h b/core/os/os.h
index e0ec320e32ba..c8ac5c8eac7a 100644
--- a/core/os/os.h
+++ b/core/os/os.h
@@ -362,9 +362,9 @@ class OS {
 	// 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();
diff --git a/core/variant/callable.cpp b/core/variant/callable.cpp
index 402dc6823c0a..0603ea273253 100644
--- a/core/variant/callable.cpp
+++ b/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) {
diff --git a/doc/classes/LineEdit.xml b/doc/classes/LineEdit.xml
index c6cdacb6ec25..879b179dbfef 100644
--- a/doc/classes/LineEdit.xml
+++ b/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.
diff --git a/doc/classes/NativeMenu.xml b/doc/classes/NativeMenu.xml
index 6081d7afc51e..5eaec56e7ec9 100644
--- a/doc/classes/NativeMenu.xml
+++ b/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">
diff --git a/doc/classes/RDTextureView.xml b/doc/classes/RDTextureView.xml
index bd8102d5531d..2e03ee44aab4 100644
--- a/doc/classes/RDTextureView.xml
+++ b/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">
diff --git a/doc/classes/RDVertexAttribute.xml b/doc/classes/RDVertexAttribute.xml
index 364b82526b6c..04e4bb53a045 100644
--- a/doc/classes/RDVertexAttribute.xml
+++ b/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">
diff --git a/doc/classes/RenderingDevice.xml b/doc/classes/RenderingDevice.xml
index 8db0272c75bf..2fc0e485447c 100644
--- a/doc/classes/RenderingDevice.xml
+++ b/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">
diff --git a/drivers/apple/joypad_apple.h b/drivers/apple/joypad_apple.h
index 909648e253dd..d89707c03d75 100644
--- a/drivers/apple/joypad_apple.h
+++ b/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();
diff --git a/drivers/apple/joypad_apple.mm b/drivers/apple/joypad_apple.mm
index 148ec4cb2718..e14fc96a2841 100644
--- a/drivers/apple/joypad_apple.mm
+++ b/drivers/apple/joypad_apple.mm
@@ -144,6 +144,12 @@ void play_strong_pattern(CHHapticPattern *p_pattern) {
 		}
 	}
 
+	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 @@ void play_strong_pattern(CHHapticPattern *p_pattern) {
 	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);
diff --git a/drivers/d3d12/rendering_device_driver_d3d12.cpp b/drivers/d3d12/rendering_device_driver_d3d12.cpp
index 29b36f140551..f5772f2e3024 100644
--- a/drivers/d3d12/rendering_device_driver_d3d12.cpp
+++ b/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) {
diff --git a/drivers/gles3/effects/copy_effects.cpp b/drivers/gles3/effects/copy_effects.cpp
index 47ca832bd7ab..4d74a4996b9f 100644
--- a/drivers/gles3/effects/copy_effects.cpp
+++ b/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);
diff --git a/drivers/gles3/rasterizer_canvas_gles3.cpp b/drivers/gles3/rasterizer_canvas_gles3.cpp
index 0ca013abd1a0..441636cd26db 100644
--- a/drivers/gles3/rasterizer_canvas_gles3.cpp
+++ b/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;
diff --git a/drivers/gles3/storage/particles_storage.cpp b/drivers/gles3/storage/particles_storage.cpp
index 73d7e340d2a8..599412c5b2de 100644
--- a/drivers/gles3/storage/particles_storage.cpp
+++ b/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) {
diff --git a/drivers/metal/pixel_formats.mm b/drivers/metal/pixel_formats.mm
index c9226590b169..c784faa8397b 100644
--- a/drivers/metal/pixel_formats.mm
+++ b/drivers/metal/pixel_formats.mm
@@ -493,32 +493,46 @@ void clear(T *p_val, size_t p_count = 1) {
 	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);
diff --git a/drivers/vulkan/rendering_device_driver_vulkan.cpp b/drivers/vulkan/rendering_device_driver_vulkan.cpp
index ae15191dd429..9c50c845a70b 100644
--- a/drivers/vulkan/rendering_device_driver_vulkan.cpp
+++ b/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);
 
diff --git a/drivers/windows/file_access_windows.cpp b/drivers/windows/file_access_windows.cpp
index a8e2a6859bc3..6835b510df73 100644
--- a/drivers/windows/file_access_windows.cpp
+++ b/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;
diff --git a/editor/animation_track_editor.cpp b/editor/animation_track_editor.cpp
index dbb23d6f17a6..bb51c16f37b5 100644
--- a/editor/animation_track_editor.cpp
+++ b/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);
diff --git a/editor/connections_dialog.cpp b/editor/connections_dialog.cpp
index dca43411e10b..5081be0d2d94 100644
--- a/editor/connections_dialog.cpp
+++ b/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")));
diff --git a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp b/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
index ba4c078d7ed5..00ae9dca7ebb 100644
--- a/editor/debugger/debug_adapter/debug_adapter_protocol.cpp
+++ b/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) {
diff --git a/editor/editor_inspector.cpp b/editor/editor_inspector.cpp
index 58b02ed2b8a1..87a7433e8cb9 100644
--- a/editor/editor_inspector.cpp
+++ b/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);
 		}
diff --git a/editor/editor_node.cpp b/editor/editor_node.cpp
index 6b725240f9e2..2261b14acbe3 100644
--- a/editor/editor_node.cpp
+++ b/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;
 
diff --git a/editor/editor_node.h b/editor/editor_node.h
index de3af997473a..63788e645f30 100644
--- a/editor/editor_node.h
+++ b/editor/editor_node.h
@@ -419,7 +419,7 @@ class EditorNode : public Node {
 	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;
diff --git a/editor/export/project_export.cpp b/editor/export/project_export.cpp
index 5b93a1a17ec7..0982520acd58 100644
--- a/editor/export/project_export.cpp
+++ b/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:") + " ");
 
diff --git a/editor/plugins/polygon_3d_editor_plugin.cpp b/editor/plugins/polygon_3d_editor_plugin.cpp
index 9d7567f2528a..f07ce22127c3 100644
--- a/editor/plugins/polygon_3d_editor_plugin.cpp
+++ b/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);
diff --git a/editor/plugins/script_editor_plugin.cpp b/editor/plugins/script_editor_plugin.cpp
index af7812abe806..45b921fad613 100644
--- a/editor/plugins/script_editor_plugin.cpp
+++ b/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);
diff --git a/editor/plugins/script_editor_plugin.h b/editor/plugins/script_editor_plugin.h
index 02e0529a7cf6..30198f1cb1c6 100644
--- a/editor/plugins/script_editor_plugin.h
+++ b/editor/plugins/script_editor_plugin.h
@@ -569,6 +569,7 @@ class ScriptEditor : public PanelContainer {
 	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);
diff --git a/editor/plugins/visual_shader_editor_plugin.cpp b/editor/plugins/visual_shader_editor_plugin.cpp
index b8af4603e80f..516ef516154c 100644
--- a/editor/plugins/visual_shader_editor_plugin.cpp
+++ b/editor/plugins/visual_shader_editor_plugin.cpp
@@ -7623,12 +7623,15 @@ class VisualShaderNodePluginInputEditor : public OptionButton {
 	}
 
 	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 @@ class VisualShaderNodePluginInputEditor : public OptionButton {
 			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 @@ class VisualShaderNodePluginVaryingEditor : public OptionButton {
 	}
 
 	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 @@ class VisualShaderNodePluginVaryingEditor : public OptionButton {
 
 		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 @@ class VisualShaderNodePluginVaryingEditor : public OptionButton {
 				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 @@ class VisualShaderNodePluginParameterRefEditor : public OptionButton {
 	}
 
 	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 @@ class VisualShaderNodePluginParameterRefEditor : public OptionButton {
 			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);
diff --git a/editor/project_manager.cpp b/editor/project_manager.cpp
index c3bd3acf775f..19bcfe8bc516 100644
--- a/editor/project_manager.cpp
+++ b/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);
diff --git a/editor/project_manager/project_list.cpp b/editor/project_manager/project_list.cpp
index b96e40b0b73e..7eeb2f8460a2 100644
--- a/editor/project_manager/project_list.cpp
+++ b/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.
diff --git a/editor/scene_tree_dock.cpp b/editor/scene_tree_dock.cpp
index 267d35f99458..1390d8256e08 100644
--- a/editor/scene_tree_dock.cpp
+++ b/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);
 				}
 			}
 		}
diff --git a/main/main.cpp b/main/main.cpp
index 10064b350376..9a1ab8771245 100644
--- a/main/main.cpp
+++ b/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);
 			}
diff --git a/modules/jolt_physics/misc/jolt_math_funcs.cpp b/modules/jolt_physics/misc/jolt_math_funcs.cpp
new file mode 100644
index 000000000000..b0ef5f45747d
--- /dev/null
+++ b/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);
+}
diff --git a/modules/jolt_physics/misc/jolt_math_funcs.h b/modules/jolt_physics/misc/jolt_math_funcs.h
new file mode 100644
index 000000000000..ba2d290ac77c
--- /dev/null
+++ b/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
diff --git a/modules/jolt_physics/objects/jolt_area_3d.cpp b/modules/jolt_physics/objects/jolt_area_3d.cpp
index b0a9ffd62225..0a2693ddb634 100644
--- a/modules/jolt_physics/objects/jolt_area_3d.cpp
+++ b/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);
diff --git a/modules/jolt_physics/objects/jolt_body_3d.cpp b/modules/jolt_physics/objects/jolt_body_3d.cpp
index ac2b37470ef1..5bc3aee1bce7 100644
--- a/modules/jolt_physics/objects/jolt_body_3d.cpp
+++ b/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);
diff --git a/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp b/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
index bfe77e008d59..f32ddad0d469 100644
--- a/modules/jolt_physics/objects/jolt_shaped_object_3d.cpp
+++ b/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];
 
diff --git a/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp b/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp
index 3852e42d51f2..d7a4afec57ba 100644
--- a/modules/jolt_physics/spaces/jolt_physics_direct_space_state_3d.cpp
+++ b/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();
 
diff --git a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
index 7a07e0a97539..22420e2b63ca 100644
--- a/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
+++ b/modules/mono/editor/GodotTools/GodotTools.ProjectEditor/ProjectUtils.cs
@@ -191,6 +191,13 @@ private static void EnsureTargetFrameworkMatchesMinimumRequirement(MSBuildProjec
                 // 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
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
index dda776fee5f5..cb180f9a8b3f 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/InteropStructs.cs
@@ -455,6 +455,12 @@ public readonly IntPtr Object
             get => _data._m_obj_data.obj;
         }
 
+        public readonly ulong ObjectId
+        {
+            [MethodImpl(MethodImplOptions.AggressiveInlining)]
+            get => _data._m_obj_data.id;
+        }
+
         public void Dispose()
         {
             switch (Type)
diff --git a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
index a48e60a30f60..9e5df0fa5c31 100644
--- a/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
+++ b/modules/mono/glue/GodotSharp/GodotSharp/Core/NativeInterop/VariantUtils.cs
@@ -485,7 +485,14 @@ public static Rid ConvertToRid(in godot_variant p_var)
                 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)
diff --git a/modules/multiplayer/editor/editor_network_profiler.cpp b/modules/multiplayer/editor/editor_network_profiler.cpp
index 1f9444ed72b7..5a2611added9 100644
--- a/modules/multiplayer/editor/editor_network_profiler.cpp
+++ b/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);
diff --git a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp b/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
index c4a05220115b..08b3afb0192e 100644
--- a/modules/openxr/extensions/platform/openxr_opengl_extension.cpp
+++ b/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...");
diff --git a/modules/openxr/register_types.cpp b/modules/openxr/register_types.cpp
index 69c119d069e3..785de3d6210b 100644
--- a/modules/openxr/register_types.cpp
+++ b/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
 	}
diff --git a/modules/text_server_adv/text_server_adv.cpp b/modules/text_server_adv/text_server_adv.cpp
index 52a61cd92446..71bded1ff1d5 100644
--- a/modules/text_server_adv/text_server_adv.cpp
+++ b/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();
diff --git a/modules/text_server_adv/text_server_adv.h b/modules/text_server_adv/text_server_adv.h
index 6561667e80e6..981cb277f70d 100644
--- a/modules/text_server_adv/text_server_adv.h
+++ b/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 {
diff --git a/modules/text_server_fb/text_server_fb.cpp b/modules/text_server_fb/text_server_fb.cpp
index 8ca81368436f..c70f5e00d113 100644
--- a/modules/text_server_fb/text_server_fb.cpp
+++ b/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();
diff --git a/modules/text_server_fb/text_server_fb.h b/modules/text_server_fb/text_server_fb.h
index b66f7189bee0..3338a8a62b1b 100644
--- a/modules/text_server_fb/text_server_fb.h
+++ b/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 {
diff --git a/platform/android/README.md b/platform/android/README.md
index 01eb9c03a664..4a08bb524180 100644
--- a/platform/android/README.md
+++ b/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:
 
diff --git a/platform/android/android_input_handler.cpp b/platform/android/android_input_handler.cpp
index 9a5af7a349dd..859f4c3c1b3e 100644
--- a/platform/android/android_input_handler.cpp
+++ b/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;
 			}
 
diff --git a/platform/android/detect.py b/platform/android/detect.py
index 571cfd9819c0..cac7c3f48d4e 100644
--- a/platform/android/detect.py
+++ b/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"
diff --git a/platform/android/java/app/AndroidManifest.xml b/platform/android/java/app/AndroidManifest.xml
index 60ce75fd16e2..1d59d15d560f 100644
--- a/platform/android/java/app/AndroidManifest.xml
+++ b/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" >
 
diff --git a/platform/android/java/editor/src/main/AndroidManifest.xml b/platform/android/java/editor/src/main/AndroidManifest.xml
index a1971554bf7d..b136e348b2c7 100644
--- a/platform/android/java/editor/src/main/AndroidManifest.xml
+++ b/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"
diff --git a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
index 6f036c15a32a..d9d1cedc6a0f 100644
--- a/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
+++ b/platform/android/java/lib/src/org/godotengine/godot/input/GodotInputHandler.java
@@ -59,6 +59,7 @@
 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 @@ private boolean isKeyEventGameDevice(int source) {
 	}
 
 	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 boolean onKeyDown(final int keyCode, KeyEvent event) {
 	}
 
 	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 boolean onTouchEvent(final MotionEvent event) {
 	}
 
 	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
diff --git a/platform/android/java_class_wrapper.cpp b/platform/android/java_class_wrapper.cpp
index c19196ae8713..4980258d9898 100644
--- a/platform/android/java_class_wrapper.cpp
+++ b/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;
 	}
 
diff --git a/platform/android/java_godot_lib_jni.cpp b/platform/android/java_godot_lib_jni.cpp
index 054c809c6e3f..0bb78bb9d974 100644
--- a/platform/android/java_godot_lib_jni.cpp
+++ b/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"
diff --git a/platform/ios/export/export_plugin.cpp b/platform/ios/export/export_plugin.cpp
index 84bf722d14f4..7ca7dc1ac675 100644
--- a/platform/ios/export/export_plugin.cpp
+++ b/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 {
diff --git a/platform/linuxbsd/README.md b/platform/linuxbsd/README.md
index c4f287cc6c8c..e1a95324ad50 100644
--- a/platform/linuxbsd/README.md
+++ b/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
 > <lewing@isc.tamu.edu> and [The GIMP](https://isc.tamu.edu/~lewing/gimp/)
diff --git a/platform/linuxbsd/os_linuxbsd.cpp b/platform/linuxbsd/os_linuxbsd.cpp
index 9fba97d552a0..466841d747f9 100644
--- a/platform/linuxbsd/os_linuxbsd.cpp
+++ b/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;
 
diff --git a/platform/linuxbsd/os_linuxbsd.h b/platform/linuxbsd/os_linuxbsd.h
index 9084061eb96c..92e57304f3c3 100644
--- a/platform/linuxbsd/os_linuxbsd.h
+++ b/platform/linuxbsd/os_linuxbsd.h
@@ -138,6 +138,9 @@ class OS_LinuxBSD : public OS_Unix {
 
 	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();
 };
diff --git a/platform/linuxbsd/wayland/detect_prime_egl.h b/platform/linuxbsd/wayland/detect_prime_egl.h
index 3391e020d8b2..eff5d8a2918d 100644
--- a/platform/linuxbsd/wayland/detect_prime_egl.h
+++ b/platform/linuxbsd/wayland/detect_prime_egl.h
@@ -77,9 +77,9 @@ class DetectPrimeEGL {
 		{ nullptr, 0 }
 	};
 
+public:
 	static void create_context(EGLenum p_platform_enum);
 
-public:
 	static int detect_prime(EGLenum p_platform_enum);
 };
 
diff --git a/platform/linuxbsd/x11/detect_prime_x11.cpp b/platform/linuxbsd/x11/detect_prime_x11.cpp
index c2cb02b93781..5ec5a53b989c 100644
--- a/platform/linuxbsd/x11/detect_prime_x11.cpp
+++ b/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;
diff --git a/platform/linuxbsd/x11/detect_prime_x11.h b/platform/linuxbsd/x11/detect_prime_x11.h
index 62fe42602678..c8da79bf9221 100644
--- a/platform/linuxbsd/x11/detect_prime_x11.h
+++ b/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
 
diff --git a/platform/linuxbsd/x11/display_server_x11.cpp b/platform/linuxbsd/x11/display_server_x11.cpp
index 26c94bf25cf1..56277d85c960 100644
--- a/platform/linuxbsd/x11/display_server_x11.cpp
+++ b/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) {
diff --git a/platform/web/README.md b/platform/web/README.md
index 906eb508fecd..eac2c7fe8e17 100644
--- a/platform/web/README.md
+++ b/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.
diff --git a/platform/windows/os_windows.cpp b/platform/windows/os_windows.cpp
index 7ef3de2dd79c..b0ec6852af2e 100644
--- a/platform/windows/os_windows.cpp
+++ b/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
diff --git a/platform/windows/os_windows.h b/platform/windows/os_windows.h
index c46d86a650ef..d9b8233e511e 100644
--- a/platform/windows/os_windows.h
+++ b/platform/windows/os_windows.h
@@ -252,8 +252,8 @@ class OS_Windows : public OS {
 
 	void set_main_window(HWND p_main_window) { main_window = p_main_window; }
 
-	virtual bool _test_create_rendering_device_and_gl() const override;
-	virtual bool _test_create_rendering_device() const 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;
 
 	HINSTANCE get_hinstance() { return hInstance; }
 	OS_Windows(HINSTANCE _hInstance);
diff --git a/scene/2d/cpu_particles_2d.cpp b/scene/2d/cpu_particles_2d.cpp
index 279bb8ff3d95..b77003509786 100644
--- a/scene/2d/cpu_particles_2d.cpp
+++ b/scene/2d/cpu_particles_2d.cpp
@@ -1194,8 +1194,6 @@ void CPUParticles2D::_notification(int p_what) {
 
 			_refresh_interpolation_state();
 
-			set_physics_process_internal(emitting && _interpolation_data.interpolated_follow);
-
 			// If we are interpolated following, then reset physics interpolation
 			// when first appearing. This won't be called by canvas item, as in the
 			// following mode, is_physics_interpolated() is actually FALSE.
diff --git a/scene/3d/navigation_link_3d.cpp b/scene/3d/navigation_link_3d.cpp
index 8f9b7a60b513..dc518ee45256 100644
--- a/scene/3d/navigation_link_3d.cpp
+++ b/scene/3d/navigation_link_3d.cpp
@@ -262,6 +262,12 @@ void NavigationLink3D::_notification(int p_what) {
 		case NOTIFICATION_EXIT_TREE: {
 			_link_exit_navigation_map();
 		} break;
+
+#ifdef DEBUG_ENABLED
+		case NOTIFICATION_VISIBILITY_CHANGED: {
+			_update_debug_mesh();
+		} break;
+#endif // DEBUG_ENABLED
 	}
 }
 
diff --git a/scene/3d/voxelizer.cpp b/scene/3d/voxelizer.cpp
index 711b265bb641..a4c36deabdec 100644
--- a/scene/3d/voxelizer.cpp
+++ b/scene/3d/voxelizer.cpp
@@ -399,6 +399,9 @@ int Voxelizer::get_bake_steps(Ref<Mesh> &p_mesh) const {
 Voxelizer::BakeResult Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh> &p_mesh, const Vector<Ref<Material>> &p_materials, const Ref<Material> &p_override_material, BakeStepFunc p_bake_step_func) {
 	ERR_FAIL_COND_V_MSG(!p_xform.is_finite(), BAKE_RESULT_INVALID_PARAMETER, "Invalid mesh bake transform.");
 
+	// Precalculate for transforming vertex normals
+	Basis normal_xform = p_xform.basis.inverse().transposed();
+
 	int bake_total = get_bake_steps(p_mesh), bake_current = 0;
 
 	for (int i = 0; i < p_mesh->get_surface_count(); i++) {
@@ -463,7 +466,7 @@ Voxelizer::BakeResult Voxelizer::plot_mesh(const Transform3D &p_xform, Ref<Mesh>
 
 				if (nr) {
 					for (int k = 0; k < 3; k++) {
-						normal[k] = nr[ir[j * 3 + k]];
+						normal[k] = normal_xform.xform(nr[ir[j * 3 + k]]).normalized();
 					}
 				}
 
diff --git a/scene/animation/animation_blend_space_1d.cpp b/scene/animation/animation_blend_space_1d.cpp
index 4716a8fcec3d..acbc892d478f 100644
--- a/scene/animation/animation_blend_space_1d.cpp
+++ b/scene/animation/animation_blend_space_1d.cpp
@@ -380,7 +380,13 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace1D::_process(const AnimationM
 				Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
 				Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
 				if (na_c.is_valid() && na_n.is_valid()) {
+					na_n->process_state = process_state;
+					na_c->process_state = process_state;
+
 					na_n->set_backward(na_c->is_backward());
+
+					na_n = nullptr;
+					na_c = nullptr;
 				}
 				// See how much animation remains.
 				pi.seeked = false;
diff --git a/scene/animation/animation_blend_space_2d.cpp b/scene/animation/animation_blend_space_2d.cpp
index 7399fea31719..ee1d4121d595 100644
--- a/scene/animation/animation_blend_space_2d.cpp
+++ b/scene/animation/animation_blend_space_2d.cpp
@@ -557,7 +557,13 @@ AnimationNode::NodeTimeInfo AnimationNodeBlendSpace2D::_process(const AnimationM
 				Ref<AnimationNodeAnimation> na_c = static_cast<Ref<AnimationNodeAnimation>>(blend_points[cur_closest].node);
 				Ref<AnimationNodeAnimation> na_n = static_cast<Ref<AnimationNodeAnimation>>(blend_points[new_closest].node);
 				if (na_c.is_valid() && na_n.is_valid()) {
+					na_n->process_state = process_state;
+					na_c->process_state = process_state;
+
 					na_n->set_backward(na_c->is_backward());
+
+					na_n = nullptr;
+					na_c = nullptr;
 				}
 				// See how much animation remains.
 				pi.seeked = false;
diff --git a/scene/debugger/scene_debugger.cpp b/scene/debugger/scene_debugger.cpp
index c620c5fd5c80..6b4a00f61719 100644
--- a/scene/debugger/scene_debugger.cpp
+++ b/scene/debugger/scene_debugger.cpp
@@ -1362,7 +1362,7 @@ void RuntimeNodeSelect::_root_window_input(const Ref<InputEvent> &p_event) {
 
 	if (camera_override) {
 		if (node_select_type == NODE_TYPE_2D) {
-			if (panner->gui_input(p_event, Rect2(Vector2(), root->get_size()))) {
+			if (panner->gui_input(p_event, Rect2(Vector2(), root->get_visible_rect().get_size()))) {
 				return;
 			}
 		} else if (node_select_type == NODE_TYPE_3D) {
@@ -1821,8 +1821,9 @@ void RuntimeNodeSelect::_find_canvas_items_at_pos(const Point2 &p_pos, Node *p_n
 }
 
 void RuntimeNodeSelect::_pan_callback(Vector2 p_scroll_vec, Ref<InputEvent> p_event) {
-	view_2d_offset.x -= p_scroll_vec.x / view_2d_zoom;
-	view_2d_offset.y -= p_scroll_vec.y / view_2d_zoom;
+	Vector2 scroll = SceneTree::get_singleton()->get_root()->get_screen_transform().affine_inverse().xform(p_scroll_vec);
+	view_2d_offset.x -= scroll.x / view_2d_zoom;
+	view_2d_offset.y -= scroll.y / view_2d_zoom;
 
 	_update_view_2d();
 }
diff --git a/scene/gui/color_picker.cpp b/scene/gui/color_picker.cpp
index aab7c37bbb5e..a5348ad3b290 100644
--- a/scene/gui/color_picker.cpp
+++ b/scene/gui/color_picker.cpp
@@ -2374,7 +2374,7 @@ ColorPicker::ColorPicker() {
 	swatches_vbc->add_child(palette_box);
 
 	btn_preset = memnew(Button);
-	btn_preset->set_text("Swatches");
+	btn_preset->set_text(ETR("Swatches"));
 	btn_preset->set_flat(true);
 	btn_preset->set_toggle_mode(true);
 	btn_preset->set_focus_mode(FOCUS_NONE);
diff --git a/scene/gui/line_edit.cpp b/scene/gui/line_edit.cpp
index 969e2fd3a35c..f4d66d5a281d 100644
--- a/scene/gui/line_edit.cpp
+++ b/scene/gui/line_edit.cpp
@@ -1733,7 +1733,7 @@ void LineEdit::_validate_caret_can_draw() {
 		draw_caret = true;
 		caret_blink_timer = 0.0;
 	}
-	caret_can_draw = editing && (window_has_focus || (menu && menu->has_focus())) && (has_focus() || caret_force_displayed);
+	caret_can_draw = (caret_force_displayed && !is_part_of_edited_scene()) || (editing && (window_has_focus || (menu && menu->has_focus())) && has_focus());
 }
 
 void LineEdit::delete_char() {
diff --git a/scene/gui/spin_box.cpp b/scene/gui/spin_box.cpp
index e2e6f076781a..c77fda1ac805 100644
--- a/scene/gui/spin_box.cpp
+++ b/scene/gui/spin_box.cpp
@@ -63,20 +63,39 @@ void SpinBox::_update_text(bool p_only_update_if_value_changed) {
 			value += " " + suffix;
 		}
 	}
+
+	if (!accepted && update_on_text_changed && !line_edit->get_text().replace(",", ".").contains_char('.')) {
+		value = String::num(get_value(), 0);
+	}
+
 	line_edit->set_text_with_selection(value);
 }
 
 void SpinBox::_text_submitted(const String &p_string) {
 	if (p_string.is_empty()) {
-		_update_text();
 		return;
 	}
 
+	String text = p_string;
+
+	if (update_on_text_changed) {
+		// Convert commas ',' to dots '.' for French/German etc. keyboard layouts.
+		text = p_string.replace(",", ".");
+
+		if (!text.begins_with(".") && p_string.ends_with(".")) {
+			return;
+		}
+
+		if (text.begins_with(".")) {
+			line_edit->set_text("0.");
+			line_edit->set_caret_column(line_edit->get_text().length());
+			return;
+		}
+	}
+
 	Ref<Expression> expr;
 	expr.instantiate();
 
-	// Convert commas ',' to dots '.' for French/German etc. keyboard layouts.
-	String text = p_string.replace(",", ".");
 	text = text.replace(";", ",");
 	text = TS->parse_number(text);
 	// Ignore the prefix and suffix in the expression.
@@ -107,12 +126,17 @@ void SpinBox::_text_submitted(const String &p_string) {
 }
 
 void SpinBox::_text_changed(const String &p_string) {
+	accepted = false;
 	int cursor_pos = line_edit->get_caret_column();
 
 	_text_submitted(p_string);
 
+	String text = p_string.replace(",", ".");
+
 	// Line edit 'set_text' method resets the cursor position so we need to undo that.
-	line_edit->set_caret_column(cursor_pos);
+	if (update_on_text_changed && !text.begins_with(".")) {
+		line_edit->set_caret_column(cursor_pos);
+	}
 }
 
 LineEdit *SpinBox::get_line_edit() {
@@ -192,6 +216,7 @@ void SpinBox::gui_input(const Ref<InputEvent> &p_event) {
 	if (mb.is_valid() && mb->is_pressed()) {
 		switch (mb->get_button_index()) {
 			case MouseButton::LEFT: {
+				accepted = true;
 				line_edit->grab_focus();
 
 				if (mouse_on_up_button || mouse_on_down_button) {
@@ -291,9 +316,12 @@ void SpinBox::_line_edit_editing_toggled(bool p_toggled_on) {
 			line_edit->select_all();
 		}
 	} else {
+		accepted = true;
+
 		if (Input::get_singleton()->is_action_pressed("ui_cancel") || line_edit->get_text().is_empty()) {
 			_update_text(); // Revert text if editing was canceled.
 		} else {
+			line_edit->set_text(line_edit->get_text().trim_suffix(".").trim_suffix(","));
 			_update_text(true); // Update text in case value was changed this frame (e.g. on `focus_exited`).
 			_text_submitted(line_edit->get_text());
 		}
diff --git a/scene/gui/spin_box.h b/scene/gui/spin_box.h
index 79076d3f4692..dcc1f73eb9a2 100644
--- a/scene/gui/spin_box.h
+++ b/scene/gui/spin_box.h
@@ -40,6 +40,7 @@ class SpinBox : public Range {
 
 	LineEdit *line_edit = nullptr;
 	bool update_on_text_changed = false;
+	bool accepted = true;
 
 	struct SizingCache {
 		int buttons_block_width = 0;
diff --git a/scene/gui/text_edit.cpp b/scene/gui/text_edit.cpp
index ad2202315008..887d4eaf00f1 100644
--- a/scene/gui/text_edit.cpp
+++ b/scene/gui/text_edit.cpp
@@ -6081,7 +6081,7 @@ void TextEdit::adjust_viewport_to_caret(int p_caret) {
 		set_line_as_last_visible(cur_line, cur_wrap);
 	}
 
-	_adjust_viewport_to_caret_horizontally(p_caret);
+	_adjust_viewport_to_caret_horizontally(p_caret, false);
 }
 
 void TextEdit::center_viewport_to_caret(int p_caret) {
@@ -8186,7 +8186,7 @@ void TextEdit::_scroll_lines_down() {
 	merge_overlapping_carets();
 }
 
-void TextEdit::_adjust_viewport_to_caret_horizontally(int p_caret) {
+void TextEdit::_adjust_viewport_to_caret_horizontally(int p_caret, bool p_maximize_selection) {
 	if (get_line_wrapping_mode() != LineWrappingMode::LINE_WRAPPING_NONE) {
 		first_visible_col = 0;
 		h_scroll->set_value(first_visible_col);
@@ -8220,7 +8220,7 @@ void TextEdit::_adjust_viewport_to_caret_horizontally(int p_caret) {
 		int ime_end_column = get_caret_column(p_caret) + (ime_selection.y > 0 ? ime_selection.x + ime_selection.y : ime_text.length());
 		caret_end_pos = _get_column_x_offset_for_line(ime_end_column, get_caret_line(p_caret), ime_end_column);
 		prioritize_end = false;
-	} else if (has_selection(p_caret) && get_selection_from_line(p_caret) == get_selection_to_line(p_caret)) {
+	} else if (p_maximize_selection && has_selection(p_caret) && get_selection_from_line(p_caret) == get_selection_to_line(p_caret)) {
 		// Use selection if it is on one line.
 		caret_start_pos = _get_column_x_offset_for_line(get_selection_from_column(p_caret), get_caret_line(p_caret), get_selection_from_column(p_caret));
 		caret_end_pos = _get_column_x_offset_for_line(get_selection_to_column(p_caret), get_caret_line(p_caret), get_selection_to_column(p_caret));
diff --git a/scene/gui/text_edit.h b/scene/gui/text_edit.h
index 21c8781e2648..b8d78283403d 100644
--- a/scene/gui/text_edit.h
+++ b/scene/gui/text_edit.h
@@ -539,7 +539,7 @@ class TextEdit : public Control {
 	void _scroll_lines_up();
 	void _scroll_lines_down();
 
-	void _adjust_viewport_to_caret_horizontally(int p_caret = 0);
+	void _adjust_viewport_to_caret_horizontally(int p_caret = 0, bool p_maximize_selection = true);
 
 	// Minimap.
 	bool draw_minimap = false;
diff --git a/scene/gui/tree.cpp b/scene/gui/tree.cpp
index cd6c51de4b2d..dc14294ea05b 100644
--- a/scene/gui/tree.cpp
+++ b/scene/gui/tree.cpp
@@ -3506,7 +3506,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
 	Ref<InputEventKey> k = p_event;
 
 	bool is_command = k.is_valid() && k->is_command_or_control_pressed();
-	if (p_event->is_action("ui_right") && p_event->is_pressed()) {
+	if (p_event->is_action(cache.rtl ? "ui_left" : "ui_right") && p_event->is_pressed()) {
 		if (!cursor_can_exit_tree) {
 			accept_event();
 		}
@@ -3524,7 +3524,7 @@ void Tree::gui_input(const Ref<InputEvent> &p_event) {
 		} else {
 			_go_down();
 		}
-	} else if (p_event->is_action("ui_left") && p_event->is_pressed()) {
+	} else if (p_event->is_action(cache.rtl ? "ui_right" : "ui_left") && p_event->is_pressed()) {
 		if (!cursor_can_exit_tree) {
 			accept_event();
 		}
@@ -4107,13 +4107,18 @@ bool Tree::edit_selected(bool p_force_edit) {
 		// "floor()" centers vertically.
 		Vector2 ofs(0, Math::floor((MAX(line_editor->get_minimum_size().height, rect.size.height - value_editor_height) - rect.size.height) / 2));
 
-		popup_rect.position = get_screen_position() + rect.position - ofs;
-		popup_rect.size = rect.size;
-
 		// Account for icon.
 		Size2 icon_size = _get_cell_icon_size(c) * popup_scale;
-		popup_rect.position.x += icon_size.x;
-		popup_rect.size.x -= icon_size.x;
+
+		popup_rect.size = rect.size;
+		popup_rect.size.x -= icon_size.x + theme_cache.h_separation;
+
+		popup_rect.position = rect.position - ofs;
+		popup_rect.position.x += icon_size.x + theme_cache.h_separation;
+		if (cache.rtl) {
+			popup_rect.position.x = get_size().width - popup_rect.position.x - popup_rect.size.x;
+		}
+		popup_rect.position += get_screen_position();
 
 		line_editor->clear();
 		line_editor->set_text(c.mode == TreeItem::CELL_MODE_STRING ? c.text : String::num(c.val, Math::range_step_decimals(c.step)));
diff --git a/scene/gui/video_stream_player.cpp b/scene/gui/video_stream_player.cpp
index fa965179a4d4..eb6aff27dc92 100644
--- a/scene/gui/video_stream_player.cpp
+++ b/scene/gui/video_stream_player.cpp
@@ -136,6 +136,7 @@ void VideoStreamPlayer::_notification(int p_notification) {
 		} break;
 
 		case NOTIFICATION_EXIT_TREE: {
+			stop();
 			AudioServer::get_singleton()->remove_mix_callback(_mix_audios, this);
 		} break;
 
diff --git a/scene/main/viewport.cpp b/scene/main/viewport.cpp
index d0054baeff25..f7fd1a2c5a7e 100644
--- a/scene/main/viewport.cpp
+++ b/scene/main/viewport.cpp
@@ -1509,6 +1509,7 @@ void Viewport::_gui_show_tooltip() {
 	// This way, the custom tooltip from `ConnectionsDockTree` can create
 	// its own tooltip without conflicting with the default one, even an empty tooltip.
 	if (base_tooltip && !base_tooltip->is_visible()) {
+		memdelete(base_tooltip);
 		return;
 	}
 
@@ -1546,6 +1547,8 @@ void Viewport::_gui_show_tooltip() {
 	panel->set_flag(Window::FLAG_POPUP, false);
 	panel->set_flag(Window::FLAG_MOUSE_PASSTHROUGH, true);
 	panel->set_wrap_controls(true);
+	panel->set_default_canvas_item_texture_filter(get_default_canvas_item_texture_filter());
+	panel->set_default_canvas_item_texture_repeat(get_default_canvas_item_texture_repeat());
 	panel->add_child(base_tooltip);
 	panel->gui_parent = this;
 
diff --git a/scene/resources/curve.cpp b/scene/resources/curve.cpp
index e982e4d13575..de68737b8c93 100644
--- a/scene/resources/curve.cpp
+++ b/scene/resources/curve.cpp
@@ -56,7 +56,7 @@ void Curve::set_point_count(int p_count) {
 	notify_property_list_changed();
 }
 
-int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode) {
+int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_tangent, TangentMode p_left_mode, TangentMode p_right_mode, bool p_mark_dirty) {
 	// Add a point and preserve order.
 
 	// Points must remain within the given value and domain ranges.
@@ -99,7 +99,9 @@ int Curve::_add_point(Vector2 p_position, real_t p_left_tangent, real_t p_right_
 
 	update_auto_tangents(ret);
 
-	mark_dirty();
+	if (p_mark_dirty) {
+		mark_dirty();
+	}
 
 	return ret;
 }
@@ -221,10 +223,12 @@ Curve::TangentMode Curve::get_point_right_mode(int p_index) const {
 	return _points[p_index].right_mode;
 }
 
-void Curve::_remove_point(int p_index) {
+void Curve::_remove_point(int p_index, bool p_mark_dirty) {
 	ERR_FAIL_UNSIGNED_INDEX((uint32_t)p_index, _points.size());
 	_points.remove_at(p_index);
-	mark_dirty();
+	if (p_mark_dirty) {
+		mark_dirty();
+	}
 }
 
 void Curve::remove_point(int p_index) {
@@ -252,16 +256,13 @@ void Curve::set_point_value(int p_index, real_t p_position) {
 int Curve::set_point_offset(int p_index, real_t p_offset) {
 	ERR_FAIL_UNSIGNED_INDEX_V((uint32_t)p_index, _points.size(), -1);
 	Point p = _points[p_index];
-	_remove_point(p_index);
-	int i = _add_point(Vector2(p_offset, p.position.y));
-	_points[i].left_tangent = p.left_tangent;
-	_points[i].right_tangent = p.right_tangent;
-	_points[i].left_mode = p.left_mode;
-	_points[i].right_mode = p.right_mode;
+	_remove_point(p_index, false);
+	int i = _add_point(Vector2(p_offset, p.position.y), p.left_tangent, p.right_tangent, p.left_mode, p.right_mode, false);
 	if (p_index != i) {
 		update_auto_tangents(p_index);
 	}
 	update_auto_tangents(i);
+	mark_dirty();
 	return i;
 }
 
diff --git a/scene/resources/curve.h b/scene/resources/curve.h
index fb774e879dff..16ce75198455 100644
--- a/scene/resources/curve.h
+++ b/scene/resources/curve.h
@@ -77,15 +77,15 @@ class Curve : public Resource {
 	void set_point_count(int p_count);
 
 	int add_point(Vector2 p_position,
-			real_t left_tangent = 0,
-			real_t right_tangent = 0,
-			TangentMode left_mode = TANGENT_FREE,
-			TangentMode right_mode = TANGENT_FREE);
+			real_t p_left_tangent = 0,
+			real_t p_right_tangent = 0,
+			TangentMode p_left_mode = TANGENT_FREE,
+			TangentMode p_right_mode = TANGENT_FREE);
 	int add_point_no_update(Vector2 p_position,
-			real_t left_tangent = 0,
-			real_t right_tangent = 0,
-			TangentMode left_mode = TANGENT_FREE,
-			TangentMode right_mode = TANGENT_FREE);
+			real_t p_left_tangent = 0,
+			real_t p_right_tangent = 0,
+			TangentMode p_left_mode = TANGENT_FREE,
+			TangentMode p_right_mode = TANGENT_FREE);
 	void remove_point(int p_index);
 	void clear_points();
 
@@ -127,10 +127,10 @@ class Curve : public Resource {
 	TangentMode get_point_left_mode(int p_index) const;
 	TangentMode get_point_right_mode(int p_index) const;
 
-	void update_auto_tangents(int i);
+	void update_auto_tangents(int p_index);
 
 	Array get_data() const;
-	void set_data(Array input);
+	void set_data(Array p_input);
 
 	void bake();
 	void _bake() const;
@@ -150,11 +150,12 @@ class Curve : public Resource {
 private:
 	void mark_dirty();
 	int _add_point(Vector2 p_position,
-			real_t left_tangent = 0,
-			real_t right_tangent = 0,
-			TangentMode left_mode = TANGENT_FREE,
-			TangentMode right_mode = TANGENT_FREE);
-	void _remove_point(int p_index);
+			real_t p_left_tangent = 0,
+			real_t p_right_tangent = 0,
+			TangentMode p_left_mode = TANGENT_FREE,
+			TangentMode p_right_mode = TANGENT_FREE,
+			bool p_mark_dirty = true);
+	void _remove_point(int p_index, bool p_mark_dirty = true);
 
 	LocalVector<Point> _points;
 	mutable bool _baked_cache_dirty = false;
diff --git a/scene/resources/packed_scene.cpp b/scene/resources/packed_scene.cpp
index 7f170e0085ec..e550a5beff90 100644
--- a/scene/resources/packed_scene.cpp
+++ b/scene/resources/packed_scene.cpp
@@ -328,7 +328,7 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
 
 						DeferredNodePathProperties dnp;
 						dnp.value = props[nprops[j].value];
-						dnp.base = node;
+						dnp.base = node->get_instance_id();
 						dnp.property = snames[name_idx];
 						deferred_node_paths.push_back(dnp);
 						continue;
@@ -521,25 +521,27 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
 
 	for (const DeferredNodePathProperties &dnp : deferred_node_paths) {
 		// Replace properties stored as NodePaths with actual Nodes.
+		Node *base = Object::cast_to<Node>(ObjectDB::get_instance(dnp.base));
+		ERR_CONTINUE_EDMSG(!base, vformat("Failed to set deferred property '%s' as the base node disappeared.", dnp.property));
 		if (dnp.value.get_type() == Variant::ARRAY) {
 			Array paths = dnp.value;
 
 			bool valid;
-			Array array = dnp.base->get(dnp.property, &valid);
-			ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, dnp.base->get_name()));
+			Array array = base->get(dnp.property, &valid);
+			ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, base->get_name()));
 			array = array.duplicate();
 
 			array.resize(paths.size());
 			for (int i = 0; i < array.size(); i++) {
-				array.set(i, dnp.base->get_node_or_null(paths[i]));
+				array.set(i, base->get_node_or_null(paths[i]));
 			}
-			dnp.base->set(dnp.property, array);
+			base->set(dnp.property, array);
 		} else if (dnp.value.get_type() == Variant::DICTIONARY) {
 			Dictionary paths = dnp.value;
 
 			bool valid;
-			Dictionary dict = dnp.base->get(dnp.property, &valid);
-			ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, dnp.base->get_name()));
+			Dictionary dict = base->get(dnp.property, &valid);
+			ERR_CONTINUE_EDMSG(!valid, vformat("Failed to get property '%s' from node '%s'.", dnp.property, base->get_name()));
 			dict = dict.duplicate();
 			bool convert_key = dict.get_typed_key_builtin() == Variant::OBJECT &&
 					ClassDB::is_parent_class(dict.get_typed_key_class_name(), "Node");
@@ -549,17 +551,17 @@ Node *SceneState::instantiate(GenEditState p_edit_state) const {
 			for (int i = 0; i < paths.size(); i++) {
 				Variant key = paths.get_key_at_index(i);
 				if (convert_key) {
-					key = dnp.base->get_node_or_null(key);
+					key = base->get_node_or_null(key);
 				}
 				Variant value = paths.get_value_at_index(i);
 				if (convert_value) {
-					value = dnp.base->get_node_or_null(value);
+					value = base->get_node_or_null(value);
 				}
 				dict[key] = value;
 			}
-			dnp.base->set(dnp.property, dict);
+			base->set(dnp.property, dict);
 		} else {
-			dnp.base->set(dnp.property, dnp.base->get_node_or_null(dnp.value));
+			base->set(dnp.property, base->get_node_or_null(dnp.value));
 		}
 	}
 
diff --git a/scene/resources/packed_scene.h b/scene/resources/packed_scene.h
index 9f8088910f18..ee2bba2f2088 100644
--- a/scene/resources/packed_scene.h
+++ b/scene/resources/packed_scene.h
@@ -70,7 +70,7 @@ class SceneState : public RefCounted {
 	};
 
 	struct DeferredNodePathProperties {
-		Node *base = nullptr;
+		ObjectID base;
 		StringName property;
 		Variant value;
 	};
diff --git a/scene/resources/particle_process_material.cpp b/scene/resources/particle_process_material.cpp
index 2c54564ff7b3..dd6e659e0612 100644
--- a/scene/resources/particle_process_material.cpp
+++ b/scene/resources/particle_process_material.cpp
@@ -1034,7 +1034,7 @@ void ParticleProcessMaterial::_update_shader() {
 			code += "	{\n";
 		}
 		code += "		float vel_mag = length(VELOCITY);\n";
-		code += "		float vel_infl = clamp(dynamic_params.turb_influence * turbulence_influence, 0.0, 1.0);\n";
+		code += "		float vel_infl = clamp(dynamic_params.turb_influence * turbulence_influence, 0.0, 1.0) * (DELTA <= 0.0 ? 0.0 : 1.0);\n";
 		code += "		VELOCITY = mix(VELOCITY, normalize(noise_direction) * vel_mag * (1.0 + (1.0 - vel_infl) * 0.2), vel_infl);\n";
 		code += "		vel_mag = length(controlled_displacement);\n";
 		code += "		controlled_displacement = mix(controlled_displacement, normalize(noise_direction) * vel_mag * (1.0 + (1.0 - vel_infl) * 0.2), vel_infl);\n";
@@ -1121,9 +1121,16 @@ void ParticleProcessMaterial::_update_shader() {
 		code += "		params.scale *= texture(scale_over_velocity_curve, vec2(0.0)).rgb;\n";
 		code += "	}\n";
 	}
-	code += "	TRANSFORM[0].xyz *= sign(params.scale.x) * max(abs(params.scale.x), 0.001);\n";
-	code += "	TRANSFORM[1].xyz *= sign(params.scale.y) * max(abs(params.scale.y), 0.001);\n";
-	code += "	TRANSFORM[2].xyz *= sign(params.scale.z) * max(abs(params.scale.z), 0.001);\n";
+	// A scale of 0 results in no emission at some emission amounts (including 3 and 6).
+	// `sign(scale)` is unsuitable, because sign(0) returns 0, nullifying the minimum value.
+	// The following evaluates to 1 when scale is 0, falling back to a positive minimum value.
+	code += "	float scale_sign_x = params.scale.x < 0.0 ? -1.0 : 1.0;\n";
+	code += "	float scale_sign_y = params.scale.y < 0.0 ? -1.0 : 1.0;\n";
+	code += "	float scale_sign_z = params.scale.z < 0.0 ? -1.0 : 1.0;\n";
+	code += "	float scale_minimum = 0.001;\n";
+	code += "	TRANSFORM[0].xyz *= scale_sign_x * max(abs(params.scale.x), scale_minimum);\n";
+	code += "	TRANSFORM[1].xyz *= scale_sign_y * max(abs(params.scale.y), scale_minimum);\n";
+	code += "	TRANSFORM[2].xyz *= scale_sign_z * max(abs(params.scale.z), scale_minimum);\n";
 	code += "\n";
 	code += "	CUSTOM.z = params.animation_offset + lifetime_percent * params.animation_speed;\n\n";
 
diff --git a/scene/theme/default_theme.cpp b/scene/theme/default_theme.cpp
index 5ae1e9baa37d..06fe298668f3 100644
--- a/scene/theme/default_theme.cpp
+++ b/scene/theme/default_theme.cpp
@@ -91,6 +91,8 @@ static Ref<ImageTexture> generate_icon(int p_index) {
 
 	Error err = ImageLoaderSVG::create_image_from_string(img, default_theme_icons_sources[p_index], scale, upsample, HashMap<Color, Color>());
 	ERR_FAIL_COND_V_MSG(err != OK, Ref<ImageTexture>(), "Failed generating icon, unsupported or invalid SVG data in default theme.");
+
+	img->fix_alpha_edges();
 #else
 	// If the SVG module is disabled, we can't really display the UI well, but at least we won't crash.
 	// 16 pixels is used as it's the most common base size for Godot icons.
diff --git a/servers/display_server.cpp b/servers/display_server.cpp
index c9399d9c65f8..87300bd8b6a2 100644
--- a/servers/display_server.cpp
+++ b/servers/display_server.cpp
@@ -1331,10 +1331,14 @@ bool DisplayServer::is_rendering_device_supported() {
 
 	Error err;
 
-#ifdef WINDOWS_ENABLED
-	// On some NVIDIA drivers combining OpenGL and RenderingDevice can result in crash, offload the check to the subprocess.
+#if defined(WINDOWS_ENABLED) || defined(LINUXBSD_ENABLED)
+	// On some drivers combining OpenGL and RenderingDevice can result in crash, offload the check to the subprocess.
 	List<String> arguments;
 	arguments.push_back("--test-rd-support");
+	if (get_singleton()) {
+		arguments.push_back("--display-driver");
+		arguments.push_back(get_singleton()->get_name().to_lower());
+	}
 
 	String pipe;
 	int exitcode = 0;
diff --git a/servers/rendering/renderer_rd/effects/luminance.cpp b/servers/rendering/renderer_rd/effects/luminance.cpp
index 4727e8fd9a9c..ab3ddb67b33b 100644
--- a/servers/rendering/renderer_rd/effects/luminance.cpp
+++ b/servers/rendering/renderer_rd/effects/luminance.cpp
@@ -102,9 +102,10 @@ void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffe
 			tf.usage_bits = RD::TEXTURE_USAGE_COLOR_ATTACHMENT_BIT | RD::TEXTURE_USAGE_SAMPLING_BIT;
 		} else {
 			tf.usage_bits = RD::TEXTURE_USAGE_STORAGE_BIT;
-			if (final) {
-				tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT;
-			}
+		}
+
+		if (final) {
+			tf.usage_bits |= RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_COPY_TO_BIT;
 		}
 
 		RID texture = RD::get_singleton()->texture_create(tf, RD::TextureView());
@@ -112,6 +113,7 @@ void Luminance::LuminanceBuffers::configure(RenderSceneBuffersRD *p_render_buffe
 
 		if (final) {
 			current = RD::get_singleton()->texture_create(tf, RD::TextureView());
+			RD::get_singleton()->texture_clear(current, Color(0.0, 0.0, 0.0), 0u, 1u, 0u, 1u);
 			break;
 		}
 	}
diff --git a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
index 5cea736eeb15..eebc6358ff62 100644
--- a/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
+++ b/servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
@@ -1837,7 +1837,7 @@ RendererCanvasRenderRD::RendererCanvasRenderRD() {
 		actions.base_varying_index = 5;
 
 		actions.global_buffer_array_variable = "global_shader_uniforms.data";
-		actions.instance_uniform_index_variable = "draw_data.instance_uniforms_ofs";
+		actions.instance_uniform_index_variable = "instances.data[instance_index].instance_uniforms_ofs";
 
 		shader.compiler.initialize(actions);
 	}
diff --git a/servers/rendering/renderer_rd/shader_rd.cpp b/servers/rendering/renderer_rd/shader_rd.cpp
index cbdbe151c82f..ed9428a6a351 100644
--- a/servers/rendering/renderer_rd/shader_rd.cpp
+++ b/servers/rendering/renderer_rd/shader_rd.cpp
@@ -555,7 +555,7 @@ void ShaderRD::_compile_version_start(Version *p_version, int p_group) {
 	compile_data.version = p_version;
 	compile_data.group = p_group;
 
-	WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_singleton()->add_template_group_task(this, &ShaderRD::_compile_variant, compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
+	WorkerThreadPool::GroupID group_task = WorkerThreadPool::get_named_pool(SNAME("ShaderCompilationPool"))->add_template_group_task(this, &ShaderRD::_compile_variant, compile_data, group_to_variant_map[p_group].size(), -1, true, SNAME("ShaderCompilation"));
 	p_version->group_compilation_tasks.write[p_group] = group_task;
 }
 
@@ -563,9 +563,8 @@ void ShaderRD::_compile_version_end(Version *p_version, int p_group) {
 	if (p_version->group_compilation_tasks.size() <= p_group || p_version->group_compilation_tasks[p_group] == 0) {
 		return;
 	}
-
 	WorkerThreadPool::GroupID group_task = p_version->group_compilation_tasks[p_group];
-	WorkerThreadPool::get_singleton()->wait_for_group_task_completion(group_task);
+	WorkerThreadPool::get_named_pool(SNAME("ShaderCompilationPool"))->wait_for_group_task_completion(group_task);
 	p_version->group_compilation_tasks.write[p_group] = 0;
 
 	bool all_valid = true;
diff --git a/servers/rendering/renderer_rd/shaders/canvas.glsl b/servers/rendering/renderer_rd/shaders/canvas.glsl
index c1510a11ca08..e2d9086a5de5 100644
--- a/servers/rendering/renderer_rd/shaders/canvas.glsl
+++ b/servers/rendering/renderer_rd/shaders/canvas.glsl
@@ -48,6 +48,8 @@ layout(set = 1, binding = 0, std140) uniform MaterialUniforms {
 /* clang-format on */
 #endif
 
+uint instance_index;
+
 #GLOBALS
 
 #ifdef USE_ATTRIBUTES
@@ -66,9 +68,9 @@ void main() {
 #endif
 
 #ifdef USE_ATTRIBUTES
-	uint instance_index = params.base_instance_index;
+	instance_index = params.base_instance_index;
 #else
-	uint instance_index = gl_InstanceIndex + params.base_instance_index;
+	instance_index = gl_InstanceIndex + params.base_instance_index;
 	instance_index_interp = instance_index;
 #endif // USE_ATTRIBUTES
 	const InstanceData draw_data = instances.data[instance_index];
@@ -240,7 +242,7 @@ void main() {
 #include "canvas_uniforms_inc.glsl"
 
 #ifndef USE_ATTRIBUTES
-layout(location = 4) in flat uint instance_index;
+layout(location = 4) in flat uint instance_index_interp;
 #endif // USE_ATTRIBUTES
 
 layout(location = 0) in vec2 uv_interp;
@@ -287,6 +289,8 @@ vec2 sdf_to_screen_uv(vec2 p_sdf) {
 	return p_sdf * canvas_data.sdf_to_screen;
 }
 
+uint instance_index;
+
 #GLOBALS
 
 #ifdef LIGHT_CODE_USED
@@ -302,6 +306,7 @@ vec4 light_compute(
 		vec2 screen_uv,
 		vec2 uv,
 		vec4 color, bool is_directional) {
+	const InstanceData draw_data = instances.data[instance_index];
 	vec4 light = vec4(0.0);
 	vec3 light_direction = vec3(0.0);
 
@@ -461,10 +466,11 @@ void main() {
 	vec2 vertex = vertex_interp;
 
 #ifdef USE_ATTRIBUTES
-	const InstanceData draw_data = instances.data[params.base_instance_index];
+	instance_index = params.base_instance_index;
 #else
-	const InstanceData draw_data = instances.data[instance_index];
+	instance_index = instance_index_interp;
 #endif // USE_ATTRIBUTES
+	const InstanceData draw_data = instances.data[instance_index];
 
 #if !defined(USE_ATTRIBUTES) && !defined(USE_PRIMITIVE)
 
diff --git a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
index d82ffed646a0..6924f11e346c 100644
--- a/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/particles_storage.cpp
@@ -814,7 +814,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
 		p_particles->particles_material_uniform_set = RD::get_singleton()->uniform_set_create(uniforms, particles_shader.default_shader_rd, 1);
 	}
 
-	double new_phase = Math::fmod((double)p_particles->phase + (p_delta / p_particles->lifetime) * p_particles->speed_scale, 1.0);
+	double new_phase = Math::fmod((double)p_particles->phase + (p_delta / p_particles->lifetime), 1.0);
 
 	//move back history (if there is any)
 	for (uint32_t i = p_particles->frame_history.size() - 1; i > 0; i--) {
@@ -839,7 +839,7 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
 	p_particles->phase = new_phase;
 
 	frame_params.time = RendererCompositorRD::get_singleton()->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;
@@ -1158,6 +1158,10 @@ void ParticlesStorage::_particles_process(Particles *p_particles, double p_delta
 		//fill the trail params
 		for (uint32_t i = 0; i < p_particles->trail_params.size(); i++) {
 			uint32_t src_idx = i * p_particles->frame_history.size() / p_particles->trail_params.size();
+			if (p_particles->speed_scale <= 0.0) {
+				// Stop trails.
+				src_idx = 0;
+			}
 			p_particles->trail_params[i] = p_particles->frame_history[src_idx];
 		}
 	} else {
@@ -1529,7 +1533,6 @@ void ParticlesStorage::update_particles() {
 			}
 		}
 
-		bool zero_time_scale = Engine::get_singleton()->get_time_scale() <= 0.0;
 		double todo = particles->request_process_time;
 		if (particles->clear) {
 			todo += particles->pre_process_time;
@@ -1554,37 +1557,26 @@ void ParticlesStorage::update_particles() {
 			particles->speed_scale = tmp_scale;
 		}
 
+		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 = RendererCompositorRD::get_singleton()->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;
 			}
-			todo = particles->frame_remainder + delta;
+			todo = particles->frame_remainder + delta * time_scale;
 
 			while (todo >= frame_time || particles->clear) {
 				_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, RendererCompositorRD::get_singleton()->get_frame_delta_time());
-			}
+			_particles_process(particles, RendererCompositorRD::get_singleton()->get_frame_delta_time() * time_scale);
 		}
 
 		// Ensure that memory is initialized (the code above should ensure that _particles_process is always called at least once upon clearing).
diff --git a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
index 02b28473951e..6808ebb06376 100644
--- a/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
+++ b/servers/rendering/renderer_rd/storage_rd/texture_storage.cpp
@@ -1275,15 +1275,21 @@ RID TextureStorage::texture_create_from_native_handle(RS::TextureType p_type, Im
 			break;
 
 		case Image::FORMAT_ASTC_4x4:
-		case Image::FORMAT_ASTC_4x4_HDR:
 			format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK;
 			break;
 
+		case Image::FORMAT_ASTC_4x4_HDR:
+			format = RD::DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK;
+			break;
+
 		case Image::FORMAT_ASTC_8x8:
-		case Image::FORMAT_ASTC_8x8_HDR:
 			format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK;
 			break;
 
+		case Image::FORMAT_ASTC_8x8_HDR:
+			format = RD::DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK;
+			break;
+
 		default:
 			// Arbitrary fallback.
 			format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
@@ -2197,24 +2203,16 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
 			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_ZERO;
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_ONE;
 		} break;
-		case Image::FORMAT_ASTC_4x4:
-		case Image::FORMAT_ASTC_4x4_HDR: {
+		case Image::FORMAT_ASTC_4x4: {
 			if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
 				r_format.format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK;
-				if (p_image->get_format() == Image::FORMAT_ASTC_4x4) {
-					r_format.format_srgb = RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK;
-				}
+				r_format.format_srgb = RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK;
 			} else {
 				//not supported, reconvert
 				image->decompress();
-				if (p_image->get_format() == Image::FORMAT_ASTC_4x4) {
-					r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
-					r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
-					image->convert(Image::FORMAT_RGBA8);
-				} else {
-					r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
-					image->convert(Image::FORMAT_RGBAH);
-				}
+				r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+				r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+				image->convert(Image::FORMAT_RGBA8);
 			}
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
 			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
@@ -2222,24 +2220,31 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 
 		} break; // astc 4x4
-		case Image::FORMAT_ASTC_8x8:
-		case Image::FORMAT_ASTC_8x8_HDR: {
+		case Image::FORMAT_ASTC_4x4_HDR: {
+			if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
+				r_format.format = RD::DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK;
+			} else {
+				//not supported, reconvert
+				image->decompress();
+				r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+				image->convert(Image::FORMAT_RGBAH);
+			}
+			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+		} break; // astc 4x4 HDR
+		case Image::FORMAT_ASTC_8x8: {
 			if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
 				r_format.format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK;
-				if (p_image->get_format() == Image::FORMAT_ASTC_8x8) {
-					r_format.format_srgb = RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK;
-				}
+				r_format.format_srgb = RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK;
 			} else {
 				//not supported, reconvert
 				image->decompress();
-				if (p_image->get_format() == Image::FORMAT_ASTC_8x8) {
-					r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
-					r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
-					image->convert(Image::FORMAT_RGBA8);
-				} else {
-					r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
-					image->convert(Image::FORMAT_RGBAH);
-				}
+				r_format.format = RD::DATA_FORMAT_R8G8B8A8_UNORM;
+				r_format.format_srgb = RD::DATA_FORMAT_R8G8B8A8_SRGB;
+				image->convert(Image::FORMAT_RGBA8);
 			}
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
 			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
@@ -2247,6 +2252,21 @@ Ref<Image> TextureStorage::_validate_texture_format(const Ref<Image> &p_image, T
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 
 		} break; // astc 8x8
+		case Image::FORMAT_ASTC_8x8_HDR: {
+			if (RD::get_singleton()->texture_is_format_supported_for_usage(RD::DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK, RD::TEXTURE_USAGE_SAMPLING_BIT | RD::TEXTURE_USAGE_CAN_UPDATE_BIT)) {
+				r_format.format = RD::DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK;
+			} else {
+				//not supported, reconvert
+				image->decompress();
+				r_format.format = RD::DATA_FORMAT_R16G16B16A16_SFLOAT;
+				image->convert(Image::FORMAT_RGBAH);
+			}
+			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
+
+		} break; // astc 8x8 HDR
 
 		default: {
 		}
@@ -2589,7 +2609,7 @@ void TextureStorage::_texture_format_from_rd(RD::DataFormat p_rd_format, Texture
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 		} break;
 		case RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: {
-			r_format.image_format = Image::FORMAT_ASTC_4x4_HDR;
+			r_format.image_format = Image::FORMAT_ASTC_4x4;
 			r_format.rd_format = RD::DATA_FORMAT_ASTC_4x4_UNORM_BLOCK;
 			r_format.rd_format_srgb = RD::DATA_FORMAT_ASTC_4x4_SRGB_BLOCK;
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
@@ -2597,6 +2617,14 @@ void TextureStorage::_texture_format_from_rd(RD::DataFormat p_rd_format, Texture
 			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 
+		} break;
+		case RD::DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK: {
+			r_format.image_format = Image::FORMAT_ASTC_4x4_HDR;
+			r_format.rd_format = RD::DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK;
+			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 		} break; // astc 4x4
 		case RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: {
 			// Q: Do we do as we do below, just create the sRGB variant?
@@ -2608,14 +2636,21 @@ void TextureStorage::_texture_format_from_rd(RD::DataFormat p_rd_format, Texture
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 		} break;
 		case RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: {
-			r_format.image_format = Image::FORMAT_ASTC_8x8_HDR;
+			r_format.image_format = Image::FORMAT_ASTC_8x8;
 			r_format.rd_format = RD::DATA_FORMAT_ASTC_8x8_UNORM_BLOCK;
 			r_format.rd_format_srgb = RD::DATA_FORMAT_ASTC_8x8_SRGB_BLOCK;
 			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
 			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
 			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
 			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
-
+		} break;
+		case RD::DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK: {
+			r_format.image_format = Image::FORMAT_ASTC_8x8_HDR;
+			r_format.rd_format = RD::DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK;
+			r_format.swizzle_r = RD::TEXTURE_SWIZZLE_R;
+			r_format.swizzle_g = RD::TEXTURE_SWIZZLE_G;
+			r_format.swizzle_b = RD::TEXTURE_SWIZZLE_B;
+			r_format.swizzle_a = RD::TEXTURE_SWIZZLE_A;
 		} break; // astc 8x8
 
 		default: {
diff --git a/servers/rendering/renderer_viewport.cpp b/servers/rendering/renderer_viewport.cpp
index b816ca7a8ab2..3bbc05b62545 100644
--- a/servers/rendering/renderer_viewport.cpp
+++ b/servers/rendering/renderer_viewport.cpp
@@ -141,12 +141,22 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
 				scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_OFF;
 			}
 
-			// Verify MetalFX upscaling support.
-			if (
-					(scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL)) ||
-					(scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL))) {
-				scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
-				WARN_PRINT_ONCE("MetalFX upscaling is not supported in the current renderer. Falling back to bilinear 3D resolution scaling.");
+			if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_TEMPORAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_TEMPORAL)) {
+				if (RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL)) {
+					// Prefer MetalFX spatial if it is supported, which will be much more efficient than FSR2,
+					// as the hardware already will struggle with FSR2.
+					scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL;
+					WARN_PRINT_ONCE("MetalFX temporal upscaling is not supported by the current renderer or hardware. Falling back to MetalFX Spatial scaling.");
+				} else {
+					scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR2;
+					WARN_PRINT_ONCE("MetalFX upscaling is not supported by the current renderer or hardware. Falling back to FSR 2 scaling.");
+				}
+				scaling_type = RS::scaling_3d_mode_type(scaling_3d_mode);
+			}
+
+			if (scaling_3d_mode == RS::VIEWPORT_SCALING_3D_MODE_METALFX_SPATIAL && !RD::get_singleton()->has_feature(RD::SUPPORTS_METALFX_SPATIAL)) {
+				scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR;
+				WARN_PRINT_ONCE("MetalFX spatial upscaling is not supported by the current renderer or hardware. Falling back to FSR scaling.");
 			}
 
 			RS::ViewportMSAA msaa_3d = p_viewport->msaa_3d;
@@ -156,11 +166,9 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
 				double min_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MIN_SCALE) / 1000'000.0;
 				double max_scale = (double)RD::get_singleton()->limit_get(RD::LIMIT_METALFX_TEMPORAL_SCALER_MAX_SCALE) / 1000'000.0;
 				if ((double)scaling_3d_scale < min_scale || (double)scaling_3d_scale > max_scale) {
-					scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
-					WARN_PRINT_ONCE(vformat("MetalFX temporal upscaling scale is outside limits; scale must be between %f and %f. Falling back to bilinear 3D resolution scaling.", min_scale, max_scale));
-				}
-
-				if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
+					scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_FSR2;
+					WARN_PRINT_ONCE(vformat("MetalFX temporal upscaling scale is outside limits; scale must be between %f and %f. Falling back to FSR 2 3D resolution scaling.", min_scale, max_scale));
+				} else if (msaa_3d != RS::VIEWPORT_MSAA_DISABLED) {
 					WARN_PRINT_ONCE("MetalFX temporal upscaling does not support 3D MSAA. Disabling 3D MSAA internally.");
 					msaa_3d = RS::VIEWPORT_MSAA_DISABLED;
 				}
@@ -170,7 +178,7 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
 			bool use_taa = p_viewport->use_taa;
 
 			if (scaling_3d_is_not_bilinear && (scaling_3d_scale >= (1.0 + EPSILON))) {
-				// FSR is not designed for downsampling.
+				// FSR and MetalFX is not designed for downsampling.
 				// Fall back to bilinear scaling.
 				WARN_PRINT_ONCE("FSR 3D resolution scaling is not designed for downsampling. Falling back to bilinear 3D resolution scaling.");
 				scaling_3d_mode = RS::VIEWPORT_SCALING_3D_MODE_BILINEAR;
@@ -184,8 +192,8 @@ void RendererViewport::_configure_3d_render_buffers(Viewport *p_viewport) {
 			}
 
 			if (use_taa && (scaling_type == RS::VIEWPORT_SCALING_3D_TYPE_TEMPORAL)) {
-				// FSR2 can't be used with TAA.
-				// Turn it off and prefer using FSR2.
+				// Temporal upscalers can't be used with TAA.
+				// Turn it off and prefer using the temporal upscaler.
 				WARN_PRINT_ONCE("FSR 2 or MetalFX Temporal is not compatible with TAA. Disabling TAA internally.");
 				use_taa = false;
 			}
diff --git a/servers/rendering/rendering_device.cpp b/servers/rendering/rendering_device.cpp
index 9a3e4fd9d7fa..cf6cbf187132 100644
--- a/servers/rendering/rendering_device.cpp
+++ b/servers/rendering/rendering_device.cpp
@@ -7680,6 +7680,20 @@ void RenderingDevice::_bind_methods() {
 	BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_422_UNORM);
 	BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16R16_2PLANE_422_UNORM);
 	BIND_ENUM_CONSTANT(DATA_FORMAT_G16_B16_R16_3PLANE_444_UNORM);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK);
+	BIND_ENUM_CONSTANT(DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK);
 	BIND_ENUM_CONSTANT(DATA_FORMAT_MAX);
 
 #ifndef DISABLE_DEPRECATED
diff --git a/servers/rendering/rendering_device_commons.cpp b/servers/rendering/rendering_device_commons.cpp
index 03fad5493a01..a11ffe57dd15 100644
--- a/servers/rendering/rendering_device_commons.cpp
+++ b/servers/rendering/rendering_device_commons.cpp
@@ -253,6 +253,20 @@ const char *const RenderingDeviceCommons::FORMAT_NAMES[DATA_FORMAT_MAX] = {
 	"G16_B16_R16_3Plane_422_Unorm",
 	"G16_B16R16_2Plane_422_Unorm",
 	"G16_B16_R16_3Plane_444_Unorm",
+	"Astc_4X4_Sfloat_Block",
+	"Astc_5X4_Sfloat_Block",
+	"Astc_5X5_Sfloat_Block",
+	"Astc_6X5_Sfloat_Block",
+	"Astc_6X6_Sfloat_Block",
+	"Astc_8X5_Sfloat_Block",
+	"Astc_8X6_Sfloat_Block",
+	"Astc_8X8_Sfloat_Block",
+	"Astc_10X5_Sfloat_Block",
+	"Astc_10X6_Sfloat_Block",
+	"Astc_10X8_Sfloat_Block",
+	"Astc_10X10_Sfloat_Block",
+	"Astc_12X10_Sfloat_Block",
+	"Astc_12X12_Sfloat_Block",
 };
 
 /*****************/
@@ -477,6 +491,20 @@ uint32_t RenderingDeviceCommons::get_image_format_pixel_size(DataFormat p_format
 		case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK:
+		case DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK:
 			return 1;
 		case DATA_FORMAT_G8B8G8R8_422_UNORM:
 		case DATA_FORMAT_B8G8R8G8_422_UNORM:
@@ -554,42 +582,56 @@ void RenderingDeviceCommons::get_compressed_image_format_block_dimensions(DataFo
 		case DATA_FORMAT_EAC_R11G11_UNORM_BLOCK:
 		case DATA_FORMAT_EAC_R11G11_SNORM_BLOCK:
 		case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc.
-		case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK: {
+		case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK: {
 			r_w = 4;
 			r_h = 4;
 		} break;
 		case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK: // Unsupported
 		case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK:
-		case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK: {
+		case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK: {
 			r_w = 4;
 			r_h = 4;
 		} break;
 		case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK:
-		case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK: {
+		case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK: {
 			r_w = 8;
 			r_h = 8;
 		} break;
 		case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK: // Unsupported
 		case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK:
 			r_w = 4;
 			r_h = 4;
 			return;
@@ -642,32 +684,46 @@ uint32_t RenderingDeviceCommons::get_compressed_image_format_block_byte_size(Dat
 			return 16;
 		case DATA_FORMAT_ASTC_4x4_UNORM_BLOCK: // Again, not sure about astc.
 		case DATA_FORMAT_ASTC_4x4_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_4x4_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_5x4_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_5x4_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_5x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_5x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_6x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_6x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_6x6_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_6x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_8x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_8x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_8x6_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_8x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x5_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x5_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x6_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x6_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x8_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x8_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_10x10_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_10x10_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_12x10_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_12x10_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_UNORM_BLOCK:
 		case DATA_FORMAT_ASTC_12x12_SRGB_BLOCK:
+		case DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK:
 			return 16;
 		default: {
 		}
@@ -691,7 +747,8 @@ uint32_t RenderingDeviceCommons::get_compressed_image_format_pixel_rshift(DataFo
 		case DATA_FORMAT_EAC_R11_SNORM_BLOCK:
 			return 1;
 		case DATA_FORMAT_ASTC_8x8_SRGB_BLOCK:
-		case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK: {
+		case DATA_FORMAT_ASTC_8x8_UNORM_BLOCK:
+		case DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK: {
 			return 2;
 		}
 		default: {
diff --git a/servers/rendering/rendering_device_commons.h b/servers/rendering/rendering_device_commons.h
index aa8beed35785..796e3c2b66c6 100644
--- a/servers/rendering/rendering_device_commons.h
+++ b/servers/rendering/rendering_device_commons.h
@@ -270,6 +270,20 @@ class RenderingDeviceCommons : public Object {
 		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, // HDR variant.
+		DATA_FORMAT_ASTC_5x4_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_5x5_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_6x5_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_6x6_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_8x5_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_8x6_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_8x8_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_10x5_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_10x6_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_10x8_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_10x10_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_12x10_SFLOAT_BLOCK, // HDR variant.
+		DATA_FORMAT_ASTC_12x12_SFLOAT_BLOCK, // HDR variant.
 		DATA_FORMAT_MAX,
 	};
 
diff --git a/servers/rendering/shader_language.cpp b/servers/rendering/shader_language.cpp
index 59adb53b7866..66a43684a52a 100644
--- a/servers/rendering/shader_language.cpp
+++ b/servers/rendering/shader_language.cpp
@@ -1342,7 +1342,7 @@ void ShaderLanguage::_parse_used_identifier(const StringName &p_identifier, Iden
 			break;
 		case IdentifierType::IDENTIFIER_VARYING:
 			if (HAS_WARNING(ShaderWarning::UNUSED_VARYING_FLAG) && used_varyings.has(p_identifier)) {
-				if (shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_VERTEX && shader->varyings[p_identifier].stage != ShaderNode::Varying::STAGE_FRAGMENT) {
+				if (shader->varyings[p_identifier].stage == ShaderNode::Varying::STAGE_UNKNOWN) {
 					used_varyings[p_identifier].used = true;
 				}
 			}
@@ -6510,6 +6510,27 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 						_set_error(RTR("Expected constant expression."));
 						return nullptr;
 					}
+					if (ident_type == IDENTIFIER_FUNCTION) {
+						_set_error(vformat(RTR("Can't use function as identifier: '%s'."), String(identifier)));
+						return nullptr;
+					}
+#ifdef DEBUG_ENABLED
+					if (check_warnings) {
+						StringName func_name;
+						BlockNode *b = p_block;
+
+						while (b) {
+							if (b->parent_function) {
+								func_name = b->parent_function->name;
+								break;
+							} else {
+								b = b->parent_block;
+							}
+						}
+
+						_parse_used_identifier(identifier, ident_type, func_name);
+					}
+#endif // DEBUG_ENABLED
 					if (ident_type == IDENTIFIER_VARYING) {
 						TkPos prev_pos = _get_tkpos();
 						Token next_token = _get_token();
@@ -6542,11 +6563,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 							}
 						}
 					}
-
-					if (ident_type == IDENTIFIER_FUNCTION) {
-						_set_error(vformat(RTR("Can't use function as identifier: '%s'."), String(identifier)));
-						return nullptr;
-					}
 #ifdef DEBUG_ENABLED
 					if (check_position_write && ident_type == IDENTIFIER_BUILTIN_VAR) {
 						if (String(identifier) == "POSITION") {
@@ -6564,7 +6580,7 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 							_set_tkpos(prev_pos);
 						}
 					}
-#endif
+#endif // DEBUG_ENABLED
 					if (is_const) {
 						last_type = IDENTIFIER_CONSTANT;
 					} else {
@@ -6656,23 +6672,6 @@ ShaderLanguage::Node *ShaderLanguage::_parse_expression(BlockNode *p_block, cons
 					varname->is_local = is_local;
 					expr = varname;
 				}
-#ifdef DEBUG_ENABLED
-				if (check_warnings) {
-					StringName func_name;
-					BlockNode *b = p_block;
-
-					while (b) {
-						if (b->parent_function) {
-							func_name = b->parent_function->name;
-							break;
-						} else {
-							b = b->parent_block;
-						}
-					}
-
-					_parse_used_identifier(identifier, ident_type, func_name);
-				}
-#endif // DEBUG_ENABLED
 			}
 		} else if (tk.type == TK_OP_ADD) {
 			continue; //this one does nothing
diff --git a/thirdparty/misc/yuv2rgb.h b/thirdparty/misc/yuv2rgb.h
index d8f7fd6de2d0..e8ccd427d027 100644
--- a/thirdparty/misc/yuv2rgb.h
+++ b/thirdparty/misc/yuv2rgb.h
@@ -30,6 +30,7 @@ ship it.
  * 2. At some point or another the code relied on the byte order of a uint32_t, this has been fixed
  * 3. Output has been reordered to struct { uint8_t r, g, b, a; } precisely in accordance with the function names
  * 4. Removing unused 'dither' parameter
+ * 5. Fix last line not being converted (and therefore was transparent)
  */
 
 #ifndef YUV2RGB_H
@@ -848,7 +849,6 @@ static void yuv422_2_rgb8888(uint8_t  *dst_ptr,
 		      int32_t   uv_span,
 		      int32_t   dst_span)
 {
-    height -= 1;
     while (height > 0)
     {
 	height -= width<<16;
@@ -1020,7 +1020,6 @@ static void yuv444_2_rgb8888(uint8_t  *dst_ptr,
 		      int32_t   uv_span,
 		      int32_t   dst_span)
 {
-    height -= 1;
     while (height > 0)
     {
 	height -= width<<16;
diff --git a/thirdparty/swappy-frame-pacing/common/gamesdk_common.h b/thirdparty/swappy-frame-pacing/common/gamesdk_common.h
index d29ac01af384..25ce813968f7 100644
--- a/thirdparty/swappy-frame-pacing/common/gamesdk_common.h
+++ b/thirdparty/swappy-frame-pacing/common/gamesdk_common.h
@@ -31,11 +31,12 @@
 
 // There are separate versions for each GameSDK component that use this format:
 #define ANDROID_GAMESDK_PACKED_VERSION(MAJOR, MINOR, BUGFIX) \
-    ((MAJOR << 16) | (MINOR << 8) | (BUGFIX))
+  ((MAJOR << 16) | (MINOR << 8) | (BUGFIX))
 // Accessors
 #define ANDROID_GAMESDK_MAJOR_VERSION(PACKED) ((PACKED) >> 16)
 #define ANDROID_GAMESDK_MINOR_VERSION(PACKED) (((PACKED) >> 8) & 0xff)
 #define ANDROID_GAMESDK_BUGFIX_VERSION(PACKED) ((PACKED) & 0xff)
 
-#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX, GIT) \
-#MAJOR "." #MINOR "." #BUGFIX "." #GIT
+#define AGDK_STRINGIFY(NUMBER) #NUMBER
+#define AGDK_STRING_VERSION(MAJOR, MINOR, BUGFIX) \
+  AGDK_STRINGIFY(MAJOR) "." AGDK_STRINGIFY(MINOR) "." AGDK_STRINGIFY(BUGFIX)
diff --git a/thirdparty/swappy-frame-pacing/swappyVk.h b/thirdparty/swappy-frame-pacing/swappyVk.h
index 020683cbc43c..654fcbf4a4a7 100644
--- a/thirdparty/swappy-frame-pacing/swappyVk.h
+++ b/thirdparty/swappy-frame-pacing/swappyVk.h
@@ -281,30 +281,30 @@ void SwappyVk_uninjectTracer(const SwappyTracer* tracer);
  * Usage of this functionality is optional.
  */
 typedef struct SwappyVkFunctionProvider {
-    /**
-     * @brief Callback to initialize the function provider.
-     *
-     * This function is called by Swappy before any functions are requested.
-     * E.g. so you can call dlopen on the Vulkan library.
-     */
-    bool (*init)();
-
-    /**
-     * @brief Callback to get the address of a function.
-     *
-     * This function is called by Swappy to get the address of a Vulkan
-     * function.
-     * @param name The null-terminated name of the function.
-     */
-    void* (*getProcAddr)(const char* name);
-
-    /**
-     * @brief Callback to close any resources owned by the function provider.
-     *
-     * This function is called by Swappy when no more functions will be
-     * requested, e.g. so you can call dlclose on the Vulkan library.
-     */
-    void (*close)();
+  /**
+   * @brief Callback to initialize the function provider.
+   *
+   * This function is called by Swappy before any functions are requested.
+   * E.g. so you can call dlopen on the Vulkan library.
+   */
+  bool (*init)();
+
+  /**
+   * @brief Callback to get the address of a function.
+   *
+   * This function is called by Swappy to get the address of a Vulkan
+   * function.
+   * @param name The null-terminated name of the function.
+   */
+  void* (*getProcAddr)(const char* name);
+
+  /**
+   * @brief Callback to close any resources owned by the function provider.
+   *
+   * This function is called by Swappy when no more functions will be
+   * requested, e.g. so you can call dlclose on the Vulkan library.
+   */
+  void (*close)();
 } SwappyVkFunctionProvider;
 
 /**
@@ -384,7 +384,8 @@ void SwappyVk_enableStats(VkSwapchainKHR swapchain, bool enabled);
 
  * @see SwappyVk_enableStats.
  */
-void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, uint32_t image);
+void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain,
+                               uint32_t image);
 
 /**
  * @brief Returns the stats collected, if statistics collection was toggled on.
@@ -396,11 +397,11 @@ void SwappyVk_recordFrameStart(VkQueue queue, VkSwapchainKHR swapchain, uint32_t
  * conditions.
  *
  * @param[in]  swapchain   - The swapchain for which stats are being queried.
- * @param      swappyStats - Pointer to a SwappyStats that will be populated with
- *                             the collected stats. Cannot be NULL.
+ * @param      swappyStats - Pointer to a SwappyStats that will be populated
+ * with the collected stats. Cannot be NULL.
  * @see SwappyStats
  */
-void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats *swappyStats);
+void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats* swappyStats);
 
 /**
  * @brief Clears the frame statistics collected so far.
@@ -413,6 +414,58 @@ void SwappyVk_getStats(VkSwapchainKHR swapchain, SwappyStats *swappyStats);
  */
 void SwappyVk_clearStats(VkSwapchainKHR swapchain);
 
+/**
+ * @brief Reset the swappy pacing mechanism
+ *
+ * In cases where the frame timing history is irrelevant (for example during
+ * scene/level transitions or after loading screens), calling this would
+ * remove all the history for frame pacing. Calling this entry point
+ * would reset the frame rate to the initial state at the end of the current
+ * frame. Then swappy would just pace as normal with fresh state from next
+ * frame. There are no error conditions associated with this call.
+ *
+ * @param[in]  swapchain   - The swapchain for which frame pacing is reset.
+ */
+void SwappyVk_resetFramePacing(VkSwapchainKHR swapchain);
+
+/**
+ * @brief Enable/Disable the swappy pacing mechanism
+ *
+ * By default frame pacing is enabled when swappy is used, however it can be
+ * disabled at runtime by calling `SwappyVk_enableFramePacing(swapchain,
+ * false)`. When the frame pacing is disabled, ::SwappyVk_enableBlockingWait
+ * will control whether
+ * ::SwappyVk_queuePresent will return immediately, or will block until the
+ * previous frame's GPU work is completed. All enabling/disabling effects take
+ * place from next frame. Once disabled, `SwappyVk_enableFramePacing(swapchain,
+ * true)` will enable frame pacing again with a fresh frame time state -
+ * equivalent of calling ::SwappyVk_resetFramePacing().
+ *
+ * @param[in]  swapchain   - The swapchain for which stats are being queried.
+ * @param      enable      - If true, enables frame pacing otherwise disables it
+ */
+void SwappyVk_enableFramePacing(VkSwapchainKHR swapchain, bool enable);
+
+/**
+ * @brief Enable/Disable blocking wait when frame-pacing is disabled
+ *
+ * By default ::SwappyVk_queuePresent will do a blocking wait until previous
+ * frame's GPU work is completed. However when frame pacing is disabled, calling
+ * `SwappyVk_enableBlockingWait(swapchain, false)` will ensure that
+ * ::SwappyVk_queuePresent returns without waiting for previous frame's GPU
+ * work. This behaviour impacts the GPU time returned in the
+ * ::SwappyPostWaitCallback callback. If the last frame's GPU work is done by
+ * the time ::SwappyVk_queuePresent for this frame is called, then the previous
+ * frame's GPU time will be returned otherwise `-1` will be delivered in the
+ * callback for the frame. This setting has no impact when frame pacing is
+ * enabled.
+ *
+ * @param[in]  swapchain   - The swapchain for which stats are being queried.
+ * @param      enable      - If true, ::SwappyVk_queuePresent will block until
+ * the previous frame's GPU work is complete.
+ */
+void SwappyVk_enableBlockingWait(VkSwapchainKHR swapchain, bool enable);
+
 #ifdef __cplusplus
 }  // extern "C"
 #endif
diff --git a/thirdparty/swappy-frame-pacing/swappy_common.h b/thirdparty/swappy-frame-pacing/swappy_common.h
index b711ca910fb3..acceb4243df6 100644
--- a/thirdparty/swappy-frame-pacing/swappy_common.h
+++ b/thirdparty/swappy-frame-pacing/swappy_common.h
@@ -48,22 +48,22 @@
 
 // Internal macros to track Swappy version, do not use directly.
 #define SWAPPY_MAJOR_VERSION 2
-#define SWAPPY_MINOR_VERSION 0
+#define SWAPPY_MINOR_VERSION 2
 #define SWAPPY_BUGFIX_VERSION 0
-#define SWAPPY_PACKED_VERSION                                                  \
-    ANDROID_GAMESDK_PACKED_VERSION(SWAPPY_MAJOR_VERSION, SWAPPY_MINOR_VERSION, \
-                                   SWAPPY_BUGFIX_VERSION)
+#define SWAPPY_PACKED_VERSION                                                \
+  ANDROID_GAMESDK_PACKED_VERSION(SWAPPY_MAJOR_VERSION, SWAPPY_MINOR_VERSION, \
+                                 SWAPPY_BUGFIX_VERSION)
 
 // Internal macros to generate a symbol to track Swappy version, do not use
 // directly.
 #define SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) \
-    PREFIX##_##MAJOR##_##MINOR##_##BUGFIX##_##GITCOMMIT
+  PREFIX##_##MAJOR##_##MINOR##_##BUGFIX##_##GITCOMMIT
 #define SWAPPY_VERSION_CONCAT(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT) \
-    SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT)
-#define SWAPPY_VERSION_SYMBOL                                          \
-    SWAPPY_VERSION_CONCAT(Swappy_version, SWAPPY_MAJOR_VERSION,        \
-                          SWAPPY_MINOR_VERSION, SWAPPY_BUGFIX_VERSION, \
-                          AGDK_GIT_COMMIT)
+  SWAPPY_VERSION_CONCAT_NX(PREFIX, MAJOR, MINOR, BUGFIX, GITCOMMIT)
+#define SWAPPY_VERSION_SYMBOL                                        \
+  SWAPPY_VERSION_CONCAT(Swappy_version, SWAPPY_MAJOR_VERSION,        \
+                        SWAPPY_MINOR_VERSION, SWAPPY_BUGFIX_VERSION, \
+                        AGDK_GIT_COMMIT)
 
 // Define this to 1 to enable all logging from Swappy, by default it is
 // disabled in a release build and enabled in a debug build.
@@ -83,29 +83,29 @@ typedef uint64_t SwappyThreadId;
  * Usage of this functionality is optional.
  */
 typedef struct SwappyThreadFunctions {
-    /** @brief Thread start callback.
-     *
-     * This function is called by Swappy to start thread_func on a new thread.
-     * @param user_data A value to be passed the thread function.
-     * If the thread was started, this function should set the thread_id and
-     * return 0. If the thread was not started, this function should return a
-     * non-zero value.
-     */
-    int (*start)(SwappyThreadId* thread_id, void* (*thread_func)(void*),
-                 void* user_data);
-
-    /** @brief Thread join callback.
-     *
-     * This function is called by Swappy to join the thread with given id.
-     */
-    void (*join)(SwappyThreadId thread_id);
-
-    /** @brief Thread joinable callback.
-     *
-     * This function is called by Swappy to discover whether the thread with the
-     * given id is joinable.
-     */
-    bool (*joinable)(SwappyThreadId thread_id);
+  /** @brief Thread start callback.
+   *
+   * This function is called by Swappy to start thread_func on a new thread.
+   * @param user_data A value to be passed the thread function.
+   * If the thread was started, this function should set the thread_id and
+   * return 0. If the thread was not started, this function should return a
+   * non-zero value.
+   */
+  int (*start)(SwappyThreadId* thread_id, void* (*thread_func)(void*),
+               void* user_data);
+
+  /** @brief Thread join callback.
+   *
+   * This function is called by Swappy to join the thread with given id.
+   */
+  void (*join)(SwappyThreadId thread_id);
+
+  /** @brief Thread joinable callback.
+   *
+   * This function is called by Swappy to discover whether the thread with the
+   * given id is joinable.
+   */
+  bool (*joinable)(SwappyThreadId thread_id);
 } SwappyThreadFunctions;
 
 #ifdef __cplusplus
@@ -138,47 +138,46 @@ const char* Swappy_versionString();
  * ::SwappyGL_enableStats or ::SwappyVk_enableStats.
  */
 typedef struct SwappyStats {
-    /** @brief Total frames swapped by swappy */
-    uint64_t totalFrames;
-
-    /** @brief Histogram of the number of screen refreshes a frame waited in the
-     * compositor queue after rendering was completed.
-     *
-     * For example:
-     *     if a frame waited 2 refresh periods in the compositor queue after
-     * rendering was done, the frame will be counted in idleFrames[2]
-     */
-    uint64_t idleFrames[MAX_FRAME_BUCKETS];
-
-    /** @brief Histogram of the number of screen refreshes passed between the
-     * requested presentation time and the actual present time.
-     *
-     * For example:
-     *     if a frame was presented 2 refresh periods after the requested
-     * timestamp swappy set, the frame will be counted in lateFrames[2]
-     */
-    uint64_t lateFrames[MAX_FRAME_BUCKETS];
-
-    /** @brief Histogram of the number of screen refreshes passed between two
-     * consecutive frames
-     *
-     * For example:
-     *     if frame N was presented 2 refresh periods after frame N-1
-     *     frame N will be counted in offsetFromPreviousFrame[2]
-     */
-    uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS];
-
-    /** @brief Histogram of the number of screen refreshes passed between the
-     * call to Swappy_recordFrameStart and the actual present time.
-     *
-     * For example:
-     *     if a frame was presented 2 refresh periods after the call to
-     * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2]
-     */
-    uint64_t latencyFrames[MAX_FRAME_BUCKETS];
+  /** @brief Total frames swapped by swappy */
+  uint64_t totalFrames;
+
+  /** @brief Histogram of the number of screen refreshes a frame waited in the
+   * compositor queue after rendering was completed.
+   *
+   * For example:
+   *     if a frame waited 2 refresh periods in the compositor queue after
+   * rendering was done, the frame will be counted in idleFrames[2]
+   */
+  uint64_t idleFrames[MAX_FRAME_BUCKETS];
+
+  /** @brief Histogram of the number of screen refreshes passed between the
+   * requested presentation time and the actual present time.
+   *
+   * For example:
+   *     if a frame was presented 2 refresh periods after the requested
+   * timestamp swappy set, the frame will be counted in lateFrames[2]
+   */
+  uint64_t lateFrames[MAX_FRAME_BUCKETS];
+
+  /** @brief Histogram of the number of screen refreshes passed between two
+   * consecutive frames
+   *
+   * For example:
+   *     if frame N was presented 2 refresh periods after frame N-1
+   *     frame N will be counted in offsetFromPreviousFrame[2]
+   */
+  uint64_t offsetFromPreviousFrame[MAX_FRAME_BUCKETS];
+
+  /** @brief Histogram of the number of screen refreshes passed between the
+   * call to Swappy_recordFrameStart and the actual present time.
+   *
+   * For example:
+   *     if a frame was presented 2 refresh periods after the call to
+   * `Swappy_recordFrameStart` the frame will be counted in latencyFrames[2]
+   */
+  uint64_t latencyFrames[MAX_FRAME_BUCKETS];
 } SwappyStats;
 
-
 #ifdef __cplusplus
 }  // extern "C"
 #endif
@@ -236,43 +235,43 @@ typedef void (*SwappySwapIntervalChangedCallback)(void*);
  * Injection of these is optional.
  */
 typedef struct SwappyTracer {
-    /**
-     * Callback called before waiting to queue the frame to the composer.
-     */
-    SwappyPreWaitCallback preWait;
-
-    /**
-     * Callback called after wait to queue the frame to the composer is done.
-     */
-    SwappyPostWaitCallback postWait;
-
-    /**
-     * Callback called before calling the function to queue the frame to the
-     * composer.
-     */
-    SwappyPreSwapBuffersCallback preSwapBuffers;
-
-    /**
-     * Callback called after calling the function to queue the frame to the
-     * composer.
-     */
-    SwappyPostSwapBuffersCallback postSwapBuffers;
-
-    /**
-     * Callback called at the start of a frame.
-     */
-    SwappyStartFrameCallback startFrame;
-
-    /**
-     * Pointer to some arbitrary data that will be passed as the first argument
-     * of callbacks.
-     */
-    void* userData;
-
-    /**
-     * Callback called when the swap interval was changed.
-     */
-    SwappySwapIntervalChangedCallback swapIntervalChanged;
+  /**
+   * Callback called before waiting to queue the frame to the composer.
+   */
+  SwappyPreWaitCallback preWait;
+
+  /**
+   * Callback called after wait to queue the frame to the composer is done.
+   */
+  SwappyPostWaitCallback postWait;
+
+  /**
+   * Callback called before calling the function to queue the frame to the
+   * composer.
+   */
+  SwappyPreSwapBuffersCallback preSwapBuffers;
+
+  /**
+   * Callback called after calling the function to queue the frame to the
+   * composer.
+   */
+  SwappyPostSwapBuffersCallback postSwapBuffers;
+
+  /**
+   * Callback called at the start of a frame.
+   */
+  SwappyStartFrameCallback startFrame;
+
+  /**
+   * Pointer to some arbitrary data that will be passed as the first argument
+   * of callbacks.
+   */
+  void* userData;
+
+  /**
+   * Callback called when the swap interval was changed.
+   */
+  SwappySwapIntervalChangedCallback swapIntervalChanged;
 } SwappyTracer;
 
 /** @} */


Test patch:
diff --git a/modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd b/modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd
index abeadbe5eedd..6b4f1b6e8350 100644
--- a/modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd
+++ b/modules/gdscript/tests/scripts/completion/argument_options/play_inferred.gd
@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim := $AnimationPlayer
 
 func test():
diff --git a/modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd b/modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd
index d11f81e98537..4b8b001528e9 100644
--- a/modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd
+++ b/modules/gdscript/tests/scripts/completion/argument_options/play_typed.gd
@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim: AnimationPlayer = $AnimationPlayer
 
 func test():
diff --git a/modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd b/modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd
index 4ddfd21ac60f..fb3fe915fd41 100644
--- a/modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd
+++ b/modules/gdscript/tests/scripts/completion/argument_options/play_untyped.gd
@@ -1,3 +1,5 @@
+extends Node
+
 @onready var anim = $AnimationPlayer
 
 func test():
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
index dc7cc9955410..5586317938ee 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/dollar.gd
@@ -1,5 +1,5 @@
 extends Node
 
 func a():
-    %AnimationPlayer.➡
+    $UniqueAnimationPlayer.➡
     pass
diff --git a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
index 5586317938ee..dc7cc9955410 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
+++ b/modules/gdscript/tests/scripts/completion/get_node/literal/percent.gd
@@ -1,5 +1,5 @@
 extends Node
 
 func a():
-    $UniqueAnimationPlayer.➡
+    %AnimationPlayer.➡
     pass
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd b/modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered/local_infered.gd
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred/local_inferred.gd
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/class_local_infered_scene.gd
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/class_local_inferred_scene.gd
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/local_infered_scene/native_local_infered_scene.gd
rename to modules/gdscript/tests/scripts/completion/get_node/local_inferred_scene/native_local_inferred_scene.gd
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd b/modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered/member_infered.gd
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred/member_inferred.gd
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.gd
similarity index 66%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/class_member_inferred_scene.gd
index 402fd1d275de..877ac442a91f 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/class_member_infered_scene.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg b/modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.cfg
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.cfg
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.gd
similarity index 55%
rename from modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd
rename to modules/gdscript/tests/scripts/completion/get_node/member_inferred_scene/native_member_inferred_scene.gd
index 97b288334ed4..0b511e4c72c9 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_infered_scene/native_member_infered_scene.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd
index 6188a6e843c0..0dad242872f0 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/class_member_scene.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd
index 80b49439532c..43e6c3bc78f0 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_scene/native_member_scene.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd
index 7f2cb4e8cc73..f4a0c6d7cec9 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene/native_member_typehint_scene.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd
index aac450be9f2b..5df4c0ec4c9d 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/class_member_typehint_scene_broad.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd
index 9eb10e49339f..991449d728a5 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_broad/native_member_typehint_scene_broad.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd
index ff8214c46329..a3cdece9561a 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/class_member_typehint_scene_incompatible.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd b/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd
index 30cd7d6a21a3..b0c99d4e16a6 100644
--- a/modules/gdscript/tests/scripts/completion/get_node/member_typehint_scene_incompatible/native_member_typehint_scene_incompatible.gd
+++ b/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.➡
diff --git a/modules/gdscript/tests/scripts/completion/types/local/infered.cfg b/modules/gdscript/tests/scripts/completion/types/local/inferred.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/types/local/infered.cfg
rename to modules/gdscript/tests/scripts/completion/types/local/inferred.cfg
diff --git a/modules/gdscript/tests/scripts/completion/types/local/infered.gd b/modules/gdscript/tests/scripts/completion/types/local/inferred.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/types/local/infered.gd
rename to modules/gdscript/tests/scripts/completion/types/local/inferred.gd
diff --git a/modules/gdscript/tests/scripts/completion/types/member/infered.cfg b/modules/gdscript/tests/scripts/completion/types/member/inferred.cfg
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/types/member/infered.cfg
rename to modules/gdscript/tests/scripts/completion/types/member/inferred.cfg
diff --git a/modules/gdscript/tests/scripts/completion/types/member/infered.gd b/modules/gdscript/tests/scripts/completion/types/member/inferred.gd
similarity index 100%
rename from modules/gdscript/tests/scripts/completion/types/member/infered.gd
rename to modules/gdscript/tests/scripts/completion/types/member/inferred.gd
diff --git a/modules/gdscript/tests/test_completion.h b/modules/gdscript/tests/test_completion.h
index 844c2675e3ea..95201190131d 100644
--- a/modules/gdscript/tests/test_completion.h
+++ b/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) {
diff --git a/tests/core/templates/test_a_hash_map.h b/tests/core/templates/test_a_hash_map.h
index 4fa6d3bca41c..6e60addc6046 100644
--- a/tests/core/templates/test_a_hash_map.h
+++ b/tests/core/templates/test_a_hash_map.h
@@ -235,9 +235,13 @@ TEST_CASE("[AHashMap] Insert, iterate and remove many elements") {
 TEST_CASE("[AHashMap] Insert, iterate and remove many strings") {
 	const int elem_max = 432;
 	AHashMap<String, String> map;
+
+	// To not print WARNING: Excessive collision count (NN), is the right hash function being used?
+	ERR_PRINT_OFF;
 	for (int i = 0; i < elem_max; i++) {
 		map.insert(itos(i), itos(i));
 	}
+	ERR_PRINT_ON;
 
 	//insert order should have been kept
 	int idx = 0;
diff --git a/tests/scene/test_code_edit.h b/tests/scene/test_code_edit.h
index 1c85d3e41c36..de55fc709603 100644
--- a/tests/scene/test_code_edit.h
+++ b/tests/scene/test_code_edit.h
@@ -4492,7 +4492,8 @@ TEST_CASE("[SceneTree][CodeEdit] symbol lookup") {
 
 		Point2 caret_pos = code_edit->get_caret_draw_pos();
 		caret_pos.x += 60;
-		SEND_GUI_MOUSE_BUTTON_EVENT(caret_pos, MouseButton::NONE, 0, Key::NONE);
+
+		SEND_GUI_MOUSE_MOTION_EVENT(caret_pos, MouseButtonMask::NONE, Key::NONE);
 		CHECK(code_edit->get_text_for_symbol_lookup() == "this is s" + String::chr(0xFFFF) + "ome text");
 
 		SIGNAL_WATCH(code_edit, "symbol_validate");
diff --git a/tests/scene/test_text_edit.h b/tests/scene/test_text_edit.h
index d7192def6212..aa7a9bb23c40 100644
--- a/tests/scene/test_text_edit.h
+++ b/tests/scene/test_text_edit.h
@@ -8014,6 +8014,8 @@ TEST_CASE("[SceneTree][TextEdit] gutters") {
 	SIGNAL_WATCH(text_edit, "gutter_removed");
 
 	SUBCASE("[TextEdit] gutter add and remove") {
+		text_edit->set_text("test1\ntest2\ntest3\ntest4");
+
 		text_edit->add_gutter();
 		CHECK(text_edit->get_gutter_count() == 1);
 		CHECK(text_edit->get_gutter_width(0) == 24);
diff --git a/tests/test_main.cpp b/tests/test_main.cpp
index c12e92708177..2d8f2c769155 100644
--- a/tests/test_main.cpp
+++ b/tests/test_main.cpp
@@ -306,7 +306,7 @@ struct GodotTestCaseListener : public doctest::IReporter {
 			// So we have to do this for each test case. Also make sure there is
 			// no residual theme from something else.
 			ThemeDB::get_singleton()->finalize_theme();
-			ThemeDB::get_singleton()->initialize_theme_noproject();
+			ThemeDB::get_singleton()->initialize_theme();
 
 #ifndef _3D_DISABLED
 			physics_server_3d = PhysicsServer3DManager::get_singleton()->new_default_server();