Buffers API
The buffers module provides OpenGL buffer management functionality for PicoGL, including vertex buffers, element buffers, and vertex array objects.
Core Classes
VertexArrayObject
- class picogl.backend.modern.core.vertex.array.object.VertexArrayObject(handle: int | None = None)[source]
Bases:
VertexBaseOpenGL Vertex Array Objects (VAO) class
- __init__(handle: int | None = None)[source]
VertexArrayObject
- Parameters:
handle – int Handle (ID) of the OpenGL Vertex Array Object (VAO).
- set_layout(layout: LayoutDescriptor) None[source]
- Parameters:
layout – LayoutDescriptor: The layout descriptor to define the vertex attribute format.
- Raises:
None
Sets the layout for the rendering setup by binding the buffers and configuring the attributes. The state is stored in the Vertex Array Object (VAO). This method assumes a single Vertex Buffer Object (VBO) holds all position data but can be adapted as required. Handles optional usage of Normal Buffer Object (NBO) and Element Buffer Object (EBO) if present.
- add_vbo_object(name: str, vbo: LegacyVBO) LegacyVBO[source]
Register a VBO by semantic name or shorthand alias.
- add_vbo(index: int, data: ndarray, size: int, dtype: int = OpenGL.raw.GL._types.GL_FLOAT, name: str | None = None, handle: int | None = None) ModernVBO[source]
Add a Vertex Buffer Object (VBO) to the VAO and set its attributes.
- Parameters:
handle
index – VAO attribute index
data – Vertex data
size – Size per vertex (e.g., 3 for vec3)
dtype – OpenGL data type (e.g., GL_FLOAT)
name – Optional semantic name (e.g., “position”, “colour”)
- Returns:
OpenGL buffer handle (GLuint)
- add_attribute(index: int, vbo: int, size: int = 3, dtype: int = OpenGL.raw.GL._types.GL_FLOAT, normalized: bool = False, stride: int = 0, offset: int = 0)[source]
- Parameters:
index – int Index of the vertex attribute.
vbo – int Vertex Buffer Object (VBO) associated with this attribute.
size – int Size of the vertex attribute (e.g., 3 for a 3D vector).
dtype – int Data type of the vertex attribute (default is GL_FLOAT).
normalized – bool Whether the data is normalized (default is False).
stride – int Byte offset between consecutive vertex attributes (default is 0).
offset – int Byte offset to the first component of the
vertex attribute (default is 0). Add a vertex attribute to the VAO.
- property index_count: str | int | None[source]
Return the number of indices in the EBO.
- Returns:
int
- draw(index_count: int | None = None, dtype: int = OpenGL.raw.GL._types.GL_UNSIGNED_INT, mode: int = OpenGL.raw.GL.VERSION.GL_1_0.GL_POINTS, pointer: int = c_void_p(None))[source]
- Parameters:
pointer – ctypes.c_void_p(0)
dtype – GL_UNSIGNED_INT
index_count – int Number of vertices to draw.
mode – int e.g. GL_POINT
- Returns:
None
The VertexArrayObject class manages OpenGL Vertex Array Objects (VAOs) for modern OpenGL rendering.
Example:
from picogl.backend.modern.core.vertex.array.object import VertexArrayObject
# Create VAO
vao = VertexArrayObject()
# Add vertex buffer
vao.add_vbo(index=0, data=vertices, size=3)
# Add color buffer
vao.add_vbo(index=1, data=colors, size=3)
# Add element buffer
vao.add_ebo(data=indices)
# Draw
vao.draw(mode=GL_TRIANGLES, index_count=len(indices))
VertexBufferGroup
The VertexBufferGroup class provides legacy OpenGL buffer management for systems without VAO support.
Example:
from picogl.buffers.vertex.legacy import VertexBufferGroup
# Create vertex buffer group
vbg = VertexBufferGroup()
# Add vertex buffer
vbg.add_vbo("position", vertices, 3)
# Add color buffer
vbg.add_vbo("color", colors, 3)
# Add element buffer
vbg.add_ebo(indices)
# Bind and draw
vbg.bind()
vbg.draw(mode=GL_TRIANGLES)
Buffer Classes
Modern Buffers
ModernVBO
ModernEBO
- class picogl.backend.modern.core.vertex.buffer.element.ModernEBO(handle: int | None = None, data: ndarray | None = None, size: int = 3, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ELEMENT_ARRAY_BUFFER)[source]
Bases:
VertexBufferOpenGL element data buffer (also known as an index buffer)
- __init__(handle: int | None = None, data: ndarray | None = None, size: int = 3, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ELEMENT_ARRAY_BUFFER)[source]
Legacy Buffers
LegacyVBO
LegacyPositionVBO
LegacyColorVBO
LegacyNormalVBO
LegacyEBO
Base Classes
VertexBase
- class picogl.buffers.base.VertexBase(handle: int | None = None)[source]
Bases:
AbstractVertexGroupGeneric OpenGL object interface with binding lifecycle.
Provides handle + context manager, leaves binding to subclasses.
- attach_buffers(nbo=None, cbo=None, vbo=None, ebo=None) None[source]
Attach the buffers that the VAO/group should coordinate.
- set_layout(layout: LayoutDescriptor) None[source]
Define the attribute layout for this VAO/group.
The VertexBase class provides the base functionality for all vertex buffer classes.
VertexBuffer
The VertexBuffer class provides the base functionality for vertex buffer objects.
Data Structures
LayoutDescriptor
- class picogl.buffers.factory.layout.LayoutDescriptor(attributes: List[picogl.buffers.attributes.AttributeSpec])[source]
Bases:
object- attributes: List[AttributeSpec]
- __init__(attributes: List[AttributeSpec]) None
The LayoutDescriptor class describes the layout of vertex attributes in a buffer.
Example:
from picogl.buffers.factory.layout import create_layout
# Create layout descriptor
layout = create_layout([
AttributeSpec(name="position", size=3, type=GL_FLOAT),
AttributeSpec(name="color", size=3, type=GL_FLOAT),
AttributeSpec(name="normal", size=3, type=GL_FLOAT)
])
AttributeSpec
- class picogl.buffers.attributes.AttributeSpec(name: str, index: int, size: int, type: int, normalized: bool, stride: int, offset: int)[source]
Bases:
object
The AttributeSpec class describes a single vertex attribute.
Example:
from picogl.buffers.attributes import AttributeSpec
# Create attribute specification
position_attr = AttributeSpec(
name="position",
size=3,
type=GL_FLOAT,
normalized=False,
stride=0,
offset=0
)
Buffer Management
Creating Buffers
Modern OpenGL (VAO/VBO):
from picogl.backend.modern.core.vertex.array.object import VertexArrayObject
# Create VAO
vao = VertexArrayObject()
# Add vertex buffer
vao.add_vbo(index=0, data=vertices, size=3)
# Add color buffer
vao.add_vbo(index=1, data=colors, size=3)
# Add element buffer
vao.add_ebo(data=indices)
Legacy OpenGL (VBO only):
from picogl.buffers.vertex.legacy import VertexBufferGroup
# Create vertex buffer group
vbg = VertexBufferGroup()
# Add vertex buffer
vbg.add_vbo(name="position", data=vertices, size=3)
# Add color buffer
vbg.add_vbo(name="color", data=colors, size=3)
# Add element buffer
vbg.add_ebo(data=indices)
Binding Buffers
Modern OpenGL:
# Bind VAO (automatically binds all VBOs)
with vao:
# Draw calls here
vao.draw(mode=GL_TRIANGLES, index_count=len(indices))
Legacy OpenGL:
# Bind vertex buffer group
vbg.bind()
# Draw calls here
vbg.draw(mode=GL_TRIANGLES)
# Unbind
vbg.unbind()
Buffer Types
Vertex Buffers
Vertex buffers store vertex data such as positions, colors, normals, and texture coordinates.
Position Buffer:
# 3D positions (N, 3) array
vertices = np.array([
[0, 0, 0], # Vertex 0
[1, 0, 0], # Vertex 1
[0, 1, 0] # Vertex 2
], dtype=np.float32)
Color Buffer:
# RGB colors (N, 3) array
colors = np.array([
[1, 0, 0], # Red
[0, 1, 0], # Green
[0, 0, 1] # Blue
], dtype=np.float32)
Normal Buffer:
# Surface normals (N, 3) array
normals = np.array([
[0, 0, 1], # Normal 0
[0, 0, 1], # Normal 1
[0, 0, 1] # Normal 2
], dtype=np.float32)
UV Buffer:
# Texture coordinates (N, 2) array
uvs = np.array([
[0, 0], # UV 0
[1, 0], # UV 1
[0, 1] # UV 2
], dtype=np.float32)
Element Buffers
Element buffers store indices that define the topology of the mesh.
Element Buffer:
# Triangle indices (M, 3) array
indices = np.array([
[0, 1, 2] # Triangle connecting vertices 0, 1, 2
], dtype=np.uint32)
Buffer Operations
Uploading Data
Modern OpenGL:
# Upload vertex data
vao.add_vbo(index=0, data=vertices, size=3)
# Upload color data
vao.add_vbo(index=1, data=colors, size=3)
# Upload element data
vao.add_ebo(data=indices)
Legacy OpenGL:
# Upload vertex data
vbg.add_vbo(index=0, vbo_name="position", data=vertices, size=3)
# Upload color data
vbg.add_vbo(index=1, vbo_name="color", data=colors, size=3)
# Upload element data
vbg.add_ebo(data=indices)
Drawing
Modern OpenGL:
# Draw with VAO
with vao:
vao.draw(mode=GL_TRIANGLES, index_count=len(indices))
Legacy OpenGL:
# Draw with vertex buffer group
vbg.bind()
vbg.draw(mode=GL_TRIANGLES)
vbg.unbind()
Cleanup
Modern OpenGL:
# Cleanup VAO
vao.delete()
Legacy OpenGL:
# Cleanup vertex buffer group
vbg.delete()
Error Handling
Buffer operations include comprehensive error handling:
OpenGL Context Errors: Check for valid OpenGL context Buffer Creation Errors: Handle OpenGL buffer creation failures Data Upload Errors: Validate data before uploading Drawing Errors: Handle OpenGL drawing failures
Example:
try:
vao.add_vbo(index=0, data=vertices, size=3)
except OpenGLError as e:
print(f"OpenGL error: {e}")
# Handle gracefully
except ValueError as e:
print(f"Data error: {e}")
# Validate data
except Exception as e:
print(f"Unexpected error: {e}")
# Handle gracefully
Performance Considerations
Modern Buffers (VAO/VBO): * Best performance with modern OpenGL * Efficient GPU memory usage * Fast drawing operations * Requires OpenGL 3.0+ support
Legacy Buffers (VBO only): * Compatible with older OpenGL versions * Good performance on older hardware * Uses client state management * Limited feature set
Immediate Mode: * Maximum compatibility * Uses glBegin/glEnd for rendering * Lower performance but works everywhere * No buffer management
Best Practices
Use appropriate buffer type for your target platform
Upload data once and reuse buffers
Use VAOs for modern OpenGL when possible
Handle errors gracefully with fallbacks
Clean up resources when done
Example:
# Choose buffer type based on OpenGL support
if has_modern_opengl():
vao = VertexArrayObject()
vao.add_vbo(index=0, data=vertices, size=3)
elif has_legacy_opengl():
vbg = VertexBufferGroup()
vbg.add_vbo("position", vertices, 3)
else:
# Use immediate mode fallback
pass