refactor(cleanup): initial swapchain logic & temporary backend creation

This commit is contained in:
PancakeTAS 2025-12-17 12:49:30 +01:00
parent 0923b4802a
commit 14182e70f2
No known key found for this signature in database
9 changed files with 457 additions and 124 deletions

View file

@ -29,6 +29,10 @@ namespace ls {
return *this->opt;
}
/// check if value is present
/// @return true if value is present
[[nodiscard]] bool has_value() const { return this->opt.has_value(); }
/// get reference to value
/// @return reference to value
/// @throws std::logic_error if no value present
@ -46,6 +50,15 @@ namespace ls {
throw std::logic_error("lazy: no value present");
return &(*this->opt);
}
/// get a mutable reference to value
/// @return mutable reference to value
/// @throws std::logic_error if no value present
T& mut() {
if (!this->opt.has_value())
throw std::logic_error("lazy: no value present");
return *this->opt;
}
private:
std::optional<T> opt{};
};

View file

@ -25,12 +25,18 @@ namespace vk {
PFN_vkGetPhysicalDeviceMemoryProperties GetPhysicalDeviceMemoryProperties;
PFN_vkCreateDevice CreateDevice;
PFN_vkGetDeviceProcAddr GetDeviceProcAddr;
// extension functions
PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR GetPhysicalDeviceSurfaceCapabilitiesKHR;
};
/// initialize vulkan instance function pointers
/// @param instance vulkan instance handle
/// @param mpa function to get instance proc addresses
VulkanInstanceFuncs initVulkanInstanceFuncs(VkInstance instance, PFN_vkGetInstanceProcAddr mpa);
/// @param graphical whether the device is graphical (rather than compute)
/// @return initialized function pointers
VulkanInstanceFuncs initVulkanInstanceFuncs(VkInstance instance, PFN_vkGetInstanceProcAddr mpa,
bool graphical);
using PhysicalDeviceSelector = const std::function<
VkPhysicalDevice(
@ -98,12 +104,18 @@ namespace vk {
PFN_vkGetMemoryFdKHR GetMemoryFdKHR;
PFN_vkImportSemaphoreFdKHR ImportSemaphoreFdKHR;
PFN_vkGetSemaphoreFdKHR GetSemaphoreFdKHR;
PFN_vkCreateSwapchainKHR CreateSwapchainKHR;
PFN_vkGetSwapchainImagesKHR GetSwapchainImagesKHR;
PFN_vkDestroySwapchainKHR DestroySwapchainKHR;
};
/// initialize vulkan device function pointers
/// @param fi instance function pointers
/// @param device logical device handle
VulkanDeviceFuncs initVulkanDeviceFuncs(const VulkanInstanceFuncs& fi, VkDevice device);
/// @param graphical whether the device is graphical (rather than compute)
/// @return initialized function pointers
VulkanDeviceFuncs initVulkanDeviceFuncs(const VulkanInstanceFuncs& fi, VkDevice device,
bool graphical);
/// vulkan version wrapper
class version {
@ -167,6 +179,9 @@ namespace vk {
/// get the vulkan device
/// @return the device handle
[[nodiscard]] const auto& dev() const { return this->device.get(); }
/// get the physical device
/// @return the physical device handle
[[nodiscard]] VkPhysicalDevice physdev() const { return this->phys_dev; }
/// get the command pool
/// @return the command pool handle
[[nodiscard]] const auto& cmdpool() const { return this->cmdPool.get(); }
@ -181,6 +196,9 @@ namespace vk {
/// @return true if fp16 is supported
[[nodiscard]] bool supportsFP16() const { return this->fp16; }
/// get instance-level function pointers
/// @return the instance function pointers
[[nodiscard]] const auto& fi() const { return this->instance_funcs; }
/// get device-level function pointers
/// @return the device function pointers
[[nodiscard]] const auto& df() const { return this->device_funcs; }
@ -191,7 +209,7 @@ namespace vk {
ls::owned_ptr<VkInstance> instance;
VulkanInstanceFuncs instance_funcs;
VkPhysicalDevice physdev;
VkPhysicalDevice phys_dev;
uint32_t queueFamilyIdx;
bool fp16;

View file

@ -173,7 +173,8 @@ namespace {
const std::vector<const char*> requestedExtensions{
VK_KHR_EXTERNAL_MEMORY_FD_EXTENSION_NAME,
VK_KHR_EXTERNAL_SEMAPHORE_FD_EXTENSION_NAME,
VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME // TODO: possibly attempt to get rid of
VK_KHR_FORMAT_FEATURE_FLAGS_2_EXTENSION_NAME, // TODO: possibly attempt to get rid of
VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME
};
const VkDeviceCreateInfo deviceInfo{
.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO,
@ -268,7 +269,8 @@ namespace {
}
/// initialize vulkan instance function pointers
VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceProcAddr mpa) {
VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceProcAddr mpa,
bool graphical) {
return {
.DestroyInstance = ipa<PFN_vkDestroyInstance>(mpa, i, "vkDestroyInstance"),
.EnumeratePhysicalDevices = ipa<PFN_vkEnumeratePhysicalDevices>(mpa, i,
@ -280,17 +282,22 @@ VulkanInstanceFuncs vk::initVulkanInstanceFuncs(VkInstance i, PFN_vkGetInstanceP
.GetPhysicalDeviceQueueFamilyProperties =
ipa<PFN_vkGetPhysicalDeviceQueueFamilyProperties>(mpa, i,
"vkGetPhysicalDeviceQueueFamilyProperties"),
.GetPhysicalDeviceFeatures2 = ipa<PFN_vkGetPhysicalDeviceFeatures2>(mpa, i,
"vkGetPhysicalDeviceFeatures2"),
.GetPhysicalDeviceFeatures2 = graphical ?
nullptr : ipa<PFN_vkGetPhysicalDeviceFeatures2>(mpa, i, "vkGetPhysicalDeviceFeatures2"),
.GetPhysicalDeviceMemoryProperties = ipa<PFN_vkGetPhysicalDeviceMemoryProperties>(mpa, i,
"vkGetPhysicalDeviceMemoryProperties"),
.CreateDevice = ipa<PFN_vkCreateDevice>(mpa, i, "vkCreateDevice"),
.GetDeviceProcAddr = ipa<PFN_vkGetDeviceProcAddr>(mpa, i, "vkGetDeviceProcAddr"),
.GetPhysicalDeviceSurfaceCapabilitiesKHR = graphical ?
ipa<PFN_vkGetPhysicalDeviceSurfaceCapabilitiesKHR>(mpa, i,
"vkGetPhysicalDeviceSurfaceCapabilitiesKHR") : nullptr
};
}
/// initialize vulkan device function pointers
VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevice d) {
VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevice d,
bool graphical) {
return {
.GetDeviceQueue = dpa<PFN_vkGetDeviceQueue>(f, d, "vkGetDeviceQueue"),
.DeviceWaitIdle = dpa<PFN_vkDeviceWaitIdle>(f, d, "vkDeviceWaitIdle"),
@ -355,6 +362,13 @@ VulkanDeviceFuncs vk::initVulkanDeviceFuncs(const VulkanInstanceFuncs& f, VkDevi
.GetMemoryFdKHR = dpa<PFN_vkGetMemoryFdKHR>(f, d, "vkGetMemoryFdKHR"),
.ImportSemaphoreFdKHR = dpa<PFN_vkImportSemaphoreFdKHR>(f, d, "vkImportSemaphoreFdKHR"),
.GetSemaphoreFdKHR = dpa<PFN_vkGetSemaphoreFdKHR>(f, d, "vkGetSemaphoreFdKHR"),
.CreateSwapchainKHR = graphical ?
dpa<PFN_vkCreateSwapchainKHR>(f, d, "vkCreateSwapchainKHR") : nullptr,
.GetSwapchainImagesKHR = graphical ?
dpa<PFN_vkGetSwapchainImagesKHR>(f, d, "vkGetSwapchainImagesKHR") : nullptr,
.DestroySwapchainKHR = graphical ?
dpa<PFN_vkDestroySwapchainKHR>(f, d, "vkDestroySwapchainKHR") : nullptr
};
}
@ -367,23 +381,23 @@ Vulkan::Vulkan(const std::string& appName, version appVersion,
appName, appVersion,
engineName, engineVersion
)),
instance_funcs(initVulkanInstanceFuncs(*this->instance, get_mpa())),
physdev(findPhysicalDevice(this->instance_funcs,
instance_funcs(initVulkanInstanceFuncs(*this->instance, get_mpa(), false)),
phys_dev(findPhysicalDevice(this->instance_funcs,
*this->instance,
selectPhysicalDevice
)),
queueFamilyIdx(findQFI(this->instance_funcs, this->physdev,
queueFamilyIdx(findQFI(this->instance_funcs, this->phys_dev,
isGraphical ? VK_QUEUE_GRAPHICS_BIT : VK_QUEUE_COMPUTE_BIT)),
fp16(checkFP16(this->instance_funcs, this->physdev)),
fp16(checkFP16(this->instance_funcs, this->phys_dev)),
device(createLogicalDevice(this->instance_funcs,
this->physdev,
this->phys_dev,
this->queueFamilyIdx,
this->fp16
)),
setLoaderData(setLoaderData),
device_funcs(initVulkanDeviceFuncs(
this->instance_funcs,
*this->device
*this->device, false
)),
computeQueue(getQueue(this->device_funcs, *this->device,
this->setLoaderData,
@ -405,8 +419,8 @@ Vulkan::Vulkan(VkInstance instance, VkDevice device,
std::optional<PFN_vkSetDeviceLoaderData> setLoaderData) :
instance(new VkInstance(instance)),
instance_funcs(instanceFuncs),
physdev(physdev),
queueFamilyIdx(findQFI(this->instance_funcs, this->physdev,
phys_dev(physdev),
queueFamilyIdx(findQFI(this->instance_funcs, this->phys_dev,
isGraphical ? VK_QUEUE_GRAPHICS_BIT : VK_QUEUE_COMPUTE_BIT)),
fp16(false),
device(new VkDevice(device)),
@ -431,7 +445,7 @@ std::optional<uint32_t> Vulkan::findMemoryTypeIndex(
VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT;
VkPhysicalDeviceMemoryProperties props;
this->instance_funcs.GetPhysicalDeviceMemoryProperties(this->physdev, &props);
this->instance_funcs.GetPhysicalDeviceMemoryProperties(this->phys_dev, &props);
std::array<VkMemoryType, 32> memTypes = std::to_array(props.memoryTypes);
for (uint32_t i = 0; i < props.memoryTypeCount; ++i)

View file

@ -2,6 +2,7 @@ set(LAYER_SOURCES
"src/configuration/config.cpp"
"src/configuration/detection.cpp"
"src/context/instance.cpp"
"src/context/swapchain.cpp"
"src/entrypoint.cpp")
add_library(lsfg-vk-layer SHARED ${LAYER_SOURCES})

View file

@ -1,20 +1,45 @@
#include "instance.hpp"
#include "../configuration/config.hpp"
#include "../configuration/detection.hpp"
#include "swapchain.hpp"
#include "lsfg-vk-backend/lsfgvk.hpp"
#include "lsfg-vk-common/helpers/errors.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
#include <algorithm>
#include <cstdint>
#include <cstdlib>
#include <filesystem>
#include <functional>
#include <iostream>
#include <optional>
#include <string>
#include <utility>
#include <vector>
#include <vulkan/vulkan_core.h>
using namespace lsfgvk;
using namespace lsfgvk::layer;
namespace {
/// helper function to add required extensions
std::vector<const char*> add_extensions(const char* const* existingExtensions, size_t count,
const std::vector<const char*>& requiredExtensions) {
std::vector<const char*> extensions(count);
std::copy_n(existingExtensions, count, extensions.data());
for (const auto& requiredExtension : requiredExtensions) {
auto it = std::ranges::find_if(extensions,
[requiredExtension](const char* extension) {
return std::string(extension) == std::string(requiredExtension);
});
if (it == extensions.end())
extensions.push_back(requiredExtension);
}
return extensions;
}
// find the shader dll
std::filesystem::path findShaderDll() {
const std::vector<std::filesystem::path> FRAGMENTS{{
@ -82,47 +107,130 @@ Root::Root() {
std::cerr << "(identified via process name)\n";
break;
}
// create backend
/*const auto& global = this->config.getGlobalConf();
this->backend.emplace(
[gpu = profile->second.gpu](
const std::string& deviceName,
std::pair<const std::string&, const std::string&> ids,
const std::optional<std::string>& pci
) {
if (!gpu)
return true;
return (deviceName == *gpu)
|| (ids.first + ":" + ids.second == *gpu)
|| (pci && *pci == *gpu);
},
global.dll.value_or(findShaderDll()),
global.allow_fp16
);*/
}
std::vector<const char*> Root::instanceExtensions() const {
void Root::update() {
if (!this->config.isUpToDate())
this->config.reload();
}
void Root::modifyInstanceCreateInfo(VkInstanceCreateInfo& createInfo,
const std::function<void(void)>& finish) const {
if (!this->active_profile.has_value())
return {};
return;
return {
"VK_KHR_get_physical_device_properties2",
"VK_KHR_external_memory_capabilities",
"VK_KHR_external_semaphore_capabilities"
};
auto extensions = add_extensions(
createInfo.ppEnabledExtensionNames,
createInfo.enabledExtensionCount,
{
"VK_KHR_get_physical_device_properties2",
"VK_KHR_external_memory_capabilities",
"VK_KHR_external_semaphore_capabilities"
}
);
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
finish();
}
std::vector<const char*> Root::deviceExtensions() const {
void Root::modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo,
const std::function<void(void)>& finish) const {
if (!this->active_profile.has_value())
return {};
return;
return {
"VK_KHR_external_memory",
"VK_KHR_external_memory_fd",
"VK_KHR_external_semaphore",
"VK_KHR_external_semaphore_fd",
"VK_KHR_timeline_semaphore"
auto extensions = add_extensions(
createInfo.ppEnabledExtensionNames,
createInfo.enabledExtensionCount,
{
"VK_KHR_external_memory",
"VK_KHR_external_memory_fd",
"VK_KHR_external_semaphore",
"VK_KHR_external_semaphore_fd",
"VK_KHR_timeline_semaphore"
}
);
createInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
createInfo.ppEnabledExtensionNames = extensions.data();
bool isFeatureEnabled = false;
auto* featureInfo = reinterpret_cast<VkBaseInStructure*>(const_cast<void*>(createInfo.pNext));
while (featureInfo) {
if (featureInfo->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES) {
auto* features = reinterpret_cast<VkPhysicalDeviceVulkan12Features*>(featureInfo);
features->timelineSemaphore = VK_TRUE;
isFeatureEnabled = true;
} else if (featureInfo->sType == VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES) {
auto* features = reinterpret_cast<VkPhysicalDeviceTimelineSemaphoreFeatures*>(featureInfo);
features->timelineSemaphore = VK_TRUE;
isFeatureEnabled = true;
}
featureInfo = const_cast<VkBaseInStructure*>(featureInfo->pNext);
}
VkPhysicalDeviceTimelineSemaphoreFeatures timelineFeatures{
.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_TIMELINE_SEMAPHORE_FEATURES,
.pNext = const_cast<void*>(createInfo.pNext),
.timelineSemaphore = VK_TRUE
};
if (!isFeatureEnabled)
createInfo.pNext = &timelineFeatures;
finish();
}
void Root::modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfoKHR& createInfo,
const std::function<void(void)>& finish) const {
if (!this->active_profile.has_value())
return;
VkSurfaceCapabilitiesKHR caps{};
auto res = vk.fi().GetPhysicalDeviceSurfaceCapabilitiesKHR(
vk.physdev(), createInfo.surface, &caps);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkGetPhysicalDeviceSurfaceCapabilitiesKHR() failed");
context_ModifySwapchainCreateInfo(*this->active_profile, caps.maxImageCount, createInfo);
finish();
}
void Root::createSwapchainContext(const vk::Vulkan& vk,
VkSwapchainKHR swapchain, const SwapchainInfo& info) {
if (!this->active_profile.has_value())
throw lsfgvk::error("attempted to create swapchain context while layer is inactive");
const auto& profile = *this->active_profile;
if (!this->backend.has_value()) { // emplace backend late, due to loader bug
const auto& global = this->config.getGlobalConf();
setenv("DISABLE_LSFGVK", "1", 1); // NOLINT (c++-include)
this->backend.emplace(
[gpu = profile.gpu](
const std::string& deviceName,
std::pair<const std::string&, const std::string&> ids,
const std::optional<std::string>& pci
) {
if (!gpu)
return true;
return (deviceName == *gpu)
|| (ids.first + ":" + ids.second == *gpu)
|| (pci && *pci == *gpu);
},
global.dll.value_or(findShaderDll()),
global.allow_fp16
);
unsetenv("DISABLE_LSFGVK"); // NOLINT (c++-include)
}
this->swapchains.emplace(swapchain,
Swapchain(vk, this->backend.mut(), profile, info));
}
void Root::removeSwapchainContext(VkSwapchainKHR swapchain) {
this->swapchains.erase(swapchain);
}

View file

@ -3,9 +3,13 @@
#include "../configuration/config.hpp"
#include "lsfg-vk-backend/lsfgvk.hpp"
#include "lsfg-vk-common/helpers/pointers.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
#include "swapchain.hpp"
#include <optional>
#include <vector>
#include <unordered_map>
#include <vulkan/vulkan_core.h>
namespace lsfgvk::layer {
@ -20,17 +24,59 @@ namespace lsfgvk::layer {
/// @return true if active
[[nodiscard]] bool active() const { return this->active_profile.has_value(); }
/// required instance extensions
/// @return list of extension names
[[nodiscard]] std::vector<const char*> instanceExtensions() const;
/// required device extensions
/// @return list of extension names
[[nodiscard]] std::vector<const char*> deviceExtensions() const;
/// ensure the layer is up-to-date
void update();
// /// required instance extensions
// /// @return list of extension names
// [[nodiscard]] std::vector<const char*> instanceExtensions() const;
// /// required device extensions
// /// @return list of extension names
// [[nodiscard]] std::vector<const char*> deviceExtensions() const;
/// modify instance create info
/// @param createInfo original create info
/// @param finish function to call after modification
void modifyInstanceCreateInfo(VkInstanceCreateInfo& createInfo,
const std::function<void(void)>& finish) const;
/// modify device create info
/// @param createInfo original create info
/// @param finish function to call after modification
void modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo,
const std::function<void(void)>& finish) const;
/// modify swapchain create info
/// @param vk vulkan instance
/// @param createInfo original create info
/// @param finish function to call after modification
void modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfoKHR& createInfo,
const std::function<void(void)>& finish) const;
/// create swapchain context
/// @param vk vulkan instance
/// @param swapchain swapchain handle
/// @param info swapchain info
void createSwapchainContext(const vk::Vulkan& vk, VkSwapchainKHR swapchain,
const SwapchainInfo& info);
/// get swapchain context
/// @param swapchain swapchain handle
/// @return swapchain context
/// @throws lsfgvk::error if not found
[[nodiscard]] Swapchain& getSwapchainContext(VkSwapchainKHR swapchain) {
const auto& it = this->swapchains.find(swapchain);
if (it == this->swapchains.end())
throw lsfgvk::error("swapchain context not found");
return it->second;
}
/// remove swapchain context
/// @param swapchain swapchain handle
void removeSwapchainContext(VkSwapchainKHR swapchain);
private:
Configuration config;
std::optional<GameConf> active_profile;
ls::lazy<lsfgvk::Instance> backend;
std::unordered_map<VkSwapchainKHR, Swapchain> swapchains;
};
}

View file

@ -0,0 +1,33 @@
#include "swapchain.hpp"
#include "../configuration/config.hpp"
#include "lsfg-vk-backend/lsfgvk.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
#include <cstdint>
#include <iostream>
#include <vulkan/vulkan_core.h>
using namespace lsfgvk;
using namespace lsfgvk::layer;
void layer::context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t maxImages,
VkSwapchainCreateInfoKHR& createInfo) {
createInfo.imageUsage |=
VK_IMAGE_USAGE_TRANSFER_DST_BIT | VK_IMAGE_USAGE_TRANSFER_SRC_BIT;
switch (profile.pacing) {
case lsfgvk::layer::Pacing::None:
createInfo.minImageCount += profile.multiplier;
if (maxImages && createInfo.minImageCount > maxImages)
createInfo.minImageCount = maxImages;
createInfo.presentMode = VK_PRESENT_MODE_FIFO_KHR;
break;
}
}
Swapchain::Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend,
const GameConf& profile, const SwapchainInfo& info) {
std::cerr << "lsfg-vk: swapchain created :3\n";
}

View file

@ -0,0 +1,42 @@
#pragma once
#include "../configuration/config.hpp"
#include "lsfg-vk-backend/lsfgvk.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
#include <vector>
#include <vulkan/vulkan_core.h>
namespace lsfgvk::layer {
/// swapchain info struct
struct SwapchainInfo {
std::vector<VkImage> images;
VkFormat format;
VkColorSpaceKHR colorSpace;
VkExtent2D extent;
VkPresentModeKHR presentMode;
};
/// modify the swapchain create info based on the profile pre-swapchain creation
/// @param profile active game profile
/// @param maxImages maximum number of images supported by the surface
/// @param createInfo swapchain create info to modify
void context_ModifySwapchainCreateInfo(const GameConf& profile, uint32_t maxImages,
VkSwapchainCreateInfoKHR& createInfo);
/// swapchain context for a layer instance
class Swapchain {
public:
/// create a new swapchain context
/// @param vk vulkan instance
/// @param profile active game profile
/// @param backend lsfg-vk backend instance
/// @param info swapchain info
Swapchain(const vk::Vulkan& vk, lsfgvk::Instance& backend,
const GameConf& profile, const SwapchainInfo& info);
private:
};
}

View file

@ -1,13 +1,13 @@
#include "context/instance.hpp"
#include "lsfg-vk-common/helpers/errors.hpp"
#include "lsfg-vk-common/vulkan/vulkan.hpp"
#include <algorithm>
#include <cstddef>
#include <cstdint>
#include <exception>
#include <iostream>
#include <string>
#include <unordered_map>
#include <utility>
#include <vector>
#include <vulkan/vk_layer.h>
@ -15,26 +15,6 @@
using namespace lsfgvk;
namespace {
/// helper function to add required extensions
std::vector<const char*> add_extensions(const char* const* existingExtensions, size_t count,
const std::vector<const char*>& requiredExtensions) {
std::vector<const char*> extensions(count);
std::copy_n(existingExtensions, count, extensions.data());
for (const auto& requiredExtension : requiredExtensions) {
auto it = std::ranges::find_if(extensions,
[requiredExtension](const char* extension) {
return std::string(extension) == std::string(requiredExtension);
});
if (it == extensions.end())
extensions.push_back(requiredExtension);
}
return extensions;
}
}
namespace {
// global layer info initialized at layer negotiation
struct LayerInfo {
@ -94,26 +74,25 @@ namespace {
return VK_ERROR_INITIALIZATION_FAILED;
}
auto extensions = add_extensions(
info->ppEnabledExtensionNames,
info->enabledExtensionCount,
layer_info->root.instanceExtensions());
VkInstanceCreateInfo newInfo = *info;
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
newInfo.ppEnabledExtensionNames = extensions.data();
auto res = vkCreateInstance(&newInfo, alloc, instance);
if (res == VK_ERROR_EXTENSION_NOT_PRESENT)
std::cerr << "lsfg-vk: required Vulkan instance extensions are not present. "
"Your GPU driver is not supported.\n";
if (res != VK_SUCCESS)
return res;
try {
VkInstanceCreateInfo newInfo = *info;
layer_info->root.modifyInstanceCreateInfo(newInfo,
[=, newInfo = &newInfo]() {
auto res = vkCreateInstance(newInfo, alloc, instance);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkCreateInstance() failed");
}
);
} catch (const ls::vulkan_error& e) {
if (e.error() == VK_ERROR_EXTENSION_NOT_PRESENT)
std::cerr << "lsfg-vk: required Vulkan instance extensions are not present. "
"Your GPU driver is not supported.\n";
return e.error();
}
instance_info = new InstanceInfo{
.handle = *instance,
.funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr),
.funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr, true),
.devices = {}
};
return VK_SUCCESS;
@ -171,22 +150,21 @@ namespace {
}
// create device
auto extensions = add_extensions(
info->ppEnabledExtensionNames,
info->enabledExtensionCount,
layer_info->root.deviceExtensions());
VkDeviceCreateInfo newInfo = *info;
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
newInfo.ppEnabledExtensionNames = extensions.data();
auto res = instance_info->funcs.CreateDevice(physdev, &newInfo, alloc, device);
if (res == VK_ERROR_EXTENSION_NOT_PRESENT)
std::cerr << "lsfg-vk: required Vulkan device extensions are not present. "
"Your GPU driver is not supported.\n";
if (res != VK_SUCCESS)
return res;
try {
VkDeviceCreateInfo newInfo = *info;
layer_info->root.modifyDeviceCreateInfo(newInfo,
[=, newInfo = &newInfo]() {
auto res = instance_info->funcs.CreateDevice(physdev, newInfo, alloc, device);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkCreateDevice() failed");
}
);
} catch (const ls::vulkan_error& e) {
if (e.error() == VK_ERROR_EXTENSION_NOT_PRESENT)
std::cerr << "lsfg-vk: required Vulkan device extensions are not present. "
"Your GPU driver is not supported.\n";
return e.error();
}
// create layer instance
try {
@ -194,7 +172,8 @@ namespace {
*device,
vk::Vulkan(
instance_info->handle, *device, physdev,
instance_info->funcs, vk::initVulkanDeviceFuncs(instance_info->funcs, *device),
instance_info->funcs, vk::initVulkanDeviceFuncs(instance_info->funcs, *device,
true),
true, setLoaderData
)
);
@ -260,30 +239,107 @@ namespace {
}
// get instance-level function pointers
PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* pName) {
if (!pName) return nullptr;
PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* name) {
if (!name) return nullptr;
if (std::string(pName) == "vkCreateInstance") // pre-instance function
if (std::string(name) == "vkCreateInstance") // pre-instance function
return reinterpret_cast<PFN_vkVoidFunction>(myvkCreateInstance);
auto func = getProcAddr(pName);
auto func = getProcAddr(name);
if (func) return func;
if (!layer_info->GetInstanceProcAddr) return nullptr;
return layer_info->GetInstanceProcAddr(instance, pName);
return layer_info->GetInstanceProcAddr(instance, name);
}
// get device-level function pointers
PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* pName) {
if (!pName) return nullptr;
PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* name) {
if (!name) return nullptr;
auto func = getProcAddr(pName);
auto func = getProcAddr(name);
if (func) return func;
if (!instance_info->funcs.GetDeviceProcAddr) return nullptr;
return instance_info->funcs.GetDeviceProcAddr(device, pName);
return instance_info->funcs.GetDeviceProcAddr(device, name);
}
}
namespace {
VkResult myvkCreateSwapchainKHR(
VkDevice device,
const VkSwapchainCreateInfoKHR* info,
const VkAllocationCallbacks* alloc,
VkSwapchainKHR* swapchain) {
const auto& it = instance_info->devices.find(device);
if (it == instance_info->devices.end())
return VK_ERROR_INITIALIZATION_FAILED;
try {
// retire old swapchain
if (info->oldSwapchain)
layer_info->root.removeSwapchainContext(info->oldSwapchain);
layer_info->root.update(); // ensure config is up to date
// create swapchain
VkSwapchainCreateInfoKHR newInfo = *info;
layer_info->root.modifySwapchainCreateInfo(it->second, newInfo,
[=, newInfo = &newInfo]() {
auto res = it->second.df().CreateSwapchainKHR(
device, newInfo, alloc, swapchain);
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkCreateSwapchainKHR() failed");
}
);
// get all swapchain images
uint32_t imageCount{};
auto res = it->second.df().GetSwapchainImagesKHR(device, *swapchain,
&imageCount, nullptr);
if (res != VK_SUCCESS || imageCount == 0)
throw ls::vulkan_error(res, "vkGetSwapchainImagesKHR() failed");
std::vector<VkImage> swapchainImages(imageCount);
res = it->second.df().GetSwapchainImagesKHR(device, *swapchain,
&imageCount, swapchainImages.data());
if (res != VK_SUCCESS)
throw ls::vulkan_error(res, "vkGetSwapchainImagesKHR() failed");
// create lsfg-vk swapchain
layer_info->root.createSwapchainContext(it->second, *swapchain, {
.images = std::move(swapchainImages),
.format = newInfo.imageFormat,
.colorSpace = newInfo.imageColorSpace,
.extent = newInfo.imageExtent,
.presentMode = newInfo.presentMode
});
return res;
} catch (const ls::vulkan_error& e) {
std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n";
std::cerr << "- " << e.what() << '\n';
return e.error();
} catch (const std::exception& e) {
std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain creation:\n";
std::cerr << "- " << e.what() << '\n';
return VK_ERROR_INITIALIZATION_FAILED;
}
}
void myvkDestroySwapchainKHR(
VkDevice device,
VkSwapchainKHR swapchain,
const VkAllocationCallbacks* alloc) {
const auto& it = instance_info->devices.find(device);
if (it == instance_info->devices.end())
return;
// destroy lsfg-vk swapchain
layer_info->root.removeSwapchainContext(swapchain);
// destroy swapchain
it->second.df().DestroySwapchainKHR(device, swapchain, alloc);
}
}
/// Vulkan layer entrypoint
@ -303,7 +359,9 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers
{ "vkCreateInstance", VKPTR(myvkCreateInstance) },
{ "vkCreateDevice", VKPTR(myvkCreateDevice) },
{ "vkDestroyDevice", VKPTR(myvkDestroyDevice) },
{ "vkDestroyInstance", VKPTR(myvkDestroyInstance) }
{ "vkDestroyInstance", VKPTR(myvkDestroyInstance) },
{ "vkCreateSwapchainKHR", VKPTR(myvkCreateSwapchainKHR) },
{ "vkDestroySwapchainKHR", VKPTR(myvkDestroySwapchainKHR) }
#undef VKPTR
},
.root = layer::Root()