Add native K/M/RR inference engines, HIP kernels, replay and validation harnesses, and Windows DLSS setup and runtime support. Include GPU profiling tools, build and packaging scripts, platform integration patches, and implementation documentation.
157 lines
9.8 KiB
C++
157 lines
9.8 KiB
C++
#pragma once
|
|
#include "cuda_image_api.h"
|
|
#include <cmath>
|
|
#include <chrono>
|
|
#include <fstream>
|
|
|
|
extern "C" void d4r_ngx_set_uint(void*, const char*, unsigned);
|
|
extern "C" void d4r_ngx_set_int(void*, const char*, int);
|
|
extern "C" void d4r_ngx_set_float(void*, const char*, float);
|
|
extern "C" void d4r_ngx_set_ull(void*, const char*, unsigned long long);
|
|
extern "C" void d4r_ngx_set_void(void*, const char*, void*);
|
|
|
|
namespace d4r::diag {
|
|
inline float half_value(uint16_t bits) {
|
|
const unsigned exponent = (bits >> 10) & 31, mantissa = bits & 1023;
|
|
const float sign = bits & 0x8000 ? -1.0f : 1.0f;
|
|
if (exponent == 31) return mantissa ? NAN : sign * INFINITY;
|
|
if (!exponent) return sign * std::ldexp(float(mantissa), -24);
|
|
return sign * std::ldexp(float(1024 + mantissa), int(exponent) - 25);
|
|
}
|
|
inline void save_output(const std::filesystem::path& directory, const std::vector<uint16_t>& output,
|
|
unsigned width, unsigned height, unsigned frame) {
|
|
const auto stem = "output-" + std::to_string(frame);
|
|
std::ofstream raw(directory / (stem + ".rgba16f"), std::ios::binary);
|
|
raw.write(reinterpret_cast<const char*>(output.data()), std::streamsize(output.size() * 2));
|
|
// BMP is only a diagnostic preview; the raw file preserves unmodified HDR values.
|
|
const unsigned stride = (width * 3 + 3) & ~3u;
|
|
std::vector<unsigned char> bytes(54 + size_t(stride) * height, 0);
|
|
auto u32 = [&](size_t offset, uint32_t value) {
|
|
for (int j = 0; j < 4; ++j) bytes[offset + j] = static_cast<unsigned char>(value >> (8 * j));
|
|
};
|
|
bytes[0] = 'B'; bytes[1] = 'M'; u32(2, static_cast<uint32_t>(bytes.size())); u32(10, 54);
|
|
u32(14, 40); u32(18, width); u32(22, height); bytes[26] = 1; bytes[28] = 24;
|
|
for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x)
|
|
for (unsigned c = 0; c < 3; ++c) {
|
|
const float value = half_value(output[(size_t(y) * width + x) * 4 + c]);
|
|
bytes[54 + size_t(height - y - 1) * stride + x * 3 + (2 - c)] =
|
|
static_cast<unsigned char>(std::clamp(value, 0.0f, 1.0f) * 255.0f + 0.5f);
|
|
}
|
|
std::ofstream bmp(directory / (stem + ".bmp"), std::ios::binary);
|
|
bmp.write(reinterpret_cast<const char*>(bytes.data()), std::streamsize(bytes.size()));
|
|
if (!raw || !bmp) throw std::runtime_error("Saving DLSS output failed");
|
|
}
|
|
|
|
inline void run_synthetic_dlss(CudaApi& cuda, Library& core, void* parameters,
|
|
unsigned preset, unsigned frames, const std::filesystem::path& directory) {
|
|
// CPU upload/readback is confined to this synthetic diagnostic. The game
|
|
// backend uses external D3D12 memory and GPU copies instead.
|
|
constexpr unsigned width = 256, height = 144, outWidth = 512, outHeight = 288;
|
|
using Create = unsigned(*)(unsigned, void*, void**);
|
|
using Evaluate = unsigned(*)(void*, void*, void*);
|
|
using Release = unsigned(*)(void*);
|
|
using ScratchSize = unsigned(*)(unsigned, void*, size_t*);
|
|
const auto create = core.symbol<Create>("NVSDK_NGX_CUDA_CreateFeature");
|
|
const auto evaluate = core.symbol<Evaluate>("NVSDK_NGX_CUDA_EvaluateFeature");
|
|
const auto release = core.symbol<Release>("NVSDK_NGX_CUDA_ReleaseFeature");
|
|
const auto getScratchBufferSize = core.symbol<ScratchSize>("NVSDK_NGX_CUDA_GetScratchBufferSize");
|
|
auto ngx_check = [](unsigned result, const char* operation) {
|
|
std::printf("NGX %s -> 0x%08x\n", operation, result);
|
|
if (result != 1) throw std::runtime_error(std::string(operation) + " result=" + std::to_string(result));
|
|
};
|
|
d4r_ngx_set_uint(parameters, "Width", width);
|
|
d4r_ngx_set_uint(parameters, "Height", height);
|
|
d4r_ngx_set_uint(parameters, "OutWidth", outWidth);
|
|
d4r_ngx_set_uint(parameters, "OutHeight", outHeight);
|
|
d4r_ngx_set_int(parameters, "PerfQualityValue", 2);
|
|
d4r_ngx_set_int(parameters, "DLSS.Feature.Create.Flags", 0);
|
|
d4r_ngx_set_int(parameters, "DLSS.Enable.Output.Subrects", 0);
|
|
d4r_ngx_set_uint(parameters, "CreationNodeMask", 1);
|
|
d4r_ngx_set_uint(parameters, "VisibilityNodeMask", 1);
|
|
for (const char* name : {"DLSS.Hint.Render.Preset.DLAA", "DLSS.Hint.Render.Preset.Quality",
|
|
"DLSS.Hint.Render.Preset.Balanced", "DLSS.Hint.Render.Preset.Performance",
|
|
"DLSS.Hint.Render.Preset.UltraPerformance", "DLSS.Hint.Render.Preset.UltraQuality"})
|
|
d4r_ngx_set_uint(parameters, name, preset);
|
|
size_t scratchBytes = 0;
|
|
const unsigned scratchResult = getScratchBufferSize(1, parameters, &scratchBytes);
|
|
if (scratchResult != 1 || !scratchBytes) {
|
|
std::printf("NGX scratch query=0x%08x fallback_bytes=67108864\n", scratchResult);
|
|
scratchBytes = 64ull * 1024 * 1024;
|
|
}
|
|
std::printf("NGX scratch bytes=%zu\n", scratchBytes);
|
|
CUdeviceptr scratch = 0;
|
|
cuda.check(cuda.cuMemAlloc_v2(&scratch, scratchBytes), "cuMemAlloc_v2(NGX scratch)");
|
|
struct ScratchCleanup { CudaApi& api; CUdeviceptr pointer; ~ScratchCleanup() { (void)api.cuMemFree_v2(pointer); } } sc{cuda, scratch};
|
|
d4r_ngx_set_void(parameters, "Scratch", reinterpret_cast<void*>(uintptr_t(scratch)));
|
|
d4r_ngx_set_ull(parameters, "Scratch.SizeInBytes", scratchBytes);
|
|
void* handle = nullptr;
|
|
std::printf("STAGE NGX_CREATE preset=%u dimensions=%ux%u->%ux%u\n", preset, width, height, outWidth, outHeight);
|
|
ngx_check(create(1, parameters, &handle), "NVSDK_NGX_CUDA_CreateFeature");
|
|
if (!handle) throw std::runtime_error("NGX CreateFeature returned a null handle");
|
|
struct FeatureCleanup { Release release; void*& handle; ~FeatureCleanup() { if (handle) (void)release(handle); } } fc{release, handle};
|
|
cuda::ImageApi images(cuda);
|
|
cuda::Image color(images, width, height, 16, 4, false, 1);
|
|
cuda::Image depth(images, width, height, 32, 1, false);
|
|
cuda::Image motion(images, width, height, 16, 2, false);
|
|
cuda::Image exposure(images, 1, 1, 32, 1, false);
|
|
cuda::Image output(images, outWidth, outHeight, 16, 4, true);
|
|
std::vector<uint16_t> input(size_t(width) * height * 4), zeroMotion(size_t(width) * height * 2, 0);
|
|
std::vector<float> inputDepth(size_t(width) * height, 0.5f);
|
|
for (unsigned y = 0; y < height; ++y) for (unsigned x = 0; x < width; ++x) {
|
|
const size_t index = (size_t(y) * width + x) * 4;
|
|
input[index] = ((x / 16 + y / 16) & 1) ? 0x3a00 : 0x3400; // .75 / .25
|
|
input[index + 1] = (x & 32) ? 0x3800 : 0x3400;
|
|
input[index + 2] = (y & 32) ? 0x3a00 : 0x3800;
|
|
input[index + 3] = 0x3c00;
|
|
}
|
|
const float one = 1.0f;
|
|
color.upload(input.data()); depth.upload(inputDepth.data()); motion.upload(zeroMotion.data()); exposure.upload(&one);
|
|
d4r_ngx_set_void(parameters, "Color", &color.object);
|
|
d4r_ngx_set_void(parameters, "Depth", &depth.object);
|
|
d4r_ngx_set_void(parameters, "MotionVectors", &motion.object);
|
|
d4r_ngx_set_void(parameters, "ExposureTexture", &exposure.object);
|
|
d4r_ngx_set_void(parameters, "Output", &output.object);
|
|
for (const char* name : {"TransparencyMask", "DLSS.Input.Bias.Current.Color.Mask"})
|
|
d4r_ngx_set_void(parameters, name, nullptr);
|
|
for (const char* name : {"Jitter.Offset.X", "Jitter.Offset.Y", "Sharpness"})
|
|
d4r_ngx_set_float(parameters, name, 0.0f);
|
|
for (const char* name : {"MV.Scale.X", "MV.Scale.Y", "DLSS.Pre.Exposure", "DLSS.Exposure.Scale"})
|
|
d4r_ngx_set_float(parameters, name, 1.0f);
|
|
d4r_ngx_set_float(parameters, "FrameTimeDeltaInMsec", 16.666667f);
|
|
d4r_ngx_set_int(parameters, "Disable.Watermark", 1);
|
|
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", width);
|
|
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", height);
|
|
for (const char* name : {"DLSS.Input.Color.Subrect.Base.X", "DLSS.Input.Color.Subrect.Base.Y",
|
|
"DLSS.Input.Depth.Subrect.Base.X", "DLSS.Input.Depth.Subrect.Base.Y", "DLSS.Input.MV.Subrect.Base.X",
|
|
"DLSS.Input.MV.Subrect.Base.Y", "DLSS.Output.Subrect.Base.X", "DLSS.Output.Subrect.Base.Y"})
|
|
d4r_ngx_set_uint(parameters, name, 0);
|
|
std::vector<uint16_t> pixels(size_t(outWidth) * outHeight * 4, 0x7e00);
|
|
for (unsigned frame = 0; frame < frames; ++frame) {
|
|
std::fill(pixels.begin(), pixels.end(), 0x7e00); // unwritten output is an explicit failure
|
|
output.upload(pixels.data());
|
|
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
|
|
std::printf("STAGE NGX_EVALUATE preset=%u frame=%u\n", preset, frame);
|
|
const auto begin = std::chrono::steady_clock::now();
|
|
ngx_check(evaluate(handle, parameters, nullptr), "NVSDK_NGX_CUDA_EvaluateFeature");
|
|
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize(NGX Evaluate)");
|
|
const double milliseconds = std::chrono::duration<double, std::milli>(std::chrono::steady_clock::now() - begin).count();
|
|
output.download(pixels.data());
|
|
size_t nonfinite = 0; double sum = 0, square = 0;
|
|
for (size_t i = 0; i < pixels.size(); ++i) {
|
|
const float value = half_value(pixels[i]);
|
|
if (!std::isfinite(value)) ++nonfinite;
|
|
if ((i & 3) < 3) { sum += value; square += double(value) * value; }
|
|
}
|
|
const double count = outWidth * outHeight * 3;
|
|
const double variance = square / count - (sum / count) * (sum / count);
|
|
std::printf("NGX_OUTPUT preset=%u frame=%u nonfinite=%zu mean=%.9g variance=%.9g evaluate_ms=%.3f\n",
|
|
preset, frame, nonfinite, sum / count, variance, milliseconds);
|
|
if (nonfinite || !std::isfinite(variance) || variance < 1e-6)
|
|
throw std::runtime_error("DLSS output has NaN/Inf, unwritten pixels or no synthetic pattern");
|
|
save_output(directory, pixels, outWidth, outHeight, frame);
|
|
}
|
|
ngx_check(release(handle), "NVSDK_NGX_CUDA_ReleaseFeature"); handle = nullptr;
|
|
std::printf("PASS NGX_EVALUATE preset=%u frames=%u finite=1 network_validation=pending\n", preset, frames);
|
|
}
|
|
}
|