@@ -191,6 +191,7 @@ struct vk_queue;
191191
192192struct vk_command_buffer {
193193 vk::CommandBuffer buf;
194+ uint64_t use_counter = 0;
194195 bool in_use = false;
195196};
196197
@@ -938,19 +939,24 @@ struct vk_subbuffer {
938939 }
939940};
940941
941- // vk_event is used for the event-related backend interfaces. It uses 'event' for
942- // event_wait and 'fence' for event_synchronize. Polling on an event for
942+ struct vk_semaphore {
943+ vk::Semaphore s;
944+ uint64_t value;
945+ };
946+
947+ // vk_event is used for the event-related backend interfaces. It uses vk::Events for
948+ // event_wait and a timeline semaphore for event_synchronize. Polling on an event for
943949// event_synchronize wouldn't be sufficient to wait for command buffers to complete,
944950// and would lead to validation errors.
945951struct vk_event {
952+ std::vector<vk::Event> events_free; // Events available for reuse
953+ std::vector<vk::Event> events_submitted; // Events that are fully submitted and can be reused on next synchronize
946954 vk::Event event;
947- vk::Fence fence;
948- vk_command_buffer* cmd_buffer = nullptr;
949- };
955+ bool has_event;
950956
951- struct vk_semaphore {
952- vk::Semaphore s ;
953- uint64_t value ;
957+ vk_semaphore tl_semaphore;
958+ vk_command_buffer* cmd_buffer = nullptr ;
959+ uint64_t cmd_buffer_use_counter = 0 ;
954960};
955961
956962struct vk_submission {
@@ -2319,7 +2325,7 @@ static vk_command_buffer* ggml_vk_create_cmd_buffer(vk_device& device, vk_comman
23192325 vk::CommandBufferLevel::ePrimary,
23202326 1);
23212327 const std::vector<vk::CommandBuffer> cmd_buffers = device->device.allocateCommandBuffers(command_buffer_alloc_info);
2322- p.cmd_buffers.push_back({ cmd_buffers.front(), true });
2328+ p.cmd_buffers.push_back({ cmd_buffers.front(), 0, true });
23232329 return &p.cmd_buffers[p.cmd_buffers.size()-1];
23242330}
23252331
@@ -2788,6 +2794,15 @@ static void ggml_vk_sync_buffers(ggml_backend_vk_context* ctx, vk_context& subct
27882794 );
27892795}
27902796
2797+ static void ggml_vk_reset_event(vk_context& ctx, vk::Event& event) {
2798+ VK_LOG_DEBUG("ggml_vk_set_event()");
2799+
2800+ ctx->s->buffer->buf.resetEvent(
2801+ event,
2802+ ctx->p->q->stage_flags
2803+ );
2804+ }
2805+
27912806static void ggml_vk_set_event(vk_context& ctx, vk::Event& event) {
27922807 VK_LOG_DEBUG("ggml_vk_set_event()");
27932808
@@ -6396,6 +6411,7 @@ static vk_subbuffer ggml_vk_tensor_subbuffer(
63966411static vk_command_buffer* ggml_vk_get_or_create_cmd_buffer(vk_device& device, vk_command_pool& pool) {
63976412 for (auto& cmd_buffer : pool.cmd_buffers) {
63986413 if (!cmd_buffer.in_use) {
6414+ cmd_buffer.use_counter++;
63996415 cmd_buffer.in_use = true;
64006416 return &cmd_buffer;
64016417 }
@@ -6500,14 +6516,15 @@ static void ggml_vk_ctx_begin(vk_device& device, vk_context& subctx) {
65006516}
65016517
65026518static vk_context ggml_vk_get_compute_ctx(ggml_backend_vk_context * ctx) {
6519+ vk_context result;
65036520 if (!ctx->compute_ctx.expired()) {
6504- return ctx->compute_ctx.lock();
6505- }
6506-
6507- vk_context result = ggml_vk_create_context(ctx, ctx->compute_cmd_pool);
6521+ result = ctx->compute_ctx.lock();
6522+ } else {
6523+ result = ggml_vk_create_context(ctx, ctx->compute_cmd_pool);
65086524
6509- ctx->compute_ctx = result;
6510- ggml_vk_ctx_begin(ctx->device, result);
6525+ ctx->compute_ctx = result;
6526+ ggml_vk_ctx_begin(ctx->device, result);
6527+ }
65116528
65126529 if (ctx->device->async_use_transfer_queue && ctx->transfer_semaphore_last_submitted < ctx->transfer_semaphore.value) {
65136530 result->s->wait_semaphores.push_back(ctx->transfer_semaphore);
@@ -13801,6 +13818,7 @@ static void ggml_vk_synchronize(ggml_backend_vk_context * ctx) {
1380113818 ctx->submit_pending = false;
1380213819 if (cmd_buf) {
1380313820 cmd_buf->in_use = false;
13821+ cmd_buf->buf.reset();
1380413822 }
1380513823 }
1380613824
@@ -14862,18 +14880,31 @@ static void ggml_backend_vk_event_record(ggml_backend_t backend, ggml_backend_ev
1486214880 vk_context compute_ctx = ggml_vk_get_compute_ctx(ctx);
1486314881 auto* cmd_buf = compute_ctx->s->buffer; // retrieve pointer before it gets reset
1486414882
14865- // the backend interface doesn't have an explicit reset, so reset it here
14866- // before we record the command to set it
14867- ctx->device->device.resetEvent(vkev->event);
14868- ctx->device->device.resetFences({ vkev->fence });
14883+ if (vkev->has_event) {
14884+ // Move existing event into submitted
14885+ vkev->events_submitted.push_back(vkev->event);
14886+ }
14887+
14888+ // Grab the next event and record it, create one if necessary
14889+ if (vkev->events_free.empty()) {
14890+ vkev->event = ctx->device->device.createEvent({});
14891+ } else {
14892+ vkev->event = vkev->events_free.back();
14893+ vkev->events_free.pop_back();
14894+ }
14895+
14896+ vkev->has_event = true;
1486914897
1487014898 ggml_vk_set_event(compute_ctx, vkev->event);
1487114899
14900+ vkev->tl_semaphore.value++;
14901+ compute_ctx->s->signal_semaphores.push_back(vkev->tl_semaphore);
1487214902 ggml_vk_ctx_end(compute_ctx);
1487314903
14874- ggml_vk_submit(compute_ctx, {vkev->fence });
14904+ ggml_vk_submit(compute_ctx, {});
1487514905 ctx->submit_pending = true;
1487614906 vkev->cmd_buffer = cmd_buf;
14907+ vkev->cmd_buffer_use_counter = cmd_buf->use_counter;
1487714908 ctx->compute_ctx.reset();
1487814909}
1487914910
@@ -14884,9 +14915,10 @@ static void ggml_backend_vk_event_wait(ggml_backend_t backend, ggml_backend_even
1488414915
1488514916 vk_context compute_ctx = ggml_vk_get_compute_ctx(ctx);
1488614917
14887- ggml_vk_wait_events(compute_ctx, {vkev->event});
14888- ggml_vk_ctx_end(compute_ctx);
14889- ctx->compute_ctx.reset();
14918+ if (vkev->has_event) {
14919+ // Wait for latest event
14920+ ggml_vk_wait_events(compute_ctx, { vkev->event });
14921+ }
1489014922}
1489114923
1489214924// TODO: enable async and synchronize
@@ -15676,10 +15708,13 @@ static ggml_backend_event_t ggml_backend_vk_device_event_new(ggml_backend_dev_t
1567615708 return nullptr;
1567715709 }
1567815710
15679- // The event/fence is expected to initially be in the signaled state.
15680- vkev->event = device->device.createEvent({});
15681- vkev->fence = device->device.createFence({vk::FenceCreateFlagBits::eSignaled});
15682- device->device.setEvent(vkev->event);
15711+ // No events initially, they get created on demand
15712+ vkev->has_event = false;
15713+
15714+ vk::SemaphoreTypeCreateInfo tci{ vk::SemaphoreType::eTimeline, 0 };
15715+ vk::SemaphoreCreateInfo ci{};
15716+ ci.setPNext(&tci);
15717+ vkev->tl_semaphore = { device->device.createSemaphore(ci), 0 };
1568315718
1568415719 return new ggml_backend_event {
1568515720 /* .device = */ dev,
@@ -15693,8 +15728,16 @@ static void ggml_backend_vk_device_event_free(ggml_backend_dev_t dev, ggml_backe
1569315728
1569415729 vk_event *vkev = (vk_event *)event->context;
1569515730
15696- device->device.destroyFence(vkev->fence);
15697- device->device.destroyEvent(vkev->event);
15731+ device->device.destroySemaphore(vkev->tl_semaphore.s);
15732+ for (auto& event : vkev->events_free) {
15733+ device->device.destroyEvent(event);
15734+ }
15735+ for (auto& event : vkev->events_submitted) {
15736+ device->device.destroyEvent(event);
15737+ }
15738+ if (vkev->has_event) {
15739+ device->device.destroyEvent(vkev->event);
15740+ }
1569815741 delete vkev;
1569915742 delete event;
1570015743}
@@ -15705,10 +15748,29 @@ static void ggml_backend_vk_device_event_synchronize(ggml_backend_dev_t dev, ggm
1570515748 auto device = ggml_vk_get_device(ctx->device);
1570615749 vk_event *vkev = (vk_event *)event->context;
1570715750
15708- VK_CHECK(device->device.waitForFences({ vkev->fence }, true, UINT64_MAX), "event_synchronize");
15709- // Finished using current command buffer so we flag for reuse
15710- if (vkev->cmd_buffer) {
15711- vkev->cmd_buffer->in_use = false;
15751+ // Only do something if the event has actually been used
15752+ if (vkev->has_event) {
15753+ vk::Semaphore sem = vkev->tl_semaphore.s;
15754+ uint64_t val = vkev->tl_semaphore.value;
15755+ vk::SemaphoreWaitInfo swi{vk::SemaphoreWaitFlags{}, sem, val};
15756+ VK_CHECK(device->device.waitSemaphores(swi, UINT64_MAX), "event_synchronize");
15757+
15758+ // Reset and move submitted events
15759+ for (auto& event : vkev->events_submitted) {
15760+ device->device.resetEvent(event);
15761+ }
15762+ vkev->events_free.insert(vkev->events_free.end(), vkev->events_submitted.begin(), vkev->events_submitted.end());
15763+ vkev->events_submitted.clear();
15764+
15765+ // Finished using current command buffer so we flag for reuse
15766+ if (vkev->cmd_buffer) {
15767+ // Only flag for reuse if it hasn't been reused already
15768+ if (vkev->cmd_buffer_use_counter == vkev->cmd_buffer->use_counter) {
15769+ vkev->cmd_buffer->in_use = false;
15770+ vkev->cmd_buffer->buf.reset();
15771+ }
15772+ vkev->cmd_buffer = nullptr;
15773+ }
1571215774 }
1571315775}
1571415776
0 commit comments