Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

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 with epu_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.md
  • nethercore/include/zx.rs (canonical ABI docs)
  • nethercore/nethercore-zx/shaders/epu/ (shader sources)

Quick Start

  1. Create a packed EPU config: 8 × 128-bit instructions (stored as 16 u64 values as 8 [hi, lo] pairs).
  2. Call epu_set(config_ptr) near the start of render(), then call draw_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.rs
  • nethercore/examples/3-inspectors/epu-showcase/src/constants.rs

Architecture Overview

The EPU uses a 128-byte instruction-based configuration:

SlotKindRecommended Use
0–3BoundsRAMP + optional bounds ops (0x02..0x07)
4–7RadianceDECAL/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

OpcodeNameBest ForNotes
0x01RAMPBase boundsOften used first to explicitly set up/ceil/floor/softness, but any bounds opcode can be layer 0.
0x02SECTOROpening wedge / interior cuesBounds modifier
0x03SILHOUETTESkyline / horizon cutoutBounds modifier
0x04SPLITGeometric divisionsBounds
0x08DECALSun disks, signage, portalsRadiance
0x09GRIDPanels, architectural linesRadiance
0x0ASCATTERStars, dust, particlesRadiance
0x0BFLOWClouds, rain, causticsRadiance
0x12LOBESun glow, lamps, neon spillRadiance
0x13BANDHorizon bands / ringsRadiance

Authoring Workflow

  • Start from a known-good preset (epu-showcase).
  • Use the epu-showcase debug 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 call draw_epu().

Slot Conventions

SlotKindRecommended Use
0-3BoundsAny bounds opcode (0x01..0x07). Common convention is RAMP first, not a requirement.
4-7RadianceDECAL / 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

  • meta5 encodes (domain_id << 3) | variant_id for 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