base swapchain context class

This commit is contained in:
PancakeTAS 2025-07-01 09:51:23 +02:00
parent b4e00d2d65
commit 59b05ce220
No known key found for this signature in database
6 changed files with 232 additions and 2 deletions

View file

@ -29,7 +29,7 @@ add_library(lsfg-vk SHARED ${SOURCES})
target_include_directories(lsfg-vk
PRIVATE include)
target_link_libraries(lsfg-vk
PRIVATE lsfg-vk-gen)
PRIVATE lsfg-vk-gen vulkan)
target_compile_options(lsfg-vk PRIVATE
-Weverything
# disable compat c++ flags

View file

@ -1,14 +1,18 @@
#ifndef APPLICATION_HPP
#define APPLICATION_HPP
#include <unordered_map>
#include <vector>
#include <vulkan/vulkan_core.h>
class SwapchainContext;
///
/// Main application class, wrapping around the Vulkan device.
///
class Application {
public:
///
/// Create the application.
///
@ -20,6 +24,31 @@ public:
///
Application(VkDevice device, VkPhysicalDevice physicalDevice);
///
/// Add a swapchain to the application.
///
/// @param handle The Vulkan handle of the swapchain.
/// @param format The format of the swapchain.
/// @param extent The extent of the images.
/// @param images The swapchain images.
///
/// @throws std::invalid_argument if the handle is already added.
/// @throws ls::vulkan_error if any Vulkan call fails.
///
void addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images);
///
/// Remove a swapchain from the application.
///
/// @param handle The Vulkan handle of the swapchain state to remove.
/// @return true if the swapchain was removed, false if it was already retired.
///
/// @throws std::invalid_argument if the handle is null.
///
bool removeSwapchain(VkSwapchainKHR handle);
/// Get the Vulkan device.
[[nodiscard]] VkDevice getDevice() const { return this->device; }
/// Get the Vulkan physical device.
@ -39,7 +68,52 @@ private:
VkPhysicalDevice physicalDevice;
// (owned resources)
std::unordered_map<VkSwapchainKHR, SwapchainContext> swapchains;
};
///
/// An application's Vulkan swapchain and it's associated resources.
///
class SwapchainContext {
public:
///
/// Create the swapchain context.
///
/// @param swapchain The Vulkan swapchain handle.
/// @param format The format of the swapchain images.
/// @param extent The extent of the swapchain images.
/// @param images The swapchain images.
///
/// @throws std::invalid_argument if any parameter is null
/// @throws ls::vulkan_error if any Vulkan call fails.
///
SwapchainContext(VkSwapchainKHR swapchain, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images);
/// Get the Vulkan swapchain handle.
[[nodiscard]] VkSwapchainKHR handle() const { return this->swapchain; }
/// Get the format of the swapchain images.
[[nodiscard]] VkFormat getFormat() const { return this->format; }
/// Get the extent of the swapchain images.
[[nodiscard]] VkExtent2D getExtent() const { return this->extent; }
/// Get the swapchain images.
[[nodiscard]] const std::vector<VkImage>& getImages() const { return this->images; }
// Non-copyable, trivially moveable and destructible
SwapchainContext(const SwapchainContext&) = delete;
SwapchainContext& operator=(const SwapchainContext&) = delete;
SwapchainContext(SwapchainContext&&) = default;
SwapchainContext& operator=(SwapchainContext&&) = default;
~SwapchainContext() = default;
private:
// (non-owned resources)
VkSwapchainKHR swapchain{};
VkFormat format{};
VkExtent2D extent{};
std::vector<VkImage> images;
// (owned resources)
};
#endif // APPLICATION_HPP

View file

@ -48,6 +48,20 @@ namespace Vulkan::Funcs {
VkDevice device,
const VkAllocationCallbacks* pAllocator
);
/// Call to the original vkCreateSwapchainKHR function.
VkResult ovkCreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSwapchainKHR* pSwapchain
);
/// Call to the original vkDestroySwapchainKHR function.
void ovkDestroySwapchainKHR(
VkDevice device,
VkSwapchainKHR swapchain,
const VkAllocationCallbacks* pAllocator
);
}
#endif // FUNCS_HPP

