tecs.gpu.passes

The render passes and render targets a frame is drawn through.

A pass names the targets it reads and the targets it writes. This module owns the declaration; the Rust backend owns the textures, the pipelines and the submission. The declaration crosses in the frame packet, so there is one copy of every pass and target name rather than one on each side of the boundary.

Passes run in declaration order rather than in a topological order. The order of a deferred pipeline is a design decision, and a graph that quietly reorders itself is harder to reason about than one that refuses to run, so a pass that reads a target no earlier pass writes is rejected where it is declared.

tecs.gpu.passes.declareTarget({name = "gameOverlay", format = "rgba8"})
tecs.gpu.passes.insertPassBefore("present", {
    name = "gameOverlay",
    inputs = {"scene"},
    outputs = {"gameOverlay"},
})

The built-in deferred graph is declared when this module loads. Its target and pass names are a compatibility surface: a game names one to place a pass of its own beside it, so they keep their established spelling wherever the code that declares them lives.

Targets are sized with the frame. A scale below one halves or quarters both axes, which is what a target holding nothing sharp asks for. The backend owns allocation and reuse: it reallocates a target only when the frame size or the declaration changes, and a target no pass reads or writes is still allocated, because a pass may be declared for it later.

One depth attachment at frame size is shared by every pass that asks for depth. depth is reserved as an input name and reads that attachment; it is not a declarable target.

The built-in graph declares the shadow and bloom passes whether or not tecs.gfx.lighting has them switched on, and the frame packet's own flags are what decide each frame. Declaring them conditionally would mean tearing the graph down and rebuilding it when a game changes its tuning, which would take every pass a game inserted with it; a backend skipping a pass whose flag is clear costs nothing and keeps every name a game placed something beside valid for the life of the process.

Built-in scope#

Custom passes supply WGSL defining postprocess(uv: vec2<f32>) -> vec4<f32>. The backend supplies scene, passSampler, the declared input0, input1, and subsequent textures, and params.values, four vectors of four scalar parameters. A depth input is a texture_depth_2d. Use setParameters for animated tuning; this updates a uniform without recompiling the pipeline. A custom pass writes one color output, or the window when outputs is empty. A pass without a shader uses its built-in body or only clears its outputs.

The Tiled subsystem owns map parsing, tilesets, edits and static TileChunks. Its chunks and animated sprites draw through this render-pass graph.

Each explicit tecs.gfx.View runs this graph at its viewport resolution. The renderer composites those results in view order. With no explicit views, the active camera draws one full-frame view.

Module contents

Types

TypeKindDescription
CleartypeA color a target or a pass clears to.
DepthModetypeSays how a pass uses the shared depth attachment.
FormattypeSelects the pixel format the backend allocates a target in.
PassrecordOne render pass, as the graph holds it.
PassSpectypeWhat a caller passes to declarePass or insertPassBefore.
TargetrecordA target the backend allocates, sizes with the frame, and owns.
TargetSpectypeWhat a caller passes to declareTarget.

Functions

FunctionKindDescription
declarePassfunctionDeclares a pass that runs after every pass declared so far.
declareTargetfunctionDeclares a target the backend allocates and owns.
formatOffunctionReturns the format a target was declared with.
insertPassBeforefunctionDeclares a pass that runs immediately before an already declared one.
passesfunctionReturns the declared passes in execution order.
resetfunctionForgets every declaration and declares the built-in graph again.
revisionfunctionReturns how many declarations have been made.
sectionfunctionEncodes the graph for the frame packet.
setParametersfunctionChanges one pass's uniform parameters without recompiling its pipeline.
setShaderfunctionReplaces a pass's custom WGSL body while preserving its inputs and outputs.
targetsfunctionReturns the declared targets in declaration order.

Values

ValueKindDescription
DEPTHvariableNames the shared depth attachment, which is not a declarable target.

Types#

Cleartype#

type Clear = {
    r: number,
    g: number,
    b: number,
    a: number
}

