vgpu2 symbolsView source ↗

Symbols in this topic

Effect

Fullscreen-fragment render unit created by effect(gpu). Use it for post-processing, gradients, blurs, and screen/target copies; use draw(gpu) for meshes, vertex buffers, instancing, or explicit vertex counts.

Import

TypeScript
1
import type { Effect, EffectOptions } 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
import type { DrawCallOptions, Target, TargetSignature } from "vgpu";
 
type SetBag = Record<string, unknown>;
 
type BlendPreset = "alpha" | "additive" | "premultiplied";
interface BlendComponentOptions { readonly src: GPUBlendFactor; readonly dst: GPUBlendFactor; readonly op?: GPUBlendOperation; }
interface BlendOptions { readonly color: BlendComponentOptions; readonly alpha?: BlendComponentOptions; }
 
interface EffectOptions {
  readonly set?: SetBag;
  readonly label?: string;
  readonly blend?: BlendPreset | BlendOptions;
  readonly writeMask?: readonly ("r" | "g" | "b" | "a")[];
}
 
interface Effect {
  readonly gpu: GPURenderPipeline | undefined;
  set(values: SetBag): this;
  draw(target?: Target | DrawCallOptions): void;
  compile(target?: Target | TargetSignature): Promise<this>;
  compileSync(target?: Target | TargetSignature): this;
}

Parameters

ParamTypeRequiredDefaultNotes
effect.sourcestring | ShaderSourceWGSL string or ShaderSource. If no @vertex entry exists, vgpu injects a fullscreen triangle vertex stage and provides @location(0) uv.
effect.optsEffectOptions{}Initial options. Passing a mesh property is rejected; effects have no vertex buffers.
opts.setRecord<string, unknown>undefinedSame as one initial .set(opts.set) call: establishes first-set binding ownership and validates reflected bindings.
opts.labelstring"effect"Used in shader reflection labels, GPU object labels, and VGPU-* error where fields.
opts.blend"alpha" | "additive" | "premultiplied" | BlendOptionsundefinedConstructor-only blend state passed through to the fullscreen draw. Presets and defaults match DrawOptions.blend; omitted explicit alpha copies color, and op defaults to "add".
opts.writeMaskreadonly ("r" | "g" | "b" | "a")[]all channelsConstructor-only color channel mask. Omit for RGBA; [] writes no channels; ["r","g","b"] skips alpha.
effect.set.valuesRecord<string, unknown>Binding values by WGSL variable name. JS values are lib-owned; resources are user-owned.
effect.draw.targetTarget | DrawCallOptions{}One-shot render pass. Pass a bare target for the common case, or an options bag when setting per-call draw options.
opts.targetTargetRequired at runtime when an options bag is used. Use a Surface or an offscreen Target.

The uv varying that effect(gpu) injects is top-origin: (0, 0) is the top-left corner and v grows downward — the same convention as WebGPU texture coordinates, @builtin(position), and target.read(). Sampling any texture with this uv needs no flip: a pass that samples src at uv reproduces the image exactly. If you are porting a WebGL or Shadertoy shader that assumes v grows upward, invert once at the boundary (1.0 - uv.y) and keep everything else flip-free.

Returns: effect(gpu) returns Effect; effect.set() and effect.compileSync() return the same Effect; effect.compile() returns Promise<this>; effect.draw() returns void after starting a one-shot draw path.

Throws: VGPU-TARGET-REQUIRED when effect.draw() or compile pre-warm is called without target; VGPU-BLEND-INVALID for an unknown blend preset or malformed blend object; VGPU-WRITEMASK-INVALID for a non-array or unknown write mask channel; VGPU-RING1-UNSUPPORTED when effect(gpu) receives mesh/vertex data; VGPU-SHADER-SOURCE-INVALID for malformed ShaderSource; VGPU-R1-BINDING-NEVER-SET when a reflected binding has no value at draw time; VGPU-R1-OWNERSHIP-FLIP when a binding switches between JS-value and resource ownership; VGPU-SET-TEXTURE-FILTERABILITY when an ordinarily sampled facade texture is not filterable (structured detail names its format/binding and paired sampler; use a filterable format, request float32-filterable, or use textureLoad without a sampler). Asynchronous draw validation errors are delivered through gpu.onError; tests can await gpu.settled().

Examples

TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { init, clock, effect, frame, target } from "vgpu/mock";
 
const gpu = await init();
const colorTarget = target(gpu, { size: [64, 64] });
const shader = effect(gpu, `
  struct Params { time: f32, speed: f32 }
  @group(0) @binding(0) var<uniform> params: Params;
 
  @fragment fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
    return vec4f(uv, sin(params.time * params.speed) * 0.5 + 0.5, 1);
  }
`, { label: "wave", set: { params: { time: 0, speed: 2 } } });
 
shader.set({ params: { time: clock(gpu).time, speed: 2 } });
frame(gpu, (currentFrame) => currentFrame.pass(colorTarget, shader));
TypeScript
1
2
3
4
5
6
7
8
9
10
import { init, effect, target } from "vgpu/mock";
 
const gpu = await init();
const colorTarget = target(gpu, { size: [32, 32] });
const copy = effect(gpu, `
  @fragment fn fs_main(@location(0) uv: vec2f) -> @location(0) vec4f {
    return vec4f(uv.x, uv.y, 0.0, 1.0);
  }
`);
copy.draw(colorTarget);

Pipeline pre-warm

Effects compile lazily for the target signature they draw into. Use await effect.compile(target) during loading to pre-warm without blocking, or effect.compileSync(target) when synchronous creation is acceptable. Signature objects follow the same shape as draws: { colors: ["bgra8unorm"], depth?, sampleCount? }.

Notes

  • A fragment-only effect is internally implemented as a Draw with an injected fullscreen triangle. Fragment-only resources receive fragment visibility only, so storage does not consume maxStorageBuffersInVertexStage.
  • blend and writeMask are immutable pipeline state, fixed at effect(gpu) construction, and apply uniformly to every color target. Use them for overlays, glow, UI, and other loaded-pass compositing. For explicit blends, op defaults to "add" and omitted alpha copies color.
  • One-shot effect.draw() does not join a surrounding frame. Inside frame(gpu), draw through frame.pass().
  • There is no implicit screen target. Browser code should create a Surface and pass it as target.
  • Do not rely on implicit uniforms like time or resolution; pass clock(gpu).time, target.size, or target.texelSize explicitly through set().
  • See also: effect, Draw, FramePass.draw, Surface, Target, SharedUniforms.