-
Notifications
You must be signed in to change notification settings - Fork 409
ResourceUploadBatch
DirectXTK |
---|
This provides a helper for uploading resources such as textures to GPU memory. This class also handles automatic generation of mipmaps using shaders on the GPU.
Related tutorial: Sprites and textures
#include "ResourceUploadBatch.h"
This helper is typically used during load and setup as follows which ends with a wait to ensure the GPU is ready before rendering is started. You start with a call to Begin, and then queue one or more upload operations. The End method returns a C++11 std::future
which you use to complete the upload asynchronously.
ResourceUploadBatch upload(device);
upload.Begin();
DX::ThrowIfFailed(
CreateDDSTextureFromFile(device, upload, L"mytexture.dds",
tex.ReleaseAndGetAddressOf())
);
spriteBatch = std::make_unique<SpriteBatch>(device, upload, pd);
spriteFont = std::make_unique<SpriteFont>(device, upload,
L"myfont.spritefont",
resourceDescriptors->GetCpuHandle(Descriptors::TextFont),
resourceDescriptors->GetGpuHandle(Descriptors::TextFont));
...
// Upload the resources to the GPU.
auto finish = upload.End(commandQueue);
// Wait for the upload thread to terminate
finish.wait();
You can also use the class to upload data to your own manually created resources via the Upload method, which need to be in the D3D12_RESOURCE_STATE_COPY_DEST
state. The class supports queuing a resource barrier to transition the state after uploading using the Transition method.
ResourceUploadBatch upload(device);
upload.Begin();
auto desc = CD3DX12_RESOURCE_DESC(
D3D12_RESOURCE_DIMENSION_TEXTURE2D,
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
c_texture_size, c_texture_size, 1, 1,
DXGI_FORMAT_R8G8_SNORM,
1, 0,
D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE);
CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT);
DX::ThrowIfFailed(device->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(tex.ReleaseAndGetAddressOf())));
D3D12_SUBRESOURCE_DATA initData = { mydata, c_texture_size * 2, 0 };
upload.Upload(tex.Get(), 0, &initData, 1);
upload.Transition(tex.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
...
// Upload the resources to the GPU.
auto finish = upload.End(commandQueue);
// Wait for the upload thread to terminate
finish.wait();
The data submitted to Upload is copied to a temporary
D3D12_HEAP_TYPE_UPLOAD
resource which is released when the upload is complete, so you don't need to keep the source buffer active beyond the call toUpload
.
The End
method takes a command queue used to post an internal command list of work for doing the uploads. This class supports three types of command queues:
-
D3D12_COMMAND_LIST_TYPE_DIRECT
: A Direct command queue is the kind typically used for graphics rendering, and is the default because this is the most likely case (i.e. it's the kind of command queue created by DeviceResources). -
D3D12_COMMAND_LIST_TYPE_COMPUTE
: A Compute command queue can perform resource copies and execute compute shaders. -
D3D12_COMMAND_LIST_TYPE_COPY
: A Copy command queue can only perform copies.
The Begin
method takes a defaulted parameter which must match the type of command queue provided to End
void Begin(D3D12_COMMAND_LIST_TYPE commandType = D3D12_COMMAND_LIST_TYPE_DIRECT);
For Direct command queues, the resource has typically been transitioned to the proper rendering resource state via the Transition method after calling Upload
:
-
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
: This is set by the texture loaders assuming the resources are most commonly used by pixel shaders. -
D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER
: This is set by the mesh loaders for static vertex buffers. -
D3D12_RESOURCE_STATE_INDEX_BUFFER
: This is set by the mesh loaders for static index buffers.
For Compute command queues, only a subset of states are supported so the Transition method will filter out attempts to transition to unsupported after states like D3D12_RESOURCE_STATE_INDEX_BUFFER
and D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
. This means in most cases texture resources and static index buffers are left in the D3D12_RESOURCE_STATE_COPY_DEST
state.
For Copy command queues, the only after states supported and not filtered out by the Transition method are D3D12_RESOURCE_STATE_COPY_DEST
and D3D12_RESOURCE_STATE_COPY_SOURCE
. All others are ignored. This means that in most cases, resources are left in the D3D12_RESOURCE_STATE_COPY_DEST
state.
See Microsoft Docs for more details.
DirectX 11 supported auto-generation of mipmaps for specific formats. This feature relied entirely on the driver, so the quality of auto-generated mipmaps was highly variable. DirectX 12 does not implement this feature, leaving it as an "exercise to the implementer". DirectX Tool Kit does this with ResourceUploadBatch
.
To use it, you must create the texture with reserved mipmaps in the D3D12_RESOURCE_STATE_COPY_DEST
state, load the image data into top-level mip, transition the resource to the D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE
state, and then queue the mipmap generation using GenerateMips as follows:
ResourceUploadBatch upload(device);
upload.Begin();
auto desc = CD3DX12_RESOURCE_DESC(
D3D12_RESOURCE_DIMENSION_TEXTURE2D,
D3D12_DEFAULT_RESOURCE_PLACEMENT_ALIGNMENT,
c_texture_size, c_texture_size, 1, c_mipmap_count,
DXGI_FORMAT_R8G8B8A8_UNORM,
1, 0,
D3D12_TEXTURE_LAYOUT_UNKNOWN, D3D12_RESOURCE_FLAG_NONE);
CD3DX12_HEAP_PROPERTIES defaultHeapProperties(D3D12_HEAP_TYPE_DEFAULT);
DX::ThrowIfFailed(device->CreateCommittedResource(
&defaultHeapProperties,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(tex.ReleaseAndGetAddressOf())));
D3D12_SUBRESOURCE_DATA initData = { mydata, c_texture_size * 4, 0 };
upload.Upload(tex.Get(), 0, &initData, 1);
upload.Transition(tex.Get(),
D3D12_RESOURCE_STATE_COPY_DEST,
D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE);
upload.GenerateMips(tex.Get());
...
// Upload the resources to the GPU.
auto finish = upload.End(commandQueue);
// Wait for the upload thread to terminate
finish.wait();
Only Direct and Compute command queues can be used for mipmap generation.
Using a Copy command queue will throw an exception if the GenerateMips method is called. If you call IsSupportedForGenerateMips inside a Begin / End group, it will return false if the command queue provided was a Copy queue. Copy queues can of course load resource with mipmaps already prepopulated.
The GenerateMips function uses a DirectCompute shader to perform the mip-generation using bi-linear interpolation. The resource format must be supported as a typed UAV for load/store, or an sRGB equivalent. For BGR support, the device must also support 'standard swizzle' texture layout. Otherwise a C++ exception is generated.
You can test whether or not a given format is supported by the device by calling IsSupportedForGenerateMips to avoid the exception case.
if (!upload.IsSupportedForGenerateMips(DXGI_FORMAT_R8G8B8A8_UNORM))
{
// The device does not support
// D3D12_FEATURE_DATA_D3D12_OPTIONS.TypedUAVLoadAdditionalFormats
// -or- a copy command queue was provided to Begin and End has not yet been called.
}
if (!upload.IsSupportedForGenerateMips(DXGI_FORMAT_R10G10B10A2_UNORM))
{
// Either the device does not support
// D3D12_FEATURE_DATA_D3D12_OPTIONS.TypedUAVLoadAdditionalFormats
// -or- it doesn't support DXGI_FORMAT_R10G10B10A2_UNORM as a typed UAV
// -or- copy command queue was provided to Begin and End has not yet been called.
}
if (!upload.IsSupportedForGenerateMips(DXGI_FORMAT_B8G8R8A8_UNORM))
{
// Either the device does not support
// D3D12_FEATURE_DATA_D3D12_OPTIONS.TypedUAVLoadAdditionalFormats
// -and/or- it doesn't support D3D12_FEATURE_DATA_D3D12_OPTIONS.StandardSwizzle64KBSupported
// -or- a copy command queue was provided to Begin and End has not yet been called.
}
See Microsoft Docs for more technical details of the limitations here.
Note: High-quality mipmap generation is best done offline, stored in
DDS
files, and then loaded in full at a runtime using DDSTextureLoader.
In addition to the texture functionality, you can also use this class to upload buffer data from GraphicsMemory allocated upload heap memory via an overload of Upload.
ResourceUploadBatch upload(device);
upload.Begin();
// Copy data into the upload heap
SharedGraphicsResource vbUpload = graphicsMemory->Allocate(vertexBufferSize);
memcpy(vbUpload.Memory(), /* source data */, vertexBufferSize);
auto desc = CD3DX12_RESOURCE_DESC::Buffer(vertexBufferSize);
CD3DX12_HEAP_PROPERTIES heapProperties(D3D12_HEAP_TYPE_DEFAULT);
DX::ThrowIfFailed(device->CreateCommittedResource(
&heapProperties,
D3D12_HEAP_FLAG_NONE,
&desc,
D3D12_RESOURCE_STATE_COPY_DEST,
nullptr,
IID_PPV_ARGS(vertexBuffer.GetAddressOf())
));
upload.Upload(vertexBuffer.Get(), vbUpload);
upload.Transition(vertexBuffer.Get(),
D3D12_RESOURCE_STATE_COPY_DEST, D3D12_RESOURCE_STATE_VERTEX_AND_CONSTANT_BUFFER);
...
// Upload the resources to the GPU.
auto finish = upload.End(commandQueue);
// Wait for the upload thread to terminate
finish.wait();
Each ResourceUploadBatch instance only supports submitting content from one thread at a time, but you can simultaneously submit content on multiple threads if you create a separate ResourceUploadBatch instance and command queue for thread.
Work Submission in Direct3D 12
Resource uploading is done for 'static' resources where the data is going to reside in GPU memory and be reused. For 'dynamic' data explicit uploading is not needed as the memory is allocated by GraphicsMemory, is both accessible to CPU and GPU, and will be released once the frame is completed.
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