quick_submit.cpp

cross platform rendering playground

src/quick_submit.cpp

4.2 KB
#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);
}