-
Notifications
You must be signed in to change notification settings - Fork 409
Resource Barriers
DirectXTK |
---|
When you are rendering or running a compute shader, many threads of work are executed on the GPU which in most cases is massively parallel hardware. In order to ensure all pending work (like rendering to a texture) is completed before you use the results in another operation, you have two choices:
- You can insert a fence, submit the command-list, and wait for the GPU to stall
- You can insert a resource barrier into the command-list which indicates the point at which one state moves to the next.
For example, when uploading textures you first copy the data a 'upload heap' source (which requires the source texture to be in D3D12_RESOURCE_STATE_COPY_SOURCE
, and the VRAM texture resource be in D3D12_RESOURCE_STATE_COPY_DEST
), then you want to render that just-loaded texture (which typically requires the VRAM texture resource to be in D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
).
A number of DirectX Tool Kit for DX12 classes include support for these state transition resource barriers. For example, the ResourceUploadBatch lets you automatically queue a transition like the one in the example above:
ResourceUploadBatch resourceUpload(device);
resourceUpload.Begin();
resourceUpload.Upload(texture.Get(), 0, &textureData, 1);
resourceUpload.Transition(
texture.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
auto uploadResourcesFinished = resourceUpload.End(m_deviceResources->GetCommandQueue());
uploadResourcesFinished.wait();
In fact, this is done automatically by the DDSTextureLoader / WICTextureLoader since this is the most common use case.
There are similar Transition methods available for GeometricPrimitive and Model when utilizing static vertex buffers & index buffers.
The easiest way to create your own resource barriers is to utilize a D3DX12 helper. Here's a case where you want to transition from being a source of a copy to something used by a compute shader:
auto barrier = CD3DX12_RESOURCE_BARRIER::Transition(texture,
D3D12_RESOURCE_STATE_COPY_SOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
commandList->ResourceBarrier(1, &barrier);
If to simplify code flow you decide to track state with a variable, you may find the DirectXHelpers TransitionResource method useful. The main reason to use this is it checks to see if the before & after states are the same before submitting the barrier.
TransitionResource(commandList, texture, m_txtState,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
...
TransitionResource(commandList, texture,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, m_txtState);
Another style of barrier is when working with two resources that are placed in the same memory. For example, if you want to treat a texture as a UAV. In this case, you need to indicate when you are done with one 'view' of the data and want to use 'another':
auto aliasBarrier = CD3DX12_RESOURCE_BARRIER::Aliasing(dataAsTexture,dataAsUAV);
commandList->ResourceBarrier(1, &aliasBarrier);
If you want to ensure the work from a compute shader has completed output to a UAV resource before proceeding, this is done with:
auto uavBarrier = CD3DX12_RESOURCE_BARRIER::UAV(uavBuffer);
commandList->ResourceBarrier(1, &uavBarrier);
auto uavBarrier = CD3DX12_RESOURCE_BARRIER::UAV(nullptr);
commandList->ResourceBarrier(1, &uavBarrier);
If you need to execute a number of resource barriers at once, it's best to insert them in a batch. This allows the driver to decide how to handle it based on the GPU architecture. For example, here we have two barriers submitting at once:
D3D12_RESOURCE_BARRIER barriers[2];
barriers[0] = CD3DX12_RESOURCE_BARRIER::Transition(uavBuffer,
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS);
barriers[1] = CD3DX12_RESOURCE_BARRIER::Transition(texture,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE);
commandList->ResourceBarrier(static_cast<UINT>(_countof(barriers)), barriers);
If you have a set of barriers you apply and then reverse, you may find DirectXHelpers's ScopedBarrier class useful:
{
ScopedBarrier barriers(commandList,
{
CD3DX12_RESOURCE_BARRIER::Transition(uavBuffer,
D3D12_RESOURCE_STATE_NON_PIXEL_SHADER_RESOURCE, D3D12_RESOURCE_STATE_UNORDERED_ACCESS),
CD3DX12_RESOURCE_BARRIER::Transition(texture,
D3D12_RESOURCE_STATE_UNORDERED_ACCESS, D3D12_RESOURCE_STATE_COPY_SOURCE)
});
...
// At the end of the scope, the same barrier with all the states swapped is executed
}
The swap-chain is the collection of 2 or more render targets associated with display presentation. In order to display a backbuffer, it must be in the D3D12_RESOURCE_STATE_PRESENT
state. In typical a rendering scenario, the current backbuffer is in this state from the previous frame, must be transitioned to the D3D12_RESOURCE_STATE_RENDER_TARGET
at the start of the render frame, and then transitioned back just before Present.
The DeviceResources abstraction supports these transitions via the default parameters to the Prepare and Present methods. You can provide different states to help optimize the backbuffer resource barriers for more complex scenes such as when doing post-processing.
void Prepare(
D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_PRESENT,
D3D12_RESOURCE_STATES afterState = D3D12_RESOURCE_STATE_RENDER_TARGET);
The Prepare method will always Reset
the command-allocator and a command-list for the new frame. It then injects a resource barrier for the current back-buffer resource from beforeState to afterState.
void Present(
D3D12_RESOURCE_STATES beforeState = D3D12_RESOURCE_STATE_RENDER_TARGET);
The Present method will inject a resource barrier for the current back-buffer resource from beforeState to D3D12_RESOURCE_STATE_PRESENT
. It calls Close
on the current command-list and then calls ExecuteCommandLists
on the command queue.
Note if you call
Prepare(D3D12_RESOURCE_STATE_PRESENT, D3D12_RESOURCE_STATE_PRESENT);
orPresent(D3D12_RESOURCE_STATE_PRESENT);
, then DeviceResources will not inject any resource barrier into the command-list and assumes you have already put the resource into the correct resource state.
Using Resource Barriers to Synchronize Resource States in Direct3D 12
All content and source code for this package are subject to the terms of the MIT License.
This project has adopted the Microsoft Open Source Code of Conduct. For more information see the Code of Conduct FAQ or contact [email protected] with any additional questions or comments.
- Universal Windows Platform apps
- Windows desktop apps
- Windows 11
- Windows 10
- Xbox One
- Xbox Series X|S
- x86
- x64
- ARM64
- Visual Studio 2022
- Visual Studio 2019 (16.11)
- clang/LLVM v12 - v18
- MinGW 12.2, 13.2
- CMake 3.20