Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
115 changes: 115 additions & 0 deletions src/DX12Interop.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
#include "DX12Interop.h"

#include <dxgi1_6.h>

#include "Features/Upscaling.h"

DX12Interop::~DX12Interop()
{
if (fenceEvent) {
CloseHandle(fenceEvent);
fenceEvent = nullptr;
}
}

void DX12Interop::Init(ID3D11Device* a_d3d11Device, ID3D11DeviceContext* a_immediateContext, IDXGIAdapter* a_adapter)
{
if (!D3D12Mode())
return;

active = true;

SetD3D11Device(a_d3d11Device);
SetD3D11DeviceContext(a_immediateContext);

CreateD3D12Device(a_adapter);

CreateInterop();
}


bool DX12Interop::Active() const
{
return active;
}

bool DX12Interop::D3D12Mode()
{
auto& upscaling = globals::features::upscaling;
if (upscaling.loaded && upscaling.HasFrameGenModule())
return true;

return false;
}

void DX12Interop::CreateD3D12Device(IDXGIAdapter* a_adapter)
{
DX::ThrowIfFailed(D3D12CreateDevice(a_adapter, D3D_FEATURE_LEVEL_12_0, IID_PPV_ARGS(&d3d12Device)));

D3D12_COMMAND_QUEUE_DESC queueDesc = {};
queueDesc.Type = D3D12_COMMAND_LIST_TYPE_DIRECT;
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
queueDesc.NodeMask = 0;

DX::ThrowIfFailed(d3d12Device->CreateCommandQueue(&queueDesc, IID_PPV_ARGS(&commandQueue)));
}

void DX12Interop::CreateInterop()
{
HANDLE sharedFenceHandle;
DX::ThrowIfFailed(d3d12Device->CreateFence(0, D3D12_FENCE_FLAG_SHARED, IID_PPV_ARGS(&d3d12Fence)));
DX::ThrowIfFailed(d3d12Device->CreateSharedHandle(d3d12Fence.get(), nullptr, GENERIC_ALL, nullptr, &sharedFenceHandle));
DX::ThrowIfFailed(d3d11Device->OpenSharedFence(sharedFenceHandle, IID_PPV_ARGS(&d3d11Fence)));
CloseHandle(sharedFenceHandle);

fenceEvent = CreateEvent(nullptr, FALSE, FALSE, nullptr);
if (!fenceEvent)
DX::ThrowIfFailed(HRESULT_FROM_WIN32(GetLastError()));
}

void DX12Interop::SetD3D11Device(ID3D11Device* a_d3d11Device)
{
DX::ThrowIfFailed(a_d3d11Device->QueryInterface(IID_PPV_ARGS(&d3d11Device)));
}

void DX12Interop::SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context)
{
DX::ThrowIfFailed(a_d3d11Context->QueryInterface(IID_PPV_ARGS(&d3d11Context)));
}

void DX12Interop::SetupResources()
{
if (!active)
return;

auto renderer = globals::game::renderer;

auto& main = renderer->GetRuntimeData().renderTargets[RE::RENDER_TARGETS::kMAIN];
D3D11_TEXTURE2D_DESC mainDesc{};
main.texture->GetDesc(&mainDesc);

// Main render target
mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS;
sharedResources.main = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get());

// Depth
mainDesc.Format = DXGI_FORMAT_R32_FLOAT;
mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS;
sharedResources.depth = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get());

// Motion vector
mainDesc.Format = DXGI_FORMAT_R16G16_FLOAT;
mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_RENDER_TARGET | D3D11_BIND_UNORDERED_ACCESS;
sharedResources.motionVector = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get());

Comment thread
Gistix marked this conversation as resolved.
// Upscaler reactive mask
mainDesc.Format = DXGI_FORMAT_R8_UNORM;
mainDesc.BindFlags = D3D11_BIND_SHADER_RESOURCE | D3D11_BIND_UNORDERED_ACCESS;
sharedResources.reactiveMask = new WrappedResource(mainDesc, d3d11Device.get(), d3d12Device.get());
}
Comment thread
Gistix marked this conversation as resolved.

UINT DX12Interop::GetFrameContextIndex() const
{
return globals::state->frameCount % kMaxFramesInFlight;
}
67 changes: 67 additions & 0 deletions src/DX12Interop.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
#pragma once

#include "Feature.h"
#include "State.h"
#include "DX12Interop/WrappedResource.h"

#include <winrt/base.h>

#include <d3d11_4.h>
#include <directx/d3dx12.h>
#include <type_traits>
#include <utility>

