(queue …)
Defines queue operations for writing data to buffers and copying images to textures.
Syntax
Section titled “Syntax”(queue :name writeUniforms (write-buffer :buffer uniforms :offset 0 :data pngine-inputs))
(queue :name loadTexture (copy-external-image-to-texture (source :image photo) (destination :texture albedo)))A (queue …) holds its actions as positional sub-forms, run in document order
when the frame reaches the queue. The five actions are:
| Action | Purpose |
|---|---|
write-buffer |
Write bytes into a buffer |
copy-buffer-to-buffer |
GPU buffer → buffer copy |
copy-texture-to-texture |
GPU texture → texture copy |
copy-external-image-to-texture |
Upload a decoded image into a texture |
resolve-query-set |
Copy resolved query results into a buffer |
(queue :name readback (resolve-query-set :query-set occ :first-query 0 :query-count 6 :destination resolveBuf :destination-offset 0) (copy-buffer-to-buffer :source resolveBuf :source-offset 0 :destination readbackBuf :destination-offset 0 :size 48))| Key | Type | Required | Description |
|---|---|---|---|
:name |
symbol | Yes | Unique name (referenced from (frame …)) |
| actions | sub-forms | No | Positional action sub-forms, run in the order written |
write-buffer
Section titled “write-buffer”Writes data to a GPU buffer.
| Key | Type | Required | Description |
|---|---|---|---|
:buffer |
reference | Yes | Target buffer (needs copy-dst usage) |
:offset |
number | No | Byte offset, a multiple of 4 (default: 0) |
:data |
reference/symbol | Yes | Where the bytes come from |
:data is one key for the three things a source can be: a built-in runtime
source, a (data …) name, or a (wasm-call …) name. Which one it is follows
from what that name was declared as, so there is no second key to choose
between them.
Data sources:
Static data reference (from (data …)):
(data :name vertexData :float32 [0 0.5 -0.5 -0.5 0.5 -0.5])
(write-buffer :buffer vertices :data vertexData)Runtime uniform data (built-in):
(write-buffer :buffer uniforms :offset 0 :data pngine-inputs)The pngine-inputs symbol, the built-in source of time and canvas size,
provides 16 bytes of runtime data written each frame by the executor (the small
WASM interpreter embedded in each payload, which turns bytecode into GPU
commands). The other built-in sources are scene-time-inputs (12 bytes) and
pointer-inputs (48 bytes).
WASM result (from a (wasm-call …), a WebAssembly module the executor calls
once per frame):
(wasm-call :name mvp :file "matrices.wasm" :func rotatingMVP :returns "mat4x4" :args [time-total canvas-width canvas-height])
(write-buffer :buffer mvpBuffer :offset 0 :data mvp)Naming a (wasm-call …) there calls it at frame time and writes its result
into the buffer.
copy-external-image-to-texture
Section titled “copy-external-image-to-texture”Copies a decoded image (an (image-bitmap …)) into a texture. Each end of a
copy is a dictionary in WebGPU, so each is a child form: exactly one
(source …) and exactly one (destination …).
(source …):
| Key | Type | Required | Description |
|---|---|---|---|
:image |
reference | Yes | The (image-bitmap …) to upload |
(destination …):
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
:texture |
reference | Yes | - | Target texture (needs copy-dst usage) |
:mip-level |
number | No | 0 |
Destination mip level |
:origin |
array | No | [0 0 0] |
[x y z] in texels; z picks the array layer or depth slice |
:mip-level and :origin belong to the destination, not to the copy: they
say which subresource receives the pixels, which is how a cubemap’s six faces
load into one texture.
(copy-external-image-to-texture (source :image photo) (destination :texture albedo :mip-level 0 :origin [0 0 0]))copy-texture-to-texture
Section titled “copy-texture-to-texture”Copies between GPU textures without a CPU round trip, which is how a frame keeps a copy of itself for feedback. Both ends are child forms here too, and each takes one key:
| Sub-form | Key | Type | Required | Description |
|---|---|---|---|---|
(source …) |
:texture |
reference | Yes | Source texture, or context-current-texture |
(destination …) |
:texture |
reference | Yes | Destination texture, or context-current-texture |
context-current-texture names the canvas’s current texture; any other value
is a (texture …) name. Neither texture copy takes a size: PNGine copies the
whole source.
copy-buffer-to-buffer
Section titled “copy-buffer-to-buffer”Copies bytes between two GPU buffers without a CPU round trip.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
:source |
reference | Yes | - | Source buffer (needs copy-src usage) |
:source-offset |
number | No | 0 |
Byte offset into the source |
:destination |
reference | Yes | - | Destination buffer (needs copy-dst usage) |
:destination-offset |
number | No | 0 |
Byte offset into the destination |
:size |
number | Yes | - | Bytes to copy |
All three of :source-offset, :destination-offset and :size must be
multiples of 4.
(queue :name stage (copy-buffer-to-buffer :source resolveBuf :source-offset 0 :destination readbackBuf :destination-offset 0 :size 48))resolve-query-set
Section titled “resolve-query-set”Copies resolved GPU query results into a buffer, 8 bytes per query.
| Key | Type | Required | Default | Description |
|---|---|---|---|---|
:query-set |
reference | Yes | - | The (query-set …) to resolve |
:first-query |
number | No | 0 |
Index of the first query to resolve |
:query-count |
number | Yes | - | Number of queries to resolve |
:destination |
reference | Yes | - | Destination buffer (needs query-resolve usage) |
:destination-offset |
number | No | 0 |
Byte offset into the destination |
WebGPU requires :destination-offset to be a multiple of 256 here.
(queue :name resolve (resolve-query-set :query-set occ :first-query 0 :query-count 6 :destination resolveBuf :destination-offset 0))Built-in Data Sources
Section titled “Built-in Data Sources”Three reserved symbols name runtime data the player fills in each frame. Any of
them is a write-buffer :data value, and none of them can be declared as a
form name:
| Symbol | Size | Contents (all f32) |
|---|---|---|
pngine-inputs |
16 bytes | time, canvasWidth, canvasHeight, aspect |
scene-time-inputs |
12 bytes | time, canvasWidth, canvasHeight (the first 12 bytes of pngine-inputs) |
pointer-inputs |
48 bytes | x, y, clickX, clickY, dx, dy, buttons, pressure, modifiers, scrollX, scrollY, _pad |
pngine-inputs
Section titled “pngine-inputs”The pngine-inputs symbol provides runtime data that updates every frame. It
writes 16 bytes:
| Field | Type | Offset | Description |
|---|---|---|---|
time |
f32 | 0 | Elapsed seconds since start |
width |
f32 | 4 | Canvas width in pixels |
height |
f32 | 8 | Canvas height in pixels |
aspect |
f32 | 12 | width / height |
Your uniform buffer must be at least 16 bytes when using pngine-inputs.
Your WGSL struct must match this layout:
struct PngineInputs { time: f32, canvasWidth: f32, canvasHeight: f32, aspect: f32,}@group(0) @binding(0) var<uniform> inputs: PngineInputs;Examples
Section titled “Examples”Write Runtime Uniforms (pngine-inputs)
Section titled “Write Runtime Uniforms (pngine-inputs)”(buffer :name uniforms :size 16 :usage [uniform copy-dst])
(queue :name writeTime (write-buffer :buffer uniforms :offset 0 :data pngine-inputs))
(frame :name main :perform [writeTime renderPass])Write Static Data
Section titled “Write Static Data”(data :name vertexData :float32 [0 0.5 -0.5 -0.5 0.5 -0.5])
(buffer :name vertices :size 24 :usage [vertex copy-dst])
(queue :name initVertices (write-buffer :buffer vertices :data vertexData))Load Texture from Image
Section titled “Load Texture from Image”(data :name imageFile :file "texture.png" :mime "image/png")
(image-bitmap :name textureBitmap :data imageFile)
(texture :name colorMap :size textureBitmap :format rgba8unorm :usage [texture-binding copy-dst render-attachment])
(queue :name loadTexture (copy-external-image-to-texture (source :image textureBitmap) (destination :texture colorMap)))
(frame :name main :perform [loadTexture renderPass])Write a WASM Result
Section titled “Write a WASM Result”(wasm-call :name mvp :file "matrices.wasm" :func rotatingMVP :args [time-total canvas-width canvas-height] :returns "mat4x4")
(buffer :name mvpBuffer :size 64 :usage [uniform copy-dst])
(queue :name writeMVP (write-buffer :buffer mvpBuffer :offset 0 :data mvp))Copy Between Textures
Section titled “Copy Between Textures”(texture :name sourceTexture :size canvas :format rgba8unorm :usage [render-attachment copy-src])(texture :name targetTexture :size canvas :format rgba8unorm :usage [texture-binding copy-dst])
(queue :name copyResult (copy-texture-to-texture (source :texture sourceTexture) (destination :texture targetTexture)))Frame Integration
Section titled “Frame Integration”Queue operations run when a (frame …) names them, in :perform alongside the
passes:
(frame :name main :perform [writeUniforms renderPass])or in :before, which runs its queues ahead of every frame’s passes:
(frame :name main :before [writeUniforms] :perform [renderPass])A frame’s :init list is for one-shot compute steps: it takes
(compute-pass …) and (init …) names only, so a queue named there is a
not_cross_ref. A queue op runs every frame, which for an image upload is
wasteful but correct.
Validation Rules
Section titled “Validation Rules”| Rule | Error |
|---|---|
| A name is unique across every form kind | duplicate_cross_ref_target |
| Referenced buffers, textures, query sets and data must exist | not_cross_ref |
write-buffer’s :data names a built-in, a (data …) or a (wasm-call …) |
union_no_branch_matched |
write-buffer’s :offset, and copy-buffer-to-buffer’s offsets and :size, are multiples of 4 |
number_not_multiple |
resolve-query-set’s :destination-offset is a multiple of 256 |
number_not_multiple |
| An action head the form does not accept | not_head_member |
Exactly one (source …) and one (destination …) per copy |
positional_missing / positional_too_many |
Usage is the compiler’s rule rather than the schema’s, because a buffer’s
usage is fixed when it is created. It reports on the (buffer …):
buffer 'src' is the :source of a copy but its :usage has no copy-srcWebGPU Mapping
Section titled “WebGPU Mapping”Maps to queue operations:
// write-buffer (static data)device.queue.writeBuffer(buffer, offset, data);
// write-buffer (pngine-inputs) - runtime provides datadevice.queue.writeBuffer(buffer, offset, new Float32Array([time, width, height, aspect]));
// copy-external-image-to-texture: (source …) and (destination …) are the// two dictionaries this call already takesdevice.queue.copyExternalImageToTexture( { source: imageBitmap }, { texture: gpuTexture, mipLevel: 0, origin: [0, 0, 0] }, [width, height]);Related
Section titled “Related”(data …)- Define static data to write(buffer …)- Target buffers(texture …)- Target textures(wasm-call …)- Compute data with WASM(query-set …)- The query setsresolve-query-setreads(frame …)- Execute queue operations