From a12126cc7e8c8be13f2a731a15128e52d4a201df Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 8 Mar 2026 20:22:11 -0700 Subject: [PATCH] Persist upscaling mode and refine FSR2 jitter behavior --- include/ui/game_screen.hpp | 1 + src/rendering/renderer.cpp | 17 +++++------------ src/ui/game_screen.cpp | 23 +++++++++++++++-------- 3 files changed, 21 insertions(+), 20 deletions(-) diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 3bb99628..82aadc4d 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -117,6 +117,7 @@ private: bool pendingPOM = true; // on by default int pendingPOMQuality = 1; // 0=Low(16), 1=Medium(32), 2=High(64) bool pendingFSR = false; + int pendingUpscalingMode = 0; // 0=Off, 1=FSR1, 2=FSR2 int pendingFSRQuality = 0; // 0=UltraQuality, 1=Quality, 2=Balanced, 3=Performance float pendingFSRSharpness = 0.5f; bool fsrSettingsApplied_ = false; diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 585434bd..ecec244e 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -1205,9 +1205,7 @@ void Renderer::endFrame() { renderFSR2Sharpen(); // Maintain frame bookkeeping - fsr2_.prevViewProjection = fsr2_.useAmdBackend - ? camera->getUnjitteredViewProjectionMatrix() - : camera->getViewProjectionMatrix(); + fsr2_.prevViewProjection = camera->getViewProjectionMatrix(); fsr2_.prevJitter = camera->getJitter(); camera->clearJitter(); if (fsr2_.useAmdBackend) { @@ -3826,9 +3824,7 @@ bool Renderer::initFSR2Resources() { fsr2_.amdScratchBufferSize = 0; } else { FfxFsr2ContextDescription ctxDesc{}; - // AMD path uses unjittered reprojection matrices for motion vectors. - // Keep jitter cancellation off to avoid double compensation jitter. - ctxDesc.flags = FFX_FSR2_ENABLE_AUTO_EXPOSURE; + ctxDesc.flags = FFX_FSR2_ENABLE_AUTO_EXPOSURE | FFX_FSR2_ENABLE_MOTION_VECTORS_JITTER_CANCELLATION; ctxDesc.maxRenderSize.width = fsr2_.internalWidth; ctxDesc.maxRenderSize.height = fsr2_.internalHeight; ctxDesc.displaySize.width = swapExtent.width; @@ -4187,17 +4183,14 @@ void Renderer::dispatchMotionVectors() { vkCmdBindDescriptorSets(currentCmd, VK_PIPELINE_BIND_POINT_COMPUTE, fsr2_.motionVecPipelineLayout, 0, 1, &fsr2_.motionVecDescSet, 0, nullptr); - // Reprojection matrices: - // AMD path uses unjittered matrices (FSR handles jitter via desc.jitterOffset). - // Internal fallback keeps existing jittered behavior. + // Reprojection with jittered matrices: + // reconstruct world position from current depth, then project into previous clip. struct { glm::mat4 prevViewProjection; glm::mat4 invCurrentViewProj; } pc; - glm::mat4 currentVP = fsr2_.useAmdBackend - ? camera->getUnjitteredViewProjectionMatrix() - : camera->getViewProjectionMatrix(); + glm::mat4 currentVP = camera->getViewProjectionMatrix(); pc.prevViewProjection = fsr2_.prevViewProjection; pc.invCurrentViewProj = glm::inverse(currentVP); diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 830bd9ec..c1045507 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -317,18 +317,17 @@ void GameScreen::render(game::GameHandler& gameHandler) { } } - // Apply saved FSR setting once when renderer is available - if (!fsrSettingsApplied_ && pendingFSR) { + // Apply saved upscaling setting once when renderer is available + if (!fsrSettingsApplied_) { auto* renderer = core::Application::getInstance().getRenderer(); if (renderer) { static const float fsrScales[] = { 0.77f, 0.67f, 0.59f, 0.50f }; renderer->setFSRQuality(fsrScales[pendingFSRQuality]); renderer->setFSRSharpness(pendingFSRSharpness); - renderer->setFSREnabled(true); + renderer->setFSREnabled(pendingUpscalingMode == 1); + renderer->setFSR2Enabled(pendingUpscalingMode == 2); fsrSettingsApplied_ = true; } - } else { - fsrSettingsApplied_ = true; } // Apply auto-loot setting to GameHandler every frame (cheap bool sync) @@ -6300,9 +6299,9 @@ void GameScreen::renderSettingsWindow() { { // FSR mode selection: Off, FSR 1.0 (Spatial), FSR 2.2 (Temporal) const char* fsrModeLabels[] = { "Off", "FSR 1.0 (Spatial)", "FSR 2.2 (Temporal)" }; - int fsrMode = pendingFSR ? 1 : 0; - if (renderer && renderer->isFSR2Enabled()) fsrMode = 2; + int fsrMode = pendingUpscalingMode; if (ImGui::Combo("Upscaling", &fsrMode, fsrModeLabels, 3)) { + pendingUpscalingMode = fsrMode; pendingFSR = (fsrMode == 1); if (renderer) { renderer->setFSREnabled(fsrMode == 1); @@ -7441,6 +7440,7 @@ void GameScreen::saveSettings() { out << "normal_map_strength=" << pendingNormalMapStrength << "\n"; out << "pom=" << (pendingPOM ? 1 : 0) << "\n"; out << "pom_quality=" << pendingPOMQuality << "\n"; + out << "upscaling_mode=" << pendingUpscalingMode << "\n"; out << "fsr=" << (pendingFSR ? 1 : 0) << "\n"; out << "fsr_quality=" << pendingFSRQuality << "\n"; out << "fsr_sharpness=" << pendingFSRSharpness << "\n"; @@ -7530,7 +7530,14 @@ void GameScreen::loadSettings() { else if (key == "normal_map_strength") pendingNormalMapStrength = std::clamp(std::stof(val), 0.0f, 2.0f); else if (key == "pom") pendingPOM = (std::stoi(val) != 0); else if (key == "pom_quality") pendingPOMQuality = std::clamp(std::stoi(val), 0, 2); - else if (key == "fsr") pendingFSR = (std::stoi(val) != 0); + else if (key == "upscaling_mode") { + pendingUpscalingMode = std::clamp(std::stoi(val), 0, 2); + pendingFSR = (pendingUpscalingMode == 1); + } else if (key == "fsr") { + pendingFSR = (std::stoi(val) != 0); + // Backward compatibility: old configs only had fsr=0/1. + if (pendingUpscalingMode == 0 && pendingFSR) pendingUpscalingMode = 1; + } else if (key == "fsr_quality") pendingFSRQuality = std::clamp(std::stoi(val), 0, 3); else if (key == "fsr_sharpness") pendingFSRSharpness = std::clamp(std::stof(val), 0.0f, 2.0f); // Controls