Passes & resources

Connect graphics and compute work with explicit inputs, outputs, history, and storage buffers.

Sulkan 0.5.1 · Minecraft 26.3 · Native format 1

Graphics passes

{
  "name": "exposure",
  "fragment": "shaders/exposure.fsh",
  "reads": { "Scene": "$scene" },
  "writes": ["display"]
}

Each pass needs name, reads, writes, and exactly one of fragment or compute. Graphics uses a generated fullscreen triangle, with vec2 texture coordinates at location 0. Output locations follow writes: 1–8 equal-dimension images, cleared before drawing.

Declare graphics samplers with reads keys; Sulkan manages their bindings.

Input sources

SourceMeaning
$sceneColor before native post-processing
$depthWorld depth before the hand clear
$handDepthSubsequent main depth, including the hand
$terrainOpaqueColor immediately before Sodium translucent terrain
$terrainOpaqueDepthCorresponding terrain-boundary depth
$texture/nameDeclared texture asset
resource.nameCurrent-frame graph image
resource.name@previousPrevious image; requires history

Sulkan…, Output0, and gl_… are reserved binding names. Terrain snapshots preserve one terrain boundary, not a second full world render or every offscreen transparency target.

Scheduling and history

Each post-scene image has one writer. Reads establish dependencies; declaration order does not. Use new names for successive outputs. Current-frame feedback and cycles fail. Unused passes are culled from the presentation root.

Scene targets cannot also have post writers. Compatible transient images with disjoint lifetimes may share storage; history and scene targets have dedicated storage.

Previous images/buffers start zeroed. Resize, world change, large camera motion/rotation, FOV change, and long frame gaps invalidate history. Honor CameraDeltaAndHistoryValid.w. History swaps only after successful submission.

Compute passes

Add a matching display resource and set present to it in the full manifest:

{
  "name": "copy",
  "compute": "shaders/copy.csh",
  "localSize": [8, 8, 1],
  "reads": { "Source": "$scene" },
  "writes": ["display"]
}
#version 450
void main() {
    ivec2 pixel = ivec2(gl_GlobalInvocationID.xy);
    ivec2 extent = imageSize(Output0);
    if (any(greaterThanEqual(pixel, extent))) return;
    vec2 uv = (vec2(pixel) + 0.5) / vec2(extent);
    imageStore(Output0, pixel, texture(Source, uv));
}

Sulkan generates the frame block, samplers, Output0 storage image, and local workgroup declaration. Do not redeclare them, including through the frame include.

Default localSize is [8,8,1]. Dispatch defaults to the first output’s dimensions; dispatch: [x,y,z] gives explicit invocation extents. It rounds up to complete workgroups: always bounds-check.

Storage formats include RGBA8_UNORM, RGBA16_FLOAT, RGBA32_FLOAT, R32_FLOAT, R32_UINT, R16_UINT, R8_UINT, R32_SINT, RGBA32_UINT, and RGBA32_SINT, subject to device support.

Storage buffers

Declare buffers at root, then bind them in compute:

{
  "buffers": { "lighting.blocks": { "bytes": 256, "history": true } }
}
{
  "name": "fill",
  "compute": "shaders/fill.csh",
  "localSize": [16, 1, 1],
  "dispatch": [64, 1, 1],
  "reads": {},
  "writes": [],
  "buffers": { "Blocks": { "resource": "lighting.blocks", "access": "write" } }
}

The shader sees Blocks.data, an unsized std430 uint[]. Use bit conversions for packed floats. Sizes are multiples of 4, from 4 bytes to 512 MiB. At most 64 buffers are allowed.

access is read or write. Write permits intra-dispatch reads and atomics and starts zeroed each frame. Each current buffer has one writer. Reads can reference @previous if history is enabled.

Buffer-only passes require explicit dispatch and a buffer writer. Connect that buffer to a later pass that contributes to present, or the writer is culled. No cross-workgroup synchronization is supplied: split dependent work into separate passes.

Conditional and filtered reads

readConditions omits bindings when options are disabled. Lists are OR clauses; nested lists are AND clauses. Positive option values count as enabled.

{
  "readConditions": { "Bloom": ["BLOOM", ["WATER", "REFLECTIONS"]] },
  "linearReads": ["Bloom"]
}

Declare the sampler in reads and options at root. Guard sampling with matching GLSL defines when bindings can disappear. linearReads accepts eligible graph color resources, not arbitrary external inputs.