Browse Source

Merge pull request #90993 from darksylinc/matias-TheForge

Add debug utilities for Vulkan
Rémi Verschelde 1 day ago
parent
commit
568589c9d8
32 changed files with 1321 additions and 108 deletions
  1. 33 0
      core/os/memory.cpp
  2. 24 0
      core/os/memory.h
  3. 124 1
      doc/classes/RenderingDevice.xml
  4. 9 0
      drivers/d3d12/rendering_device_driver_d3d12.cpp
  5. 6 0
      drivers/d3d12/rendering_device_driver_d3d12.h
  6. 5 0
      drivers/metal/rendering_device_driver_metal.h
  7. 10 0
      drivers/metal/rendering_device_driver_metal.mm
  8. 326 7
      drivers/vulkan/rendering_context_driver_vulkan.cpp
  9. 50 0
      drivers/vulkan/rendering_context_driver_vulkan.h
  10. 432 67
      drivers/vulkan/rendering_device_driver_vulkan.cpp
  11. 31 2
      drivers/vulkan/rendering_device_driver_vulkan.h
  12. 12 0
      misc/extension_api_validation/4.3-stable.expected
  13. 1 0
      misc/scripts/validate_extension_api.sh
  14. 3 2
      modules/lightmapper_rd/lightmapper_rd.cpp
  15. 1 1
      platform/android/rendering_context_driver_vulkan_android.cpp
  16. 1 1
      platform/ios/rendering_context_driver_vulkan_ios.mm
  17. 1 1
      platform/linuxbsd/wayland/rendering_context_driver_vulkan_wayland.cpp
  18. 1 1
      platform/linuxbsd/x11/rendering_context_driver_vulkan_x11.cpp
  19. 1 1
      platform/macos/rendering_context_driver_vulkan_macos.mm
  20. 1 1
      platform/windows/rendering_context_driver_vulkan_windows.cpp
  21. 1 1
      servers/rendering/renderer_rd/environment/sky.cpp
  22. 6 7
      servers/rendering/renderer_rd/forward_mobile/render_forward_mobile.cpp
  23. 2 1
      servers/rendering/renderer_rd/renderer_canvas_render_rd.cpp
  24. 40 0
      servers/rendering/rendering_context_driver.cpp
  25. 13 0
      servers/rendering/rendering_context_driver.h
  26. 10 1
      servers/rendering/rendering_device.compat.inc
  27. 91 6
      servers/rendering/rendering_device.cpp
  28. 27 2
      servers/rendering/rendering_device.h
  29. 38 0
      servers/rendering/rendering_device_commons.h
  30. 6 0
      servers/rendering/rendering_device_driver.h
  31. 9 2
      servers/rendering/rendering_device_graph.cpp
  32. 6 3
      servers/rendering/rendering_device_graph.h

+ 33 - 0
core/os/memory.cpp

@@ -35,6 +35,7 @@
 
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
 
 void *operator new(size_t p_size, const char *p_description) {
 	return Memory::alloc_static(p_size, false);
@@ -65,6 +66,38 @@ SafeNumeric<uint64_t> Memory::max_usage;
 
 SafeNumeric<uint64_t> Memory::alloc_count;
 
+inline bool is_power_of_2(size_t x) { return x && ((x & (x - 1U)) == 0U); }
+
+void *Memory::alloc_aligned_static(size_t p_bytes, size_t p_alignment) {
+	DEV_ASSERT(is_power_of_2(p_alignment));
+
+	void *p1, *p2;
+	if ((p1 = (void *)malloc(p_bytes + p_alignment - 1 + sizeof(uint32_t))) == nullptr) {
+		return nullptr;
+	}
+
+	p2 = (void *)(((uintptr_t)p1 + sizeof(uint32_t) + p_alignment - 1) & ~((p_alignment)-1));
+	*((uint32_t *)p2 - 1) = (uint32_t)((uintptr_t)p2 - (uintptr_t)p1);
+	return p2;
+}
+
+void *Memory::realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment) {
+	if (p_memory == nullptr) {
+		return alloc_aligned_static(p_bytes, p_alignment);
+	}
+
+	void *ret = alloc_aligned_static(p_bytes, p_alignment);
+	memcpy(ret, p_memory, p_prev_bytes);
+	free_aligned_static(p_memory);
+	return ret;
+}
+
+void Memory::free_aligned_static(void *p_memory) {
+	uint32_t offset = *((uint32_t *)p_memory - 1);
+	void *p = (void *)((uint8_t *)p_memory - offset);
+	free(p);
+}
+
 void *Memory::alloc_static(size_t p_bytes, bool p_pad_align) {
 #ifdef DEBUG_ENABLED
 	bool prepad = true;

+ 24 - 0
core/os/memory.h

@@ -62,6 +62,30 @@ public:
 	static void *realloc_static(void *p_memory, size_t p_bytes, bool p_pad_align = false);
 	static void free_static(void *p_ptr, bool p_pad_align = false);
 
+	//	                            ↓ return value of alloc_aligned_static
+	//	┌─────────────────┬─────────┬─────────┬──────────────────┐
+	//	│ padding (up to  │ uint32_t│ void*   │ padding (up to   │
+	//	│ p_alignment - 1)│ offset  │ p_bytes │ p_alignment - 1) │
+	//	└─────────────────┴─────────┴─────────┴──────────────────┘
+	//
+	// alloc_aligned_static will allocate p_bytes + p_alignment - 1 + sizeof(uint32_t) and
+	// then offset the pointer until alignment is satisfied.
+	//
+	// This offset is stored before the start of the returned ptr so we can retrieve the original/real
+	// start of the ptr in order to free it.
+	//
+	// The rest is wasted as padding in the beginning and end of the ptr. The sum of padding at
+	// both start and end of the block must add exactly to p_alignment - 1.
+	//
+	// p_alignment MUST be a power of 2.
+	static void *alloc_aligned_static(size_t p_bytes, size_t p_alignment);
+	static void *realloc_aligned_static(void *p_memory, size_t p_bytes, size_t p_prev_bytes, size_t p_alignment);
+	// Pass the ptr returned by alloc_aligned_static to free it.
+	// e.g.
+	//	void *data = realloc_aligned_static( bytes, 16 );
+	//  free_aligned_static( data );
+	static void free_aligned_static(void *p_memory);
+
 	static uint64_t get_mem_available();
 	static uint64_t get_mem_usage();
 	static uint64_t get_mem_max_usage();

+ 124 - 1
doc/classes/RenderingDevice.xml

@@ -218,6 +218,7 @@
 			<param index="6" name="clear_depth" type="float" default="1.0" />
 			<param index="7" name="clear_stencil" type="int" default="0" />
 			<param index="8" name="region" type="Rect2" default="Rect2(0, 0, 0, 0)" />
+			<param index="9" name="breadcrumb" type="int" default="0" />
 			<description>
 				Starts a list of raster drawing commands created with the [code]draw_*[/code] methods. The returned value should be passed to other [code]draw_list_*[/code] functions.
 				Multiple draw lists cannot be created at the same time; you must finish the previous draw list first using [method draw_list_end].
@@ -225,7 +226,7 @@
 				[codeblock]
 				var rd = RenderingDevice.new()
 				var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)])