A color a target or a pass clears to.

Every channel runs zero to one and must be finite.

DepthModetype#

type DepthMode = "none" | "testWrite" | "test"

Says how a pass uses the shared depth attachment.

none attaches no depth, so nothing rejects a fragment and nothing is written. testWrite is what geometry asks for. test rejects a fragment against what geometry already wrote without writing itself, which is what blended content that must sort against opaque geometry asks for.

Formattype#

type Format = "rgba8" | "rgba16f" | "r8" | "r16f"

Selects the pixel format the backend allocates a target in.

These identifiers reach the wire and a game's own declarations, so they are a compatibility surface.

Passrecord#

record Pass
    shader: string?
    parameters: {number}
    name: string
    inputs: {string}
    outputs: {string}
    depth: DepthMode
    depthClear: number?
    load: boolean
    clear: Clear?
end

One render pass, as the graph holds it.

Fields

shader#
shader: string?

Read-only. Supplies WGSL defining postprocess(uv), or nil for a built-in body.

parameters#
parameters: {number}

Read-only. Supplies up to sixteen scalar shader parameters.

name#
name: string

Read-only. Names the pass, unique within the graph.

inputs#
inputs: {string}

Read-only. Lists the targets this pass samples, in binding order. The list is the graph's and a caller must not write to it.

outputs#
outputs: {string}

Read-only. Lists the targets this pass writes. An empty list renders to the swapchain, which is how the final composite reaches the screen.

depth#
depth: DepthMode

Read-only. Reports how the pass uses the shared depth attachment.

depthClear#
depthClear: number?

Read-only. Reports the value the depth attachment is cleared to when the pass begins, or nil to load what is already there. One is the far plane.

load#
load: boolean

Read-only. Reports true when the pass loads every output rather than clearing it, whatever the targets themselves declare.

clear#
clear: Clear?

Read-only. Reports the color this pass clears every output to, winning over what the targets declare. Nil takes each target's own clear.

PassSpectype#

type PassSpec = {
    shader: string?,
    parameters: {number}?,
    name: string,
    inputs: {string}?,
    outputs: {string}?,
    depth: DepthMode?,
    depthClear: number?,
    load: boolean?,
    clear: Clear?
}

What a caller passes to declarePass or insertPassBefore.

The module copies every field and every list, so the tables a caller builds are theirs to reuse. inputs names the targets the pass samples, in binding order, and tecs.gpu.passes.DEPTH among them reads the shared depth attachment. outputs names the targets it writes, and an omitted or empty list renders to the swapchain. depth defaults to "none" and depthClear omitted loads what is already there. load makes the pass load every output rather than clear it, and clear overrides every output's own clear with one color.

Targetrecord#

record Target
    name: string
    format: Format
    scale: number
    clear: Clear?
end

A target the backend allocates, sizes with the frame, and owns.

Fields

name#
name: string

Read-only. Names the target for inputs, outputs, and formatOf.

format#
format: Format

Read-only. Reports the pixel format the backend allocates.

scale#
scale: number

Read-only. Reports the size relative to the frame, above zero and at most one. A half halves each axis.

clear#
clear: Clear?

Read-only. Reports the color a pass writing this target clears it to, or nil when a pass loads what is already there.

TargetSpectype#

type TargetSpec = {
    name: string,
    format: Format?,
    scale: number?,
    clear: Clear?
}

What a caller passes to declareTarget.

The module copies every field, so the table a caller builds is theirs to reuse. format defaults to "rgba8" and scale to one, and an omitted clear loads what is already in the target.

Functions#

declarePassfunction#

function declarePass(spec: PassSpec): nil

Declares a pass that runs after every pass declared so far.

Arguments

NameTypeDescription
specPassSpec

the pass to declare, copied rather than retained

Returns

TypeDescription
nil

Raises

  • when the name is already declared, when an input is undeclared or is not written by an earlier pass, when an output is undeclared, or when a pass using depth writes a target below frame scale

