mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2026-04-26 04:11:39 +00:00
refactor(cleanup): support device loader function in backend
This commit is contained in:
parent
bf1d8a8a09
commit
6f5f8edbdd
12 changed files with 40 additions and 863 deletions
|
|
@ -1,91 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include "mini/commandpool.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
/// 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:
|
||||
CommandBuffer() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the command buffer.
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param pool Vulkan command pool
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
CommandBuffer(VkDevice 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.
|
||||
///
|
||||
void begin();
|
||||
|
||||
///
|
||||
/// 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.
|
||||
///
|
||||
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 signalSemaphores Semaphores to signal after executing the command buffer
|
||||
///
|
||||
/// @throws std::logic_error if the command buffer is not in Full state.
|
||||
/// @throws LSFG::vulkan_error if submission fails.
|
||||
///
|
||||
void submit(VkQueue queue,
|
||||
const std::vector<VkSemaphore>& waitSemaphores = {},
|
||||
const std::vector<VkSemaphore>& signalSemaphores = {});
|
||||
|
||||
/// Get the state of the command buffer.
|
||||
[[nodiscard]] CommandBufferState getState() const { return *this->state; }
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->commandBuffer; }
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,60 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
///
|
||||
/// C++ wrapper class for a Vulkan image.
|
||||
///
|
||||
/// This class manages the lifetime of a Vulkan image.
|
||||
///
|
||||
class Image {
|
||||
public:
|
||||
Image() noexcept = default;
|
||||
|
||||
///
|
||||
/// Create the image and export the backing fd
|
||||
///
|
||||
/// @param device Vulkan device
|
||||
/// @param physicalDevice Vulkan physical device
|
||||
/// @param extent Extent of the image in pixels.
|
||||
/// @param format Vulkan format of the image
|
||||
/// @param usage Usage flags for the image
|
||||
/// @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.
|
||||
///
|
||||
Image(VkDevice device, VkPhysicalDevice physicalDevice, VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int* fd);
|
||||
|
||||
/// Get the Vulkan handle.
|
||||
[[nodiscard]] auto handle() const { return *this->image; }
|
||||
/// Get the Vulkan device memory handle.
|
||||
[[nodiscard]] auto getMemory() const { return *this->memory; }
|
||||
/// Get the extent of the image.
|
||||
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
|
||||
/// Get the format of the image.
|
||||
[[nodiscard]] VkFormat getFormat() const { return this->format; }
|
||||
/// Get the aspect flags of the image.
|
||||
[[nodiscard]] VkImageAspectFlags getAspectFlags() const { return this->aspectFlags; }
|
||||
|
||||
/// Trivially copyable, moveable and destructible
|
||||
Image(const Image&) noexcept = default;
|
||||
Image& operator=(const Image&) noexcept = default;
|
||||
Image(Image&&) noexcept = default;
|
||||
Image& operator=(Image&&) noexcept = default;
|
||||
~Image() = default;
|
||||
private:
|
||||
std::shared_ptr<VkImage> image;
|
||||
std::shared_ptr<VkDeviceMemory> memory;
|
||||
|
||||
VkExtent2D extent{};
|
||||
VkFormat format{};
|
||||
VkImageAspectFlags aspectFlags{};
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,50 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace Mini {
|
||||
|
||||
///
|
||||
/// 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
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if object creation fails.
|
||||
///
|
||||
Semaphore(VkDevice device);
|
||||
|
||||
///
|
||||
/// 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(VkDevice device, int* fd);
|
||||
|
||||
/// 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;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
@ -1,102 +0,0 @@
|
|||
#pragma once
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
namespace Utils {
|
||||
|
||||
///
|
||||
/// Find a queue in the physical device that supports the given queue flags.
|
||||
///
|
||||
/// @param device The Vulkan device to use for queue retrieval.
|
||||
/// @param physicalDevice The physical device to search in.
|
||||
/// @param desc The device creation info, used to determine enabled queue families.
|
||||
/// @param flags The queue flags to search for (e.g., VK_QUEUE_GRAPHICS_BIT).
|
||||
/// @return Pair of queue family index and queue handle.
|
||||
///
|
||||
/// @throws LSFG::vulkan_error if no suitable queue is found.
|
||||
///
|
||||
std::pair<uint32_t, VkQueue> findQueue(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkDeviceCreateInfo* desc, VkQueueFlags flags);
|
||||
|
||||
///
|
||||
/// Get the UUID of the physical device.
|
||||
///
|
||||
/// @param physicalDevice The physical device to get the UUID from.
|
||||
/// @return The UUID of the physical device.
|
||||
///
|
||||
uint64_t getDeviceUUID(VkPhysicalDevice physicalDevice);
|
||||
|
||||
///
|
||||
/// Get the max image count for a swapchain.
|
||||
///
|
||||
/// @param physicalDevice The physical device to query.
|
||||
/// @param surface The surface to query the capabilities for.
|
||||
/// @return The maximum image count for the swapchain.
|
||||
///
|
||||
uint32_t getMaxImageCount(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface);
|
||||
|
||||
///
|
||||
/// Ensure a list of extensions is present in the given array.
|
||||
///
|
||||
/// @param extensions The array of extensions to check.
|
||||
/// @param requiredExtensions The list of required extensions to ensure are present.
|
||||
///
|
||||
std::vector<const char*> addExtensions(const char* const* extensions, size_t count,
|
||||
const std::vector<const char*>& requiredExtensions);
|
||||
|
||||
///
|
||||
/// Copy an image from source to destination in a command buffer.
|
||||
///
|
||||
/// @param buf The command buffer to record the copy operation into.
|
||||
/// @param src The source image to copy from.
|
||||
/// @param dst The destination image to copy to.
|
||||
/// @param width The width of the image to copy.
|
||||
/// @param height The height of the image to copy.
|
||||
/// @param pre The pipeline stage to wait on.
|
||||
/// @param post The pipeline stage to provide after the copy.
|
||||
/// @param makeSrcPresentable If true, the source image will be made presentable after the copy.
|
||||
/// @param makeDstPresentable If true, the destination image will be made presentable after the copy.
|
||||
///
|
||||
void copyImage(VkCommandBuffer buf,
|
||||
VkImage src, VkImage dst,
|
||||
uint32_t width, uint32_t height,
|
||||
VkPipelineStageFlags pre, VkPipelineStageFlags post,
|
||||
bool makeSrcPresentable, bool makeDstPresentable);
|
||||
|
||||
///
|
||||
/// Log a message at most n times.
|
||||
///
|
||||
/// @param id The identifier for the log message.
|
||||
/// @param n The maximum number of times to log the message.
|
||||
/// @param message The message to log.
|
||||
///
|
||||
void logLimitN(const std::string& id, size_t n, const std::string& message);
|
||||
|
||||
///
|
||||
/// Reset the log limit for a given identifier.
|
||||
///
|
||||
/// @param id The identifier for the log message.
|
||||
///
|
||||
void resetLimitN(const std::string& id) noexcept;
|
||||
|
||||
///
|
||||
/// Get the process name of the current executable.
|
||||
///
|
||||
/// @return The name of the process.
|
||||
///
|
||||
std::pair<std::string, std::string> getProcessName();
|
||||
|
||||
///
|
||||
/// Get the configuration file path.
|
||||
///
|
||||
/// @return The path to the configuration file.
|
||||
///
|
||||
std::string getConfigFile();
|
||||
|
||||
}
|
||||
|
|
@ -10,6 +10,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <vulkan/vk_layer.h>
|
||||
|
||||
namespace vk {
|
||||
|
||||
|
|
@ -91,7 +92,6 @@ namespace vk {
|
|||
PFN_vkCreateComputePipelines CreateComputePipelines;
|
||||
PFN_vkDestroyPipeline DestroyPipeline;
|
||||
|
||||
|
||||
// extension functions
|
||||
PFN_vkSignalSemaphoreKHR SignalSemaphoreKHR;
|
||||
PFN_vkWaitSemaphoresKHR WaitSemaphoresKHR;
|
||||
|
|
@ -136,7 +136,8 @@ namespace vk {
|
|||
Vulkan(const std::string& appName, version appVersion,
|
||||
const std::string& engineName, version engineVersion,
|
||||
PhysicalDeviceSelector selectPhysicalDevice,
|
||||
bool isGraphical = false);
|
||||
bool isGraphical = false,
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData = std::nullopt);
|
||||
|
||||
/// create based on an existing externally managed vulkan instance.
|
||||
/// @param instance vulkan instance handle
|
||||
|
|
@ -150,7 +151,8 @@ namespace vk {
|
|||
VkPhysicalDevice physdev,
|
||||
VulkanInstanceFuncs instanceFuncs,
|
||||
VulkanDeviceFuncs deviceFuncs,
|
||||
bool isGraphical = true);
|
||||
bool isGraphical = true,
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData = std::nullopt);
|
||||
|
||||
/// find a memory type index
|
||||
/// @param validTypes bitset of valid memory types
|
||||
|
|
@ -182,6 +184,9 @@ namespace vk {
|
|||
/// get device-level function pointers
|
||||
/// @return the device function pointers
|
||||
[[nodiscard]] const auto& df() const { return this->device_funcs; }
|
||||
/// get optional setLoaderData function
|
||||
/// @return the setLoaderData function
|
||||
[[nodiscard]] const auto& loaderdatafunc() const { return this->setLoaderData; }
|
||||
private:
|
||||
ls::owned_ptr<VkInstance> instance;
|
||||
VulkanInstanceFuncs instance_funcs;
|
||||
|
|
@ -191,6 +196,7 @@ namespace vk {
|
|||
bool fp16;
|
||||
|
||||
ls::owned_ptr<VkDevice> device;
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData;
|
||||
VulkanDeviceFuncs device_funcs;
|
||||
|
||||
VkQueue computeQueue;
|
||||
|
|
|
|||
|
|
@ -32,6 +32,13 @@ namespace {
|
|||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "vkAllocateCommandBuffers() failed");
|
||||
|
||||
auto setLoaderData = vk.loaderdatafunc();
|
||||
if (setLoaderData) {
|
||||
res = (*setLoaderData)(vk.dev(), handle);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "vkSetDeviceLoaderData() failed");
|
||||
}
|
||||
|
||||
return ls::owned_ptr<VkCommandBuffer>(
|
||||
new VkCommandBuffer(handle),
|
||||
[dev = vk.dev(), pool = vk.cmdpool(), defunc = vk.df().FreeCommandBuffers](
|
||||
|
|
|
|||
|
|
@ -10,6 +10,7 @@
|
|||
#include <vector>
|
||||
|
||||
#include <dlfcn.h>
|
||||
#include <vulkan/vk_layer.h>
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
using namespace vk;
|
||||
|
|
@ -199,11 +200,18 @@ namespace {
|
|||
}
|
||||
|
||||
/// get a queue from the logical device
|
||||
VkQueue getQueue(const VulkanDeviceFuncs& fd,
|
||||
VkDevice device, uint32_t cfi) {
|
||||
VkQueue getQueue(const VulkanDeviceFuncs& fd, VkDevice device,
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData,
|
||||
uint32_t cfi) {
|
||||
VkQueue queue{};
|
||||
|
||||
fd.GetDeviceQueue(device, cfi, 0, &queue);
|
||||
|
||||
if (setLoaderData) { // optionally set loader data
|
||||
auto res = (*setLoaderData)(device, queue);
|
||||
if (res != VK_SUCCESS)
|
||||
throw ls::vulkan_error(res, "vkSetDeviceLoaderData() failed");
|
||||
}
|
||||
return queue;
|
||||
}
|
||||
|
||||
|
|
@ -353,7 +361,8 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi
|
|||
Vulkan::Vulkan(const std::string& appName, version appVersion,
|
||||
const std::string& engineName, version engineVersion,
|
||||
PhysicalDeviceSelector selectPhysicalDevice,
|
||||
bool isGraphical) :
|
||||
bool isGraphical,
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData) :
|
||||
instance(createInstance(
|
||||
appName, appVersion,
|
||||
engineName, engineVersion
|
||||
|
|
@ -371,11 +380,14 @@ Vulkan::Vulkan(const std::string& appName, version appVersion,
|
|||
this->queueFamilyIdx,
|
||||
this->fp16
|
||||
)),
|
||||
setLoaderData(setLoaderData),
|
||||
device_funcs(initVulkanDeviceFuncs(
|
||||
this->instance_funcs,
|
||||
*this->device
|
||||
)),
|
||||
computeQueue(getQueue(this->device_funcs, *this->device, this->queueFamilyIdx)),
|
||||
computeQueue(getQueue(this->device_funcs, *this->device,
|
||||
this->setLoaderData,
|
||||
this->queueFamilyIdx)),
|
||||
cmdPool(createCommandPool(this->device_funcs,
|
||||
*this->device,
|
||||
this->queueFamilyIdx
|
||||
|
|
@ -389,7 +401,8 @@ Vulkan::Vulkan(VkInstance instance, VkDevice device,
|
|||
VkPhysicalDevice physdev,
|
||||
VulkanInstanceFuncs instanceFuncs,
|
||||
VulkanDeviceFuncs deviceFuncs,
|
||||
bool isGraphical) :
|
||||
bool isGraphical,
|
||||
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData) :
|
||||
instance(new VkInstance(instance)),
|
||||
instance_funcs(instanceFuncs),
|
||||
physdev(physdev),
|
||||
|
|
@ -397,8 +410,11 @@ Vulkan::Vulkan(VkInstance instance, VkDevice device,
|
|||
isGraphical ? VK_QUEUE_GRAPHICS_BIT : VK_QUEUE_COMPUTE_BIT)),
|
||||
fp16(false),
|
||||
device(new VkDevice(device)),
|
||||
setLoaderData(setLoaderData),
|
||||
device_funcs(deviceFuncs),
|
||||
computeQueue(getQueue(this->device_funcs, *this->device, this->queueFamilyIdx)),
|
||||
computeQueue(getQueue(this->device_funcs, *this->device,
|
||||
this->setLoaderData,
|
||||
this->queueFamilyIdx)),
|
||||
cmdPool(createCommandPool(this->device_funcs,
|
||||
*this->device,
|
||||
this->queueFamilyIdx
|
||||
|
|
|
|||
|
|
@ -212,7 +212,8 @@ namespace {
|
|||
global->instance, *device,
|
||||
physdev,
|
||||
global->fi,
|
||||
vk::initVulkanDeviceFuncs(global->fi, *device)
|
||||
vk::initVulkanDeviceFuncs(global->fi, *device),
|
||||
setLoaderData
|
||||
)
|
||||
)
|
||||
);
|
||||
|
|
|
|||
|
|
@ -1,91 +0,0 @@
|
|||
#include "mini/commandbuffer.hpp"
|
||||
#include "mini/commandpool.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "layer.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <cstdint>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <vector>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
CommandBuffer::CommandBuffer(VkDevice device, const CommandPool& 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 = Layer::ovkAllocateCommandBuffers(device, &desc, &commandBufferHandle);
|
||||
if (res != VK_SUCCESS || commandBufferHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to allocate command buffer");
|
||||
res = Layer::ovkSetDeviceLoaderData(device, commandBufferHandle);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to set device loader data for 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, pool = pool.handle()](VkCommandBuffer* cmdBuffer) {
|
||||
Layer::ovkFreeCommandBuffers(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 = Layer::ovkBeginCommandBuffer(*this->commandBuffer, &beginInfo);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::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 = Layer::ovkEndCommandBuffer(*this->commandBuffer);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to end command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Full;
|
||||
}
|
||||
|
||||
void CommandBuffer::submit(VkQueue queue,
|
||||
const std::vector<VkSemaphore>& waitSemaphores,
|
||||
const std::vector<VkSemaphore>& signalSemaphores) {
|
||||
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);
|
||||
|
||||
const VkSubmitInfo submitInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO,
|
||||
.waitSemaphoreCount = static_cast<uint32_t>(waitSemaphores.size()),
|
||||
.pWaitSemaphores = waitSemaphores.data(),
|
||||
.pWaitDstStageMask = waitStages.data(),
|
||||
.commandBufferCount = 1,
|
||||
.pCommandBuffers = &(*this->commandBuffer),
|
||||
.signalSemaphoreCount = static_cast<uint32_t>(signalSemaphores.size()),
|
||||
.pSignalSemaphores = signalSemaphores.data()
|
||||
};
|
||||
auto res = Layer::ovkQueueSubmit(queue, 1, &submitInfo, VK_NULL_HANDLE);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to submit command buffer");
|
||||
|
||||
*this->state = CommandBufferState::Submitted;
|
||||
}
|
||||
|
|
@ -1,112 +0,0 @@
|
|||
#include "mini/image.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "layer.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
#include <cstdint>
|
||||
#include <optional>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
Image::Image(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkExtent2D extent, VkFormat format,
|
||||
VkImageUsageFlags usage, VkImageAspectFlags aspectFlags, int* fd)
|
||||
: extent(extent), format(format), aspectFlags(aspectFlags) {
|
||||
// create image
|
||||
const VkExternalMemoryImageCreateInfo externalInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_IMAGE_CREATE_INFO,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
|
||||
};
|
||||
const VkImageCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO,
|
||||
.pNext = &externalInfo,
|
||||
.imageType = VK_IMAGE_TYPE_2D,
|
||||
.format = format,
|
||||
.extent = {
|
||||
.width = extent.width,
|
||||
.height = extent.height,
|
||||
.depth = 1
|
||||
},
|
||||
.mipLevels = 1,
|
||||
.arrayLayers = 1,
|
||||
.samples = VK_SAMPLE_COUNT_1_BIT,
|
||||
.usage = usage,
|
||||
.sharingMode = VK_SHARING_MODE_EXCLUSIVE
|
||||
};
|
||||
VkImage imageHandle{};
|
||||
auto res = Layer::ovkCreateImage(device, &desc, nullptr, &imageHandle);
|
||||
if (res != VK_SUCCESS || imageHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to create Vulkan image");
|
||||
|
||||
// find memory type
|
||||
VkPhysicalDeviceMemoryProperties memProps;
|
||||
Layer::ovkGetPhysicalDeviceMemoryProperties(physicalDevice, &memProps);
|
||||
|
||||
VkMemoryRequirements memReqs;
|
||||
Layer::ovkGetImageMemoryRequirements(device, 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
|
||||
}
|
||||
if (!memType.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_UNKNOWN, "Unable to find memory type for image");
|
||||
#pragma clang diagnostic pop
|
||||
|
||||
// allocate and bind memory
|
||||
const VkMemoryDedicatedAllocateInfoKHR dedicatedInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_DEDICATED_ALLOCATE_INFO_KHR,
|
||||
.image = imageHandle,
|
||||
};
|
||||
const VkExportMemoryAllocateInfo exportInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_EXPORT_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &dedicatedInfo,
|
||||
.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR
|
||||
};
|
||||
const VkMemoryAllocateInfo allocInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO,
|
||||
.pNext = &exportInfo,
|
||||
.allocationSize = memReqs.size,
|
||||
.memoryTypeIndex = memType.value()
|
||||
};
|
||||
VkDeviceMemory memoryHandle{};
|
||||
res = Layer::ovkAllocateMemory(device, &allocInfo, nullptr, &memoryHandle);
|
||||
if (res != VK_SUCCESS || memoryHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Failed to allocate memory for Vulkan image");
|
||||
|
||||
res = Layer::ovkBindImageMemory(device, imageHandle, memoryHandle, 0);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to bind memory to Vulkan image");
|
||||
|
||||
// obtain the sharing fd
|
||||
const VkMemoryGetFdInfoKHR fdInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_MEMORY_GET_FD_INFO_KHR,
|
||||
.memory = memoryHandle,
|
||||
.handleType = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_FD_BIT_KHR,
|
||||
};
|
||||
res = Layer::ovkGetMemoryFdKHR(device, &fdInfo, fd);
|
||||
if (res != VK_SUCCESS || *fd < 0)
|
||||
throw LSFG::vulkan_error(res, "Failed to obtain sharing fd for Vulkan image");
|
||||
|
||||
// store objects in shared ptr
|
||||
this->image = std::shared_ptr<VkImage>(
|
||||
new VkImage(imageHandle),
|
||||
[dev = device](VkImage* img) {
|
||||
Layer::ovkDestroyImage(dev, *img, nullptr);
|
||||
}
|
||||
);
|
||||
this->memory = std::shared_ptr<VkDeviceMemory>(
|
||||
new VkDeviceMemory(memoryHandle),
|
||||
[dev = device](VkDeviceMemory* mem) {
|
||||
Layer::ovkFreeMemory(dev, *mem, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -1,62 +0,0 @@
|
|||
#include "mini/semaphore.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "layer.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
|
||||
#include <memory>
|
||||
|
||||
using namespace Mini;
|
||||
|
||||
Semaphore::Semaphore(VkDevice device) {
|
||||
// create semaphore
|
||||
const VkSemaphoreCreateInfo desc{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_CREATE_INFO
|
||||
};
|
||||
VkSemaphore semaphoreHandle{};
|
||||
auto res = Layer::ovkCreateSemaphore(device, &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->semaphore = std::shared_ptr<VkSemaphore>(
|
||||
new VkSemaphore(semaphoreHandle),
|
||||
[dev = device](VkSemaphore* semaphoreHandle) {
|
||||
Layer::ovkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
||||
Semaphore::Semaphore(VkDevice 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 = Layer::ovkCreateSemaphore(device, &desc, nullptr, &semaphoreHandle);
|
||||
if (res != VK_SUCCESS || semaphoreHandle == VK_NULL_HANDLE)
|
||||
throw LSFG::vulkan_error(res, "Unable to create semaphore");
|
||||
|
||||
// export semaphore to fd
|
||||
const VkSemaphoreGetFdInfoKHR fdInfo{
|
||||
.sType = VK_STRUCTURE_TYPE_SEMAPHORE_GET_FD_INFO_KHR,
|
||||
.semaphore = semaphoreHandle,
|
||||
.handleType = VK_EXTERNAL_SEMAPHORE_HANDLE_TYPE_OPAQUE_FD_BIT
|
||||
};
|
||||
res = Layer::ovkGetSemaphoreFdKHR(device, &fdInfo, fd);
|
||||
if (res != VK_SUCCESS || *fd < 0)
|
||||
throw LSFG::vulkan_error(res, "Unable to export semaphore to fd");
|
||||
|
||||
// store semaphore in shared ptr
|
||||
this->semaphore = std::shared_ptr<VkSemaphore>(
|
||||
new VkSemaphore(semaphoreHandle),
|
||||
[dev = device](VkSemaphore* semaphoreHandle) {
|
||||
Layer::ovkDestroySemaphore(dev, *semaphoreHandle, nullptr);
|
||||
}
|
||||
);
|
||||
}
|
||||
|
|
@ -1,285 +0,0 @@
|
|||
#include "utils/utils.hpp"
|
||||
#include "common/exception.hpp"
|
||||
#include "layer.hpp"
|
||||
|
||||
#include <vulkan/vulkan_core.h>
|
||||
#include <sys/types.h>
|
||||
#include <string.h> // NOLINT
|
||||
#include <unistd.h>
|
||||
|
||||
#include <unordered_map>
|
||||
#include <algorithm>
|
||||
#include <optional>
|
||||
#include <iostream>
|
||||
#include <cstdlib>
|
||||
#include <cstdint>
|
||||
#include <cstring>
|
||||
#include <utility>
|
||||
#include <fstream>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
|
||||
using namespace Utils;
|
||||
|
||||
std::pair<uint32_t, VkQueue> Utils::findQueue(VkDevice device, VkPhysicalDevice physicalDevice,
|
||||
VkDeviceCreateInfo* desc, VkQueueFlags flags) {
|
||||
std::vector<VkDeviceQueueCreateInfo> enabledQueues(desc->queueCreateInfoCount);
|
||||
std::copy_n(desc->pQueueCreateInfos, enabledQueues.size(), enabledQueues.data());
|
||||
|
||||
uint32_t familyCount{};
|
||||
Layer::ovkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount, nullptr);
|
||||
std::vector<VkQueueFamilyProperties> families(familyCount);
|
||||
Layer::ovkGetPhysicalDeviceQueueFamilyProperties(physicalDevice, &familyCount,
|
||||
families.data());
|
||||
|
||||
std::optional<uint32_t> idx;
|
||||
for (const auto& queueInfo : enabledQueues) {
|
||||
if ((queueInfo.queueFamilyIndex < families.size()) &&
|
||||
(families[queueInfo.queueFamilyIndex].queueFlags & flags)) {
|
||||
idx = queueInfo.queueFamilyIndex;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!idx.has_value())
|
||||
throw LSFG::vulkan_error(VK_ERROR_INITIALIZATION_FAILED, "No suitable queue found");
|
||||
|
||||
VkQueue queue{};
|
||||
Layer::ovkGetDeviceQueue(device, *idx, 0, &queue);
|
||||
|
||||
auto res = Layer::ovkSetDeviceLoaderData(device, queue);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Unable to set device loader data for queue");
|
||||
|
||||
return { *idx, queue };
|
||||
}
|
||||
|
||||
uint64_t Utils::getDeviceUUID(VkPhysicalDevice physicalDevice) {
|
||||
VkPhysicalDeviceProperties properties{};
|
||||
Layer::ovkGetPhysicalDeviceProperties(physicalDevice, &properties);
|
||||
|
||||
return static_cast<uint64_t>(properties.vendorID) << 32 | properties.deviceID;
|
||||
}
|
||||
|
||||
uint32_t Utils::getMaxImageCount(VkPhysicalDevice physicalDevice, VkSurfaceKHR surface) {
|
||||
VkSurfaceCapabilitiesKHR capabilities{};
|
||||
auto res = Layer::ovkGetPhysicalDeviceSurfaceCapabilitiesKHR(physicalDevice,
|
||||
surface, &capabilities);
|
||||
if (res != VK_SUCCESS)
|
||||
throw LSFG::vulkan_error(res, "Failed to get surface capabilities");
|
||||
if (capabilities.maxImageCount == 0)
|
||||
return 999; // :3
|
||||
return capabilities.maxImageCount;
|
||||
}
|
||||
|
||||
std::vector<const char*> Utils::addExtensions(const char* const* extensions, size_t count,
|
||||
const std::vector<const char*>& requiredExtensions) {
|
||||
std::vector<const char*> ext(count);
|
||||
std::copy_n(extensions, count, ext.data());
|
||||
|
||||
for (const auto& e : requiredExtensions) {
|
||||
auto it = std::ranges::find_if(ext,
|
||||
[e](const char* extName) {
|
||||
return std::string(extName) == std::string(e);
|
||||
});
|
||||
if (it == ext.end())
|
||||
ext.push_back(e);
|
||||
}
|
||||
|
||||
return ext;
|
||||
}
|
||||
|
||||
void Utils::copyImage(VkCommandBuffer buf,
|
||||
VkImage src, VkImage dst,
|
||||
uint32_t width, uint32_t height,
|
||||
VkPipelineStageFlags pre, VkPipelineStageFlags post,
|
||||
bool makeSrcPresentable, bool makeDstPresentable) {
|
||||
const VkImageMemoryBarrier srcBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.image = src,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
const VkImageMemoryBarrier dstBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.image = dst,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
const std::vector<VkImageMemoryBarrier> barriers = { srcBarrier, dstBarrier };
|
||||
Layer::ovkCmdPipelineBarrier(buf,
|
||||
pre, VK_PIPELINE_STAGE_TRANSFER_BIT, 0,
|
||||
0, nullptr, 0, nullptr,
|
||||
static_cast<uint32_t>(barriers.size()), barriers.data());
|
||||
|
||||
const VkImageBlit imageBlit{
|
||||
.srcSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1
|
||||
},
|
||||
.srcOffsets = {
|
||||
{ 0, 0, 0 },
|
||||
{ static_cast<int32_t>(width), static_cast<int32_t>(height), 1 }
|
||||
},
|
||||
.dstSubresource = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.layerCount = 1
|
||||
},
|
||||
.dstOffsets = {
|
||||
{ 0, 0, 0 },
|
||||
{ static_cast<int32_t>(width), static_cast<int32_t>(height), 1 }
|
||||
}
|
||||
};
|
||||
Layer::ovkCmdBlitImage(
|
||||
buf,
|
||||
src, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
dst, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
1, &imageBlit,
|
||||
VK_FILTER_NEAREST
|
||||
);
|
||||
|
||||
if (makeSrcPresentable) {
|
||||
const VkImageMemoryBarrier presentBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.image = src,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
Layer::ovkCmdPipelineBarrier(buf,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
||||
0, nullptr, 0, nullptr,
|
||||
1, &presentBarrier);
|
||||
}
|
||||
|
||||
if (makeDstPresentable) {
|
||||
const VkImageMemoryBarrier presentBarrier{
|
||||
.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER,
|
||||
.srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT,
|
||||
.dstAccessMask = VK_ACCESS_MEMORY_READ_BIT,
|
||||
.oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,
|
||||
.newLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,
|
||||
.image = dst,
|
||||
.subresourceRange = {
|
||||
.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT,
|
||||
.levelCount = 1,
|
||||
.layerCount = 1
|
||||
}
|
||||
};
|
||||
Layer::ovkCmdPipelineBarrier(buf,
|
||||
VK_PIPELINE_STAGE_TRANSFER_BIT, post, 0,
|
||||
0, nullptr, 0, nullptr,
|
||||
1, &presentBarrier);
|
||||
}
|
||||
}
|
||||
|
||||
namespace {
|
||||
auto& logCounts() {
|
||||
static std::unordered_map<std::string, size_t> map;
|
||||
return map;
|
||||
}
|
||||
}
|
||||
|
||||
void Utils::logLimitN(const std::string& id, size_t n, const std::string& message) {
|
||||
auto& count = logCounts()[id];
|
||||
if (count <= n)
|
||||
std::cerr << "lsfg-vk: " << message << '\n';
|
||||
if (count == n)
|
||||
std::cerr << "(above message has been repeated " << n << " times, suppressing further)\n";
|
||||
count++;
|
||||
}
|
||||
|
||||
void Utils::resetLimitN(const std::string& id) noexcept {
|
||||
logCounts().erase(id);
|
||||
}
|
||||
|
||||
std::pair<std::string, std::string> Utils::getProcessName() {
|
||||
// check benchmark flag
|
||||
const char* benchmark_flag = std::getenv("LSFG_BENCHMARK");
|
||||
if (benchmark_flag)
|
||||
return { "benchmark", "benchmark" };
|
||||
std::array<char, 4096> exe{};
|
||||
|
||||
// then check override
|
||||
const char* process_name = std::getenv("LSFG_PROCESS");
|
||||
if (process_name && *process_name != '\0')
|
||||
return { process_name, process_name };
|
||||
|
||||
// find executed binary
|
||||
const ssize_t exe_len = readlink("/proc/self/exe", exe.data(), exe.size() - 1);
|
||||
if (exe_len <= 0)
|
||||
return { "Unknown Process", "unknown" };
|
||||
exe.at(static_cast<size_t>(exe_len)) = '\0';
|
||||
std::string exe_str(exe.data());
|
||||
|
||||
// find command name as well
|
||||
std::ifstream comm_file("/proc/self/comm");
|
||||
if (!comm_file.is_open())
|
||||
return { std::string(exe.data()), "unknown" };
|
||||
std::array<char, 257> comm{};
|
||||
comm_file.read(comm.data(), 256);
|
||||
comm.at(static_cast<size_t>(comm_file.gcount())) = '\0';
|
||||
std::string comm_str(comm.data());
|
||||
if (comm_str.back() == '\n')
|
||||
comm_str.pop_back();
|
||||
|
||||
// replace binary with exe for wine apps
|
||||
if (exe_str.find("wine") != std::string::npos
|
||||
|| exe_str.find("proton") != std::string::npos) {
|
||||
|
||||
std::ifstream proc_maps("/proc/self/maps");
|
||||
if (!proc_maps.is_open())
|
||||
return{ exe_str, comm_str };
|
||||
|
||||
std::string line;
|
||||
while (std::getline(proc_maps, line)) {
|
||||
if (!line.ends_with(".exe"))
|
||||
continue;
|
||||
|
||||
size_t pos = line.find_first_of('/');
|
||||
if (pos == std::string::npos) {
|
||||
pos = line.find_last_of(' ');
|
||||
if (pos == std::string::npos)
|
||||
continue;
|
||||
pos += 1; // skip space
|
||||
}
|
||||
|
||||
const std::string exe_name = line.substr(pos);
|
||||
if (exe_name.empty())
|
||||
continue;
|
||||
|
||||
exe_str = exe_name;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return{ exe_str, comm_str };
|
||||
}
|
||||
|
||||
std::string Utils::getConfigFile() {
|
||||
const char* configFile = std::getenv("LSFG_CONFIG");
|
||||
if (configFile && *configFile != '\0')
|
||||
return{configFile};
|
||||
const char* xdgPath = std::getenv("XDG_CONFIG_HOME");
|
||||
if (xdgPath && *xdgPath != '\0')
|
||||
return std::string(xdgPath) + "/lsfg-vk/conf.toml";
|
||||
const char* homePath = std::getenv("HOME");
|
||||
if (homePath && *homePath != '\0')
|
||||
return std::string(homePath) + "/.config/lsfg-vk/conf.toml";
|
||||
return "/etc/lsfg-vk/conf.toml";
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue