junkpile

Junkpile 24 — wgpu Mesh Feedback Deformer

A standalone Tauri 2 + wgpu example that treats mesh geometry as a persistent GPU feedback system.

Instead of uploading new vertex positions from Rust every frame, a WGSL compute pass reads the previous mesh state from one storage buffer and writes the next state into another. The render pass consumes the newly written state directly. On the next frame, the buffers reverse roles.

frame N
state A ──compute──► state B ──render──► native surface

frame N + 1
state B ──compute──► state A ──render──► native surface

This is the geometry equivalent of ping-pong texture feedback.

What this example demonstrates

Run

npm install
npm run dev

Backend-specific development commands:

npm run dev:metal
npm run dev:vulkan
npm run dev:dx12

The backend must be available on the current operating system.

Window architecture

Only the controls WebView is declared in tauri.conf.json. During Tauri setup, Rust creates a second native window labeled renderer and passes that window directly to wgpu::Instance::create_surface.

┌─────────────────────────────┐       Tauri commands       ┌───────────────────────────┐
│ controls WebView            │ ─────────────────────────► │ Rust renderer thread      │
│ sliders / presets / status  │                            │ compute + render encoder  │
└─────────────────────────────┘                            └─────────────┬─────────────┘
                                                                        │
                                                                        ▼
                                                          ┌───────────────────────────┐
                                                          │ native renderer window    │
                                                          │ Metal / Vulkan / DX12     │
                                                          └───────────────────────────┘

The graphics window is not a WebView canvas. wgpu owns its native surface.

Persistent vertex state

Every vertex occupies 64 bytes in both Rust and WGSL:

position_u           vec4  xyz current position, w U coordinate
velocity_v           vec4  xyz persistent velocity, w V coordinate
normal_displacement  vec4  xyz normal, w displacement from rest
rest_seed            vec4  xyz immutable rest position, w stable seed

The default sphere uses 128 latitude segments and 192 longitude segments. The seam is duplicated so the mesh can retain standard UV coordinates.

Frame sequence

Each active simulation frame performs these steps:

  1. Rust writes only compact compute and scene uniform buffers.
  2. The compute pass reads the active storage buffer.
  3. Each workgroup updates up to 128 vertices.
  4. The compute pass writes positions, velocities, normals, and displacement into the other storage buffer.
  5. The render pass binds that destination buffer as read-only vertex-stage storage.
  6. The indexed draw reads each vertex through @builtin(vertex_index).
  7. The buffers exchange roles for the next frame.

No per-frame mesh readback or CPU vertex upload occurs.

Compute forces

The compute shader combines:

The controls deliberately separate feedback from damping:

Presets

Applying a preset resets both storage buffers to the original sphere so the preset starts from a known state.

Inspection modes

Important implementation files

src-tauri/src/main.rs       Tauri commands and two-window lifecycle
src-tauri/src/renderer.rs   wgpu device, ping-pong buffers, compute/render loop
src-tauri/src/mesh.rs       initial sphere topology and persistent vertex state
src-tauri/src/feedback_compute.wgsl compute simulation stage
src-tauri/src/feedback_render.wgsl  vertex and fragment rendering stages
src/index.html              controls window
src/app.js                  IPC and telemetry synchronization

Reset behavior

Restore sphere state writes the original vertex array into both GPU state buffers. This is an explicit user action, not part of the normal frame path.

Reset all restores the default controls and resets the mesh state.

Performance notes

The example allocates two geometry state buffers because simultaneous read/write feedback on a single buffer would introduce data hazards. wgpu inserts the required compute-to-vertex synchronization when both passes are encoded in order.

The renderer uses PresentMode::Fifo, so visible frame rate normally follows display refresh. Compute work still scales with vertex count, shader complexity, and backend.

Platform path

wgpu and Naga handle backend translation. WGSL remains the canonical source shader.