@vgpu/render 0.0.4 → 0.0.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,8 +1,8 @@
1
1
  # @vgpu/render
2
2
 
3
- > 0.0.1 — early preview
3
+ > 0.0.6 — early preview
4
4
 
5
- `@vgpu/render` is the small rendering layer on top of `@vgpu/core`. It currently focuses on two public building blocks: creating a render pipeline from a vgpu shader wrapper, and issuing a render pass against a texture or texture view. The package is intentionally narrow in 0.0.1 and leaves higher-level scene helpers for later releases.
5
+ `@vgpu/render` is the small rendering layer on top of `@vgpu/core`. It focuses on explicit WebGPU-style control: create pipelines, encode standalone render passes, or build one frame command encoder with multiple user-ordered passes.
6
6
 
7
7
  ## Install
8
8
 
@@ -13,15 +13,23 @@ pnpm add @vgpu/render
13
13
  ## Exports
14
14
 
15
15
  ### Runtime
16
- - `createRenderPipeline`
16
+ - `createRenderPipeline` / `createRenderPipelineAsync`
17
17
  - `RenderPass`
18
+ - `beginFrame` / `Frame`
19
+ - `createRenderBundle` / `RenderBundleRecorder`
20
+ - `RapidRenderer`
18
21
 
19
22
  ### Types
20
23
  - `RenderPipelineOptions`
21
24
  - `ColorAttachment`
25
+ - `DepthStencilAttachment`
22
26
  - `RenderPassOptions`
27
+ - `RenderPassDrawOptions`
28
+ - `RenderPassDynamicOffsets`
29
+ - `FrameOptions`
30
+ - `RenderBundleOptions`
23
31
 
24
- ## Usage
32
+ ## Standalone pass
25
33
 
26
34
  ```ts
27
35
  import { App } from "@vgpu/core";
@@ -47,9 +55,90 @@ const pass = new RenderPass(device, {
47
55
  });
48
56
  pass.setPipeline(pipeline);
49
57
  pass.draw(3);
50
- pass.end();
58
+ pass.end(); // finishes and submits this one-shot pass
51
59
  ```
52
60
 
61
+ ## Rapid renderer
62
+
63
+ `RapidRenderer` is available for examples and simple one-draw submissions:
64
+
65
+ ```ts
66
+ import { RapidRenderer } from "@vgpu/render";
67
+
68
+ const renderer = new RapidRenderer(device);
69
+ material.writeUniforms({ viewProjection, model, cameraPosition, light });
70
+ await renderer.draw({ material, mesh, target, depthTarget });
71
+ ```
72
+
73
+ For homepage-grade hot paths, do not resolve shaders or rebuild pipelines inside
74
+ the animation-frame path. If shader source truly changes dynamically, resolve and
75
+ create the replacement pipeline outside the frame loop, then stage or
76
+ double-buffer the swap so a completed pipeline is installed at a frame boundary.
77
+
78
+ ## Explicit multipass frame
79
+
80
+ Render bundles are setup-time reusable draw packets, not hidden passes or a render graph:
81
+
82
+ ```ts
83
+ import { beginFrame, createRenderBundle } from "@vgpu/render";
84
+
85
+ const bundle = createRenderBundle(device, {
86
+ label: "hero.light-sources.bundle",
87
+ colorFormats: ["rgba8unorm"],
88
+ depthStencilFormat: "depth24plus",
89
+ sampleCount: 1,
90
+ record(bundle) {
91
+ bundle.setPipeline(lightPipeline);
92
+ bundle.setBindGroup(0, lightBindGroup);
93
+ bundle.draw(lightVertexCount);
94
+ },
95
+ });
96
+ ```
97
+
98
+ Native WebGPU multipass code uses one command encoder, explicit pass begin/end calls, then one finish/submit:
99
+
100
+ ```ts
101
+ const encoder = device.gpu.createCommandEncoder({ label: "hero.frame" });
102
+
103
+ const lightPass = encoder.beginRenderPass(lightPassDescriptor);
104
+ lightPass.executeBundles([bundle]);
105
+ lightPass.end();
106
+
107
+ encoder.writeTimestamp(querySet, 0);
108
+ encoder.copyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, byteLength);
109
+
110
+ const compositePass = encoder.beginRenderPass(compositePassDescriptor);
111
+ compositePass.setPipeline(compositePipeline);
112
+ compositePass.setBindGroup(0, compositeBindGroup);
113
+ compositePass.draw(3);
114
+ compositePass.end();
115
+
116
+ device.queue.gpu.submit([encoder.finish()]);
117
+ ```
118
+
119
+ With VGPU `Frame`, the lifecycle is the same but pass end and final submission are harder to get wrong:
120
+
121
+ ```ts
122
+ const frame = beginFrame(device, { label: "hero.frame" });
123
+ frame.renderPass(lightPassDescriptor, (pass) => {
124
+ pass.executeBundles([bundle]);
125
+ });
126
+ frame.gpu.writeTimestamp(querySet, 0);
127
+ frame.copyBufferToBuffer(srcBuffer, dstBuffer, byteLength);
128
+ frame.renderPass(compositePassDescriptor, (pass) => {
129
+ pass.setPipeline(compositePipeline);
130
+ pass.setBindGroup(0, compositeBindGroup);
131
+ pass.draw(3);
132
+ });
133
+ frame.submit(); // finishes once and submits once
134
+ ```
135
+
136
+ `Frame` preserves authored ordering and exposes its raw `GPUCommandEncoder` as `frame.gpu` for advanced commands. Direct raw encoder calls follow WebGPU behavior; VGPU helper methods guard use after `submit()` with `VGPU-FRAME-SUBMITTED`.
137
+
138
+ For homepage-grade hot paths, create pipelines (sync or async), buffers, bind groups, and render bundles during setup/warmup or resize, then keep per-frame code to command encoding and one queue submit. `createRenderPipelineAsync()` defaults to `fallback: "sync"` with a once-only diagnostic when native async creation is unavailable; use `fallback: "throw"` if warmup must fail rather than block. Avoid runtime `resolveShader()`/shader creation in frame loops; dynamically changing shader source should be resolved and pipelined off the frame path, with staged or double-buffered swaps at frame boundaries.
139
+
140
+ Raw `.gpu` properties such as `device.gpu`, `queue.gpu`, `buffer.gpu`, `texture.gpu`, `shader.gpu`, `frame.gpu`, and `RenderBundleRecorder.gpu` are intentional advanced escape hatches to native WebGPU objects and are treated as semver-protected public API. Native WebGPU validation and lifecycle rules still apply when using them directly.
141
+
53
142
  ## License
54
143
 
55
144
  MIT.