declareTargetfunction#

function declareTarget(spec: TargetSpec): nil

Declares a target the backend allocates and owns.

Nothing is allocated here. The backend sizes the target on the first frame after the declaration reaches it.

Arguments

NameTypeDescription
specTargetSpec

the target to declare, copied rather than retained

Returns

TypeDescription
nil

Raises

  • when the name is already declared, when it is the reserved depth name, when the format is unknown, or when the scale is outside zero exclusive to one inclusive

formatOffunction#

function formatOf(name: string): Format?

Returns the format a target was declared with.

Arguments

NameTypeDescription
namestring

a declared target's name

Returns

TypeDescription
Format?

the format, or nil when nothing carries that name

insertPassBeforefunction#

function insertPassBefore(before: string, spec: PassSpec): nil

Declares a pass that runs immediately before an already declared one.

This is how a game places a pass of its own at a named seam without knowing what else the graph holds.

Arguments

NameTypeDescription
beforestring

the name of the declared pass this one runs before

specPassSpec

the pass to declare, copied rather than retained

Returns

TypeDescription
nil

Raises

  • when no pass carries the before name, and for every reason declarePass raises

passesfunction#

function passes(): {Pass}

Returns the declared passes in execution order.

Returns

TypeDescription
{Pass}

the graph's own list, which a caller reads and must not write

resetfunction#

function reset(): nil

Forgets every declaration and declares the built-in graph again.

A test uses this to start from the engine's own graph. It raises the revision, so a backend rebuilds rather than keeping what it held.

Returns

TypeDescription
nil

revisionfunction#

function revision(): integer

Returns how many declarations have been made.

The backend rebuilds its graph when this changes, so it counts every declaration rather than describing the graph's contents.

Returns

TypeDescription
integer

a value that only ever increases within one process

sectionfunction#

function section(): string

Encodes the graph for the frame packet.

The encoding is cached and rebuilt only when a declaration changes it, so a steady graph costs one string for the life of the process.

The section holds a name table, then the targets, then the passes. Names appear once and every record indexes into the table, because a target name appears once as a declaration and again in every pass that reads or writes it.

Offset Type Field
0 u32 nameCount
4 u32 nameBytes, the padded length of the name blob
8 bytes nameCount NUL-terminated names, padded to four
... u32 targetCount
... ... targetCount records of 32 bytes
... u32 passCount
... ... passCount variable-length records

A target record is nameIndex, format, scale, clearPresent, and four clear channels. A pass record is nameIndex, depthMode, depthClearPresent, depthClear, clearMode, four clear channels, inputCount, outputCount, then that many name indices of each. Name indices count from zero.

Returns

TypeDescription
string

a host-endian section the caller owns

setParametersfunction#

function setParameters(name: string, values: {number}): nil

Changes one pass's uniform parameters without recompiling its pipeline.

Arguments

NameTypeDescription
namestring

The caller supplies the existing custom pass name.

values{number}

The caller supplies at most sixteen finite scalars; omitted lanes are zero.

Returns

TypeDescription
nil

Raises

  • When the pass has no custom shader or any scalar is invalid.

setShaderfunction#

function setShader(name: string, source: string): nil

Replaces a pass's custom WGSL body while preserving its inputs and outputs.

Arguments

NameTypeDescription
namestring

The caller supplies an existing pass name.

sourcestring

The caller supplies WGSL defining postprocess(uv: vec2) -> vec4.

Returns

TypeDescription
nil

Raises

  • When the source is empty, too large, or the pass writes multiple outputs.

targetsfunction#

function targets(): {Target}

Returns the declared targets in declaration order.

Returns

TypeDescription
{Target}

the graph's own list, which a caller reads and must not write

Values#

DEPTHvariable#

const DEPTH: string

Names the shared depth attachment, which is not a declarable target.

A pass naming this as an input samples what an earlier depth-writing pass produced. This string is a compatibility surface.