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: VertexBase

OpenGL Vertex Array Objects (VAO) class

__init__(handle: int | None = None)[source]

VertexArrayObject

Parameters:

handle – int Handle (ID) of the OpenGL Vertex Array Object (VAO).

bind()[source]

Bind the VAO for use in rendering.

unbind()[source]

Unbind the VAO by binding to zero.

delete()[source]

Delete the VAO from GPU memory.

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.

get_vbo_object(name: str) LegacyVBO[source]

Retrieve a VBO by its semantic or shorthand name.

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)

delete_buffers()[source]
Returns:

None

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.

add_ebo(data: ndarray) ModernEBO[source]
Parameters:

data – np.ndarray

Returns:

int

set_ebo(ebo: int) int[source]

add_ebo

Parameters:

ebo – int

Returns:

int

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: VertexBuffer

OpenGL 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]
set_element_attributes(data: ndarray, size: int, dtype: int = OpenGL.raw.GL.VERSION.GL_1_5.GL_STATIC_DRAW)[source]
Parameters:
  • data – np.ndarray

  • size – int

  • dtype – int

Returns:

None

configure()[source]
Returns:

None

Legacy Buffers

LegacyVBO

LegacyPositionVBO

LegacyColorVBO

LegacyNormalVBO

LegacyEBO

Base Classes

VertexBase

class picogl.buffers.base.VertexBase(handle: int | None = None)[source]

Bases: AbstractVertexGroup

Generic OpenGL object interface with binding lifecycle.

Provides handle + context manager, leaves binding to subclasses.

__init__(handle: int | None = None)[source]
bind()[source]

Bind the underlying VAO/state for rendering.

unbind()[source]

Optionally unbind the VAO/state.

delete()[source]

Release resources (VAO or equivalent).

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

name: str[source]
index: int[source]
size: int[source]
type: int[source]
normalized: bool[source]
stride: int[source]
offset: int[source]
__init__(name: str, index: int, size: int, type: int, normalized: bool, stride: int, offset: int) None

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

  1. Use appropriate buffer type for your target platform

  2. Upload data once and reuse buffers

  3. Use VAOs for modern OpenGL when possible

  4. Handle errors gracefully with fallbacks

  5. 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