@@ -0,0 +1,19 @@
1
+ import { Buffer, type Device } from "@vgpu/core";
2
+ import { type RenderPass, type RenderPassOptions } from "./render-pass.ts";
3
+ export interface FrameOptions {
4
+ readonly label?: string;
5
+ }
6
+ export type FrameRenderPassCallback = (pass: RenderPass) => void;
7
+ export declare class Frame {
8
+ private readonly device;
9
+ readonly gpu: GPUCommandEncoder;
10
+ private submitted;
11
+ constructor(device: Device, opts?: FrameOptions);
12
+ renderPass(opts: RenderPassOptions, record: FrameRenderPassCallback): void;
13
+ copyBufferToBuffer(source: Buffer | GPUBuffer, destination: Buffer | GPUBuffer, size: GPUSize64, sourceOffset?: number, destinationOffset?: number): void;
14
+ submit(): void;
15
+ dispose(): void;
16
+ private assertOpen;
17
+ }
18
+ export declare function beginFrame(device: Device, opts?: FrameOptions): Frame;
19
+ //# sourceMappingURL=frame.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.d.ts","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAC5D,OAAO,EAA6B,KAAK,UAAU,EAAE,KAAK,iBAAiB,EAAE,MAAM,kBAAkB,CAAC;AAEtG,MAAM,WAAW,YAAY;IAC3B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,MAAM,uBAAuB,GAAG,CAAC,IAAI,EAAE,UAAU,KAAK,IAAI,CAAC;AAEjE,qBAAa,KAAK;IAIJ,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,QAAQ,CAAC,GAAG,EAAE,iBAAiB,CAAC;IAChC,OAAO,CAAC,SAAS,CAAS;gBAEG,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB;IAIpE,UAAU,CAAC,IAAI,EAAE,iBAAiB,EAAE,MAAM,EAAE,uBAAuB,GAAG,IAAI;IAU1E,kBAAkB,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,EAAE,WAAW,EAAE,MAAM,GAAG,SAAS,EAAE,IAAI,EAAE,SAAS,EAAE,YAAY,SAAI,EAAE,iBAAiB,SAAI,GAAG,IAAI;IAK/I,MAAM,IAAI,IAAI;IAMd,OAAO,IAAI,IAAI;IAIf,OAAO,CAAC,UAAU;CAQnB;AAED,wBAAgB,UAAU,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,GAAE,YAAiB,GAAG,KAAK,CAEzE"}
package/dist/frame.js ADDED
@@ -0,0 +1,50 @@
1
+ import { Buffer, VGPUError } from "@vgpu/core";
2
+ import { createRenderPassOnEncoder } from "./render-pass.js";
3
+ export class Frame {
4
+ device;
5
+ gpu;
6
+ submitted = false;
7
+ constructor(device, opts = {}) {
8
+ this.device = device;
9
+ this.gpu = device.gpu.createCommandEncoder({ label: opts.label });
10
+ }
11
+ renderPass(opts, record) {
12
+ this.assertOpen("Frame.renderPass");
13
+ const pass = createRenderPassOnEncoder(this.device, opts, this.gpu);
14
+ try {
15
+ record(pass);
16
+ }
17
+ finally {
18
+ pass.end();
19
+ }
20
+ }
21
+ copyBufferToBuffer(source, destination, size, sourceOffset = 0, destinationOffset = 0) {
22
+ this.assertOpen("Frame.copyBufferToBuffer");
23
+ this.gpu.copyBufferToBuffer(gpuBuffer(source), sourceOffset, gpuBuffer(destination), destinationOffset, size);
24
+ }
25
+ submit() {
26
+ this.assertOpen("Frame.submit");
27
+ this.submitted = true;
28
+ this.device.queue.gpu.submit([this.gpu.finish()]);
29
+ }
30
+ dispose() {
31
+ if (!this.submitted)
32
+ this.submit();
33
+ }
34
+ assertOpen(where) {
35
+ if (!this.submitted)
36
+ return;
37
+ throw new VGPUError({
38
+ code: "VGPU-FRAME-SUBMITTED",
39
+ message: "Frame cannot encode or submit after submit().",
40
+ where,
41
+ });
42
+ }
43
+ }
44
+ export function beginFrame(device, opts = {}) {
45
+ return new Frame(device, opts);
46
+ }
47
+ function gpuBuffer(buffer) {
48
+ return buffer instanceof Buffer ? buffer.gpu : buffer;
49
+ }
50
+ //# sourceMappingURL=frame.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"frame.js","sourceRoot":"","sources":["../src/frame.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AAC5D,OAAO,EAAE,yBAAyB,EAA2C,MAAM,kBAAkB,CAAC;AAQtG,MAAM,OAAO,KAAK;IAIa;IAHpB,GAAG,CAAoB;IACxB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAA6B,MAAc,EAAE,OAAqB,EAAE;QAAvC,WAAM,GAAN,MAAM,CAAQ;QACzC,IAAI,CAAC,GAAG,GAAG,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACpE,CAAC;IAED,UAAU,CAAC,IAAuB,EAAE,MAA+B;QACjE,IAAI,CAAC,UAAU,CAAC,kBAAkB,CAAC,CAAC;QACpC,MAAM,IAAI,GAAG,yBAAyB,CAAC,IAAI,CAAC,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC;QACpE,IAAI,CAAC;YACH,MAAM,CAAC,IAAI,CAAC,CAAC;QACf,CAAC;gBAAS,CAAC;YACT,IAAI,CAAC,GAAG,EAAE,CAAC;QACb,CAAC;IACH,CAAC;IAED,kBAAkB,CAAC,MAA0B,EAAE,WAA+B,EAAE,IAAe,EAAE,YAAY,GAAG,CAAC,EAAE,iBAAiB,GAAG,CAAC;QACtI,IAAI,CAAC,UAAU,CAAC,0BAA0B,CAAC,CAAC;QAC5C,IAAI,CAAC,GAAG,CAAC,kBAAkB,CAAC,SAAS,CAAC,MAAM,CAAC,EAAE,YAAY,EAAE,SAAS,CAAC,WAAW,CAAC,EAAE,iBAAiB,EAAE,IAAI,CAAC,CAAC;IAChH,CAAC;IAED,MAAM;QACJ,IAAI,CAAC,UAAU,CAAC,cAAc,CAAC,CAAC;QAChC,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,GAAG,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACpD,CAAC;IAED,OAAO;QACL,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,IAAI,CAAC,MAAM,EAAE,CAAC;IACrC,CAAC;IAEO,UAAU,CAAC,KAAa;QAC9B,IAAI,CAAC,IAAI,CAAC,SAAS;YAAE,OAAO;QAC5B,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,sBAAsB;YAC5B,OAAO,EAAE,+CAA+C;YACxD,KAAK;SACN,CAAC,CAAC;IACL,CAAC;CACF;AAED,MAAM,UAAU,UAAU,CAAC,MAAc,EAAE,OAAqB,EAAE;IAChE,OAAO,IAAI,KAAK,CAAC,MAAM,EAAE,IAAI,CAAC,CAAC;AACjC,CAAC;AAED,SAAS,SAAS,CAAC,MAA0B;IAC3C,OAAO,MAAM,YAAY,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACxD,CAAC"}
package/dist/index.d.ts CHANGED
@@ -1,11 +1,15 @@
1
- export { createRenderPipeline } from "./pipeline.ts";
1
+ export { beginFrame, Frame } from "./frame.ts";
2
+ export { createRenderBundle, RenderBundleRecorder } from "./render-bundle.ts";
3
+ export { createRenderPipeline, createRenderPipelineAsync } from "./pipeline.ts";
2
4
  export { RenderPass } from "./render-pass.ts";
3
5
  export { RapidRenderer } from "./rapid-renderer.ts";
4
6
  export { UniformPool } from "./uniform-pool.ts";
5
- export type { RenderPipelineOptions } from "./pipeline.ts";
7
+ export type { FrameOptions, FrameRenderPassCallback } from "./frame.ts";
8
+ export type { RenderBundleOptions } from "./render-bundle.ts";
9
+ export type { RenderPipelineAsyncFallback, RenderPipelineFragmentOptions, RenderPipelineOptions, RenderPipelineShaderInput, RenderPipelineStageOptions, RenderPipelineVertexOptions, } from "./pipeline.ts";
6
10
  export type { DrawSpec } from "./rapid-renderer.ts";
7
11
  export type { UniformLayout, UniformPoolOptions, UniformSlot } from "./uniform-pool-types.ts";
8
- export type { ColorAttachment, RenderPassDrawOptions, RenderPassDynamicOffsets, RenderPassOptions, } from "./render-pass.ts";
12
+ export type { ColorAttachment, DepthStencilAttachment, RenderPassDrawOptions, RenderPassDynamicOffsets, RenderPassOptions, } from "./render-pass.ts";
9
13
  export { box, capsule, cone, cylinder, degToRad, disk, dodecahedron, fullscreenQuad, getMaterialDeclarations, icosahedron, icosphere, material, Mesh, octahedron, orthographicCamera, perspectiveCamera, plane, ring, sampler, sphere, srgb, tetrahedron, torus, wgslDeclarations } from "./domain/index.ts";
10
14
  export type { BoxSpec, Camera, CapsuleSpec, ConeSpec, CylinderSpec, DiskSpec, FullscreenQuadSpec, IcosphereSpec, Material, MaterialSamplerSpec, MaterialSpec, MaterialTextureSpec, MaterialUniformValue, Mat4, MeshGpu, MeshPrimitive, PlaneSpec, PolyhedronSpec, RingSpec, Sampler, SamplerSpec, SphereSpec, TextureKind, TextureSpec, TextureValue, TorusSpec, Vec3, VertexAttributes, VertexLayoutKind, WgslUniformType, WriteTextureValues } from "./domain/index.ts";
11
15
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,qBAAqB,EAAE,MAAM,eAAe,CAAC;AAC3D,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC9F,YAAY,EACV,eAAe,EACf,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC7S,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,aAAa,EAAE,QAAQ,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAChD,YAAY,EAAE,YAAY,EAAE,uBAAuB,EAAE,MAAM,YAAY,CAAC;AACxE,YAAY,EAAE,mBAAmB,EAAE,MAAM,oBAAoB,CAAC;AAC9D,YAAY,EACV,2BAA2B,EAC3B,6BAA6B,EAC7B,qBAAqB,EACrB,yBAAyB,EACzB,0BAA0B,EAC1B,2BAA2B,GAC5B,MAAM,eAAe,CAAC;AACvB,YAAY,EAAE,QAAQ,EAAE,MAAM,qBAAqB,CAAC;AACpD,YAAY,EAAE,aAAa,EAAE,kBAAkB,EAAE,WAAW,EAAE,MAAM,yBAAyB,CAAC;AAC9F,YAAY,EACV,eAAe,EACf,sBAAsB,EACtB,qBAAqB,EACrB,wBAAwB,EACxB,iBAAiB,GAClB,MAAM,kBAAkB,CAAC;AAC1B,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC;AAC7S,YAAY,EAAE,OAAO,EAAE,MAAM,EAAE,WAAW,EAAE,QAAQ,EAAE,YAAY,EAAE,QAAQ,EAAE,kBAAkB,EAAE,aAAa,EAAE,QAAQ,EAAE,mBAAmB,EAAE,YAAY,EAAE,mBAAmB,EAAE,oBAAoB,EAAE,IAAI,EAAE,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,cAAc,EAAE,QAAQ,EAAE,OAAO,EAAE,WAAW,EAAE,UAAU,EAAE,WAAW,EAAE,WAAW,EAAE,YAAY,EAAE,SAAS,EAAE,IAAI,EAAE,gBAAgB,EAAE,gBAAgB,EAAE,eAAe,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC"}
package/dist/index.js CHANGED
@@ -1,4 +1,6 @@
1
- export { createRenderPipeline } from "./pipeline.js";
1
+ export { beginFrame, Frame } from "./frame.js";
2
+ export { createRenderBundle, RenderBundleRecorder } from "./render-bundle.js";
3
+ export { createRenderPipeline, createRenderPipelineAsync } from "./pipeline.js";
2
4
  export { RenderPass } from "./render-pass.js";
3
5
  export { RapidRenderer } from "./rapid-renderer.js";
4
6
  export { UniformPool } from "./uniform-pool.js";
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,oBAAoB,EAAE,MAAM,eAAe,CAAC;AACrD,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAUhD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAC/C,OAAO,EAAE,kBAAkB,EAAE,oBAAoB,EAAE,MAAM,oBAAoB,CAAC;AAC9E,OAAO,EAAE,oBAAoB,EAAE,yBAAyB,EAAE,MAAM,eAAe,CAAC;AAChF,OAAO,EAAE,UAAU,EAAE,MAAM,kBAAkB,CAAC;AAC9C,OAAO,EAAE,aAAa,EAAE,MAAM,qBAAqB,CAAC;AACpD,OAAO,EAAE,WAAW,EAAE,MAAM,mBAAmB,CAAC;AAoBhD,OAAO,EAAE,GAAG,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,QAAQ,EAAE,IAAI,EAAE,YAAY,EAAE,cAAc,EAAE,uBAAuB,EAAE,WAAW,EAAE,SAAS,EAAE,QAAQ,EAAE,IAAI,EAAE,UAAU,EAAE,kBAAkB,EAAE,iBAAiB,EAAE,KAAK,EAAE,IAAI,EAAE,OAAO,EAAE,MAAM,EAAE,IAAI,EAAE,WAAW,EAAE,KAAK,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAC"}
@@ -1,16 +1,37 @@
1
- import type { Device, Shader } from "@vgpu/core";
1
+ import { Shader, type Device } from "@vgpu/core";
2
+ export type RenderPipelineShaderInput = Shader | GPUShaderModule;
3
+ export type RenderPipelineAsyncFallback = "sync" | "throw";
4
+ export interface RenderPipelineStageOptions {
5
+ /** VGPU Shader or raw GPUShaderModule for this stage. Defaults to RenderPipelineOptions.shader. */
6
+ readonly shader?: RenderPipelineShaderInput;
7
+ /** Descriptor-like alias for shader. */
8
+ readonly module?: RenderPipelineShaderInput;
9
+ /** Entry-point name. Kept for backwards compatibility with the first render helper API. */
10
+ readonly entry?: string;
11
+ /** Descriptor-like entry-point name. */
12
+ readonly entryPoint?: string;
13
+ readonly constants?: Record<string, GPUPipelineConstantValue>;
14
+ }
15
+ export interface RenderPipelineVertexOptions extends RenderPipelineStageOptions {
16
+ readonly buffers?: readonly (GPUVertexBufferLayout | null)[];
17
+ }
18
+ export interface RenderPipelineFragmentOptions extends RenderPipelineStageOptions {
19
+ readonly targets: readonly (GPUColorTargetState | null)[];
20
+ }
2
21
  export interface RenderPipelineOptions {
3
- readonly shader: Shader;
4
- readonly vertex: {
5
- readonly entry: string;
6
- };
7
- readonly fragment: {
8
- readonly entry: string;
9
- readonly targets: readonly GPUColorTargetState[];
10
- };
22
+ /** Optional shared shader module used by vertex/fragment stages that do not provide their own module. */
23
+ readonly shader?: RenderPipelineShaderInput;
24
+ readonly vertex: RenderPipelineVertexOptions;
25
+ readonly fragment?: RenderPipelineFragmentOptions;
11
26
  readonly primitive?: GPUPrimitiveState;
27
+ readonly depthStencil?: GPUDepthStencilState;
28
+ readonly multisample?: GPUMultisampleState;
12
29
  readonly layout?: GPUPipelineLayout | "auto";
13
30
  readonly label?: string;
31
+ /** createRenderPipelineAsync fallback when GPUDevice.createRenderPipelineAsync is unavailable. Defaults to "sync". */
32
+ readonly fallback?: RenderPipelineAsyncFallback;
14
33
  }
15
34
  export declare function createRenderPipeline(device: Device, opts: RenderPipelineOptions): GPURenderPipeline;
35
+ export declare function createRenderPipelineAsync(device: Device, opts: RenderPipelineOptions): Promise<GPURenderPipeline>;
36
+ export declare function __resetCreateRenderPipelineAsyncFallbackWarningForTests(): void;
16
37
  //# sourceMappingURL=pipeline.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAEjD,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,MAAM,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAC5C,QAAQ,CAAC,QAAQ,EAAE;QAAE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;QAAC,QAAQ,CAAC,OAAO,EAAE,SAAS,mBAAmB,EAAE,CAAA;KAAE,CAAC;IAChG,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,iBAAiB,CAQnG"}
