Limits & Roadmap
What the engine refuses, what it clamps, and what is not built yet. Every cap below is enforced; the interesting part is where: a compile-time rejection is a diagnostic you see, a runtime clamp is one you don’t.
Hard caps
Section titled “Hard caps”| Limit | Value | Enforced |
|---|---|---|
| Passes per document | 32 | Compile time; the emitter rejects the document |
Bundles per :execute-bundles |
16 | Compile time |
Arguments per (wasm-call …) |
32 | Compile time |
| Color attachments per multiple-render-target (MRT) pass | 8 | Structural; the emitter cannot emit more |
| CLI input size (file or stdin) | 16 MiB | Read time |
| Browser compiler source | 256 KiB | Clamped silently (see below) |
| Browser compiler output | 256 KiB | Errors |
| Executor bytecode buffer | 256 KiB | Clamped silently (per-binary default) |
| Executor data buffer | 512 KiB | Clamped silently (per-binary default) |
| Executor command buffer | 64 KiB | Reported: init returns 6, frame returns 3 |
| Diagnostics in one report | 64 | Reported: --json carries a dropped count |
The pass cap exists because the embedded executor (the small WASM interpreter that plays the bytecode inside each payload) stores pass ranges in a fixed 32-slot table; the compiler rejecting a 33rd pass up front is what keeps an over-cap document from rendering under native tests while losing passes in browsers.
The silent clamps are the ones to know about. The in-browser compiler’s
setSourceLen truncates a document past 256 KiB instead of erroring, so an
oversized source fails validation with a diagnostic that looks unrelated. The
tell: native pngine validate accepts the same bytes. The executor’s buffer
clamps are ABI behavior (lengths are clamped, never rejected) and matter
only to hosts driving the WASM API directly.
The command-buffer cap is the counter-example, and deliberately so: a buffer that overflowed would tear the command stream mid-command, so the executor returns a status code instead of emitting a prefix, and the host must skip that buffer rather than play it.
Deliberate omissions
Section titled “Deliberate omissions”Decisions, not gaps:
- No resource destruction. The command stream is create-only; resource
lifetime is scoped to the GPU device, released by whole-instance teardown
(
destroy(p)). A payload is data: it describes what exists, not an allocation sequence to balance. - PNGB, the bytecode format, stays at version 0. Payloads evolve by shipping a newer embedded executor, never by changing the header. The executor↔JS interface is frozen as ABI v1, append-only.
- WGSL lint is
validate-only.compileandrenderdon’t pay for it, and the browser compiler omits the rule engine entirely (88 KB).
Not built yet
Section titled “Not built yet”- Multi-file documents. No import form; keep each document single-file. Compile-time reuse across files means generating the SJON source (the S-expression language PNGine reads) with your own tooling.
- Timeline authoring. The payload format has an animation table and the
runtime reads it (
p.animation,p.duration), but no SJON form emits one. Sequencing is a JavaScript concern today:setFrameover multiple(frame …)definitions. - Shader composition. One
(shader-module …)is one WGSL compilation unit. A concatenation opcode exists in the format but is reserved: never emitted, rejected by the dispatcher.
Checking a document against the caps
Section titled “Checking a document against the caps”pngine validate shader.sjon --json # schema + WGSL + cap diagnosticspngine inspect shader.sjon # what the compiled payload actually doesRelated
Section titled “Related”- CLI Reference -
validate,inspect, exit codes - WASM API - The frozen ABI behind the freeze rules
- SJON Reference - Every form that exists today