Configuring Depth and Stencil Buffers in DirectX

Depth and stencil buffers are essential components of the DirectX graphics pipeline, serving to manage visual occlusion, prevent geometry overlap, and handle complex masking operations like shadows or outlines. This article explains how depth and stencil buffers are set up and configured for depth testing in DirectX, covering resource allocation, view creation, pipeline state definition, and execution at the Output Merger stage.

1. Creating the Depth-Stencil Resource

To perform depth testing, you must first allocate a 2D texture that supports both depth and stencil data. In DirectX, this requires selecting an appropriate DXGI_FORMAT:

  • DXGI_FORMAT_D24_UNORM_S8_UINT: 24 bits allocated for depth precision and 8 bits for the stencil buffer.
  • DXGI_FORMAT_D32_FLOAT: 32-bit floating-point precision dedicated entirely to depth (no stencil).
  • DXGI_FORMAT_D32_FLOAT_S8X24_UINT: 32 bits for depth, 8 bits for stencil, and 24 bits of padding.

In Direct3D 11, the resource is created using ID3D11Device::CreateTexture2D with the D3D11_BIND_DEPTH_STENCIL flag. In Direct3D 12, the texture is committed using ID3D12Device::CreateCommittedResource, marked with D3D12_RESOURCE_FLAG_ALLOW_DEPTH_STENCIL and an initial state of D3D12_RESOURCE_STATE_DEPTH_WRITE.

2. Creating the Depth-Stencil View (DSV)

The raw texture resource cannot be bound directly to the rendering pipeline; it must be exposed via a Depth-Stencil View (DSV).

  • Direct3D 11: Populate a D3D11_DEPTH_STENCIL_VIEW_DESC structure matching your texture's format and dimension, then call ID3D11Device::CreateDepthStencilView.
  • Direct3D 12: Allocate a CPU descriptor handle from a dedicated DSV descriptor heap (D3D12_DESCRIPTOR_HEAP_TYPE_DSV), define a D3D12_DEPTH_STENCIL_VIEW_DESC, and call ID3D12Device::CreateDepthStencilView.

3. Configuring the Depth-Stencil State

The behavior of the depth and stencil tests is governed by a state descriptor (D3D11_DEPTH_STENCIL_DESC or D3D12_DEPTH_STENCIL_DESC).

To configure standard depth testing, specify the following primary fields:

  • DepthEnable: Set to TRUE to activate depth comparisons.
  • DepthWriteMask: Set to D3D11_DEPTH_WRITE_MASK_ALL (or D3D12_DEPTH_WRITE_MASK_ALL) to allow pixels that pass the depth test to write their depth value to the buffer. Use ZERO for read-only depth testing (common for transparent objects).
  • DepthFunc: Specifies the comparison function evaluating whether a incoming pixel fragment passes. The standard setting is D3D11_COMPARISON_LESS (or LESS_EQUAL), meaning incoming fragments closer to the camera replace fragments further away. When using a reverse-Z buffer setup, GREATER or GREATER_EQUAL is used instead.

If stencil testing is also required:

  • StencilEnable: Set to TRUE.
  • StencilReadMask / StencilWriteMask: Bitmasks that govern which bits can be read from or written to.
  • FrontFace / BackFace: Define D3D11_DEPTH_STENCILOP_DESC structures to specify the operations to perform if the stencil test fails (StencilFailOp), if stencil passes but depth fails (StencilDepthFailOp), or if both pass (StencilPassOp), along with the comparison function (StencilFunc).

In Direct3D 11, call ID3D11Device::CreateDepthStencilState to generate an ID3D11DepthStencilState object. In Direct3D 12, this configuration is assigned directly to the DepthStencilState member of a D3D12_GRAPHICS_PIPELINE_STATE_DESC.

4. Binding to the Pipeline and Clearing

Before rendering geometry, the views and states must be bound to the Output Merger (OM) stage of the pipeline:

  • In Direct3D 11: Call ID3D11DeviceContext::OMSetDepthStencilState to set the state, followed by ID3D11DeviceContext::OMSetRenderTargets, passing the render target view alongside your ID3D11DepthStencilView.
  • In Direct3D 12: The state is bound as part of the Pipeline State Object (PSO) via ID3D12GraphicsCommandList::SetPipelineState. The DSV descriptor handle is then bound along with render targets using ID3D12GraphicsCommandList::OMSetRenderTargets.

At the start of each frame or render pass, the buffer must be cleared to a default state using ClearDepthStencilView:

  • Standard depth buffers are cleared to 1.0f (representing the far clipping plane). Reverse-Z implementations clear to 0.0f.
  • The stencil buffer is typically cleared to 0.