Fix VRAM leak on quality changes and RGBA16 motion vectors; more games

- Shim: reuse VRAM interop buffers across features instead of freeing them;
  ROCm 7.2.x's hipExternalMemoryGetMappedBuffer retains its view twice, so
  freed buffers never released their memory (~130 MB per quality change)
- Bridge: close the exported fd after hipImportExternalMemory
- Shim: accept R16G16B16A16_TYPELESS motion vectors and convert 4-channel
  motion vectors to RG16F on the GPU, keeping VRAM interop (Dying Light:
  The Beast rendered black)
- Harness: D4R_HARNESS_RECREATE=N quality-change cycles,
  D4R_HARNESS_MV_RGBA16=1 motion vector format
- Supported games: TLOU, Control, Cyberpunk 2077, Rift Apart, HZD Remastered,
  Subnautica 2, Alan Wake 2, Silent Hill 2, Dying Light: The Beast; general notes
- README: GPU support section (native kernels only for gfx1101); ignore videos/
This commit is contained in:
ayo committed 2026-09-28 01:26:51 -04:00
1 parent 1952452830
commit da263ecf75
6 files changed
+249 -28

No files matched your search

+2 -1
View File
@@ -23,6 +23,7 @@ __pycache__/
/Archive.zip
/vkd3d-proton.cache
.vscode/
videos/
# Machine-specific notes, CI runner setup, local experiments, benchmark automation (kept locally, never published)
/private/
@@ -32,4 +33,4 @@ __pycache__/
/kernels/out/
*.hsaco
*.ptx
/kernels/extracted/
/kernels/extracted/
+16 -3
View File
@@ -14,7 +14,7 @@ See [supported games](SUPPORTED_GAMES.md) for the tested games and DLSS models.
[DLSS Ultra Performance demo video](https://cdn.ayois.gay/dlss).
Radeon RX 7700 XT (RDNA3, gfx1101), SILENT HILL Townfall at 2560×1440, the same 62-second street walk for every run, frame rates from MangoHud frametime logs. Every DLSS result is presented in the frame it belongs to (no added latency).
Radeon RX 7700 XT (RDNA3, gfx1101), SILENT HILL Townfall at 2560×1440, every DLSS result is presented in the frame it belongs to (no added latency).
| Mode | DLSS 3 CNN (preset E) | DLSS 4 (preset K) | DLSS 4.5 (preset M) | FSR 4 |
|---|---|---|---|---|
@@ -29,7 +29,20 @@ On the same GPU DLSS starts at a disadvantage: its networks were designed for NV
## Known issues
- Native upscaling can show visible artifacting.
- In some games native upscaling can show visible artifacting.
## GPU support
d4r needs RDNA3 (Radeon RX 7000 series), but full speed depends on the chip: the release ships its native kernels only for **gfx1101 (Navi 32)**, and on other GPUs DLSS 4 and 4.5 fall back to the much slower translated NVIDIA code.
| GPU | Chip | DLSS 3 CNN (E) | DLSS 4 (K) | DLSS 4.5 (M) |
|---|---|---|---|---|
| RX 7700 XT (tested), RX 7800 XT, RX 7700, Radeon PRO W7700 | gfx1101 | full speed | full speed | full speed |
| RX 7900 GRE / XT / XTX, Radeon PRO W7800 / W7900 | gfx1100 | full speed | runs without its transformer (worse image, slower) | about half speed |
| RX 7600 / 7600 XT, RX 7650 GRE | gfx1102 | full speed | runs without its transformer (worse image, slower) | about half speed |
| RDNA4, RDNA2 and older | | not supported | not supported | not supported |
Only the RX 7700 XT has been tested; the other gfx1101 cards use the same chip. Without the native kernels, NVIDIA's translated DLSS 4 layers produce invalid values and DLSS discards the network's output, so K loses the transformer's image quality. Preset E is unaffected on any RDNA3 GPU. The kernels can be built for gfx1100 and gfx1102 (`D4R_GPU_ARCHS` in `scripts/package_release.sh`), but those builds are untested.
## How it works
@@ -68,7 +81,7 @@ ROCm does not need to be installed: the zip includes its runtime (from AMD's Ubu
## Requirements (building from source)
- Linux with an AMD RDNA3 GPU. The native kernels use gfx11 WMMA; they are built for gfx1101 by default (`D4R_GPU_ARCH` for other RDNA3 chips, untested). Without them DLSS still runs through ZLUDA, much more slowly.
- Linux with an AMD RDNA3 GPU. The native kernels use gfx11 WMMA; they are built for gfx1101 by default (`D4R_GPU_ARCH` for other RDNA3 chips, untested). Without them DLSS runs through ZLUDA alone: much more slowly, and DLSS 4 (K) without its transformer (see [GPU support](#gpu-support)).
- ROCm with HIP and its clang (tested with ROCm 7.2).
- GE-Proton with OptiScaler integration (tested with GE-Proton11-3).
- Build tools: a Rust toolchain and git-lfs (ZLUDA), meson and ninja (vkd3d-proton), `winegcc`/`winebuild` (bridge), `x86_64-w64-mingw32-g++` and `clang-cl` (shim), Python 3.
+33 -5
View File
@@ -7,10 +7,38 @@ The following DirectX 12 games run DLSS Super Resolution through d4r on the test
| SILENT HILL Townfall | Works | Works | Works |
| Ghost of Tsushima DIRECTOR'S CUT | Works | Works | Works |
| Ready or Not | Works | Works; visible graphical artifacts with hdr enabled | Works |
| | | | |
| | | | |
| | | | |
| | | | |
| | | | |
| Clair Obscur: Expedition 33 | Works | Works | Works |
| Marvel's Spider-Man: Miles Morales | Works | Works; minor artifacts when HDR enabled and while the game is paused | Works |
| The Last of Us Part I | Works | Works; visible graphical artifacts, including with HDR enabled | Works |
| Control Ultimate Edition | Works | Works | Works |
| Cyberpunk 2077 | Works; occasional visual artifacts during gameplay | Works | Works |
| Alan Wake 2 | Works | Works | Works |
| SILENT HILL 2 (2024) | Works | Works | Works |
| Subnautica 2 | Works | Works; visible artifacts at Quality (other quality modes are fine) | Works |
| Horizon Zero Dawn Remastered | Works; stutters | Works | Works |
| Ratchet & Clank: Rift Apart | Works; occasional artifacts in the pause menu | Works; minor artifacts with HDR enabled and occasionally in the pause menu | Works; occasional artifacts in the pause menu |
| Dying Light: The Beast | Works | Works | Works; small visual artifacts throughout |
General notes:
- Select DLSS in the game's graphics settings where it is offered. OptiScaler can also feed DLSS from a game's FSR or XeSS inputs, but those are a fallback for games that hide DLSS and can ghost or show a black screen.
- d4r supports DLSS Super Resolution only. Keep DLSS Ray Reconstruction off in games that offer it (such as Cyberpunk 2077 and Alan Wake 2); Control requests it whenever ray tracing is on and crashes.
- Saving settings from the OptiScaler overlay ("Save INI") rewrites `OptiScaler.ini` and resets d4r's values to `auto`, including frame generation (`[FrameGen] Enabled=false`, `FGInput=nofg`, `FGOutput=nofg`) and `[Inputs] EnableDlssInputs=true`. Restore them afterwards, or keep a copy of the original file.
Game-specific setup:
- **The Last of Us Part I:** the game's Streamline integration crashes on boot with OptiScaler. Rename `sl.common.dll` in the game folder to `sl.common.dll.bak` and select FSR 3.1 in the game's graphics settings; OptiScaler passes it to DLSS through d4r.
- **Control Ultimate Edition:** turn ray tracing off. With ray tracing on, the game requests DLSS Ray Reconstruction, which d4r does not support, and crashes when loading into gameplay.
- **Cyberpunk 2077:** GE-Proton 11-3 loads Wine's own ICU libraries instead of the game's, and the game aborts at launch (`unimplemented function icuuc.dll.u_setMemoryFunctions_65`). Add `WINEDLLOVERRIDES="icuuc,icuin=n,b"` to the launch options (fixed in GE-Proton 11-4).
- **Alan Wake 2:** with the game's own DLSS inputs, every model shows heavy ghosting. Set `AutoExposure=true` under `[InitFlags]` in `OptiScaler.ini` (or enable auto exposure in the OptiScaler overlay), or select FSR 3.1 in the game instead so OptiScaler passes FSR inputs to DLSS.
- **Horizon Zero Dawn Remastered:** the game loads `dxgi.dll` only from the Windows system folder, so OptiScaler is never loaded under that name. Rename `dxgi.dll` to `version.dll` and add `WINEDLLOVERRIDES="version=n,b"` to the launch options. d4r's `d3d12core.dll` is still picked up from the game folder.
- **Ratchet & Clank: Rift Apart:** set `Dxgi=false` in `OptiScaler.ini`. With ray tracing on, OptiScaler's DXGI spoofing crashes the game at startup on AMD GPUs; the `PROTON_FORCE_NVAPI=1` launch option already reports an NVIDIA GPU.
- **Dying Light: The Beast:** use DLSS inputs; with FSR or XeSS inputs the screen stays black. The game passes its motion vectors as `R16G16B16A16_TYPELESS`, which needs a d4r build from 2026-09-28 or later (older builds reject every frame and show a black screen).
Choose a model with `Model = E`, `Model = K`, or `Model = M` under `[DLSS]` in the game's `d4r/d4r.ini`. Model compatibility does not imply equal performance; see the [Townfall measurements](docs/performance.md) for measured frame rates.
+98 -5
View File
@@ -52,6 +52,35 @@ static ID3D12Fence* g_fence;
static HANDLE g_event;
static UINT64 g_fenceValue;
static unsigned long long process_vram_kib()
{
std::vector<unsigned long long> clients;
unsigned long long total = 0;
for (int fd = 0; fd < 4096; ++fd)
{
char path[64];
std::snprintf(path, sizeof(path), "Z:\\proc\\self\\fdinfo\\%d", fd);
FILE* file = std::fopen(path, "r");
if (file == nullptr)
continue;
unsigned long long client = 0, vram = 0;
bool drm = false;
char line[256];
while (std::fgets(line, sizeof(line), file) != nullptr)
if (std::sscanf(line, "drm-client-id: %llu", &client) == 1)
drm = true;
else
std::sscanf(line, "drm-memory-vram: %llu", &vram);
std::fclose(file);
if (drm && std::find(clients.begin(), clients.end(), client) == clients.end())
{
clients.push_back(client);
total += vram;
}
}
return total;
}
static bool check(HRESULT hr, const char* what)
{
if (FAILED(hr))
@@ -487,7 +516,27 @@ int main(int argc, char** argv)
ID3D12Resource* colorTexture = create_texture(resourceWidth, inHeight,
rgba8 ? DXGI_FORMAT_R8G8B8A8_UNORM : DXGI_FORMAT_R16G16B16A16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
ID3D12Resource* depthTexture = create_texture(resourceWidth, inHeight, DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
ID3D12Resource* motionTexture = create_texture(motionWidth, motionHeight, DXGI_FORMAT_R16G16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
// D4R_HARNESS_MV_RGBA16=1: motion vectors in an R16G16B16A16_TYPELESS texture (as some games pass them), the
// unused components filled with junk that must not reach DLSS.
const bool mvRgba16 = std::getenv("D4R_HARNESS_MV_RGBA16") != nullptr && std::atoi(std::getenv("D4R_HARNESS_MV_RGBA16")) != 0;
ID3D12Resource* motionTexture = create_texture(motionWidth, motionHeight,
mvRgba16 ? DXGI_FORMAT_R16G16B16A16_TYPELESS : DXGI_FORMAT_R16G16_FLOAT, D3D12_RESOURCE_FLAG_NONE);
std::vector<uint16_t> motionWide;
auto motion_rows = [&](const uint16_t* rg) -> const void* {
if (!mvRgba16)
return rg;
const size_t pixels = static_cast<size_t>(motionWidth) * motionHeight;
motionWide.resize(pixels * 4);
for (size_t pixel = 0; pixel < pixels; ++pixel)
{
motionWide[pixel * 4 + 0] = rg[pixel * 2 + 0];
motionWide[pixel * 4 + 1] = rg[pixel * 2 + 1];
motionWide[pixel * 4 + 2] = 0x5640; // 100.0
motionWide[pixel * 4 + 3] = 0xd640; // -100.0
}
return motionWide.data();
};
const UINT motionRowBytes = motionWidth * (mvRgba16 ? 8 : 4);
ID3D12Resource* exposureTexture = create_texture(1, 1,
exposureRGBA32 ? DXGI_FORMAT_R32G32B32A32_FLOAT : DXGI_FORMAT_R32_FLOAT, D3D12_RESOURCE_FLAG_NONE);
ID3D12Resource* outputTexture = create_texture(outWidth, outHeight,
@@ -496,7 +545,7 @@ int main(int argc, char** argv)
upload(colorTexture, rgba8 ? static_cast<const void*>(color8.data()) : static_cast<const void*>(color.data()),
resourceWidth * (rgba8 ? 4 : 8), srv);
upload(depthTexture, depth.data(), resourceWidth * 4, srv);
upload(motionTexture, motion.data(), motionWidth * 4, srv);
upload(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
upload(exposureTexture, exposureRGBA32 ? static_cast<const void*>(exposureRGBA.data())
: static_cast<const void*>(&exposureValue),
exposureRGBA32 ? 16 : 4, srv);
@@ -662,7 +711,7 @@ int main(int argc, char** argv)
motion[pixel * 2 + 0] = mvX;
motion[pixel * 2 + 1] = mvY;
}
update_texture(motionTexture, motion.data(), motionWidth * 4, srv);
update_texture(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
}
d4r_ngx_set_float(parameters, "Jitter.Offset.X", signX * jx);
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", signY * jy);
@@ -700,7 +749,7 @@ int main(int argc, char** argv)
}
update_texture(colorTexture, color.data(), resourceWidth * 8, srv);
update_texture(depthTexture, depth.data(), resourceWidth * 4, srv);
update_texture(motionTexture, motion.data(), motionWidth * 4, srv);
update_texture(motionTexture, motion_rows(motion.data()), motionRowBytes, srv);
}
if (replayDir != nullptr)
{
@@ -711,7 +760,7 @@ int main(int argc, char** argv)
return 1;
update_texture(colorTexture, replay.color.data(), resourceWidth * 8, srv);
update_texture(depthTexture, replay.depth.data(), resourceWidth * 4, srv);
update_texture(motionTexture, replay.motion.data(), motionWidth * 4, srv);
update_texture(motionTexture, motion_rows(reinterpret_cast<const uint16_t*>(replay.motion.data())), motionRowBytes, srv);
d4r_ngx_set_float(parameters, "Jitter.Offset.X", replay.jitterX);
d4r_ngx_set_float(parameters, "Jitter.Offset.Y", replay.jitterY);
d4r_ngx_set_float(parameters, "MV.Scale.X", replay.mvScaleX);
@@ -836,6 +885,50 @@ int main(int argc, char** argv)
nonzero += value != 0;
std::printf("output read back: %zu of %zu bytes nonzero, written to %s\n", nonzero, output.size(), argv[2]);
// D4R_HARNESS_RECREATE=N: N more release/create cycles at alternating render sizes (as when a game's DLSS
// quality setting changes), a few evaluations each, logging this process's VRAM to find leaks per cycle.
const int recreateCycles = std::getenv("D4R_HARNESS_RECREATE") != nullptr ? std::atoi(std::getenv("D4R_HARNESS_RECREATE")) : 0;
for (int cycle = 1; cycle <= recreateCycles; ++cycle)
{
release(feature);
feature = nullptr;
const UINT width = cycle % 2 != 0 ? inWidth * 10 / 13 : inWidth;
const UINT height = cycle % 2 != 0 ? inHeight * 10 / 13 : inHeight;
d4r_ngx_set_uint(parameters, "Width", width);
d4r_ngx_set_uint(parameters, "Height", height);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Width", width);
d4r_ngx_set_uint(parameters, "DLSS.Render.Subrect.Dimensions.Height", height);
result = createFeature(g_list, 1, parameters, &feature);
if (result != NGX_SUCCESS)
{
std::printf("RECREATE cycle %d: CreateFeature -> 0x%08x\n", cycle, result);
return 1;
}
for (int frame = 0; frame < 4; ++frame)
{
d4r_ngx_set_int(parameters, "Reset", frame == 0 ? 1 : 0);
evaluate(g_list, feature, parameters, nullptr);
submit_and_wait();
Sleep(frameWaitMs);
}
const std::vector<uint8_t> cycleOutput = read_back(outputTexture, D3D12_RESOURCE_STATE_UNORDERED_ACCESS,
outWidth * (rgba8 ? 4 : 8));
if (FILE* cycleFile = std::fopen((std::string(argv[2]) + ".cycle" + std::to_string(cycle)).c_str(), "wb"))
{
std::fwrite(cycleOutput.data(), 1, cycleOutput.size(), cycleFile);
std::fclose(cycleFile);
}
// Also to the quality report file, which survives when Proton drops stdout.
const char* reportPath = std::getenv("D4R_HARNESS_QUALITY_SCENE");
FILE* report = reportPath != nullptr ? std::fopen(reportPath, "a") : nullptr;
for (FILE* out : {stdout, report})
if (out != nullptr)
std::fprintf(out, "RECREATE cycle %d (%ux%u): process VRAM %llu KiB\n", cycle, width, height,
process_vram_kib());
if (report != nullptr)
std::fclose(report);
}
release(feature);
shutdown();
return 0;
+92 -10
View File
@@ -836,7 +836,8 @@ static bool supported_input(Plane plane, DXGI_FORMAT format)
case Plane::Motion:
return format == DXGI_FORMAT_R16G16_FLOAT || format == DXGI_FORMAT_R16G16_TYPELESS ||
format == DXGI_FORMAT_R32G32_FLOAT || format == DXGI_FORMAT_R32G32_TYPELESS ||
format == DXGI_FORMAT_R16G16B16A16_FLOAT || format == DXGI_FORMAT_R32G32B32A32_FLOAT;
format == DXGI_FORMAT_R16G16B16A16_FLOAT || format == DXGI_FORMAT_R16G16B16A16_TYPELESS ||
format == DXGI_FORMAT_R32G32B32A32_FLOAT;
case Plane::Exposure:
return format == DXGI_FORMAT_R32_FLOAT || format == DXGI_FORMAT_R32_TYPELESS ||
format == DXGI_FORMAT_R16_FLOAT || format == DXGI_FORMAT_R16_TYPELESS ||
@@ -959,6 +960,7 @@ static void convert_row_in(Plane plane, DXGI_FORMAT format, const uint8_t* sourc
std::memcpy(half + x * 2, source + x * 4, 4);
break;
case DXGI_FORMAT_R16G16B16A16_FLOAT:
case DXGI_FORMAT_R16G16B16A16_TYPELESS:
std::memcpy(half + x * 2, source + x * 8, 4);
break;
case DXGI_FORMAT_R32G32B32A32_FLOAT:
@@ -1556,6 +1558,7 @@ struct Feature
// formats later stop qualifying.
bool vram = false, vramDecided = false;
VramImage colorConversion;
VramImage motionConversion;
VramImage exposureConversion;
VramImage outputConversion;
// Split frames: the rest of frame N's command list is submitted only once
@@ -2159,9 +2162,33 @@ static int device_local_type(uint32_t typeBits)
return -1;
}
// Buffers of released features, reused by later ones. ROCm up to at least 7.2.4 never frees memory mapped with
// hipExternalMemoryGetMappedBuffer (the mapping's buffer view is retained twice but released once by hipFree), so
// destroying a buffer would leak its memory: each DLSS quality change (a new feature) would add ~100 MB of VRAM.
static std::mutex g_vramPoolMutex;
static std::vector<VramBuffer> g_vramPool;
constexpr size_t kVramPoolLimit = 64;
// The smallest pooled buffer that holds `bytes`.
static bool take_pooled_vram_buffer(VramBuffer& target, size_t bytes)
{
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
auto best = g_vramPool.end();
for (auto it = g_vramPool.begin(); it != g_vramPool.end(); ++it)
if (it->bytes >= bytes && (best == g_vramPool.end() || it->bytes < best->bytes))
best = it;
if (best == g_vramPool.end())
return false;
target = *best;
g_vramPool.erase(best);
return true;
}
// Game thread. The CUDA import runs on the worker, which owns the context.
static bool create_vram_buffer(VramBuffer& target, size_t bytes)
{
if (take_pooled_vram_buffer(target, bytes))
return true;
VramBuffer buffer;
VkExternalMemoryBufferCreateInfo external = {VK_STRUCTURE_TYPE_EXTERNAL_MEMORY_BUFFER_CREATE_INFO};
external.handleTypes = VK_EXTERNAL_MEMORY_HANDLE_TYPE_OPAQUE_WIN32_BIT;
@@ -2212,8 +2239,11 @@ static void destroy_vram_buffer(VramBuffer& buffer)
{
if (buffer.external != nullptr)
g.worker.call([&buffer] {
g.cu.memFree(buffer.device);
return g_vk.release(buffer.external);
const int freed = g.cu.memFree(buffer.device);
const int released = g_vk.release(buffer.external);
if (freed != 0 || released != 0)
logf("VRAM interop: freeing %zu-byte buffer: cuMemFree %d, release %d", buffer.bytes, freed, released);
return released;
});
if (buffer.memory != VK_NULL_HANDLE)
g_vk.free(g_vk.device, buffer.memory, nullptr);
@@ -2222,6 +2252,34 @@ static void destroy_vram_buffer(VramBuffer& buffer)
buffer = VramBuffer{};
}
// Only once no command list or CUDA work uses the buffer: keeps it for a later feature (see g_vramPool).
static void recycle_vram_buffer(VramBuffer& buffer)
{
if (buffer.buffer == VK_NULL_HANDLE)
return;
{
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
if (g_vramPool.size() < kVramPoolLimit)
{
g_vramPool.push_back(buffer);
buffer = VramBuffer{};
return;
}
}
destroy_vram_buffer(buffer);
}
static void free_vram_pool()
{
std::vector<VramBuffer> pool;
{
std::lock_guard<std::mutex> lock(g_vramPoolMutex);
pool.swap(g_vramPool);
}
for (VramBuffer& buffer : pool)
destroy_vram_buffer(buffer);
}
static void destroy_vram_image(VramImage& image)
{
if (image.memory != VK_NULL_HANDLE)
@@ -2349,7 +2407,21 @@ static bool vram_exposure_blit_supported(VkFormat format)
}
}
// Whether `resource` can be copied raw (or, for colour/exposure, blitted)
// Motion vectors in a wider float format keep their first two components (as the host path does).
static bool vram_motion_blit_supported(VkFormat format)
{
switch (format)
{
case VK_FORMAT_R16G16B16A16_SFLOAT:
case VK_FORMAT_R32G32_SFLOAT:
case VK_FORMAT_R32G32B32A32_SFLOAT:
return vram_blit_supported(format, VK_FORMAT_R16G16_SFLOAT);
default:
return false;
}
}
// Whether `resource` can be copied raw (or, for colour/motion/exposure, blitted)
// to/from the canonical layout of `plane`.
static bool describe_vram_copy(ID3D12Resource* resource, Plane plane, VramCopy& copy, bool output = false)
{
@@ -2374,6 +2446,8 @@ static bool describe_vram_copy(ID3D12Resource* resource, Plane plane, VramCopy&
}
if (plane == Plane::Exposure)
copy.convert = vram_exposure_blit_supported(format);
else if (plane == Plane::Motion)
copy.convert = vram_motion_blit_supported(format);
return copy.convert || vk_texel_bytes(format) == canonical_texel_bytes(plane);
}
@@ -2415,9 +2489,10 @@ static bool record_vram_inputs(Feature& feature, ID3D12GraphicsCommandList* list
g_vk.copyImageToBuffer(cmd, copy.image, layout, slot.vram[index].buffer, 1, &region);
continue;
}
// Convert colour to RGBA16F or exposure's first component to R32F,
// then copy from the canonical image into the shared buffer.
// Convert colour to RGBA16F, motion to RG16F or exposure's first component
// to R32F, then copy from the canonical image into the shared buffer.
VkImage conversion = kPlanes[index] == Plane::Exposure ? feature.exposureConversion.image
: kPlanes[index] == Plane::Motion ? feature.motionConversion.image
: feature.colorConversion.image;
VkImageMemoryBarrier toDestination = {VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER};
toDestination.srcAccessMask = VK_ACCESS_TRANSFER_READ_BIT;
@@ -2633,14 +2708,15 @@ static void release_vram(Feature& feature)
g_vk.destroySemaphore(g_vk.device, feature.splitSemaphore, nullptr);
for (InputSlot& slot : feature.inputs)
for (VramBuffer& buffer : slot.vram)
destroy_vram_buffer(buffer);
recycle_vram_buffer(buffer);
for (OutputSlot& slot : feature.outputs)
destroy_vram_buffer(slot.vram);
destroy_vram_buffer(feature.gpuMarker);
recycle_vram_buffer(slot.vram);
recycle_vram_buffer(feature.gpuMarker);
for (VramBuffer& buffer : feature.retiredBuffers)
destroy_vram_buffer(buffer);
recycle_vram_buffer(buffer);
feature.retiredBuffers.clear();
destroy_vram_image(feature.colorConversion);
destroy_vram_image(feature.motionConversion);
destroy_vram_image(feature.exposureConversion);
destroy_vram_image(feature.outputConversion);
for (VramImage& image : feature.retiredImages)
@@ -4437,6 +4513,10 @@ static NgxResult evaluate(ID3D12GraphicsCommandList* list, const NgxHandle* hand
vramInputs[0].width, vramInputs[0].height,
VK_FORMAT_R16G16B16A16_SFLOAT))
return NGX_FAIL_PLATFORM_ERROR;
if (vramInputs[2].convert && !ensure_conversion_image(*feature, feature->motionConversion,
vramInputs[2].width, vramInputs[2].height,
VK_FORMAT_R16G16_SFLOAT))
return NGX_FAIL_PLATFORM_ERROR;
if (inputCount == 4 && vramInputs[3].convert &&
!ensure_conversion_image(*feature, feature->exposureConversion,
vramInputs[3].width, vramInputs[3].height, VK_FORMAT_R32_SFLOAT))
@@ -4688,6 +4768,8 @@ D4R_EXPORT NgxResult NVSDK_NGX_D3D12_Shutdown()
}
for (Feature* feature : features)
release_feature(feature);
if (g_vk.ready)
free_vram_pool();
std::lock_guard<std::mutex> lock(g.mutex);
if (g.ngxInitialized)
{
+8 -4
View File
@@ -2589,8 +2589,10 @@ CUresult WINAPI cuGetProcAddress(const char* symbol, void** pointer, int cuda_ve
handles (Wine 11 layout: a VkDevice client object holds the unix object
pointer at offset 8; a non-dispatchable client handle is the unix object
pointer; every unix object starts with its host handle), the host fd is
exported with vkGetMemoryFdKHR and imported into HIP, which takes ownership
of the fd. */
exported with vkGetMemoryFdKHR and imported into HIP. HIP does not take
ownership of the fd (hipDestroyExternalMemory leaves it open, and an open fd
keeps the memory alive after vkFreeMemory), so it is closed once imported, as
ROCm's own GL interop does after mapping. */
typedef struct
{
int type; /* hipExternalMemoryHandleTypeOpaqueFd = 1 */
@@ -2697,11 +2699,11 @@ CUresult WINAPI d4rImportVulkanMemory(void* client_device, uint64_t client_memor
desc.flags = 1; /* dedicated allocation */
void* external = NULL;
int result = import(&external, &desc);
close(fd);
if (result != 0)
{
tracef("d4rImportVulkanMemory: hipImportExternalMemory(fd=%d, %llu bytes) failed: %d", fd,
(unsigned long long)bytes, result);
close(fd);
return CUDA_ERROR_INVALID_VALUE;
}
D4rHipExternalMemoryBufferDesc buffer;
@@ -2835,5 +2837,7 @@ CUresult WINAPI d4rSetArrayRedirect(CUarray array, CUdeviceptr pointer, uint32_t
CUresult WINAPI d4rReleaseVulkanMemory(void* memory)
{
HIP_DESTROY_EXTERNAL_MEMORY_FN destroy = (HIP_DESTROY_EXTERNAL_MEMORY_FN)hip_symbol("hipDestroyExternalMemory");
return destroy != NULL && destroy(memory) == 0 ? CUDA_SUCCESS : CUDA_ERROR_INVALID_VALUE;
const int result = destroy != NULL ? destroy(memory) : -1;
tracef("d4rReleaseVulkanMemory: hipDestroyExternalMemory(%p) -> %d", memory, result);
return result == 0 ? CUDA_SUCCESS : CUDA_ERROR_INVALID_VALUE;
}