Buffers API
The buffers module provides OpenGL buffer management functionality for PicoGL, including vertex buffers, element buffers, and vertex array objects.
Core Classes
VertexArrayObject
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
- class picogl.backend.legacy.core.vertex.buffer.vertex.LegacyVBO(handle: int | None = None, data: ndarray | None = None, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ARRAY_BUFFER, configure: bool = True, size: int = 3, stride: int = 0, dtype: int = OpenGL.raw.GL._types.GL_FLOAT, pointer: c_void_p = c_void_p(None))[source]
Bases:
VertexBufferLegacy OpenGL Vertex Buffer Object (VBO) or Element Buffer Object (EBO).
- __init__(handle: int | None = None, data: ndarray | None = None, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ARRAY_BUFFER, configure: bool = True, size: int = 3, stride: int = 0, dtype: int = OpenGL.raw.GL._types.GL_FLOAT, pointer: c_void_p = c_void_p(None))[source]
- set_data(data: ndarray, usage: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_STATIC_DRAW) None[source]
Upload data to the GPU.
- Parameters:
data – NumPy array containing the data to upload.
usage – OpenGL usage hint (e.g., GL_STATIC_DRAW).
- Raises:
ValueError – If the data is not a NumPy array.
- allocate(data: ndarray, usage: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_STATIC_DRAW)[source]
Initial allocation (glBufferData)
- update_data(data: ndarray, offset: int = 0)[source]
Upload buffer contents; prefer SubData, fall back to full Data if needed.
Non-contiguous NumPy arrays can make
glBufferSubDatafail withGL_INVALID_VALUE(1281) under PyOpenGL; normalize layout first. If SubData still fails (e.g. stalebuffer_sizevs GPU store), reallocate.
LegacyPositionVBO
- class picogl.backend.legacy.core.vertex.buffer.position.LegacyPositionVBO(handle: int | None = None, data: ndarray | None = None, size: int = 3, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ARRAY_BUFFER, dtype: int = OpenGL.raw.GL._types.GL_FLOAT)[source]
Bases:
LegacyVBOOpenGL buffer class specialized for storing and managing position data, commonly used for rendering ribbons_legacy-like meshdata.
Inherits from LegacyVBO and adds behavior specific to position data, such as setting up the vertex pointer and handling data uploads.
LegacyColorVBO
- class picogl.backend.legacy.core.vertex.buffer.color.LegacyColorVBO(handle: int | None = None, data: ndarray | None = None, size: int = 3, dtype: int = OpenGL.raw.GL._types.GL_FLOAT)[source]
Bases:
LegacyVBOSpecialized VBO class for colour attributes.
- __init__(handle: int | None = None, data: ndarray | None = None, size: int = 3, dtype: int = OpenGL.raw.GL._types.GL_FLOAT)[source]
Initialize a colour VBO.
- Parameters:
handle – Existing OpenGL buffer handle (optional).
data – Numpy array with colour data (optional).
size – Number of components per colour (3=RGB, 4=RGBA).
LegacyNormalVBO
- class picogl.backend.legacy.core.vertex.buffer.normal.LegacyNormalVBO(handle: int | None = None, data: ndarray | None = None, size: int = 3, dtype: int = OpenGL.raw.GL._types.GL_FLOAT)[source]
Bases:
LegacyVBOSpecialized Class for Position Buffers
LegacyEBO
- class picogl.backend.legacy.core.vertex.buffer.element.LegacyEBO(handle: int | None = None, data: ndarray | None = None, target: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_ELEMENT_ARRAY_BUFFER, size: int = 3, dtype: int = OpenGL.raw.GL._types.GL_FLOAT)[source]
Bases:
LegacyVBOLegacy Element Buffer Object (EBO)
Base Classes
VertexBase
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
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
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