-				var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors)
+				var draw_list = rd.draw_list_begin(framebuffers[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS)
 
 				# Draw opaque.
 				rd.draw_list_bind_render_pipeline(draw_list, raster_pipeline)
@@ -240,6 +241,11 @@
 
 				rd.draw_list_end()
 				[/codeblock]
+				The [param breadcrumb] parameter can be an arbitrary 32-bit integer that is useful to diagnose GPU crashes. If Godot is built in dev or debug mode; when the GPU crashes Godot will dump all shaders that were being executed at the time of the crash and the breadcrumb is useful to diagnose what passes did those shaders belong to.
+				It does not affect rendering behavior and can be set to 0. It is recommended to use [enum BreadcrumbMarker] enumerations for consistency but it's not required. It is also possible to use bitwise operations to add extra data. e.g.
+				[codeblock]
+				rd.draw_list_begin(fb[i], RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_READ, RenderingDevice.INITIAL_ACTION_CLEAR, RenderingDevice.FINAL_ACTION_DISCARD, clear_colors, RenderingDevice.OPAQUE_PASS | 5)
+				[/codeblock]
 			</description>
 		</method>
 		<method name="draw_list_begin_for_screen">
@@ -487,6 +493,31 @@
 				Returns the index of the last frame rendered that has rendering timestamps available for querying.
 			</description>
 		</method>
+		<method name="get_device_allocation_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns how many allocations the GPU has performed for internal driver structures.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
+		<method name="get_device_allocs_by_object_type" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="type" type="int" />
+			<description>
+				Same as [method get_device_allocation_count] but filtered for a given object type.
+				The type argument must be in range [code][0; get_tracked_object_type_count - 1][/code]. If [method get_tracked_object_type_count] is 0, then type argument is ignored and always returns 0.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
+		<method name="get_device_memory_by_object_type" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="type" type="int" />
+			<description>
+				Same as [method get_device_total_memory] but filtered for a given object type.
+				The type argument must be in range [code][0; get_tracked_object_type_count - 1][/code]. If [method get_tracked_object_type_count] is 0, then type argument is ignored and always returns 0.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
 		<method name="get_device_name" qualifiers="const">
 			<return type="String" />
 			<description>
@@ -499,12 +530,44 @@
 				Returns the universally unique identifier for the pipeline cache. This is used to cache shader files on disk, which avoids shader recompilations on subsequent engine runs. This UUID varies depending on the graphics card model, but also the driver version. Therefore, updating graphics drivers will invalidate the shader cache.
 			</description>
 		</method>
+		<method name="get_device_total_memory" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns how much bytes the GPU is using.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
 		<method name="get_device_vendor_name" qualifiers="const">
 			<return type="String" />
 			<description>
 				Returns the vendor of the video adapter (e.g. "NVIDIA Corporation"). Equivalent to [method RenderingServer.get_video_adapter_vendor]. See also [method get_device_name].
 			</description>
 		</method>
+		<method name="get_driver_allocation_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns how many allocations the GPU driver has performed for internal driver structures.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
+		<method name="get_driver_allocs_by_object_type" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="type" type="int" />
+			<description>
+				Same as [method get_driver_allocation_count] but filtered for a given object type.
+				The type argument must be in range [code][0; get_tracked_object_type_count - 1][/code]. If [method get_tracked_object_type_count] is 0, then type argument is ignored and always returns 0.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
+		<method name="get_driver_memory_by_object_type" qualifiers="const">
+			<return type="int" />
+			<param index="0" name="type" type="int" />
+			<description>
+				Same as [method get_driver_total_memory] but filtered for a given object type.
+				The type argument must be in range [code][0; get_tracked_object_type_count - 1][/code]. If [method get_tracked_object_type_count] is 0, then type argument is ignored and always returns 0.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
 		<method name="get_driver_resource">
 			<return type="int" />
 			<param index="0" name="resource" type="int" enum="RenderingDevice.DriverResource" />
@@ -514,6 +577,13 @@
 				Returns the unique identifier of the driver [param resource] for the specified [param rid]. Some driver resource types ignore the specified [param rid] (see [enum DriverResource] descriptions). [param index] is always ignored but must be specified anyway.
 			</description>
 		</method>
+		<method name="get_driver_total_memory" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns how much bytes the GPU driver is using for internal driver structures.
+				This is only used by Vulkan in Debug builds and can return 0 when this information is not tracked or unknown.
+			</description>
+		</method>
 		<method name="get_frame_delay" qualifiers="const">
 			<return type="int" />
 			<description>
@@ -527,6 +597,33 @@
 				Returns the memory usage in bytes corresponding to the given [param type]. When using Vulkan, these statistics are calculated by [url=https://github.com/GPUOpen-LibrariesAndSDKs/VulkanMemoryAllocator]Vulkan Memory Allocator[/url].
 			</description>
 		</method>
+		<method name="get_perf_report" qualifiers="const">
+			<return type="String" />
+			<description>
+				Returns a string with a performance report from the past frame. Updates every frame.
+			</description>
+		</method>
+		<method name="get_tracked_object_name" qualifiers="const">
+			<return type="String" />
+			<param index="0" name="type_index" type="int" />
+			<description>
+				Returns the name of the type of object for the given [param type_index]. This value must be in range [code][0; get_tracked_object_type_count - 1][/code]. If [method get_tracked_object_type_count] is 0, then type argument is ignored and always returns the same string.
+				The return value is important because it gives meaning to the types passed to [method get_driver_memory_by_object_type], [method get_driver_allocs_by_object_type], [method get_device_memory_by_object_type], and [method get_device_allocs_by_object_type]. Examples of strings it can return (not exhaustive):
+				- DEVICE_MEMORY
+				- PIPELINE_CACHE
+				- SWAPCHAIN_KHR
+				- COMMAND_POOL
+				Thus if e.g. [code]get_tracked_object_name(5)[/code] returns "COMMAND_POOL", then [code]get_device_memory_by_object_type(5)[/code] returns the bytes used by the GPU for command pools.
+				This is only used by Vulkan in Debug builds.
+			</description>
+		</method>
+		<method name="get_tracked_object_type_count" qualifiers="const">
+			<return type="int" />
+			<description>
+				Returns how many types of trackable objects are.
+				This is only used by Vulkan in Debug builds.
+			</description>
+		</method>
 		<method name="index_array_create">
 			<return type="RID" />
 			<param index="0" name="index_buffer" type="RID" />
@@ -2362,5 +2459,31 @@
 		<constant name="INVALID_FORMAT_ID" value="-1">
 			Returned by functions that return a format ID if a value is invalid.
 		</constant>
+		<constant name="NONE" value="0" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="REFLECTION_PROBES" value="65536" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="SKY_PASS" value="131072" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="LIGHTMAPPER_PASS" value="196608" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="SHADOW_PASS_DIRECTIONAL" value="262144" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="SHADOW_PASS_CUBE" value="327680" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="OPAQUE_PASS" value="393216" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="ALPHA_PASS" value="458752" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="TRANSPARENT_PASS" value="524288" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="POST_PROCESSING_PASS" value="589824" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="BLIT_PASS" value="655360" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="UI_PASS" value="720896" enum="BreadcrumbMarker">
+		</constant>
+		<constant name="DEBUG_PASS" value="786432" enum="BreadcrumbMarker">
+		</constant>
 	</constants>
 </class>

+ 9 - 0
drivers/d3d12/rendering_device_driver_d3d12.cpp

@@ -3815,6 +3815,11 @@ void RenderingDeviceDriverD3D12::shader_free(ShaderID p_shader) {
 	VersatileResource::free(resources_allocator, shader_info_in);
 }
 
+void RenderingDeviceDriverD3D12::shader_destroy_modules(ShaderID p_shader) {
+	ShaderInfo *shader_info_in = (ShaderInfo *)p_shader.id;
+	shader_info_in->stages_bytecode.clear();
+}
+
 /*********************/
 /**** UNIFORM SET ****/
 /*********************/
@@ -6036,6 +6041,10 @@ void RenderingDeviceDriverD3D12::command_end_label(CommandBufferID p_cmd_buffer)
 #endif
 }
 
+void RenderingDeviceDriverD3D12::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
+	// TODO: Implement via DRED.
+}
+
 /********************/
 /**** SUBMISSION ****/
 /********************/

