Enable experimental native Windows RDNA3 and RDNA4 targets
Share explicit target metadata across build/runtime/package guards, preserve native gfx11 WMMA with a Windows LDS lane exchange, select HIP devices by D3D12 LUID, and reject wrong-target/vkd3d installations before changing game files. Review all issue #10 logs and document compile-only coverage plus RX9070XT regressions. Co-authored-by: realdody <[email protected]>
This commit is contained in:
1 parent
98de8e3a3e
commit
3fa963fd1b
32 files changed
+494
-162
No files matched your search
@@ -1,4 +1,4 @@
|
||||
"""Build a private RDNA4 texture override from the user's local DLSS DLL.
|
||||
"""Build a private target-specific texture override from the user's local DLSS DLL.
|
||||
|
||||
The resulting PTX/code object contains NVIDIA code and must remain local.
|
||||
No Wine, shell script, GPU, CUDA SDK or system installation is required.
|
||||
@@ -13,13 +13,14 @@ import re
|
||||
import shutil
|
||||
import subprocess
|
||||
import sys
|
||||
from gpu_targets import TARGETS
|
||||
|
||||
|
||||
def require_code_object_target(path, target):
|
||||
with path.open('rb') as file:
|
||||
header = file.read(64)
|
||||
if (len(header) != 64 or header[:6] != b'\x7fELF\x02\x01' or
|
||||
header[18:20] != b'\xe0\x00' or header[48] != {'gfx1200': 0x48, 'gfx1201': 0x4e}[target]):
|
||||
header[18:20] != b'\xe0\x00' or header[48] != TARGETS[target]['elfMachine']):
|
||||
raise RuntimeError(f'Code object does not target {target}: {path}')
|
||||
|
||||
|
||||
@@ -30,7 +31,7 @@ def main():
|
||||
parser.add_argument('--zluda-root', type=pathlib.Path, required=True)
|
||||
parser.add_argument('--kernel', required=True)
|
||||
parser.add_argument('--output-directory', type=pathlib.Path, required=True)
|
||||
parser.add_argument('--gpu-arch', choices=['gfx1200', 'gfx1201'], default='gfx1201')
|
||||
parser.add_argument('--gpu-arch', choices=tuple(TARGETS), default='gfx1201')
|
||||
parser.add_argument('--shader-mode', choices=['cu', 'wgp'], default='cu')
|
||||
args = parser.parse_args()
|
||||
if not re.fullmatch(r'hiluma_engine_(?:output_depth(?:inv|reg)_mv(?:hi|lo)_(?:hdr|ldr)(?:_max)?|'
|
||||
|
||||
@@ -87,7 +87,7 @@ API unsigned NVSDK_NGX_D3D12_GetFeatureRequirements(IDXGIAdapter* adapter, const
|
||||
DXGI_ADAPTER_DESC desc{}; d4r::win::dx(adapter->GetDesc(&desc), "NGX requirements adapter");
|
||||
// OptiScaler can override the DXGI vendor ID. The physical HIP
|
||||
// architecture and full adapter LUID are the authoritative identity.
|
||||
d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{}; hip.select_architecture(-1,props);
|
||||
d4r::diag::HipApi hip(d4r::win::env_path("D4R_HIP_ROOT")); hipDeviceProp_t props{}; hip.select_architecture(-1,props,&desc.AdapterLuid);
|
||||
requirements->FeatureSupported = std::memcmp(&desc.AdapterLuid,props.luid,sizeof(LUID)) ? 4u : 0u;
|
||||
std::strcpy(requirements->MinOSVersion,"10.0.22000.0");
|
||||
std::printf("D4R_REQUIREMENTS feature=%u support=%u architecture=%s\n",info->FeatureID,requirements->FeatureSupported,d4r::diag::HipApi::target_arch());
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
{
|
||||
"schema": 1,
|
||||
"targets": [
|
||||
{"architecture":"gfx1100","elfMachine":65,"isaMajor":11,"family":"RDNA3"},
|
||||
{"architecture":"gfx1101","elfMachine":70,"isaMajor":11,"family":"RDNA3"},
|
||||
{"architecture":"gfx1102","elfMachine":71,"isaMajor":11,"family":"RDNA3"},
|
||||
{"architecture":"gfx1103","elfMachine":68,"isaMajor":11,"family":"RDNA3"},
|
||||
{"architecture":"gfx1150","elfMachine":67,"isaMajor":11,"family":"RDNA3.5"},
|
||||
{"architecture":"gfx1151","elfMachine":74,"isaMajor":11,"family":"RDNA3.5"},
|
||||
{"architecture":"gfx1152","elfMachine":85,"isaMajor":11,"family":"RDNA3.5"},
|
||||
{"architecture":"gfx1153","elfMachine":88,"isaMajor":11,"family":"RDNA3.5"},
|
||||
{"architecture":"gfx1154","elfMachine":87,"isaMajor":11,"family":"RDNA3.5"},
|
||||
{"architecture":"gfx1200","elfMachine":72,"isaMajor":12,"family":"RDNA4"},
|
||||
{"architecture":"gfx1201","elfMachine":78,"isaMajor":12,"family":"RDNA4"}
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
#include "hip_api.h"
|
||||
#include <fstream>
|
||||
#include <sstream>
|
||||
#include <iomanip>
|
||||
|
||||
static std::string json_string(const std::string& value) {
|
||||
std::ostringstream out; out << '"';
|
||||
for (unsigned char c : value) {
|
||||
if (c == '"' || c == '\\') out << '\\' << char(c);
|
||||
else if (c < 32) out << "\\u" << std::hex << std::setw(4) << std::setfill('0') << unsigned(c);
|
||||
else out << char(c);
|
||||
}
|
||||
out << '"'; return out.str();
|
||||
}
|
||||
int main(int argc, char** argv) {
|
||||
using namespace d4r::diag;
|
||||
start();
|
||||
try {
|
||||
Args args(argc, argv);
|
||||
if (args.output_dir.empty()) throw std::runtime_error("--output-dir is required");
|
||||
for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"})
|
||||
if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name);
|
||||
HipApi hip(args.hip_root);
|
||||
hip.check(hip.hipInit(0), "hipInit(inventory)");
|
||||
int count=0, version=0, driver=0;
|
||||
hip.check(hip.hipRuntimeGetVersion(&version), "hipRuntimeGetVersion");
|
||||
hip.check(hip.hipDriverGetVersion(&driver), "hipDriverGetVersion");
|
||||
std::printf("GPU_INVENTORY_RUNTIME runtime=%d driver_api=%d headers=%d.%d.%d sizeof(properties)=%zu\n",
|
||||
version,driver,HIP_VERSION_MAJOR,HIP_VERSION_MINOR,HIP_VERSION_PATCH,sizeof(hipDeviceProp_t));
|
||||
hip.check(hip.hipGetDeviceCount(&count), "hipGetDeviceCount");
|
||||
std::ostringstream json;
|
||||
json << "{\"schema\":1,\"runtimeVersion\":" << version << ",\"driverApiVersion\":" << driver << ",\"compiledTarget\":"
|
||||
<< json_string(D4R_TARGET_ARCH) << ",\"gpuWorkExecuted\":false,\"devices\":[";
|
||||
for (int i=0; i<count; ++i) {
|
||||
hipDeviceProp_t p{}; hip.check(hip.hipGetDeviceProperties(&p,i), "hipGetDevicePropertiesR0600");
|
||||
const auto arch=gpu_architecture(p.gcnArchName);
|
||||
std::printf("GPU_INVENTORY ordinal=%d name=%s gcnArchName=%s wave=%d build_eligible=%d\n",
|
||||
i,p.name,p.gcnArchName,p.warpSize,supported_gpu_target(arch));
|
||||
if (i) json << ',';
|
||||
json << "{\"ordinal\":" << i << ",\"name\":" << json_string(p.name)
|
||||
<< ",\"gcnArchName\":" << json_string(p.gcnArchName) << ",\"architecture\":" << json_string(arch)
|
||||
<< ",\"wavefrontSize\":" << p.warpSize << ",\"vramBytes\":" << p.totalGlobalMem
|
||||
<< ",\"buildEligible\":" << (supported_gpu_target(arch) ? "true" : "false") << '}';
|
||||
}
|
||||
json << "]}\n";
|
||||
std::filesystem::create_directories(wide(args.output_dir));
|
||||
std::ofstream file(std::filesystem::path(wide(args.output_dir))/L"gpu-inventory.json",std::ios::binary);
|
||||
file << json.str(); file.close();
|
||||
if (!file) throw std::runtime_error("Cannot write GPU inventory");
|
||||
if (!count) throw std::runtime_error("HIP reports no devices");
|
||||
loaded_modules();
|
||||
std::printf("PASS GPU_INVENTORY devices=%d gpu_work_executed=0\n",count);
|
||||
return 0;
|
||||
} catch (const std::exception& error) {
|
||||
std::fprintf(stderr,"FAIL GPU_INVENTORY %s\n",error.what()); loaded_modules(); return 4;
|
||||
}
|
||||
}
|
||||
@@ -4,6 +4,7 @@
|
||||
#include <fstream>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "gpu_targets.generated.h"
|
||||
|
||||
#ifndef D4R_TARGET_ARCH
|
||||
#define D4R_TARGET_ARCH "gfx1201"
|
||||
@@ -11,7 +12,8 @@
|
||||
|
||||
namespace d4r::diag {
|
||||
inline bool supported_gpu_target(const std::string& target) {
|
||||
return target == "gfx1200" || target == "gfx1201";
|
||||
for (const auto& row : gpu_targets) if (target == row.architecture) return true;
|
||||
return false;
|
||||
}
|
||||
inline std::string gpu_architecture(const std::string& name) {
|
||||
return name.substr(0, name.find(':'));
|
||||
@@ -29,11 +31,8 @@ inline std::string code_object_target(const std::filesystem::path& path) {
|
||||
header[0] != 0x7f || header[1] != 'E' || header[2] != 'L' || header[3] != 'F' ||
|
||||
header[4] != 2 || header[5] != 1 || header[18] != 0xe0 || header[19] != 0)
|
||||
throw std::runtime_error("Invalid AMDGPU ELF64 code object: " + path.string());
|
||||
switch (header[48]) {
|
||||
case 0x48: return "gfx1200";
|
||||
case 0x4e: return "gfx1201";
|
||||
default: throw std::runtime_error("Unsupported AMDGPU code-object target: " + path.string());
|
||||
}
|
||||
for (const auto& row : gpu_targets) if (header[48] == row.elf_machine) return row.architecture;
|
||||
throw std::runtime_error("Unsupported AMDGPU code-object target: " + path.string());
|
||||
}
|
||||
inline void require_code_object_target(const std::filesystem::path& path, const std::string& expected) {
|
||||
require_gpu_target(code_object_target(path), expected);
|
||||
|
||||
@@ -5,10 +5,11 @@ int main(int argc, char** argv) {
|
||||
using namespace d4r::diag;
|
||||
try {
|
||||
unsigned rejected = 0, objects = 0;
|
||||
for (const char* target : {"gfx1200", "gfx1201"}) {
|
||||
for (const auto& info : gpu_targets) {
|
||||
const char* target = info.architecture;
|
||||
require_gpu_target(target, target);
|
||||
require_gpu_target(std::string(target) + ":xnack-:sramecc-", target);
|
||||
for (const char* invalid : {"gfx1101", "gfx12000", "gfx1202", "gfx1250", ""}) {
|
||||
for (const char* invalid : {"gfx1030", "gfx1104", "gfx1155", "gfx1170", "gfx12000", "gfx1202", "gfx1250", ""}) {
|
||||
try { require_gpu_target(invalid, target); }
|
||||
catch (const std::exception&) { ++rejected; continue; }
|
||||
throw std::runtime_error("Invalid GPU target accepted");
|
||||
|
||||
@@ -0,0 +1,7 @@
|
||||
#pragma once
|
||||
#include <array>
|
||||
namespace d4r::diag {
|
||||
struct GpuTarget { const char* architecture; unsigned elf_machine; unsigned isa_major; const char* family; };
|
||||
inline constexpr std::array<GpuTarget, @d4r_target_count@> gpu_targets = {{
|
||||
@d4r_target_cpp_entries@}};
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
"""Canonical Windows build target registry, shared with CMake and PowerShell."""
|
||||
import json
|
||||
import pathlib
|
||||
|
||||
data = json.loads(pathlib.Path(__file__).with_name('gpu-targets.json').read_text())
|
||||
if data['schema'] != 1:
|
||||
raise RuntimeError('Unsupported GPU target registry schema')
|
||||
TARGETS = {row['architecture']: row for row in data['targets']}
|
||||
if len(TARGETS) != len(data['targets']):
|
||||
raise RuntimeError('Duplicate GPU target')
|
||||
@@ -42,7 +42,7 @@ struct HipApi {
|
||||
if (result != hipSuccess) throw std::runtime_error(std::string(call) + ": " + hipGetErrorString(result));
|
||||
}
|
||||
static const char* target_arch() { return D4R_TARGET_ARCH; }
|
||||
int select_architecture(int requested, hipDeviceProp_t& selected) const {
|
||||
int select_architecture(int requested, hipDeviceProp_t& selected, const void* required_luid = nullptr) const {
|
||||
// Architecture spoofing invalidates a gfx12 correctness test.
|
||||
for (const char* name : {"HSA_OVERRIDE_GFX_VERSION", "HSA_OVERRIDE_GFX_VERSION_0"})
|
||||
if (std::getenv(name)) throw std::runtime_error(std::string("Unset architecture override ") + name);
|
||||
@@ -61,12 +61,14 @@ struct HipApi {
|
||||
i, props.name, props.gcnArchName, props.warpSize, props.totalGlobalMem,
|
||||
props.pciDomainID, props.pciBusID, props.pciDeviceID);
|
||||
const std::string arch = props.gcnArchName;
|
||||
if (gpu_architecture(arch) == target_arch() && (requested < 0 || requested == i) && found < 0) {
|
||||
if (gpu_architecture(arch) == target_arch() && (requested < 0 || requested == i) && found < 0 &&
|
||||
(!required_luid || std::memcmp(props.luid, required_luid, sizeof(props.luid)) == 0)) {
|
||||
found = i;
|
||||
selected = props;
|
||||
}
|
||||
}
|
||||
if (found < 0) throw std::runtime_error(std::string("No selected ") + target_arch() + " device; refusing architecture substitution");
|
||||
if (found < 0) throw std::runtime_error(std::string("No selected ") + target_arch() +
|
||||
" device for this adapter; use the package matching the actual HIP architecture (see GPU log)");
|
||||
require_gpu_target(selected.gcnArchName, target_arch());
|
||||
check(hipSetDevice(found), "hipSetDevice");
|
||||
std::printf("SELECTED HIP ordinal=%d architecture=%s validation=%s\n", found, target_arch(),
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
#!/usr/bin/env python3
|
||||
"""Compare RDNA4 K enc1/enc2 launches with the existing numpy reference."""
|
||||
"""Compare native K enc1/enc2 launches with the existing numpy reference."""
|
||||
import argparse
|
||||
import math
|
||||
import pathlib
|
||||
@@ -73,7 +73,7 @@ def main():
|
||||
# after each K stage has been compared on captured DLSS weights/activations.
|
||||
if full_abs > 0.001 or full_psnr < 60.0 or merged_abs > 0.001 or merged_psnr < 60.0:
|
||||
raise RuntimeError(f"K {args.kernel_name} numerical mismatch against pwin_model.py")
|
||||
print(f"PASS K_REFERENCE reference_layout=gfx12 kernel={args.kernel_name} "
|
||||
print(f"PASS K_REFERENCE reference=logical kernel={args.kernel_name} "
|
||||
"nonzero_weights=1 transformer_executed=1 nan_inf=0")
|
||||
|
||||
|
||||
|
||||
@@ -139,7 +139,7 @@ def validate(layer, directory, output, window_count=1, real_capture=False):
|
||||
if maximum > absolute_limit or psnr < 60:
|
||||
worst = int(difference.argmax())
|
||||
raise RuntimeError(f'K mismatch at {worst}: reference={expected[worst]}, gpu={actual[worst]}')
|
||||
print(f'PASS K_REPLAY_REFERENCE kernel={layer} reference_layout=gfx12 nan_inf=0')
|
||||
print(f'PASS K_REPLAY_REFERENCE kernel={layer} reference=logical nan_inf=0')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -153,7 +153,7 @@ def validate(layer, directory, output, block_count, real_capture, exact=False):
|
||||
maximum, peak, psnr, steps = metrics(expected, actual, f'M_REPLAY_REFERENCE kernel={layer}')
|
||||
if ((exact or not real_capture) and maximum != 0) or (real_capture and (steps.max() > 1 or psnr < 50)):
|
||||
raise RuntimeError(f'M mismatch: exact synthetic / <=1 FP8 step and >=50 dB real gate; peak={peak}')
|
||||
print(f'PASS M_REPLAY_REFERENCE kernel={layer} blocks={len(pairs)} reference_layout=gfx12 nan_inf=0')
|
||||
print(f'PASS M_REPLAY_REFERENCE kernel={layer} blocks={len(pairs)} reference=logical nan_inf=0')
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
||||
@@ -94,9 +94,9 @@ struct Runtime {
|
||||
if (!d3d) throw std::runtime_error("Null D3D12 device");
|
||||
try {
|
||||
hipDeviceProp_t props{};
|
||||
hip.select_architecture(-1, props);
|
||||
hip.verbose = cuda.verbose = !std::getenv("D4R_QUIET_API");
|
||||
const auto luid = adapter_luid(d3d);
|
||||
hip.select_architecture(-1, props, &luid);
|
||||
hip.verbose = cuda.verbose = !std::getenv("D4R_QUIET_API");
|
||||
if (std::memcmp(&luid, props.luid, sizeof(luid))) throw std::runtime_error("D3D12/HIP LUID mismatch");
|
||||
cuda.check(cuda.cuInit(0), "cuInit(Windows runtime)");
|
||||
int count = 0; cuda.check(cuda.cuDeviceGetCount(&count), "cuDeviceGetCount");
|
||||
|
||||
@@ -25,6 +25,7 @@ extern "C" __attribute__((global)) void d4r_wmma_gfx1201(
|
||||
const unsigned int group = lane >> 4;
|
||||
const _Float16* a = reinterpret_cast<const _Float16*>(a_bits);
|
||||
const _Float16* b = reinterpret_cast<const _Float16*>(b_bits);
|
||||
#if defined(__GFX12__)
|
||||
h8 a_frag, b_frag;
|
||||
f8 c_frag;
|
||||
for (unsigned int i = 0; i < 8u; ++i) {
|
||||
@@ -39,6 +40,23 @@ extern "C" __attribute__((global)) void d4r_wmma_gfx1201(
|
||||
b_frag, a_frag, result);
|
||||
for (unsigned int i = 0; i < 8u; ++i)
|
||||
output[row * 16u + group * 8u + i] = result[i];
|
||||
#else
|
||||
// Adapted from realdody/d4r ae530b0: real gfx11 operands/accumulators,
|
||||
// independent of the shared layout abstraction.
|
||||
d4r_wmma_h16 a_frag, b_frag;
|
||||
d4r_wmma_f8 c_frag;
|
||||
for (unsigned int i = 0; i < 16u; ++i) {
|
||||
a_frag[i] = a[row * 16u + i];
|
||||
b_frag[i] = b[row * 16u + i];
|
||||
}
|
||||
for (unsigned int i = 0; i < 8u; ++i)
|
||||
c_frag[i] = initial_c[(2u * i + group) * 16u + row];
|
||||
d4r_wmma_f8 result = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a_frag, b_frag, c_frag);
|
||||
if (steps == 2u)
|
||||
result = __builtin_amdgcn_wmma_f32_16x16x16_f16_w32(a_frag, b_frag, result);
|
||||
for (unsigned int i = 0; i < 8u; ++i)
|
||||
output[(2u * i + group) * 16u + row] = result[i];
|
||||
#endif
|
||||
}
|
||||
|
||||
// d4r's original gfx11 fragment contract, exercised independently of K/M.
|
||||
|
||||
Reference in new issue
Block a user