RDNA4 (experimental; untested on RDNA4 hardware) - kernels/common/wmma_layout.h picks the WMMA operand/accumulator layout per target; K (pwin_*) and M (swin_*) index through it, so the same sources build for gfx11 and gfx12. D4R_WMMA_LAYOUT=12 on gfx11 is a test shim that runs the gfx12 indexing on gfx11 WMMA. - M can use native e4m3 FP8 WMMA on gfx12 (D4R_NATIVE_FP8=1). Texture tails (enc0_tail, dec0_head) get gfx12 f16 and FP8 paths that match ZLUDA's gfx12 lowering. - ZLUDA patch 0007: gfx12 WMMA helpers, optional native FP8 MMA (D4R_ZLUDA_WMMA_FP8_NATIVE), a layout override for testing, and an architecture argument for d4r_emit. Texture kernels are now compiled offline per target through d4r_emit (D4R_ZLUDA_EMIT) instead of on the local GPU. - New d4r.ini option [Kernels] NativeFp8 (default on), wired through the shim and d4r_config.py. On gfx12 the bridge serves kernels/<arch>-fp8 when it is on. - The bridge picks the KFD GPU with the most SIMDs (so an iGPU is not chosen over a discrete GPU); D4R_GPU_ARCH overrides it. - kernels/build.sh and package_release.sh build gfx1100-gfx1103 and gfx1200/gfx1201 (plus -fp8 variants), and D4R_BUNDLE_TEX takes per-target folders. Performance (RDNA3; harness byte-identical unless noted) - ZLUDA patch 0006: m16n8k8 f16 MMAs on WMMA (E; not bit-exact, 52-55 dB per layer, D4R_ZLUDA_WMMA_K8=0 disables it), per-key weight-image slots for native prep kernels (d4r_prep_key_at, d4r_prep_key_slots), and wave64 builds of MMA-free kernels (D4R_ZLUDA_WAVE64). - M: packed FP8 encoding, conflict-free patch-merge reads, scalar weight loads in enc1, and weight prep once per weight set for the six enc3 tube blocks. - K: CU mode for the six non-wide pwin layers. - Texture kernels: every DLSS flag variant is built natively (24 K output, 8 M post, 4 M downsample, 4 M enc0), not only Townfall's; post and K output run as wave64. - M direct output: rrlite_downsample_kernel writes the shim's output buffer; the shim's default direct-output presets are now 11,13. Tools - dump_runner takes the prep grid from d4r_prep_blocks and refuses texture/surface dumps. - New pwin_check.py, swin_check.py, model_enc3.py and tail_check.hip. Docs: README GPU table and RDNA4 notes, building (patches 0002-0007, d4r_emit), native-kernels (RDNA4 section), performance (new numbers), packaging README. Version 0.1.2.
186 lines
7.2 KiB
C++
186 lines
7.2 KiB
C++
// Launch a native kernel on a replay dump (manifest.txt, args.bin, alloc-N.bin), save every allocation
|
|
// after the first launch, then time repeated launches.
|
|
// usage: dump_runner <hsaco> <kernel> <dump dir> <out dir> [iterations]
|
|
#include <hip/hip_runtime.h>
|
|
#include <algorithm>
|
|
#include <cstdio>
|
|
#include <cstdlib>
|
|
#include <cstring>
|
|
#include <fstream>
|
|
#include <sstream>
|
|
#include <string>
|
|
#include <vector>
|
|
|
|
#define CHECK(x) \
|
|
do \
|
|
{ \
|
|
hipError_t e_ = (x); \
|
|
if (e_ != hipSuccess) \
|
|
{ \
|
|
fprintf(stderr, "%s failed: %s\n", #x, hipGetErrorString(e_)); \
|
|
return 1; \
|
|
} \
|
|
} while (0)
|
|
|
|
static std::vector<char> read_file(const std::string& path)
|
|
{
|
|
std::ifstream f(path, std::ios::binary);
|
|
return std::vector<char>((std::istreambuf_iterator<char>(f)), std::istreambuf_iterator<char>());
|
|
}
|
|
|
|
int main(int argc, char** argv)
|
|
{
|
|
if (argc < 5)
|
|
{
|
|
fprintf(stderr, "usage: %s hsaco kernel dump out [iters]\n", argv[0]);
|
|
return 2;
|
|
}
|
|
const std::string dump = argv[3], out = argv[4];
|
|
const int iters = argc > 5 ? atoi(argv[5]) : 20;
|
|
hipModule_t mod;
|
|
hipFunction_t fn;
|
|
CHECK(hipModuleLoad(&mod, argv[1]));
|
|
CHECK(hipModuleGetFunction(&fn, mod, argv[2]));
|
|
hipFunction_t prep = nullptr;
|
|
if (hipModuleGetFunction(&prep, mod, (std::string(argv[2]) + "_prep").c_str()) != hipSuccess)
|
|
prep = nullptr;
|
|
// the prep grid: PREP_GRID, else the module's d4r_prep_blocks (what the ZLUDA hook launches), else 88
|
|
unsigned prepGrid = 88;
|
|
{
|
|
hipDeviceptr_t pb;
|
|
size_t pbs = 0;
|
|
uint32_t v = 0;
|
|
if (hipModuleGetGlobal(&pb, &pbs, mod, "d4r_prep_blocks") == hipSuccess && pbs == 4 &&
|
|
hipMemcpyDtoH(&v, pb, 4) == hipSuccess && v != 0)
|
|
prepGrid = v;
|
|
}
|
|
if (getenv("PREP_GRID"))
|
|
prepGrid = atoi(getenv("PREP_GRID"));
|
|
|
|
std::vector<char> args = read_file(dump + "/args.bin");
|
|
std::ifstream man(dump + "/manifest.txt");
|
|
std::string line;
|
|
unsigned grid[3] = {1, 1, 1}, block[3] = {1, 1, 1}, shared = 0;
|
|
std::vector<int> ids;
|
|
std::vector<void*> dev;
|
|
std::vector<size_t> sizes;
|
|
while (std::getline(man, line))
|
|
{
|
|
std::istringstream s(line);
|
|
std::string kind;
|
|
s >> kind;
|
|
if (kind == "texture" || kind == "surface")
|
|
{
|
|
// texture/surface object handles in the arguments would point at nothing here: the kernel faults
|
|
fprintf(stderr, "%s: the dump uses texture or surface objects, which dump_runner cannot recreate\n",
|
|
dump.c_str());
|
|
return 3;
|
|
}
|
|
if (kind == "launch")
|
|
s >> grid[0] >> grid[1] >> grid[2] >> block[0] >> block[1] >> block[2] >> shared;
|
|
else if (kind == "alloc")
|
|
{
|
|
int id;
|
|
std::string base;
|
|
size_t size;
|
|
s >> id >> base >> size;
|
|
std::vector<char> data = read_file(dump + "/alloc-" + std::to_string(id) + ".bin");
|
|
if (data.size() != size)
|
|
{
|
|
fprintf(stderr, "alloc %d size mismatch\n", id);
|
|
return 1;
|
|
}
|
|
void* d;
|
|
CHECK(hipMalloc(&d, size));
|
|
CHECK(hipMemcpy(d, data.data(), size, hipMemcpyHostToDevice));
|
|
ids.push_back(id);
|
|
dev.push_back(d);
|
|
sizes.push_back(size);
|
|
}
|
|
else if (kind == "pointer")
|
|
{
|
|
size_t off, delta;
|
|
int id;
|
|
s >> off >> id >> delta;
|
|
size_t i = std::find(ids.begin(), ids.end(), id) - ids.begin();
|
|
uint64_t v = (uint64_t)dev[i] + delta;
|
|
memcpy(args.data() + off, &v, 8);
|
|
}
|
|
}
|
|
{
|
|
hipDeviceptr_t bz;
|
|
size_t bzs = 0;
|
|
uint32_t v = 0;
|
|
if (hipModuleGetGlobal(&bz, &bzs, mod, "d4r_block_z") == hipSuccess && bzs == 4 &&
|
|
hipMemcpyDtoH(&v, bz, 4) == hipSuccess && v != 0)
|
|
block[2] = v;
|
|
}
|
|
{
|
|
hipDeviceptr_t gp;
|
|
size_t gs = 0;
|
|
uint32_t v = 0;
|
|
if (hipModuleGetGlobal(&gp, &gs, mod, "d4r_grid_x") == hipSuccess && gs == 4 &&
|
|
hipMemcpyDtoH(&v, gp, 4) == hipSuccess && v != 0)
|
|
{
|
|
grid[0] = v;
|
|
grid[1] = grid[2] = 1;
|
|
}
|
|
}
|
|
if (getenv("GRID_X"))
|
|
grid[0] = atoi(getenv("GRID_X"));
|
|
if (getenv("GRID_Y"))
|
|
grid[1] = atoi(getenv("GRID_Y"));
|
|
size_t argSize = args.size();
|
|
void* cfg[] = {HIP_LAUNCH_PARAM_BUFFER_POINTER, args.data(), HIP_LAUNCH_PARAM_BUFFER_SIZE, &argSize,
|
|
HIP_LAUNCH_PARAM_END};
|
|
const bool prepOnce = getenv("PREP_ONCE") != nullptr;
|
|
int launches = 0;
|
|
auto launch = [&]() {
|
|
if (prep != nullptr && !(prepOnce && launches++ > 0))
|
|
{
|
|
hipError_t e = hipModuleLaunchKernel(prep, prepGrid, 1, 1, 128, 1, 1, 0, nullptr, nullptr, cfg);
|
|
if (e != hipSuccess)
|
|
return e;
|
|
}
|
|
return hipModuleLaunchKernel(fn, grid[0], grid[1], grid[2], block[0], block[1], block[2], shared, nullptr,
|
|
nullptr, cfg);
|
|
};
|
|
CHECK(launch());
|
|
CHECK(hipDeviceSynchronize());
|
|
for (size_t i = 0; i < ids.size(); ++i)
|
|
{
|
|
std::vector<char> h(sizes[i]);
|
|
CHECK(hipMemcpy(h.data(), dev[i], sizes[i], hipMemcpyDeviceToHost));
|
|
std::ofstream(out + "/alloc-" + std::to_string(ids[i]) + ".bin", std::ios::binary).write(h.data(), h.size());
|
|
}
|
|
if (const char* dbg = getenv("DBG_GLOBAL"))
|
|
{
|
|
hipDeviceptr_t ptr;
|
|
size_t size = 0;
|
|
if (hipModuleGetGlobal(&ptr, &size, mod, dbg) == hipSuccess)
|
|
{
|
|
std::vector<char> h(size);
|
|
CHECK(hipMemcpyDtoH(h.data(), ptr, size));
|
|
std::ofstream(out + "/dbg.bin", std::ios::binary).write(h.data(), h.size());
|
|
}
|
|
}
|
|
hipEvent_t a, b;
|
|
CHECK(hipEventCreate(&a));
|
|
CHECK(hipEventCreate(&b));
|
|
std::vector<float> ms;
|
|
for (int i = 0; i < iters; ++i)
|
|
{
|
|
CHECK(hipEventRecord(a));
|
|
CHECK(launch());
|
|
CHECK(hipEventRecord(b));
|
|
CHECK(hipEventSynchronize(b));
|
|
float t;
|
|
CHECK(hipEventElapsedTime(&t, a, b));
|
|
ms.push_back(t);
|
|
}
|
|
std::sort(ms.begin(), ms.end());
|
|
if (!ms.empty())
|
|
printf("%s: median %.4f ms, min %.4f ms over %d launches\n", argv[2], ms[ms.size() / 2], ms[0], iters);
|
|
return 0;
|
|
}
|