Skip to content

ResourceUploadBatch

Chuck Walbourn edited this page Sep 15, 2022 · 54 revisions
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

Header

#include "ResourceUploadBatch.h"

Usage

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 to Upload.

Command queues

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.

Generating mipmaps

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.

Supported Formats

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.

Loading buffers

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();

Threading model

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

Remarks

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.

Further reading

Memory Management

Textures

Uploading Texture Data Through Buffers

For Use

  • Universal Windows Platform apps
  • Windows desktop apps
  • Windows 11
  • Windows 10
  • Xbox One
  • Xbox Series X|S

Architecture

  • x86
  • x64
  • ARM64

For Development

  • Visual Studio 2022
  • Visual Studio 2019 (16.11)
  • clang/LLVM v12 - v18
  • MinGW 12.2, 13.2
  • CMake 3.20

Related Projects

DirectX Tool Kit for DirectX 11

DirectXMesh

DirectXTex

DirectXMath

Tools

Test Suite

Model Viewer

Content Exporter

DxCapsViewer

See also

DirectX Landing Page

Clone this wiki locally