lsfg-vk/src/context.cpp

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;
}