You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1322 lines
50 KiB
C++
1322 lines
50 KiB
C++
/*
|
|
* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or
|
|
* its licensors.
|
|
*
|
|
* For complete copyright and license terms please see the LICENSE at the root of this
|
|
* distribution (the "License"). All use of this software is governed by the License,
|
|
* or, if provided, by the license below or the license accompanying this file. Do not
|
|
* remove or modify any license notices. This file is distributed on an "AS IS" BASIS,
|
|
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
*
|
|
*/
|
|
// Original file Copyright Crytek GMBH or its affiliates, used under license.
|
|
#include "RenderDll_precompiled.h"
|
|
#include "DX12CommandList.hpp"
|
|
#include "DX12Resource.hpp"
|
|
#include "DX12View.hpp"
|
|
#include "DX12SamplerState.hpp"
|
|
#include "DX12/GI/CCryDX12SwapChain.hpp"
|
|
#include "AzCore/Debug/EventTraceDrillerBus.h"
|
|
|
|
namespace DX12
|
|
{
|
|
namespace EventTrace
|
|
{
|
|
const AZStd::thread_id GpuDetailThreadId{ (size_t)2 };
|
|
const char* GpuDetailName = "GPU Detail";
|
|
const char* GpuDetailCategory = "GPU";
|
|
}
|
|
|
|
const char* StateToString(D3D12_RESOURCE_STATES state)
|
|
{
|
|
static int buf = 0;
|
|
static char str[2][512];
|
|
static char *ret;
|
|
|
|
ret = str[buf ^= 1];
|
|
*ret = '\0';
|
|
|
|
if (!state)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " Common/Present");
|
|
return ret;
|
|
}
|
|
|
|
if ((state & D3D12_RESOURCE_STATE_GENERIC_READ) == D3D12_RESOURCE_STATE_GENERIC_READ)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " Generic Read");
|
|
return ret;
|
|
}
|
|
|
|
if (state & D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " V/C Buffer");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_INDEX_BUFFER)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " I Buffer");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_RENDER_TARGET)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " RT");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_UNORDERED_ACCESS)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " UA");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_DEPTH_WRITE)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " DepthW");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_DEPTH_READ)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " DepthR");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " NoPixelR");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " PixelR");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_STREAM_OUT)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " Stream Out");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " Indirect Arg");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_COPY_DEST)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " CopyD");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_COPY_SOURCE)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " CopyS");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_RESOLVE_DEST)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " ResolveD");
|
|
}
|
|
if (state & D3D12_RESOURCE_STATE_RESOLVE_SOURCE)
|
|
{
|
|
azstrcat(ret, AZStd::size(str[0]), " ResolveS");
|
|
}
|
|
|
|
return ret;
|
|
}
|
|
|
|
CommandList::CommandList(CommandListPool& pool)
|
|
: DeviceObject(pool.GetDevice())
|
|
, m_rPool(pool)
|
|
, m_pCmdQueue{}
|
|
, m_pCmdAllocator{}
|
|
, m_CommandList{}
|
|
, m_pD3D12Device{}
|
|
, m_CurrentNumRTVs{}
|
|
, m_CurrentNumVertexBuffers{}
|
|
, m_CurrentFenceValue{}
|
|
, m_eListType(pool.GetD3D12QueueType())
|
|
, m_State(CLSTATE_FREE)
|
|
, m_nodeMask{}
|
|
#if DX12_GPU_PROFILE_MODE != DX12_GPU_PROFILE_MODE_OFF
|
|
, m_Timers{ *GetDevice() }
|
|
#endif
|
|
, m_DescriptorHeaps
|
|
{
|
|
{
|
|
GetDevice()->GetGlobalDescriptorBlock(D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV, 1024)
|
|
},
|
|
{
|
|
GetDevice()->GetGlobalDescriptorBlock(D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER, 1024)
|
|
},
|
|
{
|
|
GetDevice()->GetGlobalDescriptorBlock(D3D12_DESCRIPTOR_HEAP_TYPE_RTV, 1024)
|
|
},
|
|
{
|
|
GetDevice()->GetGlobalDescriptorBlock(D3D12_DESCRIPTOR_HEAP_TYPE_DSV, 1024)
|
|
}
|
|
}
|
|
{
|
|
ResetStateTracking(CommandModeCompute);
|
|
ResetStateTracking(CommandModeGraphics);
|
|
}
|
|
|
|
CommandList::~CommandList()
|
|
{
|
|
}
|
|
|
|
bool CommandList::Init(UINT64 currentFenceValue)
|
|
{
|
|
m_State = CLSTATE_STARTED;
|
|
m_Commands = 0;
|
|
m_CurrentPipelineState = nullptr;
|
|
m_CurrentRootSignature[DX12::CommandModeCompute] = m_CurrentRootSignature[DX12::CommandModeGraphics] = nullptr;
|
|
m_pDSV = nullptr;
|
|
m_CurrentNumRTVs = 0;
|
|
m_CurrentNumVertexBuffers = 0;
|
|
|
|
m_CurrentFenceValue = currentFenceValue;
|
|
ZeroMemory(m_UsedFenceValues, sizeof(m_UsedFenceValues));
|
|
|
|
m_pD3D12Device = GetDevice()->GetD3D12Device();
|
|
|
|
for (D3D12_DESCRIPTOR_HEAP_TYPE eType = D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV; eType < D3D12_DESCRIPTOR_HEAP_TYPE_NUM_TYPES; eType = D3D12_DESCRIPTOR_HEAP_TYPE(eType + 1))
|
|
{
|
|
m_DescriptorHeaps[eType].Reset();
|
|
}
|
|
|
|
if (!IsInitialized())
|
|
{
|
|
m_pCmdQueue = m_rPool.GetD3D12CommandQueue();
|
|
|
|
D3D12_COMMAND_LIST_TYPE eCmdListType = m_pCmdQueue->GetDesc().Type;
|
|
|
|
ID3D12CommandAllocator* pCmdAllocator = nullptr;
|
|
if (S_OK != m_pD3D12Device->CreateCommandAllocator(eCmdListType, IID_GRAPHICS_PPV_ARGS(&pCmdAllocator)))
|
|
{
|
|
DX12_ERROR("Could not create command allocator!");
|
|
return false;
|
|
}
|
|
|
|
m_pCmdAllocator = pCmdAllocator;
|
|
pCmdAllocator->Release();
|
|
|
|
ID3D12GraphicsCommandList* pCmdList = nullptr;
|
|
if (S_OK != m_pD3D12Device->CreateCommandList(m_nodeMask, eCmdListType, m_pCmdAllocator, nullptr, IID_GRAPHICS_PPV_ARGS(&pCmdList)))
|
|
{
|
|
DX12_ERROR("Could not create command list");
|
|
return false;
|
|
}
|
|
|
|
m_CommandList = pCmdList;
|
|
pCmdList->Release();
|
|
|
|
#if DX12_GPU_PROFILE_MODE != DX12_GPU_PROFILE_MODE_OFF
|
|
m_Timers.Init(TimerCountMax);
|
|
#endif
|
|
|
|
IsInitialized(true);
|
|
}
|
|
|
|
#ifdef DX12_STATS
|
|
m_NumWaitsGPU = 0;
|
|
m_NumWaitsCPU = 0;
|
|
#endif // DX12_STATS
|
|
|
|
return true;
|
|
}
|
|
|
|
void CommandList::Begin()
|
|
{
|
|
#if DX12_GPU_PROFILE_MODE != DX12_GPU_PROFILE_MODE_OFF
|
|
m_Timers.Begin();
|
|
m_TimerHandle = m_Timers.BeginTimer(*this, "CommandList");
|
|
#endif
|
|
}
|
|
|
|
void CommandList::End()
|
|
{
|
|
{
|
|
DX12_COMMANDLIST_TIMER("End");
|
|
FlushBarriers();
|
|
}
|
|
|
|
if (IsUtilized())
|
|
{
|
|
#if DX12_GPU_PROFILE_MODE != DX12_GPU_PROFILE_MODE_OFF
|
|
m_Timers.EndTimer(*this, m_TimerHandle);
|
|
m_Timers.End(*this);
|
|
#endif
|
|
|
|
#ifndef NDEBUG
|
|
HRESULT res =
|
|
#endif
|
|
m_CommandList->Close();
|
|
DX12_ASSERT(res == S_OK, "Could not close command list!");
|
|
}
|
|
|
|
m_State = CLSTATE_COMPLETED;
|
|
}
|
|
|
|
void CommandList::Schedule()
|
|
{
|
|
if (m_State < CLSTATE_SCHEDULED)
|
|
{
|
|
m_State = CLSTATE_SCHEDULED;
|
|
}
|
|
}
|
|
|
|
void CommandList::Submit()
|
|
{
|
|
if (IsUtilized())
|
|
{
|
|
// Inject a Wait() into the CommandQueue prior to executing it to wait for all required resources being available either readable or writable
|
|
#ifdef DX12_IN_ORDER_SUBMISSION
|
|
m_UsedFenceValues[CMDTYPE_ANY][CMDQUEUE_COPY ] = std::max(m_UsedFenceValues[CMDTYPE_READ][CMDQUEUE_COPY ], m_UsedFenceValues[CMDTYPE_WRITE][CMDQUEUE_COPY ]);
|
|
m_UsedFenceValues[CMDTYPE_ANY][CMDQUEUE_GRAPHICS] = std::max(m_UsedFenceValues[CMDTYPE_READ][CMDQUEUE_GRAPHICS], m_UsedFenceValues[CMDTYPE_WRITE][CMDQUEUE_GRAPHICS]);
|
|
#else
|
|
MaxFenceValue(m_UsedFenceValues[CMDTYPE_ANY][CMDQUEUE_COPY ], std::max(m_UsedFenceValues[CMDTYPE_READ][CMDQUEUE_COPY ], m_UsedFenceValues[CMDTYPE_WRITE][CMDQUEUE_COPY ]));
|
|
MaxFenceValue(m_UsedFenceValues[CMDTYPE_ANY][CMDQUEUE_GRAPHICS], std::max(m_UsedFenceValues[CMDTYPE_READ][CMDQUEUE_GRAPHICS], m_UsedFenceValues[CMDTYPE_WRITE][CMDQUEUE_GRAPHICS]));
|
|
#endif
|
|
|
|
m_rPool.WaitForFenceOnGPU(m_UsedFenceValues[CMDTYPE_ANY]);
|
|
|
|
// Then inject the Execute() which is possibly blocked by the Wait()
|
|
ID3D12CommandList* ppCommandLists[] = { GetD3D12CommandList() };
|
|
m_rPool.GetAsyncCommandQueue().ExecuteCommandLists(1, ppCommandLists); // TODO: allow to submit multiple command-lists in one go
|
|
}
|
|
|
|
// Inject the signal of the utilized fence to unblock code which picked up the fence of the command-list (even if it doesn't have contents)
|
|
m_rPool.SetSubmittedFenceValue(SignalFenceOnGPU());
|
|
m_State = CLSTATE_SUBMITTED;
|
|
}
|
|
|
|
void CommandList::Clear()
|
|
{
|
|
m_State = CLSTATE_CLEARING;
|
|
FlushBarriers();
|
|
m_rPool.GetAsyncCommandQueue().ResetCommandList(this);
|
|
}
|
|
|
|
void CommandList::SubmitTimers()
|
|
{
|
|
#if DX12_GPU_PROFILE_MODE != DX12_GPU_PROFILE_MODE_OFF
|
|
m_Timers.ReadbackTimers();
|
|
|
|
for (const Timer& timer : m_Timers.GetTimers())
|
|
{
|
|
EBUS_QUEUE_EVENT(AZ::Debug::EventTraceDrillerBus, RecordSlice, timer.m_Name, EventTrace::GpuDetailCategory, EventTrace::GpuDetailThreadId, timer.m_Timestamp, timer.m_Duration);
|
|
}
|
|
#endif
|
|
}
|
|
|
|
bool CommandList::Reset()
|
|
{
|
|
if (IsUtilized())
|
|
{
|
|
SubmitTimers();
|
|
|
|
HRESULT ret = 0;
|
|
|
|
// reset the allocator before the list re-occupies it, otherwise the whole state of the allocator starts leaking
|
|
if (m_pCmdAllocator)
|
|
{
|
|
ret = m_pCmdAllocator->Reset();
|
|
}
|
|
// make the list re-occupy this allocator, reseting the list will _not_ reset the allocator
|
|
if (m_CommandList)
|
|
{
|
|
ret = m_CommandList->Reset(m_pCmdAllocator, NULL);
|
|
}
|
|
}
|
|
|
|
m_State = CLSTATE_FREE;
|
|
m_Commands = 0;
|
|
return true;
|
|
}
|
|
|
|
void CommandList::ResetStateTracking(CommandMode commandMode)
|
|
{
|
|
memset(&m_CurrentRootParameters[commandMode], ~0, sizeof(m_CurrentRootParameters[commandMode]));
|
|
}
|
|
|
|
void CommandList::SetPipelineState(const PipelineState* pso)
|
|
{
|
|
if (m_CurrentPipelineState != pso)
|
|
{
|
|
m_CurrentPipelineState = pso;
|
|
|
|
DX12_COMMANDLIST_TIMER_DETAIL("SetPipelineState");
|
|
m_CommandList->SetPipelineState(pso->GetD3D12PipelineState());
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
}
|
|
|
|
void CommandList::SetRootSignature(CommandMode commandMode, const RootSignature* rootSignature)
|
|
{
|
|
if (m_CurrentRootSignature[commandMode] != rootSignature)
|
|
{
|
|
m_CurrentRootSignature[commandMode] = rootSignature;
|
|
|
|
if (rootSignature)
|
|
{
|
|
ResetStateTracking(commandMode);
|
|
DX12_COMMANDLIST_TIMER_DETAIL("SetRootSignature");
|
|
switch (commandMode)
|
|
{
|
|
case CommandModeGraphics:
|
|
m_CommandList->SetGraphicsRootSignature(rootSignature->GetD3D12RootSignature());
|
|
break;
|
|
|
|
case CommandModeCompute:
|
|
m_CommandList->SetComputeRootSignature(rootSignature->GetD3D12RootSignature());
|
|
break;
|
|
}
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
}
|
|
}
|
|
|
|
bool CommandList::IsFull(size_t numResources, size_t numSamplers, size_t numRendertargets, size_t numDepthStencils) const
|
|
{
|
|
return
|
|
(GetResourceHeap().GetCursor() + numResources >= GetResourceHeap().GetCapacity()) ||
|
|
(GetSamplerHeap().GetCursor() + numSamplers >= GetSamplerHeap().GetCapacity()) ||
|
|
(GetRenderTargetHeap().GetCursor() + numRendertargets >= GetRenderTargetHeap().GetCapacity()) ||
|
|
(GetDepthStencilHeap().GetCursor() + numDepthStencils >= GetDepthStencilHeap().GetCapacity());
|
|
}
|
|
|
|
void CommandList::SetDescriptorTable(CommandMode commandMode, UINT RootParameterIndex, D3D12_GPU_DESCRIPTOR_HANDLE BaseDescriptor)
|
|
{
|
|
if (m_CurrentRootParameters[commandMode][RootParameterIndex].descriptorHandle.ptr != BaseDescriptor.ptr)
|
|
{
|
|
DX12_ASSERT(RootParameterIndex < RootParameterCountMax, "Too many root parameters");
|
|
m_CurrentRootParameters[commandMode][RootParameterIndex].descriptorHandle = BaseDescriptor;
|
|
|
|
switch (commandMode)
|
|
{
|
|
case CommandModeGraphics:
|
|
m_CommandList->SetGraphicsRootDescriptorTable(RootParameterIndex, BaseDescriptor);
|
|
break;
|
|
|
|
case CommandModeCompute:
|
|
m_CommandList->SetComputeRootDescriptorTable(RootParameterIndex, BaseDescriptor);
|
|
break;
|
|
}
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
}
|
|
|
|
void CommandList::SetConstantBufferView(CommandMode commandMode, UINT RootParameterIndex, D3D12_GPU_VIRTUAL_ADDRESS BufferLocation)
|
|
{
|
|
if (m_CurrentRootParameters[commandMode][RootParameterIndex].gpuAddress != BufferLocation)
|
|
{
|
|
DX12_ASSERT(RootParameterIndex < RootParameterCountMax, "Too many root parameters");
|
|
m_CurrentRootParameters[commandMode][RootParameterIndex].gpuAddress = BufferLocation;
|
|
switch (commandMode)
|
|
{
|
|
case CommandModeGraphics:
|
|
m_CommandList->SetGraphicsRootConstantBufferView(RootParameterIndex, BufferLocation);
|
|
break;
|
|
|
|
case CommandModeCompute:
|
|
m_CommandList->SetComputeRootConstantBufferView(RootParameterIndex, BufferLocation);
|
|
break;
|
|
}
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
}
|
|
|
|
void CommandList::Set32BitConstants(CommandMode commandMode, UINT RootParameterIndex, UINT Num32BitValuesToSet, const void* pSrcData, UINT DestOffsetIn32BitValues)
|
|
{
|
|
switch (commandMode)
|
|
{
|
|
case CommandModeGraphics:
|
|
m_CommandList->SetGraphicsRoot32BitConstants(RootParameterIndex, Num32BitValuesToSet, pSrcData, DestOffsetIn32BitValues);
|
|
break;
|
|
|
|
case CommandModeCompute:
|
|
m_CommandList->SetComputeRoot32BitConstants(RootParameterIndex, Num32BitValuesToSet, pSrcData, DestOffsetIn32BitValues);
|
|
break;
|
|
}
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
|
|
void CommandList::SetDescriptorTables(CommandMode commandMode, D3D12_DESCRIPTOR_HEAP_TYPE eType)
|
|
{
|
|
DX12_ASSERT(m_CurrentRootSignature[commandMode]);
|
|
|
|
const PipelineLayout& layout = m_CurrentRootSignature[commandMode]->GetPipelineLayout();
|
|
for (AZ::u32 rootParameterIdx = 0, tableIdx = 0; rootParameterIdx < layout.m_NumRootParameters; ++rootParameterIdx)
|
|
{
|
|
const CD3DX12_ROOT_PARAMETER& rootParameter = layout.m_RootParameters[rootParameterIdx];
|
|
|
|
if (rootParameter.ParameterType != D3D12_ROOT_PARAMETER_TYPE_DESCRIPTOR_TABLE)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (layout.m_DescriptorTables[tableIdx].m_Type == eType)
|
|
{
|
|
switch (commandMode)
|
|
{
|
|
case CommandModeCompute:
|
|
m_CommandList->SetComputeRootDescriptorTable(rootParameterIdx, m_DescriptorHeaps[eType].GetHandleOffsetGPU(layout.m_DescriptorTables[tableIdx].m_Offset));
|
|
break;
|
|
|
|
case CommandModeGraphics:
|
|
m_CommandList->SetGraphicsRootDescriptorTable(rootParameterIdx, m_DescriptorHeaps[eType].GetHandleOffsetGPU(layout.m_DescriptorTables[tableIdx].m_Offset));
|
|
break;
|
|
}
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
|
|
++tableIdx;
|
|
}
|
|
}
|
|
|
|
void CommandList::BindDepthStencilView(const ResourceView& dsv)
|
|
{
|
|
Resource& resource = dsv.GetDX12Resource();
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE handle = GetDepthStencilHeap().GetHandleOffsetCPU(0);
|
|
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != dsv.GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, handle, dsv.GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_DSV);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CreateDepthStencilView(resource.GetD3D12Resource(), dsv.HasDesc() ? &(dsv.GetDSVDesc()) : NULL, handle);
|
|
}
|
|
|
|
if (dsv.GetDSVDesc().Flags & D3D12_DSV_FLAG_READ_ONLY_DEPTH)
|
|
{
|
|
TrackResourceDRVUsage(resource);
|
|
}
|
|
else
|
|
{
|
|
TrackResourceDSVUsage(resource);
|
|
}
|
|
}
|
|
|
|
void CommandList::PresentRenderTargetView(SwapChain* pDX12SwapChain)
|
|
{
|
|
QueueTransitionBarrier(pDX12SwapChain->GetCurrentBackBuffer(), D3D12_RESOURCE_STATE_PRESENT);
|
|
}
|
|
|
|
void CommandList::BindRenderTargetView(const ResourceView& renderTargetView)
|
|
{
|
|
Resource& resource = renderTargetView.GetDX12Resource();
|
|
|
|
const D3D12_CPU_DESCRIPTOR_HANDLE handle = GetRenderTargetHeap().GetHandleOffsetCPU(0);
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != renderTargetView.GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, handle, renderTargetView.GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_RTV);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CreateRenderTargetView(resource.GetD3D12Resource(), renderTargetView.HasDesc() ? &(renderTargetView.GetRTVDesc()) : NULL, handle);
|
|
}
|
|
|
|
TrackResourceRTVUsage(resource);
|
|
}
|
|
|
|
void CommandList::WriteUnorderedAccessDescriptor(const ResourceView* resourceView, INT descriptorOffset)
|
|
{
|
|
D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = GetResourceHeap().GetHandleOffsetCPU(descriptorOffset);
|
|
|
|
if (resourceView)
|
|
{
|
|
Resource& resource = resourceView->GetDX12Resource();
|
|
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != resourceView->GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, resourceView->GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CreateUnorderedAccessView(resource.GetD3D12Resource(), nullptr, resourceView->HasDesc() ? &(resourceView->GetUAVDesc()) : NULL, dstHandle);
|
|
}
|
|
|
|
TrackResourceUAVUsage(resource);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, GetDevice()->GetNullUnorderedAccessView(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
}
|
|
}
|
|
|
|
void CommandList::WriteShaderResourceDescriptor(const ResourceView* resourceView, INT descriptorOffset)
|
|
{
|
|
D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = GetResourceHeap().GetHandleOffsetCPU(descriptorOffset);
|
|
|
|
if (resourceView)
|
|
{
|
|
Resource& resource = resourceView->GetDX12Resource();
|
|
DX12_ASSERT(resource.GetD3D12Resource(), "No resource to create SRV from!");
|
|
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != resourceView->GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, resourceView->GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CreateShaderResourceView(resource.GetD3D12Resource(), &resourceView->GetSRVDesc(), dstHandle);
|
|
}
|
|
|
|
TrackResourceSRVUsage(resource);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, GetDevice()->GetNullShaderResourceView(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
}
|
|
}
|
|
|
|
void CommandList::WriteConstantBufferDescriptor(const ResourceView* resourceView, INT descriptorOffset, UINT byteOffset, UINT byteCount)
|
|
{
|
|
D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = GetResourceHeap().GetHandleOffsetCPU(descriptorOffset);
|
|
|
|
if (resourceView)
|
|
{
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != resourceView->GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, resourceView->GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV);
|
|
}
|
|
else
|
|
{
|
|
D3D12_CONSTANT_BUFFER_VIEW_DESC cbvDesc;
|
|
cbvDesc = resourceView->GetCBVDesc();
|
|
cbvDesc.BufferLocation += byteOffset;
|
|
cbvDesc.SizeInBytes = byteCount ? byteCount : cbvDesc.SizeInBytes - byteOffset;
|
|
DX12_ASSERT(byteCount < D3D12_REQ_CONSTANT_BUFFER_ELEMENT_COUNT * DX12::CONSTANT_BUFFER_ELEMENT_SIZE, "exceeded allowed size of constant buffer");
|
|
m_pD3D12Device->CreateConstantBufferView(&cbvDesc, dstHandle);
|
|
}
|
|
|
|
TrackResourceCBVUsage(resourceView->GetDX12Resource());
|
|
}
|
|
else
|
|
{
|
|
D3D12_CONSTANT_BUFFER_VIEW_DESC desc = {};
|
|
m_pD3D12Device->CreateConstantBufferView(&desc, dstHandle);
|
|
}
|
|
}
|
|
|
|
void CommandList::WriteSamplerStateDescriptor(const SamplerState* state, INT descriptorOffset)
|
|
{
|
|
D3D12_CPU_DESCRIPTOR_HANDLE dstHandle = GetSamplerHeap().GetHandleOffsetCPU(descriptorOffset);
|
|
|
|
if (state)
|
|
{
|
|
if (INVALID_CPU_DESCRIPTOR_HANDLE != state->GetDescriptorHandle())
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, state->GetDescriptorHandle(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CreateSampler(&state->GetSamplerDesc(), dstHandle);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
m_pD3D12Device->CopyDescriptorsSimple(1, dstHandle, GetDevice()->GetNullSampler(), D3D12_DESCRIPTOR_HEAP_TYPE_SAMPLER);
|
|
}
|
|
}
|
|
|
|
void CommandList::SetResourceAndSamplerStateHeaps()
|
|
{
|
|
DX12_COMMANDLIST_TIMER_DETAIL("SetResourceAndSamplerStateHeaps");
|
|
ID3D12DescriptorHeap* heaps[] =
|
|
{
|
|
GetResourceHeap().GetDescriptorHeap()->GetD3D12DescriptorHeap(),
|
|
GetSamplerHeap().GetDescriptorHeap()->GetD3D12DescriptorHeap()
|
|
};
|
|
|
|
m_CommandList->SetDescriptorHeaps(2, heaps);
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
|
|
void CommandList::BindAndSetOutputViews(INT numRTVs, const ResourceView** rtv, const ResourceView* dsv)
|
|
{
|
|
DX12_COMMANDLIST_TIMER_DETAIL("BindAndSetOutputViews");
|
|
if (dsv)
|
|
{
|
|
BindDepthStencilView(*dsv);
|
|
GetDepthStencilHeap().IncrementCursor();
|
|
}
|
|
|
|
m_pDSV = dsv;
|
|
|
|
for (INT i = 0; i < numRTVs; ++i)
|
|
{
|
|
BindRenderTargetView(*rtv[i]);
|
|
GetRenderTargetHeap().IncrementCursor();
|
|
|
|
m_pRTV[i] = rtv[i];
|
|
}
|
|
|
|
m_CurrentNumRTVs = numRTVs;
|
|
|
|
D3D12_CPU_DESCRIPTOR_HANDLE RTVDescriptors[] = { GetRenderTargetHeap().GetHandleOffsetCPU(-numRTVs) };
|
|
D3D12_CPU_DESCRIPTOR_HANDLE DSVDescriptors[] = { GetDepthStencilHeap().GetHandleOffsetCPU(-1) };
|
|
|
|
m_CommandList->OMSetRenderTargets(numRTVs, numRTVs ? RTVDescriptors : NULL, true, dsv ? DSVDescriptors : NULL);
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
|
|
bool CommandList::IsUsedByOutputViews(const Resource& res) const
|
|
{
|
|
if (m_pDSV && (&m_pDSV->GetDX12Resource() == &res))
|
|
{
|
|
return true;
|
|
}
|
|
|
|
for (INT i = 0, n = m_CurrentNumRTVs; i < n; ++i)
|
|
{
|
|
if (m_pRTV[i] && (&m_pRTV[i]->GetDX12Resource() == &res))
|
|
{
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void CommandList::BindVertexBufferView(const ResourceView& view, INT offset, const TRange<UINT>& bindRange, UINT bindStride, D3D12_VERTEX_BUFFER_VIEW (&heap)[D3D11_IA_VERTEX_INPUT_RESOURCE_SLOT_COUNT])
|
|
{
|
|
Resource& resource = view.GetDX12Resource();
|
|
|
|
D3D12_VERTEX_BUFFER_VIEW& vbvDesc = heap[offset];
|
|
vbvDesc = view.GetVBVDesc();
|
|
vbvDesc.BufferLocation += bindRange.start;
|
|
vbvDesc.SizeInBytes = bindRange.Length() > 0 ? bindRange.Length() : vbvDesc.SizeInBytes - bindRange.start;
|
|
vbvDesc.StrideInBytes = bindStride;
|
|
|
|
TrackResourceVBVUsage(resource);
|
|
}
|
|
|
|
void CommandList::ClearVertexBufferHeap(UINT num)
|
|
{
|
|
memset(m_VertexBufferHeap, 0, std::max(num, m_CurrentNumVertexBuffers) * sizeof(m_VertexBufferHeap[0]));
|
|
}
|
|
|
|
void CommandList::SetVertexBufferHeap(UINT num)
|
|
{
|
|
m_CommandList->IASetVertexBuffers(0, std::max(num, m_CurrentNumVertexBuffers), m_VertexBufferHeap);
|
|
m_Commands += CLCOUNT_SETIO;
|
|
|
|
m_CurrentNumVertexBuffers = num;
|
|
}
|
|
|
|
void CommandList::BindAndSetIndexBufferView(const ResourceView& view, DXGI_FORMAT format, UINT offset)
|
|
{
|
|
Resource& resource = view.GetDX12Resource();
|
|
|
|
D3D12_INDEX_BUFFER_VIEW ibvDesc;
|
|
ibvDesc = view.GetIBVDesc();
|
|
ibvDesc.BufferLocation += offset;
|
|
ibvDesc.SizeInBytes = ibvDesc.SizeInBytes - offset;
|
|
ibvDesc.Format = (format == DXGI_FORMAT_UNKNOWN ? resource.GetDesc().Format : format);
|
|
|
|
TrackResourceIBVUsage(resource);
|
|
|
|
m_CommandList->IASetIndexBuffer(&ibvDesc);
|
|
m_Commands += CLCOUNT_SETIO;
|
|
}
|
|
|
|
void CommandList::ClearDepthStencilView(const ResourceView& view, D3D12_CLEAR_FLAGS clearFlags, float depthValue, UINT stencilValue, UINT NumRects, const D3D12_RECT* pRect)
|
|
{
|
|
DX12_ASSERT(INVALID_CPU_DESCRIPTOR_HANDLE != view.GetDescriptorHandle(), "View has no descriptor handle, that is not allowed!");
|
|
DX12_COMMANDLIST_TIMER_DETAIL("ClearDepthStencilView");
|
|
|
|
Resource& resource = view.GetDX12Resource();
|
|
|
|
TrackResourceDSVUsage(resource);
|
|
FlushBarriers();
|
|
|
|
m_CommandList->ClearDepthStencilView(view.GetDescriptorHandle(), clearFlags, depthValue, stencilValue, NumRects, pRect);
|
|
m_Commands += CLCOUNT_CLEAR;
|
|
}
|
|
|
|
void CommandList::ClearRenderTargetView(const ResourceView& inputView, const FLOAT rgba[4], UINT NumRects, const D3D12_RECT* pRect)
|
|
{
|
|
DX12_ASSERT(INVALID_CPU_DESCRIPTOR_HANDLE != inputView.GetDescriptorHandle(), "View has no descriptor handle, that is not allowed!");
|
|
DX12_COMMANDLIST_TIMER_DETAIL("ClearRenderTargetView");
|
|
|
|
TrackResourceRTVUsage(inputView.GetDX12Resource());
|
|
FlushBarriers();
|
|
|
|
m_CommandList->ClearRenderTargetView(inputView.GetDescriptorHandle(), rgba, NumRects, pRect);
|
|
m_Commands += CLCOUNT_CLEAR;
|
|
}
|
|
|
|
void CommandList::ClearUnorderedAccessView(const ResourceView& view, const UINT rgba[4], UINT NumRects, const D3D12_RECT* pRect)
|
|
{
|
|
DX12_ASSERT(INVALID_CPU_DESCRIPTOR_HANDLE != view.GetDescriptorHandle(), "View has no descriptor handle, that is not allowed!");
|
|
DX12_COMMANDLIST_TIMER_DETAIL("ClearUnorderedAccessView");
|
|
|
|
TrackResourceUAVUsage(view.GetDX12Resource());
|
|
FlushBarriers();
|
|
|
|
m_CommandList->ClearUnorderedAccessViewUint(GetResourceHeap().GetHandleGPUFromCPU(view.GetDescriptorHandle()), view.GetDescriptorHandle(), view.GetD3D12Resource(), rgba, NumRects, pRect);
|
|
m_Commands += CLCOUNT_CLEAR;
|
|
}
|
|
|
|
void CommandList::ClearUnorderedAccessView(const ResourceView& view, const FLOAT rgba[4], UINT NumRects, const D3D12_RECT* pRect)
|
|
{
|
|
DX12_ASSERT(INVALID_CPU_DESCRIPTOR_HANDLE != view.GetDescriptorHandle(), "View has no descriptor handle, that is not allowed!");
|
|
DX12_COMMANDLIST_TIMER_DETAIL("ClearUnorderedAccessView");
|
|
|
|
TrackResourceUAVUsage(view.GetDX12Resource());
|
|
FlushBarriers();
|
|
|
|
m_CommandList->ClearUnorderedAccessViewFloat(GetResourceHeap().GetHandleGPUFromCPU(view.GetDescriptorHandle()), view.GetDescriptorHandle(), view.GetD3D12Resource(), rgba, NumRects, pRect);
|
|
m_Commands += CLCOUNT_CLEAR;
|
|
}
|
|
|
|
void CommandList::ClearView(const ResourceView& view, const FLOAT rgba[4], UINT NumRects, const D3D12_RECT* pRect)
|
|
{
|
|
DX12_ASSERT(INVALID_CPU_DESCRIPTOR_HANDLE != view.GetDescriptorHandle(), "View has no descriptor handle, that is not allowed!");
|
|
|
|
switch (view.GetType())
|
|
{
|
|
case EVT_UnorderedAccessView:
|
|
ClearUnorderedAccessView(view, rgba, NumRects, pRect);
|
|
break;
|
|
|
|
case EVT_DepthStencilView: // Optional implementation under DX11.1
|
|
ClearDepthStencilView(view, D3D12_CLEAR_FLAG_DEPTH | D3D12_CLEAR_FLAG_STENCIL, FLOAT(rgba[0]), UINT(rgba[1]), NumRects, pRect);
|
|
break;
|
|
|
|
case EVT_RenderTargetView:
|
|
ClearRenderTargetView(view, rgba, NumRects, pRect);
|
|
break;
|
|
|
|
case EVT_ShaderResourceView:
|
|
case EVT_ConstantBufferView:
|
|
case EVT_VertexBufferView:
|
|
default:
|
|
DX12_ASSERT(0, "Unsupported resource-type for input!");
|
|
break;
|
|
}
|
|
}
|
|
|
|
void CommandList::CopyResource(Resource& dstResource, Resource& srcResource)
|
|
{
|
|
if (srcResource.InitHasBeenDeferred())
|
|
{
|
|
srcResource.TryStagingUpload(this);
|
|
}
|
|
|
|
DX12_COMMANDLIST_TIMER_DETAIL("CopyResource");
|
|
MaxResourceFenceValue(dstResource, CMDTYPE_ANY);
|
|
MaxResourceFenceValue(srcResource, CMDTYPE_WRITE);
|
|
|
|
QueueTransitionBarrier(dstResource, D3D12_RESOURCE_STATE_COPY_DEST);
|
|
QueueTransitionBarrier(srcResource, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
|
FlushBarriers();
|
|
|
|
DX12_ASSERT(srcResource.GetDesc().Dimension == dstResource.GetDesc().Dimension, "Can't copy resources of different dimension");
|
|
m_CommandList->CopyResource(dstResource.GetD3D12Resource(), srcResource.GetD3D12Resource());
|
|
m_Commands += CLCOUNT_COPY;
|
|
|
|
SetResourceFenceValue(dstResource, m_CurrentFenceValue, CMDTYPE_WRITE);
|
|
SetResourceFenceValue(srcResource, m_CurrentFenceValue, CMDTYPE_READ);
|
|
}
|
|
|
|
void CommandList::CopySubresource(Resource& dstResource, UINT dstSubResource, UINT x, UINT y, UINT z, Resource& srcResource, UINT srcSubResource, const D3D12_BOX* srcBox)
|
|
{
|
|
if (srcResource.InitHasBeenDeferred())
|
|
{
|
|
srcResource.TryStagingUpload(this);
|
|
}
|
|
|
|
DX12_COMMANDLIST_TIMER_DETAIL("CopySubresource");
|
|
MaxResourceFenceValue(dstResource, CMDTYPE_ANY);
|
|
MaxResourceFenceValue(srcResource, CMDTYPE_WRITE);
|
|
|
|
// TODO: if we know early that the resource(s) will be DEST and SOURCE we can begin the barrier early and end it here
|
|
QueueTransitionBarrier(dstResource, D3D12_RESOURCE_STATE_COPY_DEST);
|
|
QueueTransitionBarrier(srcResource, D3D12_RESOURCE_STATE_COPY_SOURCE);
|
|
FlushBarriers();
|
|
|
|
if (srcResource.GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER && dstResource.GetDesc().Dimension == D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
UINT64 offset = 0;
|
|
UINT64 length = srcResource.GetDesc().Width;
|
|
|
|
if (srcBox)
|
|
{
|
|
length = srcBox->right - srcBox->left;
|
|
offset = srcBox->left;
|
|
}
|
|
|
|
m_CommandList->CopyBufferRegion(dstResource.GetD3D12Resource(), x, srcResource.GetD3D12Resource(), offset, length);
|
|
m_Commands += CLCOUNT_COPY;
|
|
}
|
|
else if (srcResource.GetDesc().Dimension != D3D12_RESOURCE_DIMENSION_BUFFER && dstResource.GetDesc().Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
CD3DX12_TEXTURE_COPY_LOCATION src(srcResource.GetD3D12Resource(), srcSubResource);
|
|
CD3DX12_TEXTURE_COPY_LOCATION dst(dstResource.GetD3D12Resource(), dstSubResource);
|
|
|
|
m_CommandList->CopyTextureRegion(&dst, x, y, z, &src, srcBox);
|
|
m_Commands += CLCOUNT_COPY;
|
|
}
|
|
else if (dstResource.GetDesc().Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
// If this assert trips, you may need to increase MAX_SUBRESOURCES. Just be wary of growing the stack too much.
|
|
AZ_Assert(dstSubResource < CCryDX12DeviceContext::MAX_SUBRESOURCES, "Too many sub resources: (sub ID %d requested, %d allowed)", dstSubResource, CCryDX12DeviceContext::MAX_SUBRESOURCES);
|
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layouts[CCryDX12DeviceContext::MAX_SUBRESOURCES];
|
|
|
|
// From our "regular" resource, get the offset and description information for the subresource so we can copy the correct
|
|
// part of the buffer resource. We need to get Layouts for subresource 0 through dstSubResource so that the offset is set
|
|
// correctly. If we just get the Layout for dstSubResource, it will have an offset of 0.
|
|
GetDevice()->GetD3D12Device()->GetCopyableFootprints(&dstResource.GetDesc(), 0, dstSubResource + 1, 0, layouts, nullptr, nullptr, nullptr);
|
|
|
|
CD3DX12_TEXTURE_COPY_LOCATION src(srcResource.GetD3D12Resource(), layouts[dstSubResource]);
|
|
CD3DX12_TEXTURE_COPY_LOCATION dst(dstResource.GetD3D12Resource(), dstSubResource);
|
|
|
|
m_CommandList->CopyTextureRegion(&dst, x, y, z, &src, srcBox);
|
|
m_Commands += CLCOUNT_COPY;
|
|
}
|
|
else if (srcResource.GetDesc().Dimension != D3D12_RESOURCE_DIMENSION_BUFFER)
|
|
{
|
|
// If this assert trips, you may need to increase MAX_SUBRESOURCES. Just be wary of growing the stack too much.
|
|
AZ_Assert(dstSubResource < CCryDX12DeviceContext::MAX_SUBRESOURCES, "Too many sub resources: (sub ID %d requested, %d allowed)", dstSubResource, CCryDX12DeviceContext::MAX_SUBRESOURCES);
|
|
D3D12_PLACED_SUBRESOURCE_FOOTPRINT layouts[CCryDX12DeviceContext::MAX_SUBRESOURCES];
|
|
|
|
// From our "regular" resource, get the offset and description information for the subresource so we can copy the correct
|
|
// part of the buffer resource. We need to get Layouts for subresource 0 through dstSubResource so that the offset is set
|
|
// correctly. If we just get the Layout for dstSubResource, it will have an offset of 0.
|
|
GetDevice()->GetD3D12Device()->GetCopyableFootprints(&srcResource.GetDesc(), 0, srcSubResource + 1, 0, layouts, nullptr, nullptr, nullptr);
|
|
|
|
CD3DX12_TEXTURE_COPY_LOCATION src(srcResource.GetD3D12Resource(), srcSubResource);
|
|
CD3DX12_TEXTURE_COPY_LOCATION dst(dstResource.GetD3D12Resource(), layouts[srcSubResource]);
|
|
|
|
m_CommandList->CopyTextureRegion(&dst, x, y, z, &src, srcBox);
|
|
m_Commands += CLCOUNT_COPY;
|
|
}
|
|
|
|
SetResourceFenceValue(dstResource, m_CurrentFenceValue, CMDTYPE_WRITE);
|
|
SetResourceFenceValue(srcResource, m_CurrentFenceValue, CMDTYPE_READ);
|
|
}
|
|
|
|
void CommandList::UpdateSubresourceRegion(Resource& resource, UINT subResource, const D3D12_BOX* box, const void* data, UINT rowPitch, UINT slicePitch)
|
|
{
|
|
if (resource.InitHasBeenDeferred())
|
|
{
|
|
resource.TryStagingUpload(this);
|
|
}
|
|
|
|
DX12_COMMANDLIST_TIMER_DETAIL("UpdateSubresourceRegion");
|
|
MaxResourceFenceValue(resource, CMDTYPE_ANY);
|
|
|
|
ID3D12Resource* res12 = resource.GetD3D12Resource();
|
|
const D3D12_RESOURCE_DESC& desc = resource.GetDesc();
|
|
|
|
// Determine temporary upload buffer size (mostly only pow2-alignment of textures)
|
|
UINT64 uploadBufferSize;
|
|
D3D12_RESOURCE_DESC uploadDesc = desc;
|
|
|
|
uploadDesc.Width = box ? box->right - box->left : uploadDesc.Width;
|
|
uploadDesc.Height = box ? box->bottom - box->top : uploadDesc.Height;
|
|
|
|
GetDevice()->GetD3D12Device()->GetCopyableFootprints(&uploadDesc, subResource, 1, 0, nullptr, nullptr, nullptr, &uploadBufferSize);
|
|
|
|
DX12_ASSERT((desc.Dimension != D3D12_RESOURCE_DIMENSION_BUFFER) || (box == nullptr), "Box used for buffer update, that's not supported!");
|
|
DX12_ASSERT((desc.Dimension == D3D12_RESOURCE_DIMENSION_BUFFER) || (slicePitch != 0), "Slice-pitch is missing for UpdateSubresourceRegion(), this is a required parameter!");
|
|
ID3D12Resource* uploadBuffer;
|
|
|
|
D3D12_SUBRESOURCE_DATA subData;
|
|
ZeroMemory(&subData, sizeof(D3D12_SUBRESOURCE_DATA));
|
|
subData.pData = data;
|
|
subData.RowPitch = rowPitch;
|
|
subData.SlicePitch = slicePitch;
|
|
assert(subData.pData != nullptr);
|
|
|
|
ResourceStates resourceStates;
|
|
const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_UPLOAD);
|
|
const CD3DX12_RESOURCE_DESC resourceDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
|
|
if (S_OK != GetDevice()->CreateOrReuseCommittedResource(
|
|
&heapProperties,
|
|
D3D12_HEAP_FLAG_NONE,
|
|
&resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
nullptr,
|
|
IID_GRAPHICS_PPV_ARGS(&uploadBuffer),
|
|
resourceStates))
|
|
{
|
|
DX12_ERROR("Could not create intermediate upload buffer!");
|
|
return;
|
|
}
|
|
|
|
// If it is reused resource, we have to transition the recycled state to new state
|
|
AZ_Assert(resourceStates.m_AnnouncedState == -1, "Recycled resource should not be in split state");
|
|
QueueTransitionBarrier(uploadBuffer, resourceStates.m_CurrentState, D3D12_RESOURCE_STATE_GENERIC_READ);
|
|
QueueTransitionBarrier(resource, D3D12_RESOURCE_STATE_COPY_DEST);
|
|
FlushBarriers();
|
|
|
|
::UpdateSubresources<1>(m_CommandList, res12, uploadBuffer, 0, subResource, 1, &subData, box);
|
|
m_Commands += CLCOUNT_COPY;
|
|
|
|
SetResourceFenceValue(resource, m_CurrentFenceValue, CMDTYPE_WRITE);
|
|
|
|
uploadBuffer->SetName(L"UpdateSubresourceRegion");
|
|
GetDevice()->ReleaseLater(uploadBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, static_cast<D3D12_RESOURCE_STATES>(-1));
|
|
}
|
|
|
|
void CommandList::UpdateSubresources(Resource& resource, UINT64 uploadBufferSize, UINT subResources, D3D12_SUBRESOURCE_DATA* subResourceData)
|
|
{
|
|
if (resource.InitHasBeenDeferred())
|
|
{
|
|
resource.TryStagingUpload(this);
|
|
}
|
|
|
|
DX12_COMMANDLIST_TIMER_DETAIL("UpdateSubresources");
|
|
MaxResourceFenceValue(resource, CMDTYPE_ANY);
|
|
|
|
ID3D12Resource* res12 = resource.GetD3D12Resource();
|
|
ID3D12Resource* uploadBuffer = NULL;
|
|
ResourceStates resourceStates;
|
|
const CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_UPLOAD);
|
|
const CD3DX12_RESOURCE_DESC resourceDesc = CD3DX12_RESOURCE_DESC::Buffer(uploadBufferSize);
|
|
if (S_OK != GetDevice()->CreateOrReuseCommittedResource(
|
|
&heapProperties,
|
|
D3D12_HEAP_FLAG_NONE,
|
|
&resourceDesc,
|
|
D3D12_RESOURCE_STATE_GENERIC_READ,
|
|
nullptr,
|
|
IID_GRAPHICS_PPV_ARGS(&uploadBuffer),
|
|
resourceStates))
|
|
{
|
|
DX12_ERROR("Could not create intermediate upload buffer!");
|
|
}
|
|
|
|
// If it is reused resource, we have to transition the recycled state to new state
|
|
AZ_Assert(resourceStates.m_AnnouncedState == -1, "Recycled resource should not be in split state");
|
|
QueueTransitionBarrier(uploadBuffer, resourceStates.m_CurrentState, D3D12_RESOURCE_STATE_GENERIC_READ);
|
|
QueueTransitionBarrier(resource, D3D12_RESOURCE_STATE_COPY_DEST);
|
|
FlushBarriers();
|
|
|
|
::UpdateSubresources(m_CommandList, res12, uploadBuffer, 0, 0, subResources, subResourceData, nullptr);
|
|
m_Commands += CLCOUNT_COPY;
|
|
|
|
SetResourceFenceValue(resource, m_CurrentFenceValue, CMDTYPE_WRITE);
|
|
|
|
uploadBuffer->SetName(L"UpdateSubresources");
|
|
GetDevice()->ReleaseLater(uploadBuffer, D3D12_RESOURCE_STATE_GENERIC_READ, static_cast<D3D12_RESOURCE_STATES>(-1));
|
|
}
|
|
|
|
void CommandList::DiscardResource(Resource& resource, const D3D12_DISCARD_REGION* pRegion)
|
|
{
|
|
DX12_COMMANDLIST_TIMER_DETAIL("DiscardResource");
|
|
FlushBarriers();
|
|
MaxResourceFenceValue(resource, CMDTYPE_ANY);
|
|
|
|
m_CommandList->DiscardResource(resource.GetD3D12Resource(), pRegion);
|
|
m_Commands += CLCOUNT_DISCARD;
|
|
}
|
|
|
|
void CommandList::QueueTransitionBarrier(Resource& resource, D3D12_RESOURCE_STATES finalState)
|
|
{
|
|
if (resource.IsOffCard())
|
|
{
|
|
if (finalState != D3D12_RESOURCE_STATE_COMMON)
|
|
{
|
|
finalState = resource.GetRequiredResourceState();
|
|
}
|
|
}
|
|
|
|
D3D12_RESOURCE_STATES currentState = resource.GetCurrentState();
|
|
D3D12_RESOURCE_STATES announcedState = resource.GetAnnouncedState();
|
|
bool bFinishedSplit = false;
|
|
|
|
// Finish a split barrier.
|
|
if (announcedState != static_cast<D3D12_RESOURCE_STATES>(-1))
|
|
{
|
|
m_Commands += CLCOUNT_BARRIER;
|
|
|
|
//AZ_Printf("DX12", "QueueTransitionBarrier: Split END %p %s -> %s", resource.GetD3D12Resource(), StateToString(currentState), StateToString(announcedState));
|
|
|
|
D3D12_RESOURCE_TRANSITION_BARRIER transition;
|
|
transition.pResource = resource.GetD3D12Resource();
|
|
transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
transition.StateBefore = currentState;
|
|
transition.StateAfter = announcedState;
|
|
m_BarrierCache.EnqueueTransition(m_CommandList, D3D12_RESOURCE_BARRIER_FLAG_END_ONLY, transition);
|
|
|
|
resource.SetAnnouncedState(static_cast<D3D12_RESOURCE_STATES>(-1));
|
|
currentState = announcedState;
|
|
resource.SetCurrentState(currentState);
|
|
bFinishedSplit = true;
|
|
}
|
|
|
|
// Now check if we still need to transition to final state.
|
|
if (currentState != finalState)
|
|
{
|
|
//AZ_Printf("DX12", "QueueTransitionBarrier: Transition %p %s -> %s", resource.GetD3D12Resource(), StateToString(currentState), StateToString(finalState));
|
|
|
|
m_Commands += CLCOUNT_BARRIER;
|
|
|
|
D3D12_RESOURCE_TRANSITION_BARRIER transition;
|
|
transition.pResource = resource.GetD3D12Resource();
|
|
transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
transition.StateBefore = currentState;
|
|
transition.StateAfter = finalState;
|
|
m_BarrierCache.EnqueueTransition(m_CommandList, D3D12_RESOURCE_BARRIER_FLAG_NONE, transition);
|
|
|
|
resource.SetCurrentState(finalState);
|
|
}
|
|
|
|
// Only need UAV barrier if we didn't do a split transition here.
|
|
else if (finalState == D3D12_RESOURCE_STATE_UNORDERED_ACCESS && !bFinishedSplit)
|
|
{
|
|
QueueUAVBarrier(resource);
|
|
}
|
|
}
|
|
|
|
|
|
void CommandList::QueueTransitionBarrierBegin(Resource& resource, D3D12_RESOURCE_STATES finalState)
|
|
{
|
|
if (resource.IsOffCard())
|
|
{
|
|
if (finalState != D3D12_RESOURCE_STATE_COMMON)
|
|
{
|
|
finalState = resource.GetRequiredResourceState();
|
|
}
|
|
}
|
|
|
|
if (resource.GetAnnouncedState() != static_cast<D3D12_RESOURCE_STATES>(-1))
|
|
{
|
|
QueueTransitionBarrier(resource, resource.GetAnnouncedState());
|
|
}
|
|
|
|
D3D12_RESOURCE_STATES stateInitial = resource.GetCurrentState();
|
|
D3D12_RESOURCE_STATES stateAnnounced = resource.GetAnnouncedState();
|
|
|
|
if (stateInitial != finalState && stateAnnounced != finalState)
|
|
{
|
|
m_Commands += CLCOUNT_BARRIER;
|
|
|
|
//AZ_Printf("DX12", "QueueTransitionBarrierBegin: Split Barrier BEGIN %p %s -> %s", resource.GetD3D12Resource(), StateToString(stateInitial), StateToString(finalState));
|
|
|
|
D3D12_RESOURCE_TRANSITION_BARRIER transition;
|
|
transition.pResource = resource.GetD3D12Resource();
|
|
transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
transition.StateBefore = stateInitial;
|
|
transition.StateAfter = finalState;
|
|
|
|
m_BarrierCache.EnqueueTransition(m_CommandList, D3D12_RESOURCE_BARRIER_FLAG_BEGIN_ONLY, transition);
|
|
|
|
resource.SetAnnouncedState(finalState);
|
|
}
|
|
}
|
|
|
|
void CommandList::QueueUAVBarrier(Resource& resource)
|
|
{
|
|
m_Commands += CLCOUNT_BARRIER;
|
|
|
|
m_BarrierCache.EnqueueUAV(m_CommandList, resource);
|
|
}
|
|
|
|
void CommandList::FlushBarriers()
|
|
{
|
|
if (m_BarrierCache.IsFlushNeeded())
|
|
{
|
|
DX12_COMMANDLIST_TIMER_DETAIL("FlushBarriers");
|
|
m_BarrierCache.Flush(m_CommandList);
|
|
}
|
|
}
|
|
|
|
void CommandList::QueueTransitionBarrier(ID3D12Resource* resource, D3D12_RESOURCE_STATES stateBefore, D3D12_RESOURCE_STATES stateAfter)
|
|
{
|
|
if (stateBefore == stateAfter)
|
|
return;
|
|
|
|
D3D12_RESOURCE_TRANSITION_BARRIER transition;
|
|
transition.pResource = resource;
|
|
transition.Subresource = D3D12_RESOURCE_BARRIER_ALL_SUBRESOURCES;
|
|
transition.StateBefore = stateBefore;
|
|
transition.StateAfter = stateAfter;
|
|
|
|
m_BarrierCache.EnqueueTransition(m_CommandList, D3D12_RESOURCE_BARRIER_FLAG_NONE, transition);
|
|
}
|
|
|
|
CommandListPool::CommandListPool(Device* device, CommandListFenceSet& rCmdFence, int nPoolFenceId)
|
|
: m_pDevice(device)
|
|
, m_rCmdFences(rCmdFence)
|
|
, m_nPoolFenceId(nPoolFenceId)
|
|
{
|
|
#ifdef DX12_STATS
|
|
m_PeakNumCommandListsAllocated = 0;
|
|
m_PeakNumCommandListsInFlight = 0;
|
|
m_NumWaitsGPU = 0;
|
|
m_NumWaitsCPU = 0;
|
|
#endif // DX12_STATS
|
|
}
|
|
|
|
CommandListPool::~CommandListPool()
|
|
{
|
|
}
|
|
|
|
bool CommandListPool::Init(D3D12_COMMAND_LIST_TYPE eType)
|
|
{
|
|
if (!m_pCmdQueue)
|
|
{
|
|
D3D12_COMMAND_QUEUE_DESC queueDesc = {};
|
|
|
|
queueDesc.Flags = D3D12_COMMAND_QUEUE_FLAG_NONE;
|
|
queueDesc.Type = m_ePoolType = eType;
|
|
queueDesc.Priority = D3D12_COMMAND_QUEUE_PRIORITY_NORMAL;
|
|
queueDesc.NodeMask = 0; // One GPU?
|
|
|
|
ID3D12CommandQueue* pCmdQueue = NULL;
|
|
if (S_OK != m_pDevice->GetD3D12Device()->CreateCommandQueue(&queueDesc, IID_GRAPHICS_PPV_ARGS(&pCmdQueue)))
|
|
{
|
|
DX12_ERROR("Could not create command queue");
|
|
return false;
|
|
}
|
|
|
|
m_pCmdQueue = pCmdQueue;
|
|
pCmdQueue->Release();
|
|
|
|
EBUS_EVENT(AZ::Debug::EventTraceDrillerSetupBus, SetThreadName, EventTrace::GpuDetailThreadId, EventTrace::GpuDetailName);
|
|
}
|
|
|
|
m_AsyncCommandQueue.Init(this);
|
|
|
|
#ifdef DX12_STATS
|
|
m_PeakNumCommandListsAllocated = 0;
|
|
m_PeakNumCommandListsInFlight = 0;
|
|
#endif // DX12_STATS
|
|
|
|
return true;
|
|
}
|
|
|
|
void CommandListPool::ScheduleCommandLists()
|
|
{
|
|
// Remove finished command-lists from the head of the live-list
|
|
while (m_LiveCommandLists.size())
|
|
{
|
|
SmartPtr<CommandList> pCmdList = m_LiveCommandLists.front();
|
|
|
|
// free -> complete -> submitted -> finished -> clearing -> free
|
|
if (pCmdList->IsFinished() && !pCmdList->IsClearing())
|
|
{
|
|
m_LiveCommandLists.pop_front();
|
|
m_BusyCommandLists.push_back(pCmdList);
|
|
|
|
pCmdList->Clear();
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Submit completed but not yet submitted command-lists from the head of the live-list
|
|
for (uint32 t = 0; t < m_LiveCommandLists.size(); ++t)
|
|
{
|
|
SmartPtr<CommandList> pCmdList = m_LiveCommandLists[t];
|
|
|
|
if (pCmdList->IsScheduled() && !pCmdList->IsSubmitted())
|
|
{
|
|
pCmdList->Submit();
|
|
}
|
|
|
|
if (!pCmdList->IsSubmitted())
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Remove cleared/deallocated command-lists from the head of the busy-list
|
|
while (m_BusyCommandLists.size())
|
|
{
|
|
SmartPtr<CommandList> pCmdList = m_BusyCommandLists.front();
|
|
|
|
// free -> complete -> submitted -> finished -> clearing -> free
|
|
if (pCmdList->IsFree())
|
|
{
|
|
m_BusyCommandLists.pop_front();
|
|
m_FreeCommandLists.push_back(pCmdList);
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
void CommandListPool::CreateOrReuseCommandList(SmartPtr<CommandList>& result)
|
|
{
|
|
if (m_FreeCommandLists.empty())
|
|
{
|
|
result = new CommandList(*this);
|
|
}
|
|
else
|
|
{
|
|
result = m_FreeCommandLists.front();
|
|
|
|
m_FreeCommandLists.pop_front();
|
|
}
|
|
|
|
// Increment fence value on allocation, this has the effect that
|
|
// acquired CommandLists need to be submitted in-order to prevent
|
|
// dead-locking
|
|
SetCurrentFenceValue(GetCurrentFenceValue() + 1);
|
|
|
|
result->Init(GetCurrentFenceValue());
|
|
m_LiveCommandLists.push_back(result);
|
|
|
|
#ifdef DX12_STATS
|
|
m_PeakNumCommandListsAllocated = std::max(m_PeakNumCommandListsAllocated, m_LiveCommandLists.size() + m_BusyCommandLists.size() + m_FreeCommandLists.size());
|
|
m_PeakNumCommandListsInFlight = std::max(m_PeakNumCommandListsInFlight, m_LiveCommandLists.size());
|
|
#endif // DX12_STATS
|
|
}
|
|
|
|
void CommandListPool::AcquireCommandList(SmartPtr<CommandList>& result)
|
|
{
|
|
static CryCriticalSectionNonRecursive csThreadSafeScope;
|
|
CryAutoLock<CryCriticalSectionNonRecursive> lThreadSafeScope(csThreadSafeScope);
|
|
|
|
ScheduleCommandLists();
|
|
|
|
CreateOrReuseCommandList(result);
|
|
}
|
|
|
|
void CommandListPool::ForfeitCommandList(SmartPtr<CommandList>& result, bool bWait)
|
|
{
|
|
static CryCriticalSectionNonRecursive csThreadSafeScope;
|
|
CryAutoLock<CryCriticalSectionNonRecursive> lThreadSafeScope(csThreadSafeScope);
|
|
SmartPtr<CommandList> pWaitable = result;
|
|
|
|
DX12_ASSERT(result->IsCompleted(), "It's not possible to forfeit an unclosed command list!");
|
|
result->Schedule();
|
|
result = nullptr;
|
|
|
|
ScheduleCommandLists();
|
|
|
|
if (bWait)
|
|
{
|
|
pWaitable->WaitForFinishOnCPU();
|
|
}
|
|
}
|
|
|
|
void CommandListPool::AcquireCommandLists(uint32 numCLs, SmartPtr<CommandList>* results)
|
|
{
|
|
static CryCriticalSectionNonRecursive csThreadSafeScope;
|
|
CryAutoLock<CryCriticalSectionNonRecursive> lThreadSafeScope(csThreadSafeScope);
|
|
|
|
ScheduleCommandLists();
|
|
|
|
for (uint32 i = 0; i < numCLs; ++i)
|
|
{
|
|
CreateOrReuseCommandList(results[i]);
|
|
}
|
|
}
|
|
|
|
void CommandListPool::ForfeitCommandLists(uint32 numCLs, SmartPtr<CommandList>* results, bool bWait)
|
|
{
|
|
static CryCriticalSectionNonRecursive csThreadSafeScope;
|
|
CryAutoLock<CryCriticalSectionNonRecursive> lThreadSafeScope(csThreadSafeScope);
|
|
SmartPtr<CommandList> pWaitable = results[numCLs - 1];
|
|
|
|
for (uint32 i = 0; i < numCLs; ++i)
|
|
{
|
|
DX12_ASSERT(results[i]->IsCompleted(), "It's not possible to forfeit an unclosed command list!");
|
|
results[i]->Schedule();
|
|
results[i] = nullptr;
|
|
}
|
|
|
|
ScheduleCommandLists();
|
|
|
|
if (bWait)
|
|
{
|
|
pWaitable->WaitForFinishOnCPU();
|
|
}
|
|
}
|
|
}
|