#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