release: prepare 0.1.5 with RDNA2 kernels and in-game DLSS/FSR switching
- Add native preset K kernels for gfx1030–gfx1036 using software matrix products, with fast and accuracy builds and RDNA2 motion kernels. - Follow OptiScaler's DLSS and RR preset overrides, sanitize unsupported values and select the preset matching the feature's quality mode. - Enable FSR 4 under NVIDIA GPU spoofing by exposing real AMD PCI IDs to OptiScaler and FidelityFX modules; bundle AMD's loader and upscaler. - Keep K on native kernels in engine auto mode unless HIP is selected, and fall back to CUDA for M/L when shader_float8 is unavailable. - Add native-engine K GPU timings, preset recreation harness controls and regression coverage; fix the input-wait test's glibc name clash. - Update configuration, installer guidance and release documentation, simplify the README and move gameplay videos to docs/videos.md. RDNA2 kernels remain untested on RDNA2 hardware; software matrix arithmetic was validated against WMMA on RDNA3.
This commit is contained in:
1 parent
34e6b0fe71
commit
0e851b7f90
24 files changed
+703
-174
No files matched your search
+112
-7
@@ -100,6 +100,7 @@
|
||||
#include <bcrypt.h>
|
||||
#include "d4r_fsr_rr.h"
|
||||
#include "d4r_fsr_rr_identity.h"
|
||||
#include "d4r_fsr4_identity.h"
|
||||
#include "d4r_fsr_rr_passes.h"
|
||||
#include "../engine/game.h"
|
||||
#include "../engine/game_m.h"
|
||||
@@ -625,6 +626,12 @@ static void load_portable_config()
|
||||
model.clear();
|
||||
if (!model.empty())
|
||||
portable_set("D4R_DLSS_PRESET", model);
|
||||
// [OptiScaler] FollowPresets: its Render Presets Override picks the model in game (d4r_render_presets.h);
|
||||
// Fsr4: OptiScaler and AMD's FidelityFX modules see the real GPU, for its FSR 4 option (d4r_fsr4_identity.h).
|
||||
if (ini_flag(ini, "optiscaler", "FollowPresets", 1) == 0)
|
||||
portable_set("D4R_FOLLOW_OPTISCALER", "0");
|
||||
if (ini_flag(ini, "optiscaler", "Fsr4", 1) == 0)
|
||||
portable_set("D4R_FSR4_IDENTITY", "0");
|
||||
|
||||
// Ray Reconstruction is configured on its own section and drives its own feature, so
|
||||
// nothing here reaches [DLSS] Model and nothing there reaches this.
|
||||
@@ -755,6 +762,10 @@ static void load_portable_config()
|
||||
else
|
||||
on = ini_flag(ini, "engine", "Enabled", 0) != 0;
|
||||
portable_set("D4R_ENGINE", on ? "1" : "0");
|
||||
// auto leaves K on the native kernels: K's Vulkan network is slower (2026-10-17, 7700 XT, 1706x960 -> 1440p:
|
||||
// 3.59 ms vs 2.35 ms dlss_gpu). Enabled = true, or Network = hip with a model that has the HIP layers, keeps it.
|
||||
if (enabled.empty() || enabled == "auto")
|
||||
portable_set("D4R_ENGINE_K", "0");
|
||||
}
|
||||
if (const std::string engineModel = ini_value(ini, "engine", "ModelDir"); !engineModel.empty())
|
||||
portable_set(L"D4R_ENGINE_MODEL_DIR", portable_path(engineModel));
|
||||
@@ -1982,6 +1993,13 @@ static NgxResult initialize(unsigned long long applicationId, const wchar_t* dat
|
||||
LUID luid;
|
||||
device->GetAdapterLuid(&luid);
|
||||
set_cuda_adapter_luid(luid);
|
||||
// OptiScaler's FSR 4 option needs the real AMD identity for OptiScaler and AMD's modules (d4r_fsr4_identity.h)
|
||||
if (env_string("D4R_FSR4_IDENTITY") != "0")
|
||||
{
|
||||
std::string detail;
|
||||
d4r_fsr4_identity::install(device, detail);
|
||||
logf("FSR 4 identity: %s", detail.c_str());
|
||||
}
|
||||
}
|
||||
|
||||
g.paths.clear();
|
||||
@@ -3364,6 +3382,9 @@ struct VulkanInterop
|
||||
VkDevice device = VK_NULL_HANDLE;
|
||||
VkPhysicalDeviceMemoryProperties memory = {};
|
||||
VkPhysicalDevice physical = VK_NULL_HANDLE;
|
||||
// VK_EXT_shader_float8 (vkd3d-proton enables it when the GPU has it): M's and L's engine layers are FP8
|
||||
// cooperative-matrix shaders, which RADV's compiler crashes on without it (RDNA3)
|
||||
bool float8 = false;
|
||||
PFN_vkGetPhysicalDeviceFormatProperties formatProperties = nullptr;
|
||||
PFN_vkCreateBuffer createBuffer = nullptr;
|
||||
PFN_vkDestroyBuffer destroyBuffer = nullptr;
|
||||
@@ -9027,6 +9048,36 @@ static NgxResult create_cuda_feature(Feature& feature)
|
||||
return result;
|
||||
}
|
||||
|
||||
// Whether the presets of this creation were picked in OptiScaler (d4r_render_presets.h): its override is on in
|
||||
// OptiScaler.ini, or the presets changed since the game's first creation. (OptiScaler forwards the game's
|
||||
// AllocateParameters to this DLL, so the table itself does not say who filled it.) D4R_FOLLOW_OPTISCALER=0 always
|
||||
// keeps d4r's model. g.mutex is held.
|
||||
static bool optiscaler_chose_presets(bool rayReconstruction, const unsigned int (&presets)[6])
|
||||
{
|
||||
static D4rPresetFollower followers[2];
|
||||
static bool iniOverride[2] = {};
|
||||
static std::once_flag iniOnce;
|
||||
if (env_string("D4R_FOLLOW_OPTISCALER") == "0")
|
||||
return false;
|
||||
std::call_once(iniOnce, [] {
|
||||
// OptiScaler reads OptiScaler.ini beside its own DLL, which d4r's install puts beside the game's EXE
|
||||
wchar_t exe[MAX_PATH] = {};
|
||||
const DWORD length = GetModuleFileNameW(nullptr, exe, MAX_PATH);
|
||||
if (length == 0 || length >= MAX_PATH)
|
||||
return;
|
||||
std::wstring path(exe, length);
|
||||
path.resize(path.find_last_of(L"\\/") + 1);
|
||||
std::string text;
|
||||
if (!read_whole_file(path + L"OptiScaler.ini", text))
|
||||
return;
|
||||
const std::vector<IniEntry> ini = parse_ini(text);
|
||||
iniOverride[0] = ini_flag(ini, "dlss", "RenderPresetOverride", 0) == 1;
|
||||
iniOverride[1] = ini_flag(ini, "dlssd", "RenderPresetOverride", 0) == 1;
|
||||
logf("OptiScaler.ini: RenderPresetOverride DLSS=%d DLSSD=%d", iniOverride[0], iniOverride[1]);
|
||||
});
|
||||
return followers[rayReconstruction].follow(presets, iniOverride[rayReconstruction]);
|
||||
}
|
||||
|
||||
static NgxResult fsr_rr_create(Feature& feature, void* parameters);
|
||||
static NgxResult create_feature_locked(unsigned int featureId, void* parameters, NgxHandle** handle,
|
||||
bool nativeVulkan);
|
||||
@@ -9111,27 +9162,40 @@ static NgxResult create_feature_locked(unsigned int featureId, void* parameters,
|
||||
presets[index] = get_uint_or(parameters, presetNames[index], 0);
|
||||
const std::string forced = env_string(d4r_render_preset_variable(rayReconstruction));
|
||||
const auto presetOverride = d4r_parse_render_preset_override(forced.c_str());
|
||||
// Flagged, retired or unknown values count as Default, both for spotting OptiScaler's change and in what is followed
|
||||
if (presetOverride.enabled)
|
||||
d4r_sanitize_presets(presets, rayReconstruction);
|
||||
const bool followed = presetOverride.enabled && optiscaler_chose_presets(rayReconstruction, presets);
|
||||
if (followed)
|
||||
{
|
||||
logf("DLSS model: OptiScaler's preset override [DLAA=%u Quality=%u Balanced=%u Performance=%u "
|
||||
"UltraPerformance=%u UltraQuality=%u] wins over %s=%s (which still fills Default slots)",
|
||||
presets[0], presets[1], presets[2], presets[3], presets[4], presets[5],
|
||||
d4r_render_preset_variable(rayReconstruction), forced.c_str());
|
||||
d4r_apply_followed_presets(presets, presetOverride);
|
||||
}
|
||||
else if (presetOverride.enabled)
|
||||
{
|
||||
const unsigned int value = presetOverride.value;
|
||||
const char* model = value == 5 ? "E" : value == 11 ? "K" : value == 12 ? "L" : value == 13 ? "M" : "custom";
|
||||
logf("DLSS model override: D4R_DLSS_PRESET=%s (model %s) overrides game/OptiScaler presets "
|
||||
logf("DLSS model override: D4R_DLSS_PRESET=%s (model %s) overrides game presets "
|
||||
"[DLAA=%u Quality=%u Balanced=%u Performance=%u UltraPerformance=%u UltraQuality=%u]; "
|
||||
"using preset=%u for all quality modes",
|
||||
forced.c_str(), model, presets[0], presets[1], presets[2], presets[3], presets[4], presets[5], value);
|
||||
presetOverride.apply(presets);
|
||||
}
|
||||
presetOverride.apply(presets);
|
||||
feature->preset = presets[1];
|
||||
// NGX runs the preset of the feature's quality mode.
|
||||
feature->preset = presets[d4r_preset_slot(quality)];
|
||||
// Direct output needs the native output kernel of the preset in use (verified: K = 11 through
|
||||
// hiluma_engine_output, M = 13 through rrlite_downsample_kernel, its last kernel). Only a forced
|
||||
// Super Sampling preset qualifies; a Ray Reconstruction preset never does here.
|
||||
// hiluma_engine_output, M = 13 through rrlite_downsample_kernel, its last kernel). Only a forced or
|
||||
// OptiScaler-chosen Super Sampling preset qualifies; a Ray Reconstruction preset never does here.
|
||||
if (!forced.empty() && !rayReconstruction)
|
||||
{
|
||||
char allowed[128] = "11,13";
|
||||
if (const std::string list = env_string("D4R_SHIM_OUTPUT_DIRECT_PRESETS"); !list.empty())
|
||||
snprintf(allowed, sizeof(allowed), "%s", list.c_str());
|
||||
for (char* token = strtok(allowed, ","); token != nullptr; token = strtok(nullptr, ","))
|
||||
feature->outputDirectAllowed |= strtoul(token, nullptr, 0) == presets[1];
|
||||
feature->outputDirectAllowed |= strtoul(token, nullptr, 0) == feature->preset;
|
||||
}
|
||||
feature->rayReconstruction = rayReconstruction;
|
||||
if (rayReconstruction)
|
||||
@@ -9139,7 +9203,7 @@ static NgxResult create_feature_locked(unsigned int featureId, void* parameters,
|
||||
feature->auxScalarBaseline[index] = get_int_or(parameters, kAuxScalars[index].name, 0);
|
||||
logf("NVSDK_NGX_D3D12_CreateFeature(feature=%u%s): %ux%u -> %ux%u quality=%d flags=0x%x subrects=%d preset=%u",
|
||||
featureId, rayReconstruction ? ", Ray Reconstruction" : "", feature->width, feature->height,
|
||||
feature->outWidth, feature->outHeight, quality, flags, subrects, presets[1]);
|
||||
feature->outWidth, feature->outHeight, quality, flags, subrects, feature->preset);
|
||||
|
||||
feature->subrects = subrects;
|
||||
std::copy(std::begin(presets), std::end(presets), feature->creationPresets.begin());
|
||||
@@ -9244,6 +9308,18 @@ static void init_engine_vulkan()
|
||||
logf("engine backend: Vulkan entry points are unavailable");
|
||||
return;
|
||||
}
|
||||
auto enumerate = reinterpret_cast<PFN_vkEnumerateDeviceExtensionProperties>(
|
||||
instanceProc(instance, "vkEnumerateDeviceExtensionProperties"));
|
||||
uint32_t count = 0;
|
||||
if (enumerate != nullptr && enumerate(g_vk.physical, nullptr, &count, nullptr) == VK_SUCCESS)
|
||||
{
|
||||
std::vector<VkExtensionProperties> extensions(count);
|
||||
if (enumerate(g_vk.physical, nullptr, &count, extensions.data()) == VK_SUCCESS)
|
||||
for (uint32_t index = 0; index < count; ++index)
|
||||
g_vk.float8 |= std::strcmp(extensions[index].extensionName, "VK_EXT_shader_float8") == 0;
|
||||
}
|
||||
logf("engine backend: VK_EXT_shader_float8 %s", g_vk.float8 ? "available (M and L engine layers)" : "unavailable: "
|
||||
"M and L run on the CUDA backend");
|
||||
}
|
||||
|
||||
static void init_engine_backend()
|
||||
@@ -9982,6 +10058,10 @@ static bool engine_evaluate_m(Feature& feature, ID3D12GraphicsCommandList* list,
|
||||
};
|
||||
// NVSDK_NGX_DLSS_Feature_Flags: IsHDR 1, MVLowRes 2, MVJittered 4, DepthInverted 8, AutoExposure 0x40.
|
||||
const bool ldr = (feature.flags & 0x1) == 0; // NGX's LDR stages: a model directory of its own, no exposure
|
||||
// The model's pipelines include the FP8 layers even when the HIP network runs them; creating those without
|
||||
// VK_EXT_shader_float8 crashes the driver (RADV on RDNA3, vkCreateComputePipelines), so never load the model.
|
||||
if (!g_vk.float8)
|
||||
return refuse("the GPU has no VK_EXT_shader_float8 for the FP8 layers");
|
||||
std::call_once(g_engineM.once[presetL][ldr], [presetL, ldr] { init_engine_backend_m_variant(presetL, ldr); });
|
||||
d4r::MModel* const model = g_engineM.models[presetL][ldr].get();
|
||||
if (model == nullptr)
|
||||
@@ -10561,6 +10641,8 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
|
||||
return engine_evaluate_rr(feature, list, color, depth, motion, output, parameters, p, frame);
|
||||
if (feature.preset == 13 || feature.preset == 12)
|
||||
return engine_evaluate_m(feature, list, color, depth, motion, output, p, frame);
|
||||
if (feature.preset == 11 && env_string("D4R_ENGINE_K") == "0" && env_string("D4R_ENGINE_NETWORK") != "hip")
|
||||
return refuse("[Engine] Enabled = auto keeps K on the native kernels (faster than the Vulkan network)");
|
||||
// Without the HDR flag NGX runs its LDR input and output kernels: a model directory of its own.
|
||||
const bool ldr = (feature.flags & 0x1) == 0;
|
||||
if (ldr)
|
||||
@@ -10745,6 +10827,24 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
|
||||
}
|
||||
if (FAILED(g_vk.lifetime->RetainExternalResources(list, feature.resources)))
|
||||
return refuse("the command list cannot retain the engine's resources");
|
||||
// D4R_PROFILE: the Vulkan network's GPU time on the game's queue (timestamps around the whole recording)
|
||||
const bool profile = profile_enabled() && env_uint("D4R_PROFILE_GPU_TIMESTAMPS", 1) != 0 &&
|
||||
profile_timestamps_ready(feature);
|
||||
if (profile)
|
||||
{
|
||||
FrameTiming measured;
|
||||
read_profile_timestamps(feature, frame, measured);
|
||||
const auto now = ProfileClock::now();
|
||||
const double interval = feature.engineRProfileAt.time_since_epoch().count() ?
|
||||
profile_ms(feature.engineRProfileAt, now) : 0.0;
|
||||
feature.engineRProfileAt = now;
|
||||
if (profile_log_frame(frame) && measured.vkTotal >= 0)
|
||||
logf("D4R_ENGINE_K_PROFILE frame=%u gpu_frame=%u render=%ux%u output=%ux%u gpu_eval=%.3f "
|
||||
"game_interval=%.3f age=0", frame, frame - kOutputSlots, p.renderWidth, p.renderHeight,
|
||||
feature.outWidth, feature.outHeight, measured.vkTotal, interval);
|
||||
profile_timestamp(feature, list, frame, 0);
|
||||
profile_timestamp(feature, list, frame, 1);
|
||||
}
|
||||
for (const Source& source : sources)
|
||||
transition(list, source.resource, source.state, source.copyState);
|
||||
VkCommandBuffer command = VK_NULL_HANDLE;
|
||||
@@ -10772,6 +10872,11 @@ static bool engine_evaluate(Feature& feature, ID3D12GraphicsCommandList* list, I
|
||||
transition(list, source.resource, source.copyState, source.state);
|
||||
if (!recorded)
|
||||
return refuse(failure);
|
||||
if (profile)
|
||||
{
|
||||
profile_timestamp(feature, list, frame, 2);
|
||||
profile_timestamp(feature, list, frame, 3);
|
||||
}
|
||||
feature.engineJitter[0] = p.jitterX;
|
||||
feature.engineJitter[1] = p.jitterY;
|
||||
if (frame <= 3 || frame % 600 == 0)
|
||||
|
||||
Reference in new issue
Block a user