Symbols in this topic
bind and explicit binding helpers
bind, createBindGroupLayout, createPipelineLayout, createBindGroup, and createSampler are thin core helpers for explicit WebGPU binding. Use them when you want raw WebGPU layouts, bind groups, and samplers without shader reflection or layout: "auto" inference.
Import
TypeScript
1
import { bind, createBindGroup, createBindGroupLayout, createPipelineLayout, createSampler } from "vgpu/core";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 { Buffer, Device, Texture } from "vgpu/core";
type BindVisibility = GPUShaderStageFlags | string | readonly ("vertex" | "fragment" | "compute")[];
type DeviceLike = GPUDevice | Device | { readonly gpu: GPUDevice };
interface CreateBindGroupLayoutOptions {
readonly label?: string;
readonly entries: readonly GPUBindGroupLayoutEntry[];
}
interface CreatePipelineLayoutOptions {
readonly label?: string;
readonly bindGroups: readonly GPUBindGroupLayout[];
}
interface CreateBindGroupOptions {
readonly label?: string;
readonly layout: GPUBindGroupLayout;
readonly entries: readonly GPUBindGroupEntry[];
}
interface SamplerDescriptorWithSugar extends GPUSamplerDescriptor {
readonly filter?: "linear" | "nearest";
readonly wrap?: "clamp" | "repeat" | "mirror";
}
declare function createBindGroupLayout(device: DeviceLike, opts: CreateBindGroupLayoutOptions): GPUBindGroupLayout;
declare function createPipelineLayout(device: DeviceLike, opts: CreatePipelineLayoutOptions): GPUPipelineLayout;
declare function createBindGroup(device: DeviceLike, opts: CreateBindGroupOptions): GPUBindGroup;
declare function createSampler(device: DeviceLike, descriptor?: SamplerDescriptorWithSugar): GPUSampler;
declare const bind: {
readonly uniform: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
readonly storage: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
readonly readonlyStorage: (binding: number, visibility: BindVisibility, opts?: Omit<GPUBufferBindingLayout, "type">) => GPUBindGroupLayoutEntry;
readonly texture: (binding: number, visibility: BindVisibility, opts?: GPUTextureBindingLayout) => GPUBindGroupLayoutEntry;
readonly storageTexture: (binding: number, visibility: BindVisibility, opts: GPUStorageTextureBindingLayout) => GPUBindGroupLayoutEntry;
readonly sampler: (binding: number, visibility: BindVisibility, opts?: GPUSamplerBindingLayout) => GPUBindGroupLayoutEntry;
readonly resource: (binding: number, value: Buffer | Texture | GPUBuffer | GPUBufferBinding | GPUBindingResource | unknown) => GPUBindGroupEntry;
};Parameters
Shared parameters
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| device | DeviceLike | ✔ | — | A raw GPUDevice, a vgpu Device, or an object with .gpu: GPUDevice. Helpers unwrap it before calling WebGPU. |
| binding | number | ✔ | — | Explicit non-negative integer @binding(n). Invalid values throw VGPU-CORE-BINDING-INVALID. |
| visibility | GPUShaderStageFlags | string | readonly ("vertex" | "fragment" | "compute")[] | ✔ | — | Numeric flags pass through. Strings split on ` |
createBindGroupLayout(device, opts)
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| opts | CreateBindGroupLayoutOptions | ✔ | — | Layout descriptor wrapper. |
| opts.label | string | ✖ | undefined | Forwarded to GPUDevice.createBindGroupLayout. |
| opts.entries | readonly GPUBindGroupLayoutEntry[] | ✔ | — | Copied with [...opts.entries] before calling WebGPU; metadata is attached to the returned layout. |
createPipelineLayout(device, opts)
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| opts | CreatePipelineLayoutOptions | ✔ | — | Pipeline layout descriptor wrapper. |
| opts.label | string | ✖ | undefined | Forwarded to GPUDevice.createPipelineLayout. |
| opts.bindGroups | readonly GPUBindGroupLayout[] | ✔ | — | Copied to WebGPU as bindGroupLayouts: [...opts.bindGroups]. |
createBindGroup(device, opts)
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| opts | CreateBindGroupOptions | ✔ | — | Bind group descriptor wrapper. |
| opts.label | string | ✖ | undefined | Forwarded to GPUDevice.createBindGroup. |
| opts.layout | GPUBindGroupLayout | ✔ | — | Required explicit layout. Missing/falsy layout throws VGPU-CORE-BIND-GROUP-LAYOUT-REQUIRED; vgpu never uses bind group layout: "auto". |
| opts.entries | readonly GPUBindGroupEntry[] | ✔ | — | Copied with [...opts.entries] before calling WebGPU; metadata is attached to the returned bind group. |
createSampler(device, descriptor?)
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| descriptor | SamplerDescriptorWithSugar | ✖ | {} | Raw GPUSamplerDescriptor plus vgpu sugar. Sugar is stripped before calling WebGPU. |
| descriptor.filter | "linear" | "nearest" | ✖ | undefined | Expands to magFilter and minFilter only. Raw magFilter/minFilter override this per key. |
| descriptor.wrap | "clamp" | "repeat" | "mirror" | ✖ | undefined | Expands to all three address modes: "clamp" → "clamp-to-edge", "repeat" → "repeat", "mirror" → "mirror-repeat". Raw address fields override per axis. |
| descriptor.mipmapFilter | GPUMipmapFilterMode | ✖ | WebGPU default | Not set by filter; pass "linear" explicitly for anisotropic sampling. |
| descriptor.maxAnisotropy | number | ✖ | WebGPU default | If filter sugar is used and maxAnisotropy > 1, all three filters must resolve to "linear". |
bind.* layout entry helpers
| Param | Type | Required | Default | Notes |
|---|---|---|---|---|
| bind.uniform opts | Omit<GPUBufferBindingLayout, "type"> | ✖ | {} | Returns { binding, visibility, buffer: { ...opts, type: "uniform" } }. |
| bind.storage opts | Omit<GPUBufferBindingLayout, "type"> | ✖ | {} | Returns { binding, visibility, buffer: { ...opts, type: "storage" } }. |
| bind.readonlyStorage opts | Omit<GPUBufferBindingLayout, "type"> | ✖ | {} | Returns { binding, visibility, buffer: { ...opts, type: "read-only-storage" } }. |
| bind.texture opts | GPUTextureBindingLayout | ✖ | {} | Returns { binding, visibility, texture: opts }. |
| bind.storageTexture opts | GPUStorageTextureBindingLayout | ✔ | — | Returns { binding, visibility, storageTexture: opts }; storage texture layout fields are not inferred. |
| bind.sampler opts | GPUSamplerBindingLayout | ✖ | {} | Returns { binding, visibility, sampler: opts }. |
| bind.resource value | unknown | ✔ | — | Converts vgpu Buffer to { buffer: buffer.gpu }, vgpu Texture/texture-like objects to createView(), raw GPUBuffer to { buffer }, and passes through other binding resources. |
Returns:
createBindGroupLayout(...)returnsGPUBindGroupLayoutwith vgpu layout metadata attached.createPipelineLayout(...)returns rawGPUPipelineLayout.createBindGroup(...)returnsGPUBindGroupwith vgpu bind-group metadata attached.createSampler(...)returns rawGPUSampler.bind.uniform(...),bind.storage(...),bind.readonlyStorage(...),bind.texture(...),bind.storageTexture(...), andbind.sampler(...)returnGPUBindGroupLayoutEntry.bind.resource(...)returnsGPUBindGroupEntry.
Throws:
VGPU-CORE-BIND-GROUP-LAYOUT-REQUIREDwhencreateBindGroup(...)receives a missing/falsyopts.layout— create and pass an explicitGPUBindGroupLayout.VGPU-CORE-SAMPLER-ANISOTROPY-FILTERSwhencreateSampler(...)usesfiltersugar withmaxAnisotropy > 1but the resolvedmagFilter,minFilter, andmipmapFilterare not all"linear"— addmipmapFilter: "linear"and avoid raw non-linear overrides.VGPU-CORE-BINDING-INVALIDwhen anybind.*helper receives a negative, non-integer, or otherwise invalidbinding— pass an explicit non-negative integer.VGPU-CORE-VISIBILITY-INVALIDwhen a string/array visibility contains an unknown shader stage name — use only"vertex","fragment", and"compute", or pass numericGPUShaderStageFlags.- Native WebGPU validation errors may occur for descriptor/layout/resource incompatibilities — fix the WebGPU descriptor or resource usage.
Examples
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
import { bind, createBindGroup, createBindGroupLayout, createPipelineLayout, createSampler } from "vgpu/core";
import { createMockAdapter } from "vgpu/mock";
const device = await createMockAdapter().requestDevice();
const uniformBuffer = device.createBuffer({ size: 64, usage: ["uniform", "copy_dst"] });
const texture = device.createTexture({
size: [1, 1],
format: "rgba8unorm",
usage: ["texture_binding", "copy_src"],
});
const sampler = createSampler(device, { filter: "linear", wrap: "clamp" });
const groupLayout = createBindGroupLayout(device, {
label: "scene.group0",
entries: [
bind.uniform(0, "vertex|fragment"),
bind.texture(1, "fragment", { sampleType: "float" }),
bind.sampler(2, ["fragment"], { type: "filtering" }),
],
});
const pipelineLayout = createPipelineLayout(device, { bindGroups: [groupLayout] });
const group = createBindGroup(device, {
layout: groupLayout,
entries: [
bind.resource(0, uniformBuffer),
bind.resource(1, texture),
bind.resource(2, sampler),
],
});
console.log(pipelineLayout, group);
device.destroy();TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
import { createSampler } from "vgpu/core";
import { createMockAdapter } from "vgpu/mock";
const device = await createMockAdapter().requestDevice();
const mixedSampler = createSampler(device, {
filter: "linear",
wrap: "repeat",
magFilter: "nearest", // raw field wins for this key only
});
const anisotropicSampler = createSampler(device, {
filter: "linear",
mipmapFilter: "linear",
maxAnisotropy: 16,
});
console.log(mixedSampler, anisotropicSampler);
device.destroy();TypeScript
1
2
3
4
5
6
7
8
9
10
11
12
13
import { bind, createBindGroupLayout } from "vgpu/core";
import { createMockAdapter } from "vgpu/mock";
const device = await createMockAdapter().requestDevice();
const computeLayout = createBindGroupLayout(device, {
entries: [
bind.storage(0, "compute", { hasDynamicOffset: true }),
bind.storageTexture(1, "compute", { access: "write-only", format: "rgba8unorm", viewDimension: "2d" }),
],
});
console.log(computeLayout);
device.destroy();Notes
- These helpers are explicit by design: no WGSL reflection, no named binding maps, no hidden resource creation, and no
layout: "auto"bind groups. filtersugar does not setmipmapFilter. For anisotropic sampling, spell trilinear filtering explicitly.- Raw WebGPU descriptor fields override sugar per key, not all-or-nothing.
bind.resource(...)creates a default texture view for vgpuTexture; use a rawGPUTextureViewwhen you need a non-default mip/array/format view.- Keep shader
@group/@bindingnumbers synchronized with helperbindingnumbers; vgpu does not reflect or renumber them here. - See also:
Device,Buffer,Texture,Queue,VGPUError.