Drag the canvas corner or change pixel ratio or resolution. A memo lookup each frame replaces the bind group after rebuilding, while a group created once retains the destroyed texture.
A renderer may need multisampled colour, depth, HDR colour and bloom textures that follow the canvas size. A WebGPU texture can’t be resized, so a new size means new textures, views and bind groups. targets.create makes a set of textures that share a size, and fit() replaces the whole set when the size changes.
Caches the last two results of fn, comparing arguments by identity. Calls fn only when
neither cached argument list matches. For example, create a bind group with const group = memo((hdr) => root.createBindGroup(layout, { hdr })), then call group(screen.textures.hdr)
each frame. Alternating between two textures, such as a history pair, calls fn only twice.
Does not destroy cached results.
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
create(
constroot:TgpuRoot
root,
constsurface:web.Surface
surface,{
scene:{
readonly format:"rgba16float";
readonly samples:4;
}
scene:{
format:"rgba16float"
format:'rgba16float',
samples:4
samples:4},// transient
depth:{
readonly format:"depth24plus";
readonly samples:4;
}
depth:{
format:"depth24plus"
format:'depth24plus',
samples:4
samples:4},// transient
hdr:{
readonly format:"rgba16float";
readonly sampled:true;
}
hdr:{
format:"rgba16float"
format:'rgba16float',
sampled:true
sampled:true},
bloom:{
readonly format:"rgba16float";
readonly scale: number;
readonly mips:5;
readonly sampled:true;
}
bloom:{
format:"rgba16float"
format:'rgba16float',
scale: number
scale:1/2,
mips:5
mips:5,
sampled:true
sampled:true},
blur:{
readonly format:"rgba16float";
readonly scale: number;
readonly sampled:true;
}
blur:{
format:"rgba16float"
format:'rgba16float',
scale: number
scale:1/4,
sampled:true
sampled:true},
});
// Runs again only when `hdr` is a new texture
const
consthdrGroup:(hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
hdr:{
texture:d.WgslTexture2d<d.F32>;
};
}>
hdrGroup=
memo<readonly [hdr: targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>],TgpuBindGroup<{
hdr:{
texture:d.WgslTexture2d<d.F32>;
};
}>>(fn:(hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
hdr:{
texture:d.WgslTexture2d<d.F32>;
};
}>):(hdr:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
hdr:{
texture:d.WgslTexture2d<d.F32>;
};
}>
Caches the last two results of fn, comparing arguments by identity. Calls fn only when
neither cached argument list matches. For example, create a bind group with const group = memo((hdr) => root.createBindGroup(layout, { hdr })), then call group(screen.textures.hdr)
each frame. Alternating between two textures, such as a history pair, calls fn only twice.
Does not destroy cached results.
Rebuilds all textures if the source size changed, destroying the old textures. Updates
regions for the current resolution. Returns true after a rebuild so callers can update bind
groups that hold old textures. Call before recording passes that use the targets. Throws after
destroy.
fit();// new textures if the canvas changed size
const
constscene:TgpuRenderPass
scene=
frame:frames.Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
's
depth component after executing the render pass.
depthStoreOp:'discard'},
});
// draw the scene
constscene:TgpuRenderPass
scene.
TgpuRenderPass.end(): void
Completes the recording of this render pass
end();
const
constfinal:TgpuRenderPass
final=
frame:frames.Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
Each key names a target in textures, views, levels and layers.
Field
Default
Meaning
format
Texture format.
scale
1
Fraction of the set’s size; each side is rounded up and at least 1.
samples
1
MSAA sample count. A multisampled target can’t be mipped or stored.
mips
1
Mip levels, at most as many as the size has. levels.name[i] views level i.
sampled
false
Also bindable as a sampled texture.
storage
false
Also bindable as a storage texture, in a format TypeGPU binds as one, such as rgba16float.
transient
true when multisampled and not sampled
Cleared and discarded in each pass: memoryless where the browser has GPUTextureUsage.TRANSIENT_ATTACHMENT, a plain attachment elsewhere.
layers
none
Layers of a 2D array texture, one sample and one mip each. layers.name[k] views layer k. Passes draw a pass into each.
A 16 × 9 target has five mip levels, so levels.bloom can be shorter than the mips asked for. Loop over levels.
create throws before making any texture for a transient target that is sampled, stored or mipped, for a multisampled target with mips or storage, and for a target with layers that is multisampled or mipped or has more layers than the device’s maxTextureArrayLayers. It checks the mips you asked for, not the clamped count. The types already refuse storage on a format with no storage binding, such as depth24plus.
A target only sometimes needed, like a depth-of-field buffer, goes in a set of its own, created when the feature turns on. There is no enabled flag.
targets.Depth types the depth that TAA, depth of field and ambient occlusion read: single-sampled and sampled, in depth16unorm, depth24plus or depth32float.
Take a pipeline’s format from screen.textures.name.props.format, and its sampleCount from the same props. Literals break only at the first draw after the spec changes.
A transient target is cleared, discarded and never read after its pass, such as MSAA colour or depth. Where memoryless attachments are available on tile-based GPUs, it uses tile memory during the pass without video-memory storage. Elsewhere, discarding the target still avoids storing its samples. Transience is a texture usage and needs no device feature request. screen.memoryless reports support.
Multisampled targets are transient by default unless sampled or stored. Other targets require transient: true. screen.transient.scene reports whether scene is transient.
A pass into a transient target clears it and uses storeOp: 'discard', so only what it resolves outlives the pass.
begin throws when a pass would keep one, as a second pass that draws over the first does. Give that target transient: false:
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
fit() compares the source’s size with the set’s. When it changed, it destroys every texture, creates new ones and returns true.
Call it before recording passes that use the targets. A pass recorded with a destroyed texture fails at submit.
Sizes round up to whole pixels, at least 1 and at most the device’s maxTextureDimension2D.
create fits once, so the textures exist before the first frame.
The source can be any SizeSource, such as a canvas surface, plain object or fixed-height getter. fit() reads it each time. A plain object of fixed size makes a texture of your own to draw into.
destroy(), or [Symbol.dispose] with using, destroys the textures. fit(), textures, views and levels then throw The target set was destroyed.
A cached bind group still refers to the texture it was created with after that texture is destroyed. memo(fn) reruns fn when an argument’s identity changes. Call it each frame with the current texture to create a group only when the texture changes. It also works with textures replaced by other modules.
Up to four arguments, so a call allocates nothing.
It keeps its last two results, so arguments that alternate between two textures run fn only twice per rebuild.
It destroys nothing; keep it for bind groups and the like.
levels.name is a new array per rebuild, so it works as an argument, as bloom keys its per-level groups and passes on chain.levels.glow.
A temporal effect, such as a cloud history, writes one target of a pair while it reads the other, and swaps each frame. One memo holds both groups:
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
create(
constroot:TgpuRoot
root,
constsurface:web.Surface
surface,{
a:{
readonly format:"rgba16float";
readonly sampled:true;
}
a:{
format:"rgba16float"
format:'rgba16float',
sampled:true
sampled:true},
b:{
readonly format:"rgba16float";
readonly sampled:true;
}
b:{
format:"rgba16float"
format:'rgba16float',
sampled:true
sampled:true},
});
const
consthistoryOf:(history:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
history:{
texture:d.WgslTexture2d<d.F32>;
};
}>
historyOf=
memo<readonly [history: targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>],TgpuBindGroup<{
history:{
texture:d.WgslTexture2d<d.F32>;
};
}>>(fn:(history:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
history:{
texture:d.WgslTexture2d<d.F32>;
};
}>):(history:targets.Texture<{
readonly format:"rgba16float";
readonly sampled:true;
}>)=>TgpuBindGroup<{
history:{
texture:d.WgslTexture2d<d.F32>;
};
}>
Caches the last two results of fn, comparing arguments by identity. Calls fn only when
neither cached argument list matches. For example, create a bind group with const group = memo((hdr) => root.createBindGroup(layout, { hdr })), then call group(screen.textures.hdr)
each frame. Alternating between two textures, such as a history pair, calls fn only twice.
Does not destroy cached results.
Rebuilds all textures if the source size changed, destroying the old textures. Updates
regions for the current resolution. Returns true after a rebuild so callers can update bind
groups that hold old textures. Call before recording passes that use the targets. Throws after
destroy.
fit();
// Even frames write a and read b, odd frames the other way
const
consteven:boolean
even=
frame:frames.Frame
frame.
Frame.index: number
Counts frames submitted before this one.
index%2===0;
const
constpass:TgpuRenderPass
pass=
frame:frames.Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
The resolution option sets the fraction of each side to draw, read on every fit(). Textures keep their full source size, so changing resolution during play allocates nothing. Passes use a viewport to draw within each target’s region. Bloom and finish read only that region:
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
create(
constroot:TgpuRoot
root,
constsurface:web.Surface
surface,
{
hdr:{
readonly format:"rgba16float";
readonly sampled:true;
}
hdr:{
format:"rgba16float"
format:'rgba16float',
sampled:true
sampled:true},
depth:{
readonly format:"depth24plus";
}
depth:{
format:"depth24plus"
format:'depth24plus'},
},
{
Options.resolution?: number |(()=> number)|undefined
Sets the fraction of each side to draw, capped at 1 and read at each fit(). Textures keep
the source size, so changing resolution allocates nothing. Passes use viewports for the drawn
regions, and sampling reads only those regions. Default 1.
Reports each drawn region from the top-left corner of its target. Multiplies target size by
resolution and rounds up. fit() updates the same objects for the lifetime of the set.
Creates bloom from a chain of half-size textures. Each level downsamples with 13 taps, then a
tent filter adds it to the next larger level. The result is mip 0 of output, at half the
source size. With region, passes draw and sample only that region; changing the region
allocates nothing.
@allocates ― on resize: a new chain and the bind groups of its passes; so does a new source
@owns ― targets, a sampler, 3 pipelines, 2 bind groups and a buffer per level: the chain of glow
levels, the settings, a group for each pass, and the region each pass reads
@records ― record after whatever wrote the source; then output holds the glow.
@param ― options.source the HDR picture, read at every record
@param ― options.region the part of the source that holds the picture, read at every record
@returns ― a bloom you record every frame and destroy
Provides the HDR picture: a rendered picture, such as aa, or a function returning a
texture, such as () => screen.textures.hdr. record reads it on every call, so the texture
can be rebuilt.
source,
Options.region?:targets.SizeSource|undefined
Selects the source region from its top-left corner, such as regions.hdr when drawing below
full resolution. record reads it on every call. Default the region of a rendered source,
else the whole texture.
Creates the final pass of a frame. It reads the source, adds glow, tone maps, grades, applies
the vignette, encodes and dithers. It draws one triangle into the caller pass.
@allocates ― on resize: a bind group for a new source or glow
@owns ― 2 buffers, a sampler, a pipeline, 2 bind groups: the settings, the regions it reads, and a
group each for the source and the glow
@records ― record into a pass of its own on the view you give, usually the canvas's, or draw
into the caller's pass, after the bloom's record.
@param ― options.source the picture, rendered or a texture, read at every draw
@param ― options.region the part of the source that holds the picture, read at every draw
@param ― options.glow a bloom, or a glow of your own, added before tone mapping
@param ― options.exposure an adapted exposure, read on the GPU
@returns ― a final pass you draw every frame and destroy
create(
constroot:TgpuRoot
root,{
Options.format: GPUTextureFormat
Sets the output format, such as the canvas format.
Provides the picture at any size: a rendered picture, such as aa or blur, or a function
returning a texture, such as () => screen.textures.hdr. draw reads it on every call, so
the texture can be rebuilt.
source,
Options.region?:targets.SizeSource|undefined
Selects the source region from its top-left corner, such as regions.hdr when drawing below
full resolution. draw reads it on every call. Default the region of a rendered source,
else the whole texture.
region,
Options.glow?:finish.Rendered|undefined
Adds a bloom, or a glow of your own such as an emission target, before tone mapping. draw
reads its output and region on every call. Default none.
Rebuilds all textures if the source size changed, destroying the old textures. Updates
regions for the current resolution. Returns true after a rebuild so callers can update bind
groups that hold old textures. Call before recording passes that use the targets. Throws after
destroy.
fit();// takes the resolution into the regions
const
constscene:TgpuRenderPass
scene=
frame:frames.Frame
frame.
Frame.encoder: TgpuCommandEncoder
Records commands for submission after the recorder returns. submit() replaces this encoder.
Read it at each call instead of keeping it, and let the frame submit it.
Opens a pass on view, such as surface.view(frame), draws into it and ends it. A
viewport draws the picture into that rectangle and clears the rest, transparent with
alpha: 'premultiplied' and black otherwise. Optional timer writes measure the pass.
record(
frame:frames.Frame
frame,
constsurface:web.Surface
surface.
Surface.view(frame: frames.Frame): GPUTextureView
Returns a view of the current canvas texture, created once per texture. Applies pending size
changes first, which a pass using context would skip.
view(
frame:frames.Frame
frame));
});
regions.name describes the drawn region from the target’s top-left corner. Its dimensions are the target size multiplied by resolution, rounded up. The objects stay the same for the life of the set, and fit() updates them.
A clear clears the whole texture; the viewport limits only the drawing.
A shader of your own that samples a target reads uv * region / size, kept half a texel inside the region, as bloom and finish do.
The resolution is at most 1.
dynamicResolution picks the resolution that keeps the frame under a GPU budget.
For a resolution that stays put, targets.scaled(source, scale) is a smaller SizeSource that sets and cameras can share. Its textures take less memory, and a new scale makes new ones at the next fit(). Both of these scale the whole set; a spec’s scale is fixed per target.
views.name (mip 0) and levels.name (one per mip) are attachment views of one mip level each, made once per rebuild. Use these for attachments. TypeGPU’s default texture view covers every mip level, which an attachment can’t accept. textures.name is the TypeGPU texture, for bind groups and other views.
layers.name holds an attachment view of each layer, and of the one 2D texture for a target without layers. views.name is layer 0. To sample every layer, view the whole array:
Creates render targets that follow a size. Replaces the whole set when the size changes. Put
targets needed only sometimes in their own set and create that set when needed. Throws for
transient targets with sampling, storage or mip levels, for multisampled targets with storage or
mip levels, and for layered targets that are multisampled, mipped or have more layers than the
device allows.
@param ― source the size it follows, such as a surface
@param ― specs a format and options per target, by name
@param ― options.resolution the fraction of each side drawn, which changes without allocating
@returns ― a set of targets you fit every frame and destroy
Nothing waits for the size to settle, so a drag-resize rebuilds the set in every frame the size changes. A rotation is one rebuild.
Two sets that follow the same source fit and rebuild separately.
A spec, such as samples, is fixed when the set is made; fit() replaces textures and keeps the specs. Another sample count is another set, and a new description of passes with new pipelines.
A set created when a feature turns on allocates its textures in that frame, mid-game.
On React Native, transient targets are plain attachments, and memoryless reports it. Budget memory for their multisampled colour and depth.
Set storeOp: 'discard' yourself when opening a pass into a transient target outside begin. Memoryless targets reject other store operations. Reading a discarded plain attachment in a later pass or copy produces no validation error.
Only single-mip attachment views are made. A sampled view of a range of mips is yours to make and keep, with memo if it follows the set.
With resolution, the textures take the full size’s memory at any resolution.
WebGPU has no view of a rectangle, so whatever samples a target crops in the shader. A shader of your own that samples with a full-texture uv reads past the region, where the texture holds an older frame or the clear colour.