vgpu5 symbolsView source ↗

Symbols in this topic

Target

Offscreen render target abstraction used by passes, draws, bundles, and ping-pong resources. Targets own size, color formats, optional depth, MSAA resolve textures, and readback. Canvas-backed targets are Surface instances created with surface(gpu, canvas).

Import

TypeScript
1
import type { Target, TargetOptions, TargetTextureOptions, PingPongTargets, PingPongStorage } from "vgpu";

Signature

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
import type { ClearColor } from "vgpu";
import type { ResourceDestroyCallback, ResourceIdentity, Texture, UnsubscribeResourceDestroy } from "vgpu/core";
 
interface TargetTextureOptions {
  readonly format?: GPUTextureFormat;
  readonly colors?: readonly { readonly format: GPUTextureFormat }[];
  readonly depth?: boolean | GPUTextureFormat;
  readonly msaa?: boolean | 4;
  readonly label?: string;
}
 
interface TargetOptions extends TargetTextureOptions {
  readonly size: readonly [number, number];
}
 
interface Target {
  readonly gpu: unknown;
  readonly size: readonly [number, number];
  readonly texelSize: readonly [number, number];
  readonly color: Texture;
  readonly colors: readonly [Texture, ...Texture[]];
  readonly depth?: Texture;
  readonly format: GPUTextureFormat;
  readonly sampleCount: 1 | 4;
  readonly resourceIdentity: ResourceIdentity;
  resize(size: readonly [number, number]): void;
  read(): Promise<Uint8Array>;
  readFloats(): Promise<Float32Array>;
  onDestroy(cb: ResourceDestroyCallback<Target>): UnsubscribeResourceDestroy;
  renderPassDescriptor(opts?: {
    readonly clear?: ClearColor;
    readonly preserve?: boolean;
    readonly clearDepth?: number;
    readonly clearStencil?: number;
    readonly depthReadOnly?: boolean;
  }): GPURenderPassDescriptor;
}
 
interface PingPongTargets { readonly read: Target; readonly write: Target; swap(): void; }
interface PingPongStorage { readonly read: import("vgpu").StorageBuffer; readonly write: import("vgpu").StorageBuffer; swap(): void; }

Parameters

ParamTypeRequiredDefaultNotes
target.optsTargetOptionsCreates an offscreen target. size is mandatory.
opts.sizereadonly [number, number]Initial offscreen texture size in physical pixels.
opts.formatGPUTextureFormat"rgba8unorm"Used for single-color targets when colors is omitted.
opts.colorsreadonly { format: GPUTextureFormat }[][{ format: opts.format ?? "rgba8unorm" }]Multiple render targets (MRT): one attachment per entry, all written by one pass — the G-buffer layout for deferred shading. target.color is colors[0].
opts.depthboolean | GPUTextureFormatundefinedtrue means "depth24plus"; a string uses that depth format; omitted means no depth. Combined depth-stencil formats such as "depth24plus-stencil8" are supported; stencil-only "stencil8" is rejected.
opts.msaaboolean | 4false / sample count 1Only true or 4 enables MSAA, creating color/depth attachments with sample count 4 and resolving to sampleable .color(s).
opts.clearColorClearColor[0, 0, 0, 1]Default clear color of this target, used by passes that clear without naming one. Writable at runtime as target.clearColor; a pass clear color still wins for that pass. Four finite numbers, or a GPUColor object.
opts.labelstringundefinedPrefix for created texture labels.
target.resize.sizereadonly [number, number]Recreates offscreen textures unless size is unchanged.
target.readNo parameters; reads target.color and returns its raw unpadded texel bytes (4 per texel for rgba8unorm, 8 for rgba16float, 16 for rgba32float). bgra8unorm / bgra8unorm-srgb are supported and swizzled to RGBA, matching canvas preferred formats on platforms such as macOS.
target.readFloatsNo parameters; reads target.color and decodes it to one f32 per component — the HDR readback for rgba16float / rgba32float targets. unorm8 formats decode to [0, 1].
target.onDestroy.cbResourceDestroyCallback<Target>Subscribes to target destruction.
target.renderPassDescriptor.clearClearColor[0, 0, 0, 1]Clear color for all color attachments unless preserve is true. Frame.pass supplies target.clearColor for omitted/true clears and a per-pass color when provided.
target.renderPassDescriptor.preservebooleanfalseOptional implementer hook used by Frame.pass({ clear: false }); when true, color and depth attachments should load existing contents and omit clear values.
target.renderPassDescriptor.clearDepthnumber1Depth clear value used when the pass clears. Frame.pass supplies FramePassOptions.clearDepth; ignored while preserving and on targets without depth.
target.renderPassDescriptor.clearStencilnumber0Stencil clear value used when the pass clears. Frame.pass supplies FramePassOptions.clearStencil; ignored while preserving and on targets whose depth format has no stencil aspect.
target.renderPassDescriptor.depthReadOnlybooleanfalseWhen true, the depth-stencil attachment is built with depthReadOnly: true and omits depthLoadOp/depthStoreOp, as WebGPU requires for read-only aspects; formats with a stencil aspect also set stencilReadOnly: true and omit the stencil ops. Frame.pass supplies FramePassOptions.depthReadOnly.
pingPong.widthnumberFloored and clamped to at least 1.
pingPong.heightnumberFloored and clamped to at least 1.
pingPong.optsTargetTextureOptions{}Texture options for both targets. Size is intentionally not accepted; positional width/height win.
pingPongStorage.bytesnumberCreates two "read-write" storage buffers.

