shim: defer CUDA feature creation for every engine feature and start the RR model at CreateFeature, waiting for it instead of compiling a CUDA fallback
This commit is contained in:
1 parent
51afe5ea39
commit
e3606cedf9
1 file changed
+49
-26
+49
-26
@@ -7610,6 +7610,7 @@ static float get_float_or(void* parameters, const char* name, float fallback)
|
|||||||
}
|
}
|
||||||
|
|
||||||
static bool engine_requested();
|
static bool engine_requested();
|
||||||
|
static int start_engine_model_rr(const Feature& feature);
|
||||||
|
|
||||||
// Called on the game thread with the feature API lock held. All CUDA resources are made on its worker.
|
// Called on the game thread with the feature API lock held. All CUDA resources are made on its worker.
|
||||||
// Vulkan-only K features retain just their creation settings until a frame actually needs CUDA.
|
// Vulkan-only K features retain just their creation settings until a frame actually needs CUDA.
|
||||||
@@ -7827,16 +7828,20 @@ static NgxResult create_feature(unsigned int featureId, void* parameters, NgxHan
|
|||||||
|
|
||||||
feature->subrects = subrects;
|
feature->subrects = subrects;
|
||||||
std::copy(std::begin(presets), std::end(presets), feature->creationPresets.begin());
|
std::copy(std::begin(presets), std::end(presets), feature->creationPresets.begin());
|
||||||
// The engine records D3D12 evaluations only; the native Vulkan frontend keeps eager CUDA creation. Presets K, L
|
// The engine records D3D12 evaluations only; the native Vulkan frontend keeps eager CUDA creation. With the
|
||||||
// and M and Ray Reconstruction (the engine's) create the CUDA feature only when a frame falls back: NGX's L kernels
|
// engine on, every feature creates its CUDA feature only when a frame falls back: NGX's L kernels take ZLUDA
|
||||||
// take ZLUDA about 20 s to compile on a cold cache, which stalled the game inside CreateFeature (Overwatch,
|
// about 20 s to compile on a cold cache, which stalled the game inside CreateFeature (Overwatch, 2026-10-11),
|
||||||
// 2026-10-11), and the denoiser's are larger still.
|
// and the denoiser's 310.9 kernels 44 s (RE Requiem, 2026-10-14). A feature the engine refuses pays that at its
|
||||||
feature->cudaCreationDeferred = !nativeVulkan && engine_requested() &&
|
// first frame instead.
|
||||||
(rayReconstruction || feature->preset == 11 || feature->preset == 12 ||
|
feature->cudaCreationDeferred = !nativeVulkan && engine_requested();
|
||||||
feature->preset == 13);
|
|
||||||
const NgxResult result = feature->cudaCreationDeferred ? NGX_SUCCESS : create_cuda_feature(*feature);
|
const NgxResult result = feature->cudaCreationDeferred ? NGX_SUCCESS : create_cuda_feature(*feature);
|
||||||
if (feature->cudaCreationDeferred)
|
if (feature->cudaCreationDeferred)
|
||||||
|
{
|
||||||
logf("engine backend: CUDA feature creation deferred until fallback is needed");
|
logf("engine backend: CUDA feature creation deferred until fallback is needed");
|
||||||
|
// the denoiser's model loads on a thread of its own: start it now, so it is usually ready by the first frame
|
||||||
|
if (rayReconstruction)
|
||||||
|
start_engine_model_rr(*feature);
|
||||||
|
}
|
||||||
if (result != NGX_SUCCESS)
|
if (result != NGX_SUCCESS)
|
||||||
{
|
{
|
||||||
release_feature(feature);
|
release_feature(feature);
|
||||||
@@ -8926,6 +8931,31 @@ static void init_engine_backend_rr(int slot)
|
|||||||
path.c_str(), std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count());
|
path.c_str(), std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - start).count());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Starts loading the Ray Reconstruction model `feature` runs on (once per preset) and returns its slot. Called at
|
||||||
|
// CreateFeature too, so the model is usually ready by the first frame.
|
||||||
|
static int start_engine_model_rr(const Feature& feature)
|
||||||
|
{
|
||||||
|
// NGX 310.7 runs preset E's kernels for the hint 5 only and preset D's (diamond_wallaby) for every other value:
|
||||||
|
// none, the retired A / B / C (1-3), 6, and values with the top bit set such as Cyberpunk 2077's 0x80000002 or
|
||||||
|
// 0x80000005 (harness captures with D4R_RR_PRESET, 2026-10-12).
|
||||||
|
// NGX 310.9 has preset F (DLSS 4.5, the rr2_* kernels) and runs it for every hint but 4 (D) and 5 (E): none,
|
||||||
|
// 6, Cyberpunk 2077's 0x80000002 (harness runs with D4R_RR_PRESET, 2026-10-13). The shim cannot see which DLL
|
||||||
|
// version the CUDA backend loaded, so that mapping applies when the user built a preset F model (engine\rr-f),
|
||||||
|
// which only a 310.9 DLL yields.
|
||||||
|
static const bool haveF = GetFileAttributesA((rr_model_directory(2) + "\\rr.txt").c_str()) != INVALID_FILE_ATTRIBUTES;
|
||||||
|
const int slot = feature.preset == 5 ? 1 : (haveF && feature.preset != 4) ? 2 : 0;
|
||||||
|
std::call_once(g_engine.vulkanOnce, init_engine_vulkan);
|
||||||
|
std::call_once(g_engineR.once[slot], [slot] {
|
||||||
|
g_engineR.state[slot] = 1;
|
||||||
|
// D4R_ENGINE_RR_SYNC=1 (harness comparisons): load in place so the engine serves the first frame
|
||||||
|
if (env_uint("D4R_ENGINE_RR_SYNC", 0) != 0)
|
||||||
|
init_engine_backend_rr(slot);
|
||||||
|
else
|
||||||
|
std::thread([slot] { init_engine_backend_rr(slot); }).detach();
|
||||||
|
});
|
||||||
|
return slot;
|
||||||
|
}
|
||||||
|
|
||||||
// Ray Reconstruction on the engine: d4r's stages in the game's command list, with the denoiser's guides sampled
|
// Ray Reconstruction on the engine: d4r's stages in the game's command list, with the denoiser's guides sampled
|
||||||
// straight from the game's textures. False: the CUDA backend serves the feature (reason logged).
|
// straight from the game's textures. False: the CUDA backend serves the feature (reason logged).
|
||||||
static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list, ID3D12Resource* color,
|
static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list, ID3D12Resource* color,
|
||||||
@@ -8949,28 +8979,21 @@ static bool engine_evaluate_rr(Feature& feature, ID3D12GraphicsCommandList* list
|
|||||||
warn_not_f("from this frame on");
|
warn_not_f("from this frame on");
|
||||||
return false;
|
return false;
|
||||||
};
|
};
|
||||||
// NGX 310.7 runs preset E's kernels for the hint 5 only and preset D's (diamond_wallaby) for every other value:
|
const int slot = start_engine_model_rr(feature);
|
||||||
// none, the retired A / B / C (1-3), 6, and values with the top bit set such as Cyberpunk 2077's 0x80000002 or
|
|
||||||
// 0x80000005 (harness captures with D4R_RR_PRESET, 2026-10-12).
|
|
||||||
// NGX 310.9 has preset F (DLSS 4.5, the rr2_* kernels) and runs it for every hint but 4 (D) and 5 (E): none,
|
|
||||||
// 6, Cyberpunk 2077's 0x80000002 (harness runs with D4R_RR_PRESET, 2026-10-13). The shim cannot see which DLL
|
|
||||||
// version the CUDA backend loaded, so that mapping applies when the user built a preset F model (engine\rr-f),
|
|
||||||
// which only a 310.9 DLL yields.
|
|
||||||
static const bool haveF = GetFileAttributesA((rr_model_directory(2) + "\\rr.txt").c_str()) != INVALID_FILE_ATTRIBUTES;
|
|
||||||
const int slot = feature.preset == 5 ? 1 : (haveF && feature.preset != 4) ? 2 : 0;
|
|
||||||
std::call_once(g_engineR.once[slot], [slot] {
|
|
||||||
g_engineR.state[slot] = 1;
|
|
||||||
// D4R_ENGINE_RR_SYNC=1 (harness comparisons): load in place so the engine serves the first frame
|
|
||||||
if (env_uint("D4R_ENGINE_RR_SYNC", 0) != 0)
|
|
||||||
init_engine_backend_rr(slot);
|
|
||||||
else
|
|
||||||
std::thread([slot] { init_engine_backend_rr(slot); }).detach();
|
|
||||||
});
|
|
||||||
cudaLacksF = slot == 2 && !denoiser_has_preset_f();
|
cudaLacksF = slot == 2 && !denoiser_has_preset_f();
|
||||||
const int state = g_engineR.state[slot].load();
|
int state = g_engineR.state[slot].load();
|
||||||
|
if (state == 1 && feature.cudaCreationDeferred)
|
||||||
|
{
|
||||||
|
// No CUDA feature yet: making one for these frames compiles NGX's kernels (44 s with 310.9 on a cold ZLUDA
|
||||||
|
// cache, RE Requiem 2026-10-14; the model was ready 7 ms after the first frame). Wait for the model instead.
|
||||||
|
logf("engine backend (Ray Reconstruction %c): frame %u waits for the model rather than creating the CUDA "
|
||||||
|
"fallback", g_rrLetter[slot], frame);
|
||||||
|
while ((state = g_engineR.state[slot].load()) == 1)
|
||||||
|
d4r_sleep_us(1000);
|
||||||
|
}
|
||||||
if (state == 1)
|
if (state == 1)
|
||||||
{
|
{
|
||||||
// still compiling: the CUDA backend serves this frame, the engine is tried again next frame
|
// still compiling and the CUDA feature exists: it serves this frame, the engine is tried again next frame
|
||||||
if (!feature.rrCompileWarned)
|
if (!feature.rrCompileWarned)
|
||||||
{
|
{
|
||||||
feature.rrCompileWarned = true;
|
feature.rrCompileWarned = true;
|
||||||
|
|||||||
Reference in new issue
Block a user