feat(bindless): Create Vulkan helper namespace

This commit is contained in:
PancakeTAS 2026-04-25 19:50:03 +02:00
parent 97faa0c067
commit bd77a7917f
No known key found for this signature in database
2 changed files with 258 additions and 0 deletions

View file

@ -0,0 +1,157 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#include "vkhelper.hpp"
#include <array>
#include <cstdint>
#include <iomanip>
#include <ios>
#include <iostream>
#include <sstream>
#include <stdexcept>
#include <string>
#include <utility>
/* Device initialization */
vk::UniqueInstance vkhelper::createInstance(vk::detail::DispatchLoaderDynamic& dld) {
dld.init();
const vk::ApplicationInfo appInfo{
.pApplicationName = "lsfg-vk",
.applicationVersion = vk::makeVersion(2, 0, 0),
.pEngineName = "lsfg-vk",
.engineVersion = vk::makeVersion(2, 0, 0),
.apiVersion = vk::ApiVersion12 // Fully supported by all Vulkan-capable GPUs
};
const vk::InstanceCreateInfo instanceInfo{
.pApplicationInfo = &appInfo
};
auto instance{vk::createInstanceUnique(instanceInfo, nullptr, dld)};
dld.init(*instance);
return instance;
}
vk::PhysicalDevice vkhelper::findPhysicalDevice(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::Instance& instance,
const std::string& id
) {
for (const auto& physdev : instance.enumeratePhysicalDevices(dld)) {
// Check for VK_EXT_pci_bus_info
bool supportsPCIEXT{false};
for (const auto& ext : physdev.enumerateDeviceExtensionProperties(nullptr, dld)) {
if (std::string(ext.extensionName) != vk::EXTPciBusInfoExtensionName)
continue;
supportsPCIEXT = true;
break;
}
// Fetch properties
vk::PhysicalDevicePCIBusInfoPropertiesEXT busInfo{};
vk::PhysicalDeviceProperties2 info{
.pNext = supportsPCIEXT ? &busInfo : nullptr
};
physdev.getProperties2(&info, dld);
auto& props{info.properties};
// Compare device name
props.deviceName.back() = '\0'; // Ensure null-termination
if (id == std::string(props.deviceName))
return physdev;
// Compare Vendor ID + Device ID
std::ostringstream gpuss;
gpuss << std::hex << std::setfill('0')
<< std::setw(4) << props.vendorID << ":"
<< std::setw(4) << props.deviceID;
if (id == gpuss.str())
return physdev;
// Compare PCI bus ID
if (!supportsPCIEXT)
continue;
std::ostringstream pciss;
pciss << std::hex << std::setfill('0')
<< std::setw(4) << busInfo.pciDomain << ":"
<< std::setw(2) << busInfo.pciBus << ":"
<< std::setw(2) << busInfo.pciDevice << "."
<< std::setw(1) << busInfo.pciFunction;
if (id == pciss.str())
return physdev;
}
throw std::runtime_error("No physical device matching '" + id + "' found");
}
uint32_t vkhelper::findComputeQueueFamilyIndex(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev
) {
uint32_t idx{0};
for (const auto& qfi : physdev.getQueueFamilyProperties2(dld)) {
if (qfi.queueFamilyProperties.queueFlags & vk::QueueFlagBits::eCompute)
return idx;
idx++;
}
throw std::runtime_error("No compute-capable queue family found");
}
bool vkhelper::checkHalfPrecisionSupport(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev
) {
vk::PhysicalDeviceVulkan12Features featuresVulkan12{};
vk::PhysicalDeviceFeatures2 features{
.pNext = &featuresVulkan12
};
physdev.getFeatures2(&features, dld);
return featuresVulkan12.shaderFloat16;
}
std::pair<vk::UniqueDevice, vk::Queue> vkhelper::createDevice(
vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev,
uint32_t qfi,
bool fp16
) {
constexpr std::array<const char*, 3> EXTENSIONS{
vk::KHRSynchronization2ExtensionName,
vk::KHRExternalMemoryFdExtensionName,
vk::KHRExternalSemaphoreFdExtensionName
};
vk::PhysicalDeviceSynchronization2FeaturesKHR sync2Info{
.synchronization2 = VK_TRUE
};
const vk::PhysicalDeviceVulkan12Features vk12Info{
.pNext = &sync2Info,
.shaderFloat16 = fp16,
.timelineSemaphore = VK_TRUE
};
const float queuePriority{1.0F}; // Highest priority
const vk::DeviceQueueCreateInfo queueInfo{
.queueFamilyIndex = qfi,
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
const vk::DeviceCreateInfo deviceInfo{
.pNext = &vk12Info,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &queueInfo,
.enabledExtensionCount = static_cast<uint32_t>(EXTENSIONS.size()),
.ppEnabledExtensionNames = EXTENSIONS.data()
};
auto device{physdev.createDeviceUnique(deviceInfo, nullptr, dld)};
dld.init(*device);
return{
std::move(device),
device->getQueue(qfi, 0, dld)
};
}

View file

@ -0,0 +1,101 @@
/* SPDX-License-Identifier: GPL-3.0-or-later */
#pragma once
#define VULKAN_HPP_DISPATCH_LOADER_DYNAMIC 1
#define VULKAN_HPP_NO_DEFAULT_DISPATCHER 1
#define VULKAN_HPP_NO_CONSTRUCTORS 1
#include <vulkan/vulkan.hpp> // IWYU pragma: export
// IWYU pragma: begin_exports
#include <vulkan/vulkan_core.h>
#include <vulkan/vulkan_enums.hpp>
#include <vulkan/vulkan_funcs.hpp>
#include <vulkan/vulkan_handles.hpp>
#include <vulkan/vulkan_hpp_macros.hpp>
#include <vulkan/vulkan_structs.hpp>
// IWYU pragma: end_exports
#include <cstdint>
#include <string>
#include <utility>
namespace vkhelper {
/* Device initialization */
///
/// Create a Vulkan 1.2 instance for lsfg-vk
///
/// @param dld Dynamic dispatch loader
/// @return RAII-wrapped Vulkan instance
/// @throws std::runtime_error on failure
///
vk::UniqueInstance createInstance(vk::detail::DispatchLoaderDynamic& dld);
///
/// Find a physical device through a custom identifier
///
/// The custom identifier may be one of:
/// - Device name (e.g. "NVIDIA GeForce RTX 5080")
/// - Vendor ID + Device ID in lowercase hexadecimal (e.g. "10de:2c02")
/// - PCI bus ID with padded zeroes (e.g. "0000:01:00.0")
///
/// @param dld Dynamic dispatch loader
/// @param instance Vulkan instance
/// @param id Custom identifier
/// @return Selected physical device
/// @throws std::runtime_error if no suitable device found
///
vk::PhysicalDevice findPhysicalDevice(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::Instance& instance,
const std::string& id
);
///
/// Find the first compute-capable queue family index
///
/// @param dld Dynamic dispatch loader
/// @param physdev Physical device
/// @return Queue family index
/// @throws std::runtime_error if no compute-capable queue found
///
uint32_t findComputeQueueFamilyIndex(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev
);
///
/// Check a physical device for half-precision float support
///
/// @param dld Dynamic dispatch loader
/// @param physdev Physical device
/// @return Whether half-precision float is supported
///
bool checkHalfPrecisionSupport(
const vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev
);
///
/// Create a Vulkan device for lsfg-vk
///
/// This device will have the core features timelineSemaphore and shaderFloat16 (if requested)
/// enabled, as well as the synchronization2, external memory & semaphore fd extensions.
///
/// @param dld Dynamic dispatch loader
/// @param physdev Physical device
/// @param qfi Queue family index of compute-capable queue
/// @param fp16 Whether to enable half-precision float support
/// @return RAII-wrapped Vulkan device & compute queue
/// @throws std::runtime_error on failure
///
std::pair<vk::UniqueDevice, vk::Queue> createDevice(
vk::detail::DispatchLoaderDynamic& dld,
const vk::PhysicalDevice& physdev,
uint32_t qfi,
bool fp16
);
}