View file

@ -7,3 +7,33 @@ Application::Application(VkDevice device, VkPhysicalDevice physicalDevice)
if (device == VK_NULL_HANDLE || physicalDevice == VK_NULL_HANDLE)
throw std::invalid_argument("Invalid Vulkan device or physical device");
}
void Application::addSwapchain(VkSwapchainKHR handle, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images) {
// add the swapchain handle
auto it = this->swapchains.find(handle);
if (it != this->swapchains.end())
throw std::invalid_argument("Swapchain handle already exists");
// initialize the swapchain context
this->swapchains.emplace(handle, SwapchainContext(handle, format, extent, images));
}
SwapchainContext::SwapchainContext(VkSwapchainKHR swapchain, VkFormat format, VkExtent2D extent,
const std::vector<VkImage>& images)
: swapchain(swapchain), format(format), extent(extent), images(images) {
if (swapchain == VK_NULL_HANDLE || format == VK_FORMAT_UNDEFINED)
throw std::invalid_argument("Invalid swapchain or images");
}
bool Application::removeSwapchain(VkSwapchainKHR handle) {
if (handle == VK_NULL_HANDLE)
throw std::invalid_argument("Invalid swapchain handle");
// remove the swapchain context
auto it = this->swapchains.find(handle);
if (it == this->swapchains.end())
return false; // already retired... hopefully
this->swapchains.erase(it);
return true;
}

View file

@ -2,6 +2,7 @@
#include "loader/dl.hpp"
#include "loader/vk.hpp"
#include "log.hpp"
#include <vulkan/vulkan_core.h>
using namespace Vulkan;
@ -11,6 +12,9 @@ namespace {
PFN_vkCreateDevice vkCreateDevice_ptr{};
PFN_vkDestroyDevice vkDestroyDevice_ptr{};
PFN_vkCreateSwapchainKHR vkCreateSwapchainKHR_ptr{};
PFN_vkDestroySwapchainKHR vkDestroySwapchainKHR_ptr{};
}
void Funcs::initialize() {
@ -51,6 +55,20 @@ void Funcs::initializeInstance(VkInstance instance) {
}
void Funcs::initializeDevice(VkDevice device) {
if (vkCreateSwapchainKHR_ptr || vkDestroySwapchainKHR_ptr) {
Log::warn("lsfg-vk(funcs): Device Vulkan functions already initialized, did you call it twice?");
return;
}
vkCreateSwapchainKHR_ptr = reinterpret_cast<PFN_vkCreateSwapchainKHR>(
Loader::VK::ovkGetDeviceProcAddr(device, "vkCreateSwapchainKHR"));
vkDestroySwapchainKHR_ptr = reinterpret_cast<PFN_vkDestroySwapchainKHR>(
Loader::VK::ovkGetDeviceProcAddr(device, "vkDestroySwapchainKHR"));
if (!vkCreateSwapchainKHR_ptr || !vkDestroySwapchainKHR_ptr) {
Log::error("lsfg-vk(funcs): Failed to initialize Vulkan device functions, missing symbols");
exit(EXIT_FAILURE);
}
Log::debug("lsfg-vk(funcs): Initialized device Vulkan functions with original pointers");
}
@ -82,3 +100,18 @@ void Funcs::ovkDestroyDevice(
const VkAllocationCallbacks* pAllocator) {
vkDestroyDevice_ptr(device, pAllocator);
}
VkResult Funcs::ovkCreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSwapchainKHR* pSwapchain) {
return vkCreateSwapchainKHR_ptr(device, pCreateInfo, pAllocator, pSwapchain);
}
void Funcs::ovkDestroySwapchainKHR(
VkDevice device,
VkSwapchainKHR swapchain,
const VkAllocationCallbacks* pAllocator) {
vkDestroySwapchainKHR_ptr(device, swapchain, pAllocator);
}

