Unpacking and Packing DXGI_FORMAT for In-Place Image Editing

The D3DX_DXGIFormatConvert.inl file contains inline format conversion functions that you can use in the compute shader or pixel shader on Direct3D 11 hardware. You can use these functions in your application to simultaneously both read from and write to a texture. That is, you can perform in-place image editing. To use these inline format conversion functions, include the D3DX_DXGIFormatConvert.inl file in your application.

The D3DX_DXGIFormatConvert.inl header ships in the legacy DirectX SDK. It is also included in the Microsoft.DXSDK.D3DX NuGet package.

Direct3D 11's Unordered Access View (UAV) of a Texture1D, Texture2D, or Texture3D resource supports random access reads and writes to memory from a compute shader or pixel shader. However, Direct3D 11 supports simultaneously both reading from and writing to only the DXGI_FORMAT_R32_UINT texture format. For example, Direct3D 11 does not support simultaneously both reading from and writing to other, more useful formats, such as DXGI_FORMAT_R8G8B8A8_UNORM. You can use only a UAV to random access write to such other formats, or you can use only a Shader Resource View (SRV) to random access read from such other formats. Format conversion hardware is not available to simultaneously both read from and write to such other formats.

However, you can still simultaneously both read from and write to such other formats by casting the texture to the DXGI_FORMAT_R32_UINT texture format when you create a UAV, as long as the original format of the resource supports casting to DXGI_FORMAT_R32_UINT. Most 32 bit per element formats support casting to DXGI_FORMAT_R32_UINT. By casting the texture to the DXGI_FORMAT_R32_UINT texture format when you create a UAV, you can then simultaneous perform reads and writes to the texture as long as the shader performs manual format unpacking on read and packing on write.

The benefit of casting the texture to the DXGI_FORMAT_R32_UINT texture format is that later on you can use the appropriate format (for example, DXGI_FORMAT_R16G16_FLOAT) with other views on the same texture, such as Render Target Views (RTVs) or SRVs. Therefore, the hardware can perform the typical automatic format unpack and pack, can perform texture filtering, and so on where there are no hardware limitations.

The following scenario requires that an application take the following sequence of actions to perform in-place image editing.

Suppose you want to make a texture on which you can use a pixel shader or compute shader to perform in-place editing, and you want the texture data to be stored in a format that is a descendant of one of the following TYPELESS formats:

  • DXGI_FORMAT_R10G10B10A2_TYPELESS
  • DXGI_FORMAT_R8G8B8A8_TYPELESS
  • DXGI_FORMAT_B8G8R8A8_TYPELESS
  • DXGI_FORMAT_B8G8R8X8_TYPELESS
  • DXGI_FORMAT_R16G16_TYPELESS

For example, the DXGI_FORMAT_R10G10B10A2_UNORM format is a descendant of the DXGI_FORMAT_R10G10B10A2_TYPELESS format. Therefore, DXGI_FORMAT_R10G10B10A2_UNORM supports the usage pattern that is described in the following sequence. Formats that descend from DXGI_FORMAT_R32_TYPELESS, such as DXGI_FORMAT_R32_FLOAT, are trivially supported without requiring any of the format conversion help that is described in the following sequence.

To perform in-place image editing

  1. Create a texture with the appropriate TYPELESS-dependent format that is specified in the previous scenario together with the required bind flags, such as D3D11_BIND_UNORDERED_ACCESS | D3D11_BIND_SHADER_RESOURCE.

  2. For in-place image editing, create a UAV with the DXGI_FORMAT_R32_UINT format. The Direct3D 11 API typically does not allow casting between different format "families." However, the Direct3D 11 API makes an exception with the DXGI_FORMAT_R32_UINT format.

  3. In the compute shader or pixel shader, use the appropriate inline format pack and unpack functions that are provided in the D3DX_DXGIFormatConvert.inl file. For example, suppose the DXGI_FORMAT_R32_UINT UAV of the texture really holds DXGI_FORMAT_R10G10B10A2_UNORM-formatted data. After the application reads a uint from the UAV into the shader, it must call the following function to unpack the texture format:

    XMFLOAT4 D3DX_R10G10B10A2_UNORM_to_FLOAT4(UINT packedInput)
    

    Then, to write to the UAV in the same shader, the application calls the following function to pack shader data into a uint that the application can write to the UAV:

    UINT D3DX_FLOAT4_to_R10G10B10A2_UNORM(hlsl_precise XMFLOAT4 unpackedInput)
    
  4. The application can then create other views, such as SRVs, with the required format. For example, the application can create a SRV with the DXGI_FORMAT_R10G10B10A2_UNORM format if the resource was created as DXGI_FORMAT_R10G10B10A2_TYPELESS. When a shader accesses that SRV, the hardware can perform automatic type conversion as usual.

Note

If the shader must write only to a UAV, or read as an SRV, none of this conversion work is needed because you can use fully typed UAVs or SRVs. The format conversion functions provided in D3DX_DXGIFormatConvert.inl are potentially useful only if you want to perform simultaneous reading from and writing to a UAV of a texture.

 

The following is the list of format conversion functions that are included in the D3DX_DXGIFormatConvert.inl file. These functions are categorized by the DXGI_FORMAT that they unpack and pack. Each of the supported formats descends from one of the TYPELESS formats listed in the preceding scenario and supports casting to DXGI_FORMAT_R32_UINT as a UAV.

