---
title: Migrating to 0.5.0
description: Ordered upgrade paths to 0.5.0 from 0.4.x and its release candidates.
---

# Migrating to 0.5.0



This guide targets stable `0.5.0`. Choose the path matching your installed version.
Update `vgpu` and any directly installed `@vgpu/*` packages in its fixed release group to
`0.5.0`; keep the optional `@vgpu/native` companion on the same exact version as `vgpu`.
The native tooling remains beta, with the qualifications described in step 9.

## From the previous stable release

For 0.4.x projects (previous stable: 0.4.1), apply the relevant steps below. Steps 7–8 also affect
WebGPU users without native tooling; valid existing values need no shape change. Step 9 is optional.
The added CLI/MCP guides require no API or command migration; find them with `vgpu docs ls /migrations`.

### 1. Update texture creation descriptors

`texture(gpu, opts)` and `device.createTexture(opts)` accept the same core `TextureOptions` and return
the same `Texture` class. The public factory only adds ownership by `gpu`; core resources still need
their existing explicit lifecycle management. `TextureOptions` and `TextureShape` are re-exported
from `vgpu`, `vgpu/node`, `vgpu/mock` and `vgpu/core`.

#### Explicit kind and usage

In 0.4.x, standalone textures were created through core (`device` is a core `Device`):

```ts illustrative
const lut = device.createTexture({
  size: [32, 32, 32], dimension: "3d", format: "rgba16float",
  usage: ["storage_binding", "texture_binding"],
});
```

After, the same core factory accepts the explicit contract:

```ts illustrative
const lut = device.createTexture({
  kind: "3d", size: [32, 32, 32], format: "rgba16float",
  usage: ["storage_binding", "texture_binding"],
});
```

Core already required `usage`; it now rejects an empty list. Version 0.5.0 also introduces
`texture(gpu, opts)` in the main API, accepting the same options and registering ownership with `gpu`.
It is a new factory, not an existing factory whose defaults changed.

No capabilities are inferred. Add `copy_src` if you read or copy from the texture, `copy_dst` for
uploads/copies into it, and `render_attachment` for rendering into it. Empty usages are rejected.
The same change applies to direct `device.createTexture(...)` and core `pingPong(device, opts)`.

#### Spatial size and layers

| Resource | Creation shape                                           |
| -------- | -------------------------------------------------------- |
| 1D       | `kind: "1d", size: [width]`                              |
| 2D       | `kind: "2d", size: [width, height]`                      |
| 3D       | `kind: "3d", size: [width, height, depth]`               |
| Array    | `kind: "2d-array", size: [width, height], layers: count` |

Before, `[width, height, count]` without a dimension created an array. Now arrays always declare
`kind` and `layers`, and `.size` remains a 2-tuple. Replace `array.size[2]` with `array.layers`.
Volume depth remains `volume.size[2]`. For shape-agnostic code, height is `texture.size[1] ?? 1`.
Use `texture.options.kind` to narrow the options union when working with shape-specific metadata.
`.kind` is semantic; `.dimension` is retained as the derived native dimension (`"2d"` for arrays).

One-layer arrays now retain array default views. Explicit native view dimensions, `cubeView` and
`layerView` remain available. Raw `GPUDevice.createTexture` descriptors are unchanged: continue to
use native `dimension`, extent and usage bit flags at that boundary.

### 2. Separate configuration from immutable resource metadata

Only allocation details have defaults: `mipLevelCount: 1`, `sampleCount: 1`, `viewFormats: []`.
Additional mips allocate storage without generating contents. Standalone MSAA textures do not
allocate or resolve another texture automatically. Alternate compatible view formats require explicit
opt-in; no conversion is performed. Public and core factories expose the same fields.

Descriptors and nested arrays are copied and frozen. Mutating the input object no longer changes
resource metadata or the options used by later pair resizes. Update application configuration separately;
do not mutate `texture.options`, `.size`, `.usage` or `.viewFormats`.

Core preflight rejects malformed shapes, unsupported usage names, invalid mip/sample combinations,
incompatible view formats, exceeded enabled limits and storage formats lacking enabled capabilities.
Native WebGPU remains authoritative for complete per-format and backend validation. These failures
are not silently converted into another allocation or usage set.

### 3. Replace standalone textures instead of resizing them

`Texture.resize()` and its resize-lock machinery are removed. Texture allocation and resource identity
do not change during its lifetime. Replace a standalone texture explicitly:

