// Replays one captured DLSS kernel launch outside Wine with buffer-only // arguments, using resource dumps from the Wine bridge's // D4R_CUDA_LAUNCH_DUMP_DIR instrumentation. // // usage: replay_dlss_layer MODULE KERNEL GX,GY,GZ BX,BY,BZ SHARED OUTDIR WORD... // MODULE PTX text or CUDA fatbin // WORD one 8-byte packed-argument word: either a hex literal (0x...) or // @FILE#0xOFFSET, which uploads FILE into a fresh device buffer (once // per distinct FILE) and passes that buffer's address plus OFFSET. // After the launch every uploaded buffer is written to OUTDIR/.out. #include #include #include #include #include #include #include #include #include using CUresult = int; using CUdevice = int; using CUcontext = void*; using CUmodule = void*; using CUfunction = void*; using CUstream = void*; using CUdeviceptr = uint64_t; template static Function load_function(void* library, const char* name) { void* raw = dlsym(library, name); Function function{}; static_assert(sizeof(function) == sizeof(raw)); std::memcpy(&function, &raw, sizeof(function)); return function; } static bool read_file(const std::string& path, std::vector& data) { std::ifstream input(path, std::ios::binary); if (!input) return false; data.assign(std::istreambuf_iterator(input), std::istreambuf_iterator()); return true; } static bool parse_triple(const char* text, unsigned int values[3]) { return std::sscanf(text, "%u,%u,%u", &values[0], &values[1], &values[2]) == 3; } struct Buffer { std::string path; CUdeviceptr device = 0; size_t bytes = 0; }; int main(int argc, char** argv) { if (argc < 8) { std::fprintf(stderr, "usage: %s MODULE KERNEL GX,GY,GZ BX,BY,BZ SHARED OUTDIR WORD...\n", argv[0]); return 2; } unsigned int grid[3], block[3]; if (!parse_triple(argv[3], grid) || !parse_triple(argv[4], block)) { std::fprintf(stderr, "grid and block must be X,Y,Z\n"); return 2; } const unsigned int shared = static_cast(std::strtoul(argv[5], nullptr, 0)); const std::string outputDirectory = argv[6]; std::vector module; if (!read_file(argv[1], module)) { std::fprintf(stderr, "cannot read %s\n", argv[1]); return 1; } module.push_back('\0'); // PTX text must be NUL terminated; harmless for fatbins void* library = dlopen("libcuda.so", RTLD_NOW | RTLD_LOCAL); if (library == nullptr) { std::fprintf(stderr, "dlopen(libcuda.so) failed: %s\n", dlerror()); return 1; } const auto init = load_function(library, "cuInit"); const auto deviceGet = load_function(library, "cuDeviceGet"); const auto contextCreate = load_function(library, "cuCtxCreate_v2"); const auto moduleLoad = load_function(library, "cuModuleLoadData"); const auto moduleGetFunction = load_function(library, "cuModuleGetFunction"); const auto alloc = load_function(library, "cuMemAlloc_v2"); const auto copyHtoD = load_function(library, "cuMemcpyHtoD_v2"); const auto copyDtoH = load_function(library, "cuMemcpyDtoH_v2"); const auto launch = load_function( library, "cuLaunchKernel"); const auto synchronize = load_function(library, "cuCtxSynchronize"); if (!init || !deviceGet || !contextCreate || !moduleLoad || !moduleGetFunction || !alloc || !copyHtoD || !copyDtoH || !launch || !synchronize) { std::fprintf(stderr, "ZLUDA libcuda.so is missing a required export\n"); return 1; } CUdevice device = 0; CUcontext context = nullptr; CUmodule loaded = nullptr; CUfunction function = nullptr; CUresult result = init(0); if (result == 0) result = deviceGet(&device, 0); if (result == 0) result = contextCreate(&context, 0, device); if (result == 0) result = moduleLoad(&loaded, module.data()); if (result == 0) result = moduleGetFunction(&function, loaded, argv[2]); if (result != 0) { std::fprintf(stderr, "module setup for %s failed: CUDA result=%d\n", argv[2], result); return 1; } std::vector buffers; std::vector words; for (int index = 7; index < argc; ++index) { const std::string word = argv[index]; if (word.empty() || word[0] != '@') { words.push_back(std::strtoull(word.c_str(), nullptr, 0)); continue; } const size_t plus = word.rfind('#'); const std::string path = word.substr(1, plus == std::string::npos ? std::string::npos : plus - 1); const uint64_t offset = plus == std::string::npos ? 0 : std::strtoull(word.c_str() + plus + 1, nullptr, 0); Buffer* buffer = nullptr; for (Buffer& existing : buffers) if (existing.path == path) buffer = &existing; if (buffer == nullptr) { std::vector contents; if (!read_file(path, contents)) { std::fprintf(stderr, "cannot read %s\n", path.c_str()); return 1; } Buffer created; created.path = path; created.bytes = contents.size(); result = alloc(&created.device, created.bytes); if (result == 0) result = copyHtoD(created.device, contents.data(), created.bytes); if (result != 0) { std::fprintf(stderr, "upload of %s failed: CUDA result=%d\n", path.c_str(), result); return 1; } buffers.push_back(created); buffer = &buffers.back(); } words.push_back(buffer->device + offset); } size_t argumentBytes = words.size() * sizeof(uint64_t); void* extra[] = {reinterpret_cast(1), words.data(), reinterpret_cast(2), &argumentBytes, nullptr}; result = launch(function, grid[0], grid[1], grid[2], block[0], block[1], block[2], shared, nullptr, nullptr, extra); if (result == 0) result = synchronize(); std::printf("%s grid=%u,%u,%u block=%u,%u,%u: CUDA result=%d\n", argv[2], grid[0], grid[1], grid[2], block[0], block[1], block[2], result); if (result != 0) return 1; for (const Buffer& buffer : buffers) { std::vector contents(buffer.bytes); result = copyDtoH(contents.data(), buffer.device, buffer.bytes); const size_t slash = buffer.path.rfind('/'); const std::string name = outputDirectory + "/" + (slash == std::string::npos ? buffer.path : buffer.path.substr(slash + 1)) + ".out"; std::ofstream output(name, std::ios::binary); output.write(contents.data(), static_cast(contents.size())); std::printf("wrote %s (%zu bytes, CUDA result=%d)\n", name.c_str(), buffer.bytes, result); } return 0; }