Skip to content

Commit 771ea88

Browse files
committed
FSR 3.1 Draft #1
-Adds FSR 3.1 Upscaling -Adds early FSR 3.1 Framegen -Adds FidelityFX-SDK FSR FG QoL fixes DLL Linking Main Menu fix Device Queue fixes FSR 3.1 Fixes and QoL updates Hudless fix -Fixes UI for FSR FG Update rcas.comp.slang FSR FG View Model fix -Removes ghosting from view model
1 parent 1540dcc commit 771ea88

34 files changed

Lines changed: 3123 additions & 47 deletions

.gitmodules

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -42,3 +42,6 @@
4242
[submodule "submodules/xess"]
4343
path = submodules/xess
4444
url = https://github.com/intel/xess.git
45+
[submodule "submodules/FidelityFX-SDK"]
46+
path = submodules/FidelityFX-SDK
47+
url = https://github.com/GPUOpen-LibrariesAndSDKs/FidelityFX-SDK.git

meson.build

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -561,6 +561,8 @@ nrc_dll_path = join_paths(global_src_root_norm, 'submodules/nrc/bin/')
561561

562562
xess_dll_path = join_paths(global_src_root_norm, 'submodules/xess/bin/')
563563

564+
fsr3_dll_path = join_paths(global_src_root_norm, 'submodules/FidelityFX-SDK/PrebuiltSignedDLL/')
565+
564566
nrd_lib_path = join_paths(global_src_root_norm, 'external/nrd/Lib/Release/')
565567
if get_option('buildtype') == 'debug'
566568
nrd_lib_path = join_paths(global_src_root_norm, 'external/nrd/Lib/Debug/')
@@ -750,6 +752,8 @@ if dxvk_is_ninja
750752

751753
install_data(join_paths(xess_dll_path, 'libxess.dll'), install_dir : output_path, install_tag: target_suffix)
752754

755+
install_data(join_paths(fsr3_dll_path, 'amd_fidelityfx_vk.dll'), install_dir : output_path, install_tag: target_suffix)
756+
753757
install_data(join_paths(dlss_lib_path, 'nvngx_dlss.dll'), install_dir : output_path, install_tag: target_suffix)
754758
install_data(join_paths(dlss_lib_path, 'nvngx_dlssd.dll'), install_dir : output_path, install_tag: target_suffix)
755759

src/d3d9/d3d9_swapchain.cpp

Lines changed: 88 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@
3636
#include "../dxvk/rtx_render/rtx_dlfg.h"
3737
// NV-DXVK end
3838

