ogl_beamforming

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

Commit: 8d401201b9f05b3c786f9454c3e496d9087af8d7
Parent: 856af6bf55ff5fffee540fcdff7d8d0c93f88531
Author: Randy Palamar
Date:   Wed, 26 Aug 2026 18:19:40 -0700

gpu: further barrier simplification

turns out these memory barriers are doing literally nothing
useful. COMPUTE->COMPUTE does not need a memory barrier since the
LLC is coherent on all modern GPUs. The barrier is needed for
execution ordering only. The driver does actually need to
invalidate if you tell it to because it has no idea what memory
you are referring to. If it is descriptor heap memory then the
texture units, which have non-coherent caches, need to be made
aware. But that is pretty much the only case. So in our case they
can just be removed.

Diffstat:
Mbeamformer_core.c | 10+++++-----
Mbeamformer_internal.h | 2+-
Mvulkan.c | 19+++++++++++++------
3 files changed, 19 insertions(+), 12 deletions(-)

diff --git a/beamformer_core.c b/beamformer_core.c @@ -1278,7 +1278,7 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; else pc.output_buffer = pp_output_pointer; - gpu_command_pipeline_barrier(cmd); + gpu_command_pipeline_barrier(cmd, 0); gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); gpu_command_dispatch_compute(cmd, dispatch); @@ -1301,7 +1301,7 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan else pc.output_buffer = pp_output_pointer; if (shader_slot != 0 || (shader_slot + 1) == das_index) - gpu_command_pipeline_barrier(cmd); + gpu_command_pipeline_barrier(cmd, 0); gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); gpu_command_dispatch_compute(cmd, dispatch); @@ -1319,14 +1319,14 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan memory_copy(pc.voxel_transform.E, cp->das_voxel_transform.E, sizeof(pc.voxel_transform)); memory_copy(pc.xdc_transform.E, cp->xdc_transform.E, sizeof(pc.xdc_transform)); - gpu_command_pipeline_barrier(cmd); + gpu_command_pipeline_barrier(cmd, 0); gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); gpu_command_dispatch_compute(cmd, dispatch); }break; case BeamformerShaderKind_CoherencyWeighting:{ BeamformerCoherencyWeightingPushConstants pc = {.coherent_sum = frame->gpu_pointer}; - gpu_command_pipeline_barrier(cmd); + gpu_command_pipeline_barrier(cmd, 0); gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); gpu_command_dispatch_compute(cmd, dispatch); }break; @@ -1344,7 +1344,7 @@ do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan if ((shader_slot + 1) == das_index) pc.output_buffer = pp_das_pointer; else pc.output_buffer = pp_output_pointer; - gpu_command_pipeline_barrier(cmd); + gpu_command_pipeline_barrier(cmd, 0); gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); gpu_command_dispatch_compute(cmd, dispatch); diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -173,7 +173,7 @@ DEBUG_IMPORT GPUCommandList gpu_command_list_begin(GPUTimeline timeline); DEBUG_IMPORT u64 gpu_command_list_end(GPUCommandList command, VulkanHandle wait_semaphore, VulkanHandle finished_semaphore); DEBUG_IMPORT void gpu_command_bind_pipeline(GPUCommandList command, VulkanHandle pipeline); -DEBUG_IMPORT void gpu_command_pipeline_barrier(GPUCommandList command); +DEBUG_IMPORT void gpu_command_pipeline_barrier(GPUCommandList command, b32 memory); DEBUG_IMPORT void gpu_command_clear_buffer(GPUCommandList command, GPUBuffer *buffer, u64 offset, u64 size, u32 clear_word); DEBUG_IMPORT void gpu_command_dispatch_compute(GPUCommandList command, uv3 dispatch); DEBUG_IMPORT void gpu_command_push_constants(GPUCommandList command, u32 offset, u32 size, void *values); diff --git a/vulkan.c b/vulkan.c @@ -2408,21 +2408,28 @@ gpu_command_bind_pipeline(GPUCommandList command, VulkanHandle pipeline) } DEBUG_IMPORT void -gpu_command_pipeline_barrier(GPUCommandList command) +gpu_command_pipeline_barrier(GPUCommandList command, b32 memory) { if (command.value) { VulkanContext *vk = vulkan_context; VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanQueue *vq = vk->queues[vcb->timeline]; + // TODO(rnp): this is not really specific enough + b32 needs_memory = memory || (vq->pipeline_stage_flags != VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT); + VkMemoryBarrier2 memory_barrier = { - .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, - .srcStageMask = vq->pipeline_stage_flags, - .srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT, - .dstStageMask = vq->pipeline_stage_flags, - .dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT, + .sType = VK_STRUCTURE_TYPE_MEMORY_BARRIER_2, + .srcStageMask = vq->pipeline_stage_flags, + .dstStageMask = vq->pipeline_stage_flags, }; + // NOTE(rnp): COMPUTE->COMPUTE does not need memory guards. LLC is coherent on modern GPUs. + if (needs_memory) { + memory_barrier.srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; + memory_barrier.dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT; + } + VkDependencyInfo dependency_info = { .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, .pMemoryBarriers = &memory_barrier,