Build patched Windows ZLUDA and execute native NGX DLSS diagnostics
This commit is contained in:
1 parent
64472e5a61
commit
3c28a82e34
17 files changed
+1114
-13
No files matched your search
@@ -7,6 +7,7 @@ using CUcontext = void*;
|
||||
using CUmodule = void*;
|
||||
using CUfunction = void*;
|
||||
struct CudaApi {
|
||||
d4r::diag::SearchDirectory search;
|
||||
d4r::diag::Library library;
|
||||
#define D4R_CU(name, ret, ...) using name##_fn = ret(WINAPI*)(__VA_ARGS__); name##_fn name = library.symbol<name##_fn>(#name)
|
||||
D4R_CU(cuInit, int, unsigned);
|
||||
@@ -33,7 +34,9 @@ struct CudaApi {
|
||||
D4R_CU(cuGetErrorName, int, int, const char**);
|
||||
D4R_CU(cuGetErrorString, int, int, const char**);
|
||||
#undef D4R_CU
|
||||
explicit CudaApi(const std::string& path) : library(d4r::diag::wide(path)) {}
|
||||
explicit CudaApi(const std::string& path) :
|
||||
search(std::filesystem::path(d4r::diag::wide(path)).parent_path()),
|
||||
library(d4r::diag::wide(path)) {}
|
||||
void check(int result, const char* call) {
|
||||
const char* name = "unknown", *message = "unknown";
|
||||
if (result) { cuGetErrorName(result, &name); cuGetErrorString(result, &message); }
|
||||
|
||||
@@ -0,0 +1,135 @@
|
||||
#pragma once
|
||||
#include "cuda_api.h"
|
||||
|
||||
namespace d4r::cuda {
|
||||
// Public CUDA Driver API layouts, kept independent of NVIDIA SDK headers.
|
||||
using Array = void*;
|
||||
using Texture = uint64_t;
|
||||
struct ArrayDescriptor { size_t width, height; uint32_t format, channels; };
|
||||
struct Array3DDescriptor {
|
||||
size_t width, height, depth;
|
||||
uint32_t format, channels, flags;
|
||||
};
|
||||
struct Copy2D {
|
||||
size_t srcX, srcY;
|
||||
uint32_t srcType, srcAlignment;
|
||||
const void* srcHost;
|
||||
CUdeviceptr srcDevice;
|
||||
Array srcArray;
|
||||
size_t srcPitch, dstX, dstY;
|
||||
uint32_t dstType, dstAlignment;
|
||||
void* dstHost;
|
||||
CUdeviceptr dstDevice;
|
||||
Array dstArray;
|
||||
size_t dstPitch, widthBytes, height;
|
||||
};
|
||||
struct ResourceDescriptor {
|
||||
uint32_t type, alignment;
|
||||
union { Array array; int reserved[32]; } resource;
|
||||
uint32_t flags, reserved;
|
||||
};
|
||||
struct TextureDescriptor {
|
||||
uint32_t addressMode[3], filterMode, flags, maxAnisotropy, mipmapFilterMode;
|
||||
float mipmapBias, minMipmapClamp, maxMipmapClamp, borderColor[4];
|
||||
int reserved[12];
|
||||
};
|
||||
static_assert(sizeof(Array3DDescriptor) == 40);
|
||||
static_assert(sizeof(ArrayDescriptor) == 24);
|
||||
static_assert(sizeof(Copy2D) == 128);
|
||||
static_assert(sizeof(ResourceDescriptor) == 144);
|
||||
static_assert(sizeof(TextureDescriptor) == 104);
|
||||
|
||||
struct ImageApi {
|
||||
CudaApi& cuda;
|
||||
#define D4R_IMAGE(name, ...) using name##_fn = int(WINAPI*)(__VA_ARGS__); name##_fn name = cuda.library.symbol<name##_fn>(#name)
|
||||
D4R_IMAGE(cuArray3DCreate_v2, Array*, const Array3DDescriptor*);
|
||||
D4R_IMAGE(cuArrayGetDescriptor_v2, ArrayDescriptor*, Array);
|
||||
D4R_IMAGE(cuArray3DGetDescriptor_v2, Array3DDescriptor*, Array);
|
||||
D4R_IMAGE(cuArrayDestroy, Array);
|
||||
D4R_IMAGE(cuMemcpy2D_v2, const Copy2D*);
|
||||
D4R_IMAGE(cuTexObjectCreate, Texture*, const ResourceDescriptor*, const TextureDescriptor*, const void*);
|
||||
D4R_IMAGE(cuTexObjectDestroy, Texture);
|
||||
D4R_IMAGE(cuTexObjectGetResourceDesc, ResourceDescriptor*, Texture);
|
||||
D4R_IMAGE(cuSurfObjectCreate, Texture*, const ResourceDescriptor*);
|
||||
D4R_IMAGE(cuSurfObjectDestroy, Texture);
|
||||
D4R_IMAGE(cuSurfObjectGetResourceDesc, ResourceDescriptor*, Texture);
|
||||
#undef D4R_IMAGE
|
||||
explicit ImageApi(CudaApi& api) : cuda(api) {}
|
||||
};
|
||||
|
||||
class Image {
|
||||
ImageApi& api_;
|
||||
void release() noexcept {
|
||||
if (object) {
|
||||
const int result = surface_ ? api_.cuSurfObjectDestroy(object) : api_.cuTexObjectDestroy(object);
|
||||
if (result) std::fprintf(stderr, "IMAGE_DESTROY object result=%d\n", result);
|
||||
object = 0;
|
||||
}
|
||||
if (array_) {
|
||||
const int result = api_.cuArrayDestroy(array_);
|
||||
if (result) std::fprintf(stderr, "IMAGE_DESTROY array result=%d\n", result);
|
||||
array_ = nullptr;
|
||||
}
|
||||
}
|
||||
Array array_ = nullptr;
|
||||
bool surface_;
|
||||
size_t rowBytes_, height_;
|
||||
public:
|
||||
Texture object = 0;
|
||||
Image(ImageApi& api, unsigned width, unsigned height, uint32_t format,
|
||||
unsigned channels, bool surface, unsigned filter = 0)
|
||||
: api_(api), surface_(surface), rowBytes_(size_t(width) * channels * (format == 16 ? 2 : 4)), height_(height) {
|
||||
try {
|
||||
const Array3DDescriptor descriptor{width, height, 0, format, channels, surface ? 2u : 0u};
|
||||
api_.cuda.check(api_.cuArray3DCreate_v2(&array_, &descriptor), "cuArray3DCreate_v2");
|
||||
ResourceDescriptor resource{};
|
||||
resource.resource.array = array_;
|
||||
if (surface) api_.cuda.check(api_.cuSurfObjectCreate(&object, &resource), "cuSurfObjectCreate");
|
||||
else {
|
||||
TextureDescriptor texture{};
|
||||
texture.addressMode[0] = texture.addressMode[1] = 1; // clamp
|
||||
texture.filterMode = filter;
|
||||
texture.flags = 2; // normalized coordinates
|
||||
api_.cuda.check(api_.cuTexObjectCreate(&object, &resource, &texture, nullptr), "cuTexObjectCreate");
|
||||
}
|
||||
ResourceDescriptor roundtrip{};
|
||||
api_.cuda.check(surface ? api_.cuSurfObjectGetResourceDesc(&roundtrip, object) :
|
||||
api_.cuTexObjectGetResourceDesc(&roundtrip, object), "CUDA object resource descriptor roundtrip");
|
||||
if (roundtrip.type != 0 || roundtrip.resource.array != array_ || roundtrip.flags != 0)
|
||||
throw std::runtime_error("CUDA object descriptor does not match its array");
|
||||
ArrayDescriptor twoD{};
|
||||
Array3DDescriptor threeD{};
|
||||
api_.cuda.check(api_.cuArrayGetDescriptor_v2(&twoD, array_), "cuArrayGetDescriptor_v2");
|
||||
api_.cuda.check(api_.cuArray3DGetDescriptor_v2(&threeD, array_), "cuArray3DGetDescriptor_v2");
|
||||
if (twoD.width != width || twoD.height != height || twoD.format != format || twoD.channels != channels ||
|
||||
threeD.width != width || threeD.height != height || threeD.depth != 0 || threeD.format != format ||
|
||||
threeD.channels != channels || threeD.flags != descriptor.flags)
|
||||
throw std::runtime_error("CUDA array descriptor geometry or format mismatch");
|
||||
} catch (...) { release(); throw; }
|
||||
}
|
||||
Image(const Image&) = delete;
|
||||
Image& operator=(const Image&) = delete;
|
||||
~Image() { release(); }
|
||||
void verify_wrong_object_kind() {
|
||||
ResourceDescriptor wrong{};
|
||||
const int result = surface_ ? api_.cuTexObjectGetResourceDesc(&wrong, object) :
|
||||
api_.cuSurfObjectGetResourceDesc(&wrong, object);
|
||||
if (!result) throw std::runtime_error("Wrong CUDA object kind was accepted as a valid resource");
|
||||
std::printf("CUDA wrong-kind descriptor safely rejected result=%d\n", result);
|
||||
}
|
||||
void upload(const void* source) {
|
||||
Copy2D copy{};
|
||||
copy.srcType = 1; copy.srcHost = source; copy.srcPitch = rowBytes_;
|
||||
copy.dstType = 3; copy.dstArray = array_;
|
||||
copy.widthBytes = rowBytes_; copy.height = height_;
|
||||
api_.cuda.check(api_.cuMemcpy2D_v2(©), "cuMemcpy2D_v2(host to diagnostic array)");
|
||||
}
|
||||
void download(void* destination) {
|
||||
Copy2D copy{};
|
||||
copy.srcType = 3; copy.srcArray = array_;
|
||||
copy.dstType = 1; copy.dstHost = destination; copy.dstPitch = rowBytes_;
|
||||
copy.widthBytes = rowBytes_; copy.height = height_;
|
||||
api_.cuda.check(api_.cuMemcpy2D_v2(©), "cuMemcpy2D_v2(diagnostic array to host)");
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -0,0 +1,51 @@
|
||||
#include "hip_api.h"
|
||||
#include "cuda_image_api.h"
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
using namespace d4r::diag;
|
||||
start();
|
||||
try {
|
||||
Args args(argc, argv);
|
||||
HipApi hip(args.hip_root);
|
||||
hipDeviceProp_t props{};
|
||||
hip.select_gfx1201(args.device, props);
|
||||
SearchDirectory search(std::filesystem::path(wide(args.cuda_dll)).parent_path());
|
||||
CudaApi cuda(args.cuda_dll);
|
||||
cuda.check(cuda.cuInit(0), "cuInit");
|
||||
CUdevice device = -1;
|
||||
int count = 0;
|
||||
cuda.check(cuda.cuDeviceGetCount(&count), "cuDeviceGetCount");
|
||||
for (int ordinal = 0; ordinal < count; ++ordinal) {
|
||||
CUdevice candidate = -1;
|
||||
int bus = -1, pciDevice = -1, domain = -1;
|
||||
cuda.check(cuda.cuDeviceGet(&candidate, ordinal), "cuDeviceGet");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&bus, 33, candidate), "cuDeviceGetAttribute(PCI_BUS_ID)");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&pciDevice, 34, candidate), "cuDeviceGetAttribute(PCI_DEVICE_ID)");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&domain, 50, candidate), "cuDeviceGetAttribute(PCI_DOMAIN_ID)");
|
||||
if (bus == props.pciBusID && pciDevice == props.pciDeviceID && domain == props.pciDomainID) device = candidate;
|
||||
}
|
||||
if (device < 0) throw std::runtime_error("CUDA image probe HIP/CUDA PCI identity mismatch");
|
||||
CUcontext context = nullptr;
|
||||
cuda.check(cuda.cuCtxCreate_v2(&context, 0, device), "cuCtxCreate_v2");
|
||||
struct Cleanup { CudaApi& api; CUcontext context; ~Cleanup() { (void)api.cuCtxDestroy_v2(context); } } cleanup{cuda, context};
|
||||
d4r::cuda::ImageApi images(cuda);
|
||||
for (unsigned iteration = 0; iteration < args.iterations; ++iteration) {
|
||||
for (unsigned channels : {1u, 2u, 4u}) for (bool surface : {false, true}) {
|
||||
d4r::cuda::Image image(images, 17, 9, 16, channels, surface);
|
||||
image.verify_wrong_object_kind();
|
||||
std::vector<uint16_t> input(17 * 9 * channels), output(input.size());
|
||||
for (size_t i = 0; i < input.size(); ++i) input[i] = uint16_t((i + iteration) % 8 * 0x100);
|
||||
image.upload(input.data());
|
||||
image.download(output.data());
|
||||
if (input != output) throw std::runtime_error("CUDA array storage roundtrip mismatch");
|
||||
}
|
||||
}
|
||||
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize");
|
||||
std::printf("PASS CUDA_IMAGES architecture=gfx1201 iterations=%u descriptor_and_storage=1\n", args.iterations);
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::fprintf(stderr, "FAIL CUDA_IMAGES %s\n", e.what());
|
||||
loaded_modules();
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -57,6 +57,18 @@ inline LONG WINAPI unhandled(EXCEPTION_POINTERS* info)
|
||||
{
|
||||
std::fprintf(stderr, "EXCEPTION code=0x%08lx address=%p\n",
|
||||
info->ExceptionRecord->ExceptionCode, info->ExceptionRecord->ExceptionAddress);
|
||||
if (info->ExceptionRecord->ExceptionCode == EXCEPTION_ACCESS_VIOLATION &&
|
||||
info->ExceptionRecord->NumberParameters >= 2)
|
||||
std::fprintf(stderr, "EXCEPTION_ACCESS operation=%llu target=0x%llx\n",
|
||||
static_cast<unsigned long long>(info->ExceptionRecord->ExceptionInformation[0]),
|
||||
static_cast<unsigned long long>(info->ExceptionRecord->ExceptionInformation[1]));
|
||||
std::fprintf(stderr, "CONTEXT rip=0x%llx rsp=0x%llx rcx=0x%llx rdx=0x%llx r8=0x%llx r9=0x%llx\n",
|
||||
static_cast<unsigned long long>(info->ContextRecord->Rip),
|
||||
static_cast<unsigned long long>(info->ContextRecord->Rsp),
|
||||
static_cast<unsigned long long>(info->ContextRecord->Rcx),
|
||||
static_cast<unsigned long long>(info->ContextRecord->Rdx),
|
||||
static_cast<unsigned long long>(info->ContextRecord->R8),
|
||||
static_cast<unsigned long long>(info->ContextRecord->R9));
|
||||
loaded_modules();
|
||||
wchar_t directory[32768]{};
|
||||
const DWORD n = GetEnvironmentVariableW(L"D4R_DIAG_DIR", directory, 32768);
|
||||
@@ -74,12 +86,24 @@ inline LONG WINAPI unhandled(EXCEPTION_POINTERS* info)
|
||||
}
|
||||
return EXCEPTION_EXECUTE_HANDLER;
|
||||
}
|
||||
inline LONG WINAPI first_chance(EXCEPTION_POINTERS* info)
|
||||
{
|
||||
// NGX installs its own final exception filter. Capture a diagnostic dump
|
||||
// before that filter can replace ours; do not swallow or retry the fault.
|
||||
const auto code = info->ExceptionRecord->ExceptionCode;
|
||||
if (code == EXCEPTION_ACCESS_VIOLATION || code == EXCEPTION_ILLEGAL_INSTRUCTION) {
|
||||
static LONG captured = 0;
|
||||
if (InterlockedCompareExchange(&captured, 1, 0) == 0) (void)unhandled(info);
|
||||
}
|
||||
return EXCEPTION_CONTINUE_SEARCH;
|
||||
}
|
||||
inline void start()
|
||||
{
|
||||
std::setvbuf(stdout, nullptr, _IONBF, 0);
|
||||
std::setvbuf(stderr, nullptr, _IONBF, 0);
|
||||
SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOGPFAULTERRORBOX);
|
||||
SetUnhandledExceptionFilter(unhandled);
|
||||
AddVectoredExceptionHandler(1, first_chance);
|
||||
std::printf("d4r native Windows diagnostic x64 pid=%lu\n", GetCurrentProcessId());
|
||||
}
|
||||
struct Args {
|
||||
@@ -89,6 +113,8 @@ struct Args {
|
||||
std::string ngx_core, dlss_dll;
|
||||
std::string nvapi_dll;
|
||||
std::string ngx_abi = "driver";
|
||||
std::string ngx_mode = "init";
|
||||
unsigned preset = 11;
|
||||
std::string fixture_dir;
|
||||
std::string kernel_name = "enc1";
|
||||
unsigned iterations = 32;
|
||||
@@ -108,6 +134,13 @@ struct Args {
|
||||
else if (key == "--dlss-dll") dlss_dll = value;
|
||||
else if (key == "--nvapi-dll") nvapi_dll = value;
|
||||
else if (key == "--ngx-abi") ngx_abi = value;
|
||||
else if (key == "--ngx-mode") ngx_mode = value;
|
||||
else if (key == "--preset") {
|
||||
size_t end = 0;
|
||||
preset = static_cast<unsigned>(std::stoul(value, &end));
|
||||
if (end != value.size() || (preset != 5 && preset != 11 && preset != 13))
|
||||
throw std::runtime_error("preset must be 5 (E), 11 (K) or 13 (M)");
|
||||
}
|
||||
else if (key == "--fixture-dir") fixture_dir = value;
|
||||
else if (key == "--kernel-name") kernel_name = value;
|
||||
else if (key == "--iterations") {
|
||||
|
||||
@@ -0,0 +1,147 @@
|
||||
#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*);
|
||||
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");
|
||||
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);
|
||||
CUdeviceptr scratch = 0;
|
||||
cuda.check(cuda.cuMemAlloc_v2(&scratch, 64ull * 1024 * 1024), "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", 64ull * 1024 * 1024);
|
||||
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);
|
||||
}
|
||||
}
|
||||
@@ -1,5 +1,6 @@
|
||||
#include "hip_api.h"
|
||||
#include "cuda_api.h"
|
||||
#include "ngx_cuda_evaluate.h"
|
||||
|
||||
// Same NGX C ABI and freestanding MSVC parameter accessors as the upstream shim.
|
||||
// NVIDIA SDK headers and proprietary binaries are not build dependencies.
|
||||
@@ -110,6 +111,9 @@ int main(int argc, char** argv)
|
||||
unsigned roundtrip = 0;
|
||||
check(d4r_ngx_get_uint(parameters, "Width", &roundtrip), "MSVC parameter ABI roundtrip");
|
||||
if (roundtrip != 640) throw std::runtime_error("NGX parameter ABI mismatch");
|
||||
if (args.ngx_mode == "evaluate")
|
||||
run_synthetic_dlss(cuda, core, parameters, args.preset, args.iterations, data_path);
|
||||
else if (args.ngx_mode != "init") throw std::runtime_error("--ngx-mode must be init or evaluate");
|
||||
check(destroy(parameters), "DestroyParameters(allocated)"); parameters = nullptr;
|
||||
check(destroy(caps), "DestroyParameters(capabilities)"); caps = nullptr;
|
||||
loaded_modules();
|
||||
|
||||
Reference in new issue
Block a user