Direct3D 11 Buffers API

This section details the Direct3D 11 API for managing and utilizing various types of buffers, which are fundamental data structures used to store vertex data, index data, constant data, and other information required by the GPU.

Understanding Buffers in D3D11

Direct3D 11 uses several types of buffers to represent different kinds of data:

  • Vertex Buffers: Store vertex data (position, color, normals, texture coordinates).
  • Index Buffers: Store indices that define the order in which vertices are drawn, enabling efficient rendering of complex geometry.
  • Constant Buffers: Store constant data (like transformation matrices, lighting parameters) that is uniform across all vertices or pixels in a draw call.
  • Shader Resource Views (SRVs): Used to access buffer data from shaders (read-only).
  • Unordered Access Views (UAVs): Allow shaders to read and write to buffer data.
  • Stream Output Buffers: Capture geometry data emitted by the geometry shader for further processing.

Key Structures and Interfaces

ID3D11Buffer

The ID3D11Buffer interface represents a generic buffer resource. It can be used to create vertex, index, constant, or other types of buffers.

To create an ID3D11Buffer, you typically use the ID3D11Device::CreateBuffer method.

D3D11_BUFFER_DESC Structure

This structure describes the buffer to be created:

typedef struct D3D11_BUFFER_DESC {
    UINT             ByteWidth;
    D3D11_USAGE      Usage;
    D3D11_BIND_FLAG  BindFlags;
    UINT             CPUAccessFlags;
    UINT             MiscFlags;
    UINT             StructureByteStride;
} D3D11_BUFFER_DESC;
                
  • ByteWidth: The size of the buffer in bytes.
  • Usage: Specifies how the buffer will be used (e.g., D3D11_USAGE_DEFAULT, D3D11_USAGE_DYNAMIC, D3D11_USAGE_IMMUTABLE, D3D11_USAGE_STAGING).
  • BindFlags: Specifies how the buffer will be bound to the graphics pipeline (e.g., D3D11_BIND_VERTEX_BUFFER, D3D11_BIND_INDEX_BUFFER, D3D11_BIND_CONSTANT_BUFFER, D3D11_BIND_SHADER_RESOURCE, D3D11_BIND_UNORDERED_ACCESS).
  • CPUAccessFlags: Specifies the CPU read/write capabilities for the buffer (e.g., D3D11_CPU_ACCESS_WRITE, D3D11_CPU_ACCESS_READ).
  • MiscFlags: Miscellaneous flags (e.g., D3D11_RESOURCE_MISC_DRAWINDIRECT_ARGS).
  • StructureByteStride: The size, in bytes, of each element in the buffer if it's an array of structures. Required for UAVs and structured buffers.

D3D11_SUBRESOURCE_DATA Structure

This structure is used to provide initial data for a buffer when it's created:

typedef struct D3D11_SUBRESOURCE_DATA {
    const void *pSysMem;
    UINT       SysMemPitch;
    UINT       SysMemSlicePitch;
} D3D11_SUBRESOURCE_DATA;
                
  • pSysMem: A pointer to the initial data.

Creating a Vertex Buffer Example

The following C++ code snippet demonstrates how to create a simple vertex buffer:


// Assume pDevice is a valid ID3D11Device pointer

struct Vertex {
    float x, y, z;
    float r, g, b;
};

Vertex vertices[] = {
    { -0.5f, -0.5f, 0.0f, 1.0f, 0.0f, 0.0f }, // Red
    {  0.5f, -0.5f, 0.0f, 0.0f, 1.0f, 0.0f }, // Green
    {  0.0f,  0.5f, 0.0f, 0.0f, 0.0f, 1.0f }  // Blue
};

D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));

bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(Vertex) * ARRAYSIZE(vertices);
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA initialData;
ZeroMemory(&initialData, sizeof(initialData));
initialData.pSysMem = vertices;

ID3D11Buffer *pVertexBuffer = nullptr;
HRESULT hr = pDevice->CreateBuffer(&bd, &initialData, &pVertexBuffer);

if (FAILED(hr)) {
    // Handle error
}
                

Common Buffer Operations

Binding Buffers to the Pipeline

Use the ID3D11DeviceContext to bind buffers to specific pipeline stages. For vertex buffers, this involves:


// Assume pImmediateContext is a valid ID3D11DeviceContext pointer
// Assume pVertexBuffer is a valid ID3D11Buffer pointer created previously
// Assume vertexStride is the size of a single vertex structure (e.g., sizeof(Vertex))
// Assume vertexOffset is the starting offset in the buffer (usually 0)

UINT stride = sizeof(Vertex);
UINT offset = 0;
pImmediateContext->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset);
                

Similarly, use IASetIndexBuffer for index buffers and VSSetConstantBuffers, PSSetConstantBuffers, etc., for constant buffers.

Updating Buffers

For dynamic buffers (D3D11_USAGE_DYNAMIC), you can map and unmap the buffer to update its contents:


// Assume pDynamicBuffer is a valid ID3D11Buffer with D3D11_USAGE_DYNAMIC and D3D11_CPU_ACCESS_WRITE
D3D11_MAPPED_SUBRESOURCE mappedResource;
pImmediateContext->Map(pDynamicBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &mappedResource);

// Copy new data to mappedResource.pData
memcpy(mappedResource.pData, newData, dataSize);

pImmediateContext->Unmap(pDynamicBuffer, 0);
                
Note: When using D3D11_MAP_WRITE_DISCARD, the GPU cannot be reading from the buffer, and the entire buffer contents are discarded. For more granular updates or if the GPU might still be reading, consider other mapping options like D3D11_MAP_WRITE_NO_OVERWRITE or using staging buffers.

Advanced Buffer Features

  • Structured Buffers: Buffers that contain arrays of structures, allowing shaders to access elements by index and stride.
  • Append/Consume Buffers: Specialized UAV buffers that support atomic append and consume operations.
  • Indirect Drawing: Buffers that contain arguments for draw calls, enabling compute shaders to orchestrate drawing operations.
Tip: Understanding the Usage, BindFlags, and CPUAccessFlags is crucial for optimal buffer performance. Choose the flags that best match your application's data access patterns.