#include "quick_submit.h"
#include <cassert>
#include "core/logger.h"
void qs_init(QuickSubmit &qs, rhi::Queue queue, rhi::Device &device) {
qs.device = &device;
qs.queue = queue;
qs.next_idx = 0;
qs.timeline_ctr = 0;
qs.timeline_value = 0;
qs.timeline = rhi::sync_create(device, 0, "quick-submit");
if (!rhi::valid(qs.timeline)) {
VEL_CRITICAL("qs_init: upload timeline creation failed");
}
rhi::CmdPoolDesc cmd_pool_dsc;
cmd_pool_dsc.queue_family_index = rhi::queue_family_index(qs.queue);
cmd_pool_dsc.flags = rhi::CmdPoolUsage::Transient | rhi::CmdPoolUsage::ResetCommandBuffer;
rhi::create_cmd_pool(device, cmd_pool_dsc, qs.pool);
for (u32 i = 0; i < qs.max_buffer_count; ++i) {
QsCmd &buf = qs.buffers[i];
buf.index = i;
buf.state = QsCmdState::Free;
buf.submitted_value = 0;
rhi::CmdBufferDesc cmd_desc;
cmd_desc.pool = &qs.pool;
cmd_desc.level = rhi::CmdBufferLevel::Primary;
rhi::create_cmd_buffer(device, cmd_desc, buf.cmd);
}
}
void qs_shutdown(QuickSubmit &qs) {
qs_wait_all(qs);
rhi::Device &device = *qs.device;
for (u32 i = 0; i < qs.max_buffer_count; ++i) {
qs.buffers[i].state = QsCmdState::Free;
qs.buffers[i].submitted_value = 0;
}
rhi::destroy_cmd_pool(device, qs.pool);
rhi::sync_destroy(qs.timeline);
qs.timeline = nullptr;
qs.next_idx = 0;
qs.timeline_value = 0;
qs.timeline_ctr = 0;
}
void qs_purge(QuickSubmit &qs) {
const u64 current = rhi::sync_get_completed_value(qs.timeline);
for (QsCmd &buf : qs.buffers) {
if (buf.state == QsCmdState::Submitted && buf.submitted_value <= current) {
buf.state = QsCmdState::Free;
}
}
}
QsCmd *qs_acquire(QuickSubmit &qs) {
qs_purge(qs);
for (u32 i = 0; i < qs.max_buffer_count; ++i) {
const u32 idx = (qs.next_idx + i) % qs.max_buffer_count;
QsCmd &buf = qs.buffers[idx];
if (buf.state == QsCmdState::Free) {
qs.next_idx = (idx + 1) % qs.max_buffer_count;
rhi::reset_cmd_buffer(buf.cmd);
rhi::begin_cmd_buffer(buf.cmd, rhi::CmdBufferBeginDesc{.usage = rhi::CmdBufferUsage::OneTimeSubmit});
buf.state = QsCmdState::Recording;
return &buf;
}
}
u64 oldest = UINT64_MAX;
for (QsCmd &buf : qs.buffers) {
if (buf.state == QsCmdState::Submitted) {
oldest = std::min(oldest, buf.submitted_value);
}
}
assert(oldest != UINT64_MAX && "No buffers available and none submitted?");
rhi::sync_host_wait(qs.timeline, oldest);
qs_purge(qs);
for (QsCmd &buf : qs.buffers) {
if (buf.state == QsCmdState::Free) {
rhi::reset_cmd_buffer(buf.cmd);
rhi::begin_cmd_buffer(buf.cmd, rhi::CmdBufferBeginDesc{.usage = rhi::CmdBufferUsage::OneTimeSubmit});
buf.state = QsCmdState::Recording;
return &buf;
}
}
VEL_CRITICAL("QuickSubmit: acquire failed after wait — all buffers exhausted");
return nullptr;
}
QsSubmitHandle qs_submit(QuickSubmit &qs, QsCmd &buf) {
assert(buf.state == QsCmdState::Recording);
rhi::end_cmd_buffer(buf.cmd);
qs.timeline_ctr++;
const u64 signal_value = qs.timeline_ctr;
qs.timeline_value = signal_value;
rhi::QueueSignal signal{{qs.timeline, signal_value}};
rhi::QueueSubmitDesc desc{};
desc.cmd_count = 1;
desc.cmds = &buf.cmd;
desc.signal_count = 1;
desc.signals = &signal;
rhi::queue_submit(*qs.device, qs.queue, desc);
buf.state = QsCmdState::Submitted;
buf.submitted_value = signal_value;
return QsSubmitHandle{signal_value};
}
void qs_submit_and_wait(QuickSubmit &qs, QsCmd &buf) {
QsSubmitHandle h = qs_submit(qs, buf);
qs_wait(qs, h);
}
bool qs_is_ready(QuickSubmit &qs, QsSubmitHandle h) {
return rhi::sync_get_completed_value(qs.timeline) >= h.value;
}
void qs_wait(QuickSubmit &qs, QsSubmitHandle h) {
rhi::sync_host_wait(qs.timeline, h.value);
}
void qs_wait_all(QuickSubmit &qs) {
if (qs.timeline_value == 0) {
return;
}
const u64 target = qs.timeline_value;
rhi::sync_host_wait(qs.timeline, target);
qs_purge(qs);
}