mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
initial set of core vulkan objects
This commit is contained in:
parent
fe5a8520e5
commit
40d7d032a3
12 changed files with 715 additions and 0 deletions
105
include/core/commandbuffer.hpp
Normal file
105
include/core/commandbuffer.hpp
Normal file
|
|
@ -0,0 +1,105 @@
|
|||
#ifndef COMMANDBUFFER_HPP
|
||||
#define COMMANDBUFFER_HPP
|
||||
|
||||
#include "core/commandpool.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "device.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <optional>
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan::Core {
|
||||
|
||||
/// State of the command buffer.
|
||||
enum class CommandBufferState {
|
||||
/// Command buffer is not initialized or has been destroyed.
|
||||
Invalid,
|
||||
/// Command buffer has been created.
|
||||
Empty,
|
||||
/// Command buffer recording has started.
|
||||
Recording,
|
||||
/// Command buffer recording has ended.
|
||||
Full,
|
||||
/// Command buffer has been submitted to a queue.
|
||||
Submitted
|
||||
};
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command buffer.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command buffer.
|
||||
///
|
||||
class CommandBuffer {
|
||||
public:
|
||||
///
|
||||
/// Create the command buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param pool Vulkan command pool
|
||||
///
|
||||
/// @throws std::invalid_argument if the device or pool are invalid.
|
||||
/// @throws ls::vulkan_error if object creation fails.
|
||||
///
|
||||
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 ls::vulkan_error if beginning the command buffer fails.
|
||||
///
|
||||
void begin();
|
||||
|
||||
///
|
||||
/// End recording commands in the command buffer.
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Recording state
|
||||
/// @throws ls::vulkan_error if ending the command buffer fails.
|
||||
///
|
||||
void end();
|
||||
|
||||
///
|
||||
/// Submit the command buffer to a queue.
|
||||
///
|
||||
/// @param queue Vulkan queue to submit to
|
||||
/// @param waitSemaphores Semaphores to wait on before executing the command buffer
|
||||
/// @param waitSemaphoreValues Values for the semaphores to wait on
|
||||
/// @param signalSemaphores Semaphores to signal after executing the command buffer
|
||||
/// @param signalSemaphoreValues Values for the semaphores to signal
|
||||
///
|
||||
/// @throws std::invalid_argument if the queue is null.
|
||||
/// @throws std::logic_error if the command buffer is not in Full state.
|
||||
/// @throws ls::vulkan_error if submission fails.
|
||||
///
|
||||
void submit(VkQueue queue, // TODO: fence
|
||||
const std::vector<Semaphore>& waitSemaphores = {},
|
||||
std::optional<std::vector<uint64_t>> waitSemaphoreValues = std::nullopt,
|
||||
const std::vector<Semaphore>& signalSemaphores = {},
|
||||
std::optional<std::vector<uint64_t>> signalSemaphoreValues = std::nullopt);
|
||||
|
||||
/// Get the state of the command buffer.
|
||||
[[nodiscard]] CommandBufferState getState() const { return *this->state; }
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { *this->commandBuffer; }
|
||||
|
||||
/// Check whether the object is valid.
|
||||
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->commandBuffer); }
|
||||
/// if (obj) operator. Checks if the object is valid.
|
||||
explicit operator bool() const { return this->isValid(); }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandBuffer(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer& operator=(const CommandBuffer&) noexcept = default;
|
||||
CommandBuffer(CommandBuffer&&) noexcept = default;
|
||||
CommandBuffer& operator=(CommandBuffer&&) noexcept = default;
|
||||
~CommandBuffer() = default;
|
||||
private:
|
||||
std::shared_ptr<CommandBufferState> state;
|
||||
std::shared_ptr<VkCommandBuffer> commandBuffer;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMANDBUFFER_HPP
|
||||
56
include/core/commandpool.hpp
Normal file
56
include/core/commandpool.hpp
Normal file
|
|
@ -0,0 +1,56 @@
|
|||
#ifndef COMMANDPOOL_HPP
|
||||
#define COMMANDPOOL_HPP
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan::Core {
|
||||
|
||||
/// Enumeration for different types of command pools.
|
||||
enum class CommandPoolType {
|
||||
/// Used for compute-type command buffers.
|
||||
Compute
|
||||
};
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan command pool.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan command pool.
|
||||
///
|
||||
class CommandPool {
|
||||
public:
|
||||
///
|
||||
/// Create the command pool.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param type Type of command pool to create.
|
||||
///
|
||||
/// @throws std::invalid_argument if the device is invalid.
|
||||
/// @throws ls::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandPool(const Device& device, CommandPoolType type);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandPool; }
|
||||
|
||||
/// Check whether the object is valid.
|
||||
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->commandPool); }
|
||||
/// if (obj) operator. Checks if the object is valid.
|
||||
explicit operator bool() const { return this->isValid(); }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
CommandPool(const CommandPool&) noexcept = default;
|
||||
CommandPool& operator=(const CommandPool&) noexcept = default;
|
||||
CommandPool(CommandPool&&) noexcept = default;
|
||||
CommandPool& operator=(CommandPool&&) noexcept = default;
|
||||
~CommandPool() = default;
|
||||
private:
|
||||
std::shared_ptr<VkCommandPool> commandPool;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // COMMANDPOOL_HPP
|
||||
69
include/core/fence.hpp
Normal file
69
include/core/fence.hpp
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
#ifndef FENCE_HPP
|
||||
#define FENCE_HPP
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan fence.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan fence.
|
||||
///
|
||||
class Fence {
|
||||
public:
|
||||
///
|
||||
/// Create the fence.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
///
|
||||
/// @throws std::invalid_argument if the device is null.
|
||||
/// @throws ls::vulkan_error if object creation fails.
|
||||
///
|
||||
Fence(const Device& device);
|
||||
|
||||
///
|
||||
/// Reset the fence to an unsignaled state.
|
||||
///
|
||||
/// @throws std::logic_error if the fence is not valid.
|
||||
/// @throws ls::vulkan_error if resetting fails.
|
||||
///
|
||||
void reset() const;
|
||||
|
||||
///
|
||||
/// Wait for the fence
|
||||
///
|
||||
/// @param timeout The timeout in nanoseconds, or UINT64_MAX for no timeout.
|
||||
/// @returns true if the fence signaled, false if it timed out.
|
||||
///
|
||||
/// @throws std::logic_error if the fence is not valid.
|
||||
/// @throws ls::vulkan_error if waiting fails.
|
||||
///
|
||||
bool wait(uint64_t timeout = UINT64_MAX);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->fence; }
|
||||
|
||||
/// Check whether the object is valid.
|
||||
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->fence); }
|
||||
/// if (obj) operator. Checks if the object is valid.
|
||||
explicit operator bool() const { return this->isValid(); }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
Fence(const Fence&) noexcept = default;
|
||||
Fence& operator=(const Fence&) noexcept = default;
|
||||
Fence(Fence&&) noexcept = default;
|
||||
Fence& operator=(Fence&&) noexcept = default;
|
||||
~Fence() = default;
|
||||
private:
|
||||
std::shared_ptr<VkFence> fence;
|
||||
VkDevice device{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // FENCE_HPP
|
||||
75
include/core/semaphore.hpp
Normal file
75
include/core/semaphore.hpp
Normal file
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef SEMAPHORE_HPP
|
||||
#define SEMAPHORE_HPP
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <optional>
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan semaphore.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan semaphore.
|
||||
///
|
||||
class Semaphore {
|
||||
public:
|
||||
///
|
||||
/// Create the semaphore.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param initial Optional initial value for creating a timeline semaphore.
|
||||
///
|
||||
/// @throws std::invalid_argument if the device is null.
|
||||
/// @throws ls::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(const Device& device, std::optional<uint32_t> initial = std::nullopt);
|
||||
|
||||
///
|
||||
/// Signal the semaphore to a specific value.
|
||||
///
|
||||
/// @param value The value to signal the semaphore to.
|
||||
///
|
||||
/// @throws std::logic_error if the semaphore is not a timeline semaphore.
|
||||
/// @throws ls::vulkan_error if signaling fails.
|
||||
///
|
||||
void signal(uint64_t value) const;
|
||||
|
||||
///
|
||||
/// Wait for the semaphore to reach a specific value.
|
||||
///
|
||||
/// @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 ls::vulkan_error if waiting fails.
|
||||
///
|
||||
bool wait(uint64_t value, uint64_t timeout = UINT64_MAX);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->semaphore; }
|
||||
|
||||
/// Check whether the object is valid.
|
||||
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->semaphore); }
|
||||
/// if (obj) operator. Checks if the object is valid.
|
||||
explicit operator bool() const { return this->isValid(); }
|
||||
|
||||
// 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;
|
||||
VkDevice device{};
|
||||
bool isTimeline{};
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SEMAPHORE_HPP
|
||||
52
include/core/shadermodule.hpp
Normal file
52
include/core/shadermodule.hpp
Normal file
|
|
@ -0,0 +1,52 @@
|
|||
#ifndef SHADERMODULE_HPP
|
||||
#define SHADERMODULE_HPP
|
||||
|
||||
#include "device.hpp"
|
||||
|
||||
#include <string>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Vulkan::Core {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan shader module.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan shader module.
|
||||
///
|
||||
class ShaderModule {
|
||||
public:
|
||||
///
|
||||
/// Create the shader module.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param path Path to the shader file.
|
||||
///
|
||||
/// @throws std::invalid_argument if the device is invalid.
|
||||
/// @throws std::system_error if the shader file cannot be opened or read.
|
||||
/// @throws ls::vulkan_error if object creation fails.
|
||||
///
|
||||
ShaderModule(const Device& device, const std::string& path);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->shaderModule; }
|
||||
|
||||
/// Check whether the object is valid.
|
||||
[[nodiscard]] bool isValid() const { return static_cast<bool>(this->shaderModule); }
|
||||
/// if (obj) operator. Checks if the object is valid.
|
||||
explicit operator bool() const { return this->isValid(); }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
ShaderModule(const ShaderModule&) noexcept = default;
|
||||
ShaderModule& operator=(const ShaderModule&) noexcept = default;
|
||||
ShaderModule(ShaderModule&&) noexcept = default;
|
||||
ShaderModule& operator=(ShaderModule&&) noexcept = default;
|
||||
~ShaderModule() = default;
|
||||
private:
|
||||
std::shared_ptr<VkShaderModule> shaderModule;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // SHADERMODULE_HPP
|
||||
37
include/utils/exceptions.hpp
Normal file
37
include/utils/exceptions.hpp
Normal file
|
|
@ -0,0 +1,37 @@
|
|||
#ifndef EXCEPTIONS_HPP
|
||||
#define EXCEPTIONS_HPP
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
|
||||
namespace ls {
|
||||
|
||||
/// Simple exception class for Vulkan errors.
|
||||
class vulkan_error : public std::runtime_error {
|
||||
public:
|
||||
///
|
||||
/// Construct a vulkan_error with a message and a Vulkan result code.
|
||||
///
|
||||
/// @param result The Vulkan result code associated with the error.
|
||||
/// @param message The error message.
|
||||
///
|
||||
explicit vulkan_error(VkResult result, const std::string& message);
|
||||
|
||||
/// Get the Vulkan result code associated with this error.
|
||||
[[nodiscard]] VkResult error() const { return this->result; }
|
||||
|
||||
// Trivially copyable, moveable and destructible
|
||||
vulkan_error(const vulkan_error&) = default;
|
||||
vulkan_error(vulkan_error&&) = default;
|
||||
vulkan_error& operator=(const vulkan_error&) = default;
|
||||
vulkan_error& operator=(vulkan_error&&) = default;
|
||||
~vulkan_error() noexcept override;
|
||||
private:
|
||||
VkResult result;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif // EXCEPTIONS_HPP
|
||||
115
src/core/commandbuffer.cpp
Normal file
115
src/core/commandbuffer.cpp
Normal file
|
|
@ -0,0 +1,115 @@
|
|||
#include "core/commandbuffer.hpp"
|
||||
#include "core/semaphore.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
using namespace Vulkan::Core;
|
||||
|
||||
CommandBuffer::CommandBuffer(const Device& device, const CommandPool& pool) {
|
||||
if (!device || !pool)
|
||||
throw std::invalid_argument("Invalid Vulkan device or command pool");
|
||||
|
||||
// create command buffer
|
||||
const VkCommandBufferAllocateInfo desc = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO,
|
||||
.commandPool = pool.handle(),
|
||||
.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY,
|
||||
.commandBufferCount = 1
|
||||
};
|
||||
VkCommandBuffer commandBufferHandle{};
|
||||
auto res = vkAllocateCommandBuffers(device.handle(), &desc, &commandBufferHandle);
|
||||
if (res != VK_SUCCESS || commandBuffer == VK_NULL_HANDLE)
|
||||
throw ls::vulkan_error(res, "Unable to allocate command buffer");
|
||||
|
||||
// store command buffer in shared ptr
|
||||
this->state = std::make_shared<CommandBufferState>(CommandBufferState::Empty);
|
||||
this->commandBuffer = std::shared_ptr<VkCommandBuffer>(
|
||||
new VkCommandBuffer(commandBufferHandle),
|
||||
[dev = device.handle(), pool = pool.handle()](VkCommandBuffer* cmdBuffer) {
|
||||
vkFreeCommandBuffers(dev, pool, 1, cmdBuffer);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void CommandBuffer::begin() {
|
||||
if (*this->state != CommandBufferState::Empty)
|
||||
throw std::logic_error("Command buffer is not in Empty state");
|
||||
|
||||
const VkCommandBufferBeginInfo beginInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO,
|
||||
.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT
|
||||
};
|
||||
auto res = vkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "Unable to begin command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Recording;
|
||||
}
|
||||
|
||||
void CommandBuffer::end() {
|
||||
if (*this->state != CommandBufferState::Recording)
|
||||
throw std::logic_error("Command buffer is not in Recording state");
|
||||
|
||||
auto res = vkEndCommandBuffer(*this->commandBuffer);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "Unable to end command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Full;
|
||||
}
|
||||
|
||||
void CommandBuffer::submit(VkQueue queue,
|
||||
const std::vector<Semaphore>& waitSemaphores,
|
||||
std::optional<std::vector<uint64_t>> waitSemaphoreValues,
|
||||
const std::vector<Semaphore>& signalSemaphores,
|
||||
std::optional<std::vector<uint64_t>> signalSemaphoreValues) {
|
||||
if (!queue)
|
||||
throw std::invalid_argument("Invalid Vulkan queue");
|
||||
if (*this->state != CommandBufferState::Full)
|
||||
throw std::logic_error("Command buffer is not in Full state");
|
||||
|
||||
const std::vector<VkPipelineStageFlags> waitStages(waitSemaphores.size(),
|
||||
VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT);
|
||||
VkTimelineSemaphoreSubmitInfo timelineInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_TIMELINE_SEMAPHORE_SUBMIT_INFO,
|
||||
};
|
||||
if (waitSemaphoreValues.has_value()) {
|
||||
timelineInfo.waitSemaphoreValueCount =
|
||||
static_cast<uint32_t>(waitSemaphoreValues->size());
|
||||
timelineInfo.pWaitSemaphoreValues = waitSemaphoreValues->data();
|
||||
}
|
||||
if (signalSemaphoreValues.has_value()) {
|
||||
timelineInfo.signalSemaphoreValueCount =
|
||||
static_cast<uint32_t>(signalSemaphoreValues->size());
|
||||
timelineInfo.pSignalSemaphoreValues = signalSemaphoreValues->data();
|
||||
}
|
||||
|
||||
std::vector<VkSemaphore> waitSemaphoresHandles;
|
||||
for (const auto& semaphore : waitSemaphores) {
|
||||
if (!semaphore)
|
||||
throw std::invalid_argument("Invalid Vulkan semaphore in waitSemaphores");
|
||||
waitSemaphoresHandles.push_back(semaphore.handle());
|
||||
}
|
||||
std::vector<VkSemaphore> signalSemaphoresHandles;
|
||||
for (const auto& semaphore : signalSemaphores) {
|
||||
if (!semaphore)
|
||||
throw std::invalid_argument("Invalid Vulkan semaphore in signalSemaphores");
|
||||
signalSemaphoresHandles.push_back(semaphore.handle());
|
||||
}
|
||||
|
||||
const VkSubmitInfo submitInfo = {
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.pNext = (waitSemaphoreValues.has_value() || signalSemaphoreValues.has_value())
|
||||
? &timelineInfo : nullptr,
|
||||
.waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size()),
|
||||
.pWaitSemaphores = waitSemaphoresHandles.data(),
|
||||
.pWaitDstStageMask = waitStages.data(),
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &(*this->commandBuffer),
|
||||
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
||||
.pSignalSemaphores = signalSemaphoresHandles.data()
|
||||
};
|
||||
auto res = vkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "Unable to submit command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Submitted;
|
||||
}
|
||||
34
src/core/commandpool.cpp
Normal file
34
src/core/commandpool.cpp
Normal file
|
|
@ -0,0 +1,34 @@
|
|||
#include "core/commandpool.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
using namespace Vulkan::Core;
|
||||
|
||||
CommandPool::CommandPool(const Device& device, CommandPoolType type) {
|
||||
if (!device)
|
||||
throw std::invalid_argument("Invalid Vulkan device");
|
||||
|
||||
uint32_t familyIdx{};
|
||||
switch (type) {
|
||||
case CommandPoolType::Compute:
|
||||
familyIdx = device.getComputeFamilyIdx();
|
||||
break;
|
||||
}
|
||||
|
||||
// create command pool
|
||||
const VkCommandPoolCreateInfo desc = {
|
||||
.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO,
|
||||
.queueFamilyIndex = familyIdx
|
||||
};
|
||||
VkCommandPool commandPoolHandle{};
|
||||
auto res = vkCreateCommandPool(device.handle(), &desc, nullptr, &commandPoolHandle);
|
||||
if (res != VK_SUCCESS || commandPoolHandle == VK_NULL_HANDLE)
|
||||
throw ls::vulkan_error(res, "Unable to create command pool");
|
||||
|
||||
// store the command pool in a shared pointer
|
||||
this->commandPool = std::shared_ptr<VkCommandPool>(
|
||||
new VkCommandPool(commandPoolHandle),
|
||||
[dev = device.handle()](VkCommandPool* commandPoolHandle) {
|
||||
vkDestroyCommandPool(dev, *commandPoolHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
50
src/core/fence.cpp
Normal file
50
src/core/fence.cpp
Normal file
|
|
@ -0,0 +1,50 @@
|
|||
#include "core/fence.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace Vulkan::Core;
|
||||
|
||||
Fence::Fence(const Device& device) {
|
||||
if (!device)
|
||||
throw std::invalid_argument("Invalid Vulkan device");
|
||||
|
||||
// create fence
|
||||
const VkFenceCreateInfo desc = {
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
|
||||
};
|
||||
VkFence fenceHandle{};
|
||||
auto res = vkCreateFence(device.handle(), &desc, nullptr, &fenceHandle);
|
||||
if (res != VK_SUCCESS || fenceHandle == VK_NULL_HANDLE)
|
||||
throw ls::vulkan_error(res, "Unable to create fence");
|
||||
|
||||
// store fence in shared ptr
|
||||
this->device = device.handle();
|
||||
this->fence = std::shared_ptr<VkFence>(
|
||||
new VkFence(fenceHandle),
|
||||
[dev = device.handle()](VkFence* fenceHandle) {
|
||||
vkDestroyFence(dev, *fenceHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Fence::reset() const {
|
||||
if (!this->isValid())
|
||||
throw std::runtime_error("Invalid fence");
|
||||
|
||||
VkFence fenceHandle = this->handle();
|
||||
auto res = vkResetFences(this->device, 1, &fenceHandle);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "Unable to reset fence");
|
||||
}
|
||||
|
||||
bool Fence::wait(uint64_t timeout) {
|
||||
if (!this->isValid())
|
||||
throw std::runtime_error("Invalid fence");
|
||||
|
||||
VkFence fenceHandle = this->handle();
|
||||
auto res = vkWaitForFences(this->device, 1, &fenceHandle, VK_TRUE, timeout);
|
||||
if (res != VK_SUCCESS && res != VK_TIMEOUT)
|
||||
throw ls::vulkan_error(res, "Unable to wait for fence");
|
||||
|
||||
return res == VK_SUCCESS;
|
||||
}
|
||||
66
src/core/semaphore.cpp
Normal file
66
src/core/semaphore.cpp
Normal file
|
|
@ -0,0 +1,66 @@
|
|||
#include "core/semaphore.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
using namespace Vulkan::Core;
|
||||
|
||||
Semaphore::Semaphore(const Device& device, std::optional<uint32_t> initial) {
|
||||
if (!device)
|
||||
throw std::invalid_argument("Invalid Vulkan device");
|
||||
|
||||
// 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 ls::vulkan_error(res, "Unable to create semaphore");
|
||||
|
||||
// store semaphore in shared ptr
|
||||
this->isTimeline = initial.has_value();
|
||||
this->device = device.handle();
|
||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||
new VkSemaphore(semaphoreHandle),
|
||||
[dev = device.handle()](VkSemaphore* semaphoreHandle) {
|
||||
vkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
void Semaphore::signal(uint64_t value) const {
|
||||
if (!this->isValid() || !this->isTimeline)
|
||||
throw std::runtime_error("Invalid timeline semaphore");
|
||||
|
||||
const VkSemaphoreSignalInfo signalInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_SIGNAL_INFO,
|
||||
.semaphore = this->handle(),
|
||||
.value = value
|
||||
};
|
||||
auto res = vkSignalSemaphore(this->device, &signalInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "Unable to signal semaphore");
|
||||
}
|
||||
|
||||
bool Semaphore::wait(uint64_t value, uint64_t timeout) {
|
||||
if (!this->isValid() || !this->isTimeline)
|
||||
throw std::runtime_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(this->device, &waitInfo, timeout);
|
||||
if (res != VK_SUCCESS && res != VK_TIMEOUT)
|
||||
throw ls::vulkan_error(res, "Unable to wait for semaphore");
|
||||
|
||||
return res == VK_SUCCESS;
|
||||
}
|
||||
46
src/core/shadermodule.cpp
Normal file
46
src/core/shadermodule.cpp
Normal file
|
|
@ -0,0 +1,46 @@
|
|||
#include "core/shadermodule.hpp"
|
||||
#include "utils/exceptions.hpp"
|
||||
|
||||
#include <fstream>
|
||||
#include <vector>
|
||||
|
||||
using namespace Vulkan::Core;
|
||||
|
||||
ShaderModule::ShaderModule(const Device& device, const std::string& path) {
|
||||
if (!device)
|
||||
throw std::invalid_argument("Invalid Vulkan device");
|
||||
|
||||
// read shader bytecode
|
||||
std::ifstream file(path, std::ios::ate | std::ios::binary);
|
||||
if (!file)
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to open shader file: " + path);
|
||||
|
||||
const std::streamsize size = file.tellg();
|
||||
std::vector<uint8_t> code(static_cast<size_t>(size));
|
||||
|
||||
file.seekg(0, std::ios::beg);
|
||||
if (!file.read(reinterpret_cast<char*>(code.data()), size))
|
||||
throw std::system_error(errno, std::generic_category(), "Failed to read shader file: " + path);
|
||||
|
||||
file.close();
|
||||
|
||||
// create shader module
|
||||
const uint8_t* data_ptr = code.data();
|
||||
const VkShaderModuleCreateInfo createInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO,
|
||||
.codeSize = code.size() * sizeof(uint32_t),
|
||||
.pCode = reinterpret_cast<const uint32_t*>(data_ptr)
|
||||
};
|
||||
VkShaderModule shaderModuleHandle{};
|
||||
auto res = vkCreateShaderModule(device.handle(), &createInfo, nullptr, &shaderModuleHandle);
|
||||
if (res != VK_SUCCESS || !shaderModuleHandle)
|
||||
throw ls::vulkan_error(res, "Failed to create shader module");
|
||||
|
||||
// store shader module in shared ptr
|
||||
this->shaderModule = std::shared_ptr<VkShaderModule>(
|
||||
new VkShaderModule(shaderModuleHandle),
|
||||
[dev = device.handle()](VkShaderModule* shaderModuleHandle) {
|
||||
vkDestroyShaderModule(dev, *shaderModuleHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
10
src/utils/exceptions.cpp
Normal file
10
src/utils/exceptions.cpp
Normal file
|
|
@ -0,0 +1,10 @@
|
|||
#include "utils/exceptions.hpp"
|
||||
|
||||
#include <format>
|
||||
|
||||
using namespace ls;
|
||||
|
||||
vulkan_error::vulkan_error(VkResult result, const std::string& message)
|
||||
: std::runtime_error(std::format("{} (error {})", message, static_cast<uint32_t>(result))), result(result) {}
|
||||
|
||||
vulkan_error::~vulkan_error() noexcept = default;
|
||||
Loading…
Add table
Reference in a new issue