1
+ {"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE5D,MAAM,MAAM,yBAAyB,GAAG,MAAM,GAAG,eAAe,CAAC;AACjE,MAAM,MAAM,2BAA2B,GAAG,MAAM,GAAG,OAAO,CAAC;AAE3D,MAAM,WAAW,0BAA0B;IACzC,mGAAmG;IACnG,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAC5C,wCAAwC;IACxC,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAC5C,2FAA2F;IAC3F,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,wCAAwC;IACxC,QAAQ,CAAC,UAAU,CAAC,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,wBAAwB,CAAC,CAAC;CAC/D;AAED,MAAM,WAAW,2BAA4B,SAAQ,0BAA0B;IAC7E,QAAQ,CAAC,OAAO,CAAC,EAAE,SAAS,CAAC,qBAAqB,GAAG,IAAI,CAAC,EAAE,CAAC;CAC9D;AAED,MAAM,WAAW,6BAA8B,SAAQ,0BAA0B;IAC/E,QAAQ,CAAC,OAAO,EAAE,SAAS,CAAC,mBAAmB,GAAG,IAAI,CAAC,EAAE,CAAC;CAC3D;AAED,MAAM,WAAW,qBAAqB;IACpC,yGAAyG;IACzG,QAAQ,CAAC,MAAM,CAAC,EAAE,yBAAyB,CAAC;IAC5C,QAAQ,CAAC,MAAM,EAAE,2BAA2B,CAAC;IAC7C,QAAQ,CAAC,QAAQ,CAAC,EAAE,6BAA6B,CAAC;IAClD,QAAQ,CAAC,SAAS,CAAC,EAAE,iBAAiB,CAAC;IACvC,QAAQ,CAAC,YAAY,CAAC,EAAE,oBAAoB,CAAC;IAC7C,QAAQ,CAAC,WAAW,CAAC,EAAE,mBAAmB,CAAC;IAC3C,QAAQ,CAAC,MAAM,CAAC,EAAE,iBAAiB,GAAG,MAAM,CAAC;IAC7C,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,sHAAsH;IACtH,QAAQ,CAAC,QAAQ,CAAC,EAAE,2BAA2B,CAAC;CACjD;AAOD,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,iBAAiB,CAEnG;AAED,wBAAsB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,qBAAqB,GAAG,OAAO,CAAC,iBAAiB,CAAC,CAkBvH;AAkDD,wBAAgB,uDAAuD,IAAI,IAAI,CAE9E"}
package/dist/pipeline.js CHANGED
@@ -1,10 +1,77 @@
1
+ import { Shader, VGPUError } from "@vgpu/core";
2
+ // Warn once per JS process. This keeps compatibility fallback visible without spamming
3
+ // apps that intentionally warm up multiple pipelines on implementations without
4
+ // GPUDevice.createRenderPipelineAsync().
5
+ let didWarnAboutAsyncFallback = false;
1
6
  export function createRenderPipeline(device, opts) {
2
- return device.gpu.createRenderPipeline({
7
+ return device.gpu.createRenderPipeline(toRenderPipelineDescriptor(opts));
8
+ }
9
+ export async function createRenderPipelineAsync(device, opts) {
10
+ const descriptor = toRenderPipelineDescriptor(opts);
11
+ const createAsync = device.gpu.createRenderPipelineAsync;
12
+ if (typeof createAsync === "function") {
13
+ return createAsync.call(device.gpu, descriptor);
14
+ }
15
+ if ((opts.fallback ?? "sync") === "throw") {
16
+ throw new VGPUError({
17
+ code: "VGPU-RENDER-PIPELINE-ASYNC-UNAVAILABLE",
18
+ message: "GPUDevice.createRenderPipelineAsync is unavailable on this WebGPU implementation.",
19
+ fix: "Use fallback: 'sync' for compatibility or call createRenderPipeline() explicitly during setup/warmup.",
20
+ where: "createRenderPipelineAsync",
21
+ });
22
+ }
23
+ warnAsyncFallbackOnce();
24
+ return device.gpu.createRenderPipeline(descriptor);
25
+ }
26
+ function toRenderPipelineDescriptor(opts) {
27
+ const descriptor = {
3
28
  label: opts.label,
4
29
  layout: opts.layout ?? "auto",
5
- vertex: { module: opts.shader.gpu, entryPoint: opts.vertex.entry },
6
- fragment: { module: opts.shader.gpu, entryPoint: opts.fragment.entry, targets: [...opts.fragment.targets] },
30
+ vertex: {
31
+ module: stageModule(opts.vertex, opts.shader, "vertex"),
32
+ entryPoint: stageEntryPoint(opts.vertex),
33
+ constants: opts.vertex.constants,
34
+ buffers: opts.vertex.buffers ? [...opts.vertex.buffers] : undefined,
35
+ },
7
36
  primitive: opts.primitive,
8
- });
37
+ depthStencil: opts.depthStencil,
38
+ multisample: opts.multisample,
39
+ };
40
+ if (opts.fragment) {
41
+ descriptor.fragment = {
42
+ module: stageModule(opts.fragment, opts.shader, "fragment"),
43
+ entryPoint: stageEntryPoint(opts.fragment),
44
+ constants: opts.fragment.constants,
45
+ targets: [...opts.fragment.targets],
46
+ };
47
+ }
48
+ return descriptor;
49
+ }
50
+ function stageModule(stage, fallback, stageName) {
51
+ const module = stage.module ?? stage.shader ?? fallback;
52
+ if (!module) {
53
+ throw new VGPUError({
54
+ code: "VGPU-RENDER-PIPELINE-MISSING-SHADER",
55
+ message: `Missing shader module for ${stageName} stage.`,
56
+ fix: "Pass options.shader for a shared module or pass vertex/fragment module or shader.",
57
+ where: `createRenderPipeline.${stageName}`,
58
+ });
59
+ }
60
+ return isShader(module) ? module.gpu : module;
61
+ }
62
+ function stageEntryPoint(stage) {
63
+ return stage.entryPoint ?? stage.entry;
64
+ }
65
+ function isShader(input) {
66
+ return input instanceof Shader;
67
+ }
68
+ export function __resetCreateRenderPipelineAsyncFallbackWarningForTests() {
69
+ didWarnAboutAsyncFallback = false;
70
+ }
71
+ function warnAsyncFallbackOnce() {
72
+ if (didWarnAboutAsyncFallback)
73
+ return;
74
+ didWarnAboutAsyncFallback = true;
75
+ globalThis.console?.warn?.("[vgpu/render] createRenderPipelineAsync is unavailable; falling back to synchronous createRenderPipeline(). Pass fallback: 'throw' to make this a structured VGPUError.");
9
76
  }
10
77
  //# sourceMappingURL=pipeline.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAWA,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,IAA2B;IAC9E,OAAO,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC;QACrC,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,MAAM,EAAE,IAAI,CAAC,MAAM,IAAI,MAAM;QAC7B,MAAM,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,MAAM,CAAC,KAAK,EAAE;QAClE,QAAQ,EAAE,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,EAAE,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,OAAO,EAAE,CAAC,GAAG,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,EAAE;QAC3G,SAAS,EAAE,IAAI,CAAC,SAAS;KAC1B,CAAC,CAAC;AACL,CAAC"}
