# pex-gpu

[![npm version](https://img.shields.io/npm/v/pex-gpu)](https://www.npmjs.com/package/pex-gpu)
[![stability-stable](https://img.shields.io/badge/stability-stable-green.svg)](https://www.npmjs.com/package/pex-gpu)
[![npm minzipped size](https://img.shields.io/bundlephobia/minzip/pex-gpu)](https://bundlephobia.com/package/pex-gpu)
[![dependencies](https://img.shields.io/librariesio/release/npm/pex-gpu)](https://github.com/pex-gl/pex-gpu/blob/main/package.json)
[![types](https://img.shields.io/npm/types/pex-gpu)](https://github.com/microsoft/TypeScript)
[![Conventional Commits](https://img.shields.io/badge/Conventional%20Commits-1.0.0-fa6673.svg)](https://conventionalcommits.org)
[![styled with prettier](https://img.shields.io/badge/styled_with-Prettier-f8bc45.svg?logo=prettier)](https://github.com/prettier/prettier)
[![linted with eslint](https://img.shields.io/badge/linted_with-ES_Lint-4B32C3.svg?logo=eslint)](https://github.com/eslint/eslint)
[![license](https://img.shields.io/github/license/pex-gpu)](https://github.com/pex-gl/pex-gpu/blob/main/LICENSE.md)

Modern WebGPU wrapper for [PEX](https://pex.gl): describe render and compute work as plain command objects, backed by automatic WGSL reflection, pipeline caching, and GPU resource allocation.

> [!NOTE]
> pex-gpu is WebGPU only and written in TypeScript. Shaders are written in [WGSL](https://www.w3.org/TR/WGSL/) and all enum-like values are raw [WebGPU strings](https://www.w3.org/TR/webgpu/) (`"rgba8unorm"`, `"triangle-list"`, ...). For the WebGL 1/2 version, see [pex-context](https://github.com/pex-gl/pex-context/tree/main).

## Installation

```bash
npm install pex-gpu
```

## Usage

Everything is a free function behind one import — `import * as gpu from "pex-gpu"` — typed, tree-shakeable, and discoverable by autocompleting `gpu.`.

Describe draws, compute dispatches and render bundles as plain objects and `gpu.submit()` them inside a `gpu.frame()` loop. Vertex layouts, bind groups, pipelines and uniform buffers are derived from your WGSL and cached automatically: you write a shader and a command, pex-gpu does the WebGPU.

When something is slow or leaking, always-on `gpu.debugStats(ctx)` counters and opt-in `gpu.debug(ctx)` logging show resource pressure, cache behavior and the submit stream at any time.

Advanced users who need to drive `GPUCommandEncoder`s directly can reach for the core helpers underneath — context/canvas setup, buffers, textures, samplers, readback, timestamp queries, render bundles — which every command is itself built on. Options follow WebGPU naming and derive from WebGPU descriptor types, so raw-WebGPU knowledge transfers 1:1, and hand-built pipelines/bind groups/encoders plug into declarative passes. See the [API](#api) docs below. The machinery itself — WGSL reflection, uniform struct packing, the pipeline cache, allocators, byte math — is importable from `"pex-gpu/internals"` for the same purpose.

```js
import * as gpu from "pex-gpu";

import { mat4 } from "pex-math";
import { cube } from "primitive-geometry";

const ctx = await gpu.createContext();

const geometry = cube();

const shader = /* wgsl */ `
struct Uniforms {
  projection: mat4x4f,
  view: mat4x4f,
  model: mat4x4f,
}
@group(0) @binding(0) var<uniform> uniforms: Uniforms;

struct VertexIn {
  @location(0) position: vec3f,
  @location(1) normal: vec3f,
}
struct VertexOut {
  @builtin(position) position: vec4f,
  @location(0) color: vec4f,
}

@vertex
fn vertexMain(input: VertexIn) -> VertexOut {
  var output: VertexOut;
  output.color = vec4f(input.normal * 0.5 + 0.5, 1.0);
  output.position =
    uniforms.projection * uniforms.view * uniforms.model * vec4f(input.position, 1.0);
  return output;
}

@fragment
fn fragmentMain(input: VertexOut) -> @location(0) vec4f {
  return input.color;
}
`;

// defineCommand is a typed identity helper: the command stays a plain object
const drawCmd = gpu.defineCommand({
  // depthClearValue implies a context-managed, canvas-sized depth texture
  pass: { clearValue: [0.2, 0.2, 0.2, 1], depthClearValue: 1 },
  pipeline: { shader, depthWriteEnabled: true, cullMode: "back" },
  attributes: {
    position: gpu.createBuffer(ctx, { usage: "vertex", data: geometry.positions }),
    normal: gpu.createBuffer(ctx, { usage: "vertex", data: geometry.normals }),
  },
  indices: gpu.createBuffer(ctx, { usage: "index", data: geometry.cells }),
  uniforms: {
    // WebGPU clip-space depth is [0, 1]: use the ZO projection variants
    projection: mat4.perspectiveZO(mat4.create(), Math.PI / 4, ctx.width / ctx.height, 0.1, 100),
    view: mat4.lookAt(mat4.create(), [2, 2, 3], [0, 0, 0], [0, 1, 0]),
    model: mat4.create(),
  },
});

gpu.frame(ctx, () => {
  gpu.submit(ctx, drawCmd);
});
```

Every WebGPU feature has a dedicated example: MRT, MSAA resolve, compute + storage buffers, indirect draws, async picking, timestamp queries and render bundles — including hand-built pipelines and bind groups replayed inside a declarative pass ([`examples/render-bundles.js`](examples/render-bundles.js)).

## API

<!-- api-start -->
<!-- api-end -->

## License

MIT. See [license file](https://github.com/pex-gl/pex-gpu/blob/main/LICENSE.md).