DXGI_FORMAT_R10G10B10A2_UNORM

XMFLOAT4 D3DX_R10G10B10A2_UNORM_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_R10G10B10A2_UNORM(hlsl_precise XMFLOAT4 unpackedInput)

DXGI_FORMAT_R10G10B10A2_UINT

XMUINT4 D3DX_R10G10B10A2_UINT_to_UINT4(UINT packedInput)
UINT    D3DX_UINT4_to_R10G10B10A2_UINT(XMUINT4 unpackedInput)

DXGI_FORMAT_R8G8B8A8_UNORM

XMFLOAT4 D3DX_R8G8B8A8_UNORM_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_R8G8B8A8_UNORM(hlsl_precise XMFLOAT4 unpackedInput)

DXGI_FORMAT_R8G8B8A8_UNORM_SRGB

XMFLOAT4 D3DX_R8G8B8A8_UNORM_SRGB_to_FLOAT4_inexact(UINT packedInput) *
XMFLOAT4 D3DX_R8G8B8A8_UNORM_SRGB_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_R8G8B8A8_UNORM_SRGB(hlsl_precise XMFLOAT4 unpackedInput)

Note

The _inexact-type function uses shader instructions that do not have high enough precision to give the exact answer. The alternative function uses a lookup table stored in the shader to give an exact SRGB->float conversion.

 

DXGI_FORMAT_R8G8B8A8_UINT

XMUINT4 D3DX_R8G8B8A8_UINT_to_UINT4(UINT packedInput)
XMUINT  D3DX_UINT4_to_R8G8B8A8_UINT(XMUINT4 unpackedInput)

DXGI_FORMAT_R8G8B8A8_SNORM

XMFLOAT4 D3DX_R8G8B8A8_SNORM_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_R8G8B8A8_SNORM(hlsl_precise XMFLOAT4 unpackedInput)

DXGI_FORMAT_R8G8B8A8_SINT

XMINT4 D3DX_R8G8B8A8_SINT_to_INT4(UINT packedInput)
UINT   D3DX_INT4_to_R8G8B8A8_SINT(XMINT4 unpackedInput)

DXGI_FORMAT_B8G8R8A8_UNORM

XMFLOAT4 D3DX_B8G8R8A8_UNORM_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_B8G8R8A8_UNORM(hlsl_precise XMFLOAT4 unpackedInput)

DXGI_FORMAT_B8G8R8A8_UNORM_SRGB

XMFLOAT4 D3DX_B8G8R8A8_UNORM_SRGB_to_FLOAT4_inexact(UINT packedInput) *
XMFLOAT4 D3DX_B8G8R8A8_UNORM_SRGB_to_FLOAT4(UINT packedInput)
UINT     D3DX_FLOAT4_to_R8G8B8A8_UNORM_SRGB(hlsl_precise XMFLOAT4 unpackedInput)

Note

The _inexact-type function uses shader instructions that do not have high enough precision to give the exact answer. The alternative function uses a lookup table stored in the shader to give an exact SRGB->float conversion.

 

DXGI_FORMAT_B8G8R8X8_UNORM

XMFLOAT3 D3DX_B8G8R8X8_UNORM_to_FLOAT3(UINT packedInput)
UINT     D3DX_FLOAT3_to_B8G8R8X8_UNORM(hlsl_precise XMFLOAT3 unpackedInput)

DXGI_FORMAT_B8G8R8X8_UNORM_SRGB

XMFLOAT3 D3DX_B8G8R8X8_UNORM_SRGB_to_FLOAT3_inexact(UINT packedInput) *
XMFLOAT3 D3DX_B8G8R8X8_UNORM_SRGB_to_FLOAT3(UINT packedInput)
UINT     D3DX_FLOAT3_to_B8G8R8X8_UNORM_SRGB(hlsl_precise XMFLOAT3 unpackedInput)

Note

The _inexact-type function uses shader instructions that do not have high enough precision to give the exact answer. The alternative function uses a lookup table stored in the shader to give an exact SRGB->float conversion.

 

DXGI_FORMAT_R16G16_FLOAT

XMFLOAT2 D3DX_R16G16_FLOAT_to_FLOAT2(UINT packedInput)
UINT     D3DX_FLOAT2_to_R16G16_FLOAT(hlsl_precise XMFLOAT2 unpackedInput)

DXGI_FORMAT_R16G16_UNORM

XMFLOAT2 D3DX_R16G16_UNORM_to_FLOAT2(UINT packedInput)
UINT     D3DX_FLOAT2_to_R16G16_UNORM(hlsl_precise FLOAT2 unpackedInput)

DXGI_FORMAT_R16G16_UINT

XMUINT2 D3DX_R16G16_UINT_to_UINT2(UINT packedInput)
UINT    D3DX_UINT2_to_R16G16_UINT(XMUINT2 unpackedInput)

DXGI_FORMAT_R16G16_SNORM

XMFLOAT2 D3DX_R16G16_SNORM_to_FLOAT2(UINT packedInput)
UINT     D3DX_FLOAT2_to_R16G16_SNORM(hlsl_precise XMFLOAT2 unpackedInput)

DXGI_FORMAT_R16G16_SINT

XMINT2 D3DX_R16G16_SINT_to_INT2(UINT packedInput)
UINT   D3DX_INT2_to_R16G16_SINT(XMINT2 unpackedInput)

Programming Guide for HLSL

Programming Guide for HLSL