@vgpu/render 0.0.6 → 0.0.7
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 +1 -1
- package/dist/index.d.ts +5 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +3 -1
- package/dist/index.js.map +1 -1
- package/dist/perf/frame-time-measure.d.ts +22 -0
- package/dist/perf/frame-time-measure.d.ts.map +1 -0
- package/dist/perf/frame-time-measure.js +91 -0
- package/dist/perf/frame-time-measure.js.map +1 -0
- package/dist/perf/gpu-frame-time.d.ts +49 -0
- package/dist/perf/gpu-frame-time.d.ts.map +1 -0
- package/dist/perf/gpu-frame-time.js +50 -0
- package/dist/perf/gpu-frame-time.js.map +1 -0
- package/dist/perf/index.d.ts +5 -0
- package/dist/perf/index.d.ts.map +1 -0
- package/dist/perf/index.js +3 -0
- package/dist/perf/index.js.map +1 -0
- package/dist/perf/pixel-diff.d.ts +27 -0
- package/dist/perf/pixel-diff.d.ts.map +1 -0
- package/dist/perf/pixel-diff.js +40 -0
- package/dist/perf/pixel-diff.js.map +1 -0
- package/dist/pipeline-descriptor.d.ts +35 -0
- package/dist/pipeline-descriptor.d.ts.map +1 -0
- package/dist/pipeline-descriptor.js +44 -0
- package/dist/pipeline-descriptor.js.map +1 -0
- package/dist/pipeline.d.ts +22 -33
- package/dist/pipeline.d.ts.map +1 -1
- package/dist/pipeline.js +30 -46
- package/dist/pipeline.js.map +1 -1
- package/dist/render-pass.d.ts.map +1 -1
- package/dist/render-pass.js +4 -0
- package/dist/render-pass.js.map +1 -1
- package/dist/storage-buffer.d.ts +81 -0
- package/dist/storage-buffer.d.ts.map +1 -0
- package/dist/storage-buffer.js +91 -0
- package/dist/storage-buffer.js.map +1 -0
- package/dist/uniform.d.ts +55 -0
- package/dist/uniform.d.ts.map +1 -0
- package/dist/uniform.js +64 -0
- package/dist/uniform.js.map +1 -0
- package/package.json +8 -3
- package/src/createRenderPipeline.docs.md +34 -0
- package/src/perf/perf.docs.md +45 -0
- package/src/storage-buffer.docs.md +102 -0
- package/src/uniform.docs.md +70 -0
package/dist/pipeline.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { VGPUError } from "@vgpu/core";
|
|
2
|
+
import { toRenderPipelineDescriptor } from "./pipeline-descriptor.js";
|
|
2
3
|
// Warn once per JS process. This keeps compatibility fallback visible without spamming
|
|
3
4
|
// apps that intentionally warm up multiple pipelines on implementations without
|
|
4
5
|
// GPUDevice.createRenderPipelineAsync().
|
|
@@ -7,64 +8,47 @@ export function createRenderPipeline(device, opts) {
|
|
|
7
8
|
return device.gpu.createRenderPipeline(toRenderPipelineDescriptor(opts));
|
|
8
9
|
}
|
|
9
10
|
export async function createRenderPipelineAsync(device, opts) {
|
|
10
|
-
|
|
11
|
+
return createPipelineAsync(device, toRenderPipelineDescriptor(opts), opts.fallback, "createRenderPipelineAsync");
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Create a render pipeline from a raw, hand-built `GPURenderPipelineDescriptor`.
|
|
15
|
+
*
|
|
16
|
+
* @remarks
|
|
17
|
+
* Use this when you already own a native descriptor and only want VGPU's
|
|
18
|
+
* `Device` wrapper to forward it — no `RenderPipelineOptions` reshape. The
|
|
19
|
+
* descriptor is passed through unchanged.
|
|
20
|
+
*/
|
|
21
|
+
export function createRenderPipelineFromDescriptor(device, descriptor) {
|
|
22
|
+
return device.gpu.createRenderPipeline(descriptor);
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* Async variant of {@link createRenderPipelineFromDescriptor} with the same
|
|
26
|
+
* async→sync compatibility fallback as {@link createRenderPipelineAsync}.
|
|
27
|
+
*
|
|
28
|
+
* @remarks
|
|
29
|
+
* The descriptor is forwarded unchanged. When `GPUDevice.createRenderPipelineAsync`
|
|
30
|
+
* is unavailable, the default `fallback: "sync"` emits a once-only diagnostic and
|
|
31
|
+
* calls the synchronous path; pass `fallback: "throw"` for a structured `VGPUError`.
|
|
32
|
+
*/
|
|
33
|
+
export async function createRenderPipelineFromDescriptorAsync(device, descriptor, fallback) {
|
|
34
|
+
return createPipelineAsync(device, descriptor, fallback, "createRenderPipelineFromDescriptorAsync");
|
|
35
|
+
}
|
|
36
|
+
async function createPipelineAsync(device, descriptor, fallback, where) {
|
|
11
37
|
const createAsync = device.gpu.createRenderPipelineAsync;
|
|
12
38
|
if (typeof createAsync === "function") {
|
|
13
39
|
return createAsync.call(device.gpu, descriptor);
|
|
14
40
|
}
|
|
15
|
-
if ((
|
|
41
|
+
if ((fallback ?? "sync") === "throw") {
|
|
16
42
|
throw new VGPUError({
|
|
17
43
|
code: "VGPU-RENDER-PIPELINE-ASYNC-UNAVAILABLE",
|
|
18
44
|
message: "GPUDevice.createRenderPipelineAsync is unavailable on this WebGPU implementation.",
|
|
19
45
|
fix: "Use fallback: 'sync' for compatibility or call createRenderPipeline() explicitly during setup/warmup.",
|
|
20
|
-
where
|
|
46
|
+
where,
|
|
21
47
|
});
|
|
22
48
|
}
|
|
23
49
|
warnAsyncFallbackOnce();
|
|
24
50
|
return device.gpu.createRenderPipeline(descriptor);
|
|
25
51
|
}
|
|
26
|
-
function toRenderPipelineDescriptor(opts) {
|
|
27
|
-
const descriptor = {
|
|
28
|
-
label: opts.label,
|
|
29
|
-
layout: opts.layout ?? "auto",
|
|
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
|
-
},
|
|
36
|
-
primitive: opts.primitive,
|
|
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
52
|
export function __resetCreateRenderPipelineAsyncFallbackWarningForTests() {
|
|
69
53
|
didWarnAboutAsyncFallback = false;
|
|
70
54
|
}
|
package/dist/pipeline.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,MAAM,EAAE,
|
|
1
|
+
{"version":3,"file":"pipeline.js","sourceRoot":"","sources":["../src/pipeline.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAe,MAAM,YAAY,CAAC;AACpD,OAAO,EAAE,0BAA0B,EAAE,MAAM,0BAA0B,CAAC;AAYtE,uFAAuF;AACvF,gFAAgF;AAChF,yCAAyC;AACzC,IAAI,yBAAyB,GAAG,KAAK,CAAC;AAItC,MAAM,UAAU,oBAAoB,CAAC,MAAc,EAAE,IAA2B;IAC9E,OAAO,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,0BAA0B,CAAC,IAAI,CAAC,CAAC,CAAC;AAC3E,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,yBAAyB,CAC7C,MAAc,EACd,IAA2B;IAE3B,OAAO,mBAAmB,CACxB,MAAM,EACN,0BAA0B,CAAC,IAAI,CAAC,EAChC,IAAI,CAAC,QAAQ,EACb,2BAA2B,CAC5B,CAAC;AACJ,CAAC;AAED;;;;;;;GAOG;AACH,MAAM,UAAU,kCAAkC,CAChD,MAAc,EACd,UAAuC;IAEvC,OAAO,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;AACrD,CAAC;AAED;;;;;;;;GAQG;AACH,MAAM,CAAC,KAAK,UAAU,uCAAuC,CAC3D,MAAc,EACd,UAAuC,EACvC,QAAsC;IAEtC,OAAO,mBAAmB,CAAC,MAAM,EAAE,UAAU,EAAE,QAAQ,EAAE,yCAAyC,CAAC,CAAC;AACtG,CAAC;AAED,KAAK,UAAU,mBAAmB,CAChC,MAAc,EACd,UAAuC,EACvC,QAAiD,EACjD,KAAyB;IAEzB,MAAM,WAAW,GAAG,MAAM,CAAC,GAAG,CAAC,yBAAyB,CAAC;IACzD,IAAI,OAAO,WAAW,KAAK,UAAU,EAAE,CAAC;QACtC,OAAO,WAAW,CAAC,IAAI,CAAC,MAAM,CAAC,GAAG,EAAE,UAAU,CAAC,CAAC;IAClD,CAAC;IAED,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,KAAK,OAAO,EAAE,CAAC;QACrC,MAAM,IAAI,SAAS,CAAC;YAClB,IAAI,EAAE,wCAAwC;YAC9C,OAAO,EAAE,mFAAmF;YAC5F,GAAG,EAAE,uGAAuG;YAC5G,KAAK;SACN,CAAC,CAAC;IACL,CAAC;IAED,qBAAqB,EAAE,CAAC;IACxB,OAAO,MAAM,CAAC,GAAG,CAAC,oBAAoB,CAAC,UAAU,CAAC,CAAC;AACrD,CAAC;AAED,MAAM,UAAU,uDAAuD;IACrE,yBAAyB,GAAG,KAAK,CAAC;AACpC,CAAC;AAED,SAAS,qBAAqB;IAC5B,IAAI,yBAAyB;QAAE,OAAO;IACtC,yBAAyB,GAAG,IAAI,CAAC;IACjC,UAAU,CAAC,OAAO,EAAE,IAAI,EAAE,CACxB,yKAAyK,CAC1K,CAAC;AACJ,CAAC"}
|
|
@@ -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,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;
|
|
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;IAQxG,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"}
|
package/dist/render-pass.js
CHANGED
|
@@ -28,6 +28,10 @@ export class RenderPass {
|
|
|
28
28
|
this.gpu.setPipeline(pipeline);
|
|
29
29
|
}
|
|
30
30
|
setBindGroup(index, group, dynamicOffsets) {
|
|
31
|
+
if (dynamicOffsets === undefined) {
|
|
32
|
+
this.gpu.setBindGroup(index, group);
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
31
35
|
this.gpu.setBindGroup(index, group, dynamicOffsets);
|
|
32
36
|
}
|
|
33
37
|
setVertexBuffer(slot, buffer, offset = 0, size) {
|
package/dist/render-pass.js.map
CHANGED
|
@@ -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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"}
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import { Buffer, type Device } from "@vgpu/core";
|
|
2
|
+
export interface StorageBufferOptions {
|
|
3
|
+
/** Byte size of the storage buffer. Must be a positive number. Storage buffers may be far larger than uniforms (up to the adapter's `maxStorageBufferBindingSize`, typically 128 MiB). */
|
|
4
|
+
readonly size: number;
|
|
5
|
+
/** Optional debug label forwarded to the buffer, bind group layout, and bind group. */
|
|
6
|
+
readonly label?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Whether the shader only reads the buffer (`"read"`, the default → `var<storage, read>`)
|
|
9
|
+
* or also writes it (`"read-write"` → `var<storage, read_write>`). This selects the
|
|
10
|
+
* bind group layout entry type (`"read-only-storage"` vs `"storage"`) and changes the
|
|
11
|
+
* default {@link StorageBufferOptions.visibility}.
|
|
12
|
+
*/
|
|
13
|
+
readonly access?: "read" | "read-write";
|
|
14
|
+
/**
|
|
15
|
+
* Shader stages that access binding 0. Defaults to `FRAGMENT | COMPUTE` for both access modes —
|
|
16
|
+
* no `VERTEX`. Read-write storage is forbidden in the vertex stage; read-only storage is allowed
|
|
17
|
+
* there but `maxStorageBuffersInVertexStage` is 0 on many adapters (software/CI Vulkan, some
|
|
18
|
+
* mobile GPUs), so vertex-stage storage is opt-in: pass `visibility` explicitly and raise that
|
|
19
|
+
* limit via `requiredLimits`. Ignored when {@link StorageBufferOptions.bindGroupLayout} is provided.
|
|
20
|
+
*/
|
|
21
|
+
readonly visibility?: GPUShaderStageFlags;
|
|
22
|
+
/**
|
|
23
|
+
* Reuse a pipeline-owned bind group layout instead of creating one.
|
|
24
|
+
* The layout's binding 0 must be a storage buffer compatible with `size` and `access`.
|
|
25
|
+
*/
|
|
26
|
+
readonly bindGroupLayout?: GPUBindGroupLayout;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* A single stable storage buffer for one render (or compute) pass, rewritten as needed.
|
|
30
|
+
*
|
|
31
|
+
* @remarks
|
|
32
|
+
* `StorageBuffer` is the storage-buffer counterpart to {@link Uniform}. Reach for it when
|
|
33
|
+
* the data is too large or too dynamic for a uniform: arrays, runtime-sized data, particle
|
|
34
|
+
* state, lookup tables. Storage buffers can be much bigger than uniforms (up to the adapter's
|
|
35
|
+
* `maxStorageBufferBindingSize`, typically 128 MiB, versus a uniform's 64 KiB) and can be
|
|
36
|
+
* written by the shader when `access: "read-write"`.
|
|
37
|
+
*
|
|
38
|
+
* Like `Uniform`, it owns exactly one buffer at binding 0 with a fixed (non-dynamic) offset,
|
|
39
|
+
* and the caller decides when to {@link StorageBuffer.write}. Unlike {@link UniformPool} — a
|
|
40
|
+
* dynamic-offset ring allocator for many small per-draw uniforms — there is no dynamic offset
|
|
41
|
+
* and the bind group never moves.
|
|
42
|
+
*
|
|
43
|
+
* `access` controls the layout entry type; both modes default to `FRAGMENT | COMPUTE` visibility:
|
|
44
|
+
* - `"read"` (default) → `var<storage, read>`, layout type `"read-only-storage"`.
|
|
45
|
+
* - `"read-write"` → `var<storage, read_write>`, layout type `"storage"`.
|
|
46
|
+
* Neither default includes `VERTEX`: read-write storage is forbidden in the vertex stage, and
|
|
47
|
+
* read-only storage — though legal there — needs `maxStorageBuffersInVertexStage > 0`, which is 0
|
|
48
|
+
* on many adapters (software/CI Vulkan, some mobile GPUs). For vertex-stage read-only storage, pass
|
|
49
|
+
* `visibility: VERTEX | …` explicitly and raise the limit via `requiredLimits`; do not rely on the default.
|
|
50
|
+
*
|
|
51
|
+
* @example
|
|
52
|
+
* ```ts
|
|
53
|
+
* // A read-only array the fragment shader samples (var<storage, read> values: array<f32>):
|
|
54
|
+
* const storage = new StorageBuffer(device, { size: 4 * count, label: "values" });
|
|
55
|
+
* storage.write(new Float32Array(values));
|
|
56
|
+
* pass.setBindGroup(0, storage.bindGroup);
|
|
57
|
+
*
|
|
58
|
+
* // A compute-written scratch buffer (var<storage, read_write> out: array<u32>):
|
|
59
|
+
* const scratch = new StorageBuffer(device, { size: 4 * count, access: "read-write" });
|
|
60
|
+
* ```
|
|
61
|
+
*/
|
|
62
|
+
export declare class StorageBuffer {
|
|
63
|
+
readonly device: Device;
|
|
64
|
+
readonly size: number;
|
|
65
|
+
readonly access: "read" | "read-write";
|
|
66
|
+
readonly buffer: Buffer;
|
|
67
|
+
readonly bindGroupLayout: GPUBindGroupLayout;
|
|
68
|
+
readonly bindGroup: GPUBindGroup;
|
|
69
|
+
private destroyed;
|
|
70
|
+
constructor(device: Device, opts: StorageBufferOptions);
|
|
71
|
+
/** The underlying storage `GPUBuffer`. */
|
|
72
|
+
get gpu(): GPUBuffer;
|
|
73
|
+
/**
|
|
74
|
+
* Upload `data` to the buffer via `queue.writeBuffer`. No dynamic offset; the
|
|
75
|
+
* bind group is unchanged. Call only when the contents actually change.
|
|
76
|
+
*/
|
|
77
|
+
write(data: BufferSource, offset?: number): void;
|
|
78
|
+
destroy(): void;
|
|
79
|
+
dispose(): void;
|
|
80
|
+
}
|
|
81
|
+
//# sourceMappingURL=storage-buffer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-buffer.d.ts","sourceRoot":"","sources":["../src/storage-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAA0C,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,oBAAoB;IACnC,0LAA0L;IAC1L,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;;;OAKG;IACH,QAAQ,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,YAAY,CAAC;IACxC;;;;;;OAMG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,kBAAkB,CAAC;CAC/C;AAUD;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,qBAAa,aAAa;IAQZ,QAAQ,CAAC,MAAM,EAAE,MAAM;IAPnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,GAAG,YAAY,CAAC;IACvC,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,kBAAkB,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,OAAO,CAAC,SAAS,CAAS;gBAEL,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,oBAAoB;IAmB/D,0CAA0C;IAC1C,IAAI,GAAG,IAAI,SAAS,CAEnB;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,SAAI,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAMf,OAAO,IAAI,IAAI;CAGhB"}
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
import { bind, createBindGroup, createBindGroupLayout } from "@vgpu/core";
|
|
2
|
+
// Both access modes default to FRAGMENT | COMPUTE (no VERTEX):
|
|
3
|
+
// - read-write storage is ILLEGAL in the vertex stage (WebGPU forbids it).
|
|
4
|
+
// - read-only storage IS legal in vertex, but maxStorageBuffersInVertexStage is 0 on many adapters
|
|
5
|
+
// (software/CI Vulkan, some mobile GPUs), so a VERTEX-visible default would silently invalidate
|
|
6
|
+
// the layout there (the draw no-ops). Vertex-stage read-only storage is opt-in: pass `visibility`
|
|
7
|
+
// explicitly AND raise the limit via requiredLimits when requesting the device.
|
|
8
|
+
const defaultVisibility = ((globalThis.GPUShaderStage?.FRAGMENT ?? 2) | (globalThis.GPUShaderStage?.COMPUTE ?? 4));
|
|
9
|
+
/**
|
|
10
|
+
* A single stable storage buffer for one render (or compute) pass, rewritten as needed.
|
|
11
|
+
*
|
|
12
|
+
* @remarks
|
|
13
|
+
* `StorageBuffer` is the storage-buffer counterpart to {@link Uniform}. Reach for it when
|
|
14
|
+
* the data is too large or too dynamic for a uniform: arrays, runtime-sized data, particle
|
|
15
|
+
* state, lookup tables. Storage buffers can be much bigger than uniforms (up to the adapter's
|
|
16
|
+
* `maxStorageBufferBindingSize`, typically 128 MiB, versus a uniform's 64 KiB) and can be
|
|
17
|
+
* written by the shader when `access: "read-write"`.
|
|
18
|
+
*
|
|
19
|
+
* Like `Uniform`, it owns exactly one buffer at binding 0 with a fixed (non-dynamic) offset,
|
|
20
|
+
* and the caller decides when to {@link StorageBuffer.write}. Unlike {@link UniformPool} — a
|
|
21
|
+
* dynamic-offset ring allocator for many small per-draw uniforms — there is no dynamic offset
|
|
22
|
+
* and the bind group never moves.
|
|
23
|
+
*
|
|
24
|
+
* `access` controls the layout entry type; both modes default to `FRAGMENT | COMPUTE` visibility:
|
|
25
|
+
* - `"read"` (default) → `var<storage, read>`, layout type `"read-only-storage"`.
|
|
26
|
+
* - `"read-write"` → `var<storage, read_write>`, layout type `"storage"`.
|
|
27
|
+
* Neither default includes `VERTEX`: read-write storage is forbidden in the vertex stage, and
|
|
28
|
+
* read-only storage — though legal there — needs `maxStorageBuffersInVertexStage > 0`, which is 0
|
|
29
|
+
* on many adapters (software/CI Vulkan, some mobile GPUs). For vertex-stage read-only storage, pass
|
|
30
|
+
* `visibility: VERTEX | …` explicitly and raise the limit via `requiredLimits`; do not rely on the default.
|
|
31
|
+
*
|
|
32
|
+
* @example
|
|
33
|
+
* ```ts
|
|
34
|
+
* // A read-only array the fragment shader samples (var<storage, read> values: array<f32>):
|
|
35
|
+
* const storage = new StorageBuffer(device, { size: 4 * count, label: "values" });
|
|
36
|
+
* storage.write(new Float32Array(values));
|
|
37
|
+
* pass.setBindGroup(0, storage.bindGroup);
|
|
38
|
+
*
|
|
39
|
+
* // A compute-written scratch buffer (var<storage, read_write> out: array<u32>):
|
|
40
|
+
* const scratch = new StorageBuffer(device, { size: 4 * count, access: "read-write" });
|
|
41
|
+
* ```
|
|
42
|
+
*/
|
|
43
|
+
export class StorageBuffer {
|
|
44
|
+
device;
|
|
45
|
+
size;
|
|
46
|
+
access;
|
|
47
|
+
buffer;
|
|
48
|
+
bindGroupLayout;
|
|
49
|
+
bindGroup;
|
|
50
|
+
destroyed = false;
|
|
51
|
+
constructor(device, opts) {
|
|
52
|
+
this.device = device;
|
|
53
|
+
this.size = opts.size;
|
|
54
|
+
this.access = opts.access ?? "read";
|
|
55
|
+
this.buffer = device.createBuffer({ size: opts.size, usage: ["storage", "copy_dst"], label: opts.label });
|
|
56
|
+
const visibility = opts.visibility ?? defaultVisibility;
|
|
57
|
+
const entry = this.access === "read-write"
|
|
58
|
+
? bind.storage(0, visibility, { minBindingSize: opts.size })
|
|
59
|
+
: bind.readonlyStorage(0, visibility, { minBindingSize: opts.size });
|
|
60
|
+
this.bindGroupLayout = opts.bindGroupLayout ?? createBindGroupLayout(device, {
|
|
61
|
+
label: opts.label ? `${opts.label}.bgl` : undefined,
|
|
62
|
+
entries: [entry],
|
|
63
|
+
});
|
|
64
|
+
this.bindGroup = createBindGroup(device, {
|
|
65
|
+
label: opts.label ? `${opts.label}.bg` : undefined,
|
|
66
|
+
layout: this.bindGroupLayout,
|
|
67
|
+
entries: [bind.resource(0, this.buffer)],
|
|
68
|
+
});
|
|
69
|
+
}
|
|
70
|
+
/** The underlying storage `GPUBuffer`. */
|
|
71
|
+
get gpu() {
|
|
72
|
+
return this.buffer.gpu;
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Upload `data` to the buffer via `queue.writeBuffer`. No dynamic offset; the
|
|
76
|
+
* bind group is unchanged. Call only when the contents actually change.
|
|
77
|
+
*/
|
|
78
|
+
write(data, offset = 0) {
|
|
79
|
+
this.buffer.write(data, offset);
|
|
80
|
+
}
|
|
81
|
+
destroy() {
|
|
82
|
+
if (this.destroyed)
|
|
83
|
+
return;
|
|
84
|
+
this.destroyed = true;
|
|
85
|
+
this.buffer.destroy();
|
|
86
|
+
}
|
|
87
|
+
dispose() {
|
|
88
|
+
this.destroy();
|
|
89
|
+
}
|
|
90
|
+
}
|
|
91
|
+
//# sourceMappingURL=storage-buffer.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"storage-buffer.js","sourceRoot":"","sources":["../src/storage-buffer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAU,eAAe,EAAE,qBAAqB,EAAe,MAAM,YAAY,CAAC;AA6B/F,+DAA+D;AAC/D,2EAA2E;AAC3E,mGAAmG;AACnG,kGAAkG;AAClG,oGAAoG;AACpG,kFAAkF;AAClF,MAAM,iBAAiB,GAAG,CAAC,CAAC,UAAU,CAAC,cAAc,EAAE,QAAQ,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,EAAE,OAAO,IAAI,CAAC,CAAC,CAAwB,CAAC;AAE1I;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiCG;AACH,MAAM,OAAO,aAAa;IAQH;IAPZ,IAAI,CAAS;IACb,MAAM,CAAwB;IAC9B,MAAM,CAAS;IACf,eAAe,CAAqB;IACpC,SAAS,CAAe;IACzB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAqB,MAAc,EAAE,IAA0B;QAA1C,WAAM,GAAN,MAAM,CAAQ;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,IAAI,MAAM,CAAC;QACpC,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1G,MAAM,UAAU,GAAG,IAAI,CAAC,UAAU,IAAI,iBAAiB,CAAC;QACxD,MAAM,KAAK,GAAG,IAAI,CAAC,MAAM,KAAK,YAAY;YACxC,CAAC,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC;YAC5D,CAAC,CAAC,IAAI,CAAC,eAAe,CAAC,CAAC,EAAE,UAAU,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;QACvE,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,qBAAqB,CAAC,MAAM,EAAE;YAC3E,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS;YACnD,OAAO,EAAE,CAAC,KAAK,CAAC;SACjB,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE;YACvC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS;YAClD,MAAM,EAAE,IAAI,CAAC,eAAe;YAC5B,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;SACzC,CAAC,CAAC;IACL,CAAC;IAED,0CAA0C;IAC1C,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAkB,EAAE,MAAM,GAAG,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;CACF"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { Buffer, type Device } from "@vgpu/core";
|
|
2
|
+
export interface UniformOptions {
|
|
3
|
+
/** Byte size of the uniform buffer. Must be a positive number. */
|
|
4
|
+
readonly size: number;
|
|
5
|
+
/** Optional debug label forwarded to the buffer, bind group layout, and bind group. */
|
|
6
|
+
readonly label?: string;
|
|
7
|
+
/**
|
|
8
|
+
* Shader stages that read binding 0. Defaults to `VERTEX | FRAGMENT`.
|
|
9
|
+
* Ignored when {@link UniformOptions.bindGroupLayout} is provided.
|
|
10
|
+
*/
|
|
11
|
+
readonly visibility?: GPUShaderStageFlags;
|
|
12
|
+
/**
|
|
13
|
+
* Reuse a pipeline-owned bind group layout instead of creating one.
|
|
14
|
+
* The layout's binding 0 must be a uniform buffer compatible with `size`.
|
|
15
|
+
*/
|
|
16
|
+
readonly bindGroupLayout?: GPUBindGroupLayout;
|
|
17
|
+
}
|
|
18
|
+
/**
|
|
19
|
+
* A single stable uniform buffer for one render pass, rewritten per frame.
|
|
20
|
+
*
|
|
21
|
+
* @remarks
|
|
22
|
+
* Unlike {@link UniformPool} (a dynamic-offset ring allocator built for many
|
|
23
|
+
* per-draw uniforms), `Uniform` owns exactly one uniform buffer at binding 0 with
|
|
24
|
+
* a fixed (non-dynamic) offset. The caller decides when to {@link Uniform.write}
|
|
25
|
+
* — typically gating on real changes — and the bind group never moves. This fits
|
|
26
|
+
* the "one camera/globals buffer per pass" case where a dynamic-offset binding
|
|
27
|
+
* would needlessly mark the layout `hasDynamicOffset` and re-upload every frame.
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* ```ts
|
|
31
|
+
* const uniform = new Uniform(device, { size: 64, label: "globals" });
|
|
32
|
+
* // per frame, only when inputs changed:
|
|
33
|
+
* uniform.write(globalsBytes);
|
|
34
|
+
* pass.setBindGroup(0, uniform.bindGroup);
|
|
35
|
+
* ```
|
|
36
|
+
*/
|
|
37
|
+
export declare class Uniform {
|
|
38
|
+
readonly device: Device;
|
|
39
|
+
readonly size: number;
|
|
40
|
+
readonly buffer: Buffer;
|
|
41
|
+
readonly bindGroupLayout: GPUBindGroupLayout;
|
|
42
|
+
readonly bindGroup: GPUBindGroup;
|
|
43
|
+
private destroyed;
|
|
44
|
+
constructor(device: Device, opts: UniformOptions);
|
|
45
|
+
/** The underlying uniform `GPUBuffer`. */
|
|
46
|
+
get gpu(): GPUBuffer;
|
|
47
|
+
/**
|
|
48
|
+
* Upload `data` to the buffer via `queue.writeBuffer`. No dynamic offset; the
|
|
49
|
+
* bind group is unchanged. Call only when the uniform contents actually change.
|
|
50
|
+
*/
|
|
51
|
+
write(data: BufferSource, offset?: number): void;
|
|
52
|
+
destroy(): void;
|
|
53
|
+
dispose(): void;
|
|
54
|
+
}
|
|
55
|
+
//# sourceMappingURL=uniform.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniform.d.ts","sourceRoot":"","sources":["../src/uniform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAQ,MAAM,EAA0C,KAAK,MAAM,EAAE,MAAM,YAAY,CAAC;AAE/F,MAAM,WAAW,cAAc;IAC7B,kEAAkE;IAClE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,uFAAuF;IACvF,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;IACxB;;;OAGG;IACH,QAAQ,CAAC,UAAU,CAAC,EAAE,mBAAmB,CAAC;IAC1C;;;OAGG;IACH,QAAQ,CAAC,eAAe,CAAC,EAAE,kBAAkB,CAAC;CAC/C;AAID;;;;;;;;;;;;;;;;;;GAkBG;AACH,qBAAa,OAAO;IAON,QAAQ,CAAC,MAAM,EAAE,MAAM;IANnC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,MAAM,EAAE,MAAM,CAAC;IACxB,QAAQ,CAAC,eAAe,EAAE,kBAAkB,CAAC;IAC7C,QAAQ,CAAC,SAAS,EAAE,YAAY,CAAC;IACjC,OAAO,CAAC,SAAS,CAAS;gBAEL,MAAM,EAAE,MAAM,EAAE,IAAI,EAAE,cAAc;IAczD,0CAA0C;IAC1C,IAAI,GAAG,IAAI,SAAS,CAEnB;IAED;;;OAGG;IACH,KAAK,CAAC,IAAI,EAAE,YAAY,EAAE,MAAM,SAAI,GAAG,IAAI;IAI3C,OAAO,IAAI,IAAI;IAMf,OAAO,IAAI,IAAI;CAGhB"}
|
package/dist/uniform.js
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import { bind, createBindGroup, createBindGroupLayout } from "@vgpu/core";
|
|
2
|
+
const defaultVisibility = ((globalThis.GPUShaderStage?.VERTEX ?? 1) | (globalThis.GPUShaderStage?.FRAGMENT ?? 2));
|
|
3
|
+
/**
|
|
4
|
+
* A single stable uniform buffer for one render pass, rewritten per frame.
|
|
5
|
+
*
|
|
6
|
+
* @remarks
|
|
7
|
+
* Unlike {@link UniformPool} (a dynamic-offset ring allocator built for many
|
|
8
|
+
* per-draw uniforms), `Uniform` owns exactly one uniform buffer at binding 0 with
|
|
9
|
+
* a fixed (non-dynamic) offset. The caller decides when to {@link Uniform.write}
|
|
10
|
+
* — typically gating on real changes — and the bind group never moves. This fits
|
|
11
|
+
* the "one camera/globals buffer per pass" case where a dynamic-offset binding
|
|
12
|
+
* would needlessly mark the layout `hasDynamicOffset` and re-upload every frame.
|
|
13
|
+
*
|
|
14
|
+
* @example
|
|
15
|
+
* ```ts
|
|
16
|
+
* const uniform = new Uniform(device, { size: 64, label: "globals" });
|
|
17
|
+
* // per frame, only when inputs changed:
|
|
18
|
+
* uniform.write(globalsBytes);
|
|
19
|
+
* pass.setBindGroup(0, uniform.bindGroup);
|
|
20
|
+
* ```
|
|
21
|
+
*/
|
|
22
|
+
export class Uniform {
|
|
23
|
+
device;
|
|
24
|
+
size;
|
|
25
|
+
buffer;
|
|
26
|
+
bindGroupLayout;
|
|
27
|
+
bindGroup;
|
|
28
|
+
destroyed = false;
|
|
29
|
+
constructor(device, opts) {
|
|
30
|
+
this.device = device;
|
|
31
|
+
this.size = opts.size;
|
|
32
|
+
this.buffer = device.createBuffer({ size: opts.size, usage: ["uniform", "copy_dst"], label: opts.label });
|
|
33
|
+
this.bindGroupLayout = opts.bindGroupLayout ?? createBindGroupLayout(device, {
|
|
34
|
+
label: opts.label ? `${opts.label}.bgl` : undefined,
|
|
35
|
+
entries: [bind.uniform(0, opts.visibility ?? defaultVisibility, { minBindingSize: opts.size })],
|
|
36
|
+
});
|
|
37
|
+
this.bindGroup = createBindGroup(device, {
|
|
38
|
+
label: opts.label ? `${opts.label}.bg` : undefined,
|
|
39
|
+
layout: this.bindGroupLayout,
|
|
40
|
+
entries: [bind.resource(0, this.buffer)],
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
/** The underlying uniform `GPUBuffer`. */
|
|
44
|
+
get gpu() {
|
|
45
|
+
return this.buffer.gpu;
|
|
46
|
+
}
|
|
47
|
+
/**
|
|
48
|
+
* Upload `data` to the buffer via `queue.writeBuffer`. No dynamic offset; the
|
|
49
|
+
* bind group is unchanged. Call only when the uniform contents actually change.
|
|
50
|
+
*/
|
|
51
|
+
write(data, offset = 0) {
|
|
52
|
+
this.buffer.write(data, offset);
|
|
53
|
+
}
|
|
54
|
+
destroy() {
|
|
55
|
+
if (this.destroyed)
|
|
56
|
+
return;
|
|
57
|
+
this.destroyed = true;
|
|
58
|
+
this.buffer.destroy();
|
|
59
|
+
}
|
|
60
|
+
dispose() {
|
|
61
|
+
this.destroy();
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
//# sourceMappingURL=uniform.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"uniform.js","sourceRoot":"","sources":["../src/uniform.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,IAAI,EAAU,eAAe,EAAE,qBAAqB,EAAe,MAAM,YAAY,CAAC;AAmB/F,MAAM,iBAAiB,GAAG,CAAC,CAAC,UAAU,CAAC,cAAc,EAAE,MAAM,IAAI,CAAC,CAAC,GAAG,CAAC,UAAU,CAAC,cAAc,EAAE,QAAQ,IAAI,CAAC,CAAC,CAAwB,CAAC;AAEzI;;;;;;;;;;;;;;;;;;GAkBG;AACH,MAAM,OAAO,OAAO;IAOG;IANZ,IAAI,CAAS;IACb,MAAM,CAAS;IACf,eAAe,CAAqB;IACpC,SAAS,CAAe;IACzB,SAAS,GAAG,KAAK,CAAC;IAE1B,YAAqB,MAAc,EAAE,IAAoB;QAApC,WAAM,GAAN,MAAM,CAAQ;QACjC,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,GAAG,MAAM,CAAC,YAAY,CAAC,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,CAAC,SAAS,EAAE,UAAU,CAAC,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;QAC1G,IAAI,CAAC,eAAe,GAAG,IAAI,CAAC,eAAe,IAAI,qBAAqB,CAAC,MAAM,EAAE;YAC3E,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,MAAM,CAAC,CAAC,CAAC,SAAS;YACnD,OAAO,EAAE,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,EAAE,IAAI,CAAC,UAAU,IAAI,iBAAiB,EAAE,EAAE,cAAc,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;SAChG,CAAC,CAAC;QACH,IAAI,CAAC,SAAS,GAAG,eAAe,CAAC,MAAM,EAAE;YACvC,KAAK,EAAE,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,KAAK,KAAK,CAAC,CAAC,CAAC,SAAS;YAClD,MAAM,EAAE,IAAI,CAAC,eAAe;YAC5B,OAAO,EAAE,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,EAAE,IAAI,CAAC,MAAM,CAAC,CAAC;SACzC,CAAC,CAAC;IACL,CAAC;IAED,0CAA0C;IAC1C,IAAI,GAAG;QACL,OAAO,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC;IACzB,CAAC;IAED;;;OAGG;IACH,KAAK,CAAC,IAAkB,EAAE,MAAM,GAAG,CAAC;QAClC,IAAI,CAAC,MAAM,CAAC,KAAK,CAAC,IAAI,EAAE,MAAM,CAAC,CAAC;IAClC,CAAC;IAED,OAAO;QACL,IAAI,IAAI,CAAC,SAAS;YAAE,OAAO;QAC3B,IAAI,CAAC,SAAS,GAAG,IAAI,CAAC;QACtB,IAAI,CAAC,MAAM,CAAC,OAAO,EAAE,CAAC;IACxB,CAAC;IAED,OAAO;QACL,IAAI,CAAC,OAAO,EAAE,CAAC;IACjB,CAAC;CACF"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@vgpu/render",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.7",
|
|
4
4
|
"description": "Render pipeline + render pass abstractions for vgpu.",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"webgpu",
|
|
@@ -47,6 +47,10 @@
|
|
|
47
47
|
"./passes": {
|
|
48
48
|
"types": "./dist/passes/index.d.ts",
|
|
49
49
|
"import": "./dist/passes/index.js"
|
|
50
|
+
},
|
|
51
|
+
"./perf": {
|
|
52
|
+
"types": "./dist/perf/index.d.ts",
|
|
53
|
+
"import": "./dist/perf/index.js"
|
|
50
54
|
}
|
|
51
55
|
},
|
|
52
56
|
"files": [
|
|
@@ -58,14 +62,15 @@
|
|
|
58
62
|
],
|
|
59
63
|
"dependencies": {
|
|
60
64
|
"wgpu-matrix": "^3.4.2",
|
|
61
|
-
"@vgpu/core": "0.0.
|
|
65
|
+
"@vgpu/core": "0.0.7"
|
|
62
66
|
},
|
|
63
67
|
"vgpuExportBundleBudgetsGzipBytes": {
|
|
64
68
|
".": 49152,
|
|
65
69
|
"./inspect": 4096,
|
|
66
70
|
"./utils": 2048,
|
|
67
71
|
"./edit": 25600,
|
|
68
|
-
"./passes": 3584
|
|
72
|
+
"./passes": 3584,
|
|
73
|
+
"./perf": 3072
|
|
69
74
|
},
|
|
70
75
|
"scripts": {
|
|
71
76
|
"build": "tsc -b",
|
|
@@ -9,6 +9,8 @@ directly to native WebGPU, `RenderPass.setPipeline()`, or render-bundle recordin
|
|
|
9
9
|
```ts
|
|
10
10
|
createRenderPipeline(device: Device, opts: RenderPipelineOptions): GPURenderPipeline
|
|
11
11
|
createRenderPipelineAsync(device: Device, opts: RenderPipelineOptions): Promise<GPURenderPipeline>
|
|
12
|
+
createRenderPipelineFromDescriptor(device: Device, descriptor: GPURenderPipelineDescriptor): GPURenderPipeline
|
|
13
|
+
createRenderPipelineFromDescriptorAsync(device: Device, descriptor: GPURenderPipelineDescriptor, fallback?: RenderPipelineAsyncFallback): Promise<GPURenderPipeline>
|
|
12
14
|
```
|
|
13
15
|
|
|
14
16
|
`createRenderPipelineAsync` calls `GPUDevice.createRenderPipelineAsync()` when it
|
|
@@ -18,6 +20,24 @@ calls `createRenderPipeline()` instead. Performance-critical warmup can pass
|
|
|
18
20
|
`fallback: "throw"` to receive a structured `VGPUError` with code
|
|
19
21
|
`VGPU-RENDER-PIPELINE-ASYNC-UNAVAILABLE` instead of accidentally blocking.
|
|
20
22
|
|
|
23
|
+
## Raw descriptor entrypoints
|
|
24
|
+
|
|
25
|
+
If you already have a hand-built `GPURenderPipelineDescriptor`, pass it straight
|
|
26
|
+
through — do not reshape it into `RenderPipelineOptions` just to get the
|
|
27
|
+
async→sync fallback:
|
|
28
|
+
|
|
29
|
+
- `createRenderPipelineFromDescriptor(device, descriptor)` forwards the descriptor
|
|
30
|
+
to `GPUDevice.createRenderPipeline()` unchanged.
|
|
31
|
+
- `createRenderPipelineFromDescriptorAsync(device, descriptor, fallback?)` forwards
|
|
32
|
+
it to `GPUDevice.createRenderPipelineAsync()` with the exact same compatibility
|
|
33
|
+
fallback as `createRenderPipelineAsync` (default `"sync"`, or `"throw"` for a
|
|
34
|
+
structured `VGPUError`).
|
|
35
|
+
|
|
36
|
+
The descriptor is forwarded verbatim, so native WebGPU validation and lifecycle
|
|
37
|
+
rules remain the caller's responsibility. These are explicit, separately named
|
|
38
|
+
exports rather than an overload so a `RenderPipelineOptions` caller can never be
|
|
39
|
+
misread as passing a raw descriptor.
|
|
40
|
+
|
|
21
41
|
VGPU does not cache pipelines: one helper call equals one WebGPU device call.
|
|
22
42
|
Keep pipeline caches explicit and owned by the caller.
|
|
23
43
|
|
|
@@ -86,6 +106,20 @@ const pipeline = createRenderPipeline(device, {
|
|
|
86
106
|
});
|
|
87
107
|
```
|
|
88
108
|
|
|
109
|
+
Existing raw `GPURenderPipelineDescriptor`, only wanting the async fallback:
|
|
110
|
+
|
|
111
|
+
```ts
|
|
112
|
+
const descriptor: GPURenderPipelineDescriptor = {
|
|
113
|
+
label: "hero.pipeline",
|
|
114
|
+
layout: pipelineLayout,
|
|
115
|
+
vertex: { module: shaderModule, entryPoint: "vs_main", buffers },
|
|
116
|
+
fragment: { module: shaderModule, entryPoint: "fs_main", targets: [{ format }] },
|
|
117
|
+
primitive: { topology: "triangle-list" },
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
const pipeline = await createRenderPipelineFromDescriptorAsync(device, descriptor, "throw");
|
|
121
|
+
```
|
|
122
|
+
|
|
89
123
|
## Raw escape hatch
|
|
90
124
|
|
|
91
125
|
`Shader.gpu` is an intentional advanced escape hatch to the underlying
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
# @vgpu/render/perf
|
|
2
|
+
|
|
3
|
+
Measurement utilities for the optimize loop: confirm a shader change is still correct and actually
|
|
4
|
+
faster. **Tooling only — never call these on a live animation-frame path.** See the `measuring`
|
|
5
|
+
guide (`vgpu docs cat /guides/measuring.docs.md`) for the workflow.
|
|
6
|
+
|
|
7
|
+
## gpuFrameTime
|
|
8
|
+
|
|
9
|
+
`gpuFrameTime(device, encode, options?)` measures median GPU time per frame for a render routine.
|
|
10
|
+
|
|
11
|
+
The `encode(frame, index)` callback records the frame's passes onto a vgpu `Frame` — the same body
|
|
12
|
+
you run in production. The harness owns warmup, the loop, submit, and timing. It uses GPU timestamp
|
|
13
|
+
queries when the device exposes `timestamp-query`, otherwise wall-clock timing via
|
|
14
|
+
`device.queue.flush()`.
|
|
15
|
+
|
|
16
|
+
```ts
|
|
17
|
+
import { gpuFrameTime } from "@vgpu/render/perf";
|
|
18
|
+
|
|
19
|
+
const { medianMs, method } = await gpuFrameTime(device, (frame, i) => {
|
|
20
|
+
frame.renderPass(scenePass, (pass) => drawScene(pass, i));
|
|
21
|
+
frame.renderPass(floorPass, (pass) => drawFloor(pass, i));
|
|
22
|
+
});
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
- `options.frames` (default 120), `options.warmup` (default 30), `options.batch` (wall-clock batch
|
|
26
|
+
size, default 8), `options.forceWallClock`, `options.label`.
|
|
27
|
+
- Returns `GpuFrameTimeResult`: `{ medianMs, meanMs, minMs, p95Ms, samples, method }` where `method`
|
|
28
|
+
is `"timestamp-query"` or `"wall-clock"`.
|
|
29
|
+
- Compare medians before vs after. For absolute numbers use a real-GPU device (`@vgpu/adapter-node`
|
|
30
|
+
Dawn or a browser); software backends give relative signal only.
|
|
31
|
+
|
|
32
|
+
## pixelDiff
|
|
33
|
+
|
|
34
|
+
`pixelDiff(a, b)` compares two renders byte-for-byte. Pass two `Texture`s (read back via
|
|
35
|
+
`Texture.read()`) or two `Uint8Array`s.
|
|
36
|
+
|
|
37
|
+
```ts
|
|
38
|
+
import { pixelDiff } from "@vgpu/render/perf";
|
|
39
|
+
|
|
40
|
+
const { maxByte } = await pixelDiff(before, after); // before/after are Texture or Uint8Array
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
Returns `PixelDiffResult`: `{ maxByte, meanByte, changedBytes, totalBytes, changedFraction }`.
|
|
44
|
+
Interpret `maxByte` (max per-channel delta, 0–255): `0` bit-exact, `1–2` imperceptible
|
|
45
|
+
(driver-rounding floor), more is a real visual change. A length mismatch surfaces as `maxByte = 255`.
|