refactor(cleanup): deinitialization logic

This commit is contained in:
PancakeTAS 2025-12-15 16:25:48 +01:00
parent 6f5f8edbdd
commit 6bd907516a

View file

@ -8,6 +8,7 @@
#include <iostream> #include <iostream>
#include <string> #include <string>
#include <unordered_map> #include <unordered_map>
#include <utility>
#include <vector> #include <vector>
#include <vulkan/vk_layer.h> #include <vulkan/vk_layer.h>
@ -33,41 +34,38 @@ namespace {
return extensions; return extensions;
} }
/// struct containing various helper globals
struct Globals {
layer::Layer layer;
VkInstance instance{VK_NULL_HANDLE}; // if there are multiple instances, we scream
vk::VulkanInstanceFuncs fi{};
std::unordered_map<std::string, PFN_vkVoidFunction> procAddrMap; // all overriden pointers
std::unordered_map<VkDevice, layer::LayerInstance> device2InstanceMap;
};
} }
namespace { namespace {
Globals* global{nullptr}; // device-wide info initialized at device creation
void populateProcAddrMap(); struct DeviceInfo {
VkDevice handle;
vk::VulkanDeviceFuncs funcs;
layer::Instance layer;
};
// instance-wide info initialized at instance creation
struct InstanceInfo {
VkInstance handle;
vk::VulkanInstanceFuncs funcs;
std::unordered_map<VkDevice, DeviceInfo*> devices;
}* instance_info;
// global layer info initialized at layer negotiation
struct LayerInfo {
layer::Layer layer; //!< basic layer info
std::unordered_map<std::string, PFN_vkVoidFunction> map; //!< function pointer override map
PFN_vkGetInstanceProcAddr GetInstanceProcAddr;
}* layer_info;
// create instance // create instance
PFN_vkGetInstanceProcAddr nxvkGetInstanceProcAddr{nullptr};
VkResult myvkCreateInstance( VkResult myvkCreateInstance(
const VkInstanceCreateInfo* info, const VkInstanceCreateInfo* info,
const VkAllocationCallbacks* alloc, const VkAllocationCallbacks* alloc,
VkInstance* instance) { VkInstance* instance) {
// try to load lsfg-vk layer
try {
if (!global) { // cleanup in vkDestroyInstance
global = new Globals();
populateProcAddrMap();
}
} catch (const std::exception& e) {
std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n";
std::cerr << "- " << e.what() << '\n';
}
// apply layer chaining // apply layer chaining
auto* layerInfo = reinterpret_cast<VkLayerInstanceCreateInfo*>(const_cast<void*>(info->pNext)); auto* layerInfo = reinterpret_cast<VkLayerInstanceCreateInfo*>(const_cast<void*>(info->pNext));
while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO
@ -87,8 +85,8 @@ namespace {
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }
nxvkGetInstanceProcAddr = linkInfo->pfnNextGetInstanceProcAddr; layer_info->GetInstanceProcAddr = linkInfo->pfnNextGetInstanceProcAddr;
if (!nxvkGetInstanceProcAddr) { if (!layer_info->GetInstanceProcAddr) {
std::cerr << "lsfg-vk: next layer's vkGetInstanceProcAddr is null, " std::cerr << "lsfg-vk: next layer's vkGetInstanceProcAddr is null, "
"the previous layer does not follow spec\n"; "the previous layer does not follow spec\n";
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
@ -98,21 +96,17 @@ namespace {
// create instance // create instance
auto* vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>( auto* vkCreateInstance = reinterpret_cast<PFN_vkCreateInstance>(
nxvkGetInstanceProcAddr(nullptr, "vkCreateInstance")); layer_info->GetInstanceProcAddr(nullptr, "vkCreateInstance"));
if (!vkCreateInstance) { if (!vkCreateInstance) {
std::cerr << "lsfg-vk: failed to get next layer's vkCreateInstance, " std::cerr << "lsfg-vk: failed to get next layer's vkCreateInstance, "
"the previous layer does not follow spec\n"; "the previous layer does not follow spec\n";
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }
const auto& l = global;
if (!l || !l->layer.active()) // skip inactive
return vkCreateInstance(info, alloc, instance);
auto extensions = add_extensions( auto extensions = add_extensions(
info->ppEnabledExtensionNames, info->ppEnabledExtensionNames,
info->enabledExtensionCount, info->enabledExtensionCount,
l->layer.instanceExtensions()); layer_info->layer.instanceExtensions());
VkInstanceCreateInfo newInfo = *info; VkInstanceCreateInfo newInfo = *info;
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
@ -126,13 +120,15 @@ namespace {
if (res != VK_SUCCESS) if (res != VK_SUCCESS)
return res; return res;
l->instance = *instance; instance_info = new InstanceInfo{
l->fi = vk::initVulkanInstanceFuncs(l->instance, nxvkGetInstanceProcAddr); .handle = *instance,
.funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr),
.devices = {}
};
return VK_SUCCESS; return VK_SUCCESS;
} }
// create device // create device
PFN_vkGetDeviceProcAddr nxvkGetDeviceProcAddr{nullptr};
VkResult myvkCreateDevice( VkResult myvkCreateDevice(
VkPhysicalDevice physdev, VkPhysicalDevice physdev,
const VkDeviceCreateInfo* info, const VkDeviceCreateInfo* info,
@ -157,8 +153,7 @@ namespace {
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
} }
global->fi.GetDeviceProcAddr = linkInfo->pfnNextGetDeviceProcAddr; instance_info->funcs.GetDeviceProcAddr = linkInfo->pfnNextGetDeviceProcAddr;
nxvkGetDeviceProcAddr = linkInfo->pfnNextGetDeviceProcAddr;
if (!linkInfo->pfnNextGetDeviceProcAddr) { if (!linkInfo->pfnNextGetDeviceProcAddr) {
std::cerr << "lsfg-vk: next layer's vkGetDeviceProcAddr is null, " std::cerr << "lsfg-vk: next layer's vkGetDeviceProcAddr is null, "
"the previous layer does not follow spec\n"; "the previous layer does not follow spec\n";
@ -188,13 +183,13 @@ namespace {
auto extensions = add_extensions( auto extensions = add_extensions(
info->ppEnabledExtensionNames, info->ppEnabledExtensionNames,
info->enabledExtensionCount, info->enabledExtensionCount,
global->layer.deviceExtensions()); layer_info->layer.deviceExtensions());
VkDeviceCreateInfo newInfo = *info; VkDeviceCreateInfo newInfo = *info;
newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size()); newInfo.enabledExtensionCount = static_cast<uint32_t>(extensions.size());
newInfo.ppEnabledExtensionNames = extensions.data(); newInfo.ppEnabledExtensionNames = extensions.data();
auto res = global->fi.CreateDevice(physdev, &newInfo, alloc, device); auto res = instance_info->funcs.CreateDevice(physdev, &newInfo, alloc, device);
if (res == VK_ERROR_EXTENSION_NOT_PRESENT) if (res == VK_ERROR_EXTENSION_NOT_PRESENT)
std::cerr << "lsfg-vk: required Vulkan device extensions are not present. " std::cerr << "lsfg-vk: required Vulkan device extensions are not present. "
"Your GPU driver is not supported.\n"; "Your GPU driver is not supported.\n";
@ -204,18 +199,21 @@ namespace {
// create layer instance // create layer instance
try { try {
global->device2InstanceMap.emplace( instance_info->devices.emplace(
*device, *device,
layer::LayerInstance( new DeviceInfo{
global->layer, .handle = *device,
vk::Vulkan( .funcs = vk::initVulkanDeviceFuncs(instance_info->funcs, *device),
global->instance, *device, .layer = layer::Instance(
physdev, layer_info->layer,
global->fi, vk::Vulkan(
vk::initVulkanDeviceFuncs(global->fi, *device), instance_info->handle, *device, physdev,
setLoaderData instance_info->funcs,
vk::initVulkanDeviceFuncs(instance_info->funcs, *device),
true, setLoaderData
)
) )
) }
); );
} catch (const std::exception& e) { } catch (const std::exception& e) {
std::cerr << "lsfg-vk: something went wrong during lsfg-vk initialization:\n"; std::cerr << "lsfg-vk: something went wrong during lsfg-vk initialization:\n";
@ -225,21 +223,57 @@ namespace {
return VK_SUCCESS; return VK_SUCCESS;
} }
// populate function pointer override map // destroy device
void populateProcAddrMap() { void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* alloc) {
#define VKPTR(name) reinterpret_cast<PFN_vkVoidFunction>(name) // destroy layer instance
global->procAddrMap = { auto it = instance_info->devices.find(device);
{ "vkCreateInstance", VKPTR(myvkCreateInstance) }, if (it != instance_info->devices.end()) {
{ "vkCreateDevice", VKPTR(myvkCreateDevice) }, delete it->second;
}; instance_info->devices.erase(it);
#undef VKPTR }
// destroy device
auto vkDestroyDevice = reinterpret_cast<PFN_vkDestroyDevice>(
instance_info->funcs.GetDeviceProcAddr(device, "vkDestroyDevice"));
if (!vkDestroyDevice) {
std::cerr << "lsfg-vk: failed to get next layer's vkDestroyDevice, "
"the previous layer does not follow spec\n";
return;
}
vkDestroyDevice(device, alloc);
}
// destroy instance
void myvkDestroyInstance(VkInstance instance, const VkAllocationCallbacks* alloc) {
// destroy instance info
delete instance_info;
instance_info = nullptr;
// destroy instance
auto vkDestroyInstance = reinterpret_cast<PFN_vkDestroyInstance>(
layer_info->GetInstanceProcAddr(instance, "vkDestroyInstance"));
if (!vkDestroyInstance) {
std::cerr << "lsfg-vk: failed to get next layer's vkDestroyInstance, "
"the previous layer does not follow spec\n";
return;
}
vkDestroyInstance(instance, alloc);
// destroy layer info
// NOTE: there's no real way of unloading the layer without a deconstructor.
// multiple instances just aren't common enough to worry about it.
// NOTE2: it doesn't really matter anyways, because the myvkDestroyDevice code
// freezes the entire thing anyways.
delete layer_info;
layer_info = nullptr;
} }
// get optional function pointer override // get optional function pointer override
PFN_vkVoidFunction getProcAddr(const std::string& name) { PFN_vkVoidFunction getProcAddr(const std::string& name) {
if (!global) return nullptr; auto it = layer_info->map.find(name);
auto it = global->procAddrMap.find(name); if (it != layer_info->map.end())
if (it != global->procAddrMap.end())
return it->second; return it->second;
return nullptr; return nullptr;
} }
@ -254,8 +288,8 @@ namespace {
auto func = getProcAddr(pName); auto func = getProcAddr(pName);
if (func) return func; if (func) return func;
if (!nxvkGetInstanceProcAddr) return nullptr; if (!layer_info->GetInstanceProcAddr) return nullptr;
return nxvkGetInstanceProcAddr(instance, pName); return layer_info->GetInstanceProcAddr(instance, pName);
} }
// get device-level function pointers // get device-level function pointers
@ -265,8 +299,8 @@ namespace {
auto func = getProcAddr(pName); auto func = getProcAddr(pName);
if (func) return func; if (func) return func;
if (!nxvkGetDeviceProcAddr) return nullptr; if (!instance_info->funcs.GetDeviceProcAddr) return nullptr;
return nxvkGetDeviceProcAddr(device, pName); return instance_info->funcs.GetDeviceProcAddr(device, pName);
} }
} }
@ -280,6 +314,28 @@ VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVers
|| pVersionStruct->loaderLayerInterfaceVersion < 2) || pVersionStruct->loaderLayerInterfaceVersion < 2)
return VK_ERROR_INITIALIZATION_FAILED; return VK_ERROR_INITIALIZATION_FAILED;
// load the layer configuration
try {
layer::Layer layer{};
if (!layer.active()) // skip inactive
return VK_ERROR_INITIALIZATION_FAILED;
layer_info = new LayerInfo{
.layer = std::move(layer),
.map = {
#define VKPTR(name) reinterpret_cast<PFN_vkVoidFunction>(name)
{ "vkCreateInstance", VKPTR(myvkCreateInstance) },
{ "vkCreateDevice", VKPTR(myvkCreateDevice) },
{ "vkDestroyDevice", VKPTR(myvkDestroyDevice) },
{ "vkDestroyInstance", VKPTR(myvkDestroyInstance) }
#undef VKPTR
}
};
} catch (const std::exception& e) {
std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n";
std::cerr << "- " << e.what() << '\n';
}
// emplace function pointers/version // emplace function pointers/version
pVersionStruct->loaderLayerInterfaceVersion = 2; pVersionStruct->loaderLayerInterfaceVersion = 2;
pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr; pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr;