Initialize Windows NGX CUDA with HIP-backed NVAPI topology
This commit is contained in:
1 parent
239a644270
commit
64669c2c36
9 files changed
+431
-47
No files matched your search
@@ -0,0 +1,44 @@
|
||||
#pragma once
|
||||
#include "diagnostic.h"
|
||||
// CUDA Driver API ABI subset. No CUDA SDK, CUDA runtime or NVIDIA binary is needed.
|
||||
using CUdevice = int;
|
||||
using CUdeviceptr = uint64_t;
|
||||
using CUcontext = void*;
|
||||
using CUmodule = void*;
|
||||
using CUfunction = void*;
|
||||
struct CudaApi {
|
||||
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);
|
||||
D4R_CU(cuDriverGetVersion, int, int*);
|
||||
D4R_CU(cuDeviceGetCount, int, int*);
|
||||
D4R_CU(cuDeviceGet, int, CUdevice*, int);
|
||||
D4R_CU(cuDeviceGetName, int, char*, int, CUdevice);
|
||||
D4R_CU(cuDeviceGetAttribute, int, int*, int, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRetain, int, CUcontext*, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRelease_v2, int, CUdevice);
|
||||
D4R_CU(cuCtxSetCurrent, int, CUcontext);
|
||||
D4R_CU(cuCtxCreate_v2, int, CUcontext*, unsigned, CUdevice);
|
||||
D4R_CU(cuCtxDestroy_v2, int, CUcontext);
|
||||
D4R_CU(cuCtxSynchronize, int);
|
||||
D4R_CU(cuMemAlloc_v2, int, CUdeviceptr*, size_t);
|
||||
D4R_CU(cuMemFree_v2, int, CUdeviceptr);
|
||||
D4R_CU(cuMemcpyHtoD_v2, int, CUdeviceptr, const void*, size_t);
|
||||
D4R_CU(cuMemcpyDtoH_v2, int, void*, CUdeviceptr, size_t);
|
||||
D4R_CU(cuModuleLoadData, int, CUmodule*, const void*);
|
||||
D4R_CU(cuModuleUnload, int, CUmodule);
|
||||
D4R_CU(cuModuleGetFunction, int, CUfunction*, CUmodule, const char*);
|
||||
D4R_CU(cuLaunchKernel, int, CUfunction, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned,
|
||||
unsigned, void*, void**, void**);
|
||||
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)) {}
|
||||
void check(int result, const char* call) {
|
||||
const char* name = "unknown", *message = "unknown";
|
||||
if (result) { cuGetErrorName(result, &name); cuGetErrorString(result, &message); }
|
||||
else name = "CUDA_SUCCESS";
|
||||
std::printf("CUDA %s -> %d (%s)\n", call, result, name ? name : "unknown");
|
||||
if (result) throw std::runtime_error(std::string(call) + ": " + (message ? message : "unknown"));
|
||||
}
|
||||
};
|
||||
@@ -1,48 +1,7 @@
|
||||
#include "hip_api.h"
|
||||
#include <algorithm>
|
||||
|
||||
// CUDA Driver API ABI subset. No CUDA SDK, CUDA runtime or NVIDIA binary is needed.
|
||||
using CUdevice = int;
|
||||
using CUdeviceptr = uint64_t;
|
||||
using CUcontext = void*;
|
||||
using CUmodule = void*;
|
||||
using CUfunction = void*;
|
||||
struct CudaApi {
|
||||
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);
|
||||
D4R_CU(cuDriverGetVersion, int, int*);
|
||||
D4R_CU(cuDeviceGetCount, int, int*);
|
||||
D4R_CU(cuDeviceGet, int, CUdevice*, int);
|
||||
D4R_CU(cuDeviceGetName, int, char*, int, CUdevice);
|
||||
D4R_CU(cuDeviceGetAttribute, int, int*, int, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRetain, int, CUcontext*, CUdevice);
|
||||
D4R_CU(cuDevicePrimaryCtxRelease_v2, int, CUdevice);
|
||||
D4R_CU(cuCtxSetCurrent, int, CUcontext);
|
||||
D4R_CU(cuCtxCreate_v2, int, CUcontext*, unsigned, CUdevice);
|
||||
D4R_CU(cuCtxDestroy_v2, int, CUcontext);
|
||||
D4R_CU(cuCtxSynchronize, int);
|
||||
D4R_CU(cuMemAlloc_v2, int, CUdeviceptr*, size_t);
|
||||
D4R_CU(cuMemFree_v2, int, CUdeviceptr);
|
||||
D4R_CU(cuMemcpyHtoD_v2, int, CUdeviceptr, const void*, size_t);
|
||||
D4R_CU(cuMemcpyDtoH_v2, int, void*, CUdeviceptr, size_t);
|
||||
D4R_CU(cuModuleLoadData, int, CUmodule*, const void*);
|
||||
D4R_CU(cuModuleUnload, int, CUmodule);
|
||||
D4R_CU(cuModuleGetFunction, int, CUfunction*, CUmodule, const char*);
|
||||
D4R_CU(cuLaunchKernel, int, CUfunction, unsigned, unsigned, unsigned, unsigned, unsigned, unsigned,
|
||||
unsigned, void*, void**, void**);
|
||||
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)) {}
|
||||
void check(int result, const char* call) {
|
||||
const char* name = "unknown", *message = "unknown";
|
||||
cuGetErrorName(result, &name);
|
||||
cuGetErrorString(result, &message);
|
||||
std::printf("CUDA %s -> %d (%s)\n", call, result, name ? name : "unknown");
|
||||
if (result) throw std::runtime_error(std::string(call) + ": " + (message ? message : "unknown"));
|
||||
}
|
||||
};
|
||||
#include "cuda_api.h"
|
||||
|
||||
static constexpr char ptx[] = R"PTX(
|
||||
.version 7.0
|
||||
|
||||
@@ -86,6 +86,9 @@ struct Args {
|
||||
std::string hip_root, cuda_dll, module, context = "primary";
|
||||
std::string stream_lifetime = "persistent";
|
||||
std::string interop_mode = "roundtrip";
|
||||
std::string ngx_core, dlss_dll;
|
||||
std::string nvapi_dll;
|
||||
std::string ngx_abi = "driver";
|
||||
unsigned iterations = 32;
|
||||
int device = -1;
|
||||
Args(int argc, char** argv) {
|
||||
@@ -99,6 +102,10 @@ struct Args {
|
||||
else if (key == "--context") context = value;
|
||||
else if (key == "--stream-lifetime") stream_lifetime = value;
|
||||
else if (key == "--interop-mode") interop_mode = value;
|
||||
else if (key == "--ngx-core") ngx_core = value;
|
||||
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 == "--iterations") {
|
||||
size_t end = 0;
|
||||
iterations = static_cast<unsigned>(std::stoul(value, &end));
|
||||
|
||||
@@ -0,0 +1,127 @@
|
||||
#include "hip_api.h"
|
||||
#include "cuda_api.h"
|
||||
|
||||
// Same NGX C ABI and freestanding MSVC parameter accessors as the upstream shim.
|
||||
// NVIDIA SDK headers and proprietary binaries are not build dependencies.
|
||||
struct NgxPathListInfo { const wchar_t* const* Path; unsigned Length; };
|
||||
struct NgxLoggingInfo { void* LoggingCallback; int MinimumLoggingLevel; bool DisableOtherLoggingSinks; };
|
||||
struct NgxFeatureCommonInfo { NgxPathListInfo PathListInfo; void* InternalData; NgxLoggingInfo LoggingInfo; };
|
||||
static_assert(sizeof(NgxFeatureCommonInfo) == 40);
|
||||
extern "C" void d4r_ngx_set_uint(void*, const char*, unsigned);
|
||||
extern "C" unsigned d4r_ngx_get_uint(void*, const char*, unsigned*);
|
||||
extern "C" unsigned d4r_ngx_get_int(void*, const char*, int*);
|
||||
|
||||
static void ngx_log(const char* message, int level, unsigned component)
|
||||
{
|
||||
if (message) std::printf("NGX_LOG level=%d component=%u %s\n", level, component, message);
|
||||
}
|
||||
static void check(unsigned result, const char* call)
|
||||
{
|
||||
std::printf("NGX %s -> 0x%08x\n", call, result);
|
||||
if (result != 1) throw std::runtime_error(std::string(call) + " NGX result=" + std::to_string(result));
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
using namespace d4r::diag;
|
||||
start();
|
||||
try {
|
||||
Args args(argc, argv);
|
||||
if (args.cuda_dll.empty() || args.ngx_core.empty() || args.dlss_dll.empty())
|
||||
throw std::runtime_error("--cuda-dll, --ngx-core and --dlss-dll absolute paths required");
|
||||
const std::filesystem::path core_path(wide(args.ngx_core)), dlss_path(wide(args.dlss_dll));
|
||||
if (dlss_path.filename() != L"nvngx_dlss.dll") throw std::runtime_error("DLSS file must be named nvngx_dlss.dll");
|
||||
HipApi hip(args.hip_root);
|
||||
hipDeviceProp_t props{};
|
||||
hip.select_gfx1201(args.device, props);
|
||||
SearchDirectory cuda_search(std::filesystem::path(wide(args.cuda_dll)).parent_path());
|
||||
CudaApi cuda(args.cuda_dll);
|
||||
cuda.check(cuda.cuInit(0), "cuInit");
|
||||
int count = 0;
|
||||
cuda.check(cuda.cuDeviceGetCount(&count), "cuDeviceGetCount");
|
||||
CUdevice device = -1;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
CUdevice candidate = -1;
|
||||
int bus = -1, pci_device = -1, domain = -1;
|
||||
cuda.check(cuda.cuDeviceGet(&candidate, i), "cuDeviceGet");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&bus, 33, candidate), "cuDeviceGetAttribute(PCI_BUS_ID)");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&pci_device, 34, candidate), "cuDeviceGetAttribute(PCI_DEVICE_ID)");
|
||||
cuda.check(cuda.cuDeviceGetAttribute(&domain, 50, candidate), "cuDeviceGetAttribute(PCI_DOMAIN_ID)");
|
||||
if (bus == props.pciBusID && pci_device == props.pciDeviceID && domain == props.pciDomainID) device = candidate;
|
||||
}
|
||||
if (device < 0) throw std::runtime_error("NGX CUDA/HIP PCI identity mismatch");
|
||||
CUcontext context = nullptr;
|
||||
cuda.check(cuda.cuDevicePrimaryCtxRetain(&context, device), "cuDevicePrimaryCtxRetain");
|
||||
struct ContextCleanup {
|
||||
CudaApi& cuda; CUcontext& context; CUdevice device;
|
||||
~ContextCleanup() { if (context) { (void)cuda.cuCtxSetCurrent(nullptr); (void)cuda.cuDevicePrimaryCtxRelease_v2(device); } }
|
||||
} cc{cuda, context, device};
|
||||
cuda.check(cuda.cuCtxSetCurrent(context), "cuCtxSetCurrent");
|
||||
auto nvapi_path = std::filesystem::path(wide(args.cuda_dll)).parent_path() / L"nvapi64.dll";
|
||||
if (!std::filesystem::exists(nvapi_path)) nvapi_path = nvapi_path.parent_path().parent_path() / L"nvapi64.dll";
|
||||
if (!args.nvapi_dll.empty()) nvapi_path = wide(args.nvapi_dll);
|
||||
Library nvapi(nvapi_path);
|
||||
SearchDirectory feature_search(dlss_path.parent_path());
|
||||
// Preload the supplied absolute file so a system copy cannot replace it.
|
||||
Library dlss(dlss_path), core(core_path);
|
||||
using Init = unsigned(*)(unsigned long long, const wchar_t*, const NgxFeatureCommonInfo*, unsigned);
|
||||
using DriverInit = unsigned(*)(unsigned long long, const wchar_t*, unsigned);
|
||||
using Shutdown = unsigned(*)();
|
||||
using Parameters = unsigned(*)(void**);
|
||||
using Destroy = unsigned(*)(void*);
|
||||
const auto init = core.symbol<Init>("NVSDK_NGX_CUDA_Init");
|
||||
const auto driver_init = core.symbol<DriverInit>("NVSDK_NGX_CUDA_Init");
|
||||
const auto shutdown = core.symbol<Shutdown>("NVSDK_NGX_CUDA_Shutdown");
|
||||
const auto capabilities = core.symbol<Parameters>("NVSDK_NGX_CUDA_GetCapabilityParameters");
|
||||
const auto allocate = core.symbol<Parameters>("NVSDK_NGX_CUDA_AllocateParameters");
|
||||
const auto destroy = core.symbol<Destroy>("NVSDK_NGX_CUDA_DestroyParameters");
|
||||
const std::wstring directory = dlss_path.parent_path().wstring();
|
||||
const wchar_t* paths[] = {directory.c_str()};
|
||||
const char* diagnostic_dir = std::getenv("D4R_DIAG_DIR");
|
||||
const auto data_path = diagnostic_dir ? std::filesystem::path(wide(diagnostic_dir)) :
|
||||
std::filesystem::current_path() / L"test-results" / L"ngx-init";
|
||||
std::filesystem::create_directories(data_path);
|
||||
NgxFeatureCommonInfo common{};
|
||||
common.PathListInfo = {paths, 1};
|
||||
common.LoggingInfo = {reinterpret_cast<void*>(&ngx_log), 2, false};
|
||||
loaded_modules();
|
||||
std::printf("STAGE NVSDK_NGX_CUDA_Init sdk=0x15 core=%s dlss=%s\n", args.ngx_core.c_str(), args.dlss_dll.c_str());
|
||||
if (args.ngx_abi == "driver") check(driver_init(241534723ull, data_path.c_str(), 0x15), "NVSDK_NGX_CUDA_Init(driver ABI)");
|
||||
else if (args.ngx_abi == "sdk") check(init(241534723ull, data_path.c_str(), &common, 0x15), "NVSDK_NGX_CUDA_Init(SDK ABI)");
|
||||
else throw std::runtime_error("--ngx-abi must be driver or sdk");
|
||||
bool initialized = true;
|
||||
struct NgxCleanup { Shutdown shutdown; bool& active; ~NgxCleanup() { if (active) (void)shutdown(); } } nc{shutdown, initialized};
|
||||
void* caps = nullptr;
|
||||
check(capabilities(&caps), "NVSDK_NGX_CUDA_GetCapabilityParameters");
|
||||
if (!caps) throw std::runtime_error("NGX returned null capability parameters");
|
||||
struct ParameterCleanup { Destroy destroy; void*& p; ~ParameterCleanup() { if (p) (void)destroy(p); } } pc{destroy, caps};
|
||||
int available = -1, init_result = -1, needs_driver = -1;
|
||||
check(d4r_ngx_get_int(caps, "SuperSampling.Available", &available), "Get(SuperSampling.Available)");
|
||||
check(d4r_ngx_get_int(caps, "SuperSampling.FeatureInitResult", &init_result), "Get(SuperSampling.FeatureInitResult)");
|
||||
check(d4r_ngx_get_int(caps, "SuperSampling.NeedsUpdatedDriver", &needs_driver), "Get(SuperSampling.NeedsUpdatedDriver)");
|
||||
std::printf("CAPABILITIES available=%d feature_init=0x%08x needs_driver=%d\n", available, static_cast<unsigned>(init_result), needs_driver);
|
||||
if (available != 1 || init_result != 1 || needs_driver != 0)
|
||||
throw std::runtime_error("DLSS CUDA feature is unavailable; initialization alone is insufficient");
|
||||
void* parameters = nullptr;
|
||||
check(allocate(¶meters), "NVSDK_NGX_CUDA_AllocateParameters");
|
||||
if (!parameters) throw std::runtime_error("Null NGX parameters");
|
||||
ParameterCleanup ac{destroy, parameters};
|
||||
d4r_ngx_set_uint(parameters, "Width", 640);
|
||||
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");
|
||||
check(destroy(parameters), "DestroyParameters(allocated)"); parameters = nullptr;
|
||||
check(destroy(caps), "DestroyParameters(capabilities)"); caps = nullptr;
|
||||
loaded_modules();
|
||||
check(shutdown(), "NVSDK_NGX_CUDA_Shutdown"); initialized = false;
|
||||
cuda.check(cuda.cuCtxSynchronize(), "cuCtxSynchronize");
|
||||
cuda.check(cuda.cuCtxSetCurrent(nullptr), "cuCtxSetCurrent(NULL)");
|
||||
cuda.check(cuda.cuDevicePrimaryCtxRelease_v2(device), "cuDevicePrimaryCtxRelease_v2"); context = nullptr;
|
||||
std::printf("PASS NGX_INIT architecture=gfx1201 sr_available=1 transformer_executed=0\n");
|
||||
return 0;
|
||||
} catch (const std::exception& e) {
|
||||
std::fprintf(stderr, "FAIL NGX_INIT %s\n", e.what());
|
||||
loaded_modules();
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
#include "diagnostic.h"
|
||||
#if defined(D4R_NVAPI_COMPAT)
|
||||
#include "hip_api.h"
|
||||
#include <memory>
|
||||
#include <mutex>
|
||||
#include <atomic>
|
||||
|
||||
namespace {
|
||||
// Public NVAPI ABI: NVIDIA/nvapi nvapi.h and nvapi_interface.h.
|
||||
// AD100 describes the CUDA/NGX model profile, not the physical HIP target.
|
||||
struct ArchInfo { unsigned version, architecture, implementation, revision; };
|
||||
struct LogicalGpuData { unsigned version; void* os_adapter_id; unsigned count; void* physical[64]; unsigned reserved[8]; };
|
||||
static_assert(sizeof(LogicalGpuData) == 568);
|
||||
std::unique_ptr<d4r::diag::HipApi> hip;
|
||||
hipDeviceProp_t properties{};
|
||||
int device_ordinal = -1;
|
||||
int physical_handle;
|
||||
int logical_handle;
|
||||
std::mutex init_mutex;
|
||||
std::atomic<bool> runtime_ready{false};
|
||||
bool valid(void* handle) { return runtime_ready.load(std::memory_order_acquire) && handle == &physical_handle; }
|
||||
int __cdecl initialize()
|
||||
{
|
||||
try {
|
||||
std::lock_guard<std::mutex> lock(init_mutex);
|
||||
if (!runtime_ready.load(std::memory_order_acquire)) {
|
||||
const char* root = std::getenv("HIP_PATH");
|
||||
if (!root) return -5;
|
||||
auto runtime = std::make_unique<d4r::diag::HipApi>(root);
|
||||
device_ordinal = runtime->select_gfx1201(-1, properties);
|
||||
hip = std::move(runtime);
|
||||
runtime_ready.store(true, std::memory_order_release);
|
||||
}
|
||||
std::printf("NVAPI_COMPAT initialize physical=gfx1201 CUDA_model_profile=AD100\n");
|
||||
return 0;
|
||||
} catch (const std::exception& e) { std::fprintf(stderr, "NVAPI_COMPAT init failed: %s\n", e.what()); return -1; }
|
||||
}
|
||||
int __cdecl enumerate(void** handles, unsigned* count)
|
||||
{
|
||||
if (!handles || !count) return -5;
|
||||
if (!runtime_ready.load(std::memory_order_acquire)) return -4;
|
||||
handles[0] = &physical_handle; *count = 1;
|
||||
std::printf("NVAPI_COMPAT EnumPhysicalGPUs count=1 HIP_ordinal=%d\n", device_ordinal);
|
||||
return 0;
|
||||
}
|
||||
int __cdecl architecture(void* handle, ArchInfo* info)
|
||||
{
|
||||
if (!valid(handle) || !info) return -5;
|
||||
if (info->version != (sizeof(ArchInfo) | 1u << 16) && info->version != (sizeof(ArchInfo) | 2u << 16)) return -9;
|
||||
info->architecture = 0x190; info->implementation = 2; info->revision = 0x11;
|
||||
std::printf("NVAPI_COMPAT GetArchInfo profile=AD102 physical=gfx1201\n");
|
||||
return 0;
|
||||
}
|
||||
int __cdecl adapter_id(void* handle, void* luid)
|
||||
{
|
||||
if (!valid(handle) || !luid) return -5;
|
||||
std::memcpy(luid, properties.luid, sizeof(LUID));
|
||||
std::printf("NVAPI_COMPAT GetAdapterIdFromPhysicalGpu matched_HIP_LUID=1\n");
|
||||
return 0;
|
||||
}
|
||||
int __cdecl logical_from_physical(void* handle, void** logical)
|
||||
{
|
||||
if (!valid(handle) || !logical) return -5;
|
||||
*logical = &logical_handle;
|
||||
std::printf("NVAPI_COMPAT GetLogicalGPUFromPhysicalGPU count=1\n");
|
||||
return 0;
|
||||
}
|
||||
int __cdecl logical_info(void* logical, LogicalGpuData* info)
|
||||
{
|
||||
std::printf("NVAPI_COMPAT GetLogicalGpuInfo handle_valid=%d info=%p version=0x%08x os_adapter_buffer=%p\n",
|
||||
runtime_ready.load(std::memory_order_acquire) && logical == &logical_handle, static_cast<void*>(info), info ? info->version : 0,
|
||||
info ? info->os_adapter_id : nullptr);
|
||||
if (!runtime_ready.load(std::memory_order_acquire) || logical != &logical_handle || !info || !info->os_adapter_id) return -5;
|
||||
if (info->version != (sizeof(LogicalGpuData) | 1u << 16)) return -9;
|
||||
std::memcpy(info->os_adapter_id, properties.luid, sizeof(LUID));
|
||||
info->count = 1;
|
||||
std::memset(info->physical, 0, sizeof(info->physical));
|
||||
std::memset(info->reserved, 0, sizeof(info->reserved));
|
||||
info->physical[0] = &physical_handle;
|
||||
std::printf("NVAPI_COMPAT GetLogicalGpuInfo matched_HIP_LUID=1\n");
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
// Trace build forwards responses unchanged. The compatibility build adds the
|
||||
// public topology APIs above; other queries still go to upstream ZLUDA NVAPI.
|
||||
extern "C" __declspec(dllexport) void* __cdecl nvapi_QueryInterface(unsigned id)
|
||||
{
|
||||
try {
|
||||
#if defined(D4R_NVAPI_COMPAT)
|
||||
switch (id) {
|
||||
case 0x0150e828: return reinterpret_cast<void*>(&initialize);
|
||||
case 0xe5ac921f: return reinterpret_cast<void*>(&enumerate);
|
||||
case 0xd8265d24: return reinterpret_cast<void*>(&architecture);
|
||||
case 0x0ff07fde: return reinterpret_cast<void*>(&adapter_id);
|
||||
case 0xadd604d1: return reinterpret_cast<void*>(&logical_from_physical);
|
||||
case 0x842b066e: return reinterpret_cast<void*>(&logical_info);
|
||||
}
|
||||
#endif
|
||||
const char* path = std::getenv("D4R_NVAPI_BACKEND");
|
||||
if (!path || !*path) throw std::runtime_error("D4R_NVAPI_BACKEND absolute path required");
|
||||
static d4r::diag::Library backend(d4r::diag::wide(path));
|
||||
using Query = void* (__cdecl*)(unsigned);
|
||||
static Query query = backend.symbol<Query>("nvapi_QueryInterface");
|
||||
void* function = query(id);
|
||||
std::printf("NVAPI_QUERY id=0x%08x supported=%d\n", id, function != nullptr);
|
||||
return function;
|
||||
} catch (const std::exception& e) {
|
||||
std::fprintf(stderr, "NVAPI_TRACE_ERROR id=0x%08x %s\n", id, e.what());
|
||||
return nullptr;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user