gl46
Python OpenGL 4.6 Direct State Access (DSA) utility library focused on compute and data-oriented workflows, wrapping PyOpenGL through an explicit GL protocol.
Overview
gl46 provides type-annotated, DSA-first GPU objects for OpenGL 4.6 core. Every GL call goes through a GL protocol (gl46.gl.GL), with a real PyOpenGLGL backend for the driver and a FakeGL recording backend for headless tests, so the same script runs against both.
Key classes, grouped by concern:
- Buffers:
Buffer,EBO,IndirectDrawBuffer(aliased asIBO),ShaderStorage - Textures:
Texture2D,Texture2DArray,TextureCubeMapArray,TextureMultisample,TextureBuffer - Shaders and programs:
Shader,ShaderStage,Program,ProgramPipeline,Sampler - Geometry and framebuffer state:
VertexArray,VertexAttrib,Framebuffer,Renderbuffer,RenderbufferMultisample - Scene helpers:
Material,Camera,Window - Compute, queries, and debug:
Barrier(aliased asMemoryBarrier),Query,TransformFeedback,DebugOutput
Not included. Windowing and input are not part of the core API — thin optional backends (gl46.window_imgui, gl46.window_qt) provide windows, render loops, and input. There is no scene graph, no math types (use numpy / pyglm), and no bindless-texture or multi-draw-indirect layer yet.
gl46 is not a ModernGL replacement: it is a minimal, DSA-first, fully testable substrate for developers assembling their own rendering framework.
Requirements
- Python 3.11+
- uv as the package manager
glfw>=2.10.2PyOpenGL(pluspyopengl-accelerate) for the real GL backend- An OpenGL 4.5+ driver; Window requests a 4.6 core context by default.
The core package has no windowing, GUI, or image-library dependency. imgui_bundle and PySide6 are optional extras, needed only by examples/imgui_scene.py and examples/qt_scene.py (uv sync --extra imgui or --extra qt; equivalent pip form: pip install "gl46[imgui]" or pip install "gl46[qt]"). Pillow is a separate optional extra for examples/instancing_mrt.py: pip install "gl46[examples]".
The committed uv.lock is resolved against a China mirror (Huawei Cloud); international contributors may want to override the index locally. This has no effect on users installing gl46 from PyPI.
Install
From PyPI (any project):
pip install gl46
Optional extras:
pip install "gl46[imgui]" # ImGui backend (gl46.window_imgui)
pip install "gl46[qt]" # PySide6 backend (gl46.window_qt)
pip install "gl46[examples]" # Pillow, for examples/instancing_mrt.py
If you use uv:
uv add gl46
uv add "gl46[imgui]" # etc.
Development install inside this repository:
uv sync --all-extras
Quick Start
Every gl46 call needs a current OpenGL 4.5+ context. The built-in Window creates one (pass visible=False for an offscreen context); you can also point gl46 at any other context you already own.
import numpy as np
from gl46 import Buffer, Window
from gl46.constants import GL_MAP_READ_BIT, GL_MAP_WRITE_BIT
with Window(320, 240, visible=False): # creates a GL context
buf = Buffer(
16 * 4,
flags=GL_MAP_READ_BIT | GL_MAP_WRITE_BIT,
initial_data=np.arange(16, dtype=np.float32),
)
with buf.map_range(0, buf.size_bytes, GL_MAP_READ_BIT) as mapped:
out = mapped.as_numpy(np.float32, (16,))
print(out) # [ 0. 1. 2. ... 15.]
For a full rendering example with a visible window, see examples/triangle.py.
Examples
Run any example with uv run python examples/<name>.py from the repo root. Each opens a visible window; press SPACE to capture the current frame as <name>_frame.png on the spot, and closing the window writes <name>_close.png.
uv run python examples/quickstart.py # Quick Start Demo
uv run python examples/query_timing.py # DSA timer-query GPU timing
uv run python examples/triangle.py # VAO/VBO + a rotating triangle
uv run python examples/offscreen_render.py # offscreen FBO + fullscreen present
uv run python examples/compute_sobel.py # compute-shader Sobel + timing
uv run python examples/instancing_mrt.py # instancing & MRT scene
uv run python examples/imgui_scene.py # gl46 + ImGui overlay (imgui_bundle/immapp)
uv run python examples/qt_scene.py # gl46 hosted in a QOpenGLWidget (PySide6)
Two examples need extra dependencies beyond the core package:
imgui_scene.pyneedsimgui-bundle>=1.92.900. ItsSMOKEcallback raisesRuntimeErrorif theRunnerParams.app_shall_exithook added in that version is missing.instancing_mrt.pyusesPillowto load its assets (theexamplesextra); its mesh and texture files live underexamples/data/, shaders underexamples/shaders/.
imgui_scene.py captures imgui_scene.png (scene + overlay) on its second frame, since the ImGui overlay renders after the first frame; qt_scene.py captures qt_scene.png via grabFramebuffer() on first draw.
PySide6 is optional: uv sync --extra qt installs it (needed only for examples/qt_scene.py / gl46.window_qt).
Building your own framework
gl46 leaves the application shell to you, but ships two thin window backends.
GLFW + ImGui (imgui_bundle)
The loop belongs to immapp.run; you supply callbacks. The scene draws in custom_background (behind the ImGui layer), GUI widgets in show_gui, one-time setup in post_init. Input for your scene is read through the ImGui API (imgui.is_key_pressed / imgui.get_io()).
from gl46.window_imgui import run_imgui_app
def scene_render() -> None: ... # draw with gl46 objects
def gui() -> None: ... # imgui widgets
run_imgui_app(scene_render=scene_render, gui=gui,
width=800, height=600, title="app")
PySide6 (Qt)
Subclass GL46Widget, override the three hooks, then host it with run_qt_app. The loop is Qt's event loop; repaint via a QTimer (default).
from gl46.window_qt import GL46Widget, run_qt_app
class AppWidget(GL46Widget):
def initialize_gl(self) -> None: ... # create gl46 objects
def resize_gl(self, w: int, h: int) -> None: ... # framebuffer pixels
def paint_gl(self) -> None: ... # draw the frame
run_qt_app(AppWidget, title="app")
Testing
uv run pytest -q
The suite runs against the FakeGL backend (no GPU required).
Quality Gates
uv sync --all-extras
uv run ruff check src tests
uv run ruff format --check src tests
uv run mypy src
Project Structure
gl46/
|-- src/
| `-- gl46/
| |-- __init__.py
| |-- py.typed
| |-- barrier.py
| |-- base.py
| |-- buffer.py
| |-- camera.py
| |-- constants.py
| |-- debug.py
| |-- errors.py
| |-- framebuffer.py
| |-- gl.py
| |-- material.py
| |-- pipeline.py
| |-- program.py
| |-- query.py
| |-- renderbuffer.py
| |-- sampler.py
| |-- shader.py
| |-- texture.py
| |-- transformfeedback.py
| |-- version.py
| |-- vertexarray.py
| |-- window.py
| |-- window_imgui.py
| `-- window_qt.py
|-- tests/
| |-- __init__.py
| |-- conftest.py
| |-- fake_gl.py
| |-- test_barrier.py
| |-- test_base.py
| |-- test_buffer.py
| |-- test_buffer_extra.py
| |-- test_camera.py
| |-- test_constants.py
| |-- test_debug.py
| |-- test_errors.py
| |-- test_fake_gl.py
| |-- test_framebuffer.py
| |-- test_gl.py
| |-- test_material.py
| |-- test_pipeline.py
| |-- test_program.py
| |-- test_query.py
| |-- test_renderbuffer.py
| |-- test_sampler.py
| |-- test_shader.py
| |-- test_texture.py
| |-- test_texture_extra.py
| |-- test_transformfeedback.py
| |-- test_utils.py
| |-- test_vertexarray.py
| |-- test_version.py
| |-- test_window.py
| |-- test_window_imgui.py
| `-- test_window_qt.py
|-- examples/
| |-- data/
| |-- shaders/
| |-- compute_sobel.py
| |-- imgui_scene.py
| |-- instancing_mrt.py
| |-- offscreen_render.py
| |-- qt_scene.py
| |-- query_timing.py
| |-- quickstart.py
| `-- triangle.py
|-- pyproject.toml
|-- uv.lock
|-- README.md
`-- LICENSE
License
MIT
Metadata
Release files for gl46 0.1.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| gl46-0.1.0.tar.gz | 3.7 MB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| gl46-0.1.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 3.7 MB
Release files / gl46-0.1.0.tar.gz
| Download URL | gl46-0.1.0.tar.gz |
|---|---|
| Size | 3.7 MB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
c5e5cb9eacd10ba1e01f21f2451ab039b252e704a9a2646285e446c18942f8dd
|
|
BLAKE2b-256 checksum How to use checksums |
7ffdaa2fd0abbd8a5678b0ae6326fce5dd796e5fb25888722fa47195ca28afe9
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|
Release files / gl46-0.1.0-py3-none-any.whl
| Download URL | gl46-0.1.0-py3-none-any.whl |
|---|---|
| Size | 48.8 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
e8fafee62d70ffca01ee83b7d8a4ec7fc2cf9d26c27693e913fed2a21b520615
|
|
BLAKE2b-256 checksum How to use checksums |
e76b61c9e70730b62558192dbcd98419e01796b47a3160c0d6c3d3b0dd57e35e
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
No |
| Uploaded via |
uv/0.11.13 {"installer":{"name":"uv","version":"0.11.13","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
|