refactor: start refactoring core Vulkan abstractions

This commit is contained in:
PancakeTAS 2025-09-01 16:47:55 +02:00
parent 0048283a8a
commit b86291b951
No known key found for this signature in database
73 changed files with 686 additions and 656 deletions

View file

@ -14,22 +14,9 @@ project(lsfg-vk-framegen
LANGUAGES C CXX)
file(GLOB SOURCES
"src/common/*.cpp"
"src/config/*.cpp"
"src/core/*.cpp"
"src/pool/*.cpp"
"src/vk/core/*.cpp"
"src/vk/*.cpp"
"src/*.cpp"
"v3.1_src/core/*.cpp"
"v3.1_src/pool/*.cpp"
"v3.1_src/shaders/*.cpp"
"v3.1_src/utils/*.cpp"
"v3.1_src/*.cpp"
"v3.1p_src/core/*.cpp"
"v3.1p_src/pool/*.cpp"
"v3.1p_src/shaders/*.cpp"
"v3.1p_src/utils/*.cpp"
"v3.1p_src/*.cpp"
"src/thirdparty/*.c"
)
add_library(lsfg-vk-framegen STATIC ${SOURCES})
@ -41,10 +28,7 @@ set_target_properties(lsfg-vk-framegen PROPERTIES
target_include_directories(lsfg-vk-framegen SYSTEM
PUBLIC include/thirdparty)
target_include_directories(lsfg-vk-framegen
PUBLIC include
PUBLIC public
PRIVATE v3.1_include
PRIVATE v3.1p_include)
PUBLIC include)
# diagnostics
if(CMAKE_BUILD_TYPE STREQUAL "Debug")

View file

@ -1,57 +0,0 @@
#pragma once
#include "core/instance.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <memory>
namespace LSFG::Core {
///
/// C++ wrapper class for a Vulkan device.
///
/// This class manages the lifetime of a Vulkan device.
///
class Device {
public:
///
/// Create the device.
///
/// @param instance Vulkan instance
/// @param deviceUUID The UUID of the Vulkan device to use.
/// @param forceDisableFp16 Force-disable FP16 shaders.
///
/// @throws LSFG::vulkan_error if object creation fails.
///
Device(const Instance& instance, uint64_t deviceUUID, bool forceDisableFp16);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->device; }
/// Get the physical device associated with this logical device.
[[nodiscard]] VkPhysicalDevice getPhysicalDevice() const { return this->physicalDevice; }
/// Get the compute queue family index.
[[nodiscard]] uint32_t getComputeFamilyIdx() const { return this->computeFamilyIdx; }
/// Get the compute queue.
[[nodiscard]] VkQueue getComputeQueue() const { return this->computeQueue; }
/// Check if the device supports FP16.
[[nodiscard]] bool getFP16Support() const { return this->supportsFP16; }
// Trivially copyable, moveable and destructible
Device(const Core::Device&) noexcept = default;
Device& operator=(const Core::Device&) noexcept = default;
Device(Device&&) noexcept = default;
Device& operator=(Device&&) noexcept = default;
~Device() = default;
private:
std::shared_ptr<VkDevice> device;
VkPhysicalDevice physicalDevice{};
uint32_t computeFamilyIdx{0};
bool supportsFP16{false};
VkQueue computeQueue{};
};
}

View file

@ -1,80 +0,0 @@
#pragma once
#include "core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <optional>
#include <memory>
namespace LSFG::Core {
///
/// C++ wrapper class for a Vulkan semaphore.
///
/// This class manages the lifetime of a Vulkan semaphore.
///
class Semaphore {
public:
Semaphore() noexcept = default;
///
/// Create the semaphore.
///
/// @param device Vulkan device
/// @param initial Optional initial value for creating a timeline semaphore.
///
/// @throws LSFG::vulkan_error if object creation fails.
///
Semaphore(const Core::Device& device, std::optional<uint32_t> initial = std::nullopt);
///
/// Import a semaphore.
///
/// @param device Vulkan device
/// @param fd File descriptor to import the semaphore from.
///
/// @throws LSFG::vulkan_error if object creation fails.
///
Semaphore(const Core::Device& device, int fd);
///
/// Signal the semaphore to a specific value.
///
/// @param device Vulkan device
/// @param value The value to signal the semaphore to.
///
/// @throws std::logic_error if the semaphore is not a timeline semaphore.
/// @throws LSFG::vulkan_error if signaling fails.
///
void signal(const Core::Device& device, uint64_t value) const;
///
/// Wait for the semaphore to reach a specific value.
///
/// @param device Vulkan device
/// @param value The value to wait for.
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
/// @returns true if the semaphore reached the value, false if it timed out.
///
/// @throws std::logic_error if the semaphore is not a timeline semaphore.
/// @throws LSFG::vulkan_error if waiting fails.
///
[[nodiscard]] bool wait(const Core::Device& device, uint64_t value, uint64_t timeout = UINT64_MAX) const;
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->semaphore; }
// Trivially copyable, moveable and destructible
Semaphore(const Semaphore&) noexcept = default;
Semaphore& operator=(const Semaphore&) noexcept = default;
Semaphore(Semaphore&&) noexcept = default;
Semaphore& operator=(Semaphore&&) noexcept = default;
~Semaphore() = default;
private:
std::shared_ptr<VkSemaphore> semaphore;
bool isTimeline{};
};
}

View file

