Buffers

In DirectX graphics, buffers are fundamental data structures used to store and transfer information between the CPU and the GPU. They are essential for rendering graphics efficiently, holding data such as vertex coordinates, texture data, constant values for shaders, and more.

Types of Buffers

DirectX defines several types of buffers, each optimized for specific data storage and access patterns:

Vertex Buffers

Vertex buffers store vertex data, which typically includes attributes like position, color, texture coordinates, and normals. The GPU reads vertex data from vertex buffers to construct geometric primitives.


// Example: Structure for vertex data
struct Vertex
{
    float x, y, z; // Position
    float r, g, b; // Color
};

// Creating a vertex buffer
ID3D11Buffer* pVertexBuffer = nullptr;
D3D11_BUFFER_DESC bd;
ZeroMemory(&bd, sizeof(bd));
bd.Usage = D3D11_USAGE_DEFAULT;
bd.ByteWidth = sizeof(Vertex) * NUM_VERTICES;
bd.BindFlags = D3D11_BIND_VERTEX_BUFFER;
bd.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA InitData;
InitData.pSysMem = vertices; // Array of Vertex structures

hr = pd3dDevice->CreateBuffer(&bd, &InitData, &pVertexBuffer);
if (FAILED(hr)) return hr;

// Binding the vertex buffer to the input assembler stage
pd3dImmediateContext->IASetVertexBuffers(0, 1, &pVertexBuffer, &stride, &offset);
                

Index Buffers

Index buffers store indices into vertex data. Instead of duplicating vertex data for each triangle, you can use indices to reference vertices from a vertex buffer, reducing memory usage and improving performance.


// Example: Creating an index buffer
ID3D11Buffer* pIndexBuffer = nullptr;
D3D11_BUFFER_DESC ibd;
ZeroMemory(&ibd, sizeof(ibd));
ibd.Usage = D3D11_USAGE_DEFAULT;
ibd.ByteWidth = sizeof(WORD) * NUM_INDICES; // Usually WORD (unsigned short)
ibd.BindFlags = D3D11_BIND_INDEX_BUFFER;
ibd.CPUAccessFlags = 0;

D3D11_SUBRESOURCE_DATA iInitData;
iInitData.pSysMem = indices; // Array of indices

hr = pd3dDevice->CreateBuffer(&ibd, &iInitData, &pIndexBuffer);
if (FAILED(hr)) return hr;

// Binding the index buffer
pd3dImmediateContext->IASetIndexBuffer(pIndexBuffer, DXGI_FORMAT_R16_UINT, 0);
                

Constant Buffers

Constant buffers hold data that is passed to shaders, such as transformation matrices (world, view, projection), lighting parameters, or material properties. Shaders can access these values uniformly across many vertices or pixels.


// Example: Structure for constant buffer data
struct ConstantBufferData
{
    DirectX::XMMATRIX worldViewProjection;
};

// Creating a constant buffer
ID3D11Buffer* pConstantBuffer = nullptr;
D3D11_BUFFER_DESC cbd;
ZeroMemory(&cbd, sizeof(cbd));
cbd.Usage = D3D11_USAGE_DYNAMIC;
cbd.ByteWidth = sizeof(ConstantBufferData);
cbd.BindFlags = D3D11_BIND_CONSTANT_BUFFER;
cbd.CPUAccessFlags = D3D11_CPU_ACCESS_WRITE;

hr = pd3dDevice->CreateBuffer(&cbd, nullptr, &pConstantBuffer);
if (FAILED(hr)) return hr;

// Updating the constant buffer
ConstantBufferData cbData;
cbData.worldViewProjection = DirectX::XMMatrixTranspose(world * view * projection);

D3D11_MAPPED_SUBRESOURCE ms;
pd3dImmediateContext->Map(pConstantBuffer, 0, D3D11_MAP_WRITE_DISCARD, 0, &ms);
memcpy(ms.pData, &cbData, sizeof(ConstantBufferData));
pd3dImmediateContext->Unmap(pConstantBuffer, 0);

// Binding the constant buffer to a shader stage (e.g., Vertex Shader)
pd3dImmediateContext->VSSetConstantBuffers(0, 1, &pConstantBuffer);
                

Shader Resource Views (SRVs) for Textures and Other Resources

While not strictly "buffers" in the same sense as vertex or index buffers, textures and other data structures are often accessed by shaders through Shader Resource Views. These views provide a way for shaders to read data from various resource types.

Unordered Access Views (UAVs)

UAVs allow shaders to both read from and write to buffers, enabling advanced techniques like compute shaders, data processing, and read-modify-write operations.

Buffer Creation and Usage

Creating a buffer typically involves these steps:

  1. Define the buffer's properties using a `D3D11_BUFFER_DESC` structure, specifying usage, size, binding flags, and CPU access.
  2. (Optional) Provide initial data using a `D3D11_SUBRESOURCE_DATA` structure.
  3. Call the appropriate `CreateBuffer` method on the Direct3D device object.
  4. Bind the buffer to a specific stage of the graphics pipeline using methods like `IASetVertexBuffers`, `IASetIndexBuffer`, or `VSSetConstantBuffers`.
  5. For dynamic buffers (e.g., constant buffers updated per frame), map the buffer to CPU-accessible memory, write data, and then unmap it.
Note: The specific buffer types and their implementations may vary slightly between DirectX versions (e.g., Direct3D 9, 11, 12). This documentation primarily focuses on Direct3D 11 concepts.

Further Reading