Files
UnrealEngine/Engine/Source/Runtime/D3D12RHI/Private/D3D12CommandList.cpp
2025-05-18 13:04:45 +08:00

516 lines
15 KiB
C++

// Copyright Epic Games, Inc. All Rights Reserved.
#include "D3D12CommandList.h"
#include "D3D12RHIPrivate.h"
#include "RHIValidation.h"
static int32 GD3D12BatchResourceBarriers = 1;
static FAutoConsoleVariableRef CVarD3D12BatchResourceBarriers(
TEXT("d3d12.BatchResourceBarriers"),
GD3D12BatchResourceBarriers,
TEXT("Whether to allow batching resource barriers"));
static int32 GD3D12ExtraDepthTransitions = 0;
static FAutoConsoleVariableRef CVarD3D12ExtraDepthTransitions(
TEXT("d3d12.ExtraDepthTransitions"),
GD3D12ExtraDepthTransitions,
TEXT("Adds extra transitions for the depth buffer to fix validation issues. However, this currently breaks async compute"));
void FD3D12CommandList::UpdateResidency(const FD3D12Resource* Resource)
{
#if ENABLE_RESIDENCY_MANAGEMENT
if (Resource->NeedsDeferredResidencyUpdate())
{
State.DeferredResidencyUpdateSet.Add(Resource);
}
else
{
AddToResidencySet(Resource->GetResidencyHandles());
}
#endif // ENABLE_RESIDENCY_MANAGEMENT
}
#if ENABLE_RESIDENCY_MANAGEMENT
FD3D12ResidencySet* FD3D12CommandList::CloseResidencySet()
{
for (const FD3D12Resource* Resource : State.DeferredResidencyUpdateSet)
{
AddToResidencySet(Resource->GetResidencyHandles());
}
if (State.DeferredResidencyUpdateSet.Num() > 0)
{
D3DX12Residency::Close(ResidencySet);
}
return ResidencySet;
}
void FD3D12CommandList::AddToResidencySet(TConstArrayView<FD3D12ResidencyHandle*> ResidencyHandles)
{
for (FD3D12ResidencyHandle* Handle : ResidencyHandles)
{
if (D3DX12Residency::IsInitialized(Handle))
{
#if DO_CHECK
check(Device->GetGPUMask() == Handle->GPUObject->GetGPUMask());
#endif
D3DX12Residency::Insert(*ResidencySet, *Handle);
}
}
}
#endif // ENABLE_RESIDENCY_MANAGEMENT
void FD3D12ContextCommon::AddTransitionBarrier(FD3D12Resource* pResource, D3D12_RESOURCE_STATES Before, D3D12_RESOURCE_STATES After, uint32 Subresource)
{
if (Before != After)
{
ResourceBarrierBatcher.AddTransition(pResource, Before, After, Subresource);
UpdateResidency(pResource);
if (!GD3D12BatchResourceBarriers)
{
FlushResourceBarriers();
}
}
else
{
ensureMsgf(0, TEXT("AddTransitionBarrier: Before == After (%d)"), (uint32)Before);
}
}
void FD3D12ContextCommon::AddUAVBarrier()
{
ResourceBarrierBatcher.AddUAV();
if (!GD3D12BatchResourceBarriers)
{
FlushResourceBarriers();
}
}
void FD3D12ContextCommon::AddAliasingBarrier(ID3D12Resource* InResourceBefore, ID3D12Resource* InResourceAfter)
{
ResourceBarrierBatcher.AddAliasingBarrier(InResourceBefore, InResourceAfter);
if (!GD3D12BatchResourceBarriers)
{
FlushResourceBarriers();
}
}
void FD3D12ContextCommon::FlushResourceBarriers()
{
if (ResourceBarrierBatcher.Num())
{
ResourceBarrierBatcher.FlushIntoCommandList(GetCommandList(), TimestampQueries);
}
}
FD3D12CommandAllocator::FD3D12CommandAllocator(FD3D12Device* Device, ED3D12QueueType QueueType)
: Device(Device)
, QueueType(QueueType)
{
VERIFYD3D12RESULT(Device->GetDevice()->CreateCommandAllocator(GetD3DCommandListType(QueueType), IID_PPV_ARGS(CommandAllocator.GetInitReference())));
INC_DWORD_STAT(STAT_D3D12NumCommandAllocators);
}
FD3D12CommandAllocator::~FD3D12CommandAllocator()
{
CommandAllocator.SafeRelease();
DEC_DWORD_STAT(STAT_D3D12NumCommandAllocators);
}
void FD3D12CommandAllocator::Reset()
{
VERIFYD3D12RESULT(CommandAllocator->Reset());
}
FD3D12CommandList::FD3D12CommandList(FD3D12CommandAllocator* CommandAllocator, FD3D12QueryAllocator* TimestampAllocator, FD3D12QueryAllocator* PipelineStatsAllocator)
: Device(CommandAllocator->Device)
, QueueType(CommandAllocator->QueueType)
, ResidencySet(D3DX12Residency::CreateResidencySet(Device->GetResidencyManager()))
, State(CommandAllocator, TimestampAllocator, PipelineStatsAllocator)
{
switch (QueueType)
{
case ED3D12QueueType::Direct:
case ED3D12QueueType::Async:
VERIFYD3D12RESULT(Device->CreateCommandList(
Device->GetGPUMask().GetNative(),
GetD3DCommandListType(QueueType),
*CommandAllocator,
nullptr,
IID_PPV_ARGS(Interfaces.GraphicsCommandList.GetInitReference())
));
Interfaces.CommandList = Interfaces.GraphicsCommandList;
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.CopyCommandList.GetInitReference()));
// Optionally obtain the versioned ID3D12GraphicsCommandList[0-9]+ interfaces, we don't check the HRESULT.
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 1
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList1.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 2
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList2.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 3
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList3.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 4
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList4.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 5
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList5.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 6
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList6.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 7
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList7.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 8
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList8.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 9
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList9.GetInitReference()));
#endif
#if D3D12_MAX_COMMANDLIST_INTERFACE >= 10
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.GraphicsCommandList10.GetInitReference()));
#endif
#if D3D12_SUPPORTS_DEBUG_COMMAND_LIST
Interfaces.CommandList->QueryInterface(IID_PPV_ARGS(Interfaces.DebugCommandList.GetInitReference()));
#endif
break;
case ED3D12QueueType::Copy:
VERIFYD3D12RESULT(Device->GetDevice()->CreateCommandList(
Device->GetGPUMask().GetNative(),
GetD3DCommandListType(QueueType),
*CommandAllocator,
nullptr,
IID_PPV_ARGS(Interfaces.CopyCommandList.GetInitReference())
));
Interfaces.CommandList = Interfaces.CopyCommandList;
break;
default:
checkNoEntry();
return;
}
INC_DWORD_STAT(STAT_D3D12NumCommandLists);
#if NV_AFTERMATH
Interfaces.AftermathHandle = UE::RHICore::Nvidia::Aftermath::D3D12::RegisterCommandList(Interfaces.CommandList);
#endif
#if INTEL_GPU_CRASH_DUMPS
Interfaces.IntelCommandListHandle = UE::RHICore::Intel::GPUCrashDumps::D3D12::RegisterCommandList( Interfaces.GraphicsCommandList );
#endif
#if RHI_USE_RESOURCE_DEBUG_NAME
const FString Name = FString::Printf(TEXT("FD3D12CommandList (GPU %d)"), Device->GetGPUIndex());
SetD3D12ObjectName(Interfaces.CommandList, GetData(Name));
#endif
D3DX12Residency::Open(ResidencySet);
BeginLocalQueries();
}
FD3D12CommandList::~FD3D12CommandList()
{
D3DX12Residency::DestroyResidencySet(Device->GetResidencyManager(), ResidencySet);
#if NV_AFTERMATH
UE::RHICore::Nvidia::Aftermath::D3D12::UnregisterCommandList(Interfaces.AftermathHandle);
#endif
DEC_DWORD_STAT(STAT_D3D12NumCommandLists);
}
void FD3D12CommandList::Reset(FD3D12CommandAllocator* NewCommandAllocator, FD3D12QueryAllocator* TimestampAllocator, FD3D12QueryAllocator* PipelineStatsAllocator)
{
check(IsClosed());
check(NewCommandAllocator->Device == Device && NewCommandAllocator->QueueType == QueueType);
if (Interfaces.CopyCommandList)
{
VERIFYD3D12RESULT(Interfaces.CopyCommandList->Reset(*NewCommandAllocator, nullptr));
}
else
{
VERIFYD3D12RESULT(Interfaces.GraphicsCommandList->Reset(*NewCommandAllocator, nullptr));
}
D3DX12Residency::Open(ResidencySet);
(&State)->~FState();
new (&State) FState(NewCommandAllocator, TimestampAllocator, PipelineStatsAllocator);
BeginLocalQueries();
}
void FD3D12CommandList::Close()
{
check(IsOpen());
EndLocalQueries();
HRESULT hr;
if (Interfaces.CopyCommandList)
{
hr = Interfaces.CopyCommandList->Close();
}
else
{
hr = Interfaces.GraphicsCommandList->Close();
}
#if DEBUG_RESOURCE_STATES
if (hr != S_OK)
LogResourceBarriers(State.ResourceBarriers, Interfaces.CommandList.GetReference(), ED3D12QueueType::Direct , FString(DX12_RESOURCE_NAME_TO_LOG));
#endif
VERIFYD3D12RESULT(hr);
if (State.DeferredResidencyUpdateSet.Num() == 0)
{
D3DX12Residency::Close(ResidencySet);
}
State.IsClosed = true;
}
void FD3D12CommandList::BeginLocalQueries()
{
#if DO_CHECK
check(!State.bLocalQueriesBegun);
State.bLocalQueriesBegun = true;
#endif
if (State.BeginTimestamp)
{
#if RHI_NEW_GPU_PROFILER
// CPUTimestamp is filled in at submission time in FlushProfilerEvents
auto& Event = EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FBeginWork>(0);
State.BeginTimestamp.Target = &Event.GPUTimestampTOP;
#endif
EndQuery(State.BeginTimestamp);
}
if (State.PipelineStats)
{
BeginQuery(State.PipelineStats);
}
}
void FD3D12CommandList::EndLocalQueries()
{
#if DO_CHECK
check(!State.bLocalQueriesEnded);
State.bLocalQueriesEnded = true;
#endif
if (State.PipelineStats)
{
EndQuery(State.PipelineStats);
}
if (State.EndTimestamp)
{
#if RHI_NEW_GPU_PROFILER
auto& Event = EmplaceProfilerEvent<UE::RHI::GPUProfiler::FEvent::FEndWork>();
State.EndTimestamp.Target = &Event.GPUTimestampBOP;
#endif
EndQuery(State.EndTimestamp);
}
}
void FD3D12CommandList::BeginQuery(FD3D12QueryLocation const& Location)
{
check(Location);
check(Location.Heap->QueryType == D3D12_QUERY_TYPE_OCCLUSION || Location.Heap->QueryType == D3D12_QUERY_TYPE_PIPELINE_STATISTICS);
GraphicsCommandList()->BeginQuery(
Location.Heap->GetD3DQueryHeap(),
Location.Heap->QueryType,
Location.Index
);
}
void FD3D12CommandList::EndQuery(FD3D12QueryLocation const& Location)
{
check(Location);
switch (Location.Heap->QueryType)
{
default:
checkNoEntry();
break;
case D3D12_QUERY_TYPE_PIPELINE_STATISTICS:
GraphicsCommandList()->EndQuery(
Location.Heap->GetD3DQueryHeap(),
Location.Heap->QueryType,
Location.Index
);
State.PipelineStatsQueries.Add(Location);
break;
case D3D12_QUERY_TYPE_OCCLUSION:
GraphicsCommandList()->EndQuery(
Location.Heap->GetD3DQueryHeap(),
Location.Heap->QueryType,
Location.Index
);
State.OcclusionQueries.Add(Location);
break;
case D3D12_QUERY_TYPE_TIMESTAMP:
{
ED3D12QueryPosition Position;
switch (Location.Type)
{
default:
checkf(false, TEXT("Query location type is not a top or bottom of pipe timestamp."));
Position = ED3D12QueryPosition::BottomOfPipe;
break;
#if RHI_NEW_GPU_PROFILER
case ED3D12QueryType::ProfilerTimestampTOP:
#else
case ED3D12QueryType::CommandListBegin:
case ED3D12QueryType::IdleBegin:
#endif
Position = ED3D12QueryPosition::TopOfPipe;
break;
case ED3D12QueryType::TimestampMicroseconds:
case ED3D12QueryType::TimestampRaw:
#if RHI_NEW_GPU_PROFILER
case ED3D12QueryType::ProfilerTimestampBOP:
#else
case ED3D12QueryType::CommandListEnd:
case ED3D12QueryType::IdleEnd:
#endif
Position = ED3D12QueryPosition::BottomOfPipe;
break;
}
WriteTimestamp(Location, Position);
#if RHI_NEW_GPU_PROFILER == 0
// Command list begin/end timestamps are handled separately by the
// submission thread, so shouldn't be in the TimestampQueries array.
if (Location.Type != ED3D12QueryType::CommandListBegin && Location.Type != ED3D12QueryType::CommandListEnd)
#endif
{
State.TimestampQueries.Add(Location);
}
}
break;
}
}
#if D3D12RHI_PLATFORM_USES_TIMESTAMP_QUERIES
void FD3D12CommandList::WriteTimestamp(FD3D12QueryLocation const& Location, ED3D12QueryPosition Position)
{
GraphicsCommandList()->EndQuery(
Location.Heap->GetD3DQueryHeap(),
Location.Heap->QueryType,
Location.Index
);
}
#endif // D3D12RHI_PLATFORM_USES_TIMESTAMP_QUERIES
FD3D12CommandList::FState::FState(FD3D12CommandAllocator* CommandAllocator, FD3D12QueryAllocator* TimestampAllocator, FD3D12QueryAllocator* PipelineStatsAllocator)
: CommandAllocator(CommandAllocator)
#if RHI_NEW_GPU_PROFILER
, EventStream(CommandAllocator->Device->GetQueue(CommandAllocator->QueueType).GetProfilerQueue())
#endif
{
if (TimestampAllocator)
{
#if RHI_NEW_GPU_PROFILER
BeginTimestamp = TimestampAllocator->Allocate(ED3D12QueryType::ProfilerTimestampTOP, nullptr);
EndTimestamp = TimestampAllocator->Allocate(ED3D12QueryType::ProfilerTimestampBOP, nullptr);
#else
BeginTimestamp = TimestampAllocator->Allocate(ED3D12QueryType::CommandListBegin, nullptr);
EndTimestamp = TimestampAllocator->Allocate(ED3D12QueryType::CommandListEnd , nullptr);
#endif
}
if (PipelineStatsAllocator)
{
PipelineStats = PipelineStatsAllocator->Allocate(ED3D12QueryType::PipelineStats, nullptr);
}
}
void FD3D12ContextCommon::TransitionResource(FD3D12Resource* Resource, D3D12_RESOURCE_STATES Before, D3D12_RESOURCE_STATES After, uint32 Subresource)
{
check(Resource);
check(Resource->RequiresResourceStateTracking());
check(!((After & (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)) && (Resource->GetDesc().Flags & D3D12_RESOURCE_FLAG_DENY_SHADER_RESOURCE)));
check(Before != D3D12_RESOURCE_STATE_TBD);
check(After != D3D12_RESOURCE_STATE_TBD);
#ifdef PLATFORM_SUPPORTS_RESOURCE_COMPRESSION
After |= Resource->GetCompressedState();
#endif
#if DEBUG_RESOURCE_STATES
FString IncompatibilityReason;
if (CheckResourceStateCompatibility(After, Resource->GetDesc().Flags, IncompatibilityReason) == false)
{
UE_LOG(LogRHI, Error, TEXT("Incompatible Transition State for Resource %s - %s"), *Resource->GetName().ToString(), *IncompatibilityReason);
}
#endif
UpdateResidency(Resource);
TransitionResource(Resource, Subresource, Before, After);
}
static inline bool IsTransitionNeeded(D3D12_RESOURCE_STATES Before, D3D12_RESOURCE_STATES After, FD3D12Resource* InResource = nullptr)
{
check(Before != D3D12_RESOURCE_STATE_CORRUPT && After != D3D12_RESOURCE_STATE_CORRUPT);
check(Before != D3D12_RESOURCE_STATE_TBD && After != D3D12_RESOURCE_STATE_TBD);
// COMMON is an oddball state that doesn't follow the RESOURE_STATE pattern of
// having exactly one bit set so we need to special case these
if (After == D3D12_RESOURCE_STATE_COMMON)
{
// Before state should not have the common state otherwise it's invalid transition
check(Before != D3D12_RESOURCE_STATE_COMMON);
return true;
}
return Before != After;
}
void FD3D12ContextCommon::TransitionResource(FD3D12Resource* InResource, uint32 InSubresourceIndex, D3D12_RESOURCE_STATES InBeforeState, D3D12_RESOURCE_STATES InAfterState)
{
check(InBeforeState != D3D12_RESOURCE_STATE_TBD);
check(InAfterState != D3D12_RESOURCE_STATE_TBD);
if (IsTransitionNeeded(InBeforeState, InAfterState, InResource))
{
AddTransitionBarrier(InResource, InBeforeState, InAfterState, InSubresourceIndex);
}
}
namespace D3D12RHI
{
void GetGfxCommandListAndQueue(FRHICommandList& RHICmdList, void*& OutGfxCmdList, void*& OutCommandQueue)
{
IRHICommandContext& RHICmdContext = RHICmdList.GetContext();
FD3D12CommandContext& CmdContext = static_cast<FD3D12CommandContext&>(RHICmdContext);
check(CmdContext.IsDefaultContext());
OutGfxCmdList = CmdContext.GraphicsCommandList().Get();
OutCommandQueue = CmdContext.Device->GetQueue(CmdContext.QueueType).D3DCommandQueue;
}
}