EPU Environments
The Environment Processing Unit (EPU) is ZX’s GPU-driven procedural background and ambient environment system.
- It renders an infinite environment when you call
draw_epu()after providing a config withepu_set(config_ptr)(packed 128-byte config). - The same environment is sampled by lit shaders for ambient/reflection lighting.
For exact FFI signatures and instruction encoding, see the Environment (EPU) API.
For the full specification (opcode catalog, packing rules, WGSL details), see:
nethercore-design/specs/epu-feature-catalog.mdnethercore/include/zx.rs(canonical ABI docs)nethercore/nethercore-zx/shaders/epu/(shader sources)
Quick Start
- Create a packed EPU config: 8 × 128-bit instructions (stored as 16
u64values as 8[hi, lo]pairs). - Call
epu_set(config_ptr)near the start ofrender(), then calldraw_epu()after your 3D geometry so the environment fills only background pixels.
Determinism note: The EPU has no host-managed time. To animate an environment, keep a deterministic u8 phase in your game state (e.g. phase = phase.wrapping_add(1) each frame), write it into the opcode parameter you want to drive (commonly param_d), and call epu_set(...) again with the updated config.
// 8 x [hi, lo]
static ENV: [[u64; 2]; 8] = [
[0, 0], [0, 0], [0, 0], [0, 0],
[0, 0], [0, 0], [0, 0], [0, 0],
];
fn render() {
unsafe {
epu_set(ENV.as_ptr().cast()); // Set environment config
// ... draw scene geometry
draw_epu(); // Draw environment background
}
}
Reference presets and packing helpers:
nethercore/examples/3-inspectors/epu-showcase/src/presets.rsnethercore/examples/3-inspectors/epu-showcase/src/constants.rs
Architecture Overview
The EPU uses a 128-byte instruction-based configuration:
| Slot | Kind | Recommended Use |
|---|---|---|
| 0–3 | Bounds | RAMP + optional bounds ops (0x02..0x07) |
| 4–7 | Radiance | DECAL/GRID/SCATTER/FLOW + radiance ops (0x0C..0x13) |
Bounds defines the low-frequency envelope and region weights (sky/walls/floor).
Radiance adds higher-frequency motifs (decals, grids, stars, clouds, etc.).
Opcode Overview
| Opcode | Name | Best For | Notes |
|---|---|---|---|
| 0x01 | RAMP | Base bounds | Often used first to explicitly set up/ceil/floor/softness, but any bounds opcode can be layer 0. |
| 0x02 | SECTOR | Opening wedge / interior cues | Bounds modifier |
| 0x03 | SILHOUETTE | Skyline / horizon cutout | Bounds modifier |
| 0x04 | SPLIT | Geometric divisions | Bounds |
| 0x08 | DECAL | Sun disks, signage, portals | Radiance |
| 0x09 | GRID | Panels, architectural lines | Radiance |
| 0x0A | SCATTER | Stars, dust, particles | Radiance |
| 0x0B | FLOW | Clouds, rain, caustics | Radiance |
| 0x12 | LOBE | Sun glow, lamps, neon spill | Radiance |
| 0x13 | BAND | Horizon bands / rings | Radiance |
Authoring Workflow
- Start from a known-good preset (
epu-showcase). - Use the
epu-showcasedebug panel (F4) to iterate on one layer at a time (opcode + params). - Copy the resulting packed 8-layer config into your game, call
epu_set(config_ptr), then calldraw_epu().
Slot Conventions
| Slot | Kind | Recommended Use |
|---|---|---|
| 0-3 | Bounds | Any bounds opcode (0x01..0x07). Common convention is RAMP first, not a requirement. |
| 4-7 | Radiance | DECAL / GRID / SCATTER / FLOW + radiance ops (0x0C..0x13) |
Bounds/Feature Cadence (Don't Waste Slots)
Bounds opcodes don't just draw color; they also rewrite the region weights (SKY/WALLS/FLOOR) that later feature opcodes use for masking.
- Avoid stacking multiple “plain bounds” layers back-to-back (e.g.
RAMP -> SILHOUETTE) unless you immediately exploit the new regions with feature layers. - Prefer a cadence like:
BOUNDS (define/reshape regions) -> FEATURES (use regions) -> BOUNDS (carve/retag: APERTURE/SPLIT) -> FEATURES (decorate + animate). - If you insert a bounds opcode later in the 8-layer program, it only affects features after it (it cannot retroactively re-mask earlier features).
meta5 Behavior
meta5encodes(domain_id << 3) | variant_idfor opcodes that support domain/variant selection.- For opcodes that do not use domain/variant, set
meta5 = 0.
Split-Screen / Multiple Viewports
Call viewport(...) and then draw_epu() per viewport/pass where you want an environment background.
See Also
- EPU API Reference - FFI signatures and instruction encoding
- EPU Architecture Overview - Compute pipeline details
- EPU Feature Catalog