+ 6 - 0
drivers/d3d12/rendering_device_driver_d3d12.h

@@ -714,6 +714,7 @@ public:
 	virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final;
 	virtual uint32_t shader_get_layout_hash(ShaderID p_shader) override final;
 	virtual void shader_free(ShaderID p_shader) override final;
+	virtual void shader_destroy_modules(ShaderID p_shader) override final;
 
 	/*********************/
 	/**** UNIFORM SET ****/
@@ -945,6 +946,11 @@ public:
 	virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
 	virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
 
+	/****************/
+	/**** DEBUG *****/
+	/****************/
+	virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
+
 	/********************/
 	/**** SUBMISSION ****/
 	/********************/

+ 5 - 0
drivers/metal/rendering_device_driver_metal.h

@@ -231,6 +231,7 @@ public:
 	virtual Vector<uint8_t> shader_compile_binary_from_spirv(VectorView<ShaderStageSPIRVData> p_spirv, const String &p_shader_name) override final;
 	virtual ShaderID shader_create_from_bytecode(const Vector<uint8_t> &p_shader_binary, ShaderDescription &r_shader_desc, String &r_name) override final;
 	virtual void shader_free(ShaderID p_shader) override final;
+	virtual void shader_destroy_modules(ShaderID p_shader) override final;
 
 #pragma mark - Uniform Set
 
@@ -376,6 +377,10 @@ public:
 	virtual void command_begin_label(CommandBufferID p_cmd_buffer, const char *p_label_name, const Color &p_color) override final;
 	virtual void command_end_label(CommandBufferID p_cmd_buffer) override final;
 
+#pragma mark - Debug
+
+	virtual void command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) override final;
+
 #pragma mark - Submission
 
 	virtual void begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) override final;

+ 10 - 0
drivers/metal/rendering_device_driver_metal.mm

@@ -2447,6 +2447,10 @@ void RenderingDeviceDriverMetal::shader_free(ShaderID p_shader) {
 	delete obj;
 }
 
+void RenderingDeviceDriverMetal::shader_destroy_modules(ShaderID p_shader) {
+	// TODO.
+}
+
 /*********************/
 /**** UNIFORM SET ****/
 /*********************/
@@ -3541,6 +3545,12 @@ void RenderingDeviceDriverMetal::command_end_label(CommandBufferID p_cmd_buffer)
 	[cb->get_command_buffer() popDebugGroup];
 }
 
