Add Nvngx FG Combo, FFX + Enabler combo mode

Mainly for use with FSR 4 FG as otherwise it doesn't make sense.
FFX for the middle fake frame, Enabler for the rest:
2x - FFX
3x - Enabler
4x - FFX + Enabler
5x - Enabler
6x - FFX + Enabler

Due to pacing, only odd number of fake frames are able to use FFX
This commit is contained in:
FakeMichau
2026-08-13 17:56:08 +02:00
parent b88d2be3a9
commit e1b5e0ae6b
9 changed files with 402 additions and 4 deletions
+2 -1
View File
@@ -47,7 +47,8 @@ FGOutput=auto
; Nukems - requires dlssg_to_fsr3_amd_is_better.dll, FSR 3 FG
; Arturs - requires dlss-enabler-headless.dll, FSR 3 MFG
; FFX - requires amd_fidelityfx_dx12.dll OR amd_fidelityfx_loader_dx12.dll + amd_fidelityfx_framegeneration_dx12.dll, FSRFG 3 or 4
; None, Nukems, Arturs, FFX - Default (auto) is None
; Combo - requires
; None, Nukems, Arturs, FFX, Combo - Default (auto) is None
FGNvngxReplacement=auto
; Frametime source, would affect frame pacing
+4
View File
@@ -115,6 +115,8 @@ bool Config::Reload(std::filesystem::path iniPath)
FGNvngxReplacement.set_from_config(FGNvngxReplacement::Arturs);
else if (lstrcmpiA(FGNvngxReplacementString.value().c_str(), "ffx") == 0)
FGNvngxReplacement.set_from_config(FGNvngxReplacement::FFX);
else if (lstrcmpiA(FGNvngxReplacementString.value().c_str(), "combo") == 0)
FGNvngxReplacement.set_from_config(FGNvngxReplacement::Combo);
}
if (auto forceXell = readBool("fakenvapi", "ForceXeLL"); forceXell.has_value() && forceXell.value())
@@ -908,6 +910,8 @@ bool Config::SaveIni()
FGNvngxReplacementString = "Arturs";
else if (FGNvngxReplacementHeld.value() == FGNvngxReplacement::FFX)
FGNvngxReplacementString = "FFX";
else if (FGNvngxReplacementHeld.value() == FGNvngxReplacement::Combo)
FGNvngxReplacementString = "Combo";
}
ini.SetValue("FrameGen", "FGNvngxReplacement", FGNvngxReplacementString.c_str());
+2
View File
@@ -303,6 +303,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
</PreBuildEvent>
</ItemDefinitionGroup>
<ItemGroup>
<ClInclude Include="framegen\nvngx\Nvngx_Combo.h" />
<ClInclude Include="fsr4\FSR4Upgrade.h" />
<ClInclude Include="hooks\Amdxc64_Hooks.h" />
<ClInclude Include="DllNames.h" />
@@ -593,6 +594,7 @@ copy NUL "$(SolutionDir)x64\Release\a\!! EXTRACT ALL FILES TO GAME FOLDER !!" /Y
<ClInclude Include="NVNGX_Parameter.h" />
</ItemGroup>
<ItemGroup>
<ClCompile Include="framegen\nvngx\Nvngx_Combo.cpp" />
<ClCompile Include="fsr4\FSR4Upgrade.cpp" />
<ClCompile Include="hooks\Amdxc64_Hooks.cpp" />
<ClCompile Include="framegen\dlssg\DLSSG_Dx12.cpp" />
+6
View File
@@ -932,6 +932,9 @@
<ClInclude Include="framegen\nvngx\Nvngx_Arturs.h">
<Filter>Header Files</Filter>
</ClInclude>
<ClInclude Include="framegen\nvngx\Nvngx_Combo.h">
<Filter>Header Files</Filter>
</ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="Config.cpp">
@@ -1496,6 +1499,9 @@
<ClCompile Include="framegen\nvngx\Nvngx_Arturs.cpp">
<Filter>Source Files</Filter>
</ClCompile>
<ClCompile Include="framegen\nvngx\Nvngx_Combo.cpp">
<Filter>Source Files</Filter>
</ClCompile>
</ItemGroup>
<ItemGroup>
<ResourceCompile Include="OptiScaler.rc" />
+1
View File
@@ -54,6 +54,7 @@ enum class FGNvngxReplacement : uint32_t
Nukems,
Arturs,
FFX,
Combo,
};
enum class WorkingMode : uint32_t
+288
View File
@@ -0,0 +1,288 @@
#include "pch.h"
#include "Nvngx_Combo.h"
#include <NVNGX_Parameter.h>
#include "proxies/NVNGX_Proxy.h"
#include "proxies/Ntdll_Proxy.h"
#include <proxies/FfxApi_Proxy.h>
#include <numbers>
#include <misc/IdentifyGpu.h>
using Microsoft::WRL::ComPtr;
NVSDK_NGX_Result Nvngx_Combo::D3D12_Init(unsigned long long InApplicationId, const wchar_t* InApplicationDataPath,
ID3D12Device* InDevice, const NVSDK_NGX_FeatureCommonInfo* InFeatureInfo,
NVSDK_NGX_Version InSDKVersion)
{
return D3D12_Init_Ext(InApplicationId, InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo);
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_Init_Ext(unsigned long long InApplicationId, const wchar_t* InApplicationDataPath,
ID3D12Device* InDevice, NVSDK_NGX_Version InSDKVersion,
const NVSDK_NGX_FeatureCommonInfo* InFeatureInfo)
{
auto resultArturs =
artursProvider->D3D12_Init_Ext(InApplicationId, InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo);
auto resultFfx =
ffxProvider->D3D12_Init_Ext(InApplicationId, InApplicationDataPath, InDevice, InSDKVersion, InFeatureInfo);
if (resultArturs != NVSDK_NGX_Result_Success || resultFfx != NVSDK_NGX_Result_Success)
{
return NVSDK_NGX_Result_Fail;
}
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_Shutdown()
{
auto resultArturs = artursProvider->D3D12_Shutdown();
auto resultFfx = ffxProvider->D3D12_Shutdown();
if (resultArturs != NVSDK_NGX_Result_Success || resultFfx != NVSDK_NGX_Result_Success)
{
return NVSDK_NGX_Result_Fail;
}
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_Shutdown1(ID3D12Device* InDevice)
{
auto resultArturs = artursProvider->D3D12_Shutdown1(InDevice);
auto resultFfx = ffxProvider->D3D12_Shutdown1(InDevice);
if (resultArturs != NVSDK_NGX_Result_Success || resultFfx != NVSDK_NGX_Result_Success)
{
return NVSDK_NGX_Result_Fail;
}
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_GetScratchBufferSize(NVSDK_NGX_Feature InFeatureId,
const NVSDK_NGX_Parameter* InParameters,
size_t* OutSizeInBytes)
{
if (!OutSizeInBytes)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
*OutSizeInBytes = 0;
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_CreateFeature(ID3D12GraphicsCommandList* InCmdList, NVSDK_NGX_Feature InFeatureID,
NVSDK_NGX_Parameter* InParameters, NVSDK_NGX_Handle** OutHandle)
{
Nvngx_Combo_Handle** OutOurHandle = (Nvngx_Combo_Handle**) OutHandle;
if (!OutOurHandle || !InParameters)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
if (InFeatureID != NVSDK_NGX_Feature_FrameGeneration)
return NVSDK_NGX_Result_FAIL_FeatureNotSupported;
*OutOurHandle = new Nvngx_Combo_Handle(lastIdCreated++ + NVNGX_PROVIDER_ID_OFFSET, nullptr);
auto resultArturs =
artursProvider->D3D12_CreateFeature(InCmdList, InFeatureID, InParameters, &(*OutOurHandle)->artursHandle);
auto resultFfx =
ffxProvider->D3D12_CreateFeature(InCmdList, InFeatureID, InParameters, &(*OutOurHandle)->ffxHandle);
if (resultArturs != NVSDK_NGX_Result_Success || resultFfx != NVSDK_NGX_Result_Success)
{
if (resultArturs == NVSDK_NGX_Result_Success)
artursProvider->D3D12_ReleaseFeature((*OutOurHandle)->artursHandle);
if (resultFfx == NVSDK_NGX_Result_Success)
ffxProvider->D3D12_ReleaseFeature((*OutOurHandle)->ffxHandle);
delete *OutOurHandle;
*OutOurHandle = nullptr;
return NVSDK_NGX_Result_Fail;
}
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_ReleaseFeature(NVSDK_NGX_Handle* InHandle)
{
Nvngx_Combo_Handle* InOurHandle = (Nvngx_Combo_Handle*) InHandle;
if (!InOurHandle || InOurHandle->Id < NVNGX_PROVIDER_ID_OFFSET)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
auto resultArturs = artursProvider->D3D12_ReleaseFeature(InOurHandle->artursHandle);
auto resultFfx = ffxProvider->D3D12_ReleaseFeature(InOurHandle->ffxHandle);
delete InOurHandle;
if (resultArturs != NVSDK_NGX_Result_Success || resultFfx != NVSDK_NGX_Result_Success)
{
return NVSDK_NGX_Result_Fail;
}
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_GetFeatureRequirements(IDXGIAdapter* Adapter,
const NVSDK_NGX_FeatureDiscoveryInfo* FeatureDiscoveryInfo,
NVSDK_NGX_FeatureRequirement* OutSupported)
{
if (!OutSupported)
return NVSDK_NGX_Result_Fail;
OutSupported->FeatureSupported = NVSDK_NGX_FeatureSupportResult_Supported;
OutSupported->MinHWArchitecture = 0x0;
strcpy_s(OutSupported->MinOSVersion, "10.0.0");
return NVSDK_NGX_Result_Success;
}
static D3D12_RESOURCE_STATES GetD3D12State(FfxApiResourceState state)
{
switch (state)
{
case FFX_API_RESOURCE_STATE_COMMON:
return D3D12_RESOURCE_STATE_COMMON;
case FFX_API_RESOURCE_STATE_UNORDERED_ACCESS:
return D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
case FFX_API_RESOURCE_STATE_COMPUTE_READ:
return D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE;
case FFX_API_RESOURCE_STATE_PIXEL_READ:
return D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE;
case FFX_API_RESOURCE_STATE_PIXEL_COMPUTE_READ:
return (D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
case FFX_API_RESOURCE_STATE_COPY_SRC:
return D3D12_RESOURCE_STATE_COPY_SOURCE;
case FFX_API_RESOURCE_STATE_COPY_DEST:
return D3D12_RESOURCE_STATE_COPY_DEST;
case FFX_API_RESOURCE_STATE_GENERIC_READ:
return D3D12_RESOURCE_STATE_GENERIC_READ;
case FFX_API_RESOURCE_STATE_INDIRECT_ARGUMENT:
return D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
case FFX_API_RESOURCE_STATE_RENDER_TARGET:
return D3D12_RESOURCE_STATE_RENDER_TARGET;
default:
return D3D12_RESOURCE_STATE_COMMON;
}
}
static void CopyTexture(ID3D12GraphicsCommandList* CommandList, const FfxApiResource* Destination,
const FfxApiResource* Source)
{
const auto cmdList12 = reinterpret_cast<ID3D12GraphicsCommandList*>(CommandList);
D3D12_RESOURCE_BARRIER barriers[2] = {};
barriers[0].Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
barriers[0].Flags = D3D12_RESOURCE_BARRIER_FLAG_NONE;
barriers[0].Transition.pResource = static_cast<ID3D12Resource*>(Destination->resource); // Destination
barriers[0].Transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
barriers[0].Transition.StateBefore = GetD3D12State((FfxApiResourceState) Destination->state);
barriers[0].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_DEST;
barriers[1] = barriers[0];
barriers[1].Transition.pResource = static_cast<ID3D12Resource*>(Source->resource); // Source
barriers[1].Transition.StateBefore = GetD3D12State((FfxApiResourceState) Source->state);
barriers[1].Transition.StateAfter = D3D12_RESOURCE_STATE_COPY_SOURCE;
cmdList12->ResourceBarrier(2, barriers);
cmdList12->CopyResource(barriers[0].Transition.pResource, barriers[1].Transition.pResource);
std::swap(barriers[0].Transition.StateBefore, barriers[0].Transition.StateAfter);
std::swap(barriers[1].Transition.StateBefore, barriers[1].Transition.StateAfter);
cmdList12->ResourceBarrier(2, barriers);
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_EvaluateFeature(ID3D12GraphicsCommandList* InCmdList,
const NVSDK_NGX_Handle* InFeatureHandle,
NVSDK_NGX_Parameter* InParameters,
PFN_NVSDK_NGX_ProgressCallback InCallback)
{
// This is our handle so can cast away const
Nvngx_Combo_Handle* InOurHandle = (Nvngx_Combo_Handle*) InFeatureHandle;
if (!InParameters || !InOurHandle || InOurHandle->Id < NVNGX_PROVIDER_ID_OFFSET)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
// Assuming ffx can only do one fake frame
uint32_t frameIndex = 1;
InParameters->Get("DLSSG.MultiFrameIndex", &frameIndex);
uint32_t frameCount = 0;
InParameters->Get("DLSSG.MultiFrameCount", &frameCount);
NVSDK_NGX_Result result {};
if ((frameCount == 1 && frameIndex == 1) || (frameCount == 3 && frameIndex == 2) ||
(frameCount == 5 && frameIndex == 3))
{
// Make the provider think we only want 2x to it generates the middle frame
InParameters->Set("DLSSG.MultiFrameIndex", 1);
InParameters->Set("DLSSG.MultiFrameCount", 1);
result = ffxProvider->D3D12_EvaluateFeature(InCmdList, InOurHandle->ffxHandle, InParameters, InCallback);
// Restore
InParameters->Set("DLSSG.MultiFrameIndex", frameIndex);
InParameters->Set("DLSSG.MultiFrameCount", frameCount);
}
else
{
result = artursProvider->D3D12_EvaluateFeature(InCmdList, InOurHandle->artursHandle, InParameters, InCallback);
}
ID3D12Resource* outputReal = nullptr;
InParameters->Get("DLSSG.OutputReal", &outputReal);
auto outputRealFfx = ffxApiGetResourceDX12(outputReal, FFX_API_RESOURCE_STATE_UNORDERED_ACCESS);
// Copy backbuffer to output real
if (outputReal)
{
ID3D12Resource* backbuffer = nullptr;
InParameters->Get("DLSSG.Backbuffer", &backbuffer);
auto backbufferFfx = ffxApiGetResourceDX12(backbuffer, FFX_API_RESOURCE_STATE_COMPUTE_READ);
if (backbuffer)
CopyTexture(InCmdList, &outputRealFfx, &backbufferFfx);
}
return result;
}
static NVSDK_NGX_Result GetCurrentSettingsCallback(Nvngx_Combo_Handle* InHandle, NVSDK_NGX_Parameter* InParameters)
{
if (!InHandle || !InParameters)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
InParameters->Set("DLSSG.MustCallEval", 1);
InParameters->Set("DLSSG.BurstCaptureRunning", 0);
return NVSDK_NGX_Result_Success;
}
static NVSDK_NGX_Result EstimateVRAMCallback(uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t, uint32_t,
uint32_t, uint32_t, size_t* EstimatedSize)
{
// Assume 300MB
if (EstimatedSize)
*EstimatedSize = 300 * 1024 * 1024;
return NVSDK_NGX_Result_Success;
}
NVSDK_NGX_Result Nvngx_Combo::D3D12_PopulateParameters_Impl(NVSDK_NGX_Parameter* InParameters)
{
if (!InParameters)
return NVSDK_NGX_Result_FAIL_InvalidParameter;
InParameters->Set("DLSSG.GetCurrentSettingsCallback", &GetCurrentSettingsCallback);
InParameters->Set("DLSSG.EstimateVRAMCallback", &EstimateVRAMCallback);
// if (inited) // Query FFX
InParameters->Set("DLSSG.MultiFrameCountMax", 1);
return NVSDK_NGX_Result_Success;
}
+69
View File
@@ -0,0 +1,69 @@
#pragma once
#include "IFGNvngx.h"
#include "Nvngx_FFX.h"
#include "Nvngx_Arturs.h"
#include <d3d12.h>
struct Nvngx_Combo_Handle
{
unsigned int Id;
NVSDK_NGX_Handle* ffxHandle = nullptr;
NVSDK_NGX_Handle* artursHandle = nullptr;
};
class Nvngx_Combo : public IFGNvngx
{
private:
std::atomic_uint32_t lastIdCreated = 0;
std::unique_ptr<Nvngx_Arturs> artursProvider = nullptr;
std::unique_ptr<Nvngx_FFX> ffxProvider = nullptr;
public:
Nvngx_Combo()
{
artursProvider = std::make_unique<Nvngx_Arturs>();
ffxProvider = std::make_unique<Nvngx_FFX>();
}
bool isDx12Available() override final
{
return artursProvider->isDx12Available() && ffxProvider->isDx12Available();
};
bool isVulkanAvailable() override final { return false; };
// DX12
NVSDK_NGX_Result D3D12_Init(unsigned long long InApplicationId, const wchar_t* InApplicationDataPath,
ID3D12Device* InDevice, const NVSDK_NGX_FeatureCommonInfo* InFeatureInfo,
NVSDK_NGX_Version InSDKVersion) override;
NVSDK_NGX_Result D3D12_Init_Ext(unsigned long long InApplicationId, const wchar_t* InApplicationDataPath,
ID3D12Device* InDevice, NVSDK_NGX_Version InSDKVersion,
const NVSDK_NGX_FeatureCommonInfo* InFeatureInfo) override;
NVSDK_NGX_Result D3D12_Shutdown() override;
NVSDK_NGX_Result D3D12_Shutdown1(ID3D12Device* InDevice) override;
NVSDK_NGX_Result D3D12_GetScratchBufferSize(NVSDK_NGX_Feature InFeatureId, const NVSDK_NGX_Parameter* InParameters,
size_t* OutSizeInBytes) override;
NVSDK_NGX_Result D3D12_CreateFeature(ID3D12GraphicsCommandList* InCmdList, NVSDK_NGX_Feature InFeatureID,
NVSDK_NGX_Parameter* InParameters, NVSDK_NGX_Handle** OutHandle) override;
NVSDK_NGX_Result D3D12_ReleaseFeature(NVSDK_NGX_Handle* InHandle) override;
NVSDK_NGX_Result D3D12_GetFeatureRequirements(IDXGIAdapter* Adapter,
const NVSDK_NGX_FeatureDiscoveryInfo* FeatureDiscoveryInfo,
NVSDK_NGX_FeatureRequirement* OutSupported) override;
NVSDK_NGX_Result D3D12_EvaluateFeature(ID3D12GraphicsCommandList* InCmdList,
const NVSDK_NGX_Handle* InFeatureHandle, NVSDK_NGX_Parameter* InParameters,
PFN_NVSDK_NGX_ProgressCallback InCallback) override;
NVSDK_NGX_Result D3D12_PopulateParameters_Impl(NVSDK_NGX_Parameter* InParameters) override;
int getMaxFakeFramesCount() override { return 5; }
FGNvngxReplacement getType() override { return FGNvngxReplacement::Combo; }
};
+8
View File
@@ -10,6 +10,7 @@
#include "Nvngx_Nukems.h"
#include "Nvngx_Arturs.h"
#include "Nvngx_FFX.h"
#include "Nvngx_Combo.h"
#include <imgui/ImGuiNotify.hpp>
IFGNvngx* Nvngx_FG::getProvider()
@@ -33,6 +34,10 @@ IFGNvngx* Nvngx_FG::getProvider()
_provider = std::make_unique<Nvngx_Arturs>();
break;
case FGNvngxReplacement::Combo:
_provider = std::make_unique<Nvngx_Combo>();
break;
case FGNvngxReplacement::None:
default:
return nullptr;
@@ -41,6 +46,7 @@ IFGNvngx* Nvngx_FG::getProvider()
if (!_provider->isDx12Available() && !_provider->isVulkanAvailable())
{
// The selected provider cannot be used, try the remaining providers as fallback, try in order
// FGNvngxReplacement::Combo doesn't make sense to try as it's Arturs + FFX
const FGNvngxReplacement fallbacks[] = {
FGNvngxReplacement::Arturs,
FGNvngxReplacement::FFX,
@@ -57,6 +63,8 @@ IFGNvngx* Nvngx_FG::getProvider()
return "Nvngx FFX";
case FGNvngxReplacement::Nukems:
return "Nukems";
case FGNvngxReplacement::Combo:
return "Combo";
case FGNvngxReplacement::None:
default:
return "???";
+22 -3
View File
@@ -1783,6 +1783,10 @@ void MenuCommon::RenderPerformanceOverlay(RenderMenuContext& ctx)
{
fgText = formatFg("FFX", Nvngx_FG::getMaxFakeFramesCount());
}
else if (activeNvngxFg == FGNvngxReplacement::Combo)
{
fgText = formatFg("Combo", Nvngx_FG::getMaxFakeFramesCount());
}
const auto overlayType = config->FpsOverlayType.value_or_default();
const bool hasFeature = currentFeature && !currentFeature->IsFrozen();
@@ -3107,6 +3111,8 @@ void MenuCommon::RenderFrameGenerationSelection(RenderMenuContext& ctx)
{ FGNvngxReplacement::Nukems, "Nukem's", "FSR 3 FG" },
{ FGNvngxReplacement::Arturs, "Enabler", "FSR 3 MFG" },
{ FGNvngxReplacement::FFX, "FSR 3/4 FG", "FSR 3/4 FG using the FFX" },
{ FGNvngxReplacement::Combo, "FFX + Enabler", "FFX for the middle fake frame, Enabler for the rest\n\n"
"2x - FFX\n3x - Enabler\n4x - FFX + Enabler\n5x - Enabler\n6x - FFX + Enabler" },
};
// clang-format on
@@ -3144,6 +3150,11 @@ void MenuCommon::RenderFrameGenerationSelection(RenderMenuContext& ctx)
nvngxOptions[fgNvngxFfxIndex].set_disabled(!FfxApiProxy::IsFGReady(false),
"Missing amd_fidelityfx_framegeneration_dx12.dll");
auto constexpr fgNvngxComboIndex = (uint32_t) FGNvngxReplacement::Combo;
nvngxOptions[fgNvngxComboIndex].set_disabled(
!FfxApiProxy::IsFGReady(false) || !state.artursFgFileAvailable,
"Missing amd_fidelityfx_framegeneration_dx12.dll\nor missing dlss-enabler-headless.dll");
// TODO: Automatically switch to any other option
if (!config->FGNvngxReplacement.has_value())
@@ -4383,6 +4394,12 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx)
{
SeparatorWithHelpMarker("Frame Generation (FSRFG via FFX)", "FFX using the DLSSG swapchain");
}
else if (activeNvngxFg == FGNvngxReplacement::Combo)
{
SeparatorWithHelpMarker("Frame Generation (Enabler + FFX)",
"FFX for middle fake frames, and Enabler for the rest\n\n2x - FFX\n"
"3x - Enabler\n4x - FFX + Enabler\n5x - Enabler\n6x - FFX + Enabler");
}
if (state.activeFgInput == FGInput::NvngxFG)
{
@@ -4448,7 +4465,7 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx)
if (isLoaded)
{
if (activeNvngxFg == FGNvngxReplacement::Arturs)
if (activeNvngxFg == FGNvngxReplacement::Arturs || activeNvngxFg == FGNvngxReplacement::Combo)
{
if (bool showDebug = config->NvngxFGShowDebug.value_or_default();
ImGui::Checkbox("Show Debug", &showDebug))
@@ -4503,7 +4520,8 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx)
config->NvngxFGDispatchFlags = temp_flags;
}
}
else if (activeNvngxFg == FGNvngxReplacement::Nukems)
if (activeNvngxFg == FGNvngxReplacement::Nukems)
{
if (ImGui::Checkbox("Enable Debug View", &state.dlssgDebugView))
{
@@ -4514,7 +4532,8 @@ void MenuCommon::RenderFrameGenerationRuntimeSettings(RenderMenuContext& ctx)
Nvngx_FG::setInterpolatedOnly(state.dlssgInterpolatedOnly);
}
}
else if (activeNvngxFg == FGNvngxReplacement::FFX)
if (activeNvngxFg == FGNvngxReplacement::FFX || activeNvngxFg == FGNvngxReplacement::Combo)
{
if (_ffxFGIndex < 0)
_ffxFGIndex = config->FfxFGIndex.value_or_default();