ReadonlycolorThe colour attachment, as a texture. Stable for the target's lifetime.
ReadonlyidStable identity (§83), assigned at construction from a monotonic counter
and formatted render-target-<n>. Unique within a process, ascending in
construction order, never reused.
Bytes this target's attachments describe (§83, §84's textureMemory).
Four bytes per texel for the "rgba8" colour attachment, plus the depth
attachment when RenderTarget.depth is on: two per texel for a
DEPTH_COMPONENT16 renderbuffer, four for a DEPTH_COMPONENT24
texture (R-18 — 24-bit depth occupies a 32-bit texel), and four for
the packed DEPTH24_STENCIL8 renderbuffer RenderTarget.stencil
asks for (R-7 — 24 bits of depth and 8 of stencil in one 32-bit texel, so
a stencil is two bytes per texel on top of the plain depth buffer, not
one). All three formats are quoted from what @fourjs/render-webgl's
gl-render-target.ts actually allocates, which is the difference between
accounting and guessing. The constants move together when §62's
floating-point formats widen RenderTargetFormat.
new RenderTarget({ width: 512, height: 512 }).byteLength; // 512*512*6
new RenderTarget({ width: 512, height: 512, depth: false }).byteLength;
// 512*512*4
new RenderTarget({ width: 512, height: 512, depthTexture: true }).byteLength;
// 512*512*8
new RenderTarget({ width: 512, height: 512, stencil: true }).byteLength;
// 512*512*8
0 once disposed — one rule with Texture.byteLength: a disposed
resource holds nothing.
The colour space of the attachment's texels (§60a) — see RenderTargetOptions.colorSpace. Fixed at construction, like RenderTarget.format: a surface does not change what its bytes mean halfway through a frame graph.
Whether the target carries a depth buffer — see RenderTargetOptions.depth. Fixed at construction: changing it means a different framebuffer, and re-creating the target says so plainly.
Whether the depth attachment is a samplable texture rather than a renderbuffer (§69) — see RenderTargetOptions.depthTexture. Fixed at construction, exactly as RenderTarget.depth is: the two are one decision about what the framebuffer is.
Whether RenderTarget.dispose has run.
Colour format of the attachment; "rgba8" in this tier (§62).
Height of the colour attachment in texels.
Whether the target carries a stencil buffer (§67) — see RenderTargetOptions.stencil. Fixed at construction, exactly as RenderTarget.depth and RenderTarget.depthTexture are: the three are one decision about what the framebuffer is.
Counter incremented on every mutation. Backends key their framebuffer
allocation on RenderTarget.id and validate it against this;
treat it as opaque and compare for inequality, exactly like
Texture.version.
Width of the colour attachment in texels.
Releases this target (§83). Idempotent and terminal.
There is nothing CPU-side to free — the framebuffer, its colour texture and
its depth buffer all belong to the backend — so this marks the target
disposed and bumps the version, which is exactly what makes the backend
delete them on the next frame that meets this target. A backend that meets
a disposed target skips it: drawing into a released framebuffer, or
sampling one, would paint undefined content rather than report the mistake
(the same rule Texture.dispose states).
Materials pointing at RenderTarget.colorTexture are not notified; ownership is explicit and upwards (§83).
It does remove this target and its bytes from the process-wide §83
totals (textureMemoryBytes, liveRenderTargetCount), exactly once: the
idempotence guard above is what makes a double dispose() subtract once
rather than twice.
Changes the size of the colour (and depth) attachment (§85 validated).
The backend re-allocates on the next frame that draws into this target, because the version advances — and the old contents are gone, which is the honest description of a re-allocated framebuffer. A pass that must survive a resize re-renders; there is no resize-preserving blit in this tier.
Resizing to the size it already has still bumps the version, and so still costs a re-allocation. Guard the call if that matters: an application following the canvas usually knows whether the size actually changed, and a no-op here would make "the version advanced" stop meaning "something changed".
Throws after disposal (§83): disposal is terminal, and silently resizing a dead target would hide the mistake.
An off-screen surface a frame can be rendered into, and sampled from afterwards (§61, §48, §63).
Version, not events
idplusversionis the cache contract every fourJS GPU resource offers (BufferGeometry,Texture), and a render target offers it for the same reason: a backend keyed on the pair compares one number per frame instead of holding a subscription per target. RenderTarget.resize and RenderTarget.dispose are the only mutations, and both bump the version — so the backend's framebuffer is re-allocated at the new size on the next frame that draws into it, with nothing for the application to call.Ownership (§83)
A target is shared, not owned by the materials that sample it — the same rule
Texturefollows. Whoever created it disposes it;Material.dispose()deliberately does not. Disposal is idempotent and terminal: a disposed target is skipped by a backend rather than drawn into, exactly as a disposed texture is skipped rather than sampled.Every target reports its size (RenderTarget.byteLength) to the process-wide §83 totals in
resource-memory.ts— at construction, onresize(), and ondispose()— so an off-screen surface that is re-created per resize instead of resized shows up in §84'stextureMemoryas the leak it is (A-5, 2026-08-07).