#include "context.hpp" #include "config/config.hpp" #include "common/exception.hpp" #include "extract/extract.hpp" #include "extract/trans.hpp" #include "utils/utils.hpp" #include "hooks.hpp" #include "layer.hpp" #include #include #include #include #include #include #include #include #include #include #include #include #include #include LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain, VkExtent2D extent, const std::vector& swapchainImages) : swapchain(swapchain), swapchainImages(swapchainImages), extent(extent) { // get updated configuration auto& conf = Config::activeConf; if (!conf.config_file.empty() && ( !std::filesystem::exists(conf.config_file) || conf.timestamp != std::filesystem::last_write_time(conf.config_file) )) { std::cerr << "lsfg-vk: Rereading configuration, as it is no longer valid.\n"; std::this_thread::sleep_for(std::chrono::milliseconds(100)); // reread configuration const std::string file = Utils::getConfigFile(); const auto name = Utils::getProcessName(); try { Config::updateConfig(file); conf = Config::getConfig(name); } catch (const std::exception& e) { std::cerr << "lsfg-vk: Failed to update configuration, continuing using old:\n"; std::cerr << "- " << e.what() << '\n'; } LSFG_3_1P::finalize(); LSFG_3_1::finalize(); // print config std::cerr << "lsfg-vk: Reloaded configuration for " << name.second << ":\n"; if (!conf.dll.empty()) std::cerr << " Using DLL from: " << conf.dll << '\n'; std::cerr << " Multiplier: " << conf.multiplier << '\n'; std::cerr << " Flow Scale: " << conf.flowScale << '\n'; std::cerr << " Performance Mode: " << (conf.performance ? "Enabled" : "Disabled") << '\n'; std::cerr << " HDR Mode: " << (conf.hdr ? "Enabled" : "Disabled") << '\n'; if (conf.e_present != 2) std::cerr << " ! Present Mode: " << conf.e_present << '\n'; if (conf.multiplier <= 1) return; } // we could take the format from the swapchain, // but honestly this is safer. const VkFormat format = conf.hdr ? VK_FORMAT_R8G8B8A8_UNORM : VK_FORMAT_R16G16B16A16_SFLOAT; // prepare textures for lsfg std::array fds{}; this->frame_0 = Mini::Image(info.device, info.physicalDevice, extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &fds.at(0)); this->frame_1 = Mini::Image(info.device, info.physicalDevice, extent, format, VK_IMAGE_USAGE_TRANSFER_DST_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &fds.at(1)); std::vector outFds(conf.multiplier - 1); for (size_t i = 0; i < (conf.multiplier - 1); ++i) this->out_n.emplace_back(info.device, info.physicalDevice, extent, format, VK_IMAGE_USAGE_TRANSFER_SRC_BIT, VK_IMAGE_ASPECT_COLOR_BIT, &outFds.at(i)); // initialize lsfg auto* lsfgInitialize = LSFG_3_1::initialize; auto* lsfgCreateContext = LSFG_3_1::createContext; auto* lsfgDeleteContext = LSFG_3_1::deleteContext; if (conf.performance) { lsfgInitialize = LSFG_3_1P::initialize; lsfgCreateContext = LSFG_3_1P::createContext; lsfgDeleteContext = LSFG_3_1P::deleteContext; } setenv("DISABLE_LSFG", "1", 1); // NOLINT lsfgInitialize( Utils::getDeviceUUID(info.physicalDevice), conf.hdr, 1.0F / conf.flowScale, conf.multiplier - 1, [](const std::string& name) { auto dxbc = Extract::getShader(name); auto spirv = Extract::translateShader(dxbc); return spirv; } ); this->lsfgCtxId = std::shared_ptr( new int32_t(lsfgCreateContext(fds.at(0), fds.at(1), outFds, extent, format)), [lsfgDeleteContext = lsfgDeleteContext](const int32_t* id) { lsfgDeleteContext(*id); } ); unsetenv("DISABLE_LSFG"); // NOLINT // prepare render passes this->cmdPool = Mini::CommandPool(info.device, info.queue.first); for (size_t i = 0; i < 8; i++) { auto& pass = this->passInfos.at(i); pass.renderSemaphores.resize(conf.multiplier - 1); pass.acquireSemaphores.resize(conf.multiplier - 1); pass.postCopyBufs.resize(conf.multiplier - 1); pass.postCopySemaphores.resize(conf.multiplier - 1); pass.prevPostCopySemaphores.resize(conf.multiplier - 1); } } VkResult LsContext::present(const Hooks::DeviceInfo& info, const void* pNext, VkQueue queue, const std::vector& gameRenderSemaphores, uint32_t presentIdx) { const auto& conf = Config::activeConf; auto& pass = this->passInfos.at(this->frameIdx % 8); // 1. copy swapchain image to frame_0/frame_1 int preCopySemaphoreFd{}; pass.preCopySemaphores.at(0) = Mini::Semaphore(info.device, &preCopySemaphoreFd); pass.preCopySemaphores.at(1) = Mini::Semaphore(info.device); pass.preCopyBuf = Mini::CommandBuffer(info.device, this->cmdPool); pass.preCopyBuf.begin(); Utils::copyImage(pass.preCopyBuf.handle(), this->swapchainImages.at(presentIdx), this->frameIdx % 2 == 0 ? this->frame_0.handle() : this->frame_1.handle(), this->extent.width, this->extent.height, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, true, false); pass.preCopyBuf.end(); std::vector gameRenderSemaphores2 = gameRenderSemaphores; if (this->frameIdx > 0) gameRenderSemaphores2.emplace_back(this->passInfos.at((this->frameIdx - 1) % 8) .preCopySemaphores.at(1).handle()); pass.preCopyBuf.submit(info.queue.second, gameRenderSemaphores2, { pass.preCopySemaphores.at(0).handle(), pass.preCopySemaphores.at(1).handle() }); // 2. render intermediary frames std::vector renderSemaphoreFds(conf.multiplier - 1); for (size_t i = 0; i < (conf.multiplier - 1); ++i) pass.renderSemaphores.at(i) = Mini::Semaphore(info.device, &renderSemaphoreFds.at(i)); if (conf.performance) LSFG_3_1P::presentContext(*this->lsfgCtxId, preCopySemaphoreFd, renderSemaphoreFds); else LSFG_3_1::presentContext(*this->lsfgCtxId, preCopySemaphoreFd, renderSemaphoreFds); for (size_t i = 0; i < (conf.multiplier - 1); i++) { // 3. acquire next swapchain image pass.acquireSemaphores.at(i) = Mini::Semaphore(info.device); uint32_t imageIdx{}; auto res = Layer::ovkAcquireNextImageKHR(info.device, this->swapchain, UINT64_MAX, pass.acquireSemaphores.at(i).handle(), VK_NULL_HANDLE, &imageIdx); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to acquire next swapchain image"); // 4. copy output image to swapchain image pass.postCopySemaphores.at(i) = Mini::Semaphore(info.device); pass.prevPostCopySemaphores.at(i) = Mini::Semaphore(info.device); pass.postCopyBufs.at(i) = Mini::CommandBuffer(info.device, this->cmdPool); pass.postCopyBufs.at(i).begin(); Utils::copyImage(pass.postCopyBufs.at(i).handle(), this->out_n.at(i).handle(), this->swapchainImages.at(imageIdx), this->extent.width, this->extent.height, VK_PIPELINE_STAGE_TOP_OF_PIPE_BIT, VK_PIPELINE_STAGE_BOTTOM_OF_PIPE_BIT, false, true); pass.postCopyBufs.at(i).end(); pass.postCopyBufs.at(i).submit(info.queue.second, { pass.acquireSemaphores.at(i).handle(), pass.renderSemaphores.at(i).handle() }, { pass.postCopySemaphores.at(i).handle(), pass.prevPostCopySemaphores.at(i).handle() }); // 5. present swapchain image std::vector waitSemaphores{ pass.postCopySemaphores.at(i).handle() }; if (i != 0) waitSemaphores.emplace_back(pass.prevPostCopySemaphores.at(i - 1).handle()); const VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .pNext = i == 0 ? pNext : nullptr, // only set on first present .waitSemaphoreCount = static_cast(waitSemaphores.size()), .pWaitSemaphores = waitSemaphores.data(), .swapchainCount = 1, .pSwapchains = &this->swapchain, .pImageIndices = &imageIdx, }; res = Layer::ovkQueuePresentKHR(queue, &presentInfo); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to present swapchain image"); } // 6. present actual next frame VkSemaphore lastPrevPostCopySemaphore = pass.prevPostCopySemaphores.at(conf.multiplier - 1 - 1).handle(); const VkPresentInfoKHR presentInfo{ .sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR, .waitSemaphoreCount = 1, .pWaitSemaphores = &lastPrevPostCopySemaphore, .swapchainCount = 1, .pSwapchains = &this->swapchain, .pImageIndices = &presentIdx, }; auto res = Layer::ovkQueuePresentKHR(queue, &presentInfo); if (res != VK_SUCCESS && res != VK_SUBOPTIMAL_KHR) throw LSFG::vulkan_error(res, "Failed to present swapchain image"); this->frameIdx++; return res; }