struct DX12Interop
{
static constexpr UINT kMaxFramesInFlight = 2;

winrt::com_ptr<ID3D12Device5> d3d12Device;

winrt::com_ptr<ID3D12CommandQueue> commandQueue;

UINT64 currentFenceValue = 0;
HANDLE fenceEvent = nullptr;

struct SharedResources
{
WrappedResource* main = nullptr;
WrappedResource* depth = nullptr;
WrappedResource* motionVector = nullptr;
WrappedResource* reactiveMask = nullptr;
} sharedResources;

winrt::com_ptr<ID3D11Device5> d3d11Device;
winrt::com_ptr<ID3D11DeviceContext4> d3d11Context;

winrt::com_ptr<ID3D11Fence> d3d11Fence;
winrt::com_ptr<ID3D12Fence> d3d12Fence;

static DX12Interop* GetSingleton()
{
static DX12Interop singleton;
return &singleton;
}

~DX12Interop();

void Init(ID3D11Device* d3d11Device, ID3D11DeviceContext* a_immediateContext, IDXGIAdapter* a_adapter);

// Resources
void SetupResources();

bool Active() const;

// Whether DirectX 12 is required or not
// True when Upscaling is loaded in frame generation mode
static bool D3D12Mode();

UINT GetFrameContextIndex() const;

private:
bool active = false;

void CreateInterop();
void SetD3D11Device(ID3D11Device* a_d3d11Device);
void SetD3D11DeviceContext(ID3D11DeviceContext* a_d3d11Context);
void CreateD3D12Device(IDXGIAdapter* a_adapter);
};
79 changes: 79 additions & 0 deletions src/DX12Interop/InteropContext.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
#pragma once

#include "DX12Interop.h"

#include <directx/d3dx12.h>

// Each execute call in the same frame requires its own context to avoid command allocator/list reuse hazards while frames are in flight.
class InteropContext
{
InteropContext()
{
auto interop = globals::dx12Interop;
for (size_t i = 0; i < DX12Interop::kMaxFramesInFlight; i++) {
DX::ThrowIfFailed(interop->d3d12Device->CreateCommandAllocator(D3D12_COMMAND_LIST_TYPE_DIRECT, IID_PPV_ARGS(&frames[i].commandAllocator)));
DX::ThrowIfFailed(interop->d3d12Device->CreateCommandList(0, D3D12_COMMAND_LIST_TYPE_DIRECT, frames[i].commandAllocator.get(), nullptr, IID_PPV_ARGS(&frames[i].commandList)));
frames[i].commandList->Close();
}
};

// Provides buffering for D3D12 command list recording and submission.
struct Frame
{
winrt::com_ptr<ID3D12CommandAllocator> commandAllocator;
winrt::com_ptr<ID3D12GraphicsCommandList4> commandList;
UINT64 fenceValueAtSubmission = 0; // what value was signaled when this frame was submitted
};

Frame frames[DX12Interop::kMaxFramesInFlight];

public:
static InteropContext* Make()
{
return new InteropContext();
}

// Fences the GPU, waits for D3D11 to be idle, executes D3D12 commands in the provided function, then waits for D3D12 to be idle before returning.
template <typename Func, typename Func2 = std::nullptr_t>
void Execute(Func func, Func2 func2 = nullptr)
{
auto interop = globals::dx12Interop;

// Get to next frame context
auto& frame = frames[interop->GetFrameContextIndex()];

// CPU-side wait: stall if this frame's previous submission isn't done yet
// (i.e. we've lapped the GPU)
if (frame.fenceValueAtSubmission != 0) {
if (interop->d3d12Fence->GetCompletedValue() < frame.fenceValueAtSubmission) {
// GPU hasn't finished with this allocator yet - stall CPU
DX::ThrowIfFailed(interop->d3d12Fence->SetEventOnCompletion(frame.fenceValueAtSubmission, interop->fenceEvent));
WaitForSingleObject(interop->fenceEvent, INFINITE);
}
}

// Safe to reset now - GPU is done with this allocator
DX::ThrowIfFailed(frame.commandAllocator->Reset());
DX::ThrowIfFailed(frame.commandList->Reset(frame.commandAllocator.get(), nullptr));

// DX11 -> DX12 handoff
DX::ThrowIfFailed(interop->d3d11Context->Signal(interop->d3d11Fence.get(), ++interop->currentFenceValue));
DX::ThrowIfFailed(interop->commandQueue->Wait(interop->d3d12Fence.get(), interop->currentFenceValue));

func(frame.commandList.get());
DX::ThrowIfFailed(frame.commandList->Close());

ID3D12CommandList* lists[] = { frame.commandList.get() };
interop->commandQueue->ExecuteCommandLists(1, lists);

if constexpr (!std::is_same_v<std::remove_cvref_t<Func2>, std::nullptr_t>)
func2();

// DX12 -> DX11 handoff
DX::ThrowIfFailed(interop->commandQueue->Signal(interop->d3d12Fence.get(), ++interop->currentFenceValue));
DX::ThrowIfFailed(interop->d3d11Context->Wait(interop->d3d11Fence.get(), interop->currentFenceValue));

// Record what fence value this frame context was submitted at
frame.fenceValueAtSubmission = interop->currentFenceValue;
}
};
106 changes: 106 additions & 0 deletions src/DX12Interop/WrappedResource.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
#include "WrappedResource.h"
#include "DX12Interop.h"

WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device)
{
// Create D3D11 shared texture directly instead of wrapping D3D12 resource
a_texDesc.MiscFlags |= D3D11_RESOURCE_MISC_SHARED;
DX::ThrowIfFailed(a_d3d11Device->CreateTexture2D(&a_texDesc, nullptr, &resource11));

// Get shared handle from D3D11 texture to enable D3D12 access
winrt::com_ptr<IDXGIResource1> dxgiResource;
DX::ThrowIfFailed(resource11->QueryInterface(IID_PPV_ARGS(dxgiResource.put())));

HANDLE sharedHandle = nullptr;
DX::ThrowIfFailed(dxgiResource->GetSharedHandle(&sharedHandle));

// Open the shared D3D11 texture as D3D12 resource
DX::ThrowIfFailed(a_d3d12Device->OpenSharedHandle(sharedHandle, IID_PPV_ARGS(resource.put())));

if (a_texDesc.BindFlags & D3D11_BIND_SHADER_RESOURCE) {
D3D11_SHADER_RESOURCE_VIEW_DESC srvDesc = {};
srvDesc.Format = a_texDesc.Format;

if (a_texDesc.ArraySize > 1)
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2DARRAY;
srvDesc.Texture2DArray.FirstArraySlice = 0;
srvDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize;
srvDesc.Texture2DArray.MostDetailedMip = 0;
srvDesc.Texture2DArray.MipLevels = 1;
}
else
{
srvDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D;
srvDesc.Texture2D.MostDetailedMip = 0;
srvDesc.Texture2D.MipLevels = 1;

}

DX::ThrowIfFailed(a_d3d11Device->CreateShaderResourceView(resource11, &srvDesc, &srv));
Comment thread
Gistix marked this conversation as resolved.
}

if (a_texDesc.BindFlags & D3D11_BIND_UNORDERED_ACCESS) {
D3D11_UNORDERED_ACCESS_VIEW_DESC uavDesc = {};
uavDesc.Format = a_texDesc.Format;

if (a_texDesc.ArraySize > 1) {
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2DARRAY;
uavDesc.Texture2DArray.FirstArraySlice = 0;
uavDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize;
} else {
uavDesc.ViewDimension = D3D11_UAV_DIMENSION_TEXTURE2D;
uavDesc.Texture2D.MipSlice = 0;
}

DX::ThrowIfFailed(a_d3d11Device->CreateUnorderedAccessView(resource11, &uavDesc, &uav));
}

if (a_texDesc.BindFlags & D3D11_BIND_RENDER_TARGET) {
D3D11_RENDER_TARGET_VIEW_DESC rtvDesc = {};
rtvDesc.Format = a_texDesc.Format;

if (a_texDesc.ArraySize > 1)
{
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2DARRAY;
rtvDesc.Texture2DArray.FirstArraySlice = 0;
rtvDesc.Texture2DArray.ArraySize = a_texDesc.ArraySize;
rtvDesc.Texture2DArray.MipSlice = 0;
}
else
{
rtvDesc.ViewDimension = D3D11_RTV_DIMENSION_TEXTURE2D;
rtvDesc.Texture2D.MipSlice = 0;
}

DX::ThrowIfFailed(a_d3d11Device->CreateRenderTargetView(resource11, &rtvDesc, &rtv));
}
}

WrappedResource::WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc) :
WrappedResource(a_texDesc,
globals::dx12Interop->d3d11Device.get(),
globals::dx12Interop->d3d12Device.get())
{
}
Comment thread
Gistix marked this conversation as resolved.

WrappedResource::~WrappedResource()
{
if (resource11) {
resource11->Release();
resource11 = nullptr;
}
if (srv) {
srv->Release();
srv = nullptr;
}
if (uav) {
uav->Release();
uav = nullptr;
}
if (rtv) {
rtv->Release();
rtv = nullptr;
}
// resource (winrt::com_ptr) will be automatically released
}
20 changes: 20 additions & 0 deletions src/DX12Interop/WrappedResource.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
#pragma once

#include <winrt/base.h>

#include <d3d11_4.h>
#include <directx/d3dx12.h>

class WrappedResource
{
public:
WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc, ID3D11Device5* a_d3d11Device, ID3D12Device* a_d3d12Device);
WrappedResource(D3D11_TEXTURE2D_DESC a_texDesc);
~WrappedResource();

ID3D11Texture2D* resource11 = nullptr;
ID3D11ShaderResourceView* srv = nullptr;
ID3D11UnorderedAccessView* uav = nullptr;
ID3D11RenderTargetView* rtv = nullptr;
winrt::com_ptr<ID3D12Resource> resource;
};
Comment thread
Gistix marked this conversation as resolved.
Loading
Loading