Overview
VertexBuffer holds a set of buffers that define the geometry of a Renderable. The geometry is defined by vertex attributes such as position, color, normals, tangents, etc.
There is no need to have a 1-to-1 mapping between attributes and buffers. A buffer can hold data for several attributes (interleaved attributes). The buffers themselves are GPU resources, so mutating their data can be relatively slow.
Key Concepts
- Attributes: Vertex data like position, color, normals, UVs
- Buffers: GPU memory regions holding attribute data
- Interleaving: Storing multiple attributes in a single buffer
- Buffer Objects: Shared GPU data that can be swapped between VertexBuffers
It is possible, and even encouraged, to use a single vertex buffer for several Renderables.
Builder
The Builder class is used to construct VertexBuffer objects.
Constructor
Creates a new VertexBuffer::Builder instance.
Configuration Methods
bufferCount
Builder& bufferCount(uint8_t bufferCount) noexcept
Defines how many buffers will be created in this vertex buffer set.
Number of buffers in this vertex buffer set (maximum is 8, default is 0)
Returns: Reference to this Builder for chaining calls.
This call is mandatory. Buffers are later referenced by index from 0 to bufferCount - 1.
vertexCount
Builder& vertexCount(uint32_t vertexCount) noexcept
Sets the size of each buffer in the set in vertices.
Number of vertices in each buffer in this set
Returns: Reference to this Builder for chaining calls.
enableBufferObjects
Builder& enableBufferObjects(bool enabled = true) noexcept
Allows buffers to be swapped out and shared using BufferObject.
If true, enables buffer object mode (default is false)
Returns: Reference to this Builder for chaining calls.
If buffer objects mode is enabled, clients must call setBufferObjectAt() rather than setBufferAt(). This allows sharing of data between VertexBuffer objects.
Attribute Methods
attribute
Builder& attribute(
VertexAttribute attribute,
uint8_t bufferIndex,
AttributeType attributeType,
uint32_t byteOffset = 0,
uint8_t byteStride = 0
) noexcept
Sets up an attribute for this vertex buffer set.
The index of the buffer containing the data for this attribute (0 to bufferCount - 1)
The type of the attribute data (e.g., FLOAT2, FLOAT3, UBYTE4)
Offset in bytes into the buffer (default 0)
Stride in bytes to the next element of this attribute. When set to 0, the attribute size is used (default 0)
Returns: Reference to this Builder for chaining calls.
VertexAttribute enum values:
POSITION - Vertex position (required)
TANGENTS - Tangent space quaternion (also encodes normals)
COLOR - Vertex color
UV0 - Texture coordinates set 0
UV1 - Texture coordinates set 1
BONE_INDICES - Skinning bone indices (up to 4)
BONE_WEIGHTS - Skinning bone weights (up to 4)
CUSTOM0 through CUSTOM7 - Custom attributes
AttributeType (ElementType) enum values:
BYTE - Signed 8-bit integer
BYTE2 - 2-component signed 8-bit integer
BYTE3 - 3-component signed 8-bit integer
BYTE4 - 4-component signed 8-bit integer
UBYTE - Unsigned 8-bit integer
UBYTE2 - 2-component unsigned 8-bit integer
UBYTE3 - 3-component unsigned 8-bit integer
UBYTE4 - 4-component unsigned 8-bit integer
SHORT - Signed 16-bit integer
SHORT2 - 2-component signed 16-bit integer
SHORT3 - 3-component signed 16-bit integer
SHORT4 - 4-component signed 16-bit integer
USHORT - Unsigned 16-bit integer
USHORT2 - 2-component unsigned 16-bit integer
USHORT3 - 3-component unsigned 16-bit integer
USHORT4 - 4-component unsigned 16-bit integer
INT - Signed 32-bit integer
UINT - Unsigned 32-bit integer
FLOAT - 32-bit float
FLOAT2 - 2-component 32-bit float
FLOAT3 - 3-component 32-bit float
FLOAT4 - 4-component 32-bit float
HALF - 16-bit float
HALF2 - 2-component 16-bit float
HALF3 - 3-component 16-bit float
HALF4 - 4-component 16-bit float
TANGENTS must be specified as a quaternion. Not all backends support 3-component attributes that are not floats.
Example:
// Interleaved position and color in buffer 0
struct Vertex {
float position[2]; // 8 bytes
uint32_t color; // 4 bytes
}; // Total: 12 bytes
VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0,
VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.build(*engine);
normalized
Builder& normalized(
VertexAttribute attribute,
bool normalize = true
) noexcept
Sets whether a given attribute should be normalized.
Attribute to set the normalization flag for
true to automatically normalize the attribute (default true)
Returns: Reference to this Builder for chaining calls.
By default, attributes are not normalized. A normalized attribute is mapped between 0 and 1 in the shader. This applies only to integer types.
Advanced Features
advancedSkinning
Builder& advancedSkinning(bool enabled) noexcept
Sets advanced skinning mode.
If true, enables advanced skinning mode (default is false)
Returns: Reference to this Builder for chaining calls.
When enabled, bone data, indices and weights are set using RenderableManager::Builder::boneIndicesAndWeights(). Works with or without buffer objects.
Asynchronous Creation
async
Builder& async(
backend::CallbackHandler* handler,
AsyncCompletionCallback callback = nullptr,
void* user = nullptr
) noexcept
Specifies a callback that will execute once the resource’s data has been fully allocated within GPU memory.
Handler to dispatch the callback or nullptr for the default handler
Function to be called upon completion of asynchronous creation
Custom data passed as second argument to the callback
Returns: Reference to this Builder for chaining calls.
To use this method, the engine must be configured for asynchronous operation.
build
VertexBuffer* build(Engine& engine)
Creates the VertexBuffer object and returns a pointer to it.
Reference to the filament::Engine to associate this VertexBuffer with
Returns: Pointer to the newly created VertexBuffer object.
Instance Methods
getVertexCount
size_t getVertexCount() const noexcept
Returns the vertex count.
Returns: Number of vertices in this vertex buffer set.
Setting Buffer Data
setBufferAt
void setBufferAt(
Engine& engine,
uint8_t bufferIndex,
BufferDescriptor&& buffer,
uint32_t byteOffset = 0
)
Copy-initializes the specified buffer from the given buffer data.
Reference to the filament::Engine
Index of the buffer to initialize (0 to bufferCount - 1)
buffer
BufferDescriptor&&
required
BufferDescriptor pointing to raw, untyped data to be copied into the buffer
Offset in bytes into the buffer (must be multiple of 4, default 0)
Do not use this if you called enableBufferObjects() on the Builder.
Example:
struct Vertex {
float position[2];
uint32_t color;
};
Vertex vertices[] = {
{{1.0f, 0.0f}, 0xffff0000u},
{{0.0f, 1.0f}, 0xff00ff00u},
{{-1.0f, 0.0f}, 0xff0000ffu}
};
vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(
vertices,
sizeof(vertices),
nullptr // no callback needed
)
);
setBufferAtAsync
AsyncCallId setBufferAtAsync(
Engine& engine,
uint8_t bufferIndex,
BufferDescriptor&& buffer,
uint32_t byteOffset,
backend::CallbackHandler* handler,
AsyncCompletionCallback callback,
void* user = nullptr
)
Asynchronous version of setBufferAt().
Handler to dispatch the callback or nullptr for the default handler
callback
AsyncCompletionCallback
required
Function to be called upon completion
Custom data passed to the callback
Returns: AsyncCallId that can be used to cancel the operation.
To use this method, the engine must be configured for asynchronous operation. Do not use if enableBufferObjects() was called.
setBufferObjectAt
void setBufferObjectAt(
Engine& engine,
uint8_t bufferIndex,
BufferObject const* bufferObject
)
Swaps in the given buffer object.
Reference to the filament::Engine
Index of the buffer slot (0 to bufferCount - 1)
bufferObject
BufferObject const*
required
The handle to the GPU data for this buffer slot
To use this, you must first call enableBufferObjects() on the Builder.
Example:
// Create shared buffer object
BufferObject* bufferObject = BufferObject::Builder()
.size(sizeof(vertices))
.build(*engine);
bufferObject->setBuffer(*engine,
BufferObject::BufferDescriptor(
vertices, sizeof(vertices), nullptr
)
);
// Share between multiple vertex buffers
vb1->setBufferObjectAt(*engine, 0, bufferObject);
vb2->setBufferObjectAt(*engine, 0, bufferObject);
setBufferObjectAtAsync
AsyncCallId setBufferObjectAtAsync(
Engine& engine,
uint8_t bufferIndex,
BufferObject const* bufferObject,
backend::CallbackHandler* handler,
AsyncCompletionCallback callback,
void* user = nullptr
)
Asynchronous version of setBufferObjectAt().
Handler to dispatch the callback or nullptr for the default handler
callback
AsyncCompletionCallback
required
Function to be called upon completion
Custom data passed to the callback
Returns: AsyncCallId that can be used to cancel the operation.
Creation Status
isCreationComplete
bool isCreationComplete() const noexcept
Checks if the resource has finished creation.
Returns: true if the resource is fully created and ready for use.
For resources created asynchronously, this returns true only after all related asynchronous tasks are complete. For normally created resources, this always returns true.
Buffer Descriptor
using BufferDescriptor = backend::BufferDescriptor;
Describes a buffer and provides a callback for when the data is consumed.
Constructor:
BufferDescriptor(
void const* buffer,
size_t size,
Callback callback = nullptr,
void* user = nullptr
)
Pointer to the buffer data
Size of the buffer in bytes
Optional callback invoked when the data has been consumed
Optional user pointer passed to the callback
Example Usage
Simple Interleaved Vertex Buffer
#include <filament/Engine.h>
#include <filament/VertexBuffer.h>
struct Vertex {
float position[2];
uint32_t color;
};
Vertex vertices[] = {
{{1.0f, 0.0f}, 0xffff0000u}, // Red
{{0.0f, 1.0f}, 0xff00ff00u}, // Green
{{-1.0f, 0.0f}, 0xff0000ffu} // Blue
};
// Create vertex buffer
VertexBuffer* vb = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(1)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT2, 0, 12)
.attribute(VertexAttribute::COLOR, 0,
VertexBuffer::AttributeType::UBYTE4, 8, 12)
.normalized(VertexAttribute::COLOR)
.build(*engine);
// Upload data
vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(
vertices, sizeof(vertices), nullptr
)
);
// Cleanup
engine->destroy(vb);
Non-Interleaved Vertex Buffer
// Separate position and color buffers
float positions[] = {
1.0f, 0.0f, // vertex 0
0.0f, 1.0f, // vertex 1
-1.0f, 0.0f // vertex 2
};
uint32_t colors[] = {
0xffff0000u, // Red
0xff00ff00u, // Green
0xff0000ffu // Blue
};
// Create vertex buffer with 2 buffers
VertexBuffer* vb = VertexBuffer::Builder()
.vertexCount(3)
.bufferCount(2)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT2)
.attribute(VertexAttribute::COLOR, 1,
VertexBuffer::AttributeType::UBYTE4)
.normalized(VertexAttribute::COLOR)
.build(*engine);
// Upload position data to buffer 0
vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(
positions, sizeof(positions), nullptr
)
);
// Upload color data to buffer 1
vb->setBufferAt(*engine, 1,
VertexBuffer::BufferDescriptor(
colors, sizeof(colors), nullptr
)
);
Using Buffer Objects for Sharing
// Create vertex buffer with buffer objects enabled
VertexBuffer* vb1 = VertexBuffer::Builder()
.vertexCount(100)
.bufferCount(1)
.enableBufferObjects(true)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT3)
.build(*engine);
VertexBuffer* vb2 = VertexBuffer::Builder()
.vertexCount(100)
.bufferCount(1)
.enableBufferObjects(true)
.attribute(VertexAttribute::POSITION, 0,
VertexBuffer::AttributeType::FLOAT3)
.build(*engine);
// Create shared buffer object
BufferObject* sharedBuffer = BufferObject::Builder()
.size(100 * sizeof(float) * 3)
.build(*engine);
// Upload data once
sharedBuffer->setBuffer(*engine,
BufferObject::BufferDescriptor(
vertexData, dataSize, nullptr
)
);
// Share between vertex buffers
vb1->setBufferObjectAt(*engine, 0, sharedBuffer);
vb2->setBufferObjectAt(*engine, 0, sharedBuffer);
Dynamic Vertex Buffer with Callback
void onBufferConsumed(void* buffer, size_t size, void* user) {
// Buffer has been consumed by GPU, safe to free
delete[] static_cast<uint8_t*>(buffer);
}
// Allocate dynamic buffer
uint8_t* dynamicVertices = new uint8_t[vertexDataSize];
// ... fill dynamicVertices ...
vb->setBufferAt(*engine, 0,
VertexBuffer::BufferDescriptor(
dynamicVertices,
vertexDataSize,
onBufferConsumed, // Callback to free memory
nullptr // User data
)
);