diff --git a/features/Upscaling/Shaders/Upscaling/DLSSperf/BoxDownscalePS.hlsl b/features/Upscaling/Shaders/Upscaling/PerfMode/BoxDownscalePS.hlsl similarity index 95% rename from features/Upscaling/Shaders/Upscaling/DLSSperf/BoxDownscalePS.hlsl rename to features/Upscaling/Shaders/Upscaling/PerfMode/BoxDownscalePS.hlsl index 755a5778f6..a1370e417b 100644 --- a/features/Upscaling/Shaders/Upscaling/DLSSperf/BoxDownscalePS.hlsl +++ b/features/Upscaling/Shaders/Upscaling/PerfMode/BoxDownscalePS.hlsl @@ -1,4 +1,4 @@ -// BoxDownscalePS.hlsl — DLSSperf downscale pass +// BoxDownscalePS.hlsl — PerfMode downscale pass // Box 3×3 filter: testTexture (3k) → kMAIN (1k). // For 3:1 downscale, each output pixel averages the 3×3 source region, // ensuring all DLSS output pixels contribute (vs bilinear's 2×2 coverage). diff --git a/features/Upscaling/Shaders/Upscaling/DLSSperf/MenuBGBlitPS.hlsl b/features/Upscaling/Shaders/Upscaling/PerfMode/MenuBGBlitPS.hlsl similarity index 93% rename from features/Upscaling/Shaders/Upscaling/DLSSperf/MenuBGBlitPS.hlsl rename to features/Upscaling/Shaders/Upscaling/PerfMode/MenuBGBlitPS.hlsl index 2e946df518..9b20ae742b 100644 --- a/features/Upscaling/Shaders/Upscaling/DLSSperf/MenuBGBlitPS.hlsl +++ b/features/Upscaling/Shaders/Upscaling/PerfMode/MenuBGBlitPS.hlsl @@ -1,4 +1,4 @@ -// MenuBGBlitPS.hlsl — DLSSperf main-menu / loading-screen BG blit. +// MenuBGBlitPS.hlsl — PerfMode main-menu / loading-screen BG blit. // Fullscreen 1:1 sample of the source texture into kTOTAL/kMENUBG. The // caller (MaybeBlitMenuBG) feeds DLSS-reconstructed testTexture (R16G16 // B16A16_FLOAT, displayRes) and the destination kTOTAL is R8G8B8A8_UNORM diff --git a/src/Features/Upscaling.cpp b/src/Features/Upscaling.cpp index 5569154abc..32c0cd170c 100644 --- a/src/Features/Upscaling.cpp +++ b/src/Features/Upscaling.cpp @@ -4,7 +4,7 @@ #include "HDRDisplay.h" #include "Hooks.h" #include "State.h" -#include "Upscaling/DLSSperf.h" +#include "Upscaling/PerfMode.h" #include "Upscaling/DX12SwapChain.h" #include "Upscaling/FidelityFX.h" #include "Upscaling/Streamline.h" @@ -34,7 +34,7 @@ NLOHMANN_DEFINE_TYPE_NON_INTRUSIVE_WITH_DEFAULT( reflexUseMarkersToOptimize, reflexUseFPSLimit, reflexFPSLimit, - enableDLSSperf); + renderAtUpscaleRes); decltype(&D3D11CreateDeviceAndSwapChain) ptrD3D11CreateDeviceAndSwapChainUpscaling; @@ -214,14 +214,14 @@ void Upscaling::DrawSettings() // Check the current upscale method auto upscaleMethod = GetUpscaleMethod(); - // DLSSperf: BSOpenVR size hook + RT::Create run once at world load, so + // PerfMode: BSOpenVR size hook + RT::Create run once at world load, so // runtime reads of method/qualityMode route through the boot snapshot. // The always-present explanation is plain text — only the staged-change // diff uses the RestartNeeded color so users learn the cue means "you // changed something that won't apply yet." - if (dlssPerf.IsHookActive()) { + if (perfMode.IsHookActive()) { ImGui::TextWrapped( - "DLSSperf is active: Method and Upscale Preset changes only take effect after a game restart. " + "Render-at-upscaled-resolution is active: Method and Upscale Preset changes only take effect after a game restart. " "Sharpness / model preset / Reflex remain live."); // Method pending-diff. Only fires when the user is editing the DLSS- @@ -270,7 +270,7 @@ void Upscaling::DrawSettings() if (baseLabel) { // Derive scale from live `settings.qualityMode` — `resolution- - // Scale` is locked to the DLSSperf boot snapshot, so reusing it + // Scale` is locked to the PerfMode boot snapshot, so reusing it // here would mismatch the slider position the user sees. const float displayScale = 1.0f / ffxFsr3GetUpscaleRatioFromQualityMode((FfxFsr3QualityMode)std::clamp(settings.qualityMode, 0u, 4u)); std::string labelWithScale = std::format("{} ( {:.2f}x )", baseLabel, displayScale); @@ -280,7 +280,7 @@ void Upscaling::DrawSettings() // Pending-diff vs the boot snapshot the runtime upscaler is // actually using. Without this the slider change looks like a // no-op. - if (dlssPerf.IsHookActive() && + if (perfMode.IsHookActive() && bootSnapshot.HasPendingChange(settings, &Settings::qualityMode)) { const uint bm = std::clamp(bootSnapshot.Boot(&Settings::qualityMode), 0u, 4u); const char* bootLabel = (upscaleMethod == UpscaleMethod::kDLSS) ? upscalePresetsDLSS[std::clamp(4 - (int)bm, 0, 4)] : upscalePresets[std::clamp(4 - (int)bm, 0, 4)]; @@ -304,39 +304,41 @@ void Upscaling::DrawSettings() } } - // VR DLSSperf: opt-in performance feature. Lives in the main + // VR PerfMode: opt-in performance feature. Lives in the main // upscaler section (not Backend Diagnostics) so users discover it // alongside the rest of the upscaler controls. Restart-gated — // the BSOpenVR size hook reads this at world load and sizes every // engine RT off the boot value. // // The setting persists across method switches (we don't auto-flip - // it when the user picks FSR/TAA), but the checkbox itself is - // disabled outside the DLSS context since the install path triple- - // gates on DLSS being the resolved method. Keep visible-but-greyed - // so users see the option exists and understand why it isn't live. + // it when the user picks TAA/NONE), but the checkbox itself is + // disabled outside upscalers that can target a separate displayRes + // output (DLSS, FSR). Keep visible-but-greyed so users see the + // option exists and understand why it isn't live. if (globals::game::isVR) { - const bool dlssAvailable = upscaleMethod == UpscaleMethod::kDLSS; - if (!dlssAvailable) + const bool methodSupportsPerf = + upscaleMethod == UpscaleMethod::kDLSS || + upscaleMethod == UpscaleMethod::kFSR; + if (!methodSupportsPerf) ImGui::BeginDisabled(); - ImGui::Checkbox("Render engine at upscaled resolution (DLSSperf)", &settings.enableDLSSperf); - if (!dlssAvailable) + ImGui::Checkbox("Render engine at upscaled resolution", &settings.renderAtUpscaleRes); + if (!methodSupportsPerf) ImGui::EndDisabled(); if (auto _tt = Util::HoverTooltipWrapper()) { ImGui::Text( "When enabled, the engine pipeline allocates render targets at the upscaled-render\n" - "resolution instead of the HMD display resolution. DLSS writes its output to a private\n" - "DisplayRes texture. Substantial VRAM and bandwidth savings, especially at high HMD\n" - "resolutions.\n" + "resolution instead of the HMD display resolution. The upscaler (DLSS or FSR) writes\n" + "its output to a private DisplayRes texture. Substantial VRAM and bandwidth savings,\n" + "especially at high HMD resolutions.\n" "\n" - "Requires the DLSS upscaler. Restart required to enable/disable. Method and Upscale\n" + "Requires DLSS or FSR. Restart required to enable/disable. Method and Upscale\n" "Preset changes also require a restart while this is active; sharpness / model preset\n" "/ Reflex remain live."); } - if (!dlssAvailable && settings.enableDLSSperf) - Util::Text::Disabled("DLSSperf requires DLSS — switch upscaler Method to DLSS to activate."); - if (dlssAvailable) - Util::UI::DrawSettingDiff(bootSnapshot, settings, &Settings::enableDLSSperf); + if (!methodSupportsPerf && settings.renderAtUpscaleRes) + Util::Text::Disabled("Render-at-upscaled-resolution requires DLSS or FSR — switch upscaler Method to activate."); + if (methodSupportsPerf) + Util::UI::DrawSettingDiff(bootSnapshot, settings, &Settings::renderAtUpscaleRes); } } @@ -701,10 +703,10 @@ void Upscaling::PostPostLoad() Upscaling::UpscaleMethod Upscaling::GetUpscaleMethod() const { - // Lock runtime to the boot upscaler under DLSSperf — engine RTs are + // Lock runtime to the boot upscaler under PerfMode — engine RTs are // sized for it, and routing a different method through testTexture/ // renderRes paths breaks the HMD. - if (globals::features::upscaling.dlssPerf.IsHookActive()) + if (globals::features::upscaling.perfMode.IsHookActive()) return static_cast(bootSnapshot.Boot(&Settings::upscaleMethod)); if (streamline.featureDLSS) return (UpscaleMethod)settings.upscaleMethod; @@ -1085,12 +1087,12 @@ void Upscaling::EnsureVRIntermediateTextures() auto screenSize = globals::state->screenSize; auto renderSize = Util::ConvertToDynamic(screenSize); - // DLSSperf: state->screenSize is polluted to RenderRes (the BSOpenVR size + // PerfMode: state->screenSize is polluted to RenderRes (the BSOpenVR size // hook spoofs HMD-recommended size). DLSS output needs to land at real - // DisplayRes, so size the OUTPUT intermediates from dlssPerf's snapshot + // DisplayRes, so size the OUTPUT intermediates from perfMode's snapshot // of the true HMD resolution. Input intermediates stay at renderSize. - const bool dlssperfActive = dlssPerf.IsHookActive() && dlssPerf.GetTestTexture(); - const float2 outputSize = dlssperfActive ? dlssPerf.GetDisplayScreenSize() : screenSize; + const bool dlssperfActive = perfMode.IsHookActive() && perfMode.GetTestTexture(); + const float2 outputSize = dlssperfActive ? perfMode.GetDisplayScreenSize() : screenSize; uint32_t eyeWidthOut = (uint32_t)(outputSize.x / 2); uint32_t eyeHeightOut = (uint32_t)outputSize.y; @@ -1164,10 +1166,18 @@ void Upscaling::FinalizePerEyeOutputs(ID3D11Resource* colorDst) state->BeginPerfEvent("VR Upscaling Finalize"); auto context = globals::d3d::context; - auto screenSize = state->screenSize; - uint32_t eyeWidthOut = (uint32_t)(screenSize.x / 2); - uint32_t eyeHeightOut = (uint32_t)screenSize.y; + // Drive output dims from the per-eye intermediate desc, not state->screenSize. + // Under PerfMode the state value is polluted to renderRes while the intermediates + // were allocated at displayRes via EnsureVRIntermediateTextures' size bridge. + if (!vrIntermediateColorOut[0]) { + if (state->frameAnnotations) + state->EndPerfEvent(); + return; + } + + uint32_t eyeWidthOut = vrIntermediateColorOut[0]->desc.Width; + uint32_t eyeHeightOut = vrIntermediateColorOut[0]->desc.Height; // Write upscaled outputs back for (uint32_t i = 0; i < 2; ++i) { @@ -1295,24 +1305,29 @@ void Upscaling::ConfigureUpscaling(RE::BSGraphics::State* a_viewport) auto screenHeight = static_cast(screenSize.y); if (upscaleMethod != UpscaleMethod::kNONE && upscaleMethod != UpscaleMethod::kTAA) { - // DLSSperf: when the BSOpenVR size hook is live, every engine RT was + // PerfMode: when the BSOpenVR size hook is live, every engine RT was // already allocated at RenderRes — so the DRS-style scale is identity. - // Jitter is still computed at the real DisplayRes phase ratio so DLSS - // has enough sub-pixel diversity for the upscale. + // Jitter is still computed at the real DisplayRes phase ratio so the + // upscaler has enough sub-pixel diversity for reconstruction. // // The upscaleMethod here comes from GetUpscaleMethod(), which under - // DLSSperf+hookActive is locked to the boot snapshot — so this gate + // PerfMode+hookActive is locked to the boot snapshot — so this gate // reads the value the user had selected at game start, not what they // later moved the slider to. Engine RTs were sized off that boot // choice (irreversible — the size hook can't un-allocate them); the - // boot-snapshot lock keeps the runtime DLSS evaluate consistent with - // those allocations. UI staged-change banners explain the restart - // requirement for method/quality edits. - if (dlssPerf.IsHookActive() && upscaleMethod == UpscaleMethod::kDLSS) { + // boot-snapshot lock keeps the runtime evaluate consistent with those + // allocations. UI staged-change banners explain the restart + // requirement for method/quality edits. Branch fires for both DLSS + // and FSR since both consume the renderRes engine RTs and write to + // perfMode.testTexture. + const bool dlssperfRenderResPath = + perfMode.IsHookActive() && + (upscaleMethod == UpscaleMethod::kDLSS || upscaleMethod == UpscaleMethod::kFSR); + if (dlssperfRenderResPath) { resolutionScale = { 1.0f, 1.0f }; - auto renderWidth = static_cast(dlssPerf.GetRenderEyeWidth()); - auto displayWidth = static_cast(dlssPerf.GetDisplayEyeWidth()); + auto renderWidth = static_cast(perfMode.GetRenderEyeWidth()); + auto displayWidth = static_cast(perfMode.GetDisplayEyeWidth()); auto phaseCount = GetJitterPhaseCount(renderWidth, displayWidth); GetJitterOffset(&jitter.x, &jitter.y, state->frameCount, phaseCount); @@ -1322,11 +1337,11 @@ void Upscaling::ConfigureUpscaling(RE::BSGraphics::State* a_viewport) else a_viewport->projectionPosScaleX = -2.0f * jitter.x / renderWidth; - a_viewport->projectionPosScaleY = 2.0f * jitter.y / static_cast(dlssPerf.GetRenderEyeHeight()); + a_viewport->projectionPosScaleY = 2.0f * jitter.y / static_cast(perfMode.GetRenderEyeHeight()); } else { - // Boot qualityMode under DLSSperf so projection stays coherent + // Boot qualityMode under PerfMode so projection stays coherent // with the engine RTs sized at install. - const uint32_t qm = globals::features::upscaling.dlssPerf.IsHookActive() ? bootSnapshot.Boot(&Settings::qualityMode) : settings.qualityMode; + const uint32_t qm = globals::features::upscaling.perfMode.IsHookActive() ? bootSnapshot.Boot(&Settings::qualityMode) : settings.qualityMode; float resolutionScaleBase = 1.0f / ffxFsr3GetUpscaleRatioFromQualityMode((FfxFsr3QualityMode)qm); auto renderWidth = static_cast(screenWidth * resolutionScaleBase); @@ -1896,7 +1911,14 @@ void Upscaling::Upscale() } streamline.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVectorCopyTexture->resource.get()); } else if (upscaleMethod == UpscaleMethod::kFSR) { - fidelityFX.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVector.texture, settings.sharpnessFSR); + // PerfMode bridge: when the engine RTs are shrunk to renderRes, FSR's displayRes + // output must land in perfMode.testTexture (the private displayRes target used for + // OpenVR submit), not back in the now-small kMAIN. Mirrors Streamline's colorOut + // routing for DLSS. + ID3D11Resource* fsrColorOut = (perfMode.IsHookActive() && perfMode.GetTestTexture()) ? + static_cast(perfMode.GetTestTexture()) : + nullptr; + fidelityFX.Upscale(main.texture, reactiveMaskTexture->resource.get(), transparencyCompositionMaskTexture->resource.get(), motionVector.texture, settings.sharpnessFSR, fsrColorOut); } state->EndPerfEvent(); @@ -2305,19 +2327,23 @@ void Upscaling::Main_PostProcessing::thunk(RE::ImageSpaceManager* a_this, uint32 if (hdrLoaded) globals::features::hdrDisplay.RedirectFramebuffer(); - // DLSSperf: hybrid Post — HandlePostProcessing performs a two-layer + // PerfMode: hybrid Post — HandlePostProcessing performs a two-layer // struct swap around the engine's func() so tonemap + refraction read // the DisplayRes testTexture instead of the small kMAIN. The supplied // lambda is the engine call we'd normally make directly. // - // Upscaler gate: testTexture is only populated by DLSS's evaluate path - // (Streamline routes its colorOut there when DLSSperf is active). Under - // DLSSperf+hookActive, GetUpscaleMethod() returns the boot snapshot so - // this kDLSS check evaluates against the install-time choice — staged - // UI method changes don't reach here until restart. ShouldHandlePost() - // covers the partial-init case (post resources missing). - if (upscaleMethod == UpscaleMethod::kDLSS && globals::features::upscaling.dlssPerf.ShouldHandlePost()) { - globals::features::upscaling.dlssPerf.HandlePostProcessing([&]() { + // Upscaler gate: testTexture is populated by whichever upscaler ran + // (Streamline routes DLSS colorOut there, FidelityFX routes FSR + // colorOut there). Under PerfMode+hookActive, GetUpscaleMethod() returns + // the boot snapshot so this check evaluates against the install-time + // choice — staged UI method changes don't reach here until restart. + // ShouldHandlePost() covers the partial-init case (post resources + // missing). + const bool upscalerWritesTestTexture = + upscaleMethod == UpscaleMethod::kDLSS || + upscaleMethod == UpscaleMethod::kFSR; + if (upscalerWritesTestTexture && globals::features::upscaling.perfMode.ShouldHandlePost()) { + globals::features::upscaling.perfMode.HandlePostProcessing([&]() { func(a_this, a3, a_target, a_4, a_5); }); } else { diff --git a/src/Features/Upscaling.h b/src/Features/Upscaling.h index 481d491b83..9fbdcf47a4 100644 --- a/src/Features/Upscaling.h +++ b/src/Features/Upscaling.h @@ -1,7 +1,7 @@ #pragma once #include "Feature.h" -#include "Upscaling/DLSSperf.h" +#include "Upscaling/PerfMode.h" #include "Upscaling/DX12SwapChain.h" #include "Upscaling/FidelityFX.h" #include "Upscaling/RCAS/RCAS.h" @@ -71,12 +71,12 @@ struct Upscaling : Feature bool reflexUseFPSLimit = false; float reflexFPSLimit = 60.0f; - // VR DLSSperf: opt-in. When set, BSShaderRenderTargets::Create installs + // VR PerfMode: opt-in. When set, BSShaderRenderTargets::Create installs // the BSOpenVR render-target-size hook at engine init so the entire // engine pipeline allocates render targets at upscaled-render resolution // instead of display resolution. Saves VRAM/bandwidth proportional to // the quality-mode scale ratio. Requires a game restart to take effect. - bool enableDLSSperf = false; + bool renderAtUpscaleRes = false; }; Settings settings; @@ -84,7 +84,7 @@ struct Upscaling : Feature // Single source of truth for restart-gated fields. Order is not load-bearing // — the call-site `DrawSettingDiff` invocations in DrawSettings() handle any // per-field conditional gating (e.g., qualityMode/upscaleMethod banners only - // render while DLSSperf's render-target hook is active). MCP discovery + // render while PerfMode's render-target hook is active). MCP discovery // reports the full set; clients can check feature state themselves. // presetDLSS is deliberately NOT here: Streamline::SetDLSSOptions reads // settings.presetDLSS per-frame and applies it via slDLSSSetOptions, so @@ -92,7 +92,7 @@ struct Upscaling : Feature inline static constexpr Util::Settings::RestartTable kRestartFields{ { UTIL_RESTART_FIELD(Settings, frameGenerationMode, "Frame Generation"), UTIL_RESTART_FIELD(Settings, frameGenerationForceEnable, "Force Enable Frame Generation"), - UTIL_RESTART_FIELD(Settings, enableDLSSperf, "DLSSperf"), + UTIL_RESTART_FIELD(Settings, renderAtUpscaleRes, "Render at Upscaled Resolution"), UTIL_RESTART_FIELD(Settings, streamlineLogLevel, "Streamline Logging"), UTIL_RESTART_FIELD(Settings, upscaleMethod, "Upscaling Method"), UTIL_RESTART_FIELD(Settings, qualityMode, "Upscale Preset"), @@ -235,7 +235,7 @@ struct Upscaling : Feature static inline FidelityFX fidelityFX; ///< Only for frame generation static inline DX12SwapChain dx12SwapChain; static inline RCAS rcas; ///< Standalone RCAS sharpening for DLSS - static inline DLSSperf dlssPerf; ///< VR-only: render engine at upscaled-render res + static inline PerfMode perfMode; ///< VR-only: render engine at upscaled-render res winrt::com_ptr copyDepthToSharedBufferPS; @@ -265,11 +265,11 @@ struct Upscaling : Feature * * Same draw as UpscaleDepth's mask branch on the full-resolution path, * extracted so callers that bypass the standard upscale flow (notably - * DLSSperf::HandlePostProcessing, where engine RTs are pre-shrunk to + * PerfMode::HandlePostProcessing, where engine RTs are pre-shrunk to * renderRes and DLSS targets a private displayRes texture) can drive * the repair without going through UpscaleDepth's wider envelope. * Sets and leaves D3D11 pipeline state dirty on exit — wrap in your - * own save/restore (DLSSperf uses its FullscreenPassScope). + * own save/restore (PerfMode uses its FullscreenPassScope). */ void RunUnderwaterMaskRepair(); diff --git a/src/Features/Upscaling/FidelityFX.cpp b/src/Features/Upscaling/FidelityFX.cpp index b9976a99c7..e22ae987b9 100644 --- a/src/Features/Upscaling/FidelityFX.cpp +++ b/src/Features/Upscaling/FidelityFX.cpp @@ -273,8 +273,17 @@ void FidelityFX::CreateFSRResources() auto screenSize = state->screenSize; auto renderSize = Util::ConvertToDynamic(screenSize); - uint32_t displayWidth = (uint32_t)(globals::game::isVR ? screenSize.x / 2 : screenSize.x); - uint32_t displayHeight = (uint32_t)screenSize.y; + // PerfMode bridge: when the BSOpenVR size hook is live, state->screenSize is polluted + // to renderRes (engine RTs were allocated small). FSR3 still needs to upscale to the + // real HMD display resolution, so use perfMode's snapshot for displaySize/maxUpscaleSize. + // maxRenderSize stays at screenSize (which IS renderRes under the hook — that's FSR's + // expected input extent). + auto& perfMode = globals::features::upscaling.perfMode; + const bool dlssperfActive = perfMode.IsHookActive(); + const auto displaySize = dlssperfActive ? perfMode.GetDisplayScreenSize() : screenSize; + + uint32_t displayWidth = (uint32_t)(globals::game::isVR ? displaySize.x / 2 : displaySize.x); + uint32_t displayHeight = (uint32_t)displaySize.y; uint32_t renderWidth = (uint32_t)(globals::game::isVR ? renderSize.x / 2 : renderSize.x); uint32_t renderHeight = (uint32_t)renderSize.y; @@ -344,7 +353,7 @@ FfxResource ffxGetResource(ID3D11Resource* dx11Resource, return resource; } -void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, float a_sharpness) +void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, float a_sharpness, ID3D11Resource* a_colorOut) { auto renderer = globals::game::renderer; auto context = globals::d3d::context; @@ -357,6 +366,10 @@ void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_r auto& upscaling = globals::features::upscaling; auto jitter = upscaling.jitter; + // Default to in-place output when caller didn't supply a separate destination. + if (!a_colorOut) + a_colorOut = a_upscalingTexture; + auto DispatchFSR = [&](uint32_t contextIndex, ID3D11Resource* r_color, ID3D11Resource* r_depth, ID3D11Resource* r_mvec, ID3D11Resource* r_reactive, ID3D11Resource* r_trans, ID3D11Resource* r_output, uint32_t r_width, float mv_scale_x) { @@ -431,8 +444,9 @@ void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_r renderSize.x / 2.0f); } - // Merge outputs back to kMAIN - upscaling.FinalizePerEyeOutputs(a_upscalingTexture); + // Merge outputs into the supplied displayRes destination (kMAIN by default; + // perfMode.testTexture when PerfMode has shrunk the engine RTs). + upscaling.FinalizePerEyeOutputs(a_colorOut); } else { DispatchFSR(0, a_upscalingTexture, @@ -440,7 +454,7 @@ void FidelityFX::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_r a_motionVectors, a_reactiveMask, a_transparencyCompositionMask, - a_upscalingTexture, // Output to same texture + a_colorOut, (uint)renderSize.x, renderSize.x); } diff --git a/src/Features/Upscaling/FidelityFX.h b/src/Features/Upscaling/FidelityFX.h index 5db922ebec..f324bb580c 100644 --- a/src/Features/Upscaling/FidelityFX.h +++ b/src/Features/Upscaling/FidelityFX.h @@ -55,7 +55,11 @@ class FidelityFX void DestroyFSRResources(); - void Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, float a_sharpness); + // a_colorOut is the destination for the upscaled result. When nullptr, output is written + // back into a_upscalingTexture (legacy in-place behavior). Callers route a separate output + // when the engine's kMAIN is renderRes (e.g., PerfMode VR mode) and the upscaled result + // must land in a displayRes target. + void Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_reactiveMask, ID3D11Resource* a_transparencyCompositionMask, ID3D11Resource* a_motionVectors, float a_sharpness, ID3D11Resource* a_colorOut = nullptr); private: // FSR scratch buffer - needs to be freed in DestroyFSRResources diff --git a/src/Features/Upscaling/DLSSperf.cpp b/src/Features/Upscaling/PerfMode.cpp similarity index 86% rename from src/Features/Upscaling/DLSSperf.cpp rename to src/Features/Upscaling/PerfMode.cpp index 713104ef79..611017c498 100644 --- a/src/Features/Upscaling/DLSSperf.cpp +++ b/src/Features/Upscaling/PerfMode.cpp @@ -1,4 +1,4 @@ -#include "DLSSperf.h" +#include "PerfMode.h" #include #include @@ -9,10 +9,10 @@ // Quality mode → render-scale resolution is supplied by the FFX SDK helper // (same one Upscaling.cpp uses at ConfigureUpscaling), avoiding a duplicate // scale table here. Decoupled from the original PR's DlssEnhancer::Bridge so -// DLSSperf can ship without the larger enhancer framework. +// PerfMode can ship without the larger enhancer framework. #include -DLSSperf::FullscreenPassScope::FullscreenPassScope(ID3D11DeviceContext* a_context) : +PerfMode::FullscreenPassScope::FullscreenPassScope(ID3D11DeviceContext* a_context) : ctx(a_context) { ctx->OMGetRenderTargets(1, &savedRTV, &savedDSV); @@ -33,7 +33,7 @@ DLSSperf::FullscreenPassScope::FullscreenPassScope(ID3D11DeviceContext* a_contex ctx->IAGetPrimitiveTopology(&savedTopology); } -DLSSperf::FullscreenPassScope::~FullscreenPassScope() +PerfMode::FullscreenPassScope::~FullscreenPassScope() { // Null the SRV slot before restoring to break any potential SRV-vs-RTV // hazard from the pass we just ran (matches the explicit null-pass the @@ -93,7 +93,7 @@ DLSSperf::FullscreenPassScope::~FullscreenPassScope() savedIB->Release(); } -void DLSSperf::InstallRenderTargetSizeHook() +void PerfMode::InstallRenderTargetSizeHook() { if (!globals::game::isVR) return; @@ -104,14 +104,14 @@ void DLSSperf::InstallRenderTargetSizeHook() // Eager capture — get real HMD resolution BEFORE installing hook auto* openvr = RE::BSOpenVR::GetSingleton(); if (!openvr || !openvr->vrSystem) { - logger::error("[DLSSperf] BSOpenVR or vrSystem not available — hook NOT installed"); + logger::error("[PerfMode] BSOpenVR or vrSystem not available — hook NOT installed"); return; } uint32_t w = 0, h = 0; openvr->vrSystem->GetRecommendedRenderTargetSize(&w, &h); if (w == 0 || h == 0) { - logger::error("[DLSSperf] GetRecommendedRenderTargetSize returned {}x{} — hook NOT installed", w, h); + logger::error("[PerfMode] GetRecommendedRenderTargetSize returned {}x{} — hook NOT installed", w, h); return; } @@ -127,13 +127,13 @@ void DLSSperf::InstallRenderTargetSizeHook() // Validate before division: a bad/corrupt JSON could put qualityMode // outside FFX's range, returning 0/inf/NaN; that would propagate to bogus // renderEye dimensions and silently mis-size every engine RT. Fail closed - // — leave hookActive=false so the rest of DLSSperf is dormant and DLSS + // — leave hookActive=false so the rest of PerfMode is dormant and DLSS // runs on dev's standard path. const uint32_t qualityModeRaw = globals::features::upscaling.settings.qualityMode; const uint32_t qualityMode = std::clamp(qualityModeRaw, 0, 4); // FfxFsr3QualityMode range const float scale = ffxFsr3GetUpscaleRatioFromQualityMode(static_cast(qualityMode)); if (!std::isfinite(scale) || scale <= 0.0f) { - logger::error("[DLSSperf] FFX returned invalid upscale ratio {} for qualityMode {} (raw {}); hook NOT installed", scale, qualityMode, qualityModeRaw); + logger::error("[PerfMode] FFX returned invalid upscale ratio {} for qualityMode {} (raw {}); hook NOT installed", scale, qualityMode, qualityModeRaw); return; } renderEyeWidth = std::max(1, (uint32_t)(w / scale)); @@ -160,18 +160,18 @@ void DLSSperf::InstallRenderTargetSizeHook() hookActive = true; } -void DLSSperf::GetRenderTargetSize_Hook::thunk(RE::BSOpenVR* a_this, uint32_t* a_width, uint32_t* a_height) +void PerfMode::GetRenderTargetSize_Hook::thunk(RE::BSOpenVR* a_this, uint32_t* a_width, uint32_t* a_height) { // Call original to get real HMD resolution func(a_this, a_width, a_height); - auto& dlssPerf = globals::features::upscaling.dlssPerf; + auto& perfMode = globals::features::upscaling.perfMode; - *a_width = dlssPerf.renderEyeWidth; - *a_height = dlssPerf.renderEyeHeight; + *a_width = perfMode.renderEyeWidth; + *a_height = perfMode.renderEyeHeight; } -void DLSSperf::SetupResources() +void PerfMode::SetupResources() { if (!globals::game::isVR) return; @@ -179,7 +179,7 @@ void DLSSperf::SetupResources() auto renderer = globals::game::renderer; auto& mainRT = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN]; if (!mainRT.texture) { - logger::error("[DLSSperf] kMAIN texture not available in SetupResources"); + logger::error("[PerfMode] kMAIN texture not available in SetupResources"); return; } @@ -204,7 +204,7 @@ void DLSSperf::SetupResources() auto device = globals::d3d::device; HRESULT hr = device->CreateTexture2D(&desc, nullptr, testTexture.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create test texture: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create test texture: {:#x}", (uint32_t)hr); return; } @@ -215,7 +215,7 @@ void DLSSperf::SetupResources() srvDesc.Texture2D.MostDetailedMip = 0; hr = device->CreateShaderResourceView(testTexture.get(), &srvDesc, testTextureSRV.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create test texture SRV: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create test texture SRV: {:#x}", (uint32_t)hr); testTexture = nullptr; testTextureUAV = nullptr; return; @@ -229,7 +229,7 @@ void DLSSperf::SetupResources() uavDesc.Texture2D.MipSlice = 0; hr = device->CreateUnorderedAccessView(testTexture.get(), &uavDesc, testTextureUAV.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create testTexture UAV: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create testTexture UAV: {:#x}", (uint32_t)hr); } } @@ -241,7 +241,7 @@ void DLSSperf::SetupResources() rtvDesc.Texture2D.MipSlice = 0; hr = device->CreateRenderTargetView(testTexture.get(), &rtvDesc, testTextureRTV.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create testTexture RTV: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create testTexture RTV: {:#x}", (uint32_t)hr); } } @@ -252,7 +252,7 @@ void DLSSperf::SetupResources() hr = device->CreateTexture2D(&refraDesc, nullptr, refraTempTex.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create refraTempTex: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create refraTempTex: {:#x}", (uint32_t)hr); } else { D3D11_SHADER_RESOURCE_VIEW_DESC refraSrvDesc{}; refraSrvDesc.Format = refraDesc.Format; @@ -261,7 +261,7 @@ void DLSSperf::SetupResources() refraSrvDesc.Texture2D.MostDetailedMip = 0; hr = device->CreateShaderResourceView(refraTempTex.get(), &refraSrvDesc, refraTempSRV.put()); if (FAILED(hr)) { - logger::error("[DLSSperf] Failed to create refraTempSRV: {:#x}", (uint32_t)hr); + logger::error("[PerfMode] Failed to create refraTempSRV: {:#x}", (uint32_t)hr); refraTempTex = nullptr; } } @@ -281,7 +281,7 @@ void DLSSperf::SetupResources() HRESULT hr2 = device->CreateTexture2D(&fakeDesc, nullptr, fakeDS.put()); if (FAILED(hr2)) { - logger::error("[DLSSperf] Failed to create fake DS texture: {:#x}", (uint32_t)hr2); + logger::error("[PerfMode] Failed to create fake DS texture: {:#x}", (uint32_t)hr2); } else { // Create DSV — format depends on typeless base format D3D11_DEPTH_STENCIL_VIEW_DESC dsvDesc{}; @@ -302,12 +302,12 @@ void DLSSperf::SetupResources() hr2 = device->CreateDepthStencilView(fakeDS.get(), &dsvDesc, fakeDSV.put()); if (FAILED(hr2)) { - logger::error("[DLSSperf] Failed to create fake DSV: {:#x}", (uint32_t)hr2); + logger::error("[PerfMode] Failed to create fake DSV: {:#x}", (uint32_t)hr2); fakeDS = nullptr; } } } else { - logger::warn("[DLSSperf] kMAIN DS texture not available, skipping fake DS creation"); + logger::warn("[PerfMode] kMAIN DS texture not available, skipping fake DS creation"); } } @@ -352,21 +352,21 @@ void DLSSperf::SetupResources() // Downscale + blit shaders if (hookActive && !boxDownscalePS) { boxDownscalePS.attach(static_cast( - Util::CompileShader(L"Data/Shaders/Upscaling/DLSSperf/BoxDownscalePS.hlsl", { { "PSHADER", "" } }, "ps_5_0"))); + Util::CompileShader(L"Data/Shaders/Upscaling/PerfMode/BoxDownscalePS.hlsl", { { "PSHADER", "" } }, "ps_5_0"))); if (!boxDownscalePS) - logger::error("[DLSSperf] Failed to compile BoxDownscalePS"); + logger::error("[PerfMode] Failed to compile BoxDownscalePS"); } if (hookActive && !boxDownscaleVS) { boxDownscaleVS.attach(static_cast( Util::CompileShader(L"Data/Shaders/Upscaling/UpscaleVS.hlsl", { { "VSHADER", "" } }, "vs_5_0"))); if (!boxDownscaleVS) - logger::error("[DLSSperf] Failed to compile BoxDownscale VS"); + logger::error("[PerfMode] Failed to compile BoxDownscale VS"); } if (hookActive && !menuBlitPS) { menuBlitPS.attach(static_cast( - Util::CompileShader(L"Data/Shaders/Upscaling/DLSSperf/MenuBGBlitPS.hlsl", { { "PSHADER", "" } }, "ps_5_0"))); + Util::CompileShader(L"Data/Shaders/Upscaling/PerfMode/MenuBGBlitPS.hlsl", { { "PSHADER", "" } }, "ps_5_0"))); if (!menuBlitPS) - logger::error("[DLSSperf] Failed to compile MenuBGBlitPS"); + logger::error("[PerfMode] Failed to compile MenuBGBlitPS"); } if (hookActive && !linearSampler) { D3D11_SAMPLER_DESC sd{}; @@ -377,7 +377,7 @@ void DLSSperf::SetupResources() sd.MaxAnisotropy = 1; sd.MaxLOD = D3D11_FLOAT32_MAX; if (FAILED(device->CreateSamplerState(&sd, linearSampler.put()))) - logger::error("[DLSSperf] Failed to create linear sampler"); + logger::error("[PerfMode] Failed to create linear sampler"); } // Fail-closed pipeline-ready gate @@ -402,7 +402,7 @@ void DLSSperf::SetupResources() if (hookActive && !postPipelineReady) { logger::error( - "[DLSSperf] Post pipeline failed to initialize fully — Post wrap " + "[PerfMode] Post pipeline failed to initialize fully — Post wrap " "disabled, engine RTs remain at RenderRes. Check upstream resource " "creation errors above."); } @@ -415,11 +415,11 @@ void DLSSperf::SetupResources() // Inner layer of two-layer swap: swaps kMAIN SRV → testTextureSRV and // kMAIN DS → fakeDS before tonemap Render(), restores after. -void DLSSperf::TonemapRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) +void PerfMode::TonemapRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) { - auto& dlssPerf = globals::features::upscaling.dlssPerf; + auto& perfMode = globals::features::upscaling.perfMode; - if (!dlssPerf.hookActive || !dlssPerf.testTextureSRV || !dlssPerf.fakeDSV) { + if (!perfMode.hookActive || !perfMode.testTextureSRV || !perfMode.fakeDSV) { func(imageSpaceShader, shape, param); return; } @@ -434,12 +434,12 @@ void DLSSperf::TonemapRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* // runs at most once per frame regardless of how many menu redraws fire. if (globals::state && globals::state->IsMainOrLoadingMenuOpen()) { func(imageSpaceShader, shape, param); - dlssPerf.MaybeBlitMenuBG(RE::RENDER_TARGETS::kTOTAL); + perfMode.MaybeBlitMenuBG(RE::RENDER_TARGETS::kTOTAL); return; } ZoneScoped; - TracyD3D11Zone(globals::state->tracyCtx, "DLSSperf::TonemapRender"); + TracyD3D11Zone(globals::state->tracyCtx, "PerfMode::TonemapRender"); auto renderer = RE::BSGraphics::Renderer::GetSingleton(); auto& rtData = renderer->GetRuntimeData(); @@ -447,43 +447,43 @@ void DLSSperf::TonemapRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* // --- Swap kMAIN SRV → testTextureSRV (so tonemap reads 3k upscaled color) --- auto& kmainRT = rtData.renderTargets[RE::RENDER_TARGETS::kMAIN]; - dlssPerf.savedKMainSRV = kmainRT.SRV; - kmainRT.SRV = dlssPerf.testTextureSRV.get(); + perfMode.savedKMainSRV = kmainRT.SRV; + kmainRT.SRV = perfMode.testTextureSRV.get(); // --- Also swap kMAIN_COPY SRV (refraction path reads this instead of kMAIN) --- auto& kmainCopyRT = rtData.renderTargets[RE::RENDER_TARGETS::kMAIN_COPY]; - dlssPerf.savedKMainCopySRV = kmainCopyRT.SRV; - kmainCopyRT.SRV = dlssPerf.testTextureSRV.get(); + perfMode.savedKMainCopySRV = kmainCopyRT.SRV; + kmainCopyRT.SRV = perfMode.testTextureSRV.get(); // --- Swap kMAIN DS views → fakeDS (so 3k RT doesn't mismatch 1k DS) --- auto& kmainDS = dsData.depthStencils[RE::RENDER_TARGETS_DEPTHSTENCIL::kMAIN]; for (int i = 0; i < 8; i++) { - dlssPerf.savedKMainViews[i] = kmainDS.views[i]; + perfMode.savedKMainViews[i] = kmainDS.views[i]; if (kmainDS.views[i]) - kmainDS.views[i] = dlssPerf.fakeDSV.get(); + kmainDS.views[i] = perfMode.fakeDSV.get(); } for (int i = 0; i < 8; i++) { - dlssPerf.savedKMainReadOnlyViews[i] = kmainDS.readOnlyViews[i]; + perfMode.savedKMainReadOnlyViews[i] = kmainDS.readOnlyViews[i]; if (kmainDS.readOnlyViews[i]) - kmainDS.readOnlyViews[i] = dlssPerf.fakeDSV.get(); + kmainDS.readOnlyViews[i] = perfMode.fakeDSV.get(); } // --- Call original (or FrameAnnotations chain) --- func(imageSpaceShader, shape, param); // --- Restore kMAIN SRV --- - kmainRT.SRV = dlssPerf.savedKMainSRV; - dlssPerf.savedKMainSRV = nullptr; + kmainRT.SRV = perfMode.savedKMainSRV; + perfMode.savedKMainSRV = nullptr; // --- Restore kMAIN_COPY SRV --- - kmainCopyRT.SRV = dlssPerf.savedKMainCopySRV; - dlssPerf.savedKMainCopySRV = nullptr; + kmainCopyRT.SRV = perfMode.savedKMainCopySRV; + perfMode.savedKMainCopySRV = nullptr; // --- Restore kMAIN DS views --- for (int i = 0; i < 8; i++) - kmainDS.views[i] = dlssPerf.savedKMainViews[i]; + kmainDS.views[i] = perfMode.savedKMainViews[i]; for (int i = 0; i < 8; i++) - kmainDS.readOnlyViews[i] = dlssPerf.savedKMainReadOnlyViews[i]; + kmainDS.readOnlyViews[i] = perfMode.savedKMainReadOnlyViews[i]; } // ============================================================================ @@ -493,17 +493,17 @@ void DLSSperf::TonemapRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* // After func() returns, D3D11 state is sticky (PS/CB/sampler/IA all still bound). // We replay the draw with our own RT (testTexture 3k), VP (3k), and SRV (refraTempTex 3k). -void DLSSperf::RefractionRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) +void PerfMode::RefractionRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) { - auto& dlssPerf = globals::features::upscaling.dlssPerf; + auto& perfMode = globals::features::upscaling.perfMode; - if (!dlssPerf.hookActive || !dlssPerf.testTextureRTV || !dlssPerf.refraTempSRV) { + if (!perfMode.hookActive || !perfMode.testTextureRTV || !perfMode.refraTempSRV) { func(imageSpaceShader, shape, param); return; } ZoneScoped; - TracyD3D11Zone(globals::state->tracyCtx, "DLSSperf::RefractionRender"); + TracyD3D11Zone(globals::state->tracyCtx, "PerfMode::RefractionRender"); // --- Pass 1: engine's normal 1k refraction (untouched) --- func(imageSpaceShader, shape, param); @@ -532,21 +532,21 @@ void DLSSperf::RefractionRender_Hook::thunk(void* imageSpaceShader, RE::BSTriSha context->PSGetShaderResources(0, 1, &savedSRV0); // Set 3k output: testTexture RTV, no DS needed for fullscreen IS shader - ID3D11RenderTargetView* rtv3k = dlssPerf.testTextureRTV.get(); + ID3D11RenderTargetView* rtv3k = perfMode.testTextureRTV.get(); context->OMSetRenderTargets(1, &rtv3k, nullptr); // Set 3k VP D3D11_VIEWPORT vp3k = {}; vp3k.TopLeftX = 0.0f; vp3k.TopLeftY = 0.0f; - vp3k.Width = static_cast(dlssPerf.displayEyeWidth * 2); - vp3k.Height = static_cast(dlssPerf.displayEyeHeight); + vp3k.Width = static_cast(perfMode.displayEyeWidth * 2); + vp3k.Height = static_cast(perfMode.displayEyeHeight); vp3k.MinDepth = 0.0f; vp3k.MaxDepth = 1.0f; context->RSSetViewports(1, &vp3k); // Set 3k input: refraTempTex as t0 (scene color for refraction sampling) - ID3D11ShaderResourceView* srv3k = dlssPerf.refraTempSRV.get(); + ID3D11ShaderResourceView* srv3k = perfMode.refraTempSRV.get(); context->PSSetShaderResources(0, 1, &srv3k); // Draw with the same geometry (BSTriShape fullscreen quad, IA still bound) @@ -574,8 +574,8 @@ void DLSSperf::RefractionRender_Hook::thunk(void* imageSpaceShader, RE::BSTriSha // ISCopyRender_Hook: stretch ISCopy when source < dest (menu compositor fix) // ============================================================================ // The VR menu compositor uses a single ISCopy draw to blit the rendered scene -// (kMAIN — RenderRes under DLSSperf) into a fixed-size projection surface -// (kPROJECTEDMENU 2048², or kMENUBG which DLSSperf enlarges to DisplayRes). +// (kMAIN — RenderRes under PerfMode) into a fixed-size projection surface +// (kPROJECTEDMENU 2048², or kMENUBG which PerfMode enlarges to DisplayRes). // Engine ISCopy uses a 1:1 viewport sized to the source, so the small source // is stamped into the top-left of the larger dest. Symptom: "main menu image // looks downscaled." @@ -586,16 +586,16 @@ void DLSSperf::RefractionRender_Hook::thunk(void* imageSpaceShader, RE::BSTriSha // relies on), so the replay needs only a viewport change + DrawIndexed and // the engine's clamp-sampler stretches the source naturally. // -// In-game ISCopy (where source.w == dest.w under DLSSperf — kMAIN renderRes +// In-game ISCopy (where source.w == dest.w under PerfMode — kMAIN renderRes // → kMAIN_COPY renderRes) takes the early-out branch and the engine's draw // is the final pixel. -void DLSSperf::ISCopyRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) +void PerfMode::ISCopyRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* shape, RE::ImageSpaceEffectParam* param) { - auto& dlssPerf = globals::features::upscaling.dlssPerf; + auto& perfMode = globals::features::upscaling.perfMode; // Inactive / non-VR: passthrough. - if (!dlssPerf.hookActive) { + if (!perfMode.hookActive) { func(imageSpaceShader, shape, param); return; } @@ -647,7 +647,7 @@ void DLSSperf::ISCopyRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* if (needsStretch) { ZoneScoped; - TracyD3D11Zone(globals::state->tracyCtx, "DLSSperf::ISCopyStretch"); + TracyD3D11Zone(globals::state->tracyCtx, "PerfMode::ISCopyStretch"); // Replay viewport: full dest extent, preserve depth range from the // original so anything sampling depth (unlikely for ISCopy but safe) @@ -680,13 +680,13 @@ void DLSSperf::ISCopyRender_Hook::thunk(void* imageSpaceShader, RE::BSTriShape* // UI pass draws VR HUD to kMENUBG (now 3k). Engine binds KMAIN(DS) as DS, // which is still 1k → size mismatch. Swap to fakeDS (3k) before, restore after. -void DLSSperf::UIPassDispatch_Hook::thunk(RE::BSGraphics::BSShaderAccumulator* shaderAccumulator, uint32_t renderFlags) +void PerfMode::UIPassDispatch_Hook::thunk(RE::BSGraphics::BSShaderAccumulator* shaderAccumulator, uint32_t renderFlags) { - auto& dlssPerf = globals::features::upscaling.dlssPerf; + auto& perfMode = globals::features::upscaling.perfMode; // Only intercept renderMode==24 (UI pass) when hook is active auto& rtData = shaderAccumulator->GetRuntimeData(); - if (!dlssPerf.hookActive || !dlssPerf.fakeDSV || rtData.renderMode != 24) { + if (!perfMode.hookActive || !perfMode.fakeDSV || rtData.renderMode != 24) { func(shaderAccumulator, renderFlags); return; } @@ -701,12 +701,12 @@ void DLSSperf::UIPassDispatch_Hook::thunk(RE::BSGraphics::BSShaderAccumulator* s for (int i = 0; i < 8; i++) { savedViews[i] = kmainDS.views[i]; if (kmainDS.views[i]) - kmainDS.views[i] = dlssPerf.fakeDSV.get(); + kmainDS.views[i] = perfMode.fakeDSV.get(); } for (int i = 0; i < 8; i++) { savedReadOnlyViews[i] = kmainDS.readOnlyViews[i]; if (kmainDS.readOnlyViews[i]) - kmainDS.readOnlyViews[i] = dlssPerf.fakeDSV.get(); + kmainDS.readOnlyViews[i] = perfMode.fakeDSV.get(); } // Force engine to re-bind DS from struct @@ -721,19 +721,19 @@ void DLSSperf::UIPassDispatch_Hook::thunk(RE::BSGraphics::BSShaderAccumulator* s savedVP = vp; vp.TopLeftX = 0.0f; vp.TopLeftY = 0.0f; - vp.Width = static_cast(dlssPerf.displayEyeWidth * 2); - vp.Height = static_cast(dlssPerf.displayEyeHeight); + vp.Width = static_cast(perfMode.displayEyeWidth * 2); + vp.Height = static_cast(perfMode.displayEyeHeight); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; globals::game::stateUpdateFlags->set(RE::BSGraphics::ShaderFlags::DIRTY_VIEWPORT); } // Skip VP compression in UpdateViewPort hook during UI pass - dlssPerf.postInterceptActive = true; + perfMode.postInterceptActive = true; func(shaderAccumulator, renderFlags); - dlssPerf.postInterceptActive = false; + perfMode.postInterceptActive = false; // Restore viewport if (ss) { @@ -758,11 +758,11 @@ void DLSSperf::UIPassDispatch_Hook::thunk(RE::BSGraphics::BSShaderAccumulator* s // After func() returns, clear postChainDone so the Present-前 UI chain // and the next frame use normal VP compression. -void DLSSperf::PlayerViewRender_Hook::thunk(void* a1, bool a2, bool a3) +void PerfMode::PlayerViewRender_Hook::thunk(void* a1, bool a2, bool a3) { func(a1, a2, a3); - globals::features::upscaling.dlssPerf.ClearPostChainDone(); + globals::features::upscaling.perfMode.ClearPostChainDone(); } // ============================================================================ @@ -770,14 +770,14 @@ void DLSSperf::PlayerViewRender_Hook::thunk(void* a1, bool a2, bool a3) // ============================================================================ // Wraps DS swap around the engine's RT/DS flush so enlarged-RT draws don't // rasterizer-clip to the smaller kMAIN DS bounds. -void DLSSperf::BSGraphics_SetDirtyStates_Hook::thunk(bool isCompute) +void PerfMode::BSGraphics_SetDirtyStates_Hook::thunk(bool isCompute) { bool swapped = false; if (!isCompute) - swapped = globals::features::upscaling.dlssPerf.MaybeSwapDSForEnlargedRT(); + swapped = globals::features::upscaling.perfMode.MaybeSwapDSForEnlargedRT(); func(isCompute); if (swapped) - globals::features::upscaling.dlssPerf.RestoreSwappedDS(); + globals::features::upscaling.perfMode.RestoreSwappedDS(); } // ============================================================================ @@ -785,33 +785,33 @@ void DLSSperf::BSGraphics_SetDirtyStates_Hook::thunk(bool isCompute) // ============================================================================ // Post-corrects the engine viewport when the bound RT and the requested VP // don't agree about render-vs-display extent. Was originally in Hooks.cpp. -void DLSSperf::BSGraphics_Renderer_UpdateViewPort_Hook::thunk(RE::BSGraphics::Renderer* a_this, uint32_t a_width, uint32_t a_height, bool a_forceMatchRT) +void PerfMode::BSGraphics_Renderer_UpdateViewPort_Hook::thunk(RE::BSGraphics::Renderer* a_this, uint32_t a_width, uint32_t a_height, bool a_forceMatchRT) { func(a_this, a_width, a_height, a_forceMatchRT); - auto& dlssPerf = globals::features::upscaling.dlssPerf; - if (!dlssPerf.IsHookActive()) + auto& perfMode = globals::features::upscaling.perfMode; + if (!perfMode.IsHookActive()) return; // During Post intercept enlarged kTEMP/kTOTAL already get the right VP // from func() because of their inflated RT dims — don't second-guess it. - if (dlssPerf.IsPostInterceptActive()) + if (perfMode.IsPostInterceptActive()) return; auto* ss = globals::game::shadowState; if (!ss) return; auto& vp = ss->GetVRRuntimeData().viewPort; - const uint32_t displayW = dlssPerf.GetDisplayEyeWidth() * 2; - const uint32_t displayH = dlssPerf.GetDisplayEyeHeight(); - const uint32_t renderW = dlssPerf.GetRenderEyeWidth() * 2; - const uint32_t renderH = dlssPerf.GetRenderEyeHeight(); + const uint32_t displayW = perfMode.GetDisplayEyeWidth() * 2; + const uint32_t displayH = perfMode.GetDisplayEyeHeight(); + const uint32_t renderW = perfMode.GetRenderEyeWidth() * 2; + const uint32_t renderH = perfMode.GetRenderEyeHeight(); // After the Post chain, UI / submit-prep draws target enlarged kTOTAL // at displayRes — expand any renderRes VP the engine sets back up. // The fade Draw(30) bypasses this path entirely (direct D3D RSSet- // Viewports) and is handled by the Draw vfunc hook in Globals.cpp. - if (dlssPerf.IsPostChainDone()) { + if (perfMode.IsPostChainDone()) { if (static_cast(vp.Width) == renderW && static_cast(vp.Height) == renderH) { vp.Width = static_cast(displayW); @@ -852,14 +852,14 @@ void DLSSperf::BSGraphics_Renderer_UpdateViewPort_Hook::thunk(RE::BSGraphics::Re // entire Post chain (covers the copy step #10 which binds kMAIN_COPY DS). // Inner layer (tonemap hook) handles kMAIN DS + kMAIN SRV for step #9. -void DLSSperf::BeginPostIntercept() +void PerfMode::BeginPostIntercept() { if (!hookActive || !fakeDSV) return; ZoneScoped; auto state = globals::state; - state->BeginPerfEvent("DLSSperf::BeginPostIntercept"); + state->BeginPerfEvent("PerfMode::BeginPostIntercept"); auto renderer = RE::BSGraphics::Renderer::GetSingleton(); auto& dsData = renderer->GetDepthStencilData(); @@ -882,14 +882,14 @@ void DLSSperf::BeginPostIntercept() state->EndPerfEvent(); } -void DLSSperf::EndPostIntercept() +void PerfMode::EndPostIntercept() { if (!hookActive || !fakeDSV) return; ZoneScoped; auto state = globals::state; - state->BeginPerfEvent("DLSSperf::EndPostIntercept"); + state->BeginPerfEvent("PerfMode::EndPostIntercept"); auto renderer = RE::BSGraphics::Renderer::GetSingleton(); auto& dsData = renderer->GetDepthStencilData(); @@ -916,7 +916,7 @@ void DLSSperf::EndPostIntercept() // - No refraction: kMAIN is the pyramid input directly. // - With refraction: engine composites kMAIN → kMAIN_COPY, which enters pyramid. -void DLSSperf::DownscaleToKMain() +void PerfMode::DownscaleToKMain() { if (!hookActive || !testTextureSRV || !boxDownscalePS || !boxDownscaleVS || !linearSampler) return; @@ -934,8 +934,8 @@ void DLSSperf::DownscaleToKMain() if (!kmain.RTV) return; - state->BeginPerfEvent("DLSSperf::DownscaleToKMain"); - TracyD3D11Zone(state->tracyCtx, "DLSSperf::DownscaleToKMain"); + state->BeginPerfEvent("PerfMode::DownscaleToKMain"); + TracyD3D11Zone(state->tracyCtx, "PerfMode::DownscaleToKMain"); { FullscreenPassScope stateScope(context); @@ -987,7 +987,7 @@ void DLSSperf::DownscaleToKMain() // dest. One-shot per frame via blittedFrameId (Present doesn't fire here // and PlayerView doesn't fire in main-menu, so the frame-id guard is the // only reliable per-frame boundary). -void DLSSperf::MaybeBlitMenuBG(uint32_t boundRTIdx) +void PerfMode::MaybeBlitMenuBG(uint32_t boundRTIdx) { const uint32_t currentFrame = globals::state ? globals::state->frameCount : 0; if (!hookActive || blittedFrameId == currentFrame || !menuBlitPS || !boxDownscaleVS || !linearSampler) @@ -1009,8 +1009,8 @@ void DLSSperf::MaybeBlitMenuBG(uint32_t boundRTIdx) ZoneScoped; auto state = globals::state; auto* context = globals::d3d::context; - state->BeginPerfEvent("DLSSperf::MenuBGBlit"); - TracyD3D11Zone(state->tracyCtx, "DLSSperf::MenuBGBlit"); + state->BeginPerfEvent("PerfMode::MenuBGBlit"); + TracyD3D11Zone(state->tracyCtx, "PerfMode::MenuBGBlit"); globals::features::upscaling.Upscale(); @@ -1055,11 +1055,11 @@ void DLSSperf::MaybeBlitMenuBG(uint32_t boundRTIdx) state->EndPerfEvent(); } -void DLSSperf::HandlePostProcessing(const std::function& enginePost) +void PerfMode::HandlePostProcessing(const std::function& enginePost) { ZoneScoped; auto state = globals::state; - state->BeginPerfEvent("DLSSperf::HandlePostProcessing"); + state->BeginPerfEvent("PerfMode::HandlePostProcessing"); // Copy testTexture → refraTempTex before Post, so ISRefraction can read 3k scene if (refraTempTex) { @@ -1071,7 +1071,7 @@ void DLSSperf::HandlePostProcessing(const std::function& enginePost) DownscaleToKMain(); // Underwater mask analytical repair. Engine RTs (depth, mask) are at - // renderRes under DLSSperf, so the full-resolution path of UpscaleDepth + // renderRes under PerfMode, so the full-resolution path of UpscaleDepth // would apply — but routing through UpscaleDepth here leaves pipeline // state dirty and the trailing enginePost() loses kMAIN. Drive the // mask-only draw directly inside our own FullscreenPassScope so the @@ -1093,7 +1093,7 @@ void DLSSperf::HandlePostProcessing(const std::function& enginePost) state->EndPerfEvent(); } -bool DLSSperf::MaybeSwapDSForEnlargedRT() +bool PerfMode::MaybeSwapDSForEnlargedRT() { if (!hookActive || postInterceptActive) return false; @@ -1105,7 +1105,7 @@ bool DLSSperf::MaybeSwapDSForEnlargedRT() return false; auto& srd = ss->GetVRRuntimeData(); - // Only the three RTs DLSSperf_MaybeEnlargeRT inflates to displayRes. + // Only the three RTs PerfMode_MaybeEnlargeRT inflates to displayRes. const uint32_t rtIdx = static_cast(srd.renderTargets[0]); if (rtIdx != RE::RENDER_TARGETS::kTOTAL && rtIdx != RE::RENDER_TARGETS::kMENUBG && @@ -1143,7 +1143,7 @@ bool DLSSperf::MaybeSwapDSForEnlargedRT() return true; } -void DLSSperf::RestoreSwappedDS() +void PerfMode::RestoreSwappedDS() { if (autoSwapDSIdx == UINT32_MAX) return; @@ -1169,22 +1169,22 @@ void DLSSperf::RestoreSwappedDS() // ID3D11DeviceContext_Draw_Hook (vtable index 13) // ============================================================================ // Engine fade-overlay Draw(30) fires after the Post chain and before Submit. -// Under DLSSperf the draw's VP is computed at renderRes while the RT (kTOTAL) +// Under PerfMode the draw's VP is computed at renderRes while the RT (kTOTAL) // is displayRes — partial-screen "black stamp" without this swap. Gate on // VertexCount==30 + isVR keeps the cost a single comparison on flat / non- // fade draws. -void DLSSperf::ID3D11DeviceContext_Draw_Hook::thunk(ID3D11DeviceContext* This, UINT VertexCount, UINT StartVertexLocation) +void PerfMode::ID3D11DeviceContext_Draw_Hook::thunk(ID3D11DeviceContext* This, UINT VertexCount, UINT StartVertexLocation) { if (VertexCount == 30 && globals::game::isVR) { - auto& dlssPerf = globals::features::upscaling.dlssPerf; - if (dlssPerf.IsHookActive() && dlssPerf.IsPostChainDone()) { + auto& perfMode = globals::features::upscaling.perfMode; + if (perfMode.IsHookActive() && perfMode.IsPostChainDone()) { UINT numVP = D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE; D3D11_VIEWPORT savedVP[D3D11_VIEWPORT_AND_SCISSORRECT_OBJECT_COUNT_PER_PIPELINE] = {}; This->RSGetViewports(&numVP, savedVP); D3D11_VIEWPORT vp{}; - vp.Width = static_cast(dlssPerf.GetDisplayEyeWidth() * 2); - vp.Height = static_cast(dlssPerf.GetDisplayEyeHeight()); + vp.Width = static_cast(perfMode.GetDisplayEyeWidth() * 2); + vp.Height = static_cast(perfMode.GetDisplayEyeHeight()); vp.MinDepth = 0.0f; vp.MaxDepth = 1.0f; This->RSSetViewports(1, &vp); @@ -1199,14 +1199,14 @@ void DLSSperf::ID3D11DeviceContext_Draw_Hook::thunk(ID3D11DeviceContext* This, U func(This, VertexCount, StartVertexLocation); } -void DLSSperf::InstallFadeOverlayHook(ID3D11DeviceContext* context) +void PerfMode::InstallFadeOverlayHook(ID3D11DeviceContext* context) { if (!globals::game::isVR || !context) return; stl::detour_vfunc<13, ID3D11DeviceContext_Draw_Hook>(context); } -void DLSSperf::InstallCreateRTThunks() +void PerfMode::InstallCreateRTThunks() { if (!REL::Module::IsVR()) return; @@ -1216,7 +1216,7 @@ void DLSSperf::InstallCreateRTThunks() stl::write_thunk_call(vrBase + 0x1547); } -void DLSSperf::BeginCreateRTEnlarge() +void PerfMode::BeginCreateRTEnlarge() { if (!hookActive) return; @@ -1225,7 +1225,7 @@ void DLSSperf::BeginCreateRTEnlarge() enlargeActive = true; } -void DLSSperf::EndCreateRTEnlarge() +void PerfMode::EndCreateRTEnlarge() { enlargeActive = false; } @@ -1234,7 +1234,7 @@ namespace { void EnlargeProps(RE::BSGraphics::RenderTargetProperties* a_props) { - auto& dp = globals::features::upscaling.dlssPerf; + auto& dp = globals::features::upscaling.perfMode; if (!dp.IsCreateRTEnlargeActive()) return; a_props->width = dp.GetEnlargeWidth(); @@ -1242,27 +1242,27 @@ namespace } } -void DLSSperf::CreateRT_MenuBG_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) +void PerfMode::CreateRT_MenuBG_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) { EnlargeProps(a_properties); func(a_this, a_target, a_properties); } -void DLSSperf::CreateRT_ImagespaceTempCopy_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) +void PerfMode::CreateRT_ImagespaceTempCopy_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) { EnlargeProps(a_properties); func(a_this, a_target, a_properties); } -void DLSSperf::CreateRT_Total_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) +void PerfMode::CreateRT_Total_Hook::thunk(RE::BSGraphics::Renderer* a_this, RE::RENDER_TARGETS::RENDER_TARGET a_target, RE::BSGraphics::RenderTargetProperties* a_properties) { EnlargeProps(a_properties); func(a_this, a_target, a_properties); } -void DLSSperf::DrawSettings() +void PerfMode::DrawSettings() { - // DLSSperf has no user-facing settings of its own — enablement is gated + // PerfMode has no user-facing settings of its own — enablement is gated // at install time by whether the BSShaderRenderTargets::Create hook ran // successfully. A future PR may surface diagnostic info here. } diff --git a/src/Features/Upscaling/DLSSperf.h b/src/Features/Upscaling/PerfMode.h similarity index 97% rename from src/Features/Upscaling/DLSSperf.h rename to src/Features/Upscaling/PerfMode.h index a5a331b2f1..dee76bc168 100644 --- a/src/Features/Upscaling/DLSSperf.h +++ b/src/Features/Upscaling/PerfMode.h @@ -1,7 +1,7 @@ #pragma once // ============================================================================ -// DLSSperf — render-target size hook + post-processing interception +// PerfMode — render-target size hook + post-processing interception // ============================================================================ // // Opt-in VR upscaling feature. Hooks BSOpenVR::GetRenderTargetSize so all @@ -34,7 +34,7 @@ #include #include -struct DLSSperf +struct PerfMode { void SetupResources(); void DrawSettings(); @@ -79,7 +79,7 @@ struct DLSSperf void DownscaleToKMain(); // Post hybrid entry point: called from Upscaling's Main_PostProcessing::thunk. - // Wraps the engine Post chain with DLSSperf's two-layer struct swap. + // Wraps the engine Post chain with PerfMode's two-layer struct swap. // Keyed on postPipelineReady (set at the end of SetupResources) so a // partial-init state can't slip past the gate into a null deref. The // runtime upscaler-method gate is enforced separately by callers (the @@ -114,7 +114,7 @@ struct DLSSperf // that fixes the scene-fade overlay viewport. Called from Globals:: // InstallD3DHooks. VR-only; thunk early-outs unless VertexCount==30 // and the hook is live, so cost is one comparison per Draw call when - // DLSSperf isn't active. + // PerfMode isn't active. void InstallFadeOverlayHook(ID3D11DeviceContext* context); // Enlarge window — set true around the engine's BSShaderRenderTargets:: @@ -229,7 +229,7 @@ struct DLSSperf // IS shader hook: ISCopy (Render vfunc 0x1 on vtable[3]). // The VR main menu / pause compositor uses a single ISCopy draw from kMAIN - // (RenderRes when DLSSperf is active) into kPROJECTEDMENU (fixed 2048²) or + // (RenderRes when PerfMode is active) into kPROJECTEDMENU (fixed 2048²) or // kMENUBG (DisplayRes via enlargement). With a 1:1 viewport the small // source gets stamped into the top-left of the larger dest — that's the // "main menu looks downscaled" bug. Strategy: let func() draw normally, @@ -265,7 +265,7 @@ struct DLSSperf // Chains via stl::detour_thunk on the same address Hooks.cpp + Terrain- // Blending already detour. Wraps MaybeSwapDSForEnlargedRT around the - // engine's RT/DS flush; runs after the prior thunk so DLSSperf's swap + // engine's RT/DS flush; runs after the prior thunk so PerfMode's swap // is the innermost wrap. struct BSGraphics_SetDirtyStates_Hook { @@ -275,7 +275,7 @@ struct DLSSperf bool setDirtyStatesHookInstalled = false; // D3D11 Draw vfunc detour. Engine's scene-fade overlay is a Draw(30) - // that fires after the Post chain and before Submit. Under DLSSperf + // that fires after the Post chain and before Submit. Under PerfMode // the draw's VP/vertices are computed at renderRes while the RT // (kTOTAL) is displayRes — produces a partial-screen "black stamp" // without this swap. diff --git a/src/Features/Upscaling/Streamline.cpp b/src/Features/Upscaling/Streamline.cpp index c5c2a812a2..29e4aebe74 100644 --- a/src/Features/Upscaling/Streamline.cpp +++ b/src/Features/Upscaling/Streamline.cpp @@ -10,7 +10,7 @@ #include "../../State.h" #include "../../Util.h" #include "../Upscaling.h" -#include "DLSSperf.h" +#include "PerfMode.h" #include "DX12SwapChain.h" namespace @@ -421,9 +421,9 @@ void Streamline::SetDLSSOptions(sl::ViewportHandle p_viewport, uint32_t width) { sl::DLSSOptions dlssOptions{}; - // Boot qualityMode under DLSSperf — DLSS dispatch must match the + // Boot qualityMode under PerfMode — DLSS dispatch must match the // renderRes the engine was sized for at install. - uint32_t qualityMode = globals::features::upscaling.dlssPerf.IsHookActive() ? globals::features::upscaling.bootSnapshot.Boot(&Upscaling::Settings::qualityMode) : globals::features::upscaling.settings.qualityMode; + uint32_t qualityMode = globals::features::upscaling.perfMode.IsHookActive() ? globals::features::upscaling.bootSnapshot.Boot(&Upscaling::Settings::qualityMode) : globals::features::upscaling.settings.qualityMode; switch (qualityMode) { case 1: dlssOptions.mode = sl::DLSSMode::eMaxQuality; @@ -444,15 +444,15 @@ void Streamline::SetDLSSOptions(sl::ViewportHandle p_viewport, uint32_t width) auto state = globals::state; - // DLSSperf bridge: state->screenSize.y is polluted to RenderRes by the - // BSOpenVR size hook; use dlssPerf's snapshot of the real DisplayRes when + // PerfMode bridge: state->screenSize.y is polluted to RenderRes by the + // BSOpenVR size hook; use perfMode's snapshot of the real DisplayRes when // the hook is live so DLSS is created at the right scale. The width arg // is already display-correct (caller computes from displaySize). - auto& dlssPerf = globals::features::upscaling.dlssPerf; - const bool dlssperfActive = dlssPerf.IsHookActive() && dlssPerf.GetTestTexture(); + auto& perfMode = globals::features::upscaling.perfMode; + const bool dlssperfActive = perfMode.IsHookActive() && perfMode.GetTestTexture(); dlssOptions.outputWidth = width; - dlssOptions.outputHeight = dlssperfActive ? (uint)dlssPerf.GetDisplayScreenSize().y : (uint)state->screenSize.y; + dlssOptions.outputHeight = dlssperfActive ? (uint)perfMode.GetDisplayScreenSize().y : (uint)state->screenSize.y; // Detect HDR from kMAIN format at runtime -- VR kMAIN may be 8-bit while SE is FP16 { @@ -606,23 +606,23 @@ void Streamline::Upscale(ID3D11Resource* a_upscalingTexture, ID3D11Resource* a_r auto screenSize = state->screenSize; auto renderSize = Util::ConvertToDynamic(screenSize); - // DLSSperf bridge: when the BSOpenVR size hook is live, state->screenSize + // PerfMode bridge: when the BSOpenVR size hook is live, state->screenSize // is polluted to RenderRes (the spoofed HMD recommended size). DLSS must // be told the TRUE DisplayRes for its output extent, otherwise NGX rejects // the evaluate as InvalidParameter (0xbad00005) because the configured // quality-scale doesn't match the actual extent ratio. The upscale also - // has to write into dlssPerf's private DisplayRes testTexture instead of + // has to write into perfMode's private DisplayRes testTexture instead of // the now-RenderRes kMAIN. auto& upscaling = globals::features::upscaling; - auto& dlssPerf = globals::features::upscaling.dlssPerf; - const bool dlssperfActive = dlssPerf.IsHookActive() && dlssPerf.GetTestTexture(); - const auto displaySize = dlssperfActive ? dlssPerf.GetDisplayScreenSize() : screenSize; + auto& perfMode = globals::features::upscaling.perfMode; + const bool dlssperfActive = perfMode.IsHookActive() && perfMode.GetTestTexture(); + const auto displaySize = dlssperfActive ? perfMode.GetDisplayScreenSize() : screenSize; // When RCAS sharpening is active, direct DLSS output to sharpenerTexture so RCAS can - // sharpen directly into kMAIN.UAV without a CopyResource round-trip. DLSSperf + // sharpen directly into kMAIN.UAV without a CopyResource round-trip. PerfMode // bypasses the sharpener entirely (writes DLSS output straight into testTexture). ID3D11Resource* colorOut = - dlssperfActive ? static_cast(dlssPerf.GetTestTexture()) : + dlssperfActive ? static_cast(perfMode.GetTestTexture()) : ((upscaling.settings.sharpnessDLSS > 0.0f && upscaling.sharpenerTexture) ? upscaling.sharpenerTexture->resource.get() : a_upscalingTexture); // VR stereo DLSS: NGX D3D11 only accepts zero-offset subrects. Non-zero offsets return diff --git a/src/Globals.cpp b/src/Globals.cpp index 8deee22e3d..68aea71e0c 100644 --- a/src/Globals.cpp +++ b/src/Globals.cpp @@ -405,15 +405,15 @@ namespace globals stl::detour_vfunc<53, ID3D11DeviceContext_ClearDepthStencilView>(a_context); } - // Scene-fade overlay Draw(30) detour — only useful when DLSSperf will + // Scene-fade overlay Draw(30) detour — only useful when PerfMode will // actually go active. The hook's thunk already early-outs on // VertexCount != 30 || !hookActive, but skipping the vtable patch - // entirely when the user has DLSSperf off avoids a foreign-interop + // entirely when the user has PerfMode off avoids a foreign-interop // surface other context-vfunc hookers could trip on. Gated by the // persisted intent, not IsHookActive(), because the hook is installed // here at D3D init time while IsHookActive() only flips true later // inside BSShaderRenderTargets::Create. - if (globals::game::isVR && globals::features::upscaling.settings.enableDLSSperf) - globals::features::upscaling.dlssPerf.InstallFadeOverlayHook(a_context); + if (globals::game::isVR && globals::features::upscaling.settings.renderAtUpscaleRes) + globals::features::upscaling.perfMode.InstallFadeOverlayHook(a_context); } } diff --git a/src/Hooks.cpp b/src/Hooks.cpp index 5c9d4abb17..497945ae16 100644 --- a/src/Hooks.cpp +++ b/src/Hooks.cpp @@ -375,37 +375,42 @@ struct BSShaderRenderTargets_Create // can diff "active at boot" vs "selected". globals::features::upscaling.bootSnapshot.LatchIfNeeded(globals::features::upscaling.settings); - // DLSSperf: install the BSOpenVR render-target-size hook before the + // PerfMode: install the BSOpenVR render-target-size hook before the // engine creates its render targets. This is the only place where // BSOpenVR is guaranteed available AND we can still influence RT // allocation. Gated on user opt-in via Upscaling::Settings AND on - // DLSS actually being the resolved upscale path — a stale config can - // leave enableDLSSperf=true while the active method is FSR/TAA or - // DLSS is unsupported on this GPU, and the rest of DLSSperf only - // makes sense for the DLSS output path. + // the resolved upscale path being one that can write its output to + // a separate displayRes target (DLSS via Streamline, FSR via + // FidelityFX). TAA/NONE have no upscale output to redirect, and a + // stale config can leave renderAtUpscaleRes=true after the user + // switched methods or after DLSS becomes unsupported on this GPU. + const auto resolvedUpscaleMethod = globals::features::upscaling.GetUpscaleMethod(); + const bool methodSupportsPerfMode = + resolvedUpscaleMethod == Upscaling::UpscaleMethod::kDLSS || + resolvedUpscaleMethod == Upscaling::UpscaleMethod::kFSR; const bool dlssperfShouldRun = globals::game::isVR && - globals::features::upscaling.settings.enableDLSSperf && - globals::features::upscaling.GetUpscaleMethod() == Upscaling::UpscaleMethod::kDLSS; + globals::features::upscaling.settings.renderAtUpscaleRes && + methodSupportsPerfMode; if (dlssperfShouldRun) { - globals::features::upscaling.dlssPerf.InstallRenderTargetSizeHook(); + globals::features::upscaling.perfMode.InstallRenderTargetSizeHook(); } - // Open DLSSperf's enlarge window across the engine's Create() so + // Open PerfMode's enlarge window across the engine's Create() so // its 3 per-site thunks override props for the displayRes RTs. - auto& dlssPerf = globals::features::upscaling.dlssPerf; - dlssPerf.BeginCreateRTEnlarge(); + auto& perfMode = globals::features::upscaling.perfMode; + perfMode.BeginCreateRTEnlarge(); func(); - dlssPerf.EndCreateRTEnlarge(); + perfMode.EndCreateRTEnlarge(); globals::ReInit(); globals::state->Setup(); - // DLSSperf is not in the Feature list (it's a worker driven by the + // PerfMode is not in the Feature list (it's a worker driven by the // upscaling toggle), so SetupResources runs here directly. - if (dlssPerf.IsHookActive()) - dlssPerf.SetupResources(); + if (perfMode.IsHookActive()) + perfMode.SetupResources(); } static inline REL::Relocation func; }; @@ -901,7 +906,7 @@ namespace Hooks stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0xA25, 0xA25, 0xCD2)); stl::write_thunk_call(REL::RelocationID(100458, 107175).address() + REL::Relocate(0xA59, 0xA59, 0xD13)); - globals::features::upscaling.dlssPerf.InstallCreateRTThunks(); + globals::features::upscaling.perfMode.InstallCreateRTThunks(); #ifdef TRACY_ENABLE stl::write_thunk_call(REL::RelocationID(35551, 36544).address() + REL::Relocate(0x11F, 0x160));