39+
// NV-DXVK start: FSR FG integration
40+
#include "../dxvk/rtx_render/rtx_fsr_framegen.h"
41+
// NV-DXVK end
42+
3943
namespace dxvk {
4044
// NV-DXVK start: App Controlled FSE
4145
enum FSEState {
@@ -406,22 +410,41 @@ namespace dxvk {
406410
if (m_dlfgPresenter == nullptr) {
407411
return true;
408412
}
413+
} else if (m_context->isFSRFGEnabled()) {
414+
// FSR FG is enabled - need FSR FG presenter
415+
if (m_fsrfgPresenter == nullptr) {
416+
Logger::info("NeedRecreatePresenter: FSR FG enabled but no FSR FG presenter, need to recreate");
417+
return true;
418+
}
419+
} else if (m_fsrfgPresenter != nullptr) {
420+
// FSR FG presenter exists but FSR FG is disabled - recreate a normal presenter
421+
// so present mode / vsync behavior is restored and future FG mode switches are clean.
422+
Logger::info("NeedRecreatePresenter: FSR FG disabled with FSR presenter active, recreating presenter");
423+
return true;
409424
} else {
410425
if (m_presenter == nullptr) {
411426
return true;
412427
}
413428
}
414429

415430
// one must be null, one must be non-null
416-
assert(m_presenter != nullptr || m_dlfgPresenter != nullptr);
417-
assert(m_presenter == nullptr || m_dlfgPresenter == nullptr);
431+
assert(m_presenter != nullptr || m_dlfgPresenter != nullptr || m_fsrfgPresenter != nullptr);
432+
assert((m_presenter == nullptr) + (m_dlfgPresenter == nullptr) + (m_fsrfgPresenter == nullptr) >= 2);
418433
return false;
419434
}
420435

421436
vk::Presenter* D3D9SwapChainEx::GetPresenter() const {
422-
const auto presenter = m_presenter != nullptr ? m_presenter.ptr() : m_dlfgPresenter.ptr();
437+
// NV-DXVK: Return whichever presenter is active (normal, DLFG, or FSR FG)
438+
vk::Presenter* presenter = nullptr;
439+
if (m_presenter != nullptr) {
440+
presenter = m_presenter.ptr();
441+
} else if (m_dlfgPresenter != nullptr) {
442+
presenter = m_dlfgPresenter.ptr();
443+
} else if (m_fsrfgPresenter != nullptr) {
444+
presenter = m_fsrfgPresenter.ptr();
445+
}
423446

424-
// Note: The returned presenter must be non-null as one of the two presenters must be non-null at all times,
447+
// Note: The returned presenter must be non-null as one of the presenters must be non-null at all times,
425448
// and because code will blindly dereference this returned pointer.
426449
assert(presenter != nullptr);
427450

@@ -1350,13 +1373,40 @@ namespace dxvk {
13501373

13511374
m_device->waitForIdle();
13521375

1376+
// NV-DXVK start: FSR FG - Capture surface before destroying old presenter
1377+
// This allows reusing the surface when switching presenter types at runtime
1378+
// We call releaseSurface() which sets the presenter's internal handle to VK_NULL_HANDLE
1379+
// so the destructor won't destroy it - we transfer ownership to the new presenter
1380+
VkSurfaceKHR existingSurface = VK_NULL_HANDLE;
1381+
vk::Presenter* currentPresenter = GetPresenter();
1382+
if (currentPresenter != nullptr) {
1383+
existingSurface = currentPresenter->releaseSurface();
1384+
Logger::info("CreatePresenter: Captured existing surface for reuse");
1385+
}
1386+
// NV-DXVK end
1387+
1388+
// Destroy old presenters - must happen before creating new ones
1389+
// to release the swapchain (but surface ownership transferred above)
13531390
m_presenter = nullptr;
13541391
m_presentStatus.result = VK_SUCCESS;
13551392

1356-
// NV-DXVK start: DLFG integration
1393+
// NV-DXVK start: DLFG/FSR FG integration
13571394
m_dlfgPresenter = nullptr;
1395+
m_fsrfgPresenter = nullptr;
1396+
1397+
// Wait after destroying presenters to ensure swapchains are fully released
1398+
m_device->waitForIdle();
1399+
13581400
const bool dlfgEnabled = m_context->isDLFGEnabled();
1359-
DxvkDeviceQueue presentQueue = dlfgEnabled ? m_device->queues().present : m_device->queues().graphics;
1401+
const bool fsrfgEnabled = m_context->isFSRFGEnabled();
1402+
// Only create FSR FG presenter if enabled - runtime enabling requires restart
1403+
// This avoids VK_ERROR_NATIVE_WINDOW_IN_USE_KHR crash at startup
1404+
const bool fsrfgSupported = DxvkFSRFrameGen::supportsFSRFrameGen();
1405+
const bool createFsrfgPresenter = fsrfgEnabled && fsrfgSupported && !dlfgEnabled;
1406+
Logger::info(str::format("CreatePresenter: dlfgEnabled=", dlfgEnabled,
1407+
", fsrfgEnabled=", fsrfgEnabled, ", fsrfgSupported=", fsrfgSupported,
1408+
", createFsrfgPresenter=", createFsrfgPresenter));
1409+
DxvkDeviceQueue presentQueue = (dlfgEnabled || createFsrfgPresenter) ? m_device->queues().present : m_device->queues().graphics;
13601410

13611411
vk::PresenterDevice presenterDevice;
13621412
presenterDevice.queueFamily = presentQueue.queueFamily;
@@ -1372,19 +1422,50 @@ namespace dxvk {
13721422
presenterDesc.numPresentModes = PickPresentModes(false, presenterDesc.presentModes);
13731423
presenterDesc.fullScreenExclusive = PickFullscreenMode();
13741424

1375-
// NV-DXVK start: DLFG integration
1425+
// NV-DXVK start: DLFG/FSR FG integration
13761426
if (dlfgEnabled) {
13771427
// DLFG presents 2 times (1 more frame) in each real frame,
13781428
// increase image count by 1 to avoid resource waiting.
13791429
presenterDesc.imageCount++;
1430+
// TODO: DLFG presenter may also need surface reuse support
1431+
// For now, destroy the existing surface if we're creating DLFG presenter
1432+
if (existingSurface != VK_NULL_HANDLE) {
1433+
m_device->adapter()->vki()->vkDestroySurfaceKHR(
1434+
m_device->adapter()->vki()->instance(), existingSurface, nullptr);
1435+
existingSurface = VK_NULL_HANDLE;
1436+
}
13801437
m_dlfgPresenter = new DxvkDLFGPresenter(m_device,
13811438
m_context,
13821439
m_window,
13831440
m_device->adapter()->vki(),
13841441
m_device->vkd(),
13851442
presenterDevice,
13861443
presenterDesc);
1444+
} else if (createFsrfgPresenter) {
1445+
// Create FSR FG presenter when enabled
1446+
// FFX contexts are created lazily in presentImage() when ready
1447+
// Pass the existing surface to avoid VK_ERROR_NATIVE_WINDOW_IN_USE_KHR
1448+
presenterDesc.imageCount++;
1449+
m_fsrfgPresenter = new DxvkFSRFGPresenter(m_device,
1450+
m_context,
1451+
m_window,
1452+
m_device->adapter()->vki(),
1453+
m_device->vkd(),
1454+
presenterDevice,
1455+
presenterDesc,
1456+
existingSurface);
1457+
existingSurface = VK_NULL_HANDLE; // Ownership transferred
1458+
Logger::info(str::format("FSR FG: Created FSR FG Presenter for swapchain ",
1459+
presenterDesc.imageExtent.width, "x", presenterDesc.imageExtent.height,
1460+
" (frame generation ", fsrfgEnabled ? "enabled" : "disabled, can be enabled at runtime", ")"));
13871461
} else {
1462+
// Regular presenter - needs to destroy existing surface and create new one
1463+
// TODO: Could also add surface reuse to regular presenter
1464+
if (existingSurface != VK_NULL_HANDLE) {
1465+
m_device->adapter()->vki()->vkDestroySurfaceKHR(
1466+
m_device->adapter()->vki()->instance(), existingSurface, nullptr);
1467+
existingSurface = VK_NULL_HANDLE;
1468+
}
13881469
m_presenter = new vk::Presenter(m_window,
13891470
m_device->adapter()->vki(),
13901471
m_device->vkd(),

src/d3d9/d3d9_swapchain.h

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,7 @@
3939
namespace dxvk {
4040

4141
class D3D9Surface;
42+
class DxvkFSRFGPresenter;
4243

4344
using D3D9SwapChainExBase = D3D9DeviceChild<IDirect3DSwapChain9Ex>;
4445
class D3D9SwapChainEx : public D3D9SwapChainExBase {
@@ -150,6 +151,10 @@ namespace dxvk {
150151
Rc <DxvkDLFGPresenter> m_dlfgPresenter;
151152
// NV-DXVK end
152153

154+
// NV-DXVK begin: FSR FG integration
155+
Rc <DxvkFSRFGPresenter> m_fsrfgPresenter;
156+
// NV-DXVK end
157+
153158
Rc<hud::Hud> m_hud;
154159

155160
std::vector<Com<D3D9Surface, false>> m_backBuffers;

src/d3d9/meson.build

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,17 @@ d3d9_dll = shared_library('d3d9', d3d9_src, dxvk_version, glsl_generator.process
106106
install: true
107107
)
108108

109+
# Copy FSR3 DLL to d3d9 output directory for runtime loading
110+
fsr3_dll_src = join_paths(global_src_root_norm, 'submodules/FidelityFX-SDK/PrebuiltSignedDLL/amd_fidelityfx_vk.dll')
111+
fsr3_dll_build = custom_target('copy_fsr3_dll',
112+
output: 'amd_fidelityfx_vk.dll',
113+
input: fsr3_dll_src,
114+
command: [python_interpreter, '-c', 'import shutil; import sys; shutil.copy(sys.argv[1], sys.argv[2])', '@INPUT@', '@OUTPUT@'],
115+
build_by_default: true,
116+
install: true,
117+
install_dir: get_option('bindir')
118+
)
119+
109120
d3d9_dep = declare_dependency(
110121
link_with : [ d3d9_dll ],
111122
include_directories : [ dxvk_include_path ])

src/dxvk/dxvk_adapter.cpp

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -175,6 +175,28 @@ namespace dxvk {
175175
}
176176
// NV_DXVK end
177177

178+
// NV-DXVK start: FSR FG integration
179+
// Image acquire queue for FSR3 Frame Interpolation - can use any queue family
180+
// Prefer a queue from a family with transfer support for efficiency
181+
uint32_t imageAcquireQueue = findQueueFamily(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT,
182+
VK_QUEUE_TRANSFER_BIT);
183+
if (imageAcquireQueue == VK_QUEUE_FAMILY_IGNORED) {
184+
// Fall back to compute+transfer if no dedicated transfer queue
185+
imageAcquireQueue = findQueueFamily(VK_QUEUE_GRAPHICS_BIT | VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT,
186+
VK_QUEUE_COMPUTE_BIT | VK_QUEUE_TRANSFER_BIT);
187+
}
188+
189+
if (imageAcquireQueue != VK_QUEUE_FAMILY_IGNORED) {
190+
queues.imageAcquire = imageAcquireQueue;
191+
}
192+
193+
// Prefer a dedicated FSR present queue when available.
194+
// Surface support gets validated later during swapchain creation where a surface is available.
195+
if (presentQueue != VK_QUEUE_FAMILY_IGNORED && presentQueue != graphicsQueue) {
196+
queues.fsrPresent = presentQueue;
197+
}
198+
// NV-DXVK end
199+
178200
return queues;
179201
}
180202

@@ -453,6 +475,11 @@ namespace dxvk {
453475
extensionsEnabled);
454476
// NV-DXVK end
455477

478+
// NV-DXVK start: Add memory requirements extension for Remix
479+
if (m_deviceExtensions.supports(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME))
480+
extensionsEnabled.add(VK_KHR_GET_MEMORY_REQUIREMENTS_2_EXTENSION_NAME);
481+
// NV-DXVK end
482+
456483
// Enable additional extensions if necessary
457484
extensionsEnabled.merge(m_extraExtensions);
458485
DxvkNameList extensionNameList = extensionsEnabled.toNameList();
@@ -758,6 +785,16 @@ namespace dxvk {
758785
handleQueueFamily(queueFamilies.present, queueInfos.present);
759786
}
760787

788+
// NV-DXVK start: FSR FG integration
789+
if (queueFamilies.imageAcquire != VK_QUEUE_FAMILY_IGNORED) {
790+
handleQueueFamily(queueFamilies.imageAcquire, queueInfos.imageAcquire);
791+
}
792+
793+
if (queueFamilies.fsrPresent != VK_QUEUE_FAMILY_IGNORED) {
794+
handleQueueFamily(queueFamilies.fsrPresent, queueInfos.fsrPresent);
795+
}
796+
// NV-DXVK end
797+
761798
// Create the requested queues
762799

763800
std::vector<VkDeviceQueueCreateInfo> queueCreateInfos{};

src/dxvk/dxvk_adapter.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -80,6 +80,10 @@ namespace dxvk {
8080
// NV-DXVK start: DLFG integration
8181
uint32_t present = VK_QUEUE_FAMILY_IGNORED;
8282
// NV-DXVK end
83+
// NV-DXVK start: FSR FG integration
84+
uint32_t imageAcquire = VK_QUEUE_FAMILY_IGNORED;
85+
uint32_t fsrPresent = VK_QUEUE_FAMILY_IGNORED; // Present queue for FSR FG (must support presentation)
86+
// NV-DXVK end
8387
};
8488

8589
// NV-DXVK begin: General Queue Allocation Improvements
@@ -100,6 +104,10 @@ namespace dxvk {
100104
DxvkAdapterQueueInfo transfer{};
101105
std::optional<DxvkAdapterQueueInfo> asyncCompute{};
102106
std::optional<DxvkAdapterQueueInfo> present{};
107+
// NV-DXVK start: FSR FG integration
108+
std::optional<DxvkAdapterQueueInfo> imageAcquire{};
109+
std::optional<DxvkAdapterQueueInfo> fsrPresent{}; // Present queue for FSR FG (must support presentation)
110+
// NV-DXVK end
103111
};
104112
// NV-DXVK end
105113

src/dxvk/dxvk_context.cpp

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,11 @@
2828
#include "../d3d9/d3d9_state.h"
2929
#include "../d3d9/d3d9_spec_constants.h"
3030

31+
// NV-DXVK start: FSR FG integration
32+
#include "rtx_render/rtx_fsr_framegen.h"
33+
#include "rtx_render/rtx_options.h"
34+
// NV-DXVK end
35+
3136
namespace dxvk {
3237
DxvkContext::DxvkContext(const Rc<DxvkDevice>& device)
3338
: m_device(device),
@@ -70,6 +75,29 @@ namespace dxvk {
7075
}
7176
// NV-DXVK end
7277

78+
// NV-DXVK start: FSR FG integration
79+
bool DxvkContext::isFSRFGEnabled() const {
80+
ScopedCpuProfileZone();
81+
const bool typeIsFSR = RtxOptions::frameGenerationType() == FrameGenerationType::FSR;
82+
const bool enableFlag = DxvkFSRFrameGen::enable();
83+
const bool supported = DxvkFSRFrameGen::supportsFSRFrameGen();
84+
const bool result = typeIsFSR && enableFlag && supported;
85+
86+
// Debug logging - log on first check or when state changes
87+
static bool lastResult = false;
88+
static bool firstCheck = true;
89+
if (firstCheck || result != lastResult) {
90+
Logger::info(str::format("FSR FG: isFSRFGEnabled = ", result,
91+
" (type=", static_cast<int>(RtxOptions::frameGenerationType()),
92+
", enable=", enableFlag, ", supported=", supported, ")"));
93+
firstCheck = false;
94+
lastResult = result;
95+
}
96+
97+
return result;
98+
}
99+
// NV-DXVK end
100+
73101
void DxvkContext::beginRecording(const Rc<DxvkCommandList>& cmdList) {
74102
ScopedCpuProfileZone();
75103
m_cmd = cmdList;

src/dxvk/dxvk_context.h

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -70,6 +70,15 @@ namespace dxvk {
7070
*/
7171
uint32_t dlfgMaxSupportedInterpolatedFrameCount() const;
7272
// NV-DXVK end
73+
74+
// NV-DXVK start: FSR FG integration
75+
/**
76+
* \brief Returns true if FSR Frame Generation is enabled
77+
*
78+
* This is a combination of the setting being enabled and the device supporting it.
79+
*/
80+
bool isFSRFGEnabled() const;
81+
// NV-DXVK end
7382

7483
/**
7584
* \brief Begins command buffer recording

src/dxvk/dxvk_device.cpp

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,6 +31,7 @@
3131
#include "rtx_render/rtx_composite.h"
3232
#include "rtx_render/rtx_debug_view.h"
3333
#include "rtx_render/rtx_xess.h"
34+
#include "rtx_render/rtx_fsr.h"
3435

3536

3637
namespace dxvk {
@@ -67,6 +68,16 @@ namespace dxvk {
6768
m_queues.present = getQueue(adapterQueueInfos.present->queueFamilyIndex, adapterQueueInfos.present->queueIndex);
6869
}
6970

71+
// NV-DXVK start: FSR FG integration
72+
if (adapterQueueInfos.imageAcquire.has_value()) {
73+
m_queues.imageAcquire = getQueue(adapterQueueInfos.imageAcquire->queueFamilyIndex, adapterQueueInfos.imageAcquire->queueIndex);
74+
}
75+
76+
if (adapterQueueInfos.fsrPresent.has_value()) {
77+
m_queues.fsrPresent = getQueue(adapterQueueInfos.fsrPresent->queueFamilyIndex, adapterQueueInfos.fsrPresent->queueIndex);
78+
}
79+
// NV-DXVK end
80+
7081
if (__DLFG_QUEUE_INFO_CHECK(adapterQueueInfos)) {
7182
// Note: When DLFG is active a separate queue is used for out of band rendering/presentation, so it should be marked accordingly.
7283
// Additionally, we do not mark the out of band render queue here as apparently it should only be marked when the out of band rendering
@@ -550,6 +561,9 @@ namespace dxvk {
550561
m_nis(device),
551562
m_taa(device),
552563
m_xess(device),
564+
m_fsr(device),
565+
m_fsrFrameGen(device),
566+
m_rcas(device),
553567
m_composite(device),
554568
m_debug_view(device),
555569
m_autoExposure(device),
@@ -582,5 +596,6 @@ namespace dxvk {
582596
m_rayReconstruction.get().onDestroy();
583597
m_dlss.get().onDestroy();
584598
m_dlfg.get().onDestroy();
599+
m_fsrFrameGen.get().onDestroy();
585600
}
586601
}

0 commit comments

Comments
 (0)