wgpu
stableGPU rendering plugin built on wgpu exposing adapter selection, device and queue management, render pipelines, buffers, textures, and a full render-pass API for cross-platform GPU graphics.
use plugin wgpu::{wgpu.request_adapter, wgpu.enumerate_adapters, wgpu.create_surface, …} Functions (38)
- wgpu.request_adapter Select a GPU adapter, optionally for a surface
- wgpu.enumerate_adapters List every available GPU adapter
- wgpu.create_surface Create a rendering surface from a native window handle
- wgpu.handle_count Count live GPU object handles (leak probe)
- GpuAdapter.name Human-readable name of the adapter
- GpuAdapter.request_device Open a device and queue from the adapter
- GpuDevice.create_shader Compile a WGSL shader module
- GpuDevice.create_render_pipeline Build a render pipeline from shaders
- GpuDevice.create_buffer Allocate a GPU buffer
- GpuDevice.create_command_encoder Begin recording GPU commands
- GpuDevice.create_pipeline_layout Create an empty pipeline layout
- GpuDevice.create_depth_view Create a depth texture view
- GpuDevice.create_bind_group Bind buffers, views, and samplers to a layout
- GpuDevice.create_texture Allocate a 2D or array texture
- GpuDevice.create_sampler Create a texture sampler
- GpuDevice.destroy Release the device handle
- GpuQueue.submit Submit command buffers for execution
- GpuQueue.write_buffer Write raw bytes into a buffer
- GpuQueue.write_floats Write a table of floats into a buffer
- GpuQueue.write_texture Upload pixel data into a texture
- GpuQueue.write_bc1_as_rgba8 Decode BC1/BC2/BC3 blocks and upload as RGBA8
- GpuSurface.configure Configure the surface for a size and device
- GpuSurface.format Get the surface's chosen texture format
- GpuSurface.get_current_texture Acquire the next swap-chain texture
- GpuSurface.save_screenshot Read back the surface and save it as PNG
- GpuSurfaceTexture.create_view Create a view of the surface texture
- GpuSurfaceTexture.present Present the rendered frame to the screen
- GpuTexture.create_view Create a view of a texture
- GpuTextureView.destroy Release a texture view handle
- GpuRenderPipeline.get_bind_group_layout Fetch an auto-generated bind group layout
- GpuCommandEncoder.begin_render_pass Start recording a render pass
- GpuCommandEncoder.finish Finish recording and produce a command buffer
- RenderPassState.set_pipeline Bind a render pipeline for the pass
- RenderPassState.set_bind_group Bind a bind group at an index
- RenderPassState.draw Record a draw call
- RenderPassState.draw_batch Record many draws in one FFI crossing
- RenderPassState.end Replay recorded commands and end the pass
- GpuBuffer.destroy Release a buffer handle
Overview
wgpu is a thin, handle-based binding over the wgpu GPU API,
mirroring the WebGPU shape used by the JS, Rust, Python, Go, and Zig bindings.
Every GPU object — adapters, devices, queues, surfaces, buffers, textures,
pipelines, and render passes — is an opaque handle returned by a factory call,
and you drive it with method-style calls (device.create_buffer(...),
queue.submit(...), tex.present()). The module-level entry points
(wgpu.request_adapter, wgpu.enumerate_adapters, wgpu.create_surface,
wgpu.handle_count) sit under the lowercase wgpu namespace; everything else is
reached by calling methods on the handles those entry points hand back.
The canonical flow is: create a surface from a native window, request an adapter
compatible with it, ask the adapter for a {device, queue} pair, configure the
surface, then each frame acquire a surface texture, record a command encoder with
a render pass, submit the resulting command buffer to the queue, and present.
Handles are stateful and own real GPU resources, so release them with
.destroy() (often via defer) and consume surface textures / command buffers
exactly once. Use this plugin when you need direct, cross-platform GPU rendering
rather than a higher-level scene API.
Common patterns
Bootstrap a renderer from a winit window — surface first, then an adapter compatible with it, then a device and queue:
use plugin winit::{Window, EventLoop}
use plugin wgpu::{wgpu}
let win = Window.new(#{"title": "Triangle", "width": 800, "height": 600})
let el = EventLoop.new()
el.run(fn(event) {
if event["event"] == "window_created" {
let surface = wgpu.create_surface(win.native_handle())
let adapter = wgpu.request_adapter(#{"compatible_surface": surface})
let dq = adapter.request_device(#{"label": "main"})
surface.configure(adapter, dq["device"], 800, 600)
print("using adapter: {adapter.name()}")
}
})
Render one frame: acquire the surface texture, clear it in a render pass, draw, finish the encoder, submit, and present:
use plugin wgpu::{wgpu}
fn draw_frame(surface, device, queue, pipeline) {
let frame = surface.get_current_texture()
if frame == nil { return }
let view = frame.create_view()
let encoder = device.create_command_encoder(#{})
let pass = encoder.begin_render_pass(#{
"color_attachments": #{
"1": #{
"view": view,
"load": "clear",
"clear_color": #{"r": 0.1, "g": 0.2, "b": 0.3, "a": 1.0},
},
},
})
pass.set_pipeline(pipeline)
pass.draw(3, 1)
pass.end()
queue.submit(#{"1": encoder.finish()})
frame.present()
}
Upload uniform data into a buffer and bind it for a draw:
use plugin wgpu::{wgpu}
fn upload_uniforms(device, queue, pipeline, values) {
let buf = device.create_buffer(#{"size": 64, "usage": "uniform|copy_dst"})
queue.write_floats(buf, 0, values)
let layout = pipeline.get_bind_group_layout(0)
let bg = device.create_bind_group(layout, #{"0": #{"buffer": buf}})
return bg
}
Select a GPU adapter, optionally for a surface
Requests a GPU adapter. Pass a config table with optional power_preference
("high", "low", or "none") and a compatible_surface handle so the adapter
is chosen to support that surface. Returns a GpuAdapter handle, or errors if no
suitable GPU is found.
use plugin wgpu::{wgpu}
let adapter = wgpu.request_adapter(#{"power_preference": "high"})
print(adapter.name())
When you already have a surface, pass it so adapter selection guarantees compatibility:
use plugin wgpu::{wgpu}
fn pick_adapter(surface) {
return wgpu.request_adapter(#{"compatible_surface": surface})
}
List every available GPU adapter
Returns a table listing every available GPU adapter. Each entry is a table with
name, backend, and device_type string fields.
use plugin wgpu::{wgpu}
let adapters = wgpu.enumerate_adapters()
print("found {#adapters} adapters")
Inspect the backend and type of each detected GPU:
use plugin wgpu::{wgpu}
for entry in wgpu.enumerate_adapters() {
print("{entry["name"]} — {entry["backend"]} ({entry["device_type"]})")
}
Create a rendering surface from a native window handle
Creates a wgpu rendering surface from a native window handle. Pass either a
window.native_handle() table (cross-platform: win32, xlib, xcb, wayland,
appkit) or, on Windows only, a raw Win32 HWND integer. Create the surface before
requesting a compatible adapter.
use plugin winit::{Window, EventLoop}
use plugin wgpu::{wgpu}
let win = Window.new(#{"title": "Demo", "width": 800, "height": 600})
let el = EventLoop.new()
el.run(fn(event) {
if event["event"] == "window_created" {
let surface = wgpu.create_surface(win.native_handle())
let adapter = wgpu.request_adapter(#{"compatible_surface": surface})
let dq = adapter.request_device(#{})
surface.configure(adapter, dq["device"], 800, 600)
}
})
Count live GPU object handles (leak probe)
Returns the number of live entries in the plugin handle table. Useful for
detecting GPU object leaks — this value should be stable across frames in a
steady-state render loop, and growth signals a handle that was created without a
matching .destroy() or consume step.
use plugin wgpu::{wgpu}
print("live handles: {wgpu.handle_count()}")
Human-readable name of the adapter
Returns the human-readable name of the adapter, e.g. the GPU model string.
use plugin wgpu::{wgpu}
let adapter = wgpu.request_adapter(#{})
print("GPU: {adapter.name()}")
Open a device and queue from the adapter
Opens a logical device and queue from the adapter. Pass an optional config table
with a label string. Returns a table with device (a GpuDevice) and queue
(a GpuQueue). BC texture compression is enabled automatically when the adapter
supports it.
use plugin wgpu::{wgpu}
let adapter = wgpu.request_adapter(#{})
let dq = adapter.request_device(#{"label": "renderer"})
let device = dq["device"]
let queue = dq["queue"]
Compile a WGSL shader module
Compiles a WGSL shader module. The config table requires a source string (the
WGSL code) and accepts an optional label. Returns a GpuShader handle to use
as a vertex or fragment stage in a render pipeline.
use plugin wgpu::{wgpu}
fn make_shader(device, wgsl) {
return device.create_shader(#{"label": "main", "source": wgsl})
}
Build a render pipeline from shaders
Builds a render pipeline. The config table requires vertex_shader (a
GpuShader handle) and accepts fragment_shader, vertex_entry (default
"vs_main"), fragment_entry (default "fs_main"), color_format (default
"bgra8unorm"), depth_format (presence enables a Depth32Float depth test),
layout, and label. Returns a GpuRenderPipeline handle.
use plugin wgpu::{wgpu}
fn make_pipeline(device, vs, fs, fmt) {
return device.create_render_pipeline(#{
"vertex_shader": vs,
"fragment_shader": fs,
"color_format": fmt,
})
}
Add a depth_format to opt into depth testing for 3D scenes:
use plugin wgpu::{wgpu}
fn make_3d_pipeline(device, vs, fs, fmt) {
return device.create_render_pipeline(#{
"vertex_shader": vs,
"fragment_shader": fs,
"color_format": fmt,
"depth_format": "depth32float",
})
}
Allocate a GPU buffer
Allocates a GPU buffer. The config table requires a size in bytes and accepts a
usage string (a |-separated list of vertex, index, uniform, storage,
copy_src, copy_dst, indirect; default "copy_dst|copy_src") plus an
optional label. Returns a GpuBuffer handle.
use plugin wgpu::{wgpu}
fn make_vertex_buffer(device, byte_size) {
return device.create_buffer(#{"size": byte_size, "usage": "vertex|copy_dst"})
}
Begin recording GPU commands
Begins recording GPU commands. Pass an optional config table with a label.
Returns a GpuCommandEncoder to open render passes on; calling finish()
consumes it.
use plugin wgpu::{wgpu}
let encoder = device.create_command_encoder(#{"label": "frame"})
Create an empty pipeline layout
Creates a pipeline layout (currently with no explicit bind group layouts or push
constant ranges). Pass an optional config table with a label. Returns a
GpuPipelineLayout handle.
use plugin wgpu::{wgpu}
let layout = device.create_pipeline_layout(#{"label": "empty"})
Create a depth texture view
Creates a Depth32Float depth texture of the given size and returns a
GpuTextureView over it, ready to use as the depth_view of a render pass. Match
its size to the surface so the depth test aligns with the color target.
use plugin wgpu::{wgpu}
let depth = device.create_depth_view(800, 600)
Bind buffers, views, and samplers to a layout
Binds resources to a GpuBindGroupLayout. Pass the layout handle plus an
entries table keyed by binding index (as string keys), where each value is a
table holding a buffer, texture_view, or sampler handle. A legacy
buffer-only form create_bind_group(layout, buffer, binding) is also accepted.
Returns a GpuBindGroup handle.
use plugin wgpu::{wgpu}
fn make_bind_group(device, pipeline, buf, view, sampler) {
let layout = pipeline.get_bind_group_layout(0)
return device.create_bind_group(layout, #{
"0": #{"buffer": buf},
"1": #{"texture_view": view},
"2": #{"sampler": sampler},
})
}
Allocate a 2D or array texture
Allocates a 2D (or array) texture. The config table accepts width (default
256), height (default 256), array_layers (default 1), format (default
"bgra8unorm"; also supports rgba8unorm, srgb variants, rgba16float,
r32float, and BC1/BC2/BC3 names), usage (a |-separated list of
texture_binding, copy_dst, copy_src, render_attachment,
storage_binding; default "texture_binding|copy_dst"), and an optional
label. Returns a GpuTexture handle.
use plugin wgpu::{wgpu}
fn make_texture(device, w, h) {
return device.create_texture(#{
"width": w,
"height": h,
"format": "rgba8unorm",
"usage": "texture_binding|copy_dst",
})
}
Create a texture sampler
Creates a texture sampler. Pass an optional config table with address_mode_u,
address_mode_v, address_mode_w ("clamp", "repeat", "mirror",
"border"), mag_filter, min_filter, mipmap_filter ("linear" or
"nearest"), and an optional label. A bare create_sampler(...) with no config
yields clamp-to-edge + linear defaults. Returns a GpuSampler handle.
use plugin wgpu::{wgpu}
fn make_sampler(device) {
return device.create_sampler(#{
"address_mode_u": "repeat",
"address_mode_v": "repeat",
"mag_filter": "nearest",
})
}
Release the device handle
Releases the device handle and frees its GPU resources. Pairs well with defer
for scope-bound cleanup.
use plugin wgpu::{wgpu}
let dq = wgpu.request_adapter(#{}).request_device(#{})
let device = dq["device"]
defer device.destroy()
Submit command buffers for execution
Submits a table of GpuCommandBuffer handles (produced by
encoder.finish()) for execution on the GPU. Each command buffer is consumed and
its handle is freed, so submit each buffer exactly once.
use plugin wgpu::{wgpu}
let cmd = encoder.finish()
queue.submit(#{"1": cmd})
Write raw bytes into a buffer
Writes raw bytes into a GpuBuffer at a byte offset. The data argument is a
byte string or byte value; for tables of numbers use write_floats instead.
use plugin wgpu::{wgpu}
queue.write_buffer(index_buffer, 0, index_bytes)
Write a table of floats into a buffer
Writes a table of numbers into a GpuBuffer as tightly-packed little-endian
f32 values, starting at byte offset. Convenient for uniforms and vertex data
built up in Zolo as a list of numbers.
use plugin wgpu::{wgpu}
let mvp = #{"1": 1.0, "2": 0.0, "3": 0.0, "4": 0.0}
queue.write_floats(uniform_buffer, 0, mvp)
Upload pixel data into a texture
Uploads pixel data into a GpuTexture. Pass the pixel bytes, the texture
width and height, and an optional layer index as a fifth argument for array
textures. Strides are computed automatically, including block alignment for BC
formats.
use plugin wgpu::{wgpu}
queue.write_texture(tex, rgba_bytes, 256, 256)
Target a specific layer of a 2D-array texture with the optional layer argument:
use plugin wgpu::{wgpu}
queue.write_texture(atlas, face_bytes, 64, 64, 3)
Decode BC1/BC2/BC3 blocks and upload as RGBA8
Software-decodes block-compressed data and uploads it as RGBA8. Defaults to BC1
(DXT1); pass an optional sixth layer argument (target array layer, default 0)
and a seventh compression argument of 3 for BC2 (DXT3) or 5 for BC3 (DXT5).
Use this when a GPU's hardware BC decoder misbehaves.
use plugin wgpu::{wgpu}
queue.write_bc1_as_rgba8(tex, dxt1_bytes, 256, 256)
Decode a DXT5 (BC3) block instead by passing the layer and compression mode:
use plugin wgpu::{wgpu}
queue.write_bc1_as_rgba8(tex, dxt5_bytes, 256, 256, 0, 5)
Configure the surface for a size and device
Configures the surface's swap chain for the given adapter, device, and pixel
size. Picks an sRGB format when available, prefers a low-latency present mode, and
enables COPY_SRC so save_screenshot can read the surface back. Re-run this on
window resize.
use plugin wgpu::{wgpu}
surface.configure(adapter, device, 1280, 720)
Get the surface's chosen texture format
Returns the texture format the surface was configured with as a string (e.g.
"bgra8unorm-srgb"). Pass this to create_render_pipeline's color_format so
the pipeline output matches the surface.
use plugin wgpu::{wgpu}
let fmt = surface.format()
let pipeline = device.create_render_pipeline(#{
"vertex_shader": vs,
"fragment_shader": fs,
"color_format": fmt,
})
Acquire the next swap-chain texture
Acquires the next swap-chain texture to render into. Returns a
GpuSurfaceTexture, or nil when the surface is outdated or lost (reconfigure
and retry next frame). Create a view from it, render, then present() it.
use plugin wgpu::{wgpu}
let frame = surface.get_current_texture()
if frame == nil { return }
let view = frame.create_view()
Read back the surface and save it as PNG
Copies the current surface texture into a CPU-mappable buffer, swizzles BGRA to
RGBA, and writes it to path as a PNG. Call this BEFORE present() — after
present the swap-chain image is consumed.
use plugin wgpu::{wgpu}
let frame = surface.get_current_texture()
if frame != nil {
surface.save_screenshot(device, queue, frame, "frame.png")
frame.present()
}
Create a view of the surface texture
Creates a GpuTextureView over the surface texture, suitable as a render-pass
color attachment. The view is owned by the swap-chain image and becomes invalid
once the texture is presented (the plugin reaps it automatically).
use plugin wgpu::{wgpu}
let frame = surface.get_current_texture()
let view = frame.create_view()
Present the rendered frame to the screen
Presents the rendered frame to the screen and consumes the surface texture (its handle and any derived views are freed). Call once per acquired frame, after submitting your command buffers.
use plugin wgpu::{wgpu}
queue.submit(#{"1": encoder.finish()})
frame.present()
Create a view of a texture
Creates a GpuTextureView over a texture. Pass an optional config table with a
dimension ("2d", "2d-array", "cube", "cube-array", "3d") to override
wgpu's default view dimension. Returns a GpuTextureView handle for binding.
use plugin wgpu::{wgpu}
let view = tex.create_view(#{})
View an array texture as a cube map:
use plugin wgpu::{wgpu}
let cube_view = cube_tex.create_view(#{"dimension": "cube"})
Release a texture view handle
Releases a texture view handle. Use for views you created from regular textures; surface-texture views are reaped automatically on present.
use plugin wgpu::{wgpu}
defer view.destroy()
Fetch an auto-generated bind group layout
Returns the auto-generated GpuBindGroupLayout at the given group index of the
pipeline. Feed it to device.create_bind_group to bind resources matching the
shader's declared bindings.
use plugin wgpu::{wgpu}
let layout = pipeline.get_bind_group_layout(0)
let bg = device.create_bind_group(layout, #{"0": #{"buffer": uniforms}})
Start recording a render pass
Starts recording a render pass. The config table holds color_attachments (a
table keyed by index, each with a view handle plus optional load
("clear"/"load"), clear_color #{r, g, b, a}, and store
("store"/"discard")), an optional depth_view handle, and an optional
depth_load. Returns a RenderPassState you record draws into; the work is
replayed when you call end().
use plugin wgpu::{wgpu}
let pass = encoder.begin_render_pass(#{
"color_attachments": #{
"1": #{
"view": view,
"load": "clear",
"clear_color": #{"r": 0.0, "g": 0.0, "b": 0.0, "a": 1.0},
},
},
})
Attach a depth view for a 3D pass:
use plugin wgpu::{wgpu}
let pass = encoder.begin_render_pass(#{
"color_attachments": #{
"1": #{"view": view, "load": "clear", "clear_color": #{"r": 0.1, "g": 0.1, "b": 0.1, "a": 1.0}},
},
"depth_view": depth_view,
"depth_load": "clear",
})
Finish recording and produce a command buffer
Finishes recording and produces a GpuCommandBuffer ready for
queue.submit. Consumes the encoder, so its handle is freed and cannot be reused.
use plugin wgpu::{wgpu}
let cmd = encoder.finish()
queue.submit(#{"1": cmd})
Bind a render pipeline for the pass
Binds a GpuRenderPipeline for subsequent draws in the pass. The binding is
recorded and applied when end() replays the pass.
use plugin wgpu::{wgpu}
pass.set_pipeline(pipeline)
Bind a bind group at an index
Records binding a GpuBindGroup at group index. Bind-group sets and draws are
replayed in the exact order you issued them, so you can interleave per-draw bind
groups with draws.
use plugin wgpu::{wgpu}
pass.set_bind_group(0, bind_group)
pass.draw(3, 1)
Record a draw call
Records a draw call for vertex_count vertices and instance_count instances,
with optional first_vertex and first_instance offsets (both default 0). The
draw is replayed in order at end().
use plugin wgpu::{wgpu}
pass.draw(3, 1)
Draw many instances from a vertex offset:
use plugin wgpu::{wgpu}
pass.draw(6, 100, 0, 0)
Record many draws in one FFI crossing
Records many draw calls in a single Lua↔Rust crossing. Pass a flat table whose
length is a multiple of 4 — [vertex_count, instance_count, first_vertex, first_instance, ...] per draw. A large win over per-draw draw() calls when
rendering thousands of slots per frame.
use plugin wgpu::{wgpu}
let draws = #{
"1": 3, "2": 1, "3": 0, "4": 0,
"5": 6, "6": 1, "7": 3, "8": 0,
}
pass.draw_batch(draws)
Replay recorded commands and end the pass
Replays the recorded pipeline, bind-group, and draw commands against the encoder, ends the render pass, and frees the pass handle. Call once after recording all draws for the pass.
use plugin wgpu::{wgpu}
pass.set_pipeline(pipeline)
pass.draw(3, 1)
pass.end()
Release a buffer handle
Releases a buffer handle and frees its GPU memory. Use defer to release buffers
that outlive a single frame.
use plugin wgpu::{wgpu}
let buf = device.create_buffer(#{"size": 1024, "usage": "vertex|copy_dst"})
defer buf.destroy()