py_gpu is a small rendering-core package for Python scene libraries that need
to grow from simple CPU rendering toward modern GPU-style throughput.
The immediate companion project is py_3d, but this repository should stay
cleanly reusable: it owns backend detection, batching contracts, frame buffers,
and accelerated renderer experiments. Scene construction, physics, and high
level demos can live elsewhere.
Game engines render huge scenes quickly because they avoid per-object Python
work in the hot path. They batch geometry, keep backend contracts stable, upload
large buffers once, reuse device resources, and let the GPU do deeply parallel
work. py_gpu is the place to build those ideas without making py_3d messy.
The target shape is:
Python scene package -> adapter -> render batch -> backend -> frame/output
Current status:
- Dependency-free
FrameBufferandDepthBuffer. - Screen-space
RasterBatch,ScreenTriangle, andScreenVertexcontracts. - A small CPU raster backend for correctness and fallback use.
- A real offscreen ModernGL backend for projected triangle batches when OpenGL is available.
- An optional NumPy raster backend selected by
select_backend("auto")when ModernGL is unavailable and NumPy is installed. - Optional backend detection for WebGPU, OpenGL, Vulkan, CuPy, Numba, and PyTorch packages.
- Optional
py_3dadapter that can return apy_3d.PixelBufferwith reference visual parity, while still exposing an experimental flat batch backend path.
This is not yet a full GPU renderer. The first goal is a clean, measurable contract that real GPU backends can implement without changing user scenes.
View the GPU lit textured shapes MP4
From PyPI after release:
python -m pip install py_gpuEditable development install:
python -m pip install -e ".[dev]"
python -m pytestOptional backend experiments:
python -m pip install -e ".[opengl]"
python -m pip install -e ".[webgpu]"
python -m pip install -e ".[numeric]"Render projected triangles into a dependency-free frame buffer:
from py_gpu import RasterBatch, ScreenTriangle, ScreenVertex, select_backend
batch = RasterBatch([
ScreenTriangle(
ScreenVertex(80, 20, 1.0),
ScreenVertex(20, 120, 1.0),
ScreenVertex(140, 120, 1.0),
(220, 80, 40),
)
])
backend = select_backend("cpu")
frame = backend.render(batch, width=160, height=140, background=(8, 10, 14))
frame.to_png("triangle.png")Use the fastest available backend:
backend = select_backend("auto")Force the offscreen OpenGL backend:
backend = select_backend("moderngl")Use the first py_3d bridge:
from py_3d import RenderEngine
from py_gpu.adapters.py3d import Py3DRasterRenderer
engine = RenderEngine(Py3DRasterRenderer())
buffer = engine.render(scene, camera, settings)
buffer.to_png("py3d-via-pygpu.png")The adapter defaults to reference-compatible rendering so live py_3d windows
match the fully shaded CPU outputs. Use Py3DRasterRenderer(reference_compatible=False)
to exercise the current fast batch backend. Fast mode projects triangles,
applies simple per-face lighting, samples textured faces at the triangle center,
expands wireframe edges into thin GPU triangles, and returns packed RGB frames
through py_3d's lazy PixelBuffer path. Smooth vertex attributes, full
material parity, persistent static geometry uploads, and direct window
presentation should be added behind this same shape before the accelerated path
becomes the default for full scenes.
Run the current screen-space raster benchmark:
python examples/benchmark_raster.py --backend auto --frames 60 --triangles 1000 --width 640 --height 360
python examples/benchmark_raster.py --backend moderngl --frames 60 --triangles 1000 --width 640 --height 360
python examples/benchmark_raster.py --backend cpu --frames 10 --triangles 1000 --width 640 --height 360Write a visual benchmark frame:
python examples/benchmark_raster.py --triangles 1000 --output raster-benchmark.pngOn one Windows development machine with an RTX 4090, the current 1000 triangle
640x360 benchmark measured about 131.6 fps through the ModernGL backend,
about 8.8 fps through the NumPy backend, and about 0.8 fps through the plain
Python CPU backend. Treat local benchmark numbers as machine-specific, not a
promise.
Through the py_3d adapter on the same machine, the fast ModernGL bridge
measured about 48 fps at 320x180 with 236 generated scene triangles and
about 63 fps at 480x270 with a lighter 132-triangle live setting. The gap
between raw raster speed and adapter speed is expected until persistent batches
and direct presentation remove more per-frame Python and readback work.
Generate README preview media:
python examples/render_preview.py --backend auto
python examples/render_lit_textured_shapes.pyPyPI Trusted Publishing should use:
| Field | Value |
|---|---|
| PyPI Project Name | py_gpu |
| Owner | Daniel-J-Mueller |
| Repository name | py_gpu |
| Workflow name | publish.yml |
| Environment name | blank / any |
Publish by committing .github/workflows/publish.yml, pushing the branch, and
pushing a version tag:
git tag v0.0.1
git push origin v0.0.1The workflow builds with python -m build and publishes with PyPI Trusted
Publishing through pypa/gh-action-pypi-publish.
py_gpu should grow in layers:
buffers: frame and depth buffers that can be written to images or copied into external packages.geometry: compact projected batch contracts.backends: protocol, capabilities, and selection logic.raster: reference CPU rasterizer for correctness.numpy_raster: optional vectorized CPU rasterizer.moderngl_backend: optional offscreen OpenGL rasterizer.adapters: optional bridges from scene packages such aspy_3d.
Backends should expose capabilities rather than pretending every implementation supports every feature. A backend can start with flat color triangles, then add textures, vertex attributes, instancing, depth formats, or compute passes.
Near-term work:
- Add a world-space mesh batch format with positions, normals, UVs, material ids, and indices.
- Add a
py_3dadapter path that preserves lights, textures, and smooth vertex normals. - Add persistent batch handles so static geometry does not need to be rebuilt every frame.
- Prototype a WebGPU backend through
wgpuor an OpenGL backend throughmoderngl. - Keep the CPU raster backend as the correctness reference for small scenes.
- Add benchmark fixtures for thousands, tens of thousands, and hundreds of thousands of triangles.
Long-term work:
- Instancing and shared mesh buffers.
- Texture atlases and sampler modes.
- Depth, normal, object-id, and color attachments.
- Async frame readback for screenshots and offline video.
- Compute-friendly buffers for simulations that later feed rendering.
- Keep scene APIs out of this core package. Scene packages should adapt into
py_gpubatches. - Keep backend dependencies optional.
- Do not make a GPU backend mandatory for correctness tests.
- Prefer explicit capabilities and clear failure modes.
- Measure every performance claim with a reproducible benchmark.
- Keep Python per-frame work small; batching and resource reuse matter more than clever per-pixel Python.

