mirror of
https://github.com/PancakeTAS/lsfg-vk.git
synced 2025-10-30 07:01:10 +00:00
235 lines
9.4 KiB
C++
235 lines
9.4 KiB
C++
#include "context.hpp"
|
|
#include "config/config.hpp"
|
|
#include "common/exception.hpp"
|
|
#include "extract/extract.hpp"
|
|
#include "utils/utils.hpp"
|
|
#include "hooks.hpp"
|
|
#include "layer.hpp"
|
|
|
|
#include <vulkan/vulkan_core.h>
|
|
#include <lsfg_3_1.hpp>
|
|
#include <lsfg_3_1p.hpp>
|
|
|
|
#include <filesystem>
|
|
#include <exception>
|
|
#include <iostream>
|
|
#include <cstdint>
|
|
#include <cstdlib>
|
|
#include <vector>
|
|
#include <chrono>
|
|
#include <memory>
|
|
#include <string>
|
|
#include <thread>
|
|
#include <array>
|
|
|
|
LsContext::LsContext(const Hooks::DeviceInfo& info, VkSwapchainKHR swapchain,
|
|
VkExtent2D extent, const std::vector<VkImage>& 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.first << ":\n";
|
|
if (!conf.dll.empty()) std::cerr << " Using DLL from: " << conf.dll << '\n';
|
|
if (conf.no_fp16) std::cerr << " FP16 Acceleration: Force-disabled\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<int, 2> 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<int> 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,
|
|
conf.no_fp16,
|
|
Extract::getShader
|
|
);
|
|
|
|
this->lsfgCtxId = std::shared_ptr<int32_t>(
|
|
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<VkSemaphore>& 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<VkSemaphore> 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<int> 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<VkSemaphore> 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<uint32_t>(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;
|
|
}
|