@ -1,13 +1,13 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <cstddef>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan buffer.
@ -25,13 +25,11 @@ namespace LSFG::Core {
/// @param data Initial data for the buffer, also specifies the size of the buffer.
/// @param usage Usage flags for the buffer
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
template<typename T>
Buffer(const Core::Device& device, const T& data, VkBufferUsageFlags usage)
: size(sizeof(T)) {
construct(device, reinterpret_cast<const void*>(&data), usage);
}
Buffer(const Device& device, const T& data, VkBufferUsageFlags usage)
: Buffer(device, reinterpret_cast<const void*>(&data), sizeof(T), usage) {}
///
/// Create the buffer.
@ -41,17 +39,16 @@ namespace LSFG::Core {
/// @param size Size of the buffer in bytes
/// @param usage Usage flags for the buffer
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Buffer(const Core::Device& device, const void* data, size_t size, VkBufferUsageFlags usage)
: size(size) {
construct(device, data, usage);
}
Buffer(const Device& device, const void* data, size_t size, VkBufferUsageFlags usage);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->buffer; }
/// Get the Vulkan device memory handle.
[[nodiscard]] auto getMemory() const { return *this->memory; }
/// Get the size of the buffer.
[[nodiscard]] size_t getSize() const { return this->size; }
[[nodiscard]] auto getSize() const { return this->size; }
/// Trivially copyable, moveable and destructible
Buffer(const Buffer&) noexcept = default;
@ -60,7 +57,6 @@ namespace LSFG::Core {
Buffer& operator=(Buffer&&) noexcept = default;
~Buffer() = default;
private:
void construct(const Core::Device& device, const void* data, VkBufferUsageFlags usage);
std::shared_ptr<VkBuffer> buffer;
std::shared_ptr<VkDeviceMemory> memory;

View file

@ -1,18 +1,19 @@
#pragma once
#include "core/commandpool.hpp"
#include "core/fence.hpp"
#include "core/semaphore.hpp"
#include "core/device.hpp"
#include "vk/core/commandpool.hpp"
#include "vk/core/semaphore.hpp"
#include "vk/core/pipeline.hpp"
#include "vk/core/device.hpp"
#include "vk/core/fence.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <optional>
#include <cstdint>
#include <vector>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
/// State of the command buffer.
enum class CommandBufferState {
@ -43,18 +44,34 @@ namespace LSFG::Core {
/// @param device Vulkan device
/// @param pool Vulkan command pool
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
CommandBuffer(const Core::Device& device, const CommandPool& pool);
CommandBuffer(const Device& device, const CommandPool& pool);
///
/// Begin recording commands in the command buffer.
///
/// @throws std::logic_error if the command buffer is in Empty state
/// @throws LSFG::vulkan_error if beginning the command buffer fails.
/// @throws std::logic_error if the command buffer is not in Empty state
/// @throws VK::vulkan_error if beginning the command buffer fails.
///
void begin();
///
/// Bind a compute pipeline to the command buffer.
///
/// @param pipeline Vulkan compute pipeline
///
/// @throws std::logic_error if the command buffer is not in Recording state
///
void bindPipeline(const Pipeline& pipeline) const;
// TODO: Method for binding a descriptor set.
// TODO: Rework abstraction for descriptor sets.
// TODO: Method for inserting a pipeline barrier.
// TODO: Rework abstraction for barriers.
// TODO: Method for copying a buffer to an image
// TODO: Method for clearing an image to a color
///
/// Dispatch a compute command.
///
@ -70,10 +87,12 @@ namespace LSFG::Core {
/// End recording commands in the command buffer.
///
/// @throws std::logic_error if the command buffer is not in Recording state
/// @throws LSFG::vulkan_error if ending the command buffer fails.
/// @throws VK::vulkan_error if ending the command buffer fails.
///
void end();
// FIXME: Submit logic is kind of janky.
///
/// Submit the command buffer to a queue.
///
@ -85,7 +104,7 @@ namespace LSFG::Core {
/// @param signalSemaphoreValues Values for the semaphores to signal
///
/// @throws std::logic_error if the command buffer is not in Full state.
/// @throws LSFG::vulkan_error if submission fails.
/// @throws VK::vulkan_error if submission fails.
///
void submit(VkQueue queue, std::optional<Fence> fence,
const std::vector<Semaphore>& waitSemaphores = {},
@ -94,7 +113,7 @@ namespace LSFG::Core {
std::optional<std::vector<uint64_t>> signalSemaphoreValues = std::nullopt);
/// Get the state of the command buffer.
[[nodiscard]] CommandBufferState getState() const { return *this->state; }
[[nodiscard]] auto getState() const { return *this->state; }
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->commandBuffer; }

View file

@ -1,12 +1,12 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan command pool.
@ -22,9 +22,9 @@ namespace LSFG::Core {
///
/// @param device Vulkan device
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
CommandPool(const Core::Device& device);
CommandPool(const Device& device);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->commandPool; }

View file

@ -1,12 +1,12 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan descriptor pool.
@ -22,9 +22,9 @@ namespace LSFG::Core {
///
/// @param device Vulkan device
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
DescriptorPool(const Core::Device& device);
DescriptorPool(const Device& device);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->descriptorPool; }

View file

@ -0,0 +1,75 @@
#pragma once
#include "vk/core/instance.hpp"
#include <vulkan/vulkan_core.h>
#include <optional>
#include <cstdint>
#include <bitset>
#include <memory>
namespace VK::Core {
// FIXME: More intelligent device choosing method. See #200.
///
/// C++ wrapper class for a Vulkan device.
///
/// This class manages the lifetime of a Vulkan device.
///
class Device {
public:
///
/// Create the device.
///
/// @param instance Vulkan instance
/// @param uuid The UUID of the Vulkan device to use.
/// @param enforceFP32 Whether to enforce FP32 support even if FP16 is available.
///
/// @throws VK::vulkan_error if object creation fails.
///
Device(const Instance& instance, uint64_t uuid, bool enforceFP32);
///
/// Find a suitable memory type.
///
/// @param validTypes A bitset representing the valid memory types.
/// @param hostVisible Whether the memory should be host visible.
///
/// @return The index of a suitable memory type, or std::nullopt if none found.
///
[[nodiscard]] std::optional<uint32_t> findMemoryType(
std::bitset<32> validTypes, bool hostVisible = false) const;
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->device; }
/// Get the physical device associated with this logical device.
[[nodiscard]] auto getPhysicalDevice() const { return this->physicalDevice; }
/// Get the compute queue.
[[nodiscard]] auto getComputeQueue() const { return this->computeQueue; }
/// Get the compute queue family index.
[[nodiscard]] auto getComputeFamilyIdx() const { return this->computeFamilyIdx; }
/// Check if the device supports FP16.
[[nodiscard]] auto supportsFP16() const { return this->fp16; }
// Trivially copyable, moveable and destructible
Device(const Device&) noexcept = default;
Device& operator=(const Device&) noexcept = default;
Device(Device&&) noexcept = default;
Device& operator=(Device&&) noexcept = default;
~Device() = default;
private:
std::shared_ptr<VkDevice> device;
VkPhysicalDevice physicalDevice{};
VkQueue computeQueue{};
uint32_t computeFamilyIdx{0};
bool fp16{false};
};
}

View file

@ -1,13 +1,13 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan fence.
@ -23,18 +23,18 @@ namespace LSFG::Core {
///
/// @param device Vulkan device
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Fence(const Core::Device& device);
Fence(const Device& device);
///
/// Reset the fence to an unsignaled state.
///
/// @param device Vulkan device
///
/// @throws LSFG::vulkan_error if resetting fails.
/// @throws VK::vulkan_error if resetting fails.
///
void reset(const Core::Device& device) const;
void reset(const Device& device) const;
///
/// Wait for the fence
@ -43,9 +43,9 @@ namespace LSFG::Core {
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
/// @returns true if the fence signaled, false if it timed out.
///
/// @throws LSFG::vulkan_error if waiting fails.
/// @throws VK::vulkan_error if waiting fails.
///
[[nodiscard]] bool wait(const Core::Device& device, uint64_t timeout = UINT64_MAX) const;
[[nodiscard]] bool wait(const Device& device, uint64_t timeout = UINT64_MAX) const;
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->fence; }

View file

@ -1,12 +1,14 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
// TODO: Refactoring
///
/// C++ wrapper class for a Vulkan image.
@ -26,9 +28,9 @@ namespace LSFG::Core {
/// @param usage Usage flags for the image
/// @param aspectFlags Aspect flags for the image view
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Image(const Core::Device& device, VkExtent2D extent,
Image(const Device& device, VkExtent2D extent,
VkFormat format = VK_FORMAT_R8G8B8A8_UNORM,
VkImageUsageFlags usage = VK_IMAGE_USAGE_STORAGE_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,
VkImageAspectFlags aspectFlags = VK_IMAGE_ASPECT_COLOR_BIT);
@ -43,9 +45,9 @@ namespace LSFG::Core {
/// @param aspectFlags Aspect flags for the image view
/// @param fd File descriptor for shared memory.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd);
///
@ -58,9 +60,9 @@ namespace LSFG::Core {
/// @param aspectFlags Aspect flags for the image view
/// @param fd Pointer to an integer where the file descriptor will be stored.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int* fd);
/// Get the Vulkan handle.
@ -70,11 +72,11 @@ namespace LSFG::Core {
/// Get the Vulkan image view handle.
[[nodiscard]] auto getView() const { return *this->view; }
/// Get the extent of the image.
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
[[nodiscard]] auto getExtent() const { return this->extent; }
/// Get the format of the image.
[[nodiscard]] VkFormat getFormat() const { return this->format; }
[[nodiscard]] auto getFormat() const { return this->format; }
/// Get the aspect flags of the image.
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
[[nodiscard]] auto getAspectFlags() const { return this->aspectFlags; }
/// Set the layout of the image.
void setLayout(VkImageLayout layout) { *this->layout = layout; }

View file

@ -4,7 +4,7 @@
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan instance.
@ -16,12 +16,12 @@ namespace LSFG::Core {
///
/// Create the instance.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Instance();
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return this->instance ? *this->instance : VK_NULL_HANDLE; }
[[nodiscard]] auto handle() const { return *this->instance; }
/// Trivially copyable, moveable and destructible
Instance(const Instance&) noexcept = default;

View file

@ -1,14 +1,13 @@
#pragma once
#include "core/commandbuffer.hpp"
#include "core/shadermodule.hpp"
#include "core/device.hpp"
#include "vk/core/shadermodule.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan pipeline.
@ -25,16 +24,9 @@ namespace LSFG::Core {
/// @param device Vulkan device
/// @param shader Shader module to use for the pipeline.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Pipeline(const Core::Device& device, const ShaderModule& shader);
///
/// Bind the pipeline to a command buffer.
///
/// @param commandBuffer Command buffer to bind the pipeline to.
///
void bind(const CommandBuffer& commandBuffer) const;
Pipeline(const Device& device, const ShaderModule& shader);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->pipeline; }

View file

@ -1,12 +1,12 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan sampler.
@ -25,12 +25,10 @@ namespace LSFG::Core {
/// @param compare Compare operation for the sampler.
/// @param isWhite Whether the border color is white.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
Sampler(const Core::Device& device,
VkSamplerAddressMode mode,
VkCompareOp compare,
bool isWhite);
Sampler(const Device& device,
VkSamplerAddressMode mode, VkCompareOp compare, bool isWhite);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->sampler; }

View file

@ -0,0 +1,44 @@
#pragma once
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <optional>
#include <memory>
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan semaphore.
///
/// This class manages the lifetime of a Vulkan semaphore.
///
class Semaphore {
public:
Semaphore() noexcept = default;
///
/// Create/Import a semaphore.
///
/// @param device Vulkan device
/// @param fd Optional file descriptor to import the semaphore from.
///
/// @throws VK::vulkan_error if object creation fails.
///
Semaphore(const Device& device, std::optional<int> fd = std::nullopt);
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->semaphore; }
// Trivially copyable, moveable and destructible
Semaphore(const Semaphore&) noexcept = default;
Semaphore& operator=(const Semaphore&) noexcept = default;
Semaphore(Semaphore&&) noexcept = default;
Semaphore& operator=(Semaphore&&) noexcept = default;
~Semaphore() = default;
private:
std::shared_ptr<VkSemaphore> semaphore;
};
}

View file

@ -1,6 +1,6 @@
#pragma once
#include "core/device.hpp"
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
@ -10,7 +10,7 @@
#include <vector>
#include <memory>
namespace LSFG::Core {
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan shader module.
@ -28,9 +28,9 @@ namespace LSFG::Core {
/// @param code SPIR-V bytecode for the shader.
/// @param descriptorTypes Descriptor types used in the shader.
///
/// @throws LSFG::vulkan_error if object creation fails.
/// @throws VK::vulkan_error if object creation fails.
///
ShaderModule(const Core::Device& device, const std::vector<uint8_t>& code,
ShaderModule(const Device& device, const std::vector<uint8_t>& code,
const std::vector<std::pair<size_t, VkDescriptorType>>& descriptorTypes);
/// Get the Vulkan handle.

View file

@ -0,0 +1,67 @@
#pragma once
#include "vk/core/device.hpp"
#include <vulkan/vulkan_core.h>
#include <cstdint>
#include <memory>
namespace VK::Core {
///
/// C++ wrapper class for a Vulkan timeline semaphore.
///
/// This class manages the lifetime of a Vulkan timeline semaphore.
///
class TimelineSemaphore {
public:
TimelineSemaphore() noexcept = default;
///
/// Create the timeline semaphore.
///
/// @param device Vulkan device
/// @param initial Initial value of the timeline semaphore.
///
/// @throws VK::vulkan_error if object creation fails.
///
TimelineSemaphore(const Device& device, uint32_t initial);
///
/// Signal the timeline semaphore to a specific value.
///
/// @param device Vulkan device
/// @param value The value to signal the semaphore to.
///
/// @throws VK::vulkan_error if signaling fails.
///
void signal(const Device& device, uint64_t value) const;
///
/// Wait for the timeline semaphore to reach a specific value.
///
/// @param device Vulkan device
/// @param value The value to wait for.
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
/// @returns true if the semaphore reached the value, false if it timed out.
///
/// @throws VK::vulkan_error if waiting fails.
///
[[nodiscard]] bool wait(const Device& device,
uint64_t value, uint64_t timeout = UINT64_MAX) const;
/// Get the Vulkan handle.
[[nodiscard]] auto handle() const { return *this->semaphore; }
// Trivially copyable, moveable and destructible
TimelineSemaphore(const TimelineSemaphore&) noexcept = default;
TimelineSemaphore& operator=(const TimelineSemaphore&) noexcept = default;
TimelineSemaphore(TimelineSemaphore&&) noexcept = default;
TimelineSemaphore& operator=(TimelineSemaphore&&) noexcept = default;
~TimelineSemaphore() = default;
private:
std::shared_ptr<VkSemaphore> semaphore;
};
}

View file

@ -6,7 +6,7 @@
#include <stdexcept>
#include <string>
namespace LSFG {
namespace VK {
/// Simple exception class for Vulkan errors.
class vulkan_error : public std::runtime_error {

View file

@ -1,134 +0,0 @@
#include <iostream>
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/device.hpp"
#include "core/instance.hpp"
#include "common/exception.hpp"
#include <cstdint>
#include <memory>
#include <optional>
#include <vector>
using namespace LSFG::Core;
const std::vector<const char*> requiredExtensions = {
"VK_KHR_external_memory_fd",
"VK_KHR_external_semaphore_fd",
"VK_EXT_robustness2"
};
Device::Device(const Instance& instance, uint64_t deviceUUID, bool forceDisableFp16) {
// get all physical devices
uint32_t deviceCount{};
auto res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, nullptr);
if (res != VK_SUCCESS || deviceCount == 0)
throw LSFG::vulkan_error(res, "Failed to enumerate physical devices");
std::vector<VkPhysicalDevice> devices(deviceCount);
res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, devices.data());
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to get physical devices");
// get device by uuid
std::optional<VkPhysicalDevice> physicalDevice;
for (const auto& device : devices) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties);
const uint64_t uuid =
static_cast<uint64_t>(properties.vendorID) << 32 | properties.deviceID;
if (deviceUUID == uuid || deviceUUID == 0x1463ABAC) {
physicalDevice = device;
break;
}
}
if (!physicalDevice)
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED,
"Could not find physical device with UUID");
// find queue family indices
uint32_t familyCount{};
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(*physicalDevice, &familyCount, queueFamilies.data());
std::optional<uint32_t> computeFamilyIdx;
for (uint32_t i = 0; i < familyCount; ++i) {
if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
computeFamilyIdx = i;
}
if (!computeFamilyIdx)
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No compute queue family found");
// check if physical device supports float16
VkPhysicalDeviceVulkan12Features supported12Features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES
};
VkPhysicalDeviceFeatures2 supportedFeatures{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &supported12Features
};
vkGetPhysicalDeviceFeatures2(*physicalDevice, &supportedFeatures);
this->supportsFP16 = !forceDisableFp16 && supported12Features.shaderFloat16;
if (this->supportsFP16)
std::cerr << "lsfg-vk: Using FP16 acceleration" << '\n';
else if (!forceDisableFp16)
std::cerr << "lsfg-vk: FP16 acceleration not supported, using FP32" << '\n';
// create logical device
const float queuePriority{1.0F}; // highest priority
VkPhysicalDeviceRobustness2FeaturesEXT robustness2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
.nullDescriptor = VK_TRUE
};
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &robustness2,
.synchronization2 = VK_TRUE
};
const VkPhysicalDeviceVulkan12Features features12{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = &features13,
.shaderFloat16 = this->supportsFP16,
.timelineSemaphore = VK_TRUE,
.vulkanMemoryModel = VK_TRUE
};
const VkDeviceQueueCreateInfo computeQueueDesc{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = *computeFamilyIdx,
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
const VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &features12,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &computeQueueDesc,
.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()),
.ppEnabledExtensionNames = requiredExtensions.data()
};
VkDevice deviceHandle{};
res = vkCreateDevice(*physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);
if (res != VK_SUCCESS | deviceHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create logical device");
volkLoadDevice(deviceHandle);
// get compute queue
VkQueue queueHandle{};
vkGetDeviceQueue(deviceHandle, *computeFamilyIdx, 0, &queueHandle);
// store in shared ptr
this->computeQueue = queueHandle;
this->computeFamilyIdx = *computeFamilyIdx;
this->physicalDevice = *physicalDevice;
this->device = std::shared_ptr<VkDevice>(
new VkDevice(deviceHandle),
[](VkDevice* device) {
vkDestroyDevice(*device, nullptr);
}
);
}

View file

@ -1,110 +0,0 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/semaphore.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include <optional>
#include <cstdint>
#include <memory>
#include <stdexcept>
using namespace LSFG::Core;
Semaphore::Semaphore(const Core::Device& device, std::optional<uint32_t> initial) {
// create semaphore
const VkSemaphoreTypeCreateInfo typeInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = initial.value_or(0)
};
const VkSemaphoreCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = initial.has_value() ? &typeInfo : nullptr,
};
VkSemaphore semaphoreHandle{};
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create semaphore");
// store semaphore in shared ptr
this->isTimeline = initial.has_value();
this->semaphore = std::shared_ptr<VkSemaphore>(
new VkSemaphore(semaphoreHandle),
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
}
);
}
Semaphore::Semaphore(const Core::Device& device, int fd) {
// create semaphore
const VkExportSemaphoreCreateInfo exportInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
};
const VkSemaphoreCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &exportInfo
};
VkSemaphore semaphoreHandle{};
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create semaphore");
// import semaphore from fd
auto vkImportSemaphoreFdKHR = reinterpret_cast<PFN_vkImportSemaphoreFdKHR>(
vkGetDeviceProcAddr(device.handle(), "vkImportSemaphoreFdKHR"));
const VkImportSemaphoreFdInfoKHR importInfo{
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
.semaphore = semaphoreHandle,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
.fd = fd // closes the fd
};
res = vkImportSemaphoreFdKHR(device.handle(), &importInfo);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to import semaphore from fd");
// store semaphore in shared ptr
this->isTimeline = false;
this->semaphore = std::shared_ptr<VkSemaphore>(
new VkSemaphore(semaphoreHandle),
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
}
);
}
void Semaphore::signal(const Core::Device& device, uint64_t value) const {
if (!this->isTimeline)
throw std::logic_error("Invalid timeline semaphore");
const VkSemaphoreSignalInfo signalInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
.semaphore = this->handle(),
.value = value
};
auto res = vkSignalSemaphore(device.handle(), &signalInfo);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to signal semaphore");
}
bool Semaphore::wait(const Core::Device& device, uint64_t value, uint64_t timeout) const {
if (!this->isTimeline)
throw std::logic_error("Invalid timeline semaphore");
VkSemaphore semaphore = this->handle();
const VkSemaphoreWaitInfo waitInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
.semaphoreCount = 1,
.pSemaphores = &semaphore,
.pValues = &value
};
auto res = vkWaitSemaphores(device.handle(), &waitInfo, timeout);
if (res != VK_SUCCESS && res != VK_TIMEOUT)
throw LSFG::vulkan_error(res, "Unable to wait for semaphore");
return res == VK_SUCCESS;
}