View file

@ -56,6 +56,73 @@ namespace {
return res;
}
VkResult myvkCreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* pCreateInfo,
const VkAllocationCallbacks* pAllocator,
VkSwapchainKHR* pSwapchain) {
auto res = Funcs::ovkCreateSwapchainKHR(device, pCreateInfo, pAllocator, pSwapchain);
// add the swapchain to the application
if (!application.has_value()) {
Log::error("Application not initialized, cannot create swapchain");
exit(EXIT_FAILURE);
}
try {
if (pCreateInfo->oldSwapchain) {
if (!application->removeSwapchain(pCreateInfo->oldSwapchain))
throw std::runtime_error("Failed to remove old swapchain");
Log::info("lsfg-vk(hooks): Swapchain retired successfully");
}
uint32_t imageCount{};
auto res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, nullptr);
if (res != VK_SUCCESS || imageCount == 0)
throw LSFG::vulkan_error(res, "Failed to get swapchain images count");
std::vector<VkImage> swapchainImages(imageCount);
res = vkGetSwapchainImagesKHR(device, *pSwapchain, &imageCount, swapchainImages.data());
if (res != VK_SUCCESS)
throw LSFG::vulkan_error(res, "Failed to get swapchain images");
application->addSwapchain(*pSwapchain,
pCreateInfo->imageFormat, pCreateInfo->imageExtent, swapchainImages);
Log::info("lsfg-vk(hooks): Swapchain created successfully with {} images",
swapchainImages.size());
} catch (const LSFG::vulkan_error& e) {
Log::error("Encountered Vulkan error {:x} while creating swapchain: {}",
static_cast<uint32_t>(e.error()), e.what());
exit(EXIT_FAILURE);
} catch (const std::exception& e) {
Log::error("Encountered error while creating swapchain: {}", e.what());
exit(EXIT_FAILURE);
}
return res;
}
void myvkDestroySwapchainKHR(
VkDevice device,
VkSwapchainKHR swapchain,
const VkAllocationCallbacks* pAllocator) {
if (!application.has_value()) {
Log::error("Application not initialized, cannot destroy swapchain");
exit(EXIT_FAILURE);
}
// remove the swapchain from the application
try {
if (application->removeSwapchain(swapchain))
Log::info("lsfg-vk(hooks): Swapchain retired successfully");
} catch (const std::exception& e) {
Log::error("Encountered error while removing swapchain: {}", e.what());
exit(EXIT_FAILURE);
}
Funcs::ovkDestroySwapchainKHR(device, swapchain, pAllocator);
}
void myvkDestroyDevice(
VkDevice device,
const VkAllocationCallbacks* pAllocator) {
@ -85,6 +152,10 @@ void Hooks::initialize() {
reinterpret_cast<void*>(myvkCreateDevice));
Loader::VK::registerSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
Loader::VK::registerSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
Loader::VK::registerSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
// register hooks to dynamic loader under libvulkan.so.1
Loader::DL::File vk1("libvulkan.so.1");
@ -94,6 +165,10 @@ void Hooks::initialize() {
reinterpret_cast<void*>(myvkCreateDevice));
vk1.defineSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
vk1.defineSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
vk1.defineSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
Loader::DL::registerFile(vk1);
// register hooks to dynamic loader under libvulkan.so
@ -104,5 +179,9 @@ void Hooks::initialize() {
reinterpret_cast<void*>(myvkCreateDevice));
vk2.defineSymbol("vkDestroyDevice",
reinterpret_cast<void*>(myvkDestroyDevice));
vk2.defineSymbol("vkCreateSwapchainKHR",
reinterpret_cast<void*>(myvkCreateSwapchainKHR));
vk2.defineSymbol("vkDestroySwapchainKHR",
reinterpret_cast<void*>(myvkDestroySwapchainKHR));
Loader::DL::registerFile(vk2);
}