/* SPDX-License-Identifier: GPL-3.0-or-later */ #include "instance.hpp" #include "lsfg-vk-common/helpers/paths.hpp" #include "swapchain.hpp" #include "lsfg-vk-common/configuration/detection.hpp" #include "lsfg-vk-common/helpers/errors.hpp" #include "lsfg-vk-common/vulkan/vulkan.hpp" #include #include #include #include #include #include #include #include #include #include #include #include using namespace lsfgvk; using namespace lsfgvk::layer; namespace { /// helper function to add required extensions std::vector add_extensions(const char* const* existingExtensions, size_t count, const std::vector& requiredExtensions) { std::vector 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; } } Root::Root() { // find active profile const auto& profile = findProfile(this->config.get(), ls::identify()); if (!profile.has_value()) return; this->active_profile = profile->second; std::cerr << "lsfg-vk: using profile with name '" << this->active_profile->name << "' "; switch (profile->first) { case ls::IdentType::OVERRIDE: std::cerr << "(identified via override)\n"; break; case ls::IdentType::EXECUTABLE: std::cerr << "(identified via executable)\n"; break; case ls::IdentType::WINE_EXECUTABLE: std::cerr << "(identified via wine executable)\n"; break; case ls::IdentType::PROCESS_NAME: std::cerr << "(identified via process name)\n"; break; } } bool Root::update() { if (!this->config.update()) return false; const auto& profile = findProfile(this->config.get(), ls::identify()); if (profile.has_value()) this->active_profile = profile->second; else this->active_profile = std::nullopt; return true; } void Root::modifyInstanceCreateInfo(VkInstanceCreateInfo& createInfo, const std::function& finish) const { if (!this->active_profile.has_value()) return; 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(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); finish(); } void Root::modifyDeviceCreateInfo(VkDeviceCreateInfo& createInfo, const std::function& finish) const { if (!this->active_profile.has_value()) return; auto extensions = add_extensions( createInfo.ppEnabledExtensionNames, createInfo.enabledExtensionCount, { "VK_KHR_external_memory", "VK_KHR_external_memory_fd", "VK_EXT_external_memory_dma_buf", "VK_KHR_image_format_list", "VK_KHR_bind_memory2", "VK_KHR_maintenance1", "VK_KHR_get_memory_requirements2", "VK_KHR_sampler_ycbcr_conversion", "VK_EXT_image_drm_format_modifier", "VK_KHR_external_semaphore", "VK_KHR_external_semaphore_fd", "VK_KHR_timeline_semaphore" } ); createInfo.enabledExtensionCount = static_cast(extensions.size()); createInfo.ppEnabledExtensionNames = extensions.data(); finish(); } void Root::modifySwapchainCreateInfo(const vk::Vulkan& vk, VkSwapchainCreateInfoKHR& createInfo, const std::function& 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 ls::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.get().global(); setenv("DISABLE_LSFGVK", "1", 1); try { std::string dll{}; if (global.dll.has_value()) dll = *global.dll; else dll = ls::findShaderDll(); this->backend.emplace( [gpu = profile.gpu]( const std::string& deviceName, std::pair ids, const std::optional& pci ) { if (!gpu) return true; return (deviceName == *gpu) || (ids.first + ":" + ids.second == *gpu) || (pci && *pci == *gpu); }, dll, global.allow_fp16 ); } catch (const std::exception& e) { unsetenv("DISABLE_LSFGVK"); throw ls::error("failed to create backend instance", e); } unsetenv("DISABLE_LSFGVK"); } this->swapchains.emplace(swapchain, Swapchain(vk, this->backend.mut(), profile, info)); } void Root::removeSwapchainContext(VkSwapchainKHR swapchain) { this->swapchains.erase(swapchain); }