View file

@ -1,51 +1,38 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/buffer.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/buffer.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <algorithm>
#include <optional>
#include <cstddef>
#include <cstdint>
#include <memory>
#include <optional>
using namespace LSFG::Core;
using namespace VK::Core;
void Buffer::construct(const Core::Device& device, const void* data, VkBufferUsageFlags usage) {
Buffer::Buffer(const Device& device, const void* data, size_t size, VkBufferUsageFlags usage) {
// create buffer
const VkBufferCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO,
.size = this->size,
.size = size,
.usage = usage,
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
};
VkBuffer bufferHandle{};
auto res = vkCreateBuffer(device.handle(), &desc, nullptr, &bufferHandle);
if (res != VK_SUCCESS || bufferHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create Vulkan buffer");
throw VK::vulkan_error(res, "Failed to create Vulkan buffer");
// find memory type
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
VkMemoryRequirements memReqs;
vkGetBufferMemoryRequirements(device.handle(), bufferHandle, &memReqs);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
std::optional<uint32_t> memType{};
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
(memProps.memoryTypes[i].propertyFlags &
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT))) {
memType.emplace(i);
break;
} // NOLINTEND
}
std::optional<uint32_t> memType = device.findMemoryType(memReqs.memoryTypeBits, true);
if (!memType.has_value())
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
#pragma clang diagnostic pop
throw VK::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
// allocate and bind memory
const VkMemoryAllocateInfo allocInfo{
@ -56,21 +43,22 @@ void Buffer::construct(const Core::Device& device, const void* data, VkBufferUsa
VkDeviceMemory memoryHandle{};
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan buffer");
throw VK::vulkan_error(res, "Failed to allocate memory for Vulkan buffer");
res = vkBindBufferMemory(device.handle(), bufferHandle, memoryHandle, 0);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan buffer");
throw VK::vulkan_error(res, "Failed to bind memory to Vulkan buffer");
// upload data to buffer
uint8_t* buf{};
res = vkMapMemory(device.handle(), memoryHandle, 0, this->size, 0, reinterpret_cast<void**>(&buf));
res = vkMapMemory(device.handle(), memoryHandle, 0, size, 0, reinterpret_cast<void**>(&buf));
if (res != VK_SUCCESS || buf == nullptr)
throw LSFG::vulkan_error(res, "Failed to map memory for Vulkan buffer");
std::copy_n(reinterpret_cast<const uint8_t*>(data), this->size, buf);
throw VK::vulkan_error(res, "Failed to map memory for Vulkan buffer");
std::copy_n(reinterpret_cast<const uint8_t*>(data), size, buf);
vkUnmapMemory(device.handle(), memoryHandle);
// store buffer and memory in shared ptr
this->size = size;
this->buffer = std::shared_ptr<VkBuffer>(
new VkBuffer(bufferHandle),
[dev = device.handle()](VkBuffer* img) {

View file

@ -1,22 +1,23 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/commandbuffer.hpp"
#include "core/device.hpp"
#include "core/commandpool.hpp"
#include "core/fence.hpp"
#include "core/semaphore.hpp"
#include "common/exception.hpp"
#include "vk/core/commandbuffer.hpp"
#include "vk/core/commandpool.hpp"
#include "vk/core/semaphore.hpp"
#include "vk/core/pipeline.hpp"
#include "vk/core/device.hpp"
#include "vk/core/fence.hpp"
#include "vk/exception.hpp"
#include <memory>
#include <stdexcept>
#include <cstdint>
#include <optional>
#include <cstdint>
#include <memory>
#include <vector>
using namespace LSFG::Core;
using namespace VK::Core;
CommandBuffer::CommandBuffer(const Core::Device& device, const CommandPool& pool) {
CommandBuffer::CommandBuffer(const Device& device, const CommandPool& pool) {
// create command buffer
const VkCommandBufferAllocateInfo desc{
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
@ -27,7 +28,7 @@ CommandBuffer::CommandBuffer(const Core::Device& device, const CommandPool& pool
VkCommandBuffer commandBufferHandle{};
auto res = vkAllocateCommandBuffers(device.handle(), &desc, &commandBufferHandle);
if (res != VK_SUCCESS || commandBufferHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to allocate command buffer");
throw VK::vulkan_error(res, "Unable to allocate command buffer");
// store command buffer in shared ptr
this->state = std::make_shared<CommandBufferState>(CommandBufferState::Empty);
@ -49,11 +50,18 @@ void CommandBuffer::begin() {
};
auto res = vkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to begin command buffer");
throw VK::vulkan_error(res, "Unable to begin command buffer");
*this->state = CommandBufferState::Recording;
}
void CommandBuffer::bindPipeline(const Pipeline& pipeline) const {
if (*this->state != CommandBufferState::Recording)
throw std::logic_error("Command buffer is not in Recording state");
vkCmdBindPipeline(*this->commandBuffer, VK_PIPELINE_BIND_POINT_COMPUTE, pipeline.handle());
}
void CommandBuffer::dispatch(uint32_t x, uint32_t y, uint32_t z) const {
if (*this->state != CommandBufferState::Recording)
throw std::logic_error("Command buffer is not in Recording state");
@ -67,7 +75,7 @@ void CommandBuffer::end() {
auto res = vkEndCommandBuffer(*this->commandBuffer);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to end command buffer");
throw VK::vulkan_error(res, "Unable to end command buffer");
*this->state = CommandBufferState::Full;
}
@ -119,7 +127,7 @@ void CommandBuffer::submit(VkQueue queue, std::optional<Fence> fence,
};
auto res = vkQueueSubmit(queue, 1, &submitInfo, fence ? fence->handle() : VK_NULL_HANDLE);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to submit command buffer");
throw VK::vulkan_error(res, "Unable to submit command buffer");
*this->state = CommandBufferState::Submitted;
}

View file

@ -1,15 +1,15 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/commandpool.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/commandpool.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <memory>
using namespace LSFG::Core;
using namespace VK::Core;
CommandPool::CommandPool(const Core::Device& device) {
CommandPool::CommandPool(const Device& device) {
// create command pool
const VkCommandPoolCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
@ -18,7 +18,7 @@ CommandPool::CommandPool(const Core::Device& device) {
VkCommandPool commandPoolHandle{};
auto res = vkCreateCommandPool(device.handle(), &desc, nullptr, &commandPoolHandle);
if (res != VK_SUCCESS || commandPoolHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create command pool");
throw VK::vulkan_error(res, "Unable to create command pool");
// store command pool in shared ptr
this->commandPool = std::shared_ptr<VkCommandPool>(

View file

@ -1,17 +1,17 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/descriptorpool.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/descriptorpool.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <array>
#include <cstdint>
#include <memory>
#include <array>
using namespace LSFG::Core;
using namespace VK::Core;
DescriptorPool::DescriptorPool(const Core::Device& device) {
DescriptorPool::DescriptorPool(const Device& device) {
// create descriptor pool
const std::array<VkDescriptorPoolSize, 4> pools{{ // arbitrary limits
{ .type = VK_DESCRIPTOR_TYPE_SAMPLER, .descriptorCount = 4096 },
@ -29,7 +29,7 @@ DescriptorPool::DescriptorPool(const Core::Device& device) {
VkDescriptorPool poolHandle{};
auto res = vkCreateDescriptorPool(device.handle(), &desc, nullptr, &poolHandle);
if (res != VK_SUCCESS || poolHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create descriptor pool");
throw VK::vulkan_error(res, "Unable to create descriptor pool");
// store pool in shared ptr
this->descriptorPool = std::shared_ptr<VkDescriptorPool>(

View file

@ -0,0 +1,169 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "vk/core/instance.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <iostream>
#include <optional>
#include <cstdint>
#include <bitset>
#include <memory>
#include <vector>
#include <array>
using namespace VK::Core;
const std::vector<const char*> requiredExtensions = {
"VK_KHR_external_memory_fd",
"VK_KHR_external_semaphore_fd",
"VK_EXT_robustness2"
};
namespace {
/// Find a physical device by its UUID.
std::optional<VkPhysicalDevice> findDeviceByUUID(const Instance& instance, uint64_t uuid) {
uint32_t deviceCount{};
auto res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, nullptr);
if (res != VK_SUCCESS || deviceCount == 0)
throw VK::vulkan_error(res, "Failed to enumerate physical devices");
std::vector<VkPhysicalDevice> devices(deviceCount);
res = vkEnumeratePhysicalDevices(instance.handle(), &deviceCount, devices.data());
if (res != VK_SUCCESS)
throw VK::vulkan_error(res, "Failed to get physical devices");
for (const auto& device : devices) {
VkPhysicalDeviceProperties properties;
vkGetPhysicalDeviceProperties(device, &properties);
const uint64_t gpuid =
static_cast<uint64_t>(properties.vendorID) << 32 | properties.deviceID;
if (uuid == gpuid || uuid == 0x1463ABAC)
return device;
}
return std::nullopt;
}
/// Find the compute queue family index.
std::optional<uint32_t> findComputeQueueFamily(VkPhysicalDevice device) {
uint32_t familyCount{};
vkGetPhysicalDeviceQueueFamilyProperties(device, &familyCount, nullptr);
std::vector<VkQueueFamilyProperties> queueFamilies(familyCount);
vkGetPhysicalDeviceQueueFamilyProperties(device, &familyCount, queueFamilies.data());
for (uint32_t i = 0; i < familyCount; ++i) {
if (queueFamilies[i].queueFlags & VK_QUEUE_COMPUTE_BIT)
return i;
}
return std::nullopt;
}
/// Query the physical device for FP16 support.
bool checkFP16Support(VkPhysicalDevice device) {
VkPhysicalDeviceVulkan12Features features{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES
};
VkPhysicalDeviceFeatures2 features2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2,
.pNext = &features
};
vkGetPhysicalDeviceFeatures2(device, &features2);
return features.shaderFloat16;
}
}
Device::Device(const Instance& instance, uint64_t uuid, bool enforceFP32) {
// get device by uuid
std::optional<VkPhysicalDevice> physicalDevice = findDeviceByUUID(instance, uuid);
if (!physicalDevice)
throw VK::vulkan_error(VK_ERROR_INITIALIZATION_FAILED,
"Could not find physical device with UUID");
// find queue family indices
std::optional<uint32_t> computeFamilyIdx = findComputeQueueFamily(*physicalDevice);
if (!computeFamilyIdx)
throw VK::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No compute queue family found");
// print fp16 debug info
const bool fp16 = !enforceFP32 && checkFP16Support(*physicalDevice);
if (fp16)
std::cerr << "lsfg-vk: Using FP16 acceleration" << '\n';
else if (!enforceFP32)
std::cerr << "lsfg-vk: FP16 acceleration not supported, using FP32" << '\n';
// create logical device
const float queuePriority{1.0F}; // highest priority
VkPhysicalDeviceRobustness2FeaturesEXT robustness2{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ROBUSTNESS_2_FEATURES_EXT,
.nullDescriptor = VK_TRUE
};
VkPhysicalDeviceVulkan13Features features13{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_3_FEATURES,
.pNext = &robustness2,
.synchronization2 = VK_TRUE
};
const VkPhysicalDeviceVulkan12Features features12{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES,
.pNext = &features13,
.shaderFloat16 = fp16,
.timelineSemaphore = VK_TRUE,
.vulkanMemoryModel = VK_TRUE
};
const VkDeviceQueueCreateInfo computeQueueDesc{
.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO,
.queueFamilyIndex = *computeFamilyIdx,
.queueCount = 1,
.pQueuePriorities = &queuePriority
};
const VkDeviceCreateInfo deviceCreateInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
.pNext = &features12,
.queueCreateInfoCount = 1,
.pQueueCreateInfos = &computeQueueDesc,
.enabledExtensionCount = static_cast<uint32_t>(requiredExtensions.size()),
.ppEnabledExtensionNames = requiredExtensions.data()
};
VkDevice deviceHandle{};
auto res = vkCreateDevice(*physicalDevice, &deviceCreateInfo, nullptr, &deviceHandle);
if (res != VK_SUCCESS | deviceHandle == VK_NULL_HANDLE)
throw VK::vulkan_error(res, "Failed to create logical device");
// get queue
volkLoadDevice(deviceHandle);
VkQueue queueHandle{};
vkGetDeviceQueue(deviceHandle, *computeFamilyIdx, 0, &queueHandle);
if (!queueHandle)
throw VK::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "Failed to get compute queue");
// store in shared ptr
this->fp16 = fp16;
this->computeFamilyIdx = *computeFamilyIdx;
this->computeQueue = queueHandle;
this->physicalDevice = *physicalDevice;
this->device = std::shared_ptr<VkDevice>(
new VkDevice(deviceHandle),
[](VkDevice* device) {
vkDestroyDevice(*device, nullptr);
}
);
}
std::optional<uint32_t> Device::findMemoryType(std::bitset<32> validTypes, bool hostVisible) const {
const VkMemoryPropertyFlags desiredProps = hostVisible ?
(VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT) :
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(this->physicalDevice, &memProps);
std::array<VkMemoryType, 32> memTypes = std::to_array(memProps.memoryTypes);
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i)
if (validTypes.test(i) && (memTypes.at(i).propertyFlags & desiredProps) == desiredProps)
return i;
return std::nullopt;
}

View file

@ -1,24 +1,24 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/fence.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/device.hpp"
#include "vk/core/fence.hpp"
#include "vk/exception.hpp"
#include <memory>
#include <cstdint>
#include <memory>
using namespace LSFG::Core;
using namespace VK::Core;
Fence::Fence(const Core::Device& device) {
// create fence
Fence::Fence(const Device& device) {
// create fence
const VkFenceCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_FENCE_CREATE_INFO
};
VkFence fenceHandle{};
auto res = vkCreateFence(device.handle(), &desc, nullptr, &fenceHandle);
if (res != VK_SUCCESS || fenceHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create fence");
throw VK::vulkan_error(res, "Unable to create fence");
// store fence in shared ptr
this->fence = std::shared_ptr<VkFence>(
@ -29,18 +29,18 @@ Fence::Fence(const Core::Device& device) {
);
}
void Fence::reset(const Core::Device& device) const {
void Fence::reset(const Device& device) const {
VkFence fenceHandle = this->handle();
auto res = vkResetFences(device.handle(), 1, &fenceHandle);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Unable to reset fence");
throw VK::vulkan_error(res, "Unable to reset fence");
}
bool Fence::wait(const Core::Device& device, uint64_t timeout) const {
bool Fence::wait(const Device& device, uint64_t timeout) const {
VkFence fenceHandle = this->handle();
auto res = vkWaitForFences(device.handle(), 1, &fenceHandle, VK_TRUE, timeout);
if (res != VK_SUCCESS && res != VK_TIMEOUT)
throw LSFG::vulkan_error(res, "Unable to wait for fence");
throw VK::vulkan_error(res, "Unable to wait for fence");
return res == VK_SUCCESS;
}

View file

@ -1,17 +1,17 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/image.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/device.hpp"
#include "vk/core/image.hpp"
#include "vk/exception.hpp"
#include <optional>
#include <cstdint>
#include <memory>
#include <optional>
using namespace LSFG::Core;
using namespace VK::Core;
Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
Image::Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags)
: extent(extent), format(format), aspectFlags(aspectFlags) {
// create image
@ -33,28 +33,15 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImage imageHandle{};
auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle);
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
throw VK::vulkan_error(res, "Failed to create Vulkan image");
// find memory type
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
std::optional<uint32_t> memType{};
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
memType.emplace(i);
break;
} // NOLINTEND
}
std::optional<uint32_t> memType = device.findMemoryType(memReqs.memoryTypeBits, false);
if (!memType.has_value())
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
#pragma clang diagnostic pop
throw VK::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
// allocate and bind memory
const VkMemoryAllocateInfo allocInfo{
@ -65,11 +52,11 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkDeviceMemory memoryHandle{};
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
throw VK::vulkan_error(res, "Failed to allocate memory for Vulkan image");
res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
throw VK::vulkan_error(res, "Failed to bind memory to Vulkan image");
// create image view
const VkImageViewCreateInfo viewDesc{
@ -93,7 +80,7 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImageView viewHandle{};
res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle);
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create image view");
throw VK::vulkan_error(res, "Failed to create image view");
// store objects in shared ptr
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);
@ -119,7 +106,9 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
// shared memory constructor
Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
// FIXME: Less constructors... jesus.
Image::Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int fd)
: extent(extent), format(format), aspectFlags(aspectFlags) {
// create image
@ -146,28 +135,15 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImage imageHandle{};
auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle);
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
throw VK::vulkan_error(res, "Failed to create Vulkan image");
// find memory type
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
std::optional<uint32_t> memType{};
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
memType.emplace(i);
break;
} // NOLINTEND
}
std::optional<uint32_t> memType = device.findMemoryType(memReqs.memoryTypeBits, false);
if (!memType.has_value())
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
#pragma clang diagnostic pop
throw VK::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
// ~~allocate~~ and bind memory
const VkMemoryDedicatedAllocateInfoKHR dedicatedInfo2{
@ -189,11 +165,11 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkDeviceMemory memoryHandle{};
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
throw VK::vulkan_error(res, "Failed to allocate memory for Vulkan image");
res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
throw VK::vulkan_error(res, "Failed to bind memory to Vulkan image");
// create image view
const VkImageViewCreateInfo viewDesc{
@ -217,7 +193,7 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImageView viewHandle{};
res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle);
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create image view");
throw VK::vulkan_error(res, "Failed to create image view");
// store objects in shared ptr
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);
@ -243,7 +219,7 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
// second shared memory constructors
Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
Image::Image(const Device& device, VkExtent2D extent, VkFormat format,
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int* fd)
: extent(extent), format(format), aspectFlags(aspectFlags) {
// create image
@ -270,28 +246,15 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImage imageHandle{};
auto res = vkCreateImage(device.handle(), &desc, nullptr, &imageHandle);
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
throw VK::vulkan_error(res, "Failed to create Vulkan image");
// find memory type
VkPhysicalDeviceMemoryProperties memProps;
vkGetPhysicalDeviceMemoryProperties(device.getPhysicalDevice(), &memProps);
VkMemoryRequirements memReqs;
vkGetImageMemoryRequirements(device.handle(), imageHandle, &memReqs);
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunsafe-buffer-usage"
std::optional<uint32_t> memType{};
for (uint32_t i = 0; i < memProps.memoryTypeCount; ++i) {
if ((memReqs.memoryTypeBits & (1 << i)) && // NOLINTBEGIN
(memProps.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT)) {
memType.emplace(i);
break;
} // NOLINTEND
}
std::optional<uint32_t> memType = device.findMemoryType(memReqs.memoryTypeBits);
if (!memType.has_value())
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
#pragma clang diagnostic pop
throw VK::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for buffer");
// allocate and bind memory
const VkMemoryDedicatedAllocateInfoKHR dedicatedInfo{
@ -312,11 +275,11 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkDeviceMemory memoryHandle{};
res = vkAllocateMemory(device.handle(), &allocInfo, nullptr, &memoryHandle);
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
throw VK::vulkan_error(res, "Failed to allocate memory for Vulkan image");
res = vkBindImageMemory(device.handle(), imageHandle, memoryHandle, 0);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
throw VK::vulkan_error(res, "Failed to bind memory to Vulkan image");
// obtain the sharing fd
const VkMemoryGetFdInfoKHR fdInfo{
@ -326,7 +289,7 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
};
res = vkGetMemoryFdKHR(device.handle(), &fdInfo, fd);
if (res != VK_SUCCESS || *fd < 0)
throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
throw VK::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
// create image view
const VkImageViewCreateInfo viewDesc{
@ -350,7 +313,7 @@ Image::Image(const Core::Device& device, VkExtent2D extent, VkFormat format,
VkImageView viewHandle{};
res = vkCreateImageView(device.handle(), &viewDesc, nullptr, &viewHandle);
if (res != VK_SUCCESS || viewHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Failed to create image view");
throw VK::vulkan_error(res, "Failed to create image view");
// store objects in shared ptr
this->layout = std::make_shared<VkImageLayout>(VK_IMAGE_LAYOUT_UNDEFINED);

View file

@ -1,21 +1,21 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/instance.hpp"
#include "common/exception.hpp"
#include "vk/core/instance.hpp"
#include "vk/exception.hpp"
#include <cstdint>
#include <memory>
#include <vector>
using namespace LSFG::Core;
using namespace VK::Core;
const std::vector<const char*> requiredExtensions = {
// empty, for now :3
};
Instance::Instance() {
volkInitialize();
volkInitialize(); // FIXME: get rid of volk dependency fully
// create Vulkan instance
const VkApplicationInfo appInfo{
@ -35,7 +35,7 @@ Instance::Instance() {
VkInstance instanceHandle{};
auto res = vkCreateInstance(&createInfo, nullptr, &instanceHandle);
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to create Vulkan instance");
throw VK::vulkan_error(res, "Failed to create Vulkan instance");
volkLoadInstance(instanceHandle);

View file

@ -1,17 +1,16 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/pipeline.hpp"
#include "core/device.hpp"
#include "core/shadermodule.hpp"
#include "core/commandbuffer.hpp"
#include "common/exception.hpp"
#include "vk/core/shadermodule.hpp"
#include "vk/core/pipeline.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <memory>
using namespace LSFG::Core;
using namespace VK::Core;
Pipeline::Pipeline(const Core::Device& device, const ShaderModule& shader) {
Pipeline::Pipeline(const Device& device, const ShaderModule& shader) {
// create pipeline layout
VkDescriptorSetLayout shaderLayout = shader.getLayout();
const VkPipelineLayoutCreateInfo layoutDesc{
@ -22,7 +21,7 @@ Pipeline::Pipeline(const Core::Device& device, const ShaderModule& shader) {
VkPipelineLayout layoutHandle{};
auto res = vkCreatePipelineLayout(device.handle(), &layoutDesc, nullptr, &layoutHandle);
if (res != VK_SUCCESS || !layoutHandle)
throw LSFG::vulkan_error(res, "Failed to create pipeline layout");
throw VK::vulkan_error(res, "Failed to create pipeline layout");
// create pipeline
const VkPipelineShaderStageCreateInfo shaderStageInfo{
@ -40,7 +39,7 @@ Pipeline::Pipeline(const Core::Device& device, const ShaderModule& shader) {
res = vkCreateComputePipelines(device.handle(),
VK_NULL_HANDLE, 1, &pipelineDesc, nullptr, &pipelineHandle);
if (res != VK_SUCCESS || !pipelineHandle)
throw LSFG::vulkan_error(res, "Failed to create compute pipeline");
throw VK::vulkan_error(res, "Failed to create compute pipeline");
// store layout and pipeline in shared ptr
this->layout = std::shared_ptr<VkPipelineLayout>(
@ -56,7 +55,3 @@ Pipeline::Pipeline(const Core::Device& device, const ShaderModule& shader) {
}
);
}
void Pipeline::bind(const CommandBuffer& commandBuffer) const {
vkCmdBindPipeline(commandBuffer.handle(), VK_PIPELINE_BIND_POINT_COMPUTE, *this->pipeline);
}

View file

@ -1,18 +1,16 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/sampler.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/sampler.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <memory>
using namespace LSFG::Core;
using namespace VK::Core;
Sampler::Sampler(const Core::Device& device,
VkSamplerAddressMode mode,
VkCompareOp compare,
bool isWhite) {
Sampler::Sampler(const Device& device,
VkSamplerAddressMode mode, VkCompareOp compare, bool isWhite) {
// create sampler
const VkSamplerCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO,
@ -31,7 +29,7 @@ Sampler::Sampler(const Core::Device& device,
VkSampler samplerHandle{};
auto res = vkCreateSampler(device.handle(), &desc, nullptr, &samplerHandle);
if (res != VK_SUCCESS || samplerHandle == VK_NULL_HANDLE)
throw LSFG::vulkan_error(res, "Unable to create sampler");
throw VK::vulkan_error(res, "Unable to create sampler");
// store sampler in shared ptr
this->sampler = std::shared_ptr<VkSampler>(

View file

@ -0,0 +1,51 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "vk/core/semaphore.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <optional>
#include <memory>
using namespace VK::Core;
Semaphore::Semaphore(const Device& device, std::optional<int> fd) {
// create semaphore
const VkExportSemaphoreCreateInfo exportInfo{
.sType = VK_STRUCTURE_TYPE_EXPORT_SEMAPHORE_CREATE_INFO,
.handleTypes = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
};
const VkSemaphoreCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = fd.has_value() ? &exportInfo : nullptr
};
VkSemaphore semaphoreHandle{};
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
throw VK::vulkan_error(res, "Unable to create semaphore");
if (fd.has_value()) {
// import semaphore from fd
auto vkImportSemaphoreFdKHR = reinterpret_cast<PFN_vkImportSemaphoreFdKHR>(
vkGetDeviceProcAddr(device.handle(), "vkImportSemaphoreFdKHR"));
const VkImportSemaphoreFdInfoKHR importInfo{
.sType = VK_STRUCTURE_TYPE_IMPORT_SEMAPHORE_FD_INFO_KHR,
.semaphore = semaphoreHandle,
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT,
.fd = *fd // closes the fd
};
res = vkImportSemaphoreFdKHR(device.handle(), &importInfo);
if (res != VK_SUCCESS)
throw VK::vulkan_error(res, "Unable to import semaphore from fd");
}
// store semaphore in shared ptr
this->semaphore = std::shared_ptr<VkSemaphore>(
new VkSemaphore(semaphoreHandle),
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
}
);
}

View file

@ -1,19 +1,19 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "core/shadermodule.hpp"
#include "core/device.hpp"
#include "common/exception.hpp"
#include "vk/core/shadermodule.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <vector>
#include <cstddef>
#include <cstdint>
#include <utility>
#include <cstddef>
#include <vector>
#include <memory>
using namespace LSFG::Core;
using namespace VK::Core;
ShaderModule::ShaderModule(const Core::Device& device, const std::vector<uint8_t>& code,
ShaderModule::ShaderModule(const Device& device, const std::vector<uint8_t>& code,
const std::vector<std::pair<size_t, VkDescriptorType>>& descriptorTypes) {
// create shader module
const uint8_t* data_ptr = code.data();
@ -25,7 +25,7 @@ ShaderModule::ShaderModule(const Core::Device& device, const std::vector<uint8_t
VkShaderModule shaderModuleHandle{};
auto res = vkCreateShaderModule(device.handle(), &createInfo, nullptr, &shaderModuleHandle);
if (res != VK_SUCCESS || !shaderModuleHandle)
throw LSFG::vulkan_error(res, "Failed to create shader module");
throw VK::vulkan_error(res, "Failed to create shader module");
// create descriptor set layout
std::vector<VkDescriptorSetLayoutBinding> layoutBindings;
@ -50,7 +50,7 @@ ShaderModule::ShaderModule(const Core::Device& device, const std::vector<uint8_t
bindIdx = &outputIdx;
break;
default:
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unsupported descriptor type");
throw VK::vulkan_error(VK_ERROR_UNKNOWN, "Unsupported descriptor type");
}
layoutBindings.emplace_back(VkDescriptorSetLayoutBinding {
@ -71,7 +71,7 @@ ShaderModule::ShaderModule(const Core::Device& device, const std::vector<uint8_t
VkDescriptorSetLayout descriptorSetLayout{};
res = vkCreateDescriptorSetLayout(device.handle(), &layoutDesc, nullptr, &descriptorSetLayout);
if (res != VK_SUCCESS || !descriptorSetLayout)
throw LSFG::vulkan_error(res, "Failed to create descriptor set layout");
throw VK::vulkan_error(res, "Failed to create descriptor set layout");
// store module and layout in shared ptr
this->shaderModule = std::shared_ptr<VkShaderModule>(

View file

@ -0,0 +1,62 @@
#include <volk.h>
#include <vulkan/vulkan_core.h>
#include "vk/core/timeline_semaphore.hpp"
#include "vk/core/device.hpp"
#include "vk/exception.hpp"
#include <cstdint>
#include <memory>
using namespace VK::Core;
TimelineSemaphore::TimelineSemaphore(const Device& device, uint32_t initial) {
// create semaphore
const VkSemaphoreTypeCreateInfo typeInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_TYPE_CREATE_INFO,
.semaphoreType = VK_SEMAPHORE_TYPE_TIMELINE,
.initialValue = initial
};
const VkSemaphoreCreateInfo desc{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO,
.pNext = &typeInfo,
};
VkSemaphore semaphoreHandle{};
auto res = vkCreateSemaphore(device.handle(), &desc, nullptr, &semaphoreHandle);
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
throw VK::vulkan_error(res, "Unable to create semaphore");
// store semaphore in shared ptr
this->semaphore = std::shared_ptr<VkSemaphore>(
new VkSemaphore(semaphoreHandle),
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
}
);
}
void TimelineSemaphore::signal(const Device& device, uint64_t value) const {
const VkSemaphoreSignalInfo signalInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
.semaphore = this->handle(),
.value = value
};
auto res = vkSignalSemaphore(device.handle(), &signalInfo);
if (res != VK_SUCCESS)
throw VK::vulkan_error(res, "Unable to signal semaphore");
}
bool TimelineSemaphore::wait(const Device& device, uint64_t value, uint64_t timeout) const {
VkSemaphore semaphore = this->handle();
const VkSemaphoreWaitInfo waitInfo{
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_WAIT_INFO,
.semaphoreCount = 1,
.pSemaphores = &semaphore,
.pValues = &value
};
auto res = vkWaitSemaphores(device.handle(), &waitInfo, timeout);
if (res != VK_SUCCESS && res != VK_TIMEOUT)
throw VK::vulkan_error(res, "Unable to wait for semaphore");
return res == VK_SUCCESS;
}

View file

@ -1,4 +1,4 @@
#include "common/exception.hpp"
#include "vk/exception.hpp"
#include <vulkan/vulkan_core.h>
@ -8,7 +8,7 @@
#include <format>
#include <string>
using namespace LSFG;
using namespace VK;
vulkan_error::vulkan_error(VkResult result, const std::string& message)
: std::runtime_error(std::format("{} (error {})", message, static_cast<int32_t>(result))),