Returns: target(gpu) returns Target; resize() returns void; read() returns Promise<Uint8Array>; readFloats() returns Promise<Float32Array>; renderPassDescriptor(opts?) returns a WebGPU render pass descriptor; pingPong(gpu) returns PingPongTargets; pingPongStorage(gpu) returns PingPongStorage.

Throws: VGPU-CORE-UNSUPPORTED-FORMAT when read() / readFloats() runs on a color format outside the readback table (see Texture); VGPU-TARGET-SIZE-REQUIRED when runtime JS calls target(gpu) without size; VGPU-TARGET-MSAA-INVALID when runtime JS passes an unsupported msaa value (only true / 4 are accepted); VGPU-TARGET-DEPTH-STENCIL-ONLY when depth receives the stencil-only "stencil8" format (stencil-only depth targets are not supported yet); VGPU-RING1-UNSUPPORTED when msaa: true / 4 with rgba16float is used on a Dawn compatibility-mode device; underlying core texture/readback operations can throw native WebGPU validation errors.

Examples

TypeScript
1
2
3
4
5
6
7
8
9
10
11
import { init, effect, frame, target } from "vgpu/mock";
 
const gpu = await init();
const scene = target(gpu, { size: [128, 128], format: "rgba16float", depth: true, msaa: true });
const post = effect(gpu, `
  @fragment fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f { return vec4f(uv, 0, 1); }
`);
 
frame(gpu, (currentFrame) => {
  currentFrame.pass({ target: scene, clear: [0, 0, 0, 1] }, (pass) => pass.draw(post));
});
TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
import { init, draw, frame, target } from "vgpu/mock";
 
const gpu = await init();
// G-buffer for deferred shading: albedo, normals, material parameters.
const gbuffer = target(gpu, {
  size: [512, 512],
  colors: [{ format: "rgba8unorm" }, { format: "rgba16float" }, { format: "rgba8unorm" }],
  depth: true,
});
const fill = draw(gpu, {
  shader: `
    @vertex fn vs_main(@builtin(vertex_index) vi: u32) -> @builtin(position) vec4f {
      var p = array<vec2f, 3>(vec2f(-1, -1), vec2f(3, -1), vec2f(-1, 3));
      return vec4f(p[vi], 0, 1);
    }
    struct GBuffer { @location(0) albedo: vec4f, @location(1) normal: vec4f, @location(2) material: vec4f }
    @fragment fn fs_main() -> GBuffer {
      return GBuffer(vec4f(0.8, 0.2, 0.2, 1), vec4f(0, 0, 1, 0), vec4f(0.5, 0.1, 0, 0));
    }
  `,
});
 
frame(gpu, (currentFrame) => {
  currentFrame.pass(gbuffer, fill); // one draw fills all three attachments
});

One geometry pass fills every G-buffer attachment; a later lighting effect samples them as gbuffer.colors[0][2]. Per-attachment blend/write-mask overrides for MRT draws live on DrawOptions.colors.

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import { init, effect, surface, target } from "vgpu/mock";
 
const gpu = await init();
const canvasSurface = surface(gpu, mockCanvas());
const bloomSize = (w: number, h: number): [number, number] => [w / 2, h / 2];
const bloom = target(gpu, { size: bloomSize(canvasSurface.size[0], canvasSurface.size[1]) });
const bright = effect(gpu, `
  struct Params { resolution: vec2f }
  @group(0) @binding(0) var<uniform> params: Params;
  @fragment fn fs_main() -> @location(0) vec4f { return vec4f(1); }
`, { set: { params: { resolution: bloom.size } } });
 
