From bdfec103acddc3c3026984f3c1aa42144b3085c2 Mon Sep 17 00:00:00 2001 From: Kelsi Date: Sun, 8 Mar 2026 22:53:21 -0700 Subject: [PATCH] Add persisted AMD FSR3 framegen runtime toggle plumbing --- README.md | 3 +++ docs/AMD_FSR2_INTEGRATION.md | 5 +++++ include/rendering/renderer.hpp | 5 +++++ include/ui/game_screen.hpp | 1 + src/rendering/renderer.cpp | 34 ++++++++++++++++++++++++++++++++++ src/ui/game_screen.cpp | 17 +++++++++++++++++ 6 files changed, 65 insertions(+) diff --git a/README.md b/README.md index af34f623..cf7b67db 100644 --- a/README.md +++ b/README.md @@ -195,6 +195,9 @@ make -j$(nproc) - `sdk/src/backends/vk/CMakeShadersOpticalflow.txt` - CMake option: - `WOWEE_ENABLE_AMD_FSR3_FRAMEGEN=ON` enables a compile-probe target (`wowee_fsr3_framegen_amd_vk_probe`) that validates SDK FI/OF/FSR3/Vulkan interface headers at build time. +- Runtime toggle: + - In settings, `AMD FSR3 Frame Generation (Experimental)` persists to config. + - Current state is staging-only (`dispatch not linked yet`) until FidelityFX-SDK runtime component binaries are integrated. ### Current FSR Defaults diff --git a/docs/AMD_FSR2_INTEGRATION.md b/docs/AMD_FSR2_INTEGRATION.md index 6608d4a7..f58be84b 100644 --- a/docs/AMD_FSR2_INTEGRATION.md +++ b/docs/AMD_FSR2_INTEGRATION.md @@ -33,6 +33,11 @@ Detection expects: - `1` when FidelityFX-SDK FI/OF/FSR3+VK headers are detected. - `0` when headers are missing (probe target disabled). +Runtime note: + +- Renderer/UI now expose a persisted experimental framegen toggle. +- Current runtime status is `staged` (no FI/OF dispatch yet) until FidelityFX-SDK runtime implementation/shader blob linking is completed. + ## Current Status - AMD FSR2 Vulkan dispatch path is integrated and used when available. diff --git a/include/rendering/renderer.hpp b/include/rendering/renderer.hpp index 5256cded..25c24e79 100644 --- a/include/rendering/renderer.hpp +++ b/include/rendering/renderer.hpp @@ -275,6 +275,8 @@ public: void setFSR2Enabled(bool enabled); bool isFSR2Enabled() const { return fsr2_.enabled; } void setFSR2DebugTuning(float jitterSign, float motionVecScaleX, float motionVecScaleY); + void setAmdFsr3FramegenEnabled(bool enabled); + bool isAmdFsr3FramegenEnabled() const { return fsr2_.amdFsr3FramegenEnabled; } float getFSR2JitterSign() const { return fsr2_.jitterSign; } float getFSR2MotionVecScaleX() const { return fsr2_.motionVecScaleX; } float getFSR2MotionVecScaleY() const { return fsr2_.motionVecScaleY; } @@ -288,6 +290,7 @@ public: #else bool isAmdFsr3FramegenSdkAvailable() const { return false; } #endif + bool isAmdFsr3FramegenRuntimeActive() const { return fsr2_.amdFsr3FramegenRuntimeActive; } void setWaterRefractionEnabled(bool enabled); bool isWaterRefractionEnabled() const; @@ -434,6 +437,8 @@ private: uint32_t frameIndex = 0; bool needsHistoryReset = true; bool useAmdBackend = false; + bool amdFsr3FramegenEnabled = false; + bool amdFsr3FramegenRuntimeActive = false; float jitterSign = 0.38f; float motionVecScaleX = 1.0f; float motionVecScaleY = 1.0f; diff --git a/include/ui/game_screen.hpp b/include/ui/game_screen.hpp index 284e241a..76d70b84 100644 --- a/include/ui/game_screen.hpp +++ b/include/ui/game_screen.hpp @@ -123,6 +123,7 @@ private: float pendingFSR2JitterSign = 0.38f; float pendingFSR2MotionVecScaleX = 1.0f; float pendingFSR2MotionVecScaleY = 1.0f; + bool pendingAMDFramegen = false; bool fsrSettingsApplied_ = false; // UI element transparency (0.0 = fully transparent, 1.0 = fully opaque) diff --git a/src/rendering/renderer.cpp b/src/rendering/renderer.cpp index 16746745..04199a80 100644 --- a/src/rendering/renderer.cpp +++ b/src/rendering/renderer.cpp @@ -3740,6 +3740,7 @@ bool Renderer::initFSR2Resources() { " -> ", swapExtent.width, "x", swapExtent.height, " (scale=", fsr2_.scaleFactor, ")"); fsr2_.useAmdBackend = false; + fsr2_.amdFsr3FramegenRuntimeActive = false; #if WOWEE_HAS_AMD_FSR2 LOG_INFO("FSR2: AMD FidelityFX SDK detected at build time."); #else @@ -3838,6 +3839,14 @@ bool Renderer::initFSR2Resources() { if (ctxErr == FFX_OK) { fsr2_.useAmdBackend = true; LOG_INFO("FSR2 AMD: context created successfully."); +#if WOWEE_HAS_AMD_FSR3_FRAMEGEN + if (fsr2_.amdFsr3FramegenEnabled) { + // Runtime dispatch path is staged behind SDK runtime binary integration. + // Keep the user toggle persisted, but report inactive runtime for now. + fsr2_.amdFsr3FramegenRuntimeActive = false; + LOG_WARNING("FSR3 framegen is enabled in settings, but runtime dispatch is not linked yet; running FSR2-only."); + } +#endif } else { LOG_WARNING("FSR2 AMD: context creation failed (", static_cast(ctxErr), "), using internal fallback."); std::free(fsr2_.amdScratchBuffer); @@ -4135,6 +4144,7 @@ void Renderer::destroyFSR2Resources() { } fsr2_.amdScratchBufferSize = 0; #endif + fsr2_.amdFsr3FramegenRuntimeActive = false; if (fsr2_.sharpenPipeline) { vkDestroyPipeline(device, fsr2_.sharpenPipeline, nullptr); fsr2_.sharpenPipeline = VK_NULL_HANDLE; } if (fsr2_.sharpenPipelineLayout) { vkDestroyPipelineLayout(device, fsr2_.sharpenPipelineLayout, nullptr); fsr2_.sharpenPipelineLayout = VK_NULL_HANDLE; } @@ -4395,6 +4405,13 @@ void Renderer::setFSR2Enabled(bool enabled) { fsr2_.enabled = enabled; if (enabled) { + static bool initFramegenToggleFromEnv = false; + if (!initFramegenToggleFromEnv) { + initFramegenToggleFromEnv = true; + if (std::getenv("WOWEE_ENABLE_AMD_FSR3_FRAMEGEN_RUNTIME") != nullptr) { + fsr2_.amdFsr3FramegenEnabled = true; + } + } // FSR2 replaces both FSR1 and MSAA if (fsr_.enabled) { fsr_.enabled = false; @@ -4421,6 +4438,23 @@ void Renderer::setFSR2DebugTuning(float jitterSign, float motionVecScaleX, float fsr2_.motionVecScaleY = glm::clamp(motionVecScaleY, -2.0f, 2.0f); } +void Renderer::setAmdFsr3FramegenEnabled(bool enabled) { + fsr2_.amdFsr3FramegenEnabled = enabled; +#if WOWEE_HAS_AMD_FSR3_FRAMEGEN + if (enabled) { + fsr2_.amdFsr3FramegenRuntimeActive = false; + LOG_WARNING("FSR3 framegen toggle enabled, but runtime dispatch is not linked yet; running FSR2-only."); + } else { + fsr2_.amdFsr3FramegenRuntimeActive = false; + } +#else + fsr2_.amdFsr3FramegenRuntimeActive = false; + if (enabled) { + LOG_WARNING("FSR3 framegen requested, but AMD FSR3 framegen SDK headers are unavailable in this build."); + } +#endif +} + // ========================= 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 6f7f0969..ebb8e761 100644 --- a/src/ui/game_screen.cpp +++ b/src/ui/game_screen.cpp @@ -326,6 +326,7 @@ void GameScreen::render(game::GameHandler& gameHandler) { renderer->setFSRQuality(fsrScales[pendingFSRQuality]); renderer->setFSRSharpness(pendingFSRSharpness); renderer->setFSR2DebugTuning(pendingFSR2JitterSign, pendingFSR2MotionVecScaleX, pendingFSR2MotionVecScaleY); + renderer->setAmdFsr3FramegenEnabled(pendingAMDFramegen); // 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); @@ -6336,6 +6337,20 @@ void GameScreen::renderSettingsWindow() { if (fsrMode == 2 && renderer) { ImGui::TextDisabled("FSR2 backend: %s", renderer->isAmdFsr2SdkAvailable() ? "AMD FidelityFX SDK" : "Internal fallback"); + if (renderer->isAmdFsr3FramegenSdkAvailable()) { + if (ImGui::Checkbox("AMD FSR3 Frame Generation (Experimental)", &pendingAMDFramegen)) { + renderer->setAmdFsr3FramegenEnabled(pendingAMDFramegen); + saveSettings(); + } + ImGui::TextDisabled("Runtime: %s", + renderer->isAmdFsr3FramegenRuntimeActive() ? "Active" : "Staged (dispatch not linked yet)"); + } else { + ImGui::BeginDisabled(); + bool disabledFg = false; + ImGui::Checkbox("AMD FSR3 Frame Generation (Experimental)", &disabledFg); + ImGui::EndDisabled(); + ImGui::TextDisabled("Requires FidelityFX-SDK framegen headers."); + } } const char* fsrQualityLabels[] = { "Native (100%)", "Ultra Quality (77%)", "Quality (67%)", "Balanced (59%)" }; static const float fsrScaleFactors[] = { 0.77f, 0.67f, 0.59f, 1.00f }; @@ -7493,6 +7508,7 @@ void GameScreen::saveSettings() { out << "fsr2_jitter_sign=" << pendingFSR2JitterSign << "\n"; out << "fsr2_mv_scale_x=" << pendingFSR2MotionVecScaleX << "\n"; out << "fsr2_mv_scale_y=" << pendingFSR2MotionVecScaleY << "\n"; + out << "amd_fsr3_framegen=" << (pendingAMDFramegen ? 1 : 0) << "\n"; // Controls out << "mouse_sensitivity=" << pendingMouseSensitivity << "\n"; @@ -7592,6 +7608,7 @@ void GameScreen::loadSettings() { 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); + else if (key == "amd_fsr3_framegen") pendingAMDFramegen = (std::stoi(val) != 0); // 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);