ogl_beamforming

Ultrasound Beamforming Implemented with OpenGL
git clone anongit@rnpnr.xyz:ogl_beamforming.git
Log | Files | Refs | Feed | Submodules | README | LICENSE

Commit: a9a36a8a15ec517a301257810a56b8149120f5fb
Parent: e0bd75572cdba7626c8dcf6d2fb87f8aa77660f7
Author: Randy Palamar
Date:   Thu,  5 Mar 2026 09:07:44 -0700

vulkan: add object labeling in debug builds

Diffstat:
Mbeamformer_internal.h | 2+-
Mui.c | 10+++++-----
Mvulkan.c | 150+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Mvulkan.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)) \