canvasSurface.onResize(({ width, height }) => {
  bloom.resize(bloomSize(width, height));
  bright.set({ params: { resolution: bloom.size } });
});
 
function mockCanvas(): HTMLCanvasElement {
  return {
    width: 10,
    height: 10,
    clientWidth: 10,
    clientHeight: 10,
    getContext() { return { configure() {}, unconfigure() {}, getCurrentTexture() { return { createView: () => ({}) }; } }; },
  } as unknown as HTMLCanvasElement;
}
TypeScript
1
2
3
4
5
6
7
8
9
10
11
import { init, effect, frame, target } from "vgpu/mock";
 
const gpu = await init();
// HDR target: readFloats() decodes the half-float texels, read() would hand back raw bytes.
const hdr = target(gpu, { size: [64, 64], format: "rgba16float" });
const bloom = effect(gpu, `@fragment fn fs_main() -> @location(0) vec4f { return vec4f(4.0, 2.0, 1.0, 1.0); }`);
 
frame(gpu, (currentFrame) => currentFrame.pass(hdr, bloom));
 
const floats = await hdr.readFloats(); // Float32Array, 64 * 64 * 4 components
console.log(floats[0]); // 4 — values above 1 survive the readback
TypeScript
1
2
3
4
5
6
7
8
9
10
import { init, effect, frame, pingPong } from "vgpu/mock";
 
const gpu = await init();
const pair = pingPong(gpu, 32.9, 32.1, { format: "rgba8unorm" });
const blur = effect(gpu, `@fragment fn fs_main() -> @location(0) vec4f { return vec4f(1); }`);
 
frame(gpu, (currentFrame) => {
  currentFrame.pass({ target: pair.write, clear: false }, (pass) => pass.draw(blur));
});
pair.swap();

Notes

  • Choose Target for offscreen intermediates that must be reused, sampled, read back, or ping-ponged; choose Surface only for the canvas swapchain (see Surface). A target can be rendered in multiple passes and sampled by later effects.
  • Use simple format for one color attachment. Use colors when a pass writes multiple attachments (MRT/G-buffer), then consume target.colors[i] in later lighting/post passes.
  • Set depth: true for ordinary z-testing; choose depth: "depth24plus-stencil8" when stencil masking is required. Enable msaa: true/4 for anti-aliased 3D geometry, but do not combine MSAA with clear: false preservation or depthReadOnly: the internal multisample render attachments (including depth) are discarded, while the resolved .color(s) remain sampleable/readable.
  • target.read() / target.readFloats() are intended for tests, snapshots, and diagnostics—not a per-frame hot path. For iterative simulation or post-processing, use pingPong(gpu, ...) and swap targets instead of readback.
  • There is no global resolution binding. Pass target.size or target.texelSize explicitly to shaders.
  • Surface.color wraps the canvas current texture; offscreen target colors are stable until resize/destroy.
  • target.read() and surface.read() return raw texel bytes in the target's own color format, with row padding removed and BGRA canvas formats swizzled to RGBA. For rgba8unorm targets that is exactly the previous RGBA byte layout.
  • Float targets (rgba16float, rgba32float, r16float, r32float, rg16float, rg32float) read back through target.readFloats(), which decodes half/float texels into a Float32Array of components — HDR values above 1 and negatives are preserved. readFloats() also works on unorm8 targets (normalized to [0, 1]), so tooling can stay format-agnostic.
  • Custom Target implementers must provide readFloats(); delegating to this.color.readFloats() (as target(gpu) and surface(gpu) do) is enough.
  • Size-dependent targets derived from a surface should be created from the real initial surface.size and resized from surface.onResize(...).
  • Custom Target implementers should honor the optional renderPassDescriptor(opts?) options-bag fields to participate in Frame.pass({ clear: false }), FramePassOptions.clearDepth, FramePassOptions.clearStencil, and FramePassOptions.depthReadOnly; implementations that ignore a field will clear (with depth 1, stencil 0) instead.
  • Depth formats with a stencil aspect ("depth24plus-stencil8", "depth32float-stencil8") emit stencilLoadOp/stencilStoreOp on the pass depth-stencil attachment, mirroring the depth load/store behavior with stencilClearValue from FramePassOptions.clearStencil (default 0), as WebGPU requires when the stencil aspect is writable.
  • target.depth is created with texture_binding usage in addition to render_attachment, so it can be bound with set() as a sampled depth texture — including inside the same pass when it opened with FramePassOptions.depthReadOnly.
  • See also: Surface, FramePassOptions, Effect, Draw, Bundle, Compute storage ping-pong.