Skip to content

Textures

Chuck Walbourn edited this page Apr 24, 2018 · 10 revisions

DirectX 12 textures are typically loaded into video memory which is not necessarily accessible to the CPU. For DirectX Tool Kit for DirectX 12, this is typically done by using the ID3D12Resource interface and the ResourceUploadBatch helper is used to transfer the texture data from the CPU to the GPU.

Creating textures

The first step is to create an ID3D12Resource interface. This can be done directly in code or by using the DDSTextureLoader / WICTextureLoader.

// This creates a 1x1 texture in RGBA format
D3D12_RESOURCE_DESC txtDesc = {};
txtDesc.MipLevels = txtDesc.DepthOrArraySize = 1;
txtDesc.Format = DXGI_FORMAT_R8G8B8A8_UNORM;
txtDesc.Width = 1;
txtDesc.Height = 1;
txtDesc.SampleDesc.Count = 1;
txtDesc.Dimension = D3D12_RESOURCE_DIMENSION_TEXTURE2D;

Microsoft::WRL::ComPtr<ID3D12Resource> texture;
DX::ThrowIfFailed(
    device->CreateCommittedResource(
        &CD3DX12_HEAP_PROPERTIES(D3D12_HEAP_TYPE_DEFAULT),
        D3D12_HEAP_FLAG_NONE,
        &txtDesc,
        D3D12_RESOURCE_STATE_COPY_DEST,
        nullptr,
        IID_GRAPHICS_PPV_ARGS(texture.ReleaseAndGetAddressOf())));

Loading texture data

After creating the resource, the texture data needs to be created. It can be done programmatically or by loading from disk or the network. Ideally you load 'fully cooked' textures including mipmaps and formatted in Direct3D specific formats like the Block Compressed formats from DDS files using DDSTextureLoader. You can load general images using WICTextureLoader. The Create*Texture* functions will take a ResourceUploadBatch for the next step in the process, while the Load*Texture* functions return the texture data in system memory instead.

static const uint32_t s_whitePixel = 0xFFFFFFFF;

D3D12_SUBRESOURCE_DATA textureData = {};
textureData.pData = &s_whitePixel;
textureData.RowPitch = txtDesc.Width * 4;
textureData.SlicePitch = txtDesc.Height * txtDesc.Width * 4;

Uploading texture data

After creating the resource and the texture data, the data needs to be uploaded to the GPU accessible memory for rendering. The ResourceUploadBatch helper can be used to manage one or more uploads in a batch, or it can be done manually using something like the D3DX12 helper UpdateSubresources using your own D3D12_HEAP_TYPE_UPLOAD resource.

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

The ResourceUploadBatch helper can also handle the resource barrier to transition from the loading state (D3D12_RESOURCE_STATE_COPY_DEST) that the original resource was created in to a rendering state (D3D12_RESOURCE_STATE_PIXEL_SHADER_RESOURCE) at the appropriate time. Otherwise you need to do the transition manually.

Creating the descriptor

Once the resource has been created and the data uploaded to the GPU, binding to the rendering pipeline requires a descriptor. All the descriptors are created into one or more heaps. This can be managed using the DescriptorHeap class. Once created, you use an index to reference the descriptor, so it can be useful to manage it with an enum

enum Descriptors
{
    Texture,
    Count
};

m_resourceDescriptors = std::make_unique<DescriptorHeap>(device,
    D3D12_DESCRIPTOR_HEAP_TYPE_CBV_SRV_UAV,
    D3D12_DESCRIPTOR_HEAP_FLAG_SHADER_VISIBLE,
    Descriptors::Count);

You can use the DirectXHelpers or create the descriptor itself manually.

CreateShaderResourceView(
    device,
    texture.Get(),
    m_resourceDescriptors->GetCpuHandle(Descriptors::Texture));

For more dynamic scenarios like complex models, etc. you can make use of the DescriptorPile class.

Binding

In order to render, you must set the descriptor heap to the command list:

ID3D12DescriptorHeap* heaps[] = { m_resourceDescriptors->Heap() };
commandList->SetDescriptorHeaps(_countof(heaps), heaps);

From there, the final step is to use the appropriate descriptor index for your root signature or Effects.

Further reading

Sprites and textures

MSDN: Resource Binding

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