Skip to content

Commit

Permalink
[D3D12] Refactored resource transition barriers.
Browse files Browse the repository at this point in the history
Resources are no longer put back into their previous state at each encoded command for performance reasons.
This requires smarter caching of state transition barriers and D3D12CommandContext now tracks the resource states
that the command list expects them to be in at the beginning as well as the states the resources will be in at the end of the command list.

D3D12CommandBuffer does separate resource transition tracking for bundles (aka secondary command buffers).

Also fixed D3D12ResourceHeap::TransitionResources(), which uses the wrong descriptor index for resource transitions.

TODO: MIP-map generator still does not transition subresources, only the whole resource, which is not correct for UAVs and sub-SRVs.
  • Loading branch information
LukasBanana committed Jan 3, 2025
1 parent 266ff2a commit fbc5f1f
Show file tree
Hide file tree
Showing 18 changed files with 501 additions and 293 deletions.
14 changes: 6 additions & 8 deletions include/LLGL/CommandBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,14 +39,12 @@ class SwapChain;
\remarks This is the main interface to encode graphics, compute, and blit commands to be submitted to the GPU.
All states that can be changed with a setter function are not persistent across several encoding sections.
Before any command can be encoded, the command buffer must be set into encode mode, which is done by the CommandBuffer::Begin function.
\remarks In a multi-threaded environment, all blit commands (e.g. CommandBuffer::UpdateBuffer, CommandBuffer::CopyBuffer etc.) <b>must not</b> be called simultaneously
with the same source and/or destination resources even if their ranges do not collide.
Depending on the backend, those resources might be transitioned into different states during those commands.
The same applies to CommandBuffer::BeginRenderPass where the specified RenderTarget might be transitioned into rendering state.
Binding resources (CommandBuffer::SetResource, CommandBuffer::SetResourceHeap) as well as vertex (CommandBuffer::SetVertexBuffer) and
index streams (CommandBuffer::SetIndexBuffer) can be performed in a multi-threaded fashion with either the same or separate resources.
Before any command can be encoded, the command buffer must be put into recording mode via the CommandBuffer::Begin function.
And before the command buffer can be submitted to the command queue, it must be put out of recording mode via the CommandBuffer::End function.
\remarks In a multi-threaded environment, buffer and texture resources <b>must not</b> be encoded in more than one command buffer at a time.
They can be used in more than one command buffer, but they cannot be encoded in parallel.
That is because some backends might modify internal data of the resources to quickly organize them in caches.
\see RenderSystem::CreateCommandBuffer
*/
Expand Down
111 changes: 55 additions & 56 deletions sources/Renderer/Direct3D12/Buffer/D3D12Buffer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
#include "../D3D12Types.h"
#include "../D3D12ObjectUtils.h"
#include "../Command/D3D12CommandContext.h"
#include "../Command/D3D12CommandQueue.h"
#include "../../DXCommon/DXCore.h"
#include "../../BufferUtils.h"
#include "../../../Core/Assertion.h"
Expand Down Expand Up @@ -239,6 +240,9 @@ void D3D12Buffer::ClearSubresourceUInt(
if (useIntermediateBuffer)
{
/* Clear intermediate buffer with UAV */
const D3D12_RESOURCE_STATES oldIntermediateBufferState = uavIntermediateBuffer_.currentState;
commandContext.TransitionResource(uavIntermediateBuffer_, D3D12_RESOURCE_STATE_UNORDERED_ACCESS, true);

ClearSubresourceWithUAV(
commandList,
uavIntermediateBuffer_.Get(),
Expand All @@ -254,33 +258,28 @@ void D3D12Buffer::ClearSubresourceUInt(
/* Copy intermediate buffer into destination buffer */
commandContext.TransitionResource(uavIntermediateBuffer_, D3D12_RESOURCE_STATE_COPY_SOURCE);
commandContext.TransitionResource(GetResource(), D3D12_RESOURCE_STATE_COPY_DEST, true);
{
if (fillSize == GetBufferSize())
commandList->CopyResource(GetNative(), uavIntermediateBuffer_.Get());
else
commandList->CopyBufferRegion(GetNative(), offset, uavIntermediateBuffer_.Get(), offset, fillSize);
}
commandContext.TransitionResource(uavIntermediateBuffer_, uavIntermediateBuffer_.usageState);
commandContext.TransitionResource(GetResource(), GetResource().usageState, true);

if (fillSize == GetBufferSize())
commandList->CopyResource(GetNative(), uavIntermediateBuffer_.Get());
else
commandList->CopyBufferRegion(GetNative(), offset, uavIntermediateBuffer_.Get(), offset, fillSize);
}
else
{
/* Clear destination buffer directly with intermediate UAV */
commandContext.TransitionResource(GetResource(), D3D12_RESOURCE_STATE_COMMON, true);
{
ClearSubresourceWithUAV(
commandList,
GetNative(),
GetBufferSize(),
gpuDescHandle,
cpuDescHandle,
offset,
fillSize,
formatStride,
values
);
}
commandContext.TransitionResource(GetResource(), GetResource().usageState, true);
commandContext.TransitionResource(GetResource(), D3D12_RESOURCE_STATE_UNORDERED_ACCESS, true);

ClearSubresourceWithUAV(
commandList,
GetNative(),
GetBufferSize(),
gpuDescHandle,
cpuDescHandle,
offset,
fillSize,
formatStride,
values
);
}

/* Reset previous staging descriptor heaps */
Expand All @@ -304,17 +303,16 @@ HRESULT D3D12Buffer::Map(
{
/* Copy content from GPU host memory to CPU memory */
commandContext.TransitionResource(resource_, D3D12_RESOURCE_STATE_COPY_SOURCE, true);
{
commandContext.GetCommandList()->CopyBufferRegion(
cpuAccessBuffer_.Get(),
range.Begin,
GetNative(),
range.Begin,
range.End - range.Begin
);
}
commandContext.TransitionResource(resource_, resource_.usageState, true);
commandContext.FinishAndSync(commandQueue);

commandContext.GetCommandList()->CopyBufferRegion(
cpuAccessBuffer_.Get(),
range.Begin,
GetNative(),
range.Begin,
range.End - range.Begin
);

commandQueue.FinishAndSubmitCommandContext(commandContext, true);

/* Map with read range */
return cpuAccessBuffer_.Get()->Map(0, &range, mappedData);
Expand Down Expand Up @@ -342,17 +340,16 @@ void D3D12Buffer::Unmap(

/* Copy content from CPU memory to GPU host memory */
commandContext.TransitionResource(resource_, D3D12_RESOURCE_STATE_COPY_DEST, true);
{
commandContext.GetCommandList()->CopyBufferRegion(
GetNative(),
mappedRange_.Begin,
cpuAccessBuffer_.Get(),
mappedRange_.Begin,
mappedRange_.End - mappedRange_.Begin
);
}
commandContext.TransitionResource(resource_, resource_.usageState, true);
commandContext.FinishAndSync(commandQueue);

commandContext.GetCommandList()->CopyBufferRegion(
GetNative(),
mappedRange_.Begin,
cpuAccessBuffer_.Get(),
mappedRange_.Begin,
mappedRange_.End - mappedRange_.Begin
);

commandQueue.FinishAndSubmitCommandContext(commandContext, true);
}
else
{
Expand All @@ -378,24 +375,26 @@ static D3D12_RESOURCE_FLAGS GetD3DResourceFlags(const BufferDescriptor& desc)
return static_cast<D3D12_RESOURCE_FLAGS>(flags);
}

//TODO: transition sources before binding
static D3D12_RESOURCE_STATES GetD3DUsageState(long bindFlags)
{
D3D12_RESOURCE_STATES flagsD3D = D3D12_RESOURCE_STATE_COMMON;

if ((bindFlags & (BindFlags::VertexBuffer | BindFlags::ConstantBuffer)) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER;
else if ((bindFlags & BindFlags::IndexBuffer) != 0)
if ((bindFlags & BindFlags::IndexBuffer) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_INDEX_BUFFER;
else if ((bindFlags & BindFlags::Storage) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
else if ((bindFlags & BindFlags::StreamOutputBuffer) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_STREAM_OUT;
else if ((bindFlags & BindFlags::IndirectBuffer) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;

//if ((bindFlags & BindFlags::Sampled) != 0)
// flagsD3D |= (D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);

if (flagsD3D == 0)
{
if ((bindFlags & BindFlags::Storage) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_UNORDERED_ACCESS;
else if ((bindFlags & BindFlags::StreamOutputBuffer) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_STREAM_OUT;
else if ((bindFlags & BindFlags::IndirectBuffer) != 0)
flagsD3D |= D3D12_RESOURCE_STATE_INDIRECT_ARGUMENT;
else if ((bindFlags & BindFlags::Sampled) != 0)
flagsD3D |= (D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE | D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
}

return flagsD3D;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@

#include "D3D12BufferConstantsPool.h"
#include "D3D12StagingBufferPool.h"
#include "../Command/D3D12CommandContext.h"
#include "../Command/D3D12CommandQueue.h"
#include "../D3DX12/d3dx12.h"
#include "../D3D12Resource.h"
#include "../../DXCommon/DXCore.h"
Expand Down Expand Up @@ -112,7 +112,7 @@ void D3D12BufferConstantsPool::CreateImmutableBuffer(

/* Initialize buffer with registered constants */
stagingBufferPool.WriteImmediate(commandContext, resource_, 0, data.data(), bufferSize);
commandContext.FinishAndSync(commandQueue);
commandQueue.FinishAndSubmitCommandContext(commandContext, true);
}


Expand Down
12 changes: 8 additions & 4 deletions sources/Renderer/Direct3D12/Buffer/D3D12StagingBufferPool.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@

#include "D3D12StagingBufferPool.h"
#include "../Command/D3D12CommandContext.h"
#include "../Command/D3D12CommandQueue.h"
#include "../D3D12Resource.h"
#include "../../../Core/CoreUtils.h"
#include <algorithm>
Expand Down Expand Up @@ -51,12 +52,13 @@ HRESULT D3D12StagingBufferPool::WriteStaged(

/* Write data to current chunk */
HRESULT hr;
const D3D12_RESOURCE_STATES oldResourceState = dstBuffer.currentState;
commandContext.TransitionResource(dstBuffer, D3D12_RESOURCE_STATE_COPY_DEST, true);
{
D3D12StagingBuffer& chunk = chunks_[chunkIdx_];
hr = chunk.WriteAndIncrementOffset(commandContext.GetCommandList(), dstBuffer.Get(), dstOffset, data, dataSize);
}
commandContext.TransitionResource(dstBuffer, dstBuffer.usageState, true);
commandContext.TransitionResource(dstBuffer, oldResourceState);
return hr;
}

Expand All @@ -70,11 +72,12 @@ HRESULT D3D12StagingBufferPool::WriteImmediate(
{
/* Write data to global upload buffer and copy region to destination buffer */
HRESULT hr;
const D3D12_RESOURCE_STATES oldResourceState = dstBuffer.currentState;
commandContext.TransitionResource(dstBuffer, D3D12_RESOURCE_STATE_COPY_DEST, true);
{
hr = GetUploadBufferAndGrow(dataSize, alignment).Write(commandContext.GetCommandList(), dstBuffer.Get(), dstOffset, data, dataSize);
}
commandContext.TransitionResource(dstBuffer, dstBuffer.usageState, true);
commandContext.TransitionResource(dstBuffer, oldResourceState);
return hr;
}

Expand All @@ -90,12 +93,13 @@ HRESULT D3D12StagingBufferPool::ReadSubresourceRegion(
D3D12StagingBuffer& readbackBuffer = GetReadbackBufferAndGrow(dataSize, alignment);

/* Copy source buffer region to readback buffer and flush command list */
const D3D12_RESOURCE_STATES oldResourceState = srcBuffer.currentState;
commandContext.TransitionResource(srcBuffer, D3D12_RESOURCE_STATE_COPY_SOURCE, true);
{
commandContext.GetCommandList()->CopyBufferRegion(readbackBuffer.GetNative(), 0, srcBuffer.Get(), srcOffset, dataSize);
}
commandContext.TransitionResource(srcBuffer, srcBuffer.usageState, true);
commandContext.FinishAndSync(commandQueue);
commandContext.TransitionResource(srcBuffer, oldResourceState);
commandQueue.FinishAndSubmitCommandContext(commandContext, true);

/* Map readback buffer to CPU memory space */
char* mappedData = nullptr;
Expand Down
Loading

0 comments on commit fbc5f1f

Please sign in to comment.