aboutsummaryrefslogtreecommitdiff
path: root/src/video_core/engines
diff options
context:
space:
mode:
authorDuck912 <350528965+Duck912@users.noreply.github.com>2026-11-02 16:12:17 -0500
committerDuck912 <350528965+Duck912@users.noreply.github.com>2026-11-02 16:28:01 -0500
commit11cebf56014a5015db28179a8acc81cad0caf641 (patch)
tree16b9dcba1fb67c1309dac7c12fe7ee5cc6e54791 /src/video_core/engines
parent08dbcc7e3b3df9b594abe1f0cbaf7d30717b21ce (diff)
video_core: replace Fortune's Weave's DLSS with AMD FSR 3.1
Fire Emblem: Fortune's Weave's DLSS network can't run here, and the temporal stand-in is only an approximation of it. FSR 3.1 takes the same inputs DLSS does: the jittered colour, motion vectors, the jitter and the depth, which the game's first DLSS pass reads through cbuf0+0x298 (Z32, reversed, the colour's size). Kepler compute keeps it next to the motion vectors. Fsr3Upscaler ports the host side of the SDK's ffx_fsr3upscaler.cpp: the eight passes (prepare inputs, luma and shading-change pyramids, shading change, reactivity, luma instability, accumulate, RCAS), the internal images and the constants, recorded through the scheduler with the shaders from externals/FidelityFX-SDK. It takes HDR colour, render-size motion vectors and reversed depth, computes its own exposure, and uses the no-wave SPD variants on devices without compute quad operations. The SDK's GLSL uses separate textures and samplers, so DescriptorBankInfo gains sampled image and sampler counts (default 0). FSR's RCAS follows the RCAS Sharpness slider. The result goes to FSR's own RGBA16F image and is drawn into the game's output, which keeps its format. TextureCacheRuntime::TemporalUpscale runs FSR 3.1 first. Without the depth, at mismatched scales, on a device lacking FSR's formats or features, or if its pipelines fail to build, the stand-in runs instead, and the bilinear blit stays last. EDEN_NX2_DLSS_REPLACEMENT=standin forces the stand-in. A gap of more than 250 ms between evaluations restarts the history. The BlitColor overload that takes a raw image view transitions its source to SHADER_READ_ONLY_OPTIMAL but described it as GENERAL in the descriptor (a validation error); it now describes the layout it uses. Assisted-by: Claude Opus 5.5 (debugging, implementation and drafting) Signed-off-by: Duck912 <350528965+Duck912@users.noreply.github.com>
Diffstat (limited to 'src/video_core/engines')
-rw-r--r--src/video_core/engines/kepler_compute.cpp18
-rw-r--r--src/video_core/engines/kepler_compute.h7
2 files changed, 18 insertions, 7 deletions
diff --git a/src/video_core/engines/kepler_compute.cpp b/src/video_core/engines/kepler_compute.cpp
index 4732167369..2d35b603ae 100644
--- a/src/video_core/engines/kepler_compute.cpp
+++ b/src/video_core/engines/kepler_compute.cpp
@@ -591,8 +591,9 @@ void KeplerCompute::DescribeNativeResources(const NativeQmdScheduler::Descriptor
std::atomic<u32> g_nx2_trace_draws_after_upscale{};
// Fire Emblem: Fortune's Weave's first DLSS pass (rejected, before the network) has the input colour
-// at cbuf0+0x278, its motion vectors (R16G16 float, the input's size) at +0x290 and this frame's
-// camera jitter (x, y; input pixels, Halton bases 2 and 3 less 0.5) at +0x1b0.
+// at cbuf0+0x278, its motion vectors (R16G16 float, the input's size) at +0x290, its depth (Z32, the
+// input's size) at +0x298 and this frame's camera jitter (x, y; input pixels, Halton bases 2 and 3
+// less 0.5) at +0x1b0.
void KeplerCompute::RememberDlssMotionVectors() {
const auto& cbuf = launch_description.const_buffer_config[0];
if (cbuf.size.Value() < 0x294) return;
@@ -610,6 +611,14 @@ void KeplerCompute::RememberDlssMotionVectors() {
return;
dlss_motion_vectors = *motion;
dlss_motion_color = color->Address();
+ const auto depth = cbuf.size.Value() >= 0x29c ? image(0x298) : std::nullopt;
+ if (depth && depth->format == Texture::TextureFormat::Z32 && depth->Address() != 0 &&
+ depth->Width() == color->Width() && depth->Height() == color->Height() &&
+ depth->IsBlockLinear()) {
+ dlss_depth = *depth;
+ } else {
+ dlss_depth.reset();
+ }
dlss_jitter = {std::bit_cast<f32>(memory_manager.Read<u32>(cbuf_address + 0x1b0)),
std::bit_cast<f32>(memory_manager.Read<u32>(cbuf_address + 0x1b4))};
}
@@ -770,8 +779,9 @@ bool KeplerCompute::UpscaleInsteadOfNativeProgram() {
return false;
Fermi2D::Surface motion = surface(*dlss_motion_vectors);
motion.format = RTFormat::R16G16_FLOAT;
- return rasterizer->AccelerateTemporalUpscale(source, surface(*output), motion, dlss_jitter,
- {1.0f, 1.0f});
+ return rasterizer->AccelerateTemporalUpscale(source, surface(*output), motion,
+ dlss_depth ? &*dlss_depth : nullptr,
+ dlss_jitter, {1.0f, 1.0f});
};
const bool done = upscale_temporally() ||
rasterizer->AccelerateSurfaceCopy(source, surface(*output), config);
diff --git a/src/video_core/engines/kepler_compute.h b/src/video_core/engines/kepler_compute.h
index b5aee3706a..6a06390294 100644
--- a/src/video_core/engines/kepler_compute.h
+++ b/src/video_core/engines/kepler_compute.h
@@ -269,10 +269,11 @@ private:
void RememberDlssMotionVectors();
bool UpscaleInsteadOfNativeProgram();
- /// Motion vectors of Fire Emblem: Fortune's Weave's DLSS evaluation, from its first pass (run
- /// earlier in the frame than the network pass the stand-in replaces), with the input colour
- /// image and camera jitter they go with.
+ /// Motion vectors and depth of Fire Emblem: Fortune's Weave's DLSS evaluation, from its first
+ /// pass (run earlier in the frame than the network pass the stand-in replaces), with the input
+ /// colour image and camera jitter they go with.
std::optional<Texture::TICEntry> dlss_motion_vectors;
+ std::optional<Texture::TICEntry> dlss_depth;
GPUVAddr dlss_motion_color{};
std::array<f32, 2> dlss_jitter{};
bool ProcessNativeMethod(Core::System& system, u32 method, u32 argument, bool is_last_call);