Bazalt
Bazalt is a Python library for the Vulkan API. Use it to prototype GPU and shader work, and to build graphical applications. A C++23 core does the Vulkan work. The Python layer keeps the setup code short.
Installation
Install bazalt with pip:
pip install bazalt
Bazalt supplies prebuilt wheels for Windows and Linux. To build from source, you need a C++23 compiler (GCC 14+ or MSVC 19.36+, Visual Studio 17.6) and the Vulkan SDK.
Key Features
- Vulkan Core: Bazalt calls Vulkan for all GPU work. You keep explicit control of the hardware.
- Shader Compilation:
ctx.compile_shader()compiles GLSL vertex, fragment and compute shaders from your code. - Compute Pipelines:
ctx.compute_pipeline()andcmd.dispatch()run GPU compute. The results go back into NumPy. You need no images. - Storage Images: Compute shaders also write images.
.storage_image(binding)andset_storage_image()bind a read/write image forimageStoreandimageLoad. Use this for post-processing and procedural textures. Bazalt records the barriers and the layout transitions between the dispatch and a later sample. - Cubemaps and Texture Arrays: Pass a list of layers to
ctx.create_image([...], cube=True)or toctx.load_image([...6 paths...], cube=True)for a cubemap. Omitcube=for a texture array. One mechanism gives yousamplerCubeandsampler2DArray. Compute can fill empty layered images, for example a procedural skybox. Seeexamples/14_skybox. - Mipmaps:
load_image()makes mips for files by default. Passmipmaps=Falseto stop this.create_image(array, mipmaps=True)makes mips for a NumPy texture.create_image(w, h, mip_levels=N)allocates an empty chain, andcmd.generate_mipmaps(image)fills it from mip 0. This works across every array layer and every cube face. - Automatic Barriers: Bazalt computes the barriers for hazards between resources at record time. Examples: dispatch to dispatch, a compute-written buffer to a vertex fetch, a compute-written image to a sample.
Context(auto_barriers=False)gives you manual control throughcmd.barrier(). - Pipeline and Buffer Management: A builder makes graphics pipelines. One function makes every buffer type.
- Command Buffers: Command recording is explicit. Calls chain, as in
cmd.bind_pipeline(p).draw(3). The blockwith cmd.rendering(target):ends the pass for you. - Asynchronous Texture Streaming:
ctx.load_image()returns immediately. The decode and the GPU copy run in the background. Anything that samples the image waits for it.ctx.upload_progressgives you the data for a loading bar. - Hot Reload:
Context(hot_reload=True)watches the shaders you loaded, their#includefiles, and your images. Bazalt applies edits live: shaders recompile and rebuild their pipelines in place, and images upload again into the same handle. Bazalt logs a typo or a bad file and keeps the last good version on screen. A mistake does not stop the application. Seeexamples/12_hot_reload. - Frame Timing and Debug Names:
Context(gpu_timing=True)makesrenderer.gpu_time_msreport the GPU time of a recent frame. This is opt-in and costs nothing when off. Uset = cmd.timer()andt.stop()to time a slice of a recording, then readt.ms. A timer is a handle, so you need nowithblock, and it reads back headless. Thename=argument and the.name()method label buffers, images and pipelines, so validation messages name the object. - Headless Rendering: Draw into an offscreen
RenderTargetand read the pixels back as a NumPy array. You need no window and no display. - Render-to-Texture, MRT and Shadow Maps: Target attachments are ordinary
Imageobjects in any supportedFormat. Sampletarget.color[0], ortarget.depthon a depth-only target, like any other texture. - Multi-Window: One Context drives any number of windows.
ctx.begin_frame()opens the frame. Each window callsrenderer.acquire()andrenderer.present(cmd), and owns its own swapchain. If you resize or close one window, the other windows continue to render. Seeexamples/19_multi_window. - GPU Selection:
bz.list_devices()lists every card with its name, type, VRAM, API version and per-feature support, before a Context exists.Context(device=...)runs on the card you chose. Omit the argument and bazalt chooses for you. - Multi-Context and Multi-GPU: Any number of Contexts can be alive at the same time, on one card or on different cards. You can bake with compute on one GPU and render on the other.
other_ctx.create_image(image)moves the pixels across with their format, their layers and their cube property. Every other resource stays with its Context. If you give a resource to the wrong command buffer, you get aResourceErrorthat names the mistake, not a driver crash. Seeexamples/20_multi_context. - Wide Hardware Support: Bazalt needs Vulkan 1.2 and uses 1.3 where the driver has it, so it also runs on older integrated GPUs. You request capabilities by name, never by version or by extension.
- Separated Layers: Windowing (GLFW), the Vulkan Context (GPU initialization) and render targets stay separate. A window is one render target among others.
Quick Start: Rendering Without a Window
The code below runs with no display attached. You can use it from CI and from tests:
import bazalt as bz
ctx = bz.Context()
target = bz.RenderTarget(ctx, 800, 600, depth=bz.Format.D32F)
pipeline = (ctx.graphics_pipeline()
.vertex_shader(ctx.compile_shader("triangle.vert", bz.ShaderStage.VERTEX))
.fragment_shader(ctx.compile_shader("triangle.frag", bz.ShaderStage.FRAGMENT))
.vertex_format([bz.VertexFormat.FLOAT3, bz.VertexFormat.FLOAT3])
.build(target))
# Interleaved position (x,y,z) and color (r,g,b)
vbuf = ctx.create_buffer([
0.0, -0.5, 0.0, 1.0, 0.0, 0.0,
-0.5, 0.5, 0.0, 0.0, 1.0, 0.0,
0.5, 0.5, 0.0, 0.0, 0.0, 1.0,
], bz.BufferType.VERTEX, bz.MemoryUsage.STATIC, bz.DataType.FLOAT)
cmd = ctx.create_command_buffer()
cmd.begin()
with cmd.rendering(target, clear_color=[0.1, 0.2, 0.3, 1.0]) as c:
c.bind_pipeline(pipeline).bind_vertex_buffer(vbuf).draw(3)
ctx.submit(cmd)
pixels = target.read_pixels() # numpy (600, 800, 4) uint8
Quick Start: GPU Compute
Compute needs no window and no images. Dispatch, then read the storage buffer back as a NumPy array:
import numpy as np
import bazalt as bz
ctx = bz.Context()
# double.comp: values[i] *= 2.0 over a std430 float array, local_size_x = 64
sim = (ctx.compute_pipeline()
.shader(ctx.compile_shader("double.comp", bz.ShaderStage.COMPUTE))
.storage_buffer(0) # no stage argument — compute has exactly one stage
.build()) # no target — compute has no attachments
data = np.arange(128, dtype=np.float32)
sbuf = ctx.create_buffer(data, bz.BufferType.STORAGE, bz.MemoryUsage.STATIC)
pool = ctx.create_descriptor_pool(max_sets=4, storage_buffers=4)
dset = pool.allocate_set(sim, set=0)
dset.set_buffer(0, sbuf)
cmd = ctx.create_command_buffer()
cmd.begin()
cmd.bind_pipeline(sim).bind_descriptor_set(dset, sim, set=0).dispatch(128 // 64)
ctx.submit(cmd)
assert np.allclose(sbuf.read(np.float32), data * 2)
You can mix compute and rendering in one command buffer. A dispatch writes the vertices
and a draw reads them, with no extra code. Bazalt records the barrier between them. See
examples/11_particles.
Quick Start: Hot Reload
Add one keyword argument. Bazalt then watches the files it loaded: the shaders, their
#include files, and the images. On each save, bazalt recompiles and uploads again:
ctx = bz.Context(logger, hot_reload=True)
vert = ctx.compile_shader("shader.vert", bz.ShaderStage.VERTEX)
frag = ctx.compile_shader("shader.frag", bz.ShaderStage.FRAGMENT)
tex = ctx.load_image("wall.png")
pipeline = ctx.graphics_pipeline().vertex_shader(vert).fragment_shader(frag)...build(renderer)
while window.is_open():
bz.poll_events()
ctx.begin_frame() # edits are applied here (and at ctx.submit)
if renderer.acquire():
renderer.present(cmd) # renderer.gpu_time_ms (with gpu_timing=True) gives GPU frame timing
When you edit shader.frag, bazalt rebuilds the pipeline in place. When you save
wall.png again with the same size and the same format, bazalt uploads it into the same
handle, so the descriptor sets stay correct. A shader typo logs a ShaderError, and the
last good pipeline continues to render. A mistake does not stop the application. For the
full demo, see examples/12_hot_reload.
Quick Start: Drawing a Triangle
This example starts the window, the Vulkan Context and the SwapchainRenderer. It then compiles the shaders, creates a pipeline and draws a triangle with three colors.
import bazalt as bz
# 1. Initialize Logger and register a callback.
# Each message carries its severity and source as data, so you can filter
# without parsing strings. This is optional: a Context created without one
# reports warnings on stderr by default.
logger = bz.Logger(min_severity=bz.Severity.WARNING)
@logger.on_message
def on_message(msg):
print(f"[{msg.severity}] {msg.text}")
# 2. Create the window, Vulkan Context, and SwapchainRenderer
window = bz.Window(1024, 720, "Bazalt Demo - Triangle", logger=logger)
ctx = bz.Context(logger)
renderer = bz.SwapchainRenderer(window, ctx)
if __name__ == "__main__":
# Load and compile shaders. The vertex shader processes our geometry,
# and the fragment shader determines the final color of the pixels.
# Shaders are compiled through the Context.
vert_spv = ctx.compile_shader("triangle.vert", bz.ShaderStage.VERTEX)
frag_spv = ctx.compile_shader("triangle.frag", bz.ShaderStage.FRAGMENT)
# The pipeline is a baked state object that tells the GPU how to interpret our data.
# It is built against a render target, which supplies the color/depth formats.
# A SwapchainRenderer is one, so is an offscreen RenderTarget — same call.
pipeline = (ctx.graphics_pipeline()
.vertex_shader(vert_spv)
.fragment_shader(frag_spv)
.vertex_format([bz.VertexFormat.FLOAT3, bz.VertexFormat.FLOAT3]) # Position + Color
.build(renderer))
# We interleave Position (x,y,z) and Color (r,g,b) in a single flat array.
# Vulkan's Normalized Device Coordinates (NDC) range from -1 to 1,
# where Y points downwards and X points to the right.
vertices = [
0.0, -0.5, 0.0, 1.0, 0.0, 0.0, # Top / Red
-0.5, 0.5, 0.0, 0.0, 1.0, 0.0, # Bottom-Left / Green
0.5, 0.5, 0.0, 0.0, 0.0, 1.0, # Bottom-Right / Blue
]
# Create Vertex Buffer through the Context
vbuf = ctx.create_buffer(vertices, bz.BufferType.VERTEX, bz.MemoryUsage.STATIC, bz.DataType.FLOAT)
# Create Index Buffer through the Context
ibuf = ctx.create_buffer([0, 1, 2], bz.BufferType.INDEX, bz.MemoryUsage.STATIC, bz.DataType.UINT32)
# Command buffers store a sequence of commands for the GPU.
# For a static triangle, we can record this buffer once during initialization
# and submit the same pre-recorded buffer every frame to save CPU time.
cmd = ctx.create_command_buffer()
cmd.begin()
# begin_rendering names the target you are drawing into, clears it, and sets
# a viewport and scissor covering it. Naming the target is what lets the same
# code render to a window or to an offscreen image.
cmd.begin_rendering(renderer, clear_color=[0.1, 0.2, 0.3, 1.0])
# Bind the baked pipeline and the geometry buffers
cmd.bind_pipeline(pipeline)
cmd.bind_vertex_buffer(vbuf)
cmd.bind_index_buffer(ibuf)
# Draw 3 indices (1 triangle)
cmd.draw_indexed(3)
cmd.end_rendering(renderer)
# Main game/rendering loop
while window.is_open():
bz.poll_events()
# begin_frame opens one logical frame for every window on this
# Context; acquire() takes this window's swapchain image and returns
# False when it should sit the frame out (minimized, mid-resize)
ctx.begin_frame()
if renderer.acquire():
renderer.present(cmd)
Metadata
Release files for bazalt 0.15.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Built distributions (wheels)
| File | Reset | |||
|---|---|---|---|---|
| bazalt-0.15.0-cp313-cp313-win_amd64.whl | CPython 3.13 | CPython 3.13 | Windows x86-64 | Details |
| bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.13 | CPython 3.13 | Linux glibc 2.28+ x86-64, Linux glibc 2.27+ x86-64 | Details |
| bazalt-0.15.0-cp312-cp312-win_amd64.whl | CPython 3.12 | CPython 3.12 | Windows x86-64 | Details |
| bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.12 | CPython 3.12 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| bazalt-0.15.0-cp311-cp311-win_amd64.whl | CPython 3.11 | CPython 3.11 | Windows x86-64 | Details |
| bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.11 | CPython 3.11 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
| bazalt-0.15.0-cp310-cp310-win_amd64.whl | CPython 3.10 | CPython 3.10 | Windows x86-64 | Details |
| bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl | CPython 3.10 | CPython 3.10 | Linux glibc 2.27+ x86-64, Linux glibc 2.28+ x86-64 | Details |
Total release size: 28.8 MB
Release files / bazalt-0.15.0-cp313-cp313-win_amd64.whl
| Download URL | bazalt-0.15.0-cp313-cp313-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.13 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
46f5e8997435e6e499c15da27a94a2fdcfdb3dc007bd38a966c2da38edb89d21
|
|
BLAKE2b-256 checksum How to use checksums |
1e3d049dc25aaedb2b2c7d318dd26176bb03b88a43c8f4c9e87ebb2584154063
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bazalt-0.15.0-cp313-cp313-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 4.6 MB |
| Tags | CPython 3.13 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
5267b89c2305c17510e2cf3087a7243688ebccfce51e7b10ccdee8a7323f3284
|
|
BLAKE2b-256 checksum How to use checksums |
55ec633c8d08375c88d887e3197e8770276a89eab81d832eb124813933f0b108
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp312-cp312-win_amd64.whl
| Download URL | bazalt-0.15.0-cp312-cp312-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.12 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
24133f24613c3399c2a16d82522628d935c8e417b13e0630c7078dff6b8dee5a
|
|
BLAKE2b-256 checksum How to use checksums |
d6fe986a538170dcb782814e78502dcdf541760d42ba9d08d43cdd9c44e23928
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bazalt-0.15.0-cp312-cp312-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 4.6 MB |
| Tags | CPython 3.12 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
2b6bbe2766dbbadbb609b4cd114eade675135b6737119135f9bd1ed3eb76855c
|
|
BLAKE2b-256 checksum How to use checksums |
b1ad95225dbeb74d39847f65684d00073149d2f04d7f5b9077f8033da36b24a3
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp311-cp311-win_amd64.whl
| Download URL | bazalt-0.15.0-cp311-cp311-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.11 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
6cb61b0a1fe36607fe3d86533c92aa9f4042b71e9746240bffbf8376cb5d7246
|
|
BLAKE2b-256 checksum How to use checksums |
855f1131f94474bcd526990c859f2e317714a486ae96967b34cbcb6f43f50126
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bazalt-0.15.0-cp311-cp311-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 4.6 MB |
| Tags | CPython 3.11 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
4b441d9152e55a41ed9c64659938934cd4eb0b7553cfefac65f32e8dcd9d67ff
|
|
BLAKE2b-256 checksum How to use checksums |
f682b3f24a44014a06e6584f60293aad4f0df74afaa68010a5cb61c8f31dc307
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp310-cp310-win_amd64.whl
| Download URL | bazalt-0.15.0-cp310-cp310-win_amd64.whl |
|---|---|
| Size | 2.6 MB |
| Tags | CPython 3.10 Windows x86-64 |
|
SHA-256 checksum How to use checksums |
54acb8bc04a4d01dc9941ba3da7a0542b099b3355e3748be452b4d1ea08e9513
|
|
BLAKE2b-256 checksum How to use checksums |
46bd92f96054546f2e82f34cb3974a24c8ff1feac9a4d06764108d717b47f382
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logRelease files / bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl
| Download URL | bazalt-0.15.0-cp310-cp310-manylinux_2_27_x86_64.manylinux_2_28_x86_64.whl |
|---|---|
| Size | 4.6 MB |
| Tags | CPython 3.10 Linux glibc 2.27+ x86-64 Linux glibc 2.28+ x86-64 |
|
SHA-256 checksum How to use checksums |
f165a1cfe9ae1d04702199ee427071b17e046d1837b8ddd9143978fc19691b49
|
|
BLAKE2b-256 checksum How to use checksums |
061f56f9ec85b1e276d18ad10e4efb619bb9cd6ff6a472c3df7cddedbee5a867
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/6.1.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logSecurity for bazalt 0.15.0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 8 files in this release have been uploaded with provenance metadata.
Attestation for bazalt-0.15.0-cp313-cp313-win_amd64.whl
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Jul 26, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.