1
+ {"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,24 @@
1
+ import { Buffer } from "@vgpu/core";
2
+ import type { RenderPassDrawOptions, RenderPassDynamicOffsets } from "./render-pass.ts";
3
+ export interface RenderBundleOptions {
4
+ readonly label?: string;
5
+ readonly colorFormats: readonly (GPUTextureFormat | null)[];
6
+ readonly depthStencilFormat?: GPUTextureFormat;
7
+ readonly sampleCount?: number;
8
+ readonly depthReadOnly?: boolean;
9
+ readonly stencilReadOnly?: boolean;
10
+ readonly record: (bundle: RenderBundleRecorder) => void;
11
+ }
12
+ export declare class RenderBundleRecorder {
13
+ readonly gpu: GPURenderBundleEncoder;
14
+ constructor(gpu: GPURenderBundleEncoder);
15
+ setPipeline(pipeline: GPURenderPipeline): void;
16
+ setBindGroup(index: number, group: GPUBindGroup | null, dynamicOffsets?: RenderPassDynamicOffsets): void;
17
+ setVertexBuffer(slot: number, buffer: Buffer | GPUBuffer | null, offset?: number, size?: GPUSize64): void;
18
+ draw(options: RenderPassDrawOptions): void;
19
+ draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void;
20
+ }
21
+ export declare function createRenderBundle(device: {
22
+ readonly gpu: GPUDevice;
23
+ }, opts: RenderBundleOptions): GPURenderBundle;
24
+ //# sourceMappingURL=render-bundle.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-bundle.d.ts","sourceRoot":"","sources":["../src/render-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AACpC,OAAO,KAAK,EAAE,qBAAqB,EAAE,wBAAwB,EAAE,MAAM,kBAAkB,CAAC;AAExF,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,YAAY,EAAE,SAAS,CAAC,gBAAgB,GAAG,IAAI,CAAC,EAAE,CAAC;IAC5D,QAAQ,CAAC,kBAAkB,CAAC,EAAE,gBAAgB,CAAC;IAC/C,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;IACnC,QAAQ,CAAC,MAAM,EAAE,CAAC,MAAM,EAAE,oBAAoB,KAAK,IAAI,CAAC;CACzD;AAED,qBAAa,oBAAoB;IACnB,QAAQ,CAAC,GAAG,EAAE,sBAAsB;gBAA3B,GAAG,EAAE,sBAAsB;IAEhD,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAI9C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,EAAE,cAAc,CAAC,EAAE,wBAAwB,GAAG,IAAI;IAIxG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,SAAI,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI;IAIpG,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAC1C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;CAatG;AAED,wBAAgB,kBAAkB,CAAC,MAAM,EAAE;IAAE,QAAQ,CAAC,GAAG,EAAE,SAAS,CAAA;CAAE,EAAE,IAAI,EAAE,mBAAmB,GAAG,eAAe,CAWlH"}
@@ -0,0 +1,39 @@
1
+ import { Buffer } from "@vgpu/core";
2
+ export class RenderBundleRecorder {
3
+ gpu;
4
+ constructor(gpu) {
5
+ this.gpu = gpu;
6
+ }
7
+ setPipeline(pipeline) {
8
+ this.gpu.setPipeline(pipeline);
9
+ }
10
+ setBindGroup(index, group, dynamicOffsets) {
11
+ this.gpu.setBindGroup(index, group, dynamicOffsets);
12
+ }
13
+ setVertexBuffer(slot, buffer, offset = 0, size) {
14
+ this.gpu.setVertexBuffer(slot, gpuBuffer(buffer), offset, size);
15
+ }
16
+ draw(optionsOrVertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
17
+ if (typeof optionsOrVertexCount === "number") {
18
+ this.gpu.draw(optionsOrVertexCount, instanceCount, firstVertex, firstInstance);
19
+ return;
20
+ }
21
+ this.gpu.draw(optionsOrVertexCount.vertexCount, optionsOrVertexCount.instanceCount ?? 1, optionsOrVertexCount.firstVertex ?? 0, optionsOrVertexCount.firstInstance ?? 0);
22
+ }
23
+ }
24
+ export function createRenderBundle(device, opts) {
25
+ const encoder = device.gpu.createRenderBundleEncoder({
26
+ label: opts.label,
27
+ colorFormats: opts.colorFormats,
28
+ depthStencilFormat: opts.depthStencilFormat,
29
+ sampleCount: opts.sampleCount,
30
+ depthReadOnly: opts.depthReadOnly,
31
+ stencilReadOnly: opts.stencilReadOnly,
32
+ });
33
+ opts.record(new RenderBundleRecorder(encoder));
34
+ return encoder.finish({ label: opts.label });
35
+ }
36
+ function gpuBuffer(buffer) {
37
+ return buffer instanceof Buffer ? buffer.gpu : buffer;
38
+ }
39
+ //# sourceMappingURL=render-bundle.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"render-bundle.js","sourceRoot":"","sources":["../src/render-bundle.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,MAAM,YAAY,CAAC;AAapC,MAAM,OAAO,oBAAoB;IACV;IAArB,YAAqB,GAA2B;QAA3B,QAAG,GAAH,GAAG,CAAwB;IAAG,CAAC;IAEpD,WAAW,CAAC,QAA2B;QACrC,IAAI,CAAC,GAAG,CAAC,WAAW,CAAC,QAAQ,CAAC,CAAC;IACjC,CAAC;IAED,YAAY,CAAC,KAAa,EAAE,KAA0B,EAAE,cAAyC;QAC/F,IAAI,CAAC,GAAG,CAAC,YAAY,CAAC,KAAK,EAAE,KAAK,EAAE,cAAc,CAAC,CAAC;IACtD,CAAC;IAED,eAAe,CAAC,IAAY,EAAE,MAAiC,EAAE,MAAM,GAAG,CAAC,EAAE,IAAgB;QAC3F,IAAI,CAAC,GAAG,CAAC,eAAe,CAAC,IAAI,EAAE,SAAS,CAAC,MAAM,CAAC,EAAE,MAAM,EAAE,IAAI,CAAC,CAAC;IAClE,CAAC;IAID,IAAI,CAAC,oBAAoD,EAAE,aAAa,GAAG,CAAC,EAAE,WAAW,GAAG,CAAC,EAAE,aAAa,GAAG,CAAC;QAC9G,IAAI,OAAO,oBAAoB,KAAK,QAAQ,EAAE,CAAC;YAC7C,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,oBAAoB,EAAE,aAAa,EAAE,WAAW,EAAE,aAAa,CAAC,CAAC;YAC/E,OAAO;QACT,CAAC;QACD,IAAI,CAAC,GAAG,CAAC,IAAI,CACX,oBAAoB,CAAC,WAAW,EAChC,oBAAoB,CAAC,aAAa,IAAI,CAAC,EACvC,oBAAoB,CAAC,WAAW,IAAI,CAAC,EACrC,oBAAoB,CAAC,aAAa,IAAI,CAAC,CACxC,CAAC;IACJ,CAAC;CACF;AAED,MAAM,UAAU,kBAAkB,CAAC,MAAmC,EAAE,IAAyB;IAC/F,MAAM,OAAO,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC;QACnD,KAAK,EAAE,IAAI,CAAC,KAAK;QACjB,YAAY,EAAE,IAAI,CAAC,YAAY;QAC/B,kBAAkB,EAAE,IAAI,CAAC,kBAAkB;QAC3C,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,aAAa,EAAE,IAAI,CAAC,aAAa;QACjC,eAAe,EAAE,IAAI,CAAC,eAAe;KACtC,CAAC,CAAC;IACH,IAAI,CAAC,MAAM,CAAC,IAAI,oBAAoB,CAAC,OAAO,CAAC,CAAC,CAAC;IAC/C,OAAO,OAAO,CAAC,MAAM,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;AAC/C,CAAC;AAED,SAAS,SAAS,CAAC,MAAiC;IAClD,OAAO,MAAM,YAAY,MAAM,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC;AACxD,CAAC"}
@@ -1,13 +1,25 @@
1
1
  import { Buffer, type Device, type Texture } from "@vgpu/core";
2
2
  export interface RenderPassOptions {
3
3
  readonly colorAttachments: readonly ColorAttachment[];
4
+ readonly depthStencilAttachment?: DepthStencilAttachment;
4
5
  readonly label?: string;
5
6
  }
6
7
  export interface ColorAttachment {
7
8
  readonly view: Texture | GPUTextureView;
8
9
  readonly loadOp: GPULoadOp;
9
10
  readonly storeOp: GPUStoreOp;
10
- readonly clearValue?: readonly [number, number, number, number];
11
+ readonly clearValue?: GPUColor;
12
+ }
13
+ export interface DepthStencilAttachment {
14
+ readonly view: Texture | GPUTextureView;
15
+ readonly depthClearValue?: number;
16
+ readonly depthLoadOp?: GPULoadOp;
17
+ readonly depthStoreOp?: GPUStoreOp;
18
+ readonly depthReadOnly?: boolean;
19
+ readonly stencilClearValue?: GPUStencilValue;
20
+ readonly stencilLoadOp?: GPULoadOp;
21
+ readonly stencilStoreOp?: GPUStoreOp;
22
+ readonly stencilReadOnly?: boolean;
11
23
  }
12
24
  export interface RenderPassDrawOptions {
13
25
  readonly vertexCount: number;
@@ -19,15 +31,18 @@ export type RenderPassDynamicOffsets = readonly GPUBufferDynamicOffset[] | Uint3
19
31
  export declare class RenderPass {
20
32
  private readonly device;
21
33
  private readonly encoder;
34
+ private readonly submitOnEnd;
22
35
  private passEncoder;
23
36
  constructor(device: Device, opts: RenderPassOptions);
24
37
  get gpu(): GPURenderPassEncoder;
25
38
  setPipeline(pipeline: GPURenderPipeline): void;
26
39
  setBindGroup(index: number, group: GPUBindGroup | null, dynamicOffsets?: RenderPassDynamicOffsets): void;
27
40
  setVertexBuffer(slot: number, buffer: Buffer | GPUBuffer | null, offset?: number, size?: GPUSize64): void;
41
+ executeBundles(bundles: Iterable<GPURenderBundle>): void;
28
42
  draw(options: RenderPassDrawOptions): void;
29
43
  draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void;
30
44
  end(): void;
31
45
  dispose(): void;
32
46
  }
47
+ export declare function createRenderPassOnEncoder(device: Device, opts: RenderPassOptions, encoder: GPUCommandEncoder): RenderPass;
33
48
  //# sourceMappingURL=render-pass.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"render-pass.d.ts","sourceRoot":"","sources":["../src/render-pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,eAAe,EAAE,CAAC;IACtD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,SAAS,CAAC,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,CAAC,CAAC;CACjE;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,wBAAwB,GAAG,SAAS,sBAAsB,EAAE,GAAG,WAAW,CAAC;AAEvF,qBAAa,UAAU;IAIT,OAAO,CAAC,QAAQ,CAAC,MAAM;IAHnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,WAAW,CAA8B;gBAEpB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;IAQpE,IAAI,GAAG,IAAI,oBAAoB,CAS9B;IAED,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAI9C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,EAAE,cAAc,CAAC,EAAE,wBAAwB,GAAG,IAAI;IAIxG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,SAAI,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI;IAIpG,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAC1C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAcrG,GAAG,IAAI,IAAI;IAQX,OAAO,IAAI,IAAI;CAGhB"}
1
+ {"version":3,"file":"render-pass.d.ts","sourceRoot":"","sources":["../src/render-pass.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAa,KAAK,MAAM,EAAE,KAAK,OAAO,EAAE,MAAM,YAAY,CAAC;AAI1E,MAAM,WAAW,iBAAiB;IAChC,QAAQ,CAAC,gBAAgB,EAAE,SAAS,eAAe,EAAE,CAAC;IACtD,QAAQ,CAAC,sBAAsB,CAAC,EAAE,sBAAsB,CAAC;IACzD,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;IACxC,QAAQ,CAAC,MAAM,EAAE,SAAS,CAAC;IAC3B,QAAQ,CAAC,OAAO,EAAE,UAAU,CAAC;IAC7B,QAAQ,CAAC,UAAU,CAAC,EAAE,QAAQ,CAAC;CAChC;AAED,MAAM,WAAW,sBAAsB;IACrC,QAAQ,CAAC,IAAI,EAAE,OAAO,GAAG,cAAc,CAAC;IACxC,QAAQ,CAAC,eAAe,CAAC,EAAE,MAAM,CAAC;IAClC,QAAQ,CAAC,WAAW,CAAC,EAAE,SAAS,CAAC;IACjC,QAAQ,CAAC,YAAY,CAAC,EAAE,UAAU,CAAC;IACnC,QAAQ,CAAC,aAAa,CAAC,EAAE,OAAO,CAAC;IACjC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,eAAe,CAAC;IAC7C,QAAQ,CAAC,aAAa,CAAC,EAAE,SAAS,CAAC;IACnC,QAAQ,CAAC,cAAc,CAAC,EAAE,UAAU,CAAC;IACrC,QAAQ,CAAC,eAAe,CAAC,EAAE,OAAO,CAAC;CACpC;AAED,MAAM,WAAW,qBAAqB;IACpC,QAAQ,CAAC,WAAW,EAAE,MAAM,CAAC;IAC7B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,WAAW,CAAC,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,CAAC;CACjC;AAED,MAAM,MAAM,wBAAwB,GAAG,SAAS,sBAAsB,EAAE,GAAG,WAAW,CAAC;AASvF,qBAAa,UAAU;IAKT,OAAO,CAAC,QAAQ,CAAC,MAAM;IAJnC,OAAO,CAAC,QAAQ,CAAC,OAAO,CAAoB;IAC5C,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAU;IACtC,OAAO,CAAC,WAAW,CAA8B;gBAEpB,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB;IAQpE,IAAI,GAAG,IAAI,oBAAoB,CAS9B;IAED,WAAW,CAAC,QAAQ,EAAE,iBAAiB,GAAG,IAAI;IAI9C,YAAY,CAAC,KAAK,EAAE,MAAM,EAAE,KAAK,EAAE,YAAY,GAAG,IAAI,EAAE,cAAc,CAAC,EAAE,wBAAwB,GAAG,IAAI;IAIxG,eAAe,CAAC,IAAI,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,IAAI,EAAE,MAAM,SAAI,EAAE,IAAI,CAAC,EAAE,SAAS,GAAG,IAAI;IAIpG,cAAc,CAAC,OAAO,EAAE,QAAQ,CAAC,eAAe,CAAC,GAAG,IAAI;IAIxD,IAAI,CAAC,OAAO,EAAE,qBAAqB,GAAG,IAAI;IAC1C,IAAI,CAAC,WAAW,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,EAAE,WAAW,CAAC,EAAE,MAAM,EAAE,aAAa,CAAC,EAAE,MAAM,GAAG,IAAI;IAcrG,GAAG,IAAI,IAAI;IAQX,OAAO,IAAI,IAAI;CAGhB;AAED,wBAAgB,yBAAyB,CAAC,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,iBAAiB,EAAE,OAAO,EAAE,iBAAiB,GAAG,UAAU,CAGzH"}
@@ -1,16 +1,18 @@
1
1
  import { Buffer, VGPUError } from "@vgpu/core";
2
2
  const textureBrand = Symbol.for("vgpu/Texture");
3
+ const renderPassEncoderSources = new WeakMap();
3
4
  export class RenderPass {
4
5
  device;
5
6
  encoder;
7
+ submitOnEnd;
6
8
  passEncoder;
7
9
  constructor(device, opts) {
8
10
  this.device = device;
9
- this.encoder = device.gpu.createCommandEncoder({ label: opts.label });
10
- this.passEncoder = this.encoder.beginRenderPass({
11
- label: opts.label,
12
- colorAttachments: opts.colorAttachments.map(colorAttachment),
13
- });
11
+ const source = renderPassEncoderSources.get(opts);
12
+ renderPassEncoderSources.delete(opts);
13
+ this.encoder = source?.encoder ?? device.gpu.createCommandEncoder({ label: opts.label });
14
+ this.submitOnEnd = source?.submitOnEnd ?? true;
15
+ this.passEncoder = this.encoder.beginRenderPass(renderPassDescriptor(opts));
14
16
  }
15
17
  get gpu() {
16
18
  if (!this.passEncoder) {
@@ -31,6 +33,9 @@ export class RenderPass {
31
33
  setVertexBuffer(slot, buffer, offset = 0, size) {
32
34
  this.gpu.setVertexBuffer(slot, gpuBuffer(buffer), offset, size);
33
35
  }
36
+ executeBundles(bundles) {
37
+ this.gpu.executeBundles(bundles);
38
+ }
34
39
  draw(optionsOrVertexCount, instanceCount = 1, firstVertex = 0, firstInstance = 0) {
35
40
  if (typeof optionsOrVertexCount === "number") {
36
41
  this.gpu.draw(optionsOrVertexCount, instanceCount, firstVertex, firstInstance);
@@ -44,20 +49,48 @@ export class RenderPass {
44
49
  const pass = this.passEncoder;
45
50
  this.passEncoder = null;
46
51
  pass.end();
47
- this.device.queue.gpu.submit([this.encoder.finish()]);
52
+ if (this.submitOnEnd)
53
+ this.device.queue.gpu.submit([this.encoder.finish()]);
48
54
  }
49
55
  dispose() {
50
56
  this.end();
51
57
  }
52
58
  }
59
+ export function createRenderPassOnEncoder(device, opts, encoder) {
60
+ renderPassEncoderSources.set(opts, { encoder, submitOnEnd: false });
61
+ return new RenderPass(device, opts);
62
+ }
63
+ function renderPassDescriptor(opts) {
64
+ return {
65
+ label: opts.label,
66
+ colorAttachments: opts.colorAttachments.map(colorAttachment),
67
+ depthStencilAttachment: opts.depthStencilAttachment ? depthStencilAttachment(opts.depthStencilAttachment) : undefined,
68
+ };
69
+ }
53
70
  function colorAttachment(attachment) {
54
71
  return {
55
- view: isVGPUTexture(attachment.view) ? attachment.view.createView() : attachment.view,
72
+ view: textureView(attachment.view),
56
73
  loadOp: attachment.loadOp,
57
74
  storeOp: attachment.storeOp,
58
75
  clearValue: attachment.clearValue,
59
76
  };
60
77
  }
78
+ function depthStencilAttachment(attachment) {
79
+ return {
80
+ view: textureView(attachment.view),
81
+ depthClearValue: attachment.depthClearValue,
82
+ depthLoadOp: attachment.depthLoadOp,
83
+ depthStoreOp: attachment.depthStoreOp,
84
+ depthReadOnly: attachment.depthReadOnly,
85
+ stencilClearValue: attachment.stencilClearValue,
86
+ stencilLoadOp: attachment.stencilLoadOp,
87
+ stencilStoreOp: attachment.stencilStoreOp,
88
+ stencilReadOnly: attachment.stencilReadOnly,
89
+ };
90
+ }
91
+ function textureView(view) {
92
+ return isVGPUTexture(view) ? view.createView() : view;
93
+ }
61
94
  function gpuBuffer(buffer) {
62
95
  return buffer instanceof Buffer ? buffer.gpu : buffer;
63
96
  }
@@ -1 +1 @@
1
- {"version":3,"file":"render-pass.js","sourceRoot":"","sources":["../src/render-pass.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"render-pass.js","sourceRoot":"","sources":["../src/render-pass.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vgpu/render",
3
- "version": "0.0.4",
3
+ "version": "0.0.6",
4
4
  "description": "Render pipeline + render pass abstractions for vgpu.",
5
5
  "keywords": [
6
6
  "webgpu",
@@ -58,7 +58,7 @@
58
58
  ],
59
59
  "dependencies": {
60
60
  "wgpu-matrix": "^3.4.2",
61
- "@vgpu/core": "0.0.4"
61
+ "@vgpu/core": "0.0.6"
62
62
  },
63
63
  "vgpuExportBundleBudgetsGzipBytes": {
64
64
  ".": 49152,
@@ -0,0 +1,79 @@
1
+ # Frame
2
+
3
+ `Frame` owns one `GPUCommandEncoder` for an explicit sequence of commands. Use
4
+ `beginFrame(device)` when one render frame needs multiple render passes, query
5
+ commands, or copies in a user-authored order, then call `submit()` once.
6
+
7
+ VGPU does not infer pass order, bind group layouts, or pipeline layouts. The
8
+ helpers keep the native WebGPU lifecycle visible while removing repeated
9
+ boilerplate around pass begin/end and final submission.
10
+
11
+ ## Native WebGPU baseline
12
+
13
+ Without `Frame`, a multipass render frame is one command encoder, multiple
14
+ passes ended in order, then one finish/submit:
15
+
16
+ ```ts
17
+ const encoder = device.gpu.createCommandEncoder({ label: "hero.frame" });
18
+
19
+ const lightPass = encoder.beginRenderPass(lightPassDescriptor);
20
+ lightPass.executeBundles([lightBundle]);
21
+ lightPass.end();
22
+
23
+ encoder.writeTimestamp(querySet, 0);
24
+ encoder.copyBufferToBuffer(srcBuffer, 0, dstBuffer, 0, byteLength);
25
+
26
+ const compositePass = encoder.beginRenderPass(compositePassDescriptor);
27
+ compositePass.setPipeline(compositePipeline);
28
+ compositePass.setBindGroup(0, compositeBindGroup);
29
+ compositePass.draw(3);
30
+ compositePass.end();
31
+
32
+ device.queue.gpu.submit([encoder.finish()]);
33
+ ```
34
+
35
+ ## With VGPU Frame
36
+
37
+ `Frame` keeps that same lifecycle: one encoder, explicit pass order, finish once,
38
+ submit once. This one-encoder/one-submit contract is intentional for homepage-style
39
+ multipass renderers: setup and warmup should create pipelines, buffers, bind
40
+ groups, and bundles up front; the per-frame path should only encode commands and
41
+ submit the finished command buffer.
42
+
43
+ ```ts
44
+ const frame = beginFrame(device, { label: "hero.frame" });
45
+
46
+ frame.renderPass(lightPassDescriptor, (pass) => {
47
+ pass.executeBundles([lightBundle]);
48
+ });
49
+
50
+ frame.gpu.writeTimestamp(querySet, 0);
51
+ frame.copyBufferToBuffer(srcBuffer, dstBuffer, byteLength);
52
+
53
+ frame.renderPass(compositePassDescriptor, (pass) => {
54
+ pass.setPipeline(compositePipeline);
55
+ pass.setBindGroup(0, compositeBindGroup);
56
+ pass.draw(3);
57
+ });
58
+
59
+ frame.submit();
60
+ ```
61
+
62
+ `frame.renderPass(options, callback)` begins a `RenderPass` wrapper on the
63
+ frame-owned encoder, runs the callback, and ends that pass in a `finally` block.
64
+ Unlike constructing a standalone `RenderPass`, a pass created by `Frame` does
65
+ **not** finish or submit the command encoder when it ends. `frame.submit()` is
66
+ the only submission point.
67
+
68
+ The raw command encoder is available as `frame.gpu` for WebGPU operations that
69
+ are intentionally not wrapped, such as `writeTimestamp()` or `resolveQuerySet()`.
70
+ This is an advanced, semver-protected public escape hatch. Direct raw encoder
71
+ calls follow native WebGPU behavior; VGPU only guards its own helper methods
72
+ after `submit()`.
73
+
74
+ `copyBufferToBuffer(source, destination, size, sourceOffset?, destinationOffset?)`
75
+ is a small typed helper that accepts either core `Buffer` instances or raw
76
+ `GPUBuffer`s. For all other encoder commands, use `frame.gpu` directly.
77
+
78
+ A `Frame` is single-use. Calling VGPU helper methods after `submit()` throws
79
+ `VGPUError` with code `VGPU-FRAME-SUBMITTED`.
@@ -5,9 +5,11 @@ attachments. Construct it with a core `Device` and `RenderPassOptions`, encode
5
5
  render commands, then call `end()` to finish the pass and submit the command
6
6
  buffer through `Device.queue`.
7
7
 
8
- `RenderPassOptions` contains `colorAttachments` and an optional `label`.
9
- `ColorAttachment.view` accepts either a core `Texture` or a raw
10
- `GPUTextureView`; `loadOp`, `storeOp`, and `clearValue` are forwarded to WebGPU.
8
+ `RenderPassOptions` contains `colorAttachments`, optional
9
+ `depthStencilAttachment`, and an optional `label`. `ColorAttachment.view` and
10
+ `DepthStencilAttachment.view` accept either a core `Texture` or a raw
11
+ `GPUTextureView`; load/store operations and clear values are forwarded to
12
+ WebGPU.
11
13
 
12
14
  Commands mirror the WebGPU render pass encoder:
13
15
 
@@ -15,6 +17,7 @@ Commands mirror the WebGPU render pass encoder:
15
17
  - `setBindGroup(index, group, dynamicOffsets?)` binds a raw `GPUBindGroup`.
16
18
  - `setVertexBuffer(slot, buffer, offset?, size?)` binds a core `Buffer` or raw
17
19
  `GPUBuffer` for vertex input.
20
+ - `executeBundles(bundles)` executes setup-time `GPURenderBundle`s in the pass.
18
21
  - `draw({ vertexCount, instanceCount?, firstVertex?, firstInstance? })` issues a
19
22
  non-indexed draw. The numeric WebGPU-style `draw(vertexCount, ...)` call is
20
23
  also accepted.
@@ -1,34 +1,94 @@
1
- # createRenderPipeline
1
+ # createRenderPipeline / createRenderPipelineAsync
2
2
 
3
- Creates a GPU render pipeline from a vgpu `Shader` plus vertex and fragment
4
- entry points. Returns a raw `GPURenderPipeline` with no wrapper.
3
+ Creates a GPU render pipeline from descriptor-like VGPU options. Both helpers
4
+ return a raw `GPURenderPipeline` with no wrapper so the result can be passed
5
+ directly to native WebGPU, `RenderPass.setPipeline()`, or render-bundle recording.
5
6
 
6
- ## Signature
7
+ ## Signatures
7
8
 
8
- `createRenderPipeline(device: Device, opts: RenderPipelineOptions): GPURenderPipeline`
9
+ ```ts
10
+ createRenderPipeline(device: Device, opts: RenderPipelineOptions): GPURenderPipeline
11
+ createRenderPipelineAsync(device: Device, opts: RenderPipelineOptions): Promise<GPURenderPipeline>
12
+ ```
13
+
14
+ `createRenderPipelineAsync` calls `GPUDevice.createRenderPipelineAsync()` when it
15
+ exists. If the implementation does not expose the async API, the default
16
+ compatibility policy is `fallback: "sync"`, which emits a once-only diagnostic and
17
+ calls `createRenderPipeline()` instead. Performance-critical warmup can pass
18
+ `fallback: "throw"` to receive a structured `VGPUError` with code
19
+ `VGPU-RENDER-PIPELINE-ASYNC-UNAVAILABLE` instead of accidentally blocking.
20
+
21
+ VGPU does not cache pipelines: one helper call equals one WebGPU device call.
22
+ Keep pipeline caches explicit and owned by the caller.
9
23
 
10
24
  ## Options
11
25
 
12
- - `shader`: the `Shader` whose compiled GPU module will back both stages.
13
- - `vertex.entry`: the vertex shader entry-point name.
14
- - `fragment.entry`: the fragment shader entry-point name.
15
- - `fragment.targets`: the color target formats and blend/write settings.
16
- - `primitive`: optional WebGPU primitive state such as topology or culling.
17
- - `layout`: optional pipeline layout, or `"auto"` to let WebGPU derive one.
26
+ - `shader`: optional shared `Shader` or raw `GPUShaderModule` used by stages that
27
+ do not provide their own module.
28
+ - `vertex`: vertex stage options.
29
+ - `shader` / `module`: optional per-stage `Shader` or raw `GPUShaderModule`.
30
+ - `entry` / `entryPoint`: vertex entry-point name.
31
+ - `buffers`: optional vertex buffer layouts and attributes.
32
+ - `constants`: optional pipeline constants.
33
+ - `fragment`: optional fragment stage options.
34
+ - `shader` / `module`: optional per-stage `Shader` or raw `GPUShaderModule`.
35
+ - `entry` / `entryPoint`: fragment entry-point name.
36
+ - `targets`: color target formats plus blend and write-mask state.
37
+ - `constants`: optional pipeline constants.
38
+ - `primitive`: optional WebGPU primitive state.
39
+ - `depthStencil`: optional depth/stencil state.
40
+ - `multisample`: optional multisample state.
41
+ - `layout`: explicit `GPUPipelineLayout`, or `"auto"`. Defaults to `"auto"`.
18
42
  - `label`: optional debug label forwarded to WebGPU.
43
+ - `fallback`: async-only fallback policy, `"sync"` or `"throw"`.
44
+
45
+ ## Examples
19
46
 
20
- ## Example
47
+ Shared VGPU `Shader` module:
21
48
 
22
49
  ```ts
23
- const pipeline = createRenderPipeline(device, {
50
+ const pipeline = await createRenderPipelineAsync(device, {
51
+ label: "hero.pipeline",
52
+ fallback: "throw",
24
53
  shader,
25
- vertex: { entry: "vs_main" },
26
- fragment: { entry: "fs_main", targets: [{ format: "rgba8unorm" }] },
27
- primitive: { topology: "triangle-list" },
54
+ layout: explicitPipelineLayout,
55
+ vertex: {
56
+ entry: "vs_main",
57
+ buffers: [{
58
+ arrayStride: 16,
59
+ attributes: [{ shaderLocation: 0, offset: 0, format: "float32x4" }],
60
+ }],
61
+ },
62
+ fragment: {
63
+ entry: "fs_main",
64
+ targets: [{
65
+ format,
66
+ blend: {
67
+ color: { operation: "add", srcFactor: "one", dstFactor: "one-minus-src-alpha" },
68
+ alpha: { operation: "add", srcFactor: "one", dstFactor: "one-minus-src-alpha" },
69
+ },
70
+ writeMask: 0xf,
71
+ }],
72
+ },
73
+ primitive: { topology: "triangle-list", cullMode: "back" },
74
+ depthStencil: { format: "depth24plus", depthWriteEnabled: true, depthCompare: "less" },
75
+ multisample: { count: 4 },
76
+ });
77
+ ```
78
+
79
+ Raw shader modules and per-stage constants:
80
+
81
+ ```ts
82
+ const pipeline = createRenderPipeline(device, {
83
+ layout: "auto",
84
+ vertex: { module: vertexModule, entryPoint: "vs", constants: { scale: 2 } },
85
+ fragment: { module: fragmentModule, entryPoint: "fs", targets: [{ format }] },
28
86
  });
29
87
  ```
30
88
 
31
- ## Notes
89
+ ## Raw escape hatch
32
90
 
33
- The returned `GPURenderPipeline` can be used directly with WebGPU APIs or
34
- passed to `RenderPass.setPipeline`.
91
+ `Shader.gpu` is an intentional advanced escape hatch to the underlying
92
+ `GPUShaderModule`. It is part of VGPU's public API surface and should be treated
93
+ as semver-protected, but direct native WebGPU usage remains responsible for
94
+ native WebGPU validation and lifecycle rules.
@@ -15,4 +15,18 @@ material has uniforms. The renderer does not write camera, transform, light, or
15
15
  material parameters.
16
16
 
17
17
  `clearValue` defaults to opaque black. `depthTarget` is optional. `renderer.gpu`
18
- returns the raw `GPUDevice`. `draw()` resolves after command submission.
18
+ returns the raw `GPUDevice`. Like other `.gpu` fields, this is an advanced,
19
+ semver-protected public escape hatch. `draw()` resolves after command submission.
20
+
21
+ ## Hot-path caveats
22
+
23
+ `RapidRenderer` is optimized for examples and simple draws, not for hiding all
24
+ homepage hot-path work. For performance-critical renderers, keep pipeline,
25
+ buffer, bind-group, and render-bundle creation in setup/warmup code. Similarly,
26
+ `material().writeUniforms()` writes CPU-side uniform data for later upload and
27
+ should be scheduled deliberately.
28
+
29
+ Do not resolve shaders, compile shaders, or rebuild pipelines inside the animation
30
+ frame path. If shader source truly changes dynamically, resolve the shader and
31
+ create the replacement pipeline outside the frame loop, then stage or
32
+ double-buffer the swap so a completed pipeline is installed at a frame boundary.
@@ -0,0 +1,46 @@
1
+ # createRenderBundle
2
+
3
+ `createRenderBundle(device, options)` records a WebGPU render bundle once during
4
+ setup and returns the finished `GPURenderBundle`.
5
+
6
+ Render bundles are reusable draw packets, not mini-passes. They do not own color
7
+ or depth attachments and they do not decide ordering. Execute them from an
8
+ explicit render pass with `pass.executeBundles([bundle])`, alongside the normal
9
+ WebGPU state constraints for bundles and render passes.
10
+
11
+ ```ts
12
+ const lightBundle = createRenderBundle(device, {
13
+ label: "hero.light-sources.bundle",
14
+ colorFormats: [format],
15
+ depthStencilFormat: "depth24plus",
16
+ sampleCount: 1,
17
+ record(bundle) {
18
+ bundle.setPipeline(lightPipeline);
19
+ bundle.setBindGroup(0, lightBindGroup);
20
+ bundle.draw(lightVertexCount);
21
+ },
22
+ });
23
+
24
+ const frame = beginFrame(device);
25
+ frame.renderPass(lightPassDescriptor, (pass) => {
26
+ pass.executeBundles([lightBundle]);
27
+ });
28
+ frame.submit();
29
+ ```
30
+
31
+ Options mirror the required parts of `GPURenderBundleEncoderDescriptor`:
32
+ `colorFormats`, optional `depthStencilFormat`, optional `sampleCount`, and the
33
+ optional depth/stencil read-only flags. The `record` callback receives a
34
+ `RenderBundleRecorder` wrapper with `gpu` escape-hatch access plus convenience
35
+ methods for `setPipeline`, `setBindGroup`, `setVertexBuffer`, and `draw`. The raw
36
+ `bundle.gpu` recorder is an advanced, semver-protected public escape hatch to the
37
+ underlying `GPURenderBundleEncoder`.
38
+
39
+ Create bundles during setup or resize, not in the per-frame hot path. A bundle is
40
+ compatible with the `colorFormats`, `depthStencilFormat`, `sampleCount`,
41
+ `depthReadOnly`, and `stencilReadOnly` values it was recorded with. If resize or
42
+ render-target reconfiguration changes any of those bundle-compatible fields,
43
+ re-record the affected bundles before the next frame.
44
+
45
+ VGPU preserves explicit pipeline, layout, and bind-group control. Bundle helpers
46
+ never infer layouts and do not switch to `layout: "auto"` for performance.
@@ -0,0 +1,15 @@
1
+ # renderTargetForCanvas
2
+
3
+ `renderTargetForCanvas(context, options?)` adapts the current texture from a
4
+ `GPUCanvasContext` into a render target that can be passed to `pass()` and other
5
+ `@vgpu/render/passes` helpers.
6
+
7
+ The returned target resolves its `color` view lazily each time it is read, so it
8
+ tracks `context.getCurrentTexture()` across frames. Use `label` and `clearColor`
9
+ in the options object to configure the generated attachment metadata and default
10
+ clear color.
11
+
12
+ ```ts
13
+ const target = renderTargetForCanvas(canvasContext, { label: "screen" });
14
+ pass({ mesh, material, target });
15
+ ```