+#pragma mark - Debug
+
+void RenderingDeviceDriverMetal::command_insert_breadcrumb(CommandBufferID p_cmd_buffer, uint32_t p_data) {
+	// TODO: Implement.
+}
+
 #pragma mark - Submission
 
 void RenderingDeviceDriverMetal::begin_segment(uint32_t p_frame_index, uint32_t p_frames_drawn) {

+ 326 - 7
drivers/vulkan/rendering_context_driver_vulkan.cpp

@@ -40,21 +40,340 @@
 #include "rendering_device_driver_vulkan.h"
 #include "vulkan_hooks.h"
 
+#if defined(VK_TRACK_DRIVER_MEMORY)
+/*************************************************/
+// Driver memory tracking
+/*************************************************/
+// Total driver memory and allocation amount.
+SafeNumeric<size_t> driver_memory_total_memory;
+SafeNumeric<size_t> driver_memory_total_alloc_count;
+// Amount of driver memory for every object type.
+SafeNumeric<size_t> driver_memory_tracker[RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_COUNT][RenderingContextDriverVulkan::VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT];
+// Amount of allocations for every object type.
+SafeNumeric<uint32_t> driver_memory_allocation_count[RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_COUNT][RenderingContextDriverVulkan::VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT];
+#endif
+
+#if defined(VK_TRACK_DEVICE_MEMORY)
+/*************************************************/
+// Device memory report
+/*************************************************/
+// Total device memory and allocation amount.
+HashMap<uint64_t, size_t> memory_report_table;
+// Total memory and allocation amount.
+SafeNumeric<uint64_t> memory_report_total_memory;
+SafeNumeric<uint64_t> memory_report_total_alloc_count;
+// Amount of device memory for every object type.
+SafeNumeric<size_t> memory_report_mem_usage[RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_COUNT];
+// Amount of device memory allocations for every object type.
+SafeNumeric<size_t> memory_report_allocation_count[RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_COUNT];
+#endif
+
+const char *RenderingContextDriverVulkan::get_tracked_object_name(uint32_t p_type_index) const {
+#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
+	static constexpr const char *vkTrackedObjectTypeNames[] = { "UNKNOWN",
+		"INSTANCE",
+		"PHYSICAL_DEVICE",
+		"DEVICE",
+		"QUEUE",
+		"SEMAPHORE",
+		"COMMAND_BUFFER",
+		"FENCE",
+		"DEVICE_MEMORY",
+		"BUFFER",
+		"IMAGE",
+		"EVENT",
+		"QUERY_POOL",
+		"BUFFER_VIEW",
+		"IMAGE_VIEW",
+		"SHADER_MODULE",
+		"PIPELINE_CACHE",
+		"PIPELINE_LAYOUT",
+		"RENDER_PASS",
+		"PIPELINE",
+		"DESCRIPTOR_SET_LAYOUT",
+		"SAMPLER",
+		"DESCRIPTOR_POOL",
+		"DESCRIPTOR_SET",
+		"FRAMEBUFFER",
+		"COMMAND_POOL",
+		"DESCRIPTOR_UPDATE_TEMPLATE_KHR",
+		"SURFACE_KHR",
+		"SWAPCHAIN_KHR",
+		"DEBUG_UTILS_MESSENGER_EXT",
+		"DEBUG_REPORT_CALLBACK_EXT",
+		"ACCELERATION_STRUCTURE",
+		"VMA_BUFFER_OR_IMAGE" };
+
+	return vkTrackedObjectTypeNames[p_type_index];
+#else
+	return "VK_TRACK_DRIVER_* disabled at build time";
+#endif
+}
+
+#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
+uint64_t RenderingContextDriverVulkan::get_tracked_object_type_count() const {
+	return VK_TRACKED_OBJECT_TYPE_COUNT;
+}
+#endif
+
+#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
+RenderingContextDriverVulkan::VkTrackedObjectType vk_object_to_tracked_object(VkObjectType p_type) {
+	if (p_type > VK_OBJECT_TYPE_COMMAND_POOL && p_type != (VkObjectType)RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_VMA) {
+		switch (p_type) {
+			case VK_OBJECT_TYPE_SURFACE_KHR:
+				return RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_SURFACE;
+			case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
+				return RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_SWAPCHAIN;
+			case VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT:
+				return RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT;
+			case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
+				return RenderingContextDriverVulkan::VK_TRACKED_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT;
+			default:
+				_err_print_error(FUNCTION_STR, __FILE__, __LINE__, "Unknown VkObjectType enum value " + itos((uint32_t)p_type) + ".Please add it to VkTrackedObjectType, switch statement in "
+																																 "vk_object_to_tracked_object and get_tracked_object_name.",
+						(int)p_type);
+				return (RenderingContextDriverVulkan::VkTrackedObjectType)VK_OBJECT_TYPE_UNKNOWN;
+		}
+	}
+
+	return (RenderingContextDriverVulkan::VkTrackedObjectType)p_type;
+}
+#endif
+
+#if defined(VK_TRACK_DEVICE_MEMORY)
+uint64_t RenderingContextDriverVulkan::get_device_total_memory() const {
+	return memory_report_total_memory.get();
+}
+
+uint64_t RenderingContextDriverVulkan::get_device_allocation_count() const {
+	return memory_report_total_alloc_count.get();
+}
+
+uint64_t RenderingContextDriverVulkan::get_device_memory_by_object_type(uint32_t p_type) const {
+	return memory_report_mem_usage[p_type].get();
+}
+
+uint64_t RenderingContextDriverVulkan::get_device_allocs_by_object_type(uint32_t p_type) const {
+	return memory_report_allocation_count[p_type].get();
+}
+#endif
+
+#if defined(VK_TRACK_DRIVER_MEMORY)
+uint64_t RenderingContextDriverVulkan::get_driver_total_memory() const {
+	return driver_memory_total_memory.get();
+}
+
+uint64_t RenderingContextDriverVulkan::get_driver_allocation_count() const {
+	return driver_memory_total_alloc_count.get();
+}
+
+uint64_t RenderingContextDriverVulkan::get_driver_memory_by_object_type(uint32_t p_type) const {
+	uint64_t ret = 0;
+	for (uint32_t i = 0; i < VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT; i++) {
+		ret += driver_memory_tracker[p_type][i].get();
+	}
+
+	return ret;
+}
+
+uint64_t RenderingContextDriverVulkan::get_driver_allocs_by_object_type(uint32_t p_type) const {
+	uint64_t ret = 0;
+	for (uint32_t i = 0; i < VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT; i++) {
+		ret += driver_memory_allocation_count[p_type][i].get();
+	}
+
+	return ret;
+}
+#endif
+
+#if defined(VK_TRACK_DEVICE_MEMORY)
+void RenderingContextDriverVulkan::memory_report_callback(const VkDeviceMemoryReportCallbackDataEXT *p_callback_data, void *p_user_data) {
+	if (!p_callback_data) {
+		return;
+	}
+	const RenderingContextDriverVulkan::VkTrackedObjectType obj_type = vk_object_to_tracked_object(p_callback_data->objectType);
+	uint64_t obj_id = p_callback_data->memoryObjectId;
+
+	if (p_callback_data->type == VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_ALLOCATE_EXT) {
+		// Realloc, update size
+		if (memory_report_table.has(obj_id)) {
+			memory_report_total_memory.sub(memory_report_table[obj_id]);
+			memory_report_mem_usage[obj_type].sub(memory_report_table[obj_id]);
+
+			memory_report_total_memory.add(p_callback_data->size);
+			memory_report_mem_usage[obj_type].add(p_callback_data->size);
+
+			memory_report_table[p_callback_data->memoryObjectId] = p_callback_data->size;
+		} else {
+			memory_report_table[obj_id] = p_callback_data->size;
+
+			memory_report_total_alloc_count.increment();
+			memory_report_allocation_count[obj_type].increment();
+			memory_report_mem_usage[obj_type].add(p_callback_data->size);
+			memory_report_total_memory.add(p_callback_data->size);
+		}
+	} else if (p_callback_data->type == VK_DEVICE_MEMORY_REPORT_EVENT_TYPE_FREE_EXT) {
+		if (memory_report_table.has(obj_id)) {
+			memory_report_total_alloc_count.decrement();
+			memory_report_allocation_count[obj_type].decrement();
+			memory_report_mem_usage[obj_type].sub(p_callback_data->size);
+			memory_report_total_memory.sub(p_callback_data->size);
+
+			memory_report_table.remove(memory_report_table.find(obj_id));
+		}
+	}
+}
+#endif
+
+VkAllocationCallbacks *RenderingContextDriverVulkan::get_allocation_callbacks(VkObjectType p_type) {
+#if !defined(VK_TRACK_DRIVER_MEMORY)
+	return nullptr;
+#else
+	struct TrackedMemHeader {
+		size_t size;
+		VkSystemAllocationScope allocation_scope;
+		VkTrackedObjectType type;
+	};
+	VkAllocationCallbacks tracking_callbacks = {
+		// Allocation function
+		nullptr,
+		[](
+				void *p_user_data,
+				size_t size,
+				size_t alignment,
+				VkSystemAllocationScope allocation_scope) -> void * {
+			static constexpr size_t tracking_data_size = 32;
+			VkTrackedObjectType type = static_cast<VkTrackedObjectType>(*reinterpret_cast<VkTrackedObjectType *>(p_user_data));
+
+			driver_memory_total_memory.add(size);
+			driver_memory_total_alloc_count.increment();
+			driver_memory_tracker[type][allocation_scope].add(size);
+			driver_memory_allocation_count[type][allocation_scope].increment();
+
+			alignment = MAX(alignment, tracking_data_size);
+
+			uint8_t *ret = reinterpret_cast<uint8_t *>(Memory::alloc_aligned_static(size + alignment, alignment));
+			if (ret == nullptr) {
+				return nullptr;
+			}
+
+			// Track allocation
+			TrackedMemHeader *header = reinterpret_cast<TrackedMemHeader *>(ret);
+			header->size = size;
+			header->allocation_scope = allocation_scope;
+			header->type = type;
+			*reinterpret_cast<size_t *>(ret + alignment - sizeof(size_t)) = alignment;
+
+			// Return first available chunk of memory
+			return ret + alignment;
+		},
+
+		// Reallocation function
+		[](
+				void *p_user_data,
+				void *p_original,
+				size_t size,
+				size_t alignment,
+				VkSystemAllocationScope allocation_scope) -> void * {
+			if (p_original == nullptr) {
+				VkObjectType type = static_cast<VkObjectType>(*reinterpret_cast<uint32_t *>(p_user_data));
+				return get_allocation_callbacks(type)->pfnAllocation(p_user_data, size, alignment, allocation_scope);
+			}
+
+			uint8_t *mem = reinterpret_cast<uint8_t *>(p_original);
+			// Retrieve alignment
+			alignment = *reinterpret_cast<size_t *>(mem - sizeof(size_t));
+			// Retrieve allocation data
+			TrackedMemHeader *header = reinterpret_cast<TrackedMemHeader *>(mem - alignment);
+
+			// Update allocation size
+			driver_memory_total_memory.sub(header->size);
+			driver_memory_total_memory.add(size);
+			driver_memory_tracker[header->type][header->allocation_scope].sub(header->size);
+			driver_memory_tracker[header->type][header->allocation_scope].add(size);
+
+			uint8_t *ret = reinterpret_cast<uint8_t *>(Memory::realloc_aligned_static(header, size + alignment, header->size + alignment, alignment));
+			if (ret == nullptr) {
+				return nullptr;
+			}
+			// Update tracker
+			header = reinterpret_cast<TrackedMemHeader *>(ret);
+			header->size = size;
+			return ret + alignment;
+		},
+
+		// Free function
+		[](
+				void *p_user_data,
+				void *p_memory) {
+			if (!p_memory) {
+				return;
+			}
+
+			uint8_t *mem = reinterpret_cast<uint8_t *>(p_memory);
+			size_t alignment = *reinterpret_cast<size_t *>(mem - sizeof(size_t));
+			TrackedMemHeader *header = reinterpret_cast<TrackedMemHeader *>(mem - alignment);
+
+			driver_memory_total_alloc_count.decrement();
+			driver_memory_total_memory.sub(header->size);
+			driver_memory_tracker[header->type][header->allocation_scope].sub(header->size);
+			driver_memory_allocation_count[header->type][header->allocation_scope].decrement();
+
+			Memory::free_aligned_static(header);
+		},
+		// Internal allocation / deallocation. We don't track them as they cannot really be controlled or optimized by the programmer.
+		[](
+				void *p_user_data,
+				size_t size,
+				VkInternalAllocationType allocation_type,
+				VkSystemAllocationScope allocation_scope) {
+		},
+		[](
+				void *p_user_data,
+				size_t size,
+				VkInternalAllocationType allocation_type,
+				VkSystemAllocationScope allocation_scope) {
+		},
+	};
+
+	// Create a callback per object type
+	static VkAllocationCallbacks object_callbacks[VK_TRACKED_OBJECT_TYPE_COUNT] = {};
+	static uint32_t object_user_data[VK_TRACKED_OBJECT_TYPE_COUNT] = {};
+
+	// Only build the first time
+	if (!object_callbacks[0].pfnAllocation) {
+		for (uint32_t c = 0; c < VK_TRACKED_OBJECT_TYPE_COUNT; ++c) {
+			object_callbacks[c] = tracking_callbacks;
+			object_user_data[c] = c;
+			object_callbacks[c].pUserData = &object_user_data[c];
+
+			for (uint32_t i = 0; i < VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT; i++) {
+				driver_memory_tracker[c][i].set(0);
+				driver_memory_allocation_count[c][i].set(0);
+			}
+		}
+	}
+
+	uint32_t type_index = vk_object_to_tracked_object(p_type);
+	return &object_callbacks[type_index];
+#endif
+}
+
 RenderingContextDriverVulkan::RenderingContextDriverVulkan() {
 	// Empty constructor.
 }
 
 RenderingContextDriverVulkan::~RenderingContextDriverVulkan() {
 	if (debug_messenger != VK_NULL_HANDLE && functions.DestroyDebugUtilsMessengerEXT != nullptr) {
-		functions.DestroyDebugUtilsMessengerEXT(instance, debug_messenger, nullptr);
+		functions.DestroyDebugUtilsMessengerEXT(instance, debug_messenger, get_allocation_callbacks(VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT));
 	}
 
 	if (debug_report != VK_NULL_HANDLE && functions.DestroyDebugReportCallbackEXT != nullptr) {
-		functions.DestroyDebugReportCallbackEXT(instance, debug_report, nullptr);
+		functions.DestroyDebugReportCallbackEXT(instance, debug_report, get_allocation_callbacks(VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT));
 	}
 
 	if (instance != VK_NULL_HANDLE) {
-		vkDestroyInstance(instance, nullptr);
+		vkDestroyInstance(instance, get_allocation_callbacks(VK_OBJECT_TYPE_INSTANCE));
 	}
 }
 
@@ -441,7 +760,7 @@ Error RenderingContextDriverVulkan::_initialize_instance() {
 			ERR_FAIL_V_MSG(ERR_CANT_CREATE, "GetProcAddr: Failed to init VK_EXT_debug_utils\nGetProcAddr: Failure");
 		}
 
-		VkResult res = functions.CreateDebugUtilsMessengerEXT(instance, &debug_messenger_create_info, nullptr, &debug_messenger);
+		VkResult res = functions.CreateDebugUtilsMessengerEXT(instance, &debug_messenger_create_info, get_allocation_callbacks(VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT), &debug_messenger);
 		switch (res) {
 			case VK_SUCCESS:
 				break;
@@ -461,7 +780,7 @@ Error RenderingContextDriverVulkan::_initialize_instance() {
 			ERR_FAIL_V_MSG(ERR_CANT_CREATE, "GetProcAddr: Failed to init VK_EXT_debug_report\nGetProcAddr: Failure");
 		}
 
-		VkResult res = functions.CreateDebugReportCallbackEXT(instance, &debug_report_callback_create_info, nullptr, &debug_report);
+		VkResult res = functions.CreateDebugReportCallbackEXT(instance, &debug_report_callback_create_info, get_allocation_callbacks(VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT), &debug_report);
 		switch (res) {
 			case VK_SUCCESS:
 				break;
@@ -560,7 +879,7 @@ Error RenderingContextDriverVulkan::_create_vulkan_instance(const VkInstanceCrea
 	if (VulkanHooks::get_singleton() != nullptr) {
 		return VulkanHooks::get_singleton()->create_vulkan_instance(p_create_info, r_instance) ? OK : ERR_CANT_CREATE;
 	} else {
-		VkResult err = vkCreateInstance(p_create_info, nullptr, r_instance);
+		VkResult err = vkCreateInstance(p_create_info, get_allocation_callbacks(VK_OBJECT_TYPE_INSTANCE), r_instance);
 		ERR_FAIL_COND_V_MSG(err == VK_ERROR_INCOMPATIBLE_DRIVER, ERR_CANT_CREATE,
 				"Cannot find a compatible Vulkan installable client driver (ICD).\n\n"
 				"vkCreateInstance Failure");
@@ -679,7 +998,7 @@ bool RenderingContextDriverVulkan::surface_get_needs_resize(SurfaceID p_surface)
 
 void RenderingContextDriverVulkan::surface_destroy(SurfaceID p_surface) {
 	Surface *surface = (Surface *)(p_surface);
-	vkDestroySurfaceKHR(instance, surface->vk_surface, nullptr);
+	vkDestroySurfaceKHR(instance, surface->vk_surface, get_allocation_callbacks(VK_OBJECT_TYPE_SURFACE_KHR));
 	memdelete(surface);
 }
 

+ 50 - 0
drivers/vulkan/rendering_context_driver_vulkan.h

@@ -35,6 +35,11 @@
 
 #include "servers/rendering/rendering_context_driver.h"
 
+#if defined(DEBUG_ENABLED) || defined(DEV_ENABLED)
+#define VK_TRACK_DRIVER_MEMORY
+#define VK_TRACK_DEVICE_MEMORY
+#endif
+
 #ifdef USE_VOLK
 #include <volk.h>
 #else
@@ -77,6 +82,12 @@ public:
 		PFN_vkDebugReportMessageEXT DebugReportMessageEXT = nullptr;
 		PFN_vkDestroyDebugReportCallbackEXT DestroyDebugReportCallbackEXT = nullptr;
 
+		// Debug marker extensions.
+		PFN_vkCmdDebugMarkerBeginEXT CmdDebugMarkerBeginEXT = nullptr;
+		PFN_vkCmdDebugMarkerEndEXT CmdDebugMarkerEndEXT = nullptr;
+		PFN_vkCmdDebugMarkerInsertEXT CmdDebugMarkerInsertEXT = nullptr;
+		PFN_vkDebugMarkerSetObjectNameEXT DebugMarkerSetObjectNameEXT = nullptr;
+
 		bool debug_report_functions_available() const {
 			return CreateDebugReportCallbackEXT != nullptr &&
 					DebugReportMessageEXT != nullptr &&
@@ -110,6 +121,8 @@ private:
 	// Static callbacks.
 	static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_messenger_callback(VkDebugUtilsMessageSeverityFlagBitsEXT p_message_severity, VkDebugUtilsMessageTypeFlagsEXT p_message_type, const VkDebugUtilsMessengerCallbackDataEXT *p_callback_data, void *p_user_data);
 	static VKAPI_ATTR VkBool32 VKAPI_CALL _debug_report_callback(VkDebugReportFlagsEXT p_flags, VkDebugReportObjectTypeEXT p_object_type, uint64_t p_object, size_t p_location, int32_t p_message_code, const char *p_layer_prefix, const char *p_message, void *p_user_data);
+	// Debug marker extensions.
+	VkDebugReportObjectTypeEXT _convert_to_debug_report_objectType(VkObjectType p_object_type);
 
 protected:
 	Error _find_validation_layers(TightLocalVector<const char *> &r_layer_names) const;
@@ -153,6 +166,43 @@ public:
 	bool queue_family_supports_present(VkPhysicalDevice p_physical_device, uint32_t p_queue_family_index, SurfaceID p_surface) const;
 	const Functions &functions_get() const;
 
+	static VkAllocationCallbacks *get_allocation_callbacks(VkObjectType p_type);
+
+#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
+	enum VkTrackedObjectType{
+		VK_TRACKED_OBJECT_TYPE_SURFACE = VK_OBJECT_TYPE_COMMAND_POOL + 1,
+		VK_TRACKED_OBJECT_TYPE_SWAPCHAIN,
+		VK_TRACKED_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT,
+		VK_TRACKED_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT,
+		VK_TRACKED_OBJECT_TYPE_VMA,
+		VK_TRACKED_OBJECT_TYPE_COUNT
+	};
+
+	enum VkTrackedSystemAllocationScope{
+		VK_TRACKED_SYSTEM_ALLOCATION_SCOPE_COUNT = VK_SYSTEM_ALLOCATION_SCOPE_INSTANCE + 1
+	};
+#endif
+
+	const char *get_tracked_object_name(uint32_t p_type_index) const override;
+#if defined(VK_TRACK_DRIVER_MEMORY) || defined(VK_TRACK_DEVICE_MEMORY)
+	uint64_t get_tracked_object_type_count() const override;
+#endif
+
+#if defined(VK_TRACK_DRIVER_MEMORY)
+	uint64_t get_driver_total_memory() const override;
+	uint64_t get_driver_allocation_count() const override;
+	uint64_t get_driver_memory_by_object_type(uint32_t p_type) const override;
+	uint64_t get_driver_allocs_by_object_type(uint32_t p_type) const override;
+#endif
+
+#if defined(VK_TRACK_DEVICE_MEMORY)
+	uint64_t get_device_total_memory() const override;
+	uint64_t get_device_allocation_count() const override;
+	uint64_t get_device_memory_by_object_type(uint32_t p_type) const override;
+	uint64_t get_device_allocs_by_object_type(uint32_t p_type) const override;
+	static VKAPI_ATTR void VKAPI_CALL memory_report_callback(const VkDeviceMemoryReportCallbackDataEXT *p_callback_data, void *p_user_data);
+#endif
+
 	RenderingContextDriverVulkan();
 	virtual ~RenderingContextDriverVulkan() override;
 };

+ 432 - 67
drivers/vulkan/rendering_device_driver_vulkan.cpp

@@ -502,6 +502,24 @@ Error RenderingDeviceDriverVulkan::_initialize_device_extensions() {
 		_register_requested_device_extension(VK_KHR_SHADER_NON_SEMANTIC_INFO_EXTENSION_NAME, true);
 	}
 
+#if defined(VK_TRACK_DEVICE_MEMORY)
+	_register_requested_device_extension(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME, false);
+#endif
+	_register_requested_device_extension(VK_EXT_DEVICE_FAULT_EXTENSION_NAME, false);
+
+	{
+		// Debug marker extensions.
+		// Should be last element in the array.
+#ifdef DEV_ENABLED
+		bool want_debug_markers = true;
+#else
+		bool want_debug_markers = OS::get_singleton()->is_stdout_verbose();
+#endif
+		if (want_debug_markers) {
+			_register_requested_device_extension(VK_EXT_DEBUG_MARKER_EXTENSION_NAME, false);
+		}
+	}
+
 	uint32_t device_extension_count = 0;
 	VkResult err = vkEnumerateDeviceExtensionProperties(physical_device, nullptr, &device_extension_count, nullptr);
 	ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
@@ -745,6 +763,15 @@ Error RenderingDeviceDriverVulkan::_check_device_capabilities() {
 		if (enabled_device_extension_names.has(VK_EXT_PIPELINE_CREATION_CACHE_CONTROL_EXTENSION_NAME)) {
 			pipeline_cache_control_support = pipeline_cache_control_features.pipelineCreationCacheControl;
 		}
+
+		if (enabled_device_extension_names.has(VK_EXT_DEVICE_FAULT_EXTENSION_NAME)) {
+			device_fault_support = true;
+		}
+#if defined(VK_TRACK_DEVICE_MEMORY)
+		if (enabled_device_extension_names.has(VK_EXT_DEVICE_MEMORY_REPORT_EXTENSION_NAME)) {
+			device_memory_report_support = true;
+		}
+#endif
 	}
 
 	if (functions.GetPhysicalDeviceProperties2 != nullptr) {
@@ -913,6 +940,26 @@ Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector<VkDevice
 		create_info_next = &pipeline_cache_control_features;
 	}
 
+	VkPhysicalDeviceFaultFeaturesEXT device_fault_features = {};
+	if (device_fault_support) {
+		device_fault_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FAULT_FEATURES_EXT;
+		device_fault_features.pNext = create_info_next;
+		create_info_next = &device_fault_features;
+	}
+
+#if defined(VK_TRACK_DEVICE_MEMORY)
+	VkDeviceDeviceMemoryReportCreateInfoEXT memory_report_info = {};
+	if (device_memory_report_support) {
+		memory_report_info.sType = VK_STRUCTURE_TYPE_DEVICE_DEVICE_MEMORY_REPORT_CREATE_INFO_EXT;
+		memory_report_info.pfnUserCallback = RenderingContextDriverVulkan::memory_report_callback;
+		memory_report_info.pNext = create_info_next;
+		memory_report_info.flags = 0;
+		memory_report_info.pUserData = this;
+
+		create_info_next = &memory_report_info;
+	}
+#endif
+
 	VkPhysicalDeviceVulkan11Features vulkan_1_1_features = {};
 	VkPhysicalDevice16BitStorageFeaturesKHR storage_features = {};
 	VkPhysicalDeviceMultiviewFeatures multiview_features = {};
@@ -968,7 +1015,7 @@ Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector<VkDevice
 		bool device_created = VulkanHooks::get_singleton()->create_vulkan_device(&create_info, &vk_device);
 		ERR_FAIL_COND_V(!device_created, ERR_CANT_CREATE);
 	} else {
-		VkResult err = vkCreateDevice(physical_device, &create_info, nullptr, &vk_device);
+		VkResult err = vkCreateDevice(physical_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_DEVICE), &vk_device);
 		ERR_FAIL_COND_V(err != VK_SUCCESS, ERR_CANT_CREATE);
 	}
 
@@ -989,6 +1036,19 @@ Error RenderingDeviceDriverVulkan::_initialize_device(const LocalVector<VkDevice
 		if (enabled_device_extension_names.has(VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME)) {
 			device_functions.CreateRenderPass2KHR = PFN_vkCreateRenderPass2KHR(functions.GetDeviceProcAddr(vk_device, "vkCreateRenderPass2KHR"));
 		}
+
+		// Debug marker extensions.
+		if (enabled_device_extension_names.has(VK_EXT_DEBUG_MARKER_EXTENSION_NAME)) {
+			device_functions.CmdDebugMarkerBeginEXT = (PFN_vkCmdDebugMarkerBeginEXT)functions.GetDeviceProcAddr(vk_device, "vkCmdDebugMarkerBeginEXT");
+			device_functions.CmdDebugMarkerEndEXT = (PFN_vkCmdDebugMarkerEndEXT)functions.GetDeviceProcAddr(vk_device, "vkCmdDebugMarkerEndEXT");
+			device_functions.CmdDebugMarkerInsertEXT = (PFN_vkCmdDebugMarkerInsertEXT)functions.GetDeviceProcAddr(vk_device, "vkCmdDebugMarkerInsertEXT");
+			device_functions.DebugMarkerSetObjectNameEXT = (PFN_vkDebugMarkerSetObjectNameEXT)functions.GetDeviceProcAddr(vk_device, "vkDebugMarkerSetObjectNameEXT");
+		}
+
+		// Debug device fault extension.
+		if (device_fault_support) {
+			device_functions.GetDeviceFaultInfoEXT = (PFN_vkGetDeviceFaultInfoEXT)functions.GetDeviceProcAddr(vk_device, "vkGetDeviceFaultInfoEXT");
+		}
 	}
 
 	return OK;
@@ -1148,17 +1208,102 @@ bool RenderingDeviceDriverVulkan::_recreate_image_semaphore(CommandQueue *p_comm
 	VkSemaphore semaphore;
 	VkSemaphoreCreateInfo create_info = {};
 	create_info.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
-	VkResult err = vkCreateSemaphore(vk_device, &create_info, nullptr, &semaphore);
+	VkResult err = vkCreateSemaphore(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_SEMAPHORE), &semaphore);
 	ERR_FAIL_COND_V(err != VK_SUCCESS, false);
 
 	// Indicate the semaphore is free again and destroy the previous one before storing the new one.
-	vkDestroySemaphore(vk_device, p_command_queue->image_semaphores[p_semaphore_index], nullptr);
+	vkDestroySemaphore(vk_device, p_command_queue->image_semaphores[p_semaphore_index], VKC::get_allocation_callbacks(VK_OBJECT_TYPE_SEMAPHORE));
 
 	p_command_queue->image_semaphores[p_semaphore_index] = semaphore;
 	p_command_queue->free_image_semaphores.push_back(p_semaphore_index);
 
 	return true;
 }
+// Debug marker extensions.
+VkDebugReportObjectTypeEXT RenderingDeviceDriverVulkan::_convert_to_debug_report_objectType(VkObjectType p_object_type) {
+	switch (p_object_type) {
+		case VK_OBJECT_TYPE_UNKNOWN:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;
+		case VK_OBJECT_TYPE_INSTANCE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_INSTANCE_EXT;
+		case VK_OBJECT_TYPE_PHYSICAL_DEVICE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_PHYSICAL_DEVICE_EXT;
+		case VK_OBJECT_TYPE_DEVICE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_EXT;
+		case VK_OBJECT_TYPE_QUEUE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_QUEUE_EXT;
+		case VK_OBJECT_TYPE_SEMAPHORE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SEMAPHORE_EXT;
+		case VK_OBJECT_TYPE_COMMAND_BUFFER:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_BUFFER_EXT;
+		case VK_OBJECT_TYPE_FENCE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_FENCE_EXT;
+		case VK_OBJECT_TYPE_DEVICE_MEMORY:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DEVICE_MEMORY_EXT;
+		case VK_OBJECT_TYPE_BUFFER:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_EXT;
+		case VK_OBJECT_TYPE_IMAGE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_EXT;
+		case VK_OBJECT_TYPE_EVENT:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_EVENT_EXT;
+		case VK_OBJECT_TYPE_QUERY_POOL:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_QUERY_POOL_EXT;
+		case VK_OBJECT_TYPE_BUFFER_VIEW:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_BUFFER_VIEW_EXT;
+		case VK_OBJECT_TYPE_IMAGE_VIEW:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_IMAGE_VIEW_EXT;
+		case VK_OBJECT_TYPE_SHADER_MODULE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SHADER_MODULE_EXT;
+		case VK_OBJECT_TYPE_PIPELINE_CACHE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_CACHE_EXT;
+		case VK_OBJECT_TYPE_PIPELINE_LAYOUT:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_LAYOUT_EXT;
+		case VK_OBJECT_TYPE_RENDER_PASS:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_RENDER_PASS_EXT;
+		case VK_OBJECT_TYPE_PIPELINE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_PIPELINE_EXT;
+		case VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT_EXT;
+		case VK_OBJECT_TYPE_SAMPLER:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_EXT;
+		case VK_OBJECT_TYPE_DESCRIPTOR_POOL:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_POOL_EXT;
+		case VK_OBJECT_TYPE_DESCRIPTOR_SET:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_SET_EXT;
+		case VK_OBJECT_TYPE_FRAMEBUFFER:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_FRAMEBUFFER_EXT;
+		case VK_OBJECT_TYPE_COMMAND_POOL:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_COMMAND_POOL_EXT;
+		case VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION_EXT;
+		case VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE_EXT;
+		case VK_OBJECT_TYPE_SURFACE_KHR:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SURFACE_KHR_EXT;
+		case VK_OBJECT_TYPE_SWAPCHAIN_KHR:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_SWAPCHAIN_KHR_EXT;
+		case VK_OBJECT_TYPE_DISPLAY_KHR:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_KHR_EXT;
+		case VK_OBJECT_TYPE_DISPLAY_MODE_KHR:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DISPLAY_MODE_KHR_EXT;
+		case VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT_EXT;
+		case VK_OBJECT_TYPE_CU_MODULE_NVX:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_CU_MODULE_NVX_EXT;
+		case VK_OBJECT_TYPE_CU_FUNCTION_NVX:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_CU_FUNCTION_NVX_EXT;
+		case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR_EXT;
+		case VK_OBJECT_TYPE_VALIDATION_CACHE_EXT:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_VALIDATION_CACHE_EXT_EXT;
+		case VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV:
+			return VK_DEBUG_REPORT_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV_EXT;
+		default:
+			break;
+	}
+
+	return VK_DEBUG_REPORT_OBJECT_TYPE_UNKNOWN_EXT;
+}
 
 void RenderingDeviceDriverVulkan::_set_object_name(VkObjectType p_object_type, uint64_t p_object_handle, String p_object_name) {
 	const RenderingContextDriverVulkan::Functions &functions = context_driver->functions_get();
@@ -1171,6 +1316,16 @@ void RenderingDeviceDriverVulkan::_set_object_name(VkObjectType p_object_type, u
 		name_info.objectHandle = p_object_handle;
 		name_info.pObjectName = obj_data.get_data();
 		functions.SetDebugUtilsObjectNameEXT(vk_device, &name_info);
+	} else if (functions.DebugMarkerSetObjectNameEXT != nullptr) {
+		// Debug marker extensions.
+		CharString obj_data = p_object_name.utf8();
+		VkDebugMarkerObjectNameInfoEXT name_info;
+		name_info.sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT;
+		name_info.pNext = nullptr;
+		name_info.objectType = _convert_to_debug_report_objectType(p_object_type);
+		name_info.object = p_object_handle;
+		name_info.pObjectName = obj_data.get_data();
+		functions.DebugMarkerSetObjectNameEXT(vk_device, &name_info);
 	}
 }
 
@@ -1211,6 +1366,7 @@ Error RenderingDeviceDriverVulkan::initialize(uint32_t p_device_index, uint32_t
 	ERR_FAIL_COND_V(err != OK, err);
 
 	max_descriptor_sets_per_pool = GLOBAL_GET("rendering/rendering_device/vulkan/max_descriptors_per_pool");
+	breadcrumb_buffer = buffer_create(sizeof(uint32_t), BufferUsageBits::BUFFER_USAGE_TRANSFER_TO_BIT, MemoryAllocationType::MEMORY_ALLOCATION_TYPE_CPU);
 
 	return OK;
 }
@@ -1279,11 +1435,10 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
 				// Looks like a readback buffer: GPU copies from VRAM, then CPU maps and reads.
 				alloc_create_info.flags = VMA_ALLOCATION_CREATE_HOST_ACCESS_RANDOM_BIT;
 			}
-			alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_HOST;
 			alloc_create_info.requiredFlags = (VK_MEMORY_PROPERTY_HOST_COHERENT_BIT | VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT);
 		} break;
 		case MEMORY_ALLOCATION_TYPE_GPU: {
-			alloc_create_info.usage = VMA_MEMORY_USAGE_AUTO_PREFER_DEVICE;
+			alloc_create_info.preferredFlags = VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
 			if (p_size <= SMALL_ALLOCATION_MAX_SIZE) {
 				uint32_t mem_type_index = 0;
 				vmaFindMemoryTypeIndexForBufferInfo(allocator, &create_info, &alloc_create_info, &mem_type_index);
@@ -1295,11 +1450,15 @@ RDD::BufferID RenderingDeviceDriverVulkan::buffer_create(uint64_t p_size, BitFie
 	VkBuffer vk_buffer = VK_NULL_HANDLE;
 	VmaAllocation allocation = nullptr;
 	VmaAllocationInfo alloc_info = {};
-	VkResult err = vmaCreateBuffer(allocator, &create_info, &alloc_create_info, &vk_buffer, &allocation, &alloc_info);
+
+	VkResult err = vkCreateBuffer(vk_device, &create_info, VKC::get_allocation_callbacks(VK_OBJECT_TYPE_BUFFER), &vk_buffer);
 	ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't create buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
+	err = vmaAllocateMemoryForBuffer(allocator, vk_buffer, &alloc_create_info, &allocation, &alloc_info);
+	ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't allocate memory for buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
+	err = vmaBindBufferMemory2(allocator, allocation, 0, vk_buffer, NULL);
+	ERR_FAIL_COND_V_MSG(err, BufferID(), "Can't bind memory to buffer of size: " + itos(p_size) + ", error " + itos(err) + ".");
 
 	// Bookkeep.
-
 	BufferInfo *buf_info = VersatileResource::allocate<BufferInfo>(resources_allocator);
 	buf_info->vk_buffer = vk_buffer;
 	buf_info->allocation.handle = allocation;