/* SPDX-License-Identifier: GPL-3.0-or-later */ #include "instance.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/helpers/pointers.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include "swapchain.hpp" #include #include #include #include #include #include #include #include #include #include using namespace lsfgvk::layer; namespace { // global layer info initialized at layer negotiation struct LayerInfo { std::unordered_map map; //!< function pointer override map PFN_vkGetInstanceProcAddr GetInstanceProcAddr; Root root; }* layer_info; // instance-wide info initialized at instance creation struct InstanceInfo { VkInstance handle; vk::VulkanInstanceFuncs funcs; std::unordered_map devices; std::unordered_map> swapchains; std::unordered_map swapchainInfos; }* instance_info; // create instance VkResult myvkCreateInstance( const VkInstanceCreateInfo* info, const VkAllocationCallbacks* alloc, VkInstance* instance) { // apply layer chaining auto* layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_INSTANCE_CREATE_INFO || layerInfo->function != VK_LAYER_LINK_INFO)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { std::cerr << "lsfg-vk: no layer info found in pNext chain, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* linkInfo = layerInfo->u.pLayerInfo; if (!linkInfo) { std::cerr << "lsfg-vk: link info is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } layer_info->GetInstanceProcAddr = linkInfo->pfnNextGetInstanceProcAddr; if (!layer_info->GetInstanceProcAddr) { std::cerr << "lsfg-vk: next layer's vkGetInstanceProcAddr is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } layerInfo->u.pLayerInfo = linkInfo->pNext; // advance for next layer // create instance auto* vkCreateInstance = reinterpret_cast( layer_info->GetInstanceProcAddr(nullptr, "vkCreateInstance")); if (!vkCreateInstance) { std::cerr << "lsfg-vk: failed to get next layer's vkCreateInstance, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } 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"); } ); instance_info = new InstanceInfo{ .handle = *instance, .funcs = vk::initVulkanInstanceFuncs(*instance, layer_info->GetInstanceProcAddr, true), .devices = {} }; return VK_SUCCESS; } 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(); } } // create device VkResult myvkCreateDevice( VkPhysicalDevice physdev, const VkDeviceCreateInfo* info, const VkAllocationCallbacks* alloc, VkDevice* device) { // apply layer chaining auto* layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerInfo->function != VK_LAYER_LINK_INFO)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { std::cerr << "lsfg-vk: no layer info found in pNext chain, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* linkInfo = layerInfo->u.pLayerInfo; if (!linkInfo) { std::cerr << "lsfg-vk: link info is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } instance_info->funcs.GetDeviceProcAddr = linkInfo->pfnNextGetDeviceProcAddr; if (!linkInfo->pfnNextGetDeviceProcAddr) { std::cerr << "lsfg-vk: next layer's vkGetDeviceProcAddr is null, " "the previous layer does not follow spec\n"; return VK_ERROR_INITIALIZATION_FAILED; } layerInfo->u.pLayerInfo = linkInfo->pNext; // advance for next layer // fetch device loader functions layerInfo = reinterpret_cast(const_cast(info->pNext)); while (layerInfo && (layerInfo->sType != VK_STRUCTURE_TYPE_LOADER_DEVICE_CREATE_INFO || layerInfo->function != VK_LOADER_DATA_CALLBACK)) { layerInfo = reinterpret_cast(const_cast(layerInfo->pNext)); } if (!layerInfo) { std::cerr << "lsfg-vk: no layer loader data found in pNext chain.\n"; return VK_ERROR_INITIALIZATION_FAILED; } auto* setLoaderData = layerInfo->u.pfnSetDeviceLoaderData; if (!setLoaderData) { std::cerr << "lsfg-vk: instance loader data function is null.\n"; return VK_ERROR_INITIALIZATION_FAILED; } // create device 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 { instance_info->devices.emplace( *device, vk::Vulkan( instance_info->handle, *device, physdev, instance_info->funcs, vk::initVulkanDeviceFuncs(instance_info->funcs, *device, true), true, setLoaderData ) ); } catch (const std::exception& e) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk initialization:\n"; std::cerr << "- " << e.what() << '\n'; } return VK_SUCCESS; } // destroy device void myvkDestroyDevice(VkDevice device, const VkAllocationCallbacks* alloc) { // destroy layer instance auto it = instance_info->devices.find(device); if (it != instance_info->devices.end()) instance_info->devices.erase(it); // destroy device auto vkDestroyDevice = reinterpret_cast( 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( 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 PFN_vkVoidFunction getProcAddr(const std::string& name) { auto it = layer_info->map.find(name); if (it != layer_info->map.end()) return it->second; return nullptr; } // get instance-level function pointers PFN_vkVoidFunction myvkGetInstanceProcAddr(VkInstance instance, const char* name) { if (!name) return nullptr; if (std::string(name) == "vkCreateInstance") // pre-instance function return reinterpret_cast(myvkCreateInstance); auto func = getProcAddr(name); if (func) return func; if (!layer_info->GetInstanceProcAddr) return nullptr; return layer_info->GetInstanceProcAddr(instance, name); } // get device-level function pointers PFN_vkVoidFunction myvkGetDeviceProcAddr(VkDevice device, const char* name) { if (!name) return nullptr; auto func = getProcAddr(name); if (func) return func; if (!instance_info->funcs.GetDeviceProcAddr) return nullptr; 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) { const auto& info_mapping = instance_info->swapchainInfos.find(info->oldSwapchain); if (info_mapping != instance_info->swapchainInfos.end()) instance_info->swapchainInfos.erase(info_mapping); const auto& mapping = instance_info->swapchains.find(info->oldSwapchain); if (mapping != instance_info->swapchains.end()) instance_info->swapchains.erase(mapping); 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 swapchainImages(imageCount); res = it->second.df().GetSwapchainImagesKHR(device, *swapchain, &imageCount, swapchainImages.data()); if (res != VK_SUCCESS) throw ls::vulkan_error(res, "vkGetSwapchainImagesKHR() failed"); auto& info = instance_info->swapchainInfos.emplace(*swapchain, SwapchainInfo { .images = std::move(swapchainImages), .format = newInfo.imageFormat, .colorSpace = newInfo.imageColorSpace, .extent = newInfo.imageExtent, .presentMode = newInfo.presentMode }).first->second; // create lsfg-vk swapchain layer_info->root.createSwapchainContext(it->second, *swapchain, info); instance_info->swapchains.emplace(*swapchain, ls::R(it->second)); 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; } } VkResult myvkQueuePresentKHR(VkQueue queue, const VkPresentInfoKHR* info) { #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wunknown-warning-option" #pragma clang diagnostic ignored "-Wunsafe-buffer-usage" VkResult result = VK_SUCCESS; // ensure layer config is up to date bool reload{}; try { reload = layer_info->root.update(); } catch (const std::exception&) { reload = false; // ignore parse errors } if (reload) { try { for (const auto& [swapchain, vk] : instance_info->swapchains) { auto& info = instance_info->swapchainInfos.at(swapchain); layer_info->root.removeSwapchainContext(swapchain); layer_info->root.createSwapchainContext(vk, swapchain, info); } std::cerr << "lsfg-vk: updated lsfg-vk configuration\n"; } catch (const std::exception& e) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk configuration update:\n"; std::cerr << "- " << e.what() << '\n'; } } // present each swapchain for (size_t i = 0; i < info->swapchainCount; i++) { const auto& swapchain = info->pSwapchains[i]; // NOLINT (array index) const auto& it = instance_info->swapchains.find(swapchain); if (it == instance_info->swapchains.end()) return VK_ERROR_INITIALIZATION_FAILED; try { std::vector waitSemaphores; waitSemaphores.reserve(info->waitSemaphoreCount); for (size_t j = 0; j < info->waitSemaphoreCount; j++) waitSemaphores.push_back(info->pWaitSemaphores[j]); // NOLINT (array index) auto& context = layer_info->root.getSwapchainContext(swapchain); result = context.present(it->second, queue, swapchain, const_cast(info->pNext), info->pImageIndices[i], // NOLINT (array index) { waitSemaphores.begin(), waitSemaphores.end() } ); } catch (const ls::vulkan_error& e) { if (e.error() != VK_ERROR_OUT_OF_DATE_KHR) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain presentation:\n"; std::cerr << "- " << e.what() << '\n'; } // silently swallow out-of-date errors result = e.error(); } catch (const std::exception& e) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk swapchain presentation:\n"; std::cerr << "- " << e.what() << '\n'; result = VK_ERROR_UNKNOWN; } if (result != VK_SUCCESS && info->pResults) info->pResults[i] = result; // NOLINT (array index) } return result; #pragma clang diagnostic pop } void myvkDestroySwapchainKHR( VkDevice device, VkSwapchainKHR swapchain, const VkAllocationCallbacks* alloc) { const auto& it = instance_info->devices.find(device); if (it == instance_info->devices.end()) return; const auto& info_mapping = instance_info->swapchainInfos.find(swapchain); if (info_mapping != instance_info->swapchainInfos.end()) instance_info->swapchainInfos.erase(info_mapping); const auto& mapping = instance_info->swapchains.find(swapchain); if (mapping != instance_info->swapchains.end()) instance_info->swapchains.erase(mapping); layer_info->root.removeSwapchainContext(swapchain); // destroy swapchain it->second.df().DestroySwapchainKHR(device, swapchain, alloc); } } /// Vulkan layer entrypoint __attribute__((visibility("default"))) VkResult vkNegotiateLoaderLayerInterfaceVersion(VkNegotiateLayerInterface* pVersionStruct) { // ensure loader compatibility if (!pVersionStruct || pVersionStruct->sType != LAYER_NEGOTIATE_INTERFACE_STRUCT || pVersionStruct->loaderLayerInterfaceVersion < 2) return VK_ERROR_INITIALIZATION_FAILED; // load the layer configuration try { layer_info = new LayerInfo { .map = { #define VKPTR(name) reinterpret_cast(name) { "vkCreateInstance", VKPTR(myvkCreateInstance) }, { "vkCreateDevice", VKPTR(myvkCreateDevice) }, { "vkDestroyDevice", VKPTR(myvkDestroyDevice) }, { "vkDestroyInstance", VKPTR(myvkDestroyInstance) }, { "vkCreateSwapchainKHR", VKPTR(myvkCreateSwapchainKHR) }, { "vkQueuePresentKHR", VKPTR(myvkQueuePresentKHR) }, { "vkDestroySwapchainKHR", VKPTR(myvkDestroySwapchainKHR) } #undef VKPTR }, .root = Root() }; if (!layer_info->root.active()) { // skip inactive delete layer_info; layer_info = nullptr; return VK_ERROR_INITIALIZATION_FAILED; } } catch (const std::exception& e) { std::cerr << "lsfg-vk: something went wrong during lsfg-vk layer initialization:\n"; std::cerr << "- " << e.what() << '\n'; return VK_ERROR_INITIALIZATION_FAILED; } // emplace function pointers/version pVersionStruct->loaderLayerInterfaceVersion = 2; pVersionStruct->pfnGetPhysicalDeviceProcAddr = nullptr; pVersionStruct->pfnGetDeviceProcAddr = myvkGetDeviceProcAddr; pVersionStruct->pfnGetInstanceProcAddr = myvkGetInstanceProcAddr; return VK_SUCCESS; }