```ts illustrative
const previous = image;
image = texture(gpu, {
  kind: "2d", size: [width, height], format: "rgba8unorm",
  usage: ["texture_binding", "copy_dst"],
});
post.set({ src: image });
previous.destroy();
```

Re-upload/copy contents as needed; replacement never preserves them implicitly. Destroying a wrapper
invalidates views and bindings. External wrappers never destroy the native resource owned by another
library or swapchain. The obsolete `VGPU-CORE-EXTERNAL-TEXTURE`/`VGPU-CORE-TEXTURE-RESIZE-LOCKED`
resize errors disappear with the method.

Targets retain synchronous `resize(size): void`; core texture pairs retain `resize(size): boolean`.
Each prepares its complete replacement before committing it. Synchronous preparation failure cleans
partial allocations and preserves the old size, attachments/halves, contents and pair parity. Successful
texture-pair replacement resets orientation and discards both halves' contents. Array layers and the
creation format/usage/mip/sample settings stay fixed. An unchanged valid size is a no-op.

These are bounded preparation guarantees: late native GPU errors still use normal device reporting and
do not roll back. Callback errors occur after commit; they do not restore old attachments, and cleanup
and remaining notifications still run. Offscreen targets reject recursive resize from replacement
callbacks and reject all resize calls after destruction. Buffer-pair behavior is outside this checkpoint.

### 4. Rebind replacements and recreate affected bundles

```ts illustrative
// Automatically follows successful Target attachment replacement:
post.set({ src: scene });
scene.resize([width, height]);

// Explicit attachment reference: rebind after replacement:
post.set({ src: scene.color });
scene.resize([width * 2, height * 2]);
post.set({ src: scene.color });

// Core texture pairs also require explicit rebinding after replacement:
if (pair.resize([width, height])) {
  fill.set({ dst: pair.write });
  post.set({ src: pair.read });
  // Reseed before using the new contents.
}
```

Using a destroyed tracked texture in `set`, draw or compute dispatch now fails with
`VGPU-R1-BINDING-DESTROYED`, naming the binding and resource. This includes retained old target
attachments and pair halves. Rebinding a live replacement recovers ordinary rendering. Bundles freeze
their commands/resources: they become `VGPU-R3-BUNDLE-STALE` and must be recorded again, even if
the Draw has since been rebound.

Raw `GPUTextureView`/native bind groups have no tracked parent in this API. Direct native destruction
also bypasses wrapper signals. Native WebGPU remains the validation fallback; no managed-view or raw
view lifetime guarantee is added here.

### 5. Select the attachment, mip and region for readback

Both fields are mandatory. Select the attachment first; `Target.read`, `Target.readFloats`,
`Surface.read` and `Surface.readFloats` have been removed. Buffer reads are unchanged.

```ts illustrative
// Before
const pixels = await output.read();
const values = await volume.readFloats();

// After
const pixels = await output.color.read({ mipLevel: 0, region: "all" });
const values = await volume.readFloats({ mipLevel: 0, region: "all" });
const normals = await gbuffer.colors[1].readFloats({ mipLevel: 0, region: "all" });

// Read a crop from one allocated mip. Coordinates are mip-relative texels.
const crop = await volume.readFloats({
  mipLevel: 1,
  region: { origin: [2, 3, 1], size: [4, 5, 2] },
});
```

`TextureReadOptions` is shared by core and public entrypoints. `region: "all"` means the whole
selected mip, including **every** array layer or 3D slice (previously reads copied only the first slice).
To retain that previous selection, use `region: { origin: [0, 0, 0], size: [width, height, 1] }`.
Width/height/depth shrink at each 3D mip; array layers never shrink. Results are tightly packed:
X fastest, then Y, then Z; no row or slice padding remains.

Declare `copy_src` at creation, including for mocks. Invalid/missing mip or region, non-integer or
out-of-bounds coordinates, missing usage, multisampling and oversized allocations fail before staging
allocation with `VGPU-CORE-TEXTURE-READ-INVALID`. No clipping or implicit resolve occurs; read an
MSAA Target's resolved `.color` attachment instead of its multisampled texture. Allocation checks
cover the device's `maxBufferSize` and a portable host byte limit of `2^32 - 1`; decoded float sizes
are also checked. Read smaller regions when necessary.

Formats and conversion semantics are unchanged: `read` returns raw bytes (BGRA swizzled to RGBA),
`readFloats` widens half/float components and normalizes unorm8 without sRGB decoding. Unsupported
depth, compressed, packed and integer formats still fail with `VGPU-CORE-UNSUPPORTED-FORMAT`.
Mock storage now distinguishes allocated mips and slices; nonzero-mip uploads are supported.

### 6. Provide Vulkan for Node/Linux deployments

Linux now selects Vulkan even when X11 or Wayland is configured. Existing code stays the same:

```ts illustrative
import { init } from "vgpu/node";
const gpu = await init(); // Vulkan on Linux; other platforms retain their existing defaults.
```

If your Linux environment previously depended on automatic OpenGL selection, install a hardware
Vulkan driver or use the existing portable CPU-renderer installer:

```bash
npx vgpu install-software-renderer
```

Auto mode may use that installed renderer if normal discovery fails. It does not silently switch to
OpenGL or download drivers. A missing adapter produces `VGPU-NODE-NO-ADAPTER` with remediation.
An explicit override remains available for legacy uses:

```bash
VGPU_DAWN_FLAGS=backend=opengl node render.mjs
```

That opt-in retains Dawn's restricted-mip-view/storage-write bug (392121637). macOS, Windows,
browser WebGPU and the compatibility feature level are unchanged. For the pinned Linux Vulkan
reference environment and comparison policy, see [Visual snapshots](https://github.com/vercel-labs/vgpu/blob/v0.5.0/docs/visual-snapshots.md).

### 7. Regenerate reflected host layouts and authored module metadata

If you cache reflection or pack buffers yourself, discard metadata produced with
`layoutMode: "naga-standard"`. The replacement is `"wgsl-host-shareable-v1"`, using intrinsic
WGSL alignment, sizes, offsets and strides rather than implicit uniform-only padding.
Read address space from the binding; `HostShareableLayout.addressSpace` has been removed.

```ts illustrative
// Before
const space = binding.layout.addressSpace;
if (binding.layout.layoutMode === "naga-standard") useCachedUniformOffsets();

// After
const space = binding.addressSpace;
if (binding.layout?.layoutMode === "wgsl-host-shareable-v1") {
  useReflectedOffsetsAndStrides(binding.layout);
}
```

Address-space validation does not rewrite layout bytes. Replace implicit uniform padding with
explicit WGSL alignment/size and revalidate. Recompute cached offsets, strides and buffer sizes
before uploads. Custom `f16` packers and byte snapshots now use round-to-nearest, ties-to-even.

Handwritten `WGSLModule` objects require `entryPointDeclarations`: authored names, stages and
spans with 1-based lines/UTF-16 columns and exclusive ends. Use `[]` only without entry points.
Resolver-produced modules already supply these. Captured-source-graph APIs are additive.

### 8. Supply complete, correctly shaped JavaScript-owned binding values

Review initial values and updates passed to `effect`, `draw`, `compute` and shared `uniforms`.
Numeric scalars must be numbers, and `i32`/`u32` must be integers in their respective ranges;
string coercion, integer truncation and wrapping are no longer accepted. Vectors, column-major
matrices and fixed arrays require exact component/element counts in arrays or typed arrays.
Structs require every reflected member, including explicit shader padding, with no unrelated
fields. Select shader fields instead of passing a larger settings object.

```ts illustrative
// WGSL: struct Params { gain: f32, tint: vec3f }
// Before: coercion, short vectors and extra fields were tolerated.
shader.set({ params: { gain: "1", tint: [1], debug: true } });

// After: complete initial value, then a valid partial update.
shader.set({ params: { gain: 1, tint: [1, 0, 0] } });
shader.set({ params: { gain: 0.5 } });
```

Direct struct updates shallow-merge with the existing binding value; shared uniforms deep-merge
plain objects. The resulting candidate must be valid. Initial direct struct values must be
complete; member shorthand instead starts from reflected zero values when no previous value
exists. Preserve partial updates for animation/resizing, and initialize resolution/texel-size
values from the actual render target dimensions.

Invalid candidates throw `VGPU-SET-VALUE-INVALID` with `reason`, `path`, `expected`, `actual`
and, where applicable, scalar `type`. Validation rejection preserves that binding's previous
accepted values, bytes and ownership. Initial shared values are checked when the first shader
adopts their layout, not when `uniforms(...)` is created.

There is **no global atomicity**: `set({ a, b })` can update `a` before `b` fails.
Separate member-shorthand keys are also sequential, even for one binding. Arbitrary GPU
errors do not roll back.

### 9. Opt into the native beta only when generating Swift/Metal packages

Existing WebGPU projects do not need `@vgpu/native`. To adopt the build-time companion,
install matching, exact versions as development dependencies:

```bash
npm install --save-dev --save-exact vgpu@0.5.0 @vgpu/native@0.5.0
```

Replace the empty `@vgpu/native@0.0.1` name reservation with `0.5.0`; it has no API or outputs
to migrate. This beta supersedes intermediate private-companion documentation. Use `vgpu native`
commands, not direct imports of the internal TypeScript generator.

Prepare an Apple Silicon build host with Node.js 22 and Xcode plus its Metal compiler component.
Follow the [native configuration guide](/docs/native/macos/metal/tooling/configuration)
to create `vgpu.native.json`. The package bundles the pinned Tint worker, schemas, helper sources
and license notices; consumers do not build or download Tint at installation.

The worker is ad hoc signed, **not** Developer ID signed or notarized. Validate it in your own
environment without disabling system security. This beta does not qualify Intel GPU support,
a minimum-macOS matrix or general end-user distribution. APIs, generated Swift interfaces,
configuration and toolchain requirements may change; pin versions and review upgrades.
Only generated Swift and compiled Metal resources belong in the consuming app. Node, Tint
and Xcode are build-time requirements, not shipped runtime dependencies.

## From release candidates

### From 0.5.0-rc.0

The texture contract and Vulkan default above already shipped in `0.5.0-rc.0`. If your project
has adopted that RC, **skip steps 1–6** and apply steps 7–8 for reflection and strict host values.
These changes first shipped in `0.5.0-rc.1`, including for browser/WebGPU users. Apply step 9 only if
opting into native generation. If you installed rc.0 without adapting the application, first
complete the applicable texture/Linux steps from the stable-origin path.

The CLI/MCP guides added in rc.1 need no application or command changes. The independent native
bootstrap follows step 9, not the RC0 path.

### From 0.5.0-rc.1

Update package versions to `0.5.0`, including both exact dependencies in step 9 for native users.
No additional API, shader, binding-value or configuration migration is required: stable retains
rc.1's implementation and defaults. Keep already-applied adaptations; if any remain incomplete,
follow your original stable or RC0 path. Native tooling remains beta with the same qualifications;
regenerate and verify output with `0.5.0` so its version metadata matches.

The two published RCs above require no reversals. Run the regression checks below.

## Verification

1. Typecheck the updated project. Confirm descriptors use `kind`, arrays have separate `layers`,
   and texture reads select an attachment plus explicit `mipLevel` and `region`.
2. Exercise resizing and replacement. Recreate raw views and bundles, rebind replacement textures,
   and re-upload contents; confirm no disposed attachment or pair half is reused.
3. Test mip-relative crops and array/volume readback on the target backend. Confirm whether the
   application expects all layers/slices or only the first, and select the region accordingly.
4. On Linux, run the project-local `vgpu doctor` and the application's GPU tests. Verify the selected
   backend and storage writes/readback at nonzero mip levels. Test the installed CPU-renderer fallback
   separately if the deployment relies on it.
5. Typecheck reflection consumers and handwritten `WGSLModule` objects. Recompute host-layout
   fixtures, compare offsets/strides/buffer sizes and check halfway `f16` rounding.
6. Exercise complete values, valid partial updates, wrong vector/matrix/array sizes, missing/extra
   fields and out-of-range integers. Confirm a rejected binding keeps its previous values and
   bytes, and that callers do not assume global atomicity. Run affected render and compute paths.
7. For native opt-in, outside the vgpu checkout run the project-local `vgpu native doctor`,
   `check`, `build` and `verify`. Build a Swift consumer of the output, relocate it and execute
   the intended compute/render programs on the target GPU. Check that the app bundle does not
   contain Tint or a JavaScript runtime introduced by this tooling. Validate your own build/CI
   environment; local qualification is not a clean-OS or broad compatibility claim.

RC adopters should run the same regression checks without repeating already-applied migrations.
Code blocks marked illustrative show partial/historical usage, not standalone typechecked programs.


---

For a semantic overview of all documentation, see [/sitemap.md](/sitemap.md)

For an index of all available documentation, see [/llms.txt](/llms.txt)

For agent-facing discovery, including API and MCP surfaces, see [/agents.md](/agents.md)