Skip to content

Commit

Permalink
Merge pull request #90993 from darksylinc/matias-TheForge
Browse files Browse the repository at this point in the history
Add debug utilities for Vulkan
  • Loading branch information
akien-mga authored Aug 21, 2024
2 parents 37ae2a2 + 364f916 commit 568589c
Show file tree
Hide file tree
Showing 32 changed files with 1,321 additions and 108 deletions.
33 changes: 33 additions & 0 deletions core/os/memory.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down Expand Up @@ -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;
Expand Down
24 changes: 24 additions & 0 deletions core/os/memory.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,30 @@ class Memory {
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();
Expand Down
125 changes: 124 additions & 1 deletion doc/classes/RenderingDevice.xml
Original file line number Diff line number Diff line change
Expand Up @@ -218,14 +218,15 @@
<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].
A simple drawing operation might look like this (code is not a complete example):
[codeblock]
var rd = RenderingDevice.new()
var clear_colors = PackedColorArray([Color(0, 0, 0, 0), Color(0, 0, 0, 0), Color(0, 0, 0, 0)])
var 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)
Expand All @@ -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">
Expand Down Expand Up @@ -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>
Expand All @@ -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" />
Expand All @@ -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>
Expand All @@ -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" />
Expand Down Expand Up @@ -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 changes: 9 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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 ****/
/*********************/
Expand Down Expand Up @@ -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 ****/
/********************/
Expand Down
6 changes: 6 additions & 0 deletions drivers/d3d12/rendering_device_driver_d3d12.h
Original file line number Diff line number Diff line change
Expand Up @@ -714,6 +714,7 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
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 ****/
Expand Down Expand Up @@ -945,6 +946,11 @@ class RenderingDeviceDriverD3D12 : public RenderingDeviceDriver {
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 ****/
/********************/
Expand Down
5 changes: 5 additions & 0 deletions drivers/metal/rendering_device_driver_metal.h
Original file line number Diff line number Diff line change
Expand Up @@ -231,6 +231,7 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : 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

Expand Down Expand Up @@ -376,6 +377,10 @@ class API_AVAILABLE(macos(11.0), ios(14.0)) RenderingDeviceDriverMetal : 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;
Expand Down
10 changes: 10 additions & 0 deletions drivers/metal/rendering_device_driver_metal.mm
Original file line number Diff line number Diff line change
Expand Up @@ -2447,6 +2447,10 @@ void deserialize(BufReader &p_reader) {
delete obj;
}

void RenderingDeviceDriverMetal::shader_destroy_modules(ShaderID p_shader) {
// TODO.
}

/*********************/
/**** UNIFORM SET ****/
/*********************/
Expand Down Expand Up @@ -3541,6 +3545,12 @@ bool isArrayTexture(MTLTextureType p_type) {
[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) {
Expand Down
Loading

0 comments on commit 568589c

Please sign in to comment.