Commit: a9a36a8a15ec517a301257810a56b8149120f5fb
Parent: e0bd75572cdba7626c8dcf6d2fb87f8aa77660f7
Author: Randy Palamar
Date: Thu, 5 Mar 2026 09:07:44 -0700
vulkan: add object labeling in debug builds
Diffstat:
| M | beamformer_internal.h | | | 2 | +- |
| M | ui.c | | | 10 | +++++----- |
| M | vulkan.c | | | 150 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------- |
| M | vulkan.h | | | 75 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
4 files changed, 211 insertions(+), 26 deletions(-)
diff --git a/beamformer_internal.h b/beamformer_internal.h
@@ -147,7 +147,7 @@ DEBUG_IMPORT void vk_buffer_range_download(void *output, GPUBuffer *, u64 source
DEBUG_IMPORT u64 vk_round_up_to_sync_size(u64, u64 min);
// NOTE: images are 2D only, any other use case should just use a buffer and index in the shader
-DEBUG_IMPORT void vk_image_allocate(GPUImage *, u32 width, u32 height, u32 mips, u32 samples, VulkanImageUsage usage, VulkanUsageFlags flags, OSHandle *export);
+DEBUG_IMPORT void vk_image_allocate(GPUImage *, u32 width, u32 height, u32 mips, u32 samples, VulkanImageUsage usage, VulkanUsageFlags flags, OSHandle *export, s8 label);
DEBUG_IMPORT void vk_image_release(GPUImage *);
DEBUG_IMPORT void vk_render_model_allocate(GPUBuffer *, void *indices, u64 index_count, u64 model_size, s8 label);
diff --git a/ui.c b/ui.c
@@ -972,8 +972,10 @@ resize_frame_view(BeamformerFrameView *view, uv2 dim)
glDeleteTextures(1, &view->texture);
glCreateTextures(GL_TEXTURE_2D, 1, &view->texture);
+ /* TODO(rnp): add some ID for the specific view here */
+ s8 label = s8("Frame View Texture");
vk_image_allocate(&view->colour_image, dim.w, dim.h, 1, 1, VulkanImageUsage_Colour,
- VulkanUsageFlag_ImageSampling, &view->export_handle);
+ VulkanUsageFlag_ImageSampling, &view->export_handle, label);
glMemoryObjectParameterivEXT(view->memory_object, GL_DEDICATED_MEMORY_OBJECT_EXT, (GLint []){1});
@@ -1001,8 +1003,6 @@ resize_frame_view(BeamformerFrameView *view, uv2 dim)
glTextureParameteri(view->texture, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
glTextureParameteri(view->texture, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
- /* TODO(rnp): add some ID for the specific view here */
- s8 label = s8("Frame View Texture");
glObjectLabel(GL_TEXTURE, view->texture, (i32)label.len, (char *)label.data);
}
@@ -4079,8 +4079,8 @@ ui_init(BeamformerCtx *ctx, Arena store)
split->region_split.right = add_compute_stats_view(ui, split, &ui->arena, ctx);
u32 samples = vk_gpu_info()->max_msaa_samples;
- vk_image_allocate(&ui->render_3d_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_Colour, 0, 0);
- vk_image_allocate(&ui->render_3d_depth_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_DepthStencil, 0, 0);
+ vk_image_allocate(&ui->render_3d_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_Colour, 0, 0, s8("Render Target Colour"));
+ vk_image_allocate(&ui->render_3d_depth_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_DepthStencil, 0, 0, s8("Render Target Depth"));
glGenSemaphoresEXT(countof(ui->render_semaphores_gl), ui->render_semaphores_gl);
for EachElement(ui->render_semaphores, it)
diff --git a/vulkan.c b/vulkan.c
@@ -18,9 +18,6 @@
#define MaxCommandBuffersInFlight BeamformerMaxRawDataFramesInFlight
#define MaxCommandBufferTimestamps (1024)
-// TODO(rnp): labelling
-#define vk_label_object(...)
-
typedef enum {
VulkanQueueKind_Graphics,
VulkanQueueKind_Compute,
@@ -231,6 +228,13 @@ read_only global const char *vk_debug_extensions[] = {VK_DEBUG_EXTENSIONS};
read_only global u32 vk_debug_extension_name_lengths[] = {VK_DEBUG_EXTENSIONS};
#undef X
+#define VK_INSTANCE_DEBUG_EXTENSIONS_LIST \
+ X(VK_EXT, debug_utils) \
+
+#define X(p, s, ...) s8_comp(#p "_" #s),
+read_only global s8 vk_instance_debug_extensions[] = {VK_INSTANCE_DEBUG_EXTENSIONS_LIST};
+#undef X
+
global union {
struct {
#define X(_, name, ...) b8 name;
@@ -238,6 +242,15 @@ global union {
#undef X
};
b8 E[countof(vk_debug_extensions)];
+
+ union {
+ struct {
+ #define X(_, name, ...) b8 name;
+ VK_INSTANCE_DEBUG_EXTENSIONS_LIST
+ #undef X
+ };
+ b8 E[countof(vk_instance_debug_extensions)];
+ } instance;
} vulkan_debug;
global VulkanContext vulkan_context[1];
@@ -337,6 +350,32 @@ vk_renderdoc_instance_handle(void)
}
#endif
+#if BEAMFORMER_DEBUG
+#define vk_label_object(k, h, label, extra) vk_label_object_(VK_OBJECT_TYPE_##k, (u64)h, label, extra)
+function void
+vk_label_object_(VkObjectType kind, u64 handle, s8 label, s8 extra)
+{
+ local_persist u8 buffer[1024];
+ Stream sb = arena_stream(arena_from_memory(buffer, sizeof(buffer)));
+ if (vulkan_debug.instance.debug_utils && label.len > 0) {
+ stream_append_s8s(&sb, label, s8(" ("), extra, s8(")"));
+ stream_append_byte(&sb, 0);
+ if (!sb.errors) {
+ VkDebugUtilsObjectNameInfoEXT object_name_info = {
+ .sType = VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT,
+ .objectType = kind,
+ .objectHandle = handle,
+ .pObjectName = (char *)sb.data,
+ };
+ vkSetDebugUtilsObjectNameEXT(vulkan_context->device, &object_name_info);
+ }
+ }
+}
+#else
+#define vk_label_object(...)
+#define vk_label_object_(...)
+#endif
+
function VulkanEntity *
vk_entity_allocate(VulkanEntityKind kind)
{
@@ -880,6 +919,7 @@ vk_buffer_allocate_common(VulkanBuffer *vb, VulkanBufferAllocateInfo *ai)
buffer_create_info.usage |= VK_BUFFER_USAGE_INDEX_BUFFER_BIT;
vkCreateBuffer(vk->device, &buffer_create_info, 0, &vb->buffer);
+ vk_label_object(BUFFER, vb->buffer, ai->label, s8("Buffer"));
VkMemoryRequirements memory_requirements;
vkGetBufferMemoryRequirements(vk->device, vb->buffer, &memory_requirements);
@@ -914,6 +954,8 @@ vk_buffer_allocate_common(VulkanBuffer *vb, VulkanBufferAllocateInfo *ai)
vb->index_type = ai->index_type;
+ vk_label_object(DEVICE_MEMORY, vb->memory, ai->label, s8("Memory"));
+
if (host_read_write)
vkMapMemory(vk->device, vb->memory, 0, size, 0, &vb->host_pointer);
@@ -928,12 +970,85 @@ vk_buffer_allocate_common(VulkanBuffer *vb, VulkanBufferAllocateInfo *ai)
}
function void
-vk_load_instance(void)
+vk_load_instance(Arena arena, Stream *err)
{
#define X(name, ...) name = (name##_fn *)vkGetInstanceProcAddr(0, #name);
VkBaseProcedureList
#undef X
+ s8 validation_layers[] = {
+ #if BEAMFORMER_DEBUG
+ s8_comp("VK_LAYER_KHRONOS_validation"),
+ #endif
+ };
+
+ u32 enabled_validation_layers_count = 0;
+ const char *enabled_validation_layers[countof(validation_layers)];
+
+ u32 enabled_instance_extensions_count = 0;
+ const char *enabled_instance_extensions[countof(vk_required_instance_extensions) + countof(vk_instance_debug_extensions)];
+
+ static_assert(countof(vk_required_instance_extensions) == 0, "");
+ //for EachElement(vk_required_instance_extensions, it)
+ // enabled_instance_extensions[enabled_instance_extensions_count++] = vk_required_instance_extensions[it];
+
+ #if BEAMFORMER_DEBUG
+ {
+ u32 layer_count = 0;
+ vkEnumerateInstanceLayerProperties(&layer_count, 0);
+
+ VkLayerProperties *layers = push_array(&arena, VkLayerProperties, layer_count);
+ s8 *layer_s8s = push_array(&arena, s8, layer_count);
+ vkEnumerateInstanceLayerProperties(&layer_count, layers);
+
+ for (u32 i = 0; i < layer_count; i++)
+ layer_s8s[i] = c_str_to_s8(layers[i].layerName);
+
+ b32 supported_layers[countof(validation_layers)] = {0};
+ for EachElement(validation_layers, it) {
+ for(u32 i = 0; i < layer_count; i++) {
+ if (s8_equal(validation_layers[it], layer_s8s[i])) {
+ u32 index = enabled_validation_layers_count++;
+ enabled_validation_layers[index] = (char *)validation_layers[it].data;
+ supported_layers[it] = 1;
+ break;
+ }
+ }
+ }
+
+ if (countof(validation_layers) != enabled_validation_layers_count) {
+ i32 missing_count = countof(validation_layers) - enabled_validation_layers_count;
+ stream_append_s8s(err, vulkan_info("missing validation layer"),
+ missing_count > 1 ? s8("s:") : s8(":"), s8("\n"));
+
+ for EachElement(validation_layers, it) {
+ if (supported_layers[it] == 0)
+ stream_append_s8s(err, s8(" "), validation_layers[it], s8("\n"));
+ }
+ }
+
+ u32 instance_extension_count = 0;
+ vkEnumerateInstanceExtensionProperties(0, &instance_extension_count, 0);
+
+ VkExtensionProperties *instance_extensions = push_array(&arena, VkExtensionProperties, instance_extension_count);
+ s8 *instance_ext_s8s = push_array(&arena, s8, instance_extension_count);
+ vkEnumerateInstanceExtensionProperties(0, &instance_extension_count, instance_extensions);
+ for EachIndex(instance_extension_count, it)
+ instance_ext_s8s[it] = c_str_to_s8(instance_extensions[it].extensionName);
+
+ for EachElement(vk_instance_debug_extensions, it) {
+ for EachIndex(instance_extension_count, i) {
+ if (s8_equal(vk_instance_debug_extensions[it], instance_ext_s8s[i])) {
+ u32 index = enabled_instance_extensions_count++;
+ enabled_instance_extensions[index] = (char *)vk_instance_debug_extensions[it].data;
+ vulkan_debug.instance.E[it] = 1;
+ break;
+ }
+ }
+ }
+ }
+ #endif
+
VkApplicationInfo app_info = {
.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO,
.pApplicationName = BEAMFORMER_NAME_STRING,
@@ -943,20 +1058,13 @@ vk_load_instance(void)
.apiVersion = VK_MAKE_API_VERSION(1, 3, 0, 0),
};
- /* TODO(rnp): debug only, and check for these before enabling */
- const char *validation_layers[] = {
- #if BEAMFORMER_DEBUG
- "VK_LAYER_KHRONOS_validation",
- #endif
- };
-
VkInstanceCreateInfo instance_create_info = {
.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO,
.pApplicationInfo = &app_info,
- .ppEnabledExtensionNames = vk_required_instance_extensions,
- .enabledExtensionCount = countof(vk_required_instance_extensions),
- .ppEnabledLayerNames = validation_layers,
- .enabledLayerCount = countof(validation_layers),
+ .ppEnabledExtensionNames = enabled_instance_extensions,
+ .enabledExtensionCount = enabled_instance_extensions_count,
+ .ppEnabledLayerNames = enabled_validation_layers,
+ .enabledLayerCount = enabled_validation_layers_count,
};
#if 0 && BEAMFORMER_DEBUG
@@ -1613,9 +1721,9 @@ vk_load(OSLibrary vulkan_library_handle, Arena *memory, Stream *err)
vk->entity_arena = sub_arena_end(memory, KB(64), KB(4));
vk->arena = sub_arena_end(memory, KB(96), KB(4));
- vk_load_instance();
- vk_load_physical_device(vulkan_context->arena, err);
- vk_load_queues(&vulkan_context->arena, err);
+ vk_load_instance(vk->arena, err);
+ vk_load_physical_device(vk->arena, err);
+ vk_load_queues(&vk->arena, err);
vk_load_graphics();
vk_load_descriptor_block();
@@ -1926,7 +2034,7 @@ vk_image_release(GPUImage *image)
DEBUG_IMPORT void
vk_image_allocate(GPUImage *image, u32 width, u32 height, u32 mips, u32 samples,
- VulkanImageUsage usage, VulkanUsageFlags flags, OSHandle *export)
+ VulkanImageUsage usage, VulkanUsageFlags flags, OSHandle *export, s8 label)
{
assert(IsPowerOfTwo(samples));
@@ -2026,6 +2134,10 @@ vk_image_allocate(GPUImage *image, u32 width, u32 height, u32 mips, u32 samples,
},
};
vkCreateImageView(vk->device, &image_view_info, 0, &vi->view);
+
+ vk_label_object(IMAGE, vi->image, label, s8("Image"));
+ vk_label_object(IMAGE_VIEW, vi->view, label, s8("Image View"));
+ vk_label_object(DEVICE_MEMORY, vi->memory, label, s8("Memory"));
} else {
vkDestroyImage(vk->device, vi->image, 0);
vk_entity_release(e);
diff --git a/vulkan.h b/vulkan.h
@@ -122,6 +122,7 @@ typedef enum {
VK_STRUCTURE_TYPE_SEMAPHORE_GET_WIN32_HANDLE_INFO_KHR = 1000078003,
VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR = 1000079001,
VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO = 1000127001,
+ VK_STRUCTURE_TYPE_DEBUG_UTILS_OBJECT_NAME_INFO_EXT = 1000128000,
VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO = 1000207002,
VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO = 1000207003,
VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO = 1000207004,
@@ -142,6 +143,68 @@ typedef enum {
} VkStructureType;
typedef enum {
+ VK_OBJECT_TYPE_UNKNOWN = 0,
+ VK_OBJECT_TYPE_INSTANCE = 1,
+ VK_OBJECT_TYPE_PHYSICAL_DEVICE = 2,
+ VK_OBJECT_TYPE_DEVICE = 3,
+ VK_OBJECT_TYPE_QUEUE = 4,
+ VK_OBJECT_TYPE_SEMAPHORE = 5,
+ VK_OBJECT_TYPE_COMMAND_BUFFER = 6,
+ VK_OBJECT_TYPE_FENCE = 7,
+ VK_OBJECT_TYPE_DEVICE_MEMORY = 8,
+ VK_OBJECT_TYPE_BUFFER = 9,
+ VK_OBJECT_TYPE_IMAGE = 10,
+ VK_OBJECT_TYPE_EVENT = 11,
+ VK_OBJECT_TYPE_QUERY_POOL = 12,
+ VK_OBJECT_TYPE_BUFFER_VIEW = 13,
+ VK_OBJECT_TYPE_IMAGE_VIEW = 14,
+ VK_OBJECT_TYPE_SHADER_MODULE = 15,
+ VK_OBJECT_TYPE_PIPELINE_CACHE = 16,
+ VK_OBJECT_TYPE_PIPELINE_LAYOUT = 17,
+ VK_OBJECT_TYPE_RENDER_PASS = 18,
+ VK_OBJECT_TYPE_PIPELINE = 19,
+ VK_OBJECT_TYPE_DESCRIPTOR_SET_LAYOUT = 20,
+ VK_OBJECT_TYPE_SAMPLER = 21,
+ VK_OBJECT_TYPE_DESCRIPTOR_POOL = 22,
+ VK_OBJECT_TYPE_DESCRIPTOR_SET = 23,
+ VK_OBJECT_TYPE_FRAMEBUFFER = 24,
+ VK_OBJECT_TYPE_COMMAND_POOL = 25,
+ VK_OBJECT_TYPE_DESCRIPTOR_UPDATE_TEMPLATE = 1000085000,
+ VK_OBJECT_TYPE_SAMPLER_YCBCR_CONVERSION = 1000156000,
+ VK_OBJECT_TYPE_PRIVATE_DATA_SLOT = 1000295000,
+ VK_OBJECT_TYPE_SURFACE_KHR = 1000000000,
+ VK_OBJECT_TYPE_SWAPCHAIN_KHR = 1000001000,
+ VK_OBJECT_TYPE_DISPLAY_KHR = 1000002000,
+ VK_OBJECT_TYPE_DISPLAY_MODE_KHR = 1000002001,
+ VK_OBJECT_TYPE_DEBUG_REPORT_CALLBACK_EXT = 1000011000,
+ VK_OBJECT_TYPE_VIDEO_SESSION_KHR = 1000023000,
+ VK_OBJECT_TYPE_VIDEO_SESSION_PARAMETERS_KHR = 1000023001,
+ VK_OBJECT_TYPE_CU_MODULE_NVX = 1000029000,
+ VK_OBJECT_TYPE_CU_FUNCTION_NVX = 1000029001,
+ VK_OBJECT_TYPE_DEBUG_UTILS_MESSENGER_EXT = 1000128000,
+ VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_KHR = 1000150000,
+ VK_OBJECT_TYPE_VALIDATION_CACHE_EXT = 1000160000,
+ VK_OBJECT_TYPE_ACCELERATION_STRUCTURE_NV = 1000165000,
+ VK_OBJECT_TYPE_PERFORMANCE_CONFIGURATION_INTEL = 1000210000,
+ VK_OBJECT_TYPE_DEFERRED_OPERATION_KHR = 1000268000,
+ VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_NV = 1000277000,
+ VK_OBJECT_TYPE_CUDA_MODULE_NV = 1000307000,
+ VK_OBJECT_TYPE_CUDA_FUNCTION_NV = 1000307001,
+ VK_OBJECT_TYPE_BUFFER_COLLECTION_FUCHSIA = 1000366000,
+ VK_OBJECT_TYPE_MICROMAP_EXT = 1000396000,
+ VK_OBJECT_TYPE_TENSOR_ARM = 1000460000,
+ VK_OBJECT_TYPE_TENSOR_VIEW_ARM = 1000460001,
+ VK_OBJECT_TYPE_OPTICAL_FLOW_SESSION_NV = 1000464000,
+ VK_OBJECT_TYPE_SHADER_EXT = 1000482000,
+ VK_OBJECT_TYPE_PIPELINE_BINARY_KHR = 1000483000,
+ VK_OBJECT_TYPE_DATA_GRAPH_PIPELINE_SESSION_ARM = 1000507000,
+ VK_OBJECT_TYPE_EXTERNAL_COMPUTE_QUEUE_NV = 1000556000,
+ VK_OBJECT_TYPE_INDIRECT_COMMANDS_LAYOUT_EXT = 1000572000,
+ VK_OBJECT_TYPE_INDIRECT_EXECUTION_SET_EXT = 1000572001,
+ VK_OBJECT_TYPE_MAX_ENUM = 0x7FFFFFFF
+} VkObjectType;
+
+typedef enum {
VK_PHYSICAL_DEVICE_TYPE_OTHER = 0,
VK_PHYSICAL_DEVICE_TYPE_INTEGRATED_GPU = 1,
VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU = 2,
@@ -2935,6 +2998,13 @@ typedef struct {
const VkValidationFeatureDisableEXT * pDisabledValidationFeatures;
} VkValidationFeaturesEXT;
+typedef struct {
+ VkStructureType sType;
+ const void * pNext;
+ VkObjectType objectType;
+ uint64_t objectHandle;
+ const char * pObjectName;
+} VkDebugUtilsObjectNameInfoEXT;
/* X(name, ret, params) */
#define VkLoaderProcedureList \
@@ -2942,7 +3012,9 @@ typedef struct {
/* X(name, ret, params) */
#define VkBaseProcedureList \
- X(vkCreateInstance, VkResult, (const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance)) \
+ X(vkCreateInstance, VkResult, (const VkInstanceCreateInfo *pCreateInfo, const VkAllocationCallbacks *pAllocator, VkInstance *pInstance)) \
+ X(vkEnumerateInstanceExtensionProperties, VkResult, (const char *pLayerName, uint32_t *pPropertyCount, VkExtensionProperties *pProperties)) \
+ X(vkEnumerateInstanceLayerProperties, VkResult, (uint32_t *pPropertyCount, VkLayerProperties *pProperties)) \
/* X(name, ret, params) */
#define VkInstanceProcedureList \
@@ -2995,6 +3067,7 @@ typedef struct {
X(vkGetSemaphoreWin32HandleKHR, VkResult, (VkDevice device, const VkSemaphoreGetWin32HandleInfoKHR *pGetWin32HandleInfo, void **pHandle)) \
X(vkInvalidateMappedMemoryRanges, VkResult, (VkDevice device, uint32_t memoryRangeCount, const VkMappedMemoryRange *pMemoryRanges)) \
X(vkMapMemory, VkResult, (VkDevice device, VkDeviceMemory memory, VkDeviceSize offset, VkDeviceSize size, VkMemoryMapFlags flags, void **ppData)) \
+ X(vkSetDebugUtilsObjectNameEXT, VkResult, (VkDevice device, const VkDebugUtilsObjectNameInfoEXT *pNameInfo)) \
X(vkSignalSemaphore, VkResult, (VkDevice device, const VkSemaphoreSignalInfo *pSignalInfo)) \
X(vkUnmapMemory, void, (VkDevice device, VkDeviceMemory memory)) \
X(vkUpdateDescriptorSets, void, (VkDevice device, uint32_t descriptorWriteCount, const VkWriteDescriptorSet *pDescriptorWrites, uint32_t descriptorCopyCount, const VkCopyDescriptorSet *pDescriptorCopies)) \