ogl_beamforming

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

Commit: c8af5968f1b5fe9ea82759c147b893254f4d4649
Parent: 69799d54a9fddad0670eb6b776f79926282775c6
Author: Randy Palamar
Date:   Mon, 10 Aug 2026 05:43:08 -0700

gpu: rename interface from vk to gpu

This isn't really meant to be exclusively for vulkan from the
beamformer's perspective.

Diffstat:
Mbeamformer.c | 6+++---
Mbeamformer_core.c | 85+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mbeamformer_internal.h | 65++++++++++++++++++++++++++++++++++-------------------------------
Mui.c | 46+++++++++++++++++++++++-----------------------
Mvulkan.c | 309++++++++++++++++++++++++++++++++++++++++---------------------------------------
Mvulkan.h | 2++
6 files changed, 260 insertions(+), 253 deletions(-)

diff --git a/beamformer.c b/beamformer.c @@ -96,7 +96,7 @@ beamformer_load_cuda_library(BeamformerCtx *ctx, OSLibrary cuda, Arena *scratch) /* TODO(rnp): (25.10.30) registering the rf buffer with CUDA is currently * causing a major performance regression. for now we are disabling its use * altogether. it will be reenabled once the issue can be fixed */ - b32 result = 0 && vk_gpu_info()->vendor == GPUVendor_NVIDIA && ValidHandle(cuda); + b32 result = 0 && gpu_info()->vendor == GPUVendor_NVIDIA && ValidHandle(cuda); if (result) { Stream err = arena_stream(scratch); @@ -194,7 +194,7 @@ beamformer_init(BeamformerInput *input) // NOTE(rnp): allocate beamformed image ring buffer { - u64 gpu_heap_size = vk_gpu_info()->gpu_heap_size; + u64 gpu_heap_size = gpu_info()->gpu_heap_size; u64 trial_sizes[] = { GB(4), GB(2), @@ -211,7 +211,7 @@ beamformer_init(BeamformerInput *input) for (u32 i = base_index; i < countof(trial_sizes); i++) { // TODO(rnp): it may be better to download data from this using the transfer queue - VulkanTimeline timelines[] = {VulkanTimeline_Compute, VulkanTimeline_Graphics}; + GPUTimeline timelines[] = {GPUTimeline_Compute, GPUTimeline_Graphics}; GPUBufferAllocateInfo allocate_info = { .size = trial_sizes[i], .flags = VulkanUsageFlag_TransferDestination|VulkanUsageFlag_TransferSource|VulkanUsageFlag_HostReadWrite, diff --git a/beamformer_core.c b/beamformer_core.c @@ -325,7 +325,7 @@ layout_for_output(iv3 points) b32 has_y = points.y > 1; b32 has_z = points.z > 1; - u32 subgroup_size = vk_gpu_info()->subgroup_size; + u32 subgroup_size = gpu_info()->subgroup_size; u32 grid_3d_z_size = Max(1, subgroup_size / (4 * 4)); u32 grid_2d_y_size = Max(1, subgroup_size / 8); @@ -488,7 +488,7 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A switch (pb->pipeline.shaders[it]) { case BeamformerShaderKind_Decode:{ b32 low_precision = beamformer_data_kind_element_size[input_data_kind] < 4; - b32 use_coop_matrix = vk_gpu_info()->cooperative_matrix && + b32 use_coop_matrix = gpu_info()->cooperative_matrix && low_precision && (acquisition_count % 16 == 0) && (chunk_channel_count % 16 == 0); @@ -584,7 +584,7 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A graph.last->output_data_kind = graph.last->input_data_kind; f32 time_offset = pb->parameters.time_offset; - u32 subgroup_size = vk_gpu_info()->subgroup_size; + u32 subgroup_size = gpu_info()->subgroup_size; cp->first_image_shader_index = 0; cp->pipeline.shader_count = 0; @@ -616,7 +616,7 @@ plan_compute_pipeline(BeamformerComputePlan *cp, BeamformerParameterBlock *pb, A db->ToProcess = 1; - b32 use_coop_matrix = vk_gpu_info()->cooperative_matrix && + b32 use_coop_matrix = gpu_info()->cooperative_matrix && node->input_data_kind == BeamformerDataKind_Float16 && (db->TransmitCount % 16 == 0) && (chunk_channel_count % 16 == 0); @@ -978,7 +978,7 @@ beamformer_reload_compute_pipeline(VulkanHandle *pipeline, BeamformerShaderKind BeamformerShaderDescriptor *shader_descriptor, Arena *scratch) { i32 index = beamformer_shader_reloadable_index_by_shader[shader]; - uv3 layout = shader_descriptor ? shader_descriptor->layout : (uv3){{vk_gpu_info()->subgroup_size, 1, 1}}; + uv3 layout = shader_descriptor ? shader_descriptor->layout : (uv3){{gpu_info()->subgroup_size, 1, 1}}; BeamformerShaderReloadInfo info = { .shader = shader, .shader_kind = VulkanShaderKind_Compute, @@ -1058,7 +1058,7 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, cp->hadamard_order != (i32)cp->acquisition_count) { beamformer_update_hadamard(cp, BeamformerComputeArrayParametersField_DecodeHadamard, - cp->acquisition_count, vk_gpu_info()->cooperative_matrix, scratch); + cp->acquisition_count, gpu_info()->cooperative_matrix, scratch); if (pb->parameters.readi_group_count > 1) beamformer_update_hadamard(cp, BeamformerComputeArrayParametersField_DASHadamard, pb->parameters.readi_group_count, 0, scratch); @@ -1108,8 +1108,8 @@ beamformer_commit_parameter_block(BeamformerCtx *ctx, BeamformerComputePlan *cp, } function void -do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *cp, BeamformerFrame *frame, - u32 shader_slot, u32 channel_offset, u64 rf_pointer) +do_compute_shader(BeamformerCtx *ctx, GPUCommandList cmd, BeamformerComputePlan *cp, + BeamformerFrame *frame, u32 shader_slot, u32 channel_offset, u64 rf_pointer) { BeamformerComputeContext *cc = &ctx->compute_context; @@ -1126,7 +1126,7 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c uv3 dispatch = cp->shader_descriptors[shader_slot].dispatch; - vk_command_bind_pipeline(cmd, cp->vulkan_pipelines[shader_slot]); + gpu_command_bind_pipeline(cmd, cp->vulkan_pipelines[shader_slot]); switch (cp->pipeline.shaders[shader_slot]) { @@ -1158,9 +1158,9 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c if (shader_slot + 1 == das_index) barrier_count++; - vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); - vk_command_push_constants(cmd, 0, sizeof(pc), &pc); - vk_command_dispatch_compute(cmd, dispatch); + gpu_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); + gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); + gpu_command_dispatch_compute(cmd, dispatch); cc->ping_pong_input_index = !cc->ping_pong_input_index; }break; @@ -1212,10 +1212,10 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c barrier_count--; if (barrier_count) - vk_command_buffer_memory_barriers(cmd, barriers, barrier_count); + gpu_command_buffer_memory_barriers(cmd, barriers, barrier_count); - vk_command_push_constants(cmd, 0, sizeof(pc), &pc); - vk_command_dispatch_compute(cmd, dispatch); + gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); + gpu_command_dispatch_compute(cmd, dispatch); cc->ping_pong_input_index = !cc->ping_pong_input_index; }break; @@ -1247,8 +1247,7 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c b32 coherent = (cp->shader_descriptors[shader_slot].compile_flags & BeamformerDASCompileFlags_CoherencyWeighting) != 0; - GPUMemoryBarrierInfo memory_barriers[] = { - // NOTE(rnp): last stage data output barrier + GPUMemoryBarrierInfo barrier_infos[] = { { .gpu_buffer = &cc->ping_pong_buffer, .offset = pp_das_pointer - cc->ping_pong_buffer.gpu_pointer, @@ -1267,12 +1266,12 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c }, }; - u32 barrier_count = countof(memory_barriers); + u32 barrier_count = countof(barrier_infos); if (!coherent) barrier_count--; - vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); - vk_command_push_constants(cmd, 0, sizeof(pc), &pc); - vk_command_dispatch_compute(cmd, dispatch); + gpu_command_buffer_memory_barriers(cmd, barrier_infos, barrier_count); + gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); + gpu_command_dispatch_compute(cmd, dispatch); }break; case BeamformerShaderKind_CoherencyWeighting:{ @@ -1303,9 +1302,9 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c }, }; - vk_command_buffer_memory_barriers(cmd, memory_barriers, countof(memory_barriers)); - vk_command_push_constants(cmd, 0, sizeof(pc), &pc); - vk_command_dispatch_compute(cmd, dispatch); + gpu_command_buffer_memory_barriers(cmd, memory_barriers, countof(memory_barriers)); + gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); + gpu_command_dispatch_compute(cmd, dispatch); }break; case BeamformerShaderKind_Reshape:{ @@ -1340,9 +1339,9 @@ do_compute_shader(BeamformerCtx *ctx, VulkanHandle cmd, BeamformerComputePlan *c if (shader_slot + 1 == das_index) barrier_count++; - vk_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); - vk_command_push_constants(cmd, 0, sizeof(pc), &pc); - vk_command_dispatch_compute(cmd, dispatch); + gpu_command_buffer_memory_barriers(cmd, memory_barriers, barrier_count); + gpu_command_push_constants(cmd, 0, sizeof(pc), &pc); + gpu_command_dispatch_compute(cmd, dispatch); cc->ping_pong_input_index = !cc->ping_pong_input_index; }break; @@ -1437,7 +1436,7 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) // so we either need to count the total size of all requested frames first or // just fill up as much as possible. if (exported_size + frame_size <= ec->size) { - vk_host_wait_timeline(VulkanTimeline_Compute, f->timeline_valid_value, -1ULL); + gpu_host_wait_timeline(GPUTimeline_Compute, f->timeline_valid_value, -1ULL); vk_buffer_range_download(sm_output + exported_size, bl->buffer, f->buffer_offset, frame_size, 1); exported_size += frame_size; } @@ -1531,17 +1530,17 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) frame->view_plane_tag = work->compute_context.view_plane; memory_copy(frame->voxel_transform.E, cp->voxel_transform.E, sizeof(cp->voxel_transform)); - VulkanHandle cmd = vk_command_begin(VulkanTimeline_Compute); - vk_command_timestamp(cmd); + GPUCommandList cmd = gpu_command_list_begin(GPUTimeline_Compute); + gpu_command_timestamp(cmd); if (das_index >= 0) { u64 frame_size = beamformer_frame_byte_size(frame->points, frame->data_kind); GPUBuffer *backlog = cs->backlog.buffer; - vk_command_clear_buffer(cmd, backlog, frame->buffer_offset, frame_size, 0); + gpu_command_clear_buffer(cmd, backlog, frame->buffer_offset, frame_size, 0); if (das_coherent) { u64 coherent_size = frame_size / beamformer_data_kind_element_count[frame->data_kind]; - vk_command_clear_buffer(cmd, backlog, backlog->size - coherent_size, coherent_size, 0); + gpu_command_clear_buffer(cmd, backlog, backlog->size - coherent_size, coherent_size, 0); } } @@ -1557,7 +1556,7 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) /* NOTE(rnp): if the GPU supports BAR there may be no need to synchronize * other than the above spin */ if (vk_buffer_needs_sync(&rf->buffer)) - vk_command_wait_timeline(cmd, VulkanTimeline_Transfer, rf->upload_complete_values[slot]); + gpu_command_wait_timeline(cmd, GPUTimeline_Transfer, rf->upload_complete_values[slot]); } else { slot = (rf->compute_index - 1) % countof(rf->upload_complete_values); } @@ -1570,16 +1569,15 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) rf_pointer += cp->raw_channel_byte_stride * channel_offset; for (u32 i = 0; i < cp->first_image_shader_index; i++) { do_compute_shader(ctx, cmd, cp, frame, i, channel_offset, rf_pointer); - vk_command_timestamp(cmd); + gpu_command_timestamp(cmd); } } for (u32 i = cp->first_image_shader_index; i < cp->pipeline.shader_count; i++) { do_compute_shader(ctx, cmd, cp, frame, i, 0, 0); - vk_command_timestamp(cmd); + gpu_command_timestamp(cmd); } - - u64 end_timeline_value = vk_command_end(cmd, (VulkanHandle){0}, (VulkanHandle){0}); + u64 end_timeline_value = gpu_command_list_end(cmd, (VulkanHandle){0}, (VulkanHandle){0}); if (work->kind == BeamformerWorkKind_ComputeIndirect) { atomic_store_u64(rf->compute_complete_values + slot, end_timeline_value); atomic_add_u64(&rf->compute_index, 1); @@ -1591,14 +1589,15 @@ complete_queue(BeamformerCtx *ctx, BeamformWorkQueue *q, Arena *arena) DeferLoop(scratch = temp_begin(arena), temp_end(scratch)) { /* NOTE(rnp): this blocks until work completes */ - u64 *timestamps = vk_command_read_timestamps(VulkanTimeline_Compute, arena); + u64 count = 0; + u64 *timestamps = gpu_read_timestamps(GPUTimeline_Compute, &count, arena); i32 steps = ((i32)cp->channel_count / BeamformerChunkChannelCount) - 1; i32 step = 0; u32 shader_index = 0; - u64 last_time = timestamps[0] > 0 ? timestamps[1] : 0; + u64 last_time = count > 0 ? timestamps[0] : 0; - for (u64 i = 2; i < timestamps[0] + 1; i++) { + for (u64 i = 1; i < count; i++) { push_compute_timing_info(ctx->compute_timing_table, (ComputeTimingInfo){ .kind = ComputeTimingInfoKind_Shader, .shader = cp->pipeline.shaders[shader_index], @@ -1650,7 +1649,7 @@ coalesce_timing_table(ComputeTimingTable *t, ComputeShaderStats *stats) u32 stats_index = stats->latest_frame_index; b32 has_rf = 0; - f32 gpu_clocks_to_nano = 1.0e-9f * vk_gpu_info()->timestamp_period_ns; + f32 gpu_clocks_to_nano = 1.0e-9f * gpu_info()->timestamp_period_ns; // NOTE(rnp): not equal (the index may wrap) while (t->read_index != target) { @@ -1741,7 +1740,7 @@ DEBUG_EXPORT BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload) /* NOTE(rnp): don't overwrite slot if the compute thread hasn't processed it */ spin_wait(atomic_load_u64(&rf->compute_index) < rf->insertion_index); - vk_host_wait_timeline(VulkanTimeline_Compute, rf->compute_complete_values[slot], -1ULL); + gpu_host_wait_timeline(GPUTimeline_Compute, rf->compute_complete_values[slot], -1ULL); vk_buffer_range_upload(&rf->buffer, beamformer_shared_memory_data_pointer(sm, ctx->shared_memory_size), slot * rf->active_rf_size, rf->active_rf_size, 1); @@ -1750,7 +1749,7 @@ DEBUG_EXPORT BEAMFORMER_RF_UPLOAD_FN(beamformer_rf_upload) beamformer_shared_memory_release_lock(ctx->shared_memory, (i32)scratch_lock); post_sync_barrier(ctx->shared_memory, upload_lock); - atomic_store_u64(rf->upload_complete_values + slot, vk_host_signal_timeline(VulkanTimeline_Transfer)); + atomic_store_u64(rf->upload_complete_values + slot, gpu_host_signal_timeline(GPUTimeline_Transfer)); atomic_add_u64(&rf->insertion_index, 1); os_wake_all_waiters(ctx->compute_worker_sync); diff --git a/beamformer_internal.h b/beamformer_internal.h @@ -18,14 +18,17 @@ #define os_path_separator() (str8){.data = &os_system_info()->path_separator_byte, .length = 1} +typedef struct { u64 value; } GPUCommandList; +typedef struct { u64 value; } GPUSplitBarrier; + typedef struct { u64 value[1]; } VulkanHandle; typedef enum { - VulkanTimeline_Graphics, - VulkanTimeline_Compute, - VulkanTimeline_Transfer, - VulkanTimeline_Count, -} VulkanTimeline; + GPUTimeline_Graphics, + GPUTimeline_Compute, + GPUTimeline_Transfer, + GPUTimeline_Count, +} GPUTimeline; typedef enum { VulkanShaderKind_Vertex, @@ -112,7 +115,7 @@ typedef struct { VulkanUsageFlags flags; // NOTE(rnp): only required if buffer will be used on multiple timelines - VulkanTimeline *timelines_used; + GPUTimeline *timelines_used; u32 timeline_count; OSHandle *export; @@ -146,7 +149,7 @@ typedef struct { // NOTE: vulkan layer API DEBUG_IMPORT void vk_load(OSLibrary vulkan, Stream *error); -DEBUG_IMPORT GPUInfo *vk_gpu_info(void); +DEBUG_IMPORT GPUInfo *gpu_info(void); DEBUG_IMPORT void vk_buffer_allocate(GPUBuffer *, GPUBufferAllocateInfo *info); DEBUG_IMPORT void vk_buffer_release(GPUBuffer *); @@ -176,31 +179,31 @@ DEBUG_IMPORT b32 vk_buffer_needs_sync(GPUBuffer *); DEBUG_IMPORT VulkanHandle vk_create_semaphore(OSHandle *export); -DEBUG_IMPORT b32 vk_host_wait_timeline(VulkanTimeline timeline, u64 value, u64 timeout_ns); -DEBUG_IMPORT u64 vk_host_signal_timeline(VulkanTimeline timeline); - -DEBUG_IMPORT VulkanHandle vk_command_begin(VulkanTimeline timeline); -DEBUG_IMPORT void vk_command_bind_pipeline(VulkanHandle command, VulkanHandle pipeline); -DEBUG_IMPORT void vk_command_buffer_memory_barriers(VulkanHandle command, GPUMemoryBarrierInfo *barriers, u64 count); -DEBUG_IMPORT void vk_command_clear_buffer(VulkanHandle command, GPUBuffer *buffer, u64 offset, u64 size, u32 clear_word); -DEBUG_IMPORT void vk_command_dispatch_compute(VulkanHandle command, uv3 dispatch); -DEBUG_IMPORT void vk_command_push_constants(VulkanHandle command, u32 offset, u32 size, void *values); -DEBUG_IMPORT void vk_command_timestamp(VulkanHandle command); -DEBUG_IMPORT void vk_command_wait_timeline(VulkanHandle command, VulkanTimeline timeline, u64 value); -// NOTE: extra semaphores only exist for synchronization with OpenGL and will be removed in the future -DEBUG_IMPORT u64 vk_command_end(VulkanHandle command, VulkanHandle wait_semaphore, VulkanHandle finished_semaphore); - -DEBUG_IMPORT void vk_command_begin_rendering(VulkanHandle command, GPUImage *restrict colour, GPUImage *restrict depth, GPUImage *restrict resolve); -DEBUG_IMPORT void vk_command_draw(VulkanHandle command, GPUBuffer *model); -DEBUG_IMPORT void vk_command_scissor(VulkanHandle command, u32 width, u32 height, u32 x_offset, u32 y_offset); -DEBUG_IMPORT void vk_command_viewport(VulkanHandle command, f32 width, f32 height, f32 x_offset, f32 y_offset, f32 min_depth, f32 max_depth); -DEBUG_IMPORT void vk_command_end_rendering(VulkanHandle command); +DEBUG_IMPORT b32 gpu_host_wait_timeline(GPUTimeline timeline, u64 value, u64 timeout_ns); +DEBUG_IMPORT u64 gpu_host_signal_timeline(GPUTimeline timeline); -DEBUG_IMPORT void vk_command_copy_buffer(VulkanHandle command, GPUBuffer *restrict destination, GPUBuffer *restrict source, u64 source_offset, i64 size); - -// NOTE: returns array of valid timestamps + 1, first element is the count. -// Calling thread may stall until results available. -DEBUG_IMPORT u64 * vk_command_read_timestamps(VulkanTimeline timeline, Arena *arena); +DEBUG_IMPORT GPUCommandList gpu_command_list_begin(GPUTimeline timeline); +// NOTE: extra semaphores only exist for synchronization with OpenGL and will be removed in the future +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_buffer_memory_barriers(GPUCommandList command, GPUMemoryBarrierInfo *barriers, u64 count); +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); +DEBUG_IMPORT void gpu_command_timestamp(GPUCommandList command); +DEBUG_IMPORT void gpu_command_wait_timeline(GPUCommandList command, GPUTimeline timeline, u64 value); + +DEBUG_IMPORT void gpu_command_begin_rendering(GPUCommandList command, GPUImage *restrict colour, GPUImage *restrict depth, GPUImage *restrict resolve); +DEBUG_IMPORT void gpu_command_draw(GPUCommandList command, GPUBuffer *model); +DEBUG_IMPORT void gpu_command_scissor(GPUCommandList command, u32 width, u32 height, u32 x_offset, u32 y_offset); +DEBUG_IMPORT void gpu_command_viewport(GPUCommandList command, f32 width, f32 height, f32 x_offset, f32 y_offset, f32 min_depth, f32 max_depth); +DEBUG_IMPORT void gpu_command_end_rendering(GPUCommandList command); + +DEBUG_IMPORT void gpu_command_copy_buffer(GPUCommandList command, GPUBuffer *restrict destination, GPUBuffer *restrict source, u64 source_offset, i64 size); + +// NOTE: returns array of valid timestamps. Calling thread may stall until results available. +DEBUG_IMPORT u64 * gpu_read_timestamps(GPUTimeline timeline, u64 *count, Arena *arena); #if BEAMFORMER_RENDERDOC_HOOKS DEBUG_IMPORT void * vk_renderdoc_instance_handle(void); diff --git a/ui.c b/ui.c @@ -854,10 +854,10 @@ beamformer_ui_frame_view_copy_frame(BeamformerFrameView *new, BeamformerFrameVie vk_buffer_allocate(&new->copy_buffer, &allocate_info); GPUBuffer *backlog = beamformer_context->compute_context.backlog.buffer; - VulkanHandle cmd = vk_command_begin(VulkanTimeline_Compute); - vk_command_wait_timeline(cmd, VulkanTimeline_Compute, old->frame.timeline_valid_value); - vk_command_copy_buffer(cmd, &new->copy_buffer, backlog, old->frame.buffer_offset, frame_size); - new->frame.timeline_valid_value = vk_command_end(cmd, (VulkanHandle){0}, (VulkanHandle){0}); + GPUCommandList cmd = gpu_command_list_begin(GPUTimeline_Compute); + gpu_command_wait_timeline(cmd, GPUTimeline_Compute, old->frame.timeline_valid_value); + gpu_command_copy_buffer(cmd, &new->copy_buffer, backlog, old->frame.buffer_offset, frame_size); + new->frame.timeline_valid_value = gpu_command_list_end(cmd, (VulkanHandle){0}, (VulkanHandle){0}); } function BeamformerFrameView * @@ -1001,7 +1001,7 @@ x_plane_raycast(BeamformerFrameView *view, BeamformerFrame *frame, v2 uv) function void render_single_xplane(BeamformerFrameView *view, BeamformerFrame *frame, v3 translate, f32 rotation_turns, - VulkanHandle command, BeamformerRenderBeamformedPushConstants *pc, m4 vp_m, b32 drag_plane) + GPUCommandList command, BeamformerRenderBeamformedPushConstants *pc, m4 vp_m, b32 drag_plane) { GPUBuffer *beamformed_buffer = beamformer_context->compute_context.backlog.buffer; pc->input_data = frame->timeline_valid_value ? beamformed_buffer->gpu_pointer + frame->buffer_offset : 0; @@ -1011,9 +1011,9 @@ render_single_xplane(BeamformerFrameView *view, BeamformerFrame *frame, v3 trans pc->data_kind = frame->data_kind; pc->mvp_matrix = m4_mul(vp_m, y_aligned_volume_transform(x_plane_display_size(frame), translate, rotation_turns)); - vk_command_wait_timeline(command, VulkanTimeline_Compute, frame->timeline_valid_value); - vk_command_push_constants(command, 0, sizeof(*pc), pc); - vk_command_draw(command, &ui_context->unit_cube_model.model); + gpu_command_wait_timeline(command, GPUTimeline_Compute, frame->timeline_valid_value); + gpu_command_push_constants(command, 0, sizeof(*pc), pc); + gpu_command_draw(command, &ui_context->unit_cube_model.model); v3 xp_delta = v3_sub(view->hit_test_point, view->hit_start_point); if (drag_plane && !f32_equal(v3_magnitude_squared(xp_delta), 0)) { @@ -1026,13 +1026,13 @@ render_single_xplane(BeamformerFrameView *view, BeamformerFrame *frame, v3 trans pc->bounding_box_fraction = 1.0f; pc->input_data = 0; - vk_command_push_constants(command, 0, sizeof(*pc), pc); - vk_command_draw(command, &ui_context->unit_cube_model.model); + gpu_command_push_constants(command, 0, sizeof(*pc), pc); + gpu_command_draw(command, &ui_context->unit_cube_model.model); } } function void -render_3d_xplane(BeamformerFrameView *view, VulkanHandle command, BeamformerRenderBeamformedPushConstants *pc) +render_3d_xplane(BeamformerFrameView *view, GPUCommandList command, BeamformerRenderBeamformedPushConstants *pc) { if (view->demo) { view->rotation += dt_for_frame * 0.125f; @@ -1068,7 +1068,7 @@ render_3d_xplane(BeamformerFrameView *view, VulkanHandle command, BeamformerRend } function void -render_2d_plane(BeamformerFrameView *view, VulkanHandle command, BeamformerRenderBeamformedPushConstants *pc) +render_2d_plane(BeamformerFrameView *view, GPUCommandList command, BeamformerRenderBeamformedPushConstants *pc) { m4 view_m = m4_identity(); m4 model = m4_scale((v3){{2.0f, 2.0f, 0.0f}}); @@ -1082,9 +1082,9 @@ render_2d_plane(BeamformerFrameView *view, VulkanHandle command, BeamformerRende pc->input_size_z = view->frame.points.z; pc->data_kind = view->frame.data_kind; - vk_command_wait_timeline(command, VulkanTimeline_Compute, view->frame.timeline_valid_value); - vk_command_push_constants(command, 0, sizeof(*pc), pc); - vk_command_draw(command, &ui_context->unit_cube_model.model); + gpu_command_wait_timeline(command, GPUTimeline_Compute, view->frame.timeline_valid_value); + gpu_command_push_constants(command, 0, sizeof(*pc), pc); + gpu_command_draw(command, &ui_context->unit_cube_model.model); } function b32 @@ -1128,18 +1128,18 @@ update_frame_views(BeamformerUI *ui, Rect window) glSignalSemaphoreEXT(ui->render_semaphores_gl[0], 0, 0, 1, &view->texture, (GLenum []){GL_NONE}); - VulkanHandle cmd = vk_command_begin(VulkanTimeline_Graphics); - vk_command_bind_pipeline(cmd, ui->pipelines[BeamformerShaderKind_RenderBeamformed - BeamformerShaderKind_RenderFirst]); - vk_command_begin_rendering(cmd, &ui->render_3d_image, &ui->render_3d_depth_image, &view->colour_image); - vk_command_viewport(cmd, view->colour_image.width, view->colour_image.height, 0, 0, 0.0f, 1.0f); - vk_command_scissor(cmd, view->colour_image.width, view->colour_image.height, 0, 0); + GPUCommandList cmd = gpu_command_list_begin(GPUTimeline_Graphics); + gpu_command_bind_pipeline(cmd, ui->pipelines[BeamformerShaderKind_RenderBeamformed - BeamformerShaderKind_RenderFirst]); + gpu_command_begin_rendering(cmd, &ui->render_3d_image, &ui->render_3d_depth_image, &view->colour_image); + gpu_command_viewport(cmd, view->colour_image.width, view->colour_image.height, 0, 0, 0.0f, 1.0f); + gpu_command_scissor(cmd, view->colour_image.width, view->colour_image.height, 0, 0); if (view->kind == BeamformerFrameViewKind_3DXPlane) { render_3d_xplane(view, cmd, &pc); } else { render_2d_plane(view, cmd, &pc); } - vk_command_end_rendering(cmd); - vk_command_end(cmd, ui->render_semaphores[0], ui->render_semaphores[1]); + gpu_command_end_rendering(cmd); + gpu_command_list_end(cmd, ui->render_semaphores[0], ui->render_semaphores[1]); glWaitSemaphoreEXT(ui->render_semaphores_gl[1], 0, 0, 1, &view->texture, (GLenum[]){GL_LAYOUT_COLOR_ATTACHMENT_EXT}); @@ -5124,7 +5124,7 @@ ui_init(BeamformerCtx *ctx, Arena *store) } } - u32 samples = vk_gpu_info()->max_msaa_samples; + u32 samples = gpu_info()->max_msaa_samples; vk_image_allocate(&ui->render_3d_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_Colour, 0, 0, str8("Render Target Colour")); vk_image_allocate(&ui->render_3d_depth_image, FRAME_VIEW_RENDER_TARGET_SIZE, 1, samples, VulkanImageUsage_DepthStencil, 0, 0, str8("Render Target Depth")); diff --git a/vulkan.c b/vulkan.c @@ -15,7 +15,7 @@ #define ValidVulkanHandle(h) ((h).value[0] != 0) -#define MaxCommandBuffersInFlight BeamformerMaxRawDataFramesInFlight +#define MaxCommandBuffersInFlight (3) #define MaxCommandBufferTimestamps (1024) typedef enum { @@ -63,9 +63,8 @@ typedef struct { } VulkanSemaphore; typedef struct { - VulkanTimeline timeline; - u32 buffer_index; - + GPUTimeline timeline; + u32 buffer_index; // NOTE(rnp): since there may not be QueueKind_Count queues, when putting values into this // array you must be careful to map through the queue_indices array in the vulkan_context. u64 in_flight_wait_values[VulkanQueueKind_Count]; @@ -108,16 +107,16 @@ static_assert(alignof(VulkanQueue) == 64, "VulkanQueue must be placed on its own typedef alignas(64) struct { i32 lock; - u32 next_index; + u32 next_command_buffer_index; VulkanPipeline *bound_pipeline; + u64 last_submission_values[MaxCommandBuffersInFlight]; + u64 timestamp_counts[MaxCommandBuffersInFlight]; + VkCommandPool handle; VkQueryPool query_pool; VkCommandBuffer buffers[MaxCommandBuffersInFlight]; - - u64 submission_values[MaxCommandBuffersInFlight]; - u32 queries_occupied[MaxCommandBuffersInFlight]; } VulkanCommandPool; typedef struct { @@ -150,8 +149,8 @@ typedef struct { static_assert(VK_MAX_MEMORY_TYPES < U8_MAX, ""); } memory_info; - VulkanCommandPool * command_pools[VulkanTimeline_Count]; - VulkanQueue * queues[VulkanQueueKind_Count]; + VulkanCommandPool *command_pools[GPUTimeline_Count]; + VulkanQueue *queues[VulkanQueueKind_Count]; // NOTE(rnp): there are a few places in the code where simply going through the queues map // is not sufficient. those places need to know of the unique queues which unique queue // is being referred to. that code uses this map instead. @@ -161,6 +160,7 @@ typedef struct { VkFormat swap_chain_image_format; VkFormat depth_stencil_format; + VulkanEntity *entity_freelist; Arena *entity_arena; i32 entity_lock; @@ -451,17 +451,17 @@ vk_entity_release(VulkanEntity *entity) } function void * -vk_entity_data(VulkanHandle h, VulkanEntityKind kind) +vk_entity_data(u64 handle, VulkanEntityKind kind) { - VulkanEntity *e = (VulkanEntity *)h.value[0]; - assert(ValidVulkanHandle(h) && e->kind == kind); + VulkanEntity *e = (VulkanEntity *)handle; + assert(handle && e->kind == kind); return &e->as; } function VkCommandBuffer -vk_command_buffer(VulkanHandle h) +vk_command_buffer(GPUCommandList h) { - VulkanCommandBuffer *vcb = vk_entity_data(h, VulkanEntityKind_CommandBuffer); + VulkanCommandBuffer *vcb = vk_entity_data(h.value, VulkanEntityKind_CommandBuffer); VulkanCommandPool *vcp = vulkan_context->command_pools[vcb->timeline]; VkCommandBuffer result = vcp->buffers[vcb->buffer_index]; return result; @@ -963,7 +963,7 @@ typedef struct { u64 size; VulkanUsageFlags flags; u32 queue_family_count; - u32 queue_family_indices[VulkanTimeline_Count]; + u32 queue_family_indices[GPUTimeline_Count]; VkIndexType index_type; OSHandle *export; str8 label; @@ -1950,7 +1950,7 @@ vk_load(OSLibrary vulkan_library_handle, Stream *err) } DEBUG_IMPORT GPUInfo * -vk_gpu_info(void) +gpu_info(void) { return &vulkan_context->gpu_info; } @@ -1975,7 +1975,7 @@ DEBUG_IMPORT void vk_buffer_release(GPUBuffer *b) { if ValidVulkanHandle(b->handle) - vk_vulkan_buffer_release(vk_entity_data(b->handle, VulkanEntityKind_Buffer)); + vk_vulkan_buffer_release(vk_entity_data(b->handle.value[0], VulkanEntityKind_Buffer)); zero_struct(b); } @@ -2021,7 +2021,7 @@ vk_buffer_needs_sync(GPUBuffer *b) { b32 result = 0; if ValidVulkanHandle(b->handle) { - VulkanBuffer *vb = vk_entity_data(b->handle, VulkanEntityKind_Buffer); + VulkanBuffer *vb = vk_entity_data(b->handle.value[0], VulkanEntityKind_Buffer); // TODO(rnp): not correct check. need to check if we used transfer queue result = vb->memory_kind != VulkanMemoryKind_BAR; @@ -2114,7 +2114,7 @@ vk_buffer_buffer_copy(VulkanBuffer *destination, VulkanBuffer *source, u64 desti DEBUG_IMPORT void vk_buffer_range_upload(GPUBuffer *b, void *data, u64 offset, u64 size, b32 non_temporal) { - VulkanBuffer *db = vk_entity_data(b->handle, VulkanEntityKind_Buffer); + VulkanBuffer *db = vk_entity_data(b->handle.value[0], VulkanEntityKind_Buffer); VulkanBuffer sb = { .host_pointer = data, .memory_kind = VulkanMemoryKind_Host, @@ -2125,7 +2125,7 @@ vk_buffer_range_upload(GPUBuffer *b, void *data, u64 offset, u64 size, b32 non_t DEBUG_IMPORT void vk_buffer_range_download(void *destination, GPUBuffer *source, u64 offset, u64 size, b32 non_temporal) { - VulkanBuffer *sb = vk_entity_data(source->handle, VulkanEntityKind_Buffer); + VulkanBuffer *sb = vk_entity_data(source->handle.value[0], VulkanEntityKind_Buffer); VulkanBuffer db = { .host_pointer = destination, .memory_kind = VulkanMemoryKind_Host, @@ -2137,7 +2137,7 @@ DEBUG_IMPORT void vk_render_model_release(GPUBuffer *model) { if ValidVulkanHandle(model->handle) - vk_vulkan_buffer_release(vk_entity_data(model->handle, VulkanEntityKind_RenderModel)); + vk_vulkan_buffer_release(vk_entity_data(model->handle.value[0], VulkanEntityKind_RenderModel)); zero_struct(model); } @@ -2186,7 +2186,7 @@ vk_render_model_allocate(GPUBuffer *model, void *indices, u64 index_count, u64 m DEBUG_IMPORT void vk_render_model_range_upload(GPUBuffer *model, void *data, u64 offset, u64 size, b32 non_temporal) { - VulkanBuffer *db = vk_entity_data(model->handle, VulkanEntityKind_RenderModel); + VulkanBuffer *db = vk_entity_data(model->handle.value[0], VulkanEntityKind_RenderModel); VulkanBuffer sb = { .host_pointer = data, .memory_kind = VulkanMemoryKind_Host, @@ -2202,7 +2202,7 @@ vk_image_release(GPUImage *image) { if ValidVulkanHandle(image->image) { VulkanContext *vk = vulkan_context; - VulkanImage *vi = vk_entity_data(image->image, VulkanEntityKind_Image); + VulkanImage *vi = vk_entity_data(image->image.value[0], VulkanEntityKind_Image); vkDestroyImageView(vk->device, vi->view, 0); vkDestroyImage(vk->device, vi->image, 0); @@ -2336,10 +2336,10 @@ vk_create_semaphore(OSHandle *export) } DEBUG_IMPORT b32 -vk_host_wait_timeline(VulkanTimeline timeline, u64 value, u64 timeout_ns) +gpu_host_wait_timeline(GPUTimeline timeline, u64 value, u64 timeout_ns) { b32 result = 0; - if Between(timeline, 0, VulkanTimeline_Count - 1) { + if Between(timeline, 0, GPUTimeline_Count - 1) { VulkanContext *vk = vulkan_context; VulkanQueue *vq = vk->queues[timeline]; VkSemaphoreWaitInfo semaphore_wait_info = { @@ -2354,10 +2354,10 @@ vk_host_wait_timeline(VulkanTimeline timeline, u64 value, u64 timeout_ns) } DEBUG_IMPORT u64 -vk_host_signal_timeline(VulkanTimeline timeline) +gpu_host_signal_timeline(GPUTimeline timeline) { u64 result = -1; - if Between(timeline, 0, VulkanTimeline_Count - 1) { + if Between(timeline, 0, GPUTimeline_Count - 1) { VulkanContext *vk = vulkan_context; VulkanQueue *vq = vk->queues[timeline]; VulkanSemaphore *vs = &vq->timeline_semaphore; @@ -2397,7 +2397,7 @@ vk_pipeline_valid(VulkanHandle h) { b32 result = 0; if ValidVulkanHandle(h) { - VulkanPipeline *vp = vk_entity_data(h, VulkanEntityKind_Pipeline); + VulkanPipeline *vp = vk_entity_data(h.value[0], VulkanEntityKind_Pipeline); if (vp->stage_flags == VK_SHADER_STAGE_COMPUTE_BIT) result = vp->pipeline != vulkan_context->default_compute_pipeline.pipeline; else @@ -2411,15 +2411,17 @@ vk_pipeline_release(VulkanHandle h) { if (vk_pipeline_valid(h)) { VulkanEntity *e = (VulkanEntity *)h.value[0]; - VulkanTimeline timeline; - if (e->as.pipeline.stage_flags == VK_SHADER_STAGE_COMPUTE_BIT) timeline = VulkanTimeline_Compute; - else timeline = VulkanTimeline_Graphics; + GPUTimeline timeline; + // TODO(rnp): this is not correct, compute shaders can also appear on graphics timeline + if (e->as.pipeline.stage_flags == VK_SHADER_STAGE_COMPUTE_BIT) timeline = GPUTimeline_Compute; + else timeline = GPUTimeline_Graphics; // NOTE(rnp): block more command buffers from being recorded VulkanCommandPool *vcp = vulkan_context->command_pools[timeline]; - DeferLoop(take_lock(&vcp->lock, -1), release_lock(&vcp->lock)) { - u32 index = (vcp->next_index - 1) % countof(vcp->buffers); - vk_host_wait_timeline(timeline, vcp->submission_values[index], -1ULL); + DeferLoop(take_lock(&vcp->lock, -1), release_lock(&vcp->lock)) + { + u32 index = (vcp->next_command_buffer_index - 1) % MaxCommandBuffersInFlight; + gpu_host_wait_timeline(timeline, vcp->last_submission_values[index], -1ULL); vkDestroyPipeline(vulkan_context->device, e->as.pipeline.pipeline, 0); vkDestroyPipelineLayout(vulkan_context->device, e->as.pipeline.layout, 0); @@ -2440,7 +2442,7 @@ vk_bind_shader_resources(BeamformerShaderResourceInfo *infos, u64 info_count) for EachIndex(info_count, it) { switch (infos[it].kind) { case BeamformerShaderResourceKind_Buffer:{ - VulkanBuffer *vb = vk_entity_data(infos[it].handle, VulkanEntityKind_Buffer); + VulkanBuffer *vb = vk_entity_data(infos[it].handle.value[0], VulkanEntityKind_Buffer); vk->descriptor_buffer_infos[infos[it].slot].buffer = vb->buffer; vk->descriptor_buffer_infos[infos[it].slot].offset = 0; vk->descriptor_buffer_infos[infos[it].slot].range = vb->memory_size; @@ -2460,27 +2462,28 @@ vk_bind_shader_resources(BeamformerShaderResourceInfo *infos, u64 info_count) vkUpdateDescriptorSets(vk->device, countof(write_sets), write_sets, 0, 0); } -DEBUG_IMPORT VulkanHandle -vk_command_begin(VulkanTimeline timeline) +DEBUG_IMPORT GPUCommandList +gpu_command_list_begin(GPUTimeline timeline) { - VulkanHandle result = {0}; - if Between(timeline, 0, VulkanTimeline_Count - 1) { + GPUCommandList result = {0}; + if Between(timeline, 0, GPUTimeline_Count - 1) { VulkanContext *vk = vulkan_context; VulkanCommandPool *vcp = vk->command_pools[timeline]; take_lock(&vcp->lock, -1); + VulkanEntity *e = vk_entity_allocate(VulkanEntityKind_CommandBuffer); + result.value = (u64)e; - VulkanEntity *e = vk_entity_allocate(VulkanEntityKind_CommandBuffer); VulkanCommandBuffer *vcb = &e->as.command_buffer; vcb->timeline = timeline; - vcb->buffer_index = vcp->next_index++ % countof(vcp->buffers); + vcb->buffer_index = (vcp->next_command_buffer_index++) % MaxCommandBuffersInFlight; u32 index = vcb->buffer_index; // TODO(rnp): probably not the best to have this here but it will likely not be hit - b32 wait_result = vk_host_wait_timeline(timeline, vcp->submission_values[index], -1ULL); + b32 wait_result = gpu_host_wait_timeline(timeline, vcp->last_submission_values[index], -1ULL); assert(wait_result); - vcp->queries_occupied[index] = 0; + vcp->timestamp_counts[index] = 0; VkCommandBufferBeginInfo buffer_begin_info = { .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO, @@ -2490,159 +2493,163 @@ vk_command_begin(VulkanTimeline timeline) vkBeginCommandBuffer(vcp->buffers[index], &buffer_begin_info); vkCmdResetQueryPool(vcp->buffers[index], vcp->query_pool, index * MaxCommandBufferTimestamps, MaxCommandBufferTimestamps); - - result = (VulkanHandle){(u64)e}; } return result; } DEBUG_IMPORT void -vk_command_bind_pipeline(VulkanHandle command, VulkanHandle pipeline) +gpu_command_bind_pipeline(GPUCommandList command, VulkanHandle pipeline) { - if ValidVulkanHandle(command) { + if (command.value) { VulkanContext *vk = vulkan_context; - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanCommandPool *vcp = vk->command_pools[vcb->timeline]; VulkanPipeline *vp = 0; if ValidVulkanHandle(pipeline) { - vp = vk_entity_data(pipeline, VulkanEntityKind_Pipeline); - } else if (vcb->timeline == VulkanTimeline_Compute) { + vp = vk_entity_data(pipeline.value[0], VulkanEntityKind_Pipeline); + } else if (vcb->timeline == GPUTimeline_Compute) { vp = &vk->default_compute_pipeline; - } else if (vcb->timeline == VulkanTimeline_Graphics) { + } else if (vcb->timeline == GPUTimeline_Graphics) { vp = &vk->default_graphics_pipeline; } else { InvalidCodePath; } - read_only local_persist VkPipelineBindPoint bind_point_lut[VulkanTimeline_Count] = { - [VulkanTimeline_Graphics] = VK_PIPELINE_BIND_POINT_GRAPHICS, - [VulkanTimeline_Compute] = VK_PIPELINE_BIND_POINT_COMPUTE, - [VulkanTimeline_Transfer] = -1, + read_only local_persist VkPipelineBindPoint bind_point_lut[GPUTimeline_Count] = { + [GPUTimeline_Graphics] = VK_PIPELINE_BIND_POINT_GRAPHICS, + [GPUTimeline_Compute] = VK_PIPELINE_BIND_POINT_COMPUTE, + [GPUTimeline_Transfer] = -1, }; VkPipelineBindPoint bind_point = bind_point_lut[vcb->timeline]; assert(bind_point != (VkPipelineBindPoint)-1); - vkCmdBindPipeline(vcp->buffers[vcb->buffer_index], bind_point, vp->pipeline); - vkCmdBindDescriptorSets(vcp->buffers[vcb->buffer_index], bind_point, vp->layout, - 0, countof(vk->descriptor_sets), vk->descriptor_sets, 0, 0); + VkCommandBuffer cmd = vk_command_buffer(command); + vkCmdBindPipeline(cmd, bind_point, vp->pipeline); + vkCmdBindDescriptorSets(cmd, bind_point, vp->layout, 0, countof(vk->descriptor_sets), + vk->descriptor_sets, 0, 0); vcp->bound_pipeline = vp; } } +function VkDependencyInfo +vk_dependency_info_from_memory_barrier_info(Arena *arena, VulkanQueue *vq, GPUMemoryBarrierInfo *barriers, u64 count) +{ + u64 valid_count = 0; + VkBufferMemoryBarrier2 *memory_barriers = push_array(arena, VkBufferMemoryBarrier2, count); + for (u64 it = 0; it < count; it++) { + if ValidVulkanHandle(barriers[it].gpu_buffer->handle) { + u32 index = valid_count++; + VulkanBuffer *vb = vk_entity_data(barriers[it].gpu_buffer->handle.value[0], VulkanEntityKind_Buffer); + memory_barriers[index].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; + memory_barriers[index].srcStageMask = vq->pipeline_stage_flags; + memory_barriers[index].srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; + memory_barriers[index].dstStageMask = vq->pipeline_stage_flags; + memory_barriers[index].dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT; + memory_barriers[index].srcQueueFamilyIndex = vq->queue_family; + memory_barriers[index].dstQueueFamilyIndex = vq->queue_family; + memory_barriers[index].buffer = vb->buffer; + memory_barriers[index].offset = barriers[it].offset; + memory_barriers[index].size = barriers[it].size; + } + } + + VkDependencyInfo result = { + .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, + .bufferMemoryBarrierCount = valid_count, + .pBufferMemoryBarriers = memory_barriers, + }; + return result; +} + DEBUG_IMPORT void -vk_command_buffer_memory_barriers(VulkanHandle command, GPUMemoryBarrierInfo *barriers, u64 count) +gpu_command_buffer_memory_barriers(GPUCommandList command, GPUMemoryBarrierInfo *barriers, u64 count) { - if ValidVulkanHandle(command) { + if (command.value) { VulkanContext *vk = vulkan_context; - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); - VulkanCommandPool *vcp = vk->command_pools[vcb->timeline]; + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanQueue *vq = vk->queues[vcb->timeline]; Temp scratch; DeferLoop(take_lock(&vk->arena_lock, -1), release_lock(&vk->arena_lock)) DeferLoop(scratch = temp_begin(vk->arena), temp_end(scratch)) { - u32 valid_count = 0; - VkBufferMemoryBarrier2 *memory_barriers = push_array(vk->arena, VkBufferMemoryBarrier2, count); - for (u64 it = 0; it < count; it++) { - if ValidVulkanHandle(barriers[it].gpu_buffer->handle) { - u32 index = valid_count++; - VulkanBuffer *vb = vk_entity_data(barriers[it].gpu_buffer->handle, VulkanEntityKind_Buffer); - memory_barriers[index].sType = VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2; - memory_barriers[index].srcStageMask = vq->pipeline_stage_flags; - memory_barriers[index].srcAccessMask = VK_ACCESS_2_MEMORY_WRITE_BIT; - memory_barriers[index].dstStageMask = vq->pipeline_stage_flags; - memory_barriers[index].dstAccessMask = VK_ACCESS_2_MEMORY_READ_BIT; - memory_barriers[index].srcQueueFamilyIndex = vq->queue_family; - memory_barriers[index].dstQueueFamilyIndex = vq->queue_family; - memory_barriers[index].buffer = vb->buffer; - memory_barriers[index].offset = barriers[it].offset; - memory_barriers[index].size = barriers[it].size; - } - } - - VkDependencyInfo dependancy_info = { - .sType = VK_STRUCTURE_TYPE_DEPENDENCY_INFO, - .bufferMemoryBarrierCount = valid_count, - .pBufferMemoryBarriers = memory_barriers, - }; - - vkCmdPipelineBarrier2(vcp->buffers[vcb->buffer_index], &dependancy_info); + VkDependencyInfo dependancy_info = vk_dependency_info_from_memory_barrier_info(scratch.arena, vq, barriers, count); + vkCmdPipelineBarrier2(vk_command_buffer(command), &dependancy_info); } } } DEBUG_IMPORT void -vk_command_clear_buffer(VulkanHandle command, GPUBuffer *buffer, u64 offset, u64 size, u32 clear_word) +gpu_command_clear_buffer(GPUCommandList command, GPUBuffer *buffer, u64 offset, u64 size, u32 clear_word) { assert((offset % 4) == 0); assert((size % 4) == 0); - if ValidVulkanHandle(command) { - VulkanBuffer *vb = vk_entity_data(buffer->handle, VulkanEntityKind_Buffer); + if (command.value) { + VulkanBuffer *vb = vk_entity_data(buffer->handle.value[0], VulkanEntityKind_Buffer); VkCommandBuffer cmd = vk_command_buffer(command); vkCmdFillBuffer(cmd, vb->buffer, offset, size, clear_word); } } DEBUG_IMPORT void -vk_command_dispatch_compute(VulkanHandle command, uv3 dispatch) +gpu_command_dispatch_compute(GPUCommandList command, uv3 dispatch) { assert(dispatch.x <= U16_MAX); assert(dispatch.y <= U16_MAX); assert(dispatch.z <= U16_MAX); - if ValidVulkanHandle(command) { + if (command.value) { VkCommandBuffer cmd = vk_command_buffer(command); vkCmdDispatch(cmd, dispatch.x, dispatch.y, dispatch.z); } } DEBUG_IMPORT void -vk_command_push_constants(VulkanHandle command, u32 offset, u32 size, void *values) +gpu_command_push_constants(GPUCommandList command, u32 offset, u32 size, void *values) { - if ValidVulkanHandle(command) { - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); + if (command.value) { + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanCommandPool *vcp = vulkan_context->command_pools[vcb->timeline]; VulkanPipeline *vp = vcp->bound_pipeline; assert(vp); - vkCmdPushConstants(vcp->buffers[vcb->buffer_index], vp->layout, vp->stage_flags, offset, size, values); + vkCmdPushConstants(vk_command_buffer(command), vp->layout, vp->stage_flags, offset, size, values); } } DEBUG_IMPORT void -vk_command_timestamp(VulkanHandle command) +gpu_command_timestamp(GPUCommandList command) { - if ValidVulkanHandle(command) { + if (command.value) { VulkanContext *vk = vulkan_context; - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanCommandPool *vcp = vk->command_pools[vcb->timeline]; - read_only local_persist VkPipelineStageFlags2 stage_lut[VulkanTimeline_Count] = { - [VulkanTimeline_Graphics] = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, - [VulkanTimeline_Compute] = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, - [VulkanTimeline_Transfer] = -1, + read_only local_persist VkPipelineStageFlags2 stage_lut[GPUTimeline_Count] = { + [GPUTimeline_Graphics] = VK_PIPELINE_STAGE_2_ALL_GRAPHICS_BIT, + [GPUTimeline_Compute] = VK_PIPELINE_STAGE_2_COMPUTE_SHADER_BIT, + [GPUTimeline_Transfer] = -1, }; VkPipelineStageFlags2 stage = stage_lut[vcb->timeline]; assert(stage != (VkPipelineStageFlags2)-1); - if (vcp->queries_occupied[vcb->buffer_index] < MaxCommandBufferTimestamps) { - u32 query_index = vcp->queries_occupied[vcb->buffer_index]++; - vkCmdWriteTimestamp2(vcp->buffers[vcb->buffer_index], stage, vcp->query_pool, + if (vcp->timestamp_counts[vcb->buffer_index] < MaxCommandBufferTimestamps) { + u64 query_index = vcp->timestamp_counts[vcb->buffer_index]++; + vkCmdWriteTimestamp2(vk_command_buffer(command), stage, vcp->query_pool, vcb->buffer_index * MaxCommandBufferTimestamps + query_index); } } } DEBUG_IMPORT void -vk_command_wait_timeline(VulkanHandle command, VulkanTimeline timeline, u64 value) +gpu_command_wait_timeline(GPUCommandList command, GPUTimeline timeline, u64 value) { - if (ValidVulkanHandle(command) && Between(timeline, 0, VulkanTimeline_Count - 1)) { + if (command.value && Between(timeline, 0, GPUTimeline_Count - 1)) { VulkanContext *vk = vulkan_context; - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); u32 wait_index = vk->queue_indices[timeline]; vcb->in_flight_wait_values[wait_index] = Max(value, vcb->in_flight_wait_values[wait_index]); @@ -2650,12 +2657,12 @@ vk_command_wait_timeline(VulkanHandle command, VulkanTimeline timeline, u64 valu } DEBUG_IMPORT u64 -vk_command_end(VulkanHandle command, VulkanHandle wait_semaphore, VulkanHandle finished_semaphore) +gpu_command_list_end(GPUCommandList command, VulkanHandle wait_semaphore, VulkanHandle finished_semaphore) { u64 result = -1; - if ValidVulkanHandle(command) { + if (command.value) { VulkanContext *vk = vulkan_context; - VulkanCommandBuffer *vcb = vk_entity_data(command, VulkanEntityKind_CommandBuffer); + VulkanCommandBuffer *vcb = vk_entity_data(command.value, VulkanEntityKind_CommandBuffer); VulkanCommandPool *vcp = vk->command_pools[vcb->timeline]; VulkanQueue *vq = vk->queues[vcb->timeline]; VulkanSemaphore *vs = &vq->timeline_semaphore; @@ -2679,7 +2686,7 @@ vk_command_end(VulkanHandle command, VulkanHandle wait_semaphore, VulkanHandle f }}; if ValidVulkanHandle(finished_semaphore) { - VulkanSemaphore *fs = vk_entity_data(finished_semaphore, VulkanEntityKind_Semaphore); + VulkanSemaphore *fs = vk_entity_data(finished_semaphore.value[0], VulkanEntityKind_Semaphore); signal_submit_infos[signal_submit_info_count++] = (VkSemaphoreSubmitInfo){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .semaphore = fs->semaphore, @@ -2704,7 +2711,7 @@ vk_command_end(VulkanHandle command, VulkanHandle wait_semaphore, VulkanHandle f } if ValidVulkanHandle(wait_semaphore) { - VulkanSemaphore *ws = vk_entity_data(wait_semaphore, VulkanEntityKind_Semaphore); + VulkanSemaphore *ws = vk_entity_data(wait_semaphore.value[0], VulkanEntityKind_Semaphore); wait_submit_infos[wait_submit_info_count++] = (VkSemaphoreSubmitInfo){ .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO, .semaphore = ws->semaphore, @@ -2725,29 +2732,28 @@ vk_command_end(VulkanHandle command, VulkanHandle wait_semaphore, VulkanHandle f vkQueueSubmit2(vq->queue, 1, &submit_info, 0); vcp->bound_pipeline = 0; - - atomic_store_u64(vcp->submission_values + vcb->buffer_index, result); + atomic_store_u64(vcp->last_submission_values + vcb->buffer_index, result); } release_lock(&vcp->lock); - vk_entity_release((VulkanEntity *)command.value[0]); + vk_entity_release((VulkanEntity *)command.value); } return result; } DEBUG_IMPORT void -vk_command_begin_rendering(VulkanHandle command, GPUImage *colour, GPUImage *depth, GPUImage *resolve) +gpu_command_begin_rendering(GPUCommandList command, GPUImage *colour, GPUImage *depth, GPUImage *resolve) { - if ValidVulkanHandle(command) { + if (command.value) { VkCommandBuffer cmd = vk_command_buffer(command); assert((colour->width == depth->width) && (colour->height == depth->height)); - VulkanImage *ci = vk_entity_data(colour->image, VulkanEntityKind_Image); - VulkanImage *di = vk_entity_data(depth->image, VulkanEntityKind_Image); + VulkanImage *ci = vk_entity_data(colour->image.value[0], VulkanEntityKind_Image); + VulkanImage *di = vk_entity_data(depth->image.value[0], VulkanEntityKind_Image); VulkanImage *ri = 0; - if (resolve) ri = vk_entity_data(resolve->image, VulkanEntityKind_Image); + if (resolve) ri = vk_entity_data(resolve->image.value[0], VulkanEntityKind_Image); // NOTE: Layout Transitions { @@ -2852,20 +2858,20 @@ vk_command_begin_rendering(VulkanHandle command, GPUImage *colour, GPUImage *dep } DEBUG_IMPORT void -vk_command_draw(VulkanHandle command, GPUBuffer *model) +gpu_command_draw(GPUCommandList command, GPUBuffer *model) { - if (ValidVulkanHandle(command) && ValidVulkanHandle(model->handle)) { + if (command.value && ValidVulkanHandle(model->handle)) { VkCommandBuffer cmd = vk_command_buffer(command); - VulkanBuffer *vb = vk_entity_data(model->handle, VulkanEntityKind_RenderModel); + VulkanBuffer *vb = vk_entity_data(model->handle.value[0], VulkanEntityKind_RenderModel); vkCmdBindIndexBuffer2(cmd, vb->buffer, 0, vk_index_size(vb->index_type) * model->index_count, vb->index_type); vkCmdDrawIndexed(cmd, model->index_count, 1, 0, 0, 0); } } DEBUG_IMPORT void -vk_command_scissor(VulkanHandle command, u32 width, u32 height, u32 x_offset, u32 y_offset) +gpu_command_scissor(GPUCommandList command, u32 width, u32 height, u32 x_offset, u32 y_offset) { - if ValidVulkanHandle(command) { + if (command.value) { VkCommandBuffer cmd = vk_command_buffer(command); VkRect2D scissor = {.offset = {x_offset, y_offset}, .extent = {width, height}}; vkCmdSetScissor(cmd, 0, 1, &scissor); @@ -2873,9 +2879,9 @@ vk_command_scissor(VulkanHandle command, u32 width, u32 height, u32 x_offset, u3 } DEBUG_IMPORT void -vk_command_viewport(VulkanHandle command, f32 width, f32 height, f32 x_offset, f32 y_offset, f32 min_depth, f32 max_depth) +gpu_command_viewport(GPUCommandList command, f32 width, f32 height, f32 x_offset, f32 y_offset, f32 min_depth, f32 max_depth) { - if ValidVulkanHandle(command) { + if (command.value) { VkCommandBuffer cmd = vk_command_buffer(command); VkViewport viewport = {x_offset, y_offset, width, height, min_depth, max_depth}; vkCmdSetViewport(cmd, 0, 1, &viewport); @@ -2883,19 +2889,19 @@ vk_command_viewport(VulkanHandle command, f32 width, f32 height, f32 x_offset, f } DEBUG_IMPORT void -vk_command_end_rendering(VulkanHandle command) +gpu_command_end_rendering(GPUCommandList command) { - if ValidVulkanHandle(command) vkCmdEndRendering(vk_command_buffer(command)); + if (command.value) vkCmdEndRendering(vk_command_buffer(command)); } DEBUG_IMPORT void -vk_command_copy_buffer(VulkanHandle command, GPUBuffer *restrict destination, - GPUBuffer *restrict source, u64 source_offset, i64 size) +gpu_command_copy_buffer(GPUCommandList command, GPUBuffer *restrict destination, + GPUBuffer *restrict source, u64 source_offset, i64 size) { - if (ValidVulkanHandle(command) && ValidVulkanHandle(destination->handle) && ValidVulkanHandle(source->handle)) { + if (command.value && ValidVulkanHandle(destination->handle) && ValidVulkanHandle(source->handle)) { VkCommandBuffer cmd = vk_command_buffer(command); - VulkanBuffer *db = vk_entity_data(destination->handle, VulkanEntityKind_Buffer); - VulkanBuffer *sb = vk_entity_data(source->handle, VulkanEntityKind_Buffer); + VulkanBuffer *db = vk_entity_data(destination->handle.value[0], VulkanEntityKind_Buffer); + VulkanBuffer *sb = vk_entity_data(source->handle.value[0], VulkanEntityKind_Buffer); VkBufferCopy2 buffer_copy = { .sType = VK_STRUCTURE_TYPE_BUFFER_COPY_2, @@ -2917,27 +2923,24 @@ vk_command_copy_buffer(VulkanHandle command, GPUBuffer *restrict destination, } DEBUG_IMPORT u64 * -vk_command_read_timestamps(VulkanTimeline timeline, Arena *arena) +gpu_read_timestamps(GPUTimeline timeline, u64 *count, Arena *arena) { u64 *result = 0; - if Between(timeline, 0, VulkanTimeline_Count - 1) { + if Between(timeline, 0, GPUTimeline_Count - 1) { VulkanContext *vk = vulkan_context; VulkanCommandPool *vcp = vk->command_pools[timeline]; - DeferLoop(take_lock(&vcp->lock, -1), release_lock(&vcp->lock)) { - u32 index = (vcp->next_index - 1) % countof(vcp->buffers); - u32 count = vcp->queries_occupied[index]; - if (count > 0) { - result = push_array(arena, u64, count + 1); - result[0] = count; - - vk_host_wait_timeline(timeline, vcp->submission_values[index], -1ULL); - - vkGetQueryPoolResults(vk->device, vcp->query_pool, index * MaxCommandBufferTimestamps, count, - count * sizeof(u64), result + 1, 8, VK_QUERY_RESULT_WAIT_BIT); + DeferLoop(take_lock(&vcp->lock, -1), release_lock(&vcp->lock)) + { + u32 index = (vcp->next_command_buffer_index - 1) % MaxCommandBuffersInFlight; + *count = vcp->timestamp_counts[index]; + if (*count > 0) { + result = push_array(arena, u64, *count); + gpu_host_wait_timeline(timeline, vcp->last_submission_values[index], -1ULL); + + vkGetQueryPoolResults(vk->device, vcp->query_pool, index * MaxCommandBufferTimestamps, *count, + *count * sizeof(u64), result, 8, VK_QUERY_RESULT_WAIT_BIT); } } - } else { - result = push_array(arena, u64, 1); } return result; } diff --git a/vulkan.h b/vulkan.h @@ -137,6 +137,7 @@ typedef enum { VK_STRUCTURE_TYPE_BUFFER_DEVICE_ADDRESS_INFO = 1000244001, VK_STRUCTURE_TYPE_VALIDATION_FEATURES_EXT = 1000247000, VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_KHR = 1000286000, + VK_STRUCTURE_TYPE_MEMORY_BARRIER_2 = 1000314000, VK_STRUCTURE_TYPE_BUFFER_MEMORY_BARRIER_2 = 1000314001, VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER_2 = 1000314002, VK_STRUCTURE_TYPE_DEPENDENCY_INFO = 1000314003, @@ -1637,6 +1638,7 @@ typedef enum { VK_DEPENDENCY_VIEW_LOCAL_BIT = 0x00000002, VK_DEPENDENCY_FEEDBACK_LOOP_BIT_EXT = 0x00000008, VK_DEPENDENCY_QUEUE_FAMILY_OWNERSHIP_TRANSFER_USE_ALL_STAGES_BIT_KHR = 0x00000020, + VK_DEPENDENCY_ASYMMETRIC_EVENT_BIT_KHR = 0x00000040, VK_DEPENDENCY_FLAG_BITS_MAX_ENUM = 0x7FFFFFFF } VkDependencyFlagBits; typedef VkFlags VkDependencyFlags;