DirectX-Specs

HLSL Advanced Texture Operations

v1.0 2022-08-01

Shader Model 6.7 introduces support for a collection of advanced texture operations


Contents


Notation

In HLSL code below, these notation conventions are used for non-native HLSL code:

Integer Sampling

Values from unsigned integer formats can now be used by texture retrieval intrinsics with certain restrictions on filtering. Integer samples may not use any LINEAR or ANISOTROPIC filtering modes. These D3D12_FILTER options are allowed:

To fully enable integer sampling requires a way to specify integer border colors that were previously only float values. This adds a new D3D12_SAMPLER_DESC variant that can specify float or integer border values.

typedef struct D3D12_SAMPLER_DESC2 {
  D3D12_FILTER               Filter;
  D3D12_TEXTURE_ADDRESS_MODE AddressU;
  D3D12_TEXTURE_ADDRESS_MODE AddressV;
  D3D12_TEXTURE_ADDRESS_MODE AddressW;
  FLOAT                      MipLODBias;
  UINT                       MaxAnisotropy;
  D3D12_COMPARISON_FUNC      ComparisonFunc;
  union {
    FLOAT                    FloatBorderColor[4];
    UINT                     UintBorderColor[4];
  }
  FLOAT                      MinLOD;
  FLOAT                      MaxLOD;
  D3D12_SAMPLER_FLAGS        Flags;
} D3D12_SAMPLER_DESC2;

Where Flags is the enum:

enum D3D12_SAMPLER_FLAGS {
  D3D12_SAMPLER_FLAG_NONE = 0
  D3D12_SAMPLER_FLAG_UINT_BORDER_COLOR = 0x1
}

Setting the D3D12_SAMPLER_FLAG_UINT_BORDER_COLOR bit in the Flags field indicates that the sampler should be treated as having integer border color values and the UintBorderColor field should contain valid integer values representing those border colors. Otherwise, the sampler has float border color values and the FloatBorderColor field should be used.

For signed texture formats, the border color values are treated as 32-bit signed integers instead. For texture formats where the components are smaller than 32 bits, it is undefined whether the hardware will truncate or clamp.

Note: D3D does not currently define an ordering between retrieving border color values and applying SRV swizzles, and different hardware may apply these in opposite orders. Additionally, for stencil data, D3D defines that the shader should retrieve the value in the G channel, which some hardware may achieve by using SRV swizzles. Therefore, it is possible that when sampling stencil and passing out-of-bounds coordinates to return a border color, the border value returned to the shader in the G channel (unless otherwise redirected via an SRV swizzle) may be the value specified in the sampler’s border color in either the R or G channel.

Additional changes are made to the D3D12_STATIC_BORDER_COLOR enum used by the D3D12_STATIC_SAMPLER_DESC struct to include integer border color variants:

typedef enum D3D12_STATIC_BORDER_COLOR {
  D3D12_STATIC_BORDER_COLOR_TRANSPARENT_BLACK,
  D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK,
  D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE,
  D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK_UINT,
  D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE_UINT
};

Static samplers used with unsigned integer formats must use either D3D12_STATIC_BORDER_COLOR_OPAQUE_BLACK_UINT for black borders or D3D12_STATIC_BORDER_COLOR_OPAQUE_WHITE_UINT for white borders.

Support for non-normalized coordinate samplers added D3D12_STATIC_SAMPLER_DESC1 with a D3D12_SAMPLER_FLAGS member. In the context of static samplers, D3D12_SAMPLER_FLAG_UINT_BORDER_COLOR is shadowed by the static border color, and not applicable. However, root signature creation will fail if this flag is used with a floating-point border color; with a uint border color it is redundant, but not an error.

Raw Gather

To enable access to four appropriately-sized elements that would be used for bilinear interpolation when sampling in the form of the indicated appropriately-sized unsigned integer values requires resource aliasing and new HLSL gather methods.

Resource Integer Aliasing

To enable retrieval of elements into raw integers, single-channel unsigned integer resource views can now be created by ID3D12Device::CreateShaderResourceView for resources with identical element bit widths that have been appropriately flagged at creation. For example, a resource view of type DXGI_FORMAT_R32_UINT can be created for a resource of type DXGI_FORMAT_R8G8B8A8_UINT and a resource view of type DXGI_FORMAT_R16_UINT can be created for a resource of type DXGI_FORMAT_R8G8_UINT. Additionally, same-size and same-channel aliasing can be performed as with a resource view of type DXGI_FORMAT_R16_UINT created for a resource of type of DXGI_FORMAT_R16_FLOAT.

In order to be able to create an single-channel integer resource view, a resource must be created using the CreateCommittedResource3, CreatePlacedResource2, or CreateReservedResource2 using the new API fields, NumCastableFormats and pCastableFormats to specify the list of acceptable casts.

Raw Gather Methods

To access single-channel integer values from formats representing multichannel texture elements, a new gather method is introduced:

uint<bits>_t4 <TexObject2D>.GatherRaw(SamplerState S, float<N> Location, [int2 Offset], [out uint Status]);

These are distinct from existing Gather methods because rather than retrieving a single channel of however many the format element contains, they retrieve a single value that represents a raw, bitwise copy of all of the element’s channels without any conversion of texture contents. The variable represents the number of bits corresponding to the type of . Note that `` does not include cube textures. Given that cube texture sampling does not always involve four elements, they are not usable with raw gather.

The unsigned integer formats (DXGI_FORMAT_* values) usable by GatherRaw:

No other formats may be used with GatherRaw. To perform a raw gather on another format, resource aliasing to an integer format must be used as described above.

The uint16_t GatherRaw overload is only available on platforms with native 16-bit shader op support.

The uint64_t GatherRaw overload is only available on platforms with 64-bit shader op support.

Programmable Offsets

These HLSL texture access methods have an optional Offset parameter representing integer offsets to the loaded or sampled location. Earlier shader models required that this offset be an immediate value.

Format <TexObject>::Load( int<N> Location, int<N> Offset, [out uint Status] );
Format <TexObject>::Sample( SamplerState S, float<N> Location, int<N> Offset,
                          [float Clamp], [out uint Status] );
Format <TexObject>::SampleBias( SamplerState S, float<N> Location, float Bias,
                              int<N> Offset, [float Clamp], [out uint Status] );
Format <TexObject>::SampleCmp( SamplerComparisonState S, float<N> Location,
                             float CompareValue, int<N> Offset,
                             [float Clamp], [out uint Status] );
Format <TexObject>::SampleCmpLevelZero( SamplerComparisonState S, float<N> Location,
                                      float CompareValue, int<N> Offset, [out uint Status] );
Format <TexObject>::SampleGrad( SamplerState S, float<N> Location, float DDX, float DDY,
                              int<N> Offset, [float Clamp], [out uint<N> Status]);
Format <TexObject>::SampleLevel( SamplerState S, float<N> Location, float LOD, int<N> Offset,
                               [out uint Status]);

In Shader Model 6.7, the Offset parameters can be variables where the 4 least significant bits are honored as a signed value, yielding a [-8..7] range

Note: no DXIL changes are needed as the operations already take i32 values for offsets.

SampleCmpLevel

Shader Model 6.7 introduces a new SampleCmp texture method to perform the existing Sample/Compare operation with an explicitly specified MIP level of detail(LOD) where a default or implicit level was previously used. This intrinsic is available in all shader stages.

Format <TexObject>::SampleCmpLevel( SamplerComparisonState S, float<N> Location,
                                  float CompareValue, float LOD, [int<N> Offset],
                                  [out uint Status]);
Format TextureCube::SampleCmpLevel( SamplerComparisonState S, float<N> Location,
                                  float CompareValue, float LOD,
                                  [out uint Status]);
Format TextureCubeArray::SampleCmpLevel( SamplerComparisonState S, float<N> Location,
                                  float CompareValue, float LOD,
                                  [out uint Status]);

Note that <TexObject>::SampleCmpLevel has programmable offsets as described above.

Writable MSAA Textures

Shader Model 6.7 introduces writable multi-sampled texture resources:

The Type and Samples template variables represent the HLSL type of the resource and the number of samples. Unlike existing texture resource types, they are required.

These texture resources share the existing methods of the Texture2DMS and Texture2DMSArray resource types with two exceptions: the Operator[] and sample.Operator[][] methods return writable resource variables. The first references the location in sample index 0. The second references the location in the provided sample index.

R RWTexture2DMS::Operator[](uint2 pos);
R RWTexture2DMS::sample.Operator[][](uint sampleIndex, uint2 pos);
R RWTexture2DMSArray::Operator[](uint3 pos);
R RWTexture2DMSArray::sample.Operator[][](uint sampleIndex, uint3 pos);

Support for writable MSAA textures is determined by the WritableMSAATexturesSupported field of D3D12_FEATURE_DATA_D3D12_OPTIONS14.

Device Capability

Devices that support D3D_SHADER_MODEL_6_7 may optionally support these new intrinsics and types as indicated by a capability bit for writable MSAA textures and another indicating support for the other advanced texture operations documented here.

16-bit GatherRaw overload is available on devices that support D3D_SHADER_MODEL_6_7 and support 16-bit integer shader operations as indicated by the Native16BitShaderOpsSupported member of D3D12_FEATURE_D3D12_OPTIONS4.

64-bit GatherRaw overload is available on devices that support D3D_SHADER_MODEL_6_7 and support 64-bit integer shader operations as indicated by the Int64ShaderOps member of D3D12_FEATURE_D3D12_OPTIONS1.

Capability Queries

Applications can query the availability of these features by passing D3D12_FEATURE_D3D12_OPTIONS14 as the Feature parameter and retrieving the pFeatureSupportData parameter as a struct of type D3D12_FEATURE_DATA_D3D12_OPTIONS14. The relevant parts of these structs are defined below.

typedef enum D3D12_FEATURE {
    ...
    D3D12_FEATURE_D3D12_OPTIONS14
} D3D12_FEATURE;

typedef struct D3D12_FEATURE_DATA_D3D12_OPTIONS14 {
    ...
    BOOL AdvancedTextureOpsSupported;
    BOOL WritableMSAATexturesSupported;
} D3D12_FEATURE_DATA_D3D12_OPTIONS14;

WritableMSAATexturesSupported is a boolean that specifies whether writable MSAA textures and their methods, particularly sample.Operator[][], are supported with a given hardware and runtime.

AdvancedTextureOpsSupported is a boolean that specifies whether the features described here are supported with a given hardware and runtime.

Note that D3D12_FEATURE_DATA_D3D12_OPTIONS12::RelaxedFormatCastingSupported would technically be used to indicate support for the functionality that enables integer aliasing, but it is being considered a prerequisite for enabling AdvancedTextureOpsSupported.

Issues

  1. What should Raw Gather be called? GatherRaw is a bit ambiguous, but acceptable with sufficient explanation.

  2. Which DXGI formats should be castable to uint views? No Planar formats due to their complexity and limited usage.

  3. How can we represent elements greater than 32 bits in a uint resource view? We will be reusing the approach where the resource is R32G32_UINT, but it is declared as uint64_t in the shader. This approach has worked before and adding a new format would be too disruptive.

  4. What textures should be raw gatherable? 2D textures and 2Darray textures. Gather isn’t compatible with 3d textures. Cube textures are possible, but there are complications and they are less interesting.

Change Log

Version|Date|Description