Run native Windows D3D12 DLSS K and M through shared VRAM
This commit is contained in:
1 parent
2db93adb1a
commit
e15f6a73a6
12 files changed
+1049
-19
No files matched your search
@@ -0,0 +1,190 @@
|
||||
#pragma once
|
||||
#if defined(__MINGW32__)
|
||||
#define WIDL_EXPLICIT_AGGREGATE_RETURNS
|
||||
#endif
|
||||
#include "hip_api.h"
|
||||
#include <d3d12.h>
|
||||
#include <wrl/client.h>
|
||||
|
||||
namespace d4r::win {
|
||||
using Microsoft::WRL::ComPtr;
|
||||
using diag::HipApi;
|
||||
inline void dx(HRESULT result, const char* operation) {
|
||||
if (FAILED(result)) throw std::runtime_error(std::string(operation) + " HRESULT=" + std::to_string(result));
|
||||
}
|
||||
struct Handle {
|
||||
HANDLE value = nullptr;
|
||||
Handle() = default;
|
||||
Handle(const Handle&) = delete;
|
||||
~Handle() { if (value) CloseHandle(value); }
|
||||
};
|
||||
struct ExternalApi {
|
||||
HipApi& hip;
|
||||
#define D4R_EXTERNAL(name) decltype(&::name) name = hip.library.symbol<decltype(&::name)>(#name)
|
||||
D4R_EXTERNAL(hipImportExternalMemory);
|
||||
D4R_EXTERNAL(hipExternalMemoryGetMappedBuffer);
|
||||
D4R_EXTERNAL(hipDestroyExternalMemory);
|
||||
D4R_EXTERNAL(hipImportExternalSemaphore);
|
||||
D4R_EXTERNAL(hipDestroyExternalSemaphore);
|
||||
D4R_EXTERNAL(hipWaitExternalSemaphoresAsync);
|
||||
D4R_EXTERNAL(hipSignalExternalSemaphoresAsync);
|
||||
#undef D4R_EXTERNAL
|
||||
};
|
||||
inline D3D12_RESOURCE_DESC resource_desc(ID3D12Resource* resource) {
|
||||
#if defined(__MINGW32__)
|
||||
D3D12_RESOURCE_DESC desc{}; resource->GetDesc(&desc); return desc;
|
||||
#else
|
||||
return resource->GetDesc();
|
||||
#endif
|
||||
}
|
||||
inline LUID adapter_luid(ID3D12Device* device) {
|
||||
#if defined(__MINGW32__)
|
||||
LUID luid{}; device->GetAdapterLuid(&luid); return luid;
|
||||
#else
|
||||
return device->GetAdapterLuid();
|
||||
#endif
|
||||
}
|
||||
inline ComPtr<ID3D12Resource> make_buffer(ID3D12Device* device, uint64_t bytes,
|
||||
D3D12_HEAP_TYPE heapType, D3D12_HEAP_FLAGS flags, D3D12_RESOURCE_STATES state) {
|
||||
D3D12_HEAP_PROPERTIES heap{}; heap.Type = heapType;
|
||||
heap.CreationNodeMask = heap.VisibleNodeMask = 1;
|
||||
D3D12_RESOURCE_DESC desc{};
|
||||
desc.Dimension = D3D12_RESOURCE_DIMENSION_BUFFER; desc.Width = bytes;
|
||||
desc.Height = desc.DepthOrArraySize = desc.MipLevels = desc.SampleDesc.Count = 1;
|
||||
desc.Layout = D3D12_TEXTURE_LAYOUT_ROW_MAJOR;
|
||||
ComPtr<ID3D12Resource> resource;
|
||||
dx(device->CreateCommittedResource(&heap, flags, &desc, state, nullptr,
|
||||
IID_PPV_ARGS(resource.GetAddressOf())), "CreateCommittedResource(buffer)");
|
||||
return resource;
|
||||
}
|
||||
inline void transition(ID3D12GraphicsCommandList* list, ID3D12Resource* resource,
|
||||
D3D12_RESOURCE_STATES before, D3D12_RESOURCE_STATES after) {
|
||||
if (before == after) return;
|
||||
D3D12_RESOURCE_BARRIER barrier{};
|
||||
barrier.Type = D3D12_RESOURCE_BARRIER_TYPE_TRANSITION;
|
||||
barrier.Transition = {resource, D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES, before, after};
|
||||
list->ResourceBarrier(1, &barrier);
|
||||
}
|
||||
|
||||
class SharedPlane {
|
||||
ExternalApi& api_;
|
||||
hipExternalMemory_t memory_ = nullptr;
|
||||
void release() noexcept {
|
||||
if (mapped) { (void)api_.hip.hipFree(mapped); mapped = nullptr; }
|
||||
if (memory_) { (void)api_.hipDestroyExternalMemory(memory_); memory_ = nullptr; }
|
||||
}
|
||||
public:
|
||||
ComPtr<ID3D12Resource> buffer;
|
||||
D3D12_PLACED_SUBRESOURCE_FOOTPRINT footprint{};
|
||||
UINT64 bytes = 0;
|
||||
void* mapped = nullptr;
|
||||
SharedPlane(ExternalApi& api, ID3D12Device* device, const D3D12_RESOURCE_DESC& texture) : api_(api) {
|
||||
UINT rows = 0; UINT64 rowBytes = 0;
|
||||
device->GetCopyableFootprints(&texture, 0, 1, 0, &footprint, &rows, &rowBytes, &bytes);
|
||||
if (!bytes || bytes == UINT64_MAX || !footprint.Footprint.RowPitch)
|
||||
throw std::runtime_error("Invalid shared texture footprint");
|
||||
buffer = make_buffer(device, bytes, D3D12_HEAP_TYPE_DEFAULT, D3D12_HEAP_FLAG_SHARED, D3D12_RESOURCE_STATE_COMMON);
|
||||
const auto desc = resource_desc(buffer.Get());
|
||||
D3D12_RESOURCE_ALLOCATION_INFO allocation{};
|
||||
#if defined(__MINGW32__)
|
||||
device->GetResourceAllocationInfo(&allocation, 0, 1, &desc);
|
||||
#else
|
||||
allocation = device->GetResourceAllocationInfo(0, 1, &desc);
|
||||
#endif
|
||||
Handle handle;
|
||||
dx(device->CreateSharedHandle(buffer.Get(), nullptr, GENERIC_ALL, nullptr, &handle.value), "CreateSharedHandle(VRAM)");
|
||||
try {
|
||||
hipExternalMemoryHandleDesc import{};
|
||||
import.type = hipExternalMemoryHandleTypeD3D12Resource;
|
||||
import.handle.win32.handle = handle.value;
|
||||
import.size = allocation.SizeInBytes; import.flags = hipExternalMemoryDedicated;
|
||||
api.hip.check(api.hipImportExternalMemory(&memory_, &import), "hipImportExternalMemory(D3D12)");
|
||||
hipExternalMemoryBufferDesc map{}; map.size = bytes;
|
||||
api.hip.check(api.hipExternalMemoryGetMappedBuffer(&mapped, memory_, &map), "hipExternalMemoryGetMappedBuffer");
|
||||
} catch (...) { release(); throw; }
|
||||
// Win32 imports retain their own reference; the exporter closes its HANDLE.
|
||||
std::printf("D4R_IMPORT width=%u height=%u pitch=%u allocation=%llu cpu_copy=0\n",
|
||||
footprint.Footprint.Width, footprint.Footprint.Height, footprint.Footprint.RowPitch, allocation.SizeInBytes);
|
||||
}
|
||||
SharedPlane(const SharedPlane&) = delete;
|
||||
~SharedPlane() { release(); }
|
||||
void copy_input(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, D3D12_RESOURCE_STATES state) {
|
||||
transition(list, texture, state, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
D3D12_TEXTURE_COPY_LOCATION source{}, dest{};
|
||||
source.pResource = texture; source.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
dest.pResource = buffer.Get(); dest.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; dest.PlacedFootprint = footprint;
|
||||
list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
|
||||
transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_COMMON);
|
||||
transition(list, texture, D3D12_RESOURCE_STATE_COPY_SOURCE, state);
|
||||
}
|
||||
void copy_output(ID3D12GraphicsCommandList* list, ID3D12Resource* texture, D3D12_RESOURCE_STATES state) {
|
||||
transition(list, texture, state, D3D12_RESOURCE_STATE_COPY_DEST);
|
||||
transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COMMON, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
||||
D3D12_TEXTURE_COPY_LOCATION source{}, dest{};
|
||||
source.pResource = buffer.Get(); source.Type = D3D12_TEXTURE_COPY_TYPE_PLACED_FOOTPRINT; source.PlacedFootprint = footprint;
|
||||
dest.pResource = texture; dest.Type = D3D12_TEXTURE_COPY_TYPE_SUBRESOURCE_INDEX;
|
||||
list->CopyTextureRegion(&dest, 0, 0, 0, &source, nullptr);
|
||||
transition(list, buffer.Get(), D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_COMMON);
|
||||
transition(list, texture, D3D12_RESOURCE_STATE_COPY_DEST, state);
|
||||
}
|
||||
};
|
||||
|
||||
class SharedTimeline {
|
||||
ExternalApi& api_;
|
||||
hipExternalSemaphore_t semaphore_ = nullptr;
|
||||
hipStream_t stream_ = nullptr;
|
||||
Handle event_;
|
||||
uint64_t value_ = 0;
|
||||
public:
|
||||
ComPtr<ID3D12Fence> fence;
|
||||
SharedTimeline(ExternalApi& api, ID3D12Device* device) : api_(api) {
|
||||
dx(device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(fence.GetAddressOf())), "CreateFence(D3D12/HIP)");
|
||||
Handle handle;
|
||||
dx(device->CreateSharedHandle(fence.Get(), nullptr, GENERIC_ALL, nullptr, &handle.value), "CreateSharedHandle(fence)");
|
||||
event_.value = CreateEventW(nullptr, FALSE, FALSE, nullptr);
|
||||
if (!event_.value) throw std::runtime_error("CreateEvent(fence) failed");
|
||||
hipExternalSemaphoreHandleDesc import{};
|
||||
import.type = hipExternalSemaphoreHandleTypeD3D12Fence;
|
||||
import.handle.win32.handle = handle.value;
|
||||
api.hip.check(api.hipImportExternalSemaphore(&semaphore_, &import), "hipImportExternalSemaphore(D3D12Fence)");
|
||||
try { api.hip.check(api.hip.hipStreamCreateWithFlags(&stream_, hipStreamNonBlocking), "hipStreamCreate(interop)"); }
|
||||
catch (...) { (void)api.hipDestroyExternalSemaphore(semaphore_); semaphore_ = nullptr; throw; }
|
||||
}
|
||||
~SharedTimeline() {
|
||||
if (stream_) { (void)api_.hip.hipStreamSynchronize(stream_); (void)api_.hip.hipStreamDestroy(stream_); }
|
||||
if (semaphore_) (void)api_.hipDestroyExternalSemaphore(semaphore_);
|
||||
}
|
||||
// Caller must serialize transactions and submit the producer prefix first.
|
||||
void wait_input(ID3D12CommandQueue* queue) {
|
||||
const uint64_t ready = ++value_;
|
||||
dx(queue->Signal(fence.Get(), ready), "Queue Signal(input)");
|
||||
hipExternalSemaphoreWaitParams wait{}; wait.params.fence.value = ready;
|
||||
api_.hip.check(api_.hipWaitExternalSemaphoresAsync(&semaphore_, &wait, 1, stream_), "HIP wait(D3D12 input)");
|
||||
api_.hip.check(api_.hip.hipStreamSynchronize(stream_), "HIP input wait completion");
|
||||
// Establish host-side completion as well: the Windows HIP semaphore
|
||||
// wait currently needs a separately verified completion boundary before
|
||||
// work is launched on CUDA's other streams. No image crosses the CPU.
|
||||
if (fence->GetCompletedValue() < ready) {
|
||||
dx(fence->SetEventOnCompletion(ready, event_.value), "SetEventOnCompletion(input)");
|
||||
if (WaitForSingleObject(event_.value, 30000) != WAIT_OBJECT_0)
|
||||
throw std::runtime_error("D3D12 input completion timeout");
|
||||
}
|
||||
}
|
||||
void signal_output(ID3D12CommandQueue* queue) {
|
||||
const uint64_t done = ++value_;
|
||||
hipExternalSemaphoreSignalParams signal{}; signal.params.fence.value = done;
|
||||
api_.hip.check(api_.hipSignalExternalSemaphoresAsync(&semaphore_, &signal, 1, stream_), "HIP signal(output)");
|
||||
dx(queue->Wait(fence.Get(), done), "Queue Wait(HIP output)");
|
||||
}
|
||||
void drain(ID3D12CommandQueue* queue) {
|
||||
const uint64_t done = ++value_;
|
||||
dx(queue->Signal(fence.Get(), done), "Queue Signal(completion)");
|
||||
if (fence->GetCompletedValue() < done) {
|
||||
dx(fence->SetEventOnCompletion(done, event_.value), "SetEventOnCompletion");
|
||||
if (WaitForSingleObject(event_.value, 30000) != WAIT_OBJECT_0)
|
||||
throw std::runtime_error("D3D12 completion timeout; resources must remain alive");
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
Reference in new issue
Block a user