diff --git a/include/rendering/renderer.hpp b/include/rendering/renderer.hpp index cc17efc4..0122134a 100644 --- a/include/rendering/renderer.hpp +++ b/include/rendering/renderer.hpp @@ -274,6 +274,10 @@ public: float getFSRSharpness() const { return fsr_.sharpness; } void setFSR2Enabled(bool enabled); bool isFSR2Enabled() const { return fsr2_.enabled; } + void setFSR2DebugTuning(float jitterSign, float motionVecScaleX, float motionVecScaleY); + float getFSR2JitterSign() const { return fsr2_.jitterSign; } + float getFSR2MotionVecScaleX() const { return fsr2_.motionVecScaleX; } + float getFSR2MotionVecScaleY() const { return fsr2_.motionVecScaleY; } #if WOWEE_HAS_AMD_FSR2 bool isAmdFsr2SdkAvailable() const { return true; } #else @@ -425,6 +429,9 @@ private: uint32_t frameIndex = 0; bool needsHistoryReset = true; bool useAmdBackend = false; + float jitterSign = -1.0f; + float motionVecScaleX = 1.0f; + float motionVecScaleY = 1.0f; #if WOWEE_HAS_AMD_FSR2 FfxFsr2Context amdContext{}; FfxFsr2Interface amdInterface{}; diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 82aadc4d..28a24b9f 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -120,6 +120,9 @@ private: int pendingUpscalingMode = 0; // 0=Off, 1=FSR1, 2=FSR2 int pendingFSRQuality = 0; // 0=UltraQuality, 1=Quality, 2=Balanced, 3=Performance float pendingFSRSharpness = 0.5f; + float pendingFSR2JitterSign = -1.0f; + float pendingFSR2MotionVecScaleX = 1.0f; + float pendingFSR2MotionVecScaleY = 1.0f; bool fsrSettingsApplied_ = false; // UI element transparency (0.0 = fully transparent, 1.0 = fully opaque) diff --git a/src/rendering/performance_hud.cpp b/src/rendering/performance_hud.cpp index 86dc2f21..2e2f19f2 100644 --- a/src/rendering/performance_hud.cpp +++ b/src/rendering/performance_hud.cpp @@ -200,6 +200,13 @@ void PerformanceHUD::render(const Renderer* renderer, const Camera* camera) { ImGui::Text(" %ux%u -> %ux%u (%.0f%%)", iw, ih, ext.width, ext.height, sf * 100.0f); } } + if (renderer->isFSR2Enabled()) { + ImGui::TextColored(ImVec4(0.4f, 0.9f, 1.0f, 1.0f), "FSR 2.2: ON"); + ImGui::Text(" JitterSign=%.2f MVScale=(%.2f, %.2f)", + renderer->getFSR2JitterSign(), + renderer->getFSR2MotionVecScaleX(), + renderer->getFSR2MotionVecScaleY()); + } ImGui::Spacing(); } diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index bd22c400..23107232 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -4323,13 +4323,11 @@ void Renderer::dispatchAmdFsr2() { L"FSR2_Output", FFX_RESOURCE_STATE_UNORDERED_ACCESS); // Camera jitter is stored as NDC projection offsets; convert to render-pixel offsets. - // AMD jitter convention is opposite our projection offset sign in Vulkan space. - const float jitterSign = static_cast(envIntOrDefault("WOWEE_FSR2_JITTER_SIGN", -1)); glm::vec2 jitterNdc = camera->getJitter(); - desc.jitterOffset.x = jitterSign * jitterNdc.x * 0.5f * static_cast(fsr2_.internalWidth); - desc.jitterOffset.y = jitterSign * jitterNdc.y * 0.5f * static_cast(fsr2_.internalHeight); - desc.motionVectorScale.x = static_cast(fsr2_.internalWidth); - desc.motionVectorScale.y = static_cast(fsr2_.internalHeight); + desc.jitterOffset.x = fsr2_.jitterSign * jitterNdc.x * 0.5f * static_cast(fsr2_.internalWidth); + desc.jitterOffset.y = fsr2_.jitterSign * jitterNdc.y * 0.5f * static_cast(fsr2_.internalHeight); + desc.motionVectorScale.x = static_cast(fsr2_.internalWidth) * fsr2_.motionVecScaleX; + desc.motionVectorScale.y = static_cast(fsr2_.internalHeight) * fsr2_.motionVecScaleY; desc.renderSize.width = fsr2_.internalWidth; desc.renderSize.height = fsr2_.internalHeight; desc.enableSharpening = false; // Keep existing RCAS post pass. @@ -4415,6 +4413,12 @@ void Renderer::setFSR2Enabled(bool enabled) { } } +void Renderer::setFSR2DebugTuning(float jitterSign, float motionVecScaleX, float motionVecScaleY) { + fsr2_.jitterSign = glm::clamp(jitterSign, -2.0f, 2.0f); + fsr2_.motionVecScaleX = glm::clamp(motionVecScaleX, -2.0f, 2.0f); + fsr2_.motionVecScaleY = glm::clamp(motionVecScaleY, -2.0f, 2.0f); +} + // ========================= End FSR 2.2 ========================= void Renderer::renderWorld(game::World* world, game::GameHandler* gameHandler) { diff --git a/src/ui/game_screen.cpp b/src/ui/game_screen.cpp index 0325bb34..a8a4062b 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -324,6 +324,7 @@ void GameScreen::render(game::GameHandler& gameHandler) { static const float fsrScales[] = { 0.77f, 0.67f, 0.59f, 0.50f }; renderer->setFSRQuality(fsrScales[pendingFSRQuality]); renderer->setFSRSharpness(pendingFSRSharpness); + renderer->setFSR2DebugTuning(pendingFSR2JitterSign, pendingFSR2MotionVecScaleX, pendingFSR2MotionVecScaleY); // Safety fallback: persisted FSR2 can still hang on some systems during startup. // Require explicit opt-in for startup FSR2; otherwise fall back to FSR1. const bool allowStartupFsr2 = (std::getenv("WOWEE_ALLOW_STARTUP_FSR2") != nullptr); @@ -6345,6 +6346,37 @@ void GameScreen::renderSettingsWindow() { if (renderer) renderer->setFSRSharpness(pendingFSRSharpness); saveSettings(); } + if (fsrMode == 2) { + ImGui::SeparatorText("FSR2 Tuning"); + if (ImGui::SliderFloat("Jitter Sign", &pendingFSR2JitterSign, -2.0f, 2.0f, "%.2f")) { + if (renderer) { + renderer->setFSR2DebugTuning( + pendingFSR2JitterSign, + pendingFSR2MotionVecScaleX, + pendingFSR2MotionVecScaleY); + } + saveSettings(); + } + if (ImGui::SliderFloat("MV Scale X", &pendingFSR2MotionVecScaleX, -2.0f, 2.0f, "%.2f")) { + if (renderer) { + renderer->setFSR2DebugTuning( + pendingFSR2JitterSign, + pendingFSR2MotionVecScaleX, + pendingFSR2MotionVecScaleY); + } + saveSettings(); + } + if (ImGui::SliderFloat("MV Scale Y", &pendingFSR2MotionVecScaleY, -2.0f, 2.0f, "%.2f")) { + if (renderer) { + renderer->setFSR2DebugTuning( + pendingFSR2JitterSign, + pendingFSR2MotionVecScaleX, + pendingFSR2MotionVecScaleY); + } + saveSettings(); + } + ImGui::TextDisabled("Tip: default is jitter=-1, mv=(1,1)."); + } } } if (ImGui::SliderInt("Ground Clutter Density", &pendingGroundClutterDensity, 0, 150, "%d%%")) { @@ -7465,6 +7497,9 @@ void GameScreen::saveSettings() { out << "fsr=" << (pendingFSR ? 1 : 0) << "\n"; out << "fsr_quality=" << pendingFSRQuality << "\n"; out << "fsr_sharpness=" << pendingFSRSharpness << "\n"; + out << "fsr2_jitter_sign=" << pendingFSR2JitterSign << "\n"; + out << "fsr2_mv_scale_x=" << pendingFSR2MotionVecScaleX << "\n"; + out << "fsr2_mv_scale_y=" << pendingFSR2MotionVecScaleY << "\n"; // Controls out << "mouse_sensitivity=" << pendingMouseSensitivity << "\n"; @@ -7561,6 +7596,9 @@ void GameScreen::loadSettings() { } 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); + else if (key == "fsr2_jitter_sign") pendingFSR2JitterSign = std::clamp(std::stof(val), -2.0f, 2.0f); + else if (key == "fsr2_mv_scale_x") pendingFSR2MotionVecScaleX = std::clamp(std::stof(val), -2.0f, 2.0f); + else if (key == "fsr2_mv_scale_y") pendingFSR2MotionVecScaleY = std::clamp(std::stof(val), -2.0f, 2.0f); // Controls else if (key == "mouse_sensitivity") pendingMouseSensitivity = std::clamp(std::stof(val), 0.05f, 1.0f); else if (key == "invert_mouse") pendingInvertMouse = (std::stoi(val) != 0);