queue.cpp

cross platform rendering playground

src/backend/vulkan/queue.cpp

5.15 KB
#include <volk.h>

#include <cstdio>
#include <cstdlib>
#include <new>
#include <vector>

#include "backend/vulkan/debug.h"
#include "backend/vulkan/utils.h"
#include "backend/vulkan/vk_api.h"
#include "backend/vulkan/vk_conversion.h"

namespace rhi {
using namespace vk;

Queue find_queue(Device &device, u32 required, u32 excluded) {
    u32 count = 0;
    vkGetPhysicalDeviceQueueFamilyProperties(device.physical, &count, nullptr);
    std::vector<VkQueueFamilyProperties> families(count);
    vkGetPhysicalDeviceQueueFamilyProperties(device.physical, &count, families.data());

    for (u32 i = 0; i < count; ++i) {
        bool has_required = (families[i].queueFlags & required) == required;
        bool has_excluded = (families[i].queueFlags & excluded) != 0;
        if (has_required && !has_excluded) {
            return {0, i, 0};
        }
    }

    for (u32 i = 0; i < count; ++i) {
        if (families[i].queueFlags & required) {
            return {0, i, 0};
        }
    }
    return {};
}

Queue graphics_queue(Device &device) {
    return device.graphics;
}

u32 queue_family_index(const Queue &queue) {
    return queue.family_index;
}

u64 sync_get_completed_value(Sync *sync) {
    if (sync == nullptr || sync->timeline == VK_NULL_HANDLE) {
        return 0;
    }
    u64 value = 0;
    VK_CHECK(vkGetSemaphoreCounterValue(sync->device, sync->timeline, &value));
    return value;
}

static VkSemaphoreSubmitInfo sync_edge_to_vk(const SyncPoint &point, rhi::PipelineStages stages) {
    if (point.sync == nullptr || point.sync->timeline == VK_NULL_HANDLE || point.value == 0) {
        VEL_CRITICAL("queue_submit: invalid SyncPoint edge (null sync or zero value)");
        std::abort();
    }
    return VkSemaphoreSubmitInfo{
        .sType = VK_STRUCTURE_TYPE_SEMAPHORE_SUBMIT_INFO,
        .semaphore = point.sync->timeline,
        .value = point.value,
        .stageMask = pipeline_stage_flags_to_vk(stages),
        .deviceIndex = 0,
    };
}

void queue_submit(Device &device, Queue queue, const QueueSubmitDesc &desc) {
    VkDevice vk_dev = device.logical;
    (void)vk_dev;

    std::vector<VkSemaphoreSubmitInfo> wait_infos(desc.wait_count);
    for (u32 i = 0; i < desc.wait_count; ++i) {
        wait_infos[i] = sync_edge_to_vk(desc.waits[i].point, desc.waits[i].stage_mask);
    }

    std::vector<VkCommandBufferSubmitInfo> cmd_infos(desc.cmd_count);
    for (u32 i = 0; i < desc.cmd_count; ++i) {
        cmd_infos[i] = VkCommandBufferSubmitInfo{
            .sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_SUBMIT_INFO,
            .commandBuffer = desc.cmds[i].handle,
            .deviceMask = 0,
        };
    }

    std::vector<VkSemaphoreSubmitInfo> signal_infos(desc.signal_count);
    for (u32 i = 0; i < desc.signal_count; ++i) {
        signal_infos[i] = sync_edge_to_vk(desc.signals[i].point, rhi::PipelineStages::ALL_COMMANDS);
    }

    VkSubmitInfo2 submit_info{
        .sType = VK_STRUCTURE_TYPE_SUBMIT_INFO_2,
        .waitSemaphoreInfoCount = desc.wait_count,
        .pWaitSemaphoreInfos = wait_infos.data(),
        .commandBufferInfoCount = desc.cmd_count,
        .pCommandBufferInfos = cmd_infos.data(),
        .signalSemaphoreInfoCount = desc.signal_count,
        .pSignalSemaphoreInfos = signal_infos.data(),
    };

    VK_CHECK(vkQueueSubmit2(queue.handle, 1, &submit_info, VK_NULL_HANDLE));
}

Sync *sync_create(Device &device, u64 initial_value, const char *name) {
    VkSemaphoreTypeCreateInfo timeline_ci{
        .sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
        .pNext = nullptr,
        .semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
        .initialValue = initial_value,
    };
    VkSemaphoreCreateInfo ci{};
    ci.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO;
    ci.pNext = &timeline_ci;
    VkSemaphore handle = VK_NULL_HANDLE;
    if (vkCreateSemaphore(device.logical, &ci, nullptr, &handle) != VK_SUCCESS || handle == VK_NULL_HANDLE) {
        VEL_CRITICAL("sync_create: timeline semaphore creation failed");
        return nullptr;
    }
    Sync *sync = new (std::nothrow) Sync{};
    if (sync == nullptr) {
        vkDestroySemaphore(device.logical, handle, nullptr);
        return nullptr;
    }
    sync->timeline = handle;
    sync->device = device.logical;
    if (name != nullptr) {
        char sync_name[128];
        (void)snprintf(sync_name, sizeof(sync_name), "sync/%s", name);
        set_name(device, handle, sync_name);
    }
    return sync;
}

void sync_destroy(Sync *sync) {
    if (sync == nullptr) {
        return;
    }
    if (sync->timeline != VK_NULL_HANDLE) {
        vkDestroySemaphore(sync->device, sync->timeline, nullptr);
        sync->timeline = VK_NULL_HANDLE;
    }
    delete sync;
}

bool sync_host_wait(Sync *sync, u64 value, u64 timeout_ns) {
    if (sync == nullptr || sync->timeline == VK_NULL_HANDLE) {
        VEL_CRITICAL("sync_host_wait: null sync object");
        return false;
    }
    VkSemaphoreWaitInfo wait_info{
        .sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
        .semaphoreCount = 1,
        .pSemaphores = &sync->timeline,
        .pValues = &value,
    };
    // timeout aborts, no false path
    VK_CHECK(vkWaitSemaphores(sync->device, &wait_info, timeout_ns));
    return true;
}

} // namespace rhi