Buffer
A Buffer represents a block of memory that can be used in GPU operations. You can use buffers to store a wide variety of data, including position vectors, normal vectors, texture coordinates in a vertex buffer, and indexes in an index buffer, for example.
Creation
To create a buffer, first, you need to create the BufferDescription struct:
// Populate some data for the buffer...
Vector4[] vertexData = new Vector4[]
{
new Vector4(0.0f, 0.2f, 0.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
};
uint expectedSize = (4 * 4) * (uint)vertexData.Length;
BufferFlags expectedFlags = BufferFlags.VertexBuffer;
ResourceUsage expectedUsage = ResourceUsage.Default;
// Create the BufferDescription....
BufferDescription bufferDescription = new BufferDescription(expectedSize, expectedFlags, expectedUsage);
// Create the Buffer
Buffer buffer = this.GraphicsContext.Factory.CreateBuffer(vertexData, ref bufferDescription);
BufferDescription
Property | Type | Description |
---|---|---|
SizeInBytes | uint |
Retrieves or sets the size of the new buffer. |
Flags | BufferFlags |
Buffer flags describing the buffer type. |
CpuAccess | ResourceCpuAccess |
Specifies the types of CPU access allowed for this buffer. |
Usage | ResourceUsage |
Usage of this buffer. |
StructureByteStride | int |
The structure byte stride. |
ResourceUsage
Identifies expected resource usage during rendering.
ResourceUsage | Description |
---|---|
Default | A resource that requires read and write access by the GPU. Default value. |
Immutable | A resource that can only be read by the GPU. It cannot be written by the GPU and cannot be accessed at all by the CPU. |
Dynamic | A resource that is accessible by both the GPU (read-only) and the CPU (write-only). |
Staging | A resource that supports data transfer (copy) from the GPU to the CPU. |
BufferFlags
Identifies how to bind a buffer. This flag gives a hint to the graphics API about how this buffer will be used.
BufferFlags | Description |
---|---|
None | No option. |
VertexBuffer | Bind a buffer as a vertex buffer to the input-assembler stage. |
IndexBuffer | Bind a buffer as an index buffer to the input-assembler stage. |
ConstantBuffer | Bind a buffer as a constant buffer to a shader stage. This flag may NOT be combined with any other bind flag. |
ShaderResource | Bind a buffer or texture to a shader stage. |
AccelerationStructure | Bind a buffer to be used in a raytracing stage. |
RenderTarget | Bind a texture as a render target for the output-merger stage. |
UnorderedAccess | Bind a buffer as an unordered access resource. |
BufferStructured | Bind a buffer as a structured buffer resource. |
IndirectBuffer | Bind a buffer as an indirect buffer to the input-assembler stage. |
ResourceCpuAccess
Specifies the types of CPU access allowed for a resource.
ResourceCpuAccess | Description |
---|---|
None | Not specified. Default value. |
Write | The CPU can write to this resource. |
Read | The CPU can read from this resource. |
Using Buffers
How to update a Default Buffer (Buffer created with ResourceUsage.Default)
In this case, you just need to execute the GraphicsContext.UpdateBufferData(...)
method:
var vertexData = new Vector4[]
{
new Vector4(0.0f, 0.2f, 0.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
};
// Creates a Buffer without data...
uint sizeInBytes = (4 * 4) * (uint)vertexData.Length;
var bufferDescription = new BufferDescription(sizeInBytes, BufferFlags.VertexBuffer, ResourceUsage.Default);
var buffer = this.GraphicsContext.Factory.CreateBuffer(ref bufferDescription);
// Update buffer...
this.GraphicsContext.UpdateBufferData(buffer, vertexData);
How to copy a Default Buffer into another Default Buffer
In this case, you need to execute the CommandBuffer.CopyBufferDataTo(...)
method. To do this, you need to obtain a CommandBuffer
instance and enqueue the copy command:
var vertexData = new Vector4[]
{
new Vector4(0.0f, 0.2f, 0.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
};
// Creates the source buffer with some vertex data...
var description = new BufferDescription(
4 * 4 * (uint)vertexData.Length,
BufferFlags.VertexBuffer,
ResourceUsage.Default);
var buffer = this.GraphicsContext.Factory.CreateBuffer(vertexData, ref description);
// Creates an empty buffer with the same size and properties as before...
var bufferCopyDescription = new BufferDescription(
(4 * 4) * (uint)vertexData.Length,
BufferFlags.VertexBuffer,
ResourceUsage.Default);
var bufferCopy = this.GraphicsContext.Factory.CreateBuffer(ref bufferCopyDescription);
// Creates a CommandBuffer to execute the copy command...
var queue = this.GraphicsContext.Factory.CreateCommandQueue();
var command = queue.CommandBuffer();
command.Begin();
// Execute the CopyBufferDataTo() commandBuffer method to copy data from one buffer to another...
command.CopyBufferDataTo(buffer, bufferCopy, buffer.Description.SizeInBytes);
// Commit and submit the commandBuffer...
command.End();
command.Commit();
queue.Submit();
queue.WaitIdle();
buffer.Dispose();
bufferCopy.Dispose();
queue.Dispose();
How to read a Default Buffer content (by using a Staging Buffer)
In order to read a Default Buffer, you need to copy the content into a Staging Buffer first. Once you do this, you can map the Staging Buffer to CPU memory and access the data without problems:
var vertexData = new Vector4[]
{
new Vector4(0.0f, 0.2f, 0.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
};
// Create the source buffer with some data...
var description = new BufferDescription(
4 * 4 * (uint)vertexData.Length,
BufferFlags.VertexBuffer,
ResourceUsage.Default);
var buffer = this.GraphicsContext.Factory.CreateBuffer(vertexData, ref description);
// Creates the staging buffer...
var stagingDescription = new BufferDescription(
4 * 4 * (uint)vertexData.Length,
BufferFlags.None,
ResourceUsage.Staging, // Use Staging as ResourceUsage...
ResourceCpuAccess.Read);
var stagingBuffer = this.GraphicsContext.Factory.CreateBuffer(ref stagingDescription);
// Copy the buffer data like the previous example...
var queue = this.GraphicsContext.Factory.CreateCommandQueue();
var command = queue.CommandBuffer();
command.Begin();
command.CopyBufferDataTo(buffer, stagingBuffer, buffer.Description.SizeInBytes);
command.End();
command.Commit();
queue.Submit();
queue.WaitIdle();
// To read the buffer data, map the buffer into the CPU memory...
var readableResource = this.GraphicsContext.MapMemory(stagingBuffer, MapMode.Read);
// Check if the staging buffer content is the same as that we used before to create
// the default buffer...
for (int i = 0; i < vertexData.Length; i++)
{
Vector4* pointer = (Vector4*)(readableResource.Data + (i * sizeof(Vector4)));
Assert.Equal(*pointer, vertexData[i]);
}
// Unmap the memory to free the CPU memory resources...
this.GraphicsContext.UnmapMemory(stagingBuffer);
buffer.Dispose();
stagingBuffer.Dispose();
queue.Dispose();
How to update a Dynamic Buffer from CPU
A Dynamic Buffer can be updated directly from the CPU. To do this, you only need to map a Buffer and write the data directly to the mapped pointer:
var vectorSize = (uint)Unsafe.SizeOf<Vector4>();
var vertexData = new Vector4[]
{
new Vector4(0.0f, 0.2f, 0.0f, 1.0f), new Vector4(1.0f, 0.0f, 0.0f, 1.0f),
new Vector4(0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 1.0f, 0.0f, 1.0f),
new Vector4(-0.2f, -0.2f, 0.0f, 1.0f), new Vector4(0.0f, 0.0f, 1.0f, 1.0f),
};
var dynamicDescription = new BufferDescription(
vectorSize * (uint)vertexData.Length,
BufferFlags.VertexBuffer,
ResourceUsage.Dynamic,
ResourceCpuAccess.Write);
var dynamicBuffer = this.GraphicsContext.Factory.CreateBuffer(ref dynamicDescription);
// Map the write staging and leave mapped...
var writableResource = this.GraphicsContext.MapMemory(dynamicBuffer, MapMode.Write);
Vector4* pointer = (Vector4*)writableResource.Data;
for (int i = 0; i < vertexData.Length; i++)
{
*pointer = vertexData[i];
pointer++;
}
// Once the buffer is unmapped, the new buffer content is accessible by the GPU...
this.GraphicsContext.UnmapMemory(dynamicBuffer);
dynamicBuffer.Dispose();