typegpu 0.12.4 → 0.12.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/bin.mjs +39 -67
- package/common/index.d.ts +1 -0
- package/common/index.js +1 -0
- package/common/writeChannels.d.ts +31 -0
- package/common/writeChannels.js +32 -0
- package/core/commandEncoder/computePass.d.ts +13 -0
- package/core/commandEncoder/computePass.js +5 -2
- package/core/commandEncoder/renderPass.d.ts +18 -4
- package/core/commandEncoder/renderPass.js +6 -2
- package/core/declare/tgpuDeclare.d.ts +11 -0
- package/core/declare/tgpuDeclare.js +1 -1
- package/core/immediate/immediateVar.d.ts +52 -0
- package/core/immediate/immediateVar.js +149 -0
- package/core/pipeline/computePipeline.d.ts +23 -0
- package/core/pipeline/computePipeline.js +26 -9
- package/core/pipeline/drawState.d.ts +8 -4
- package/core/pipeline/drawState.js +41 -51
- package/core/pipeline/priors.js +1 -0
- package/core/pipeline/renderPipeline.d.ts +24 -1
- package/core/pipeline/renderPipeline.js +18 -1
- package/core/root/init.js +3 -0
- package/core/root/rootTypes.d.ts +11 -0
- package/core/slot/slotTypes.d.ts +2 -1
- package/core/texture/texture.d.ts +20 -14
- package/core/texture/texture.js +70 -65
- package/core/texture/textureUtils.d.ts +3 -6
- package/core/texture/textureUtils.js +142 -165
- package/core/texture/textureWrite.d.ts +61 -0
- package/core/texture/textureWrite.js +59 -0
- package/data/dataIO.d.ts +2 -2
- package/data/generalizeFn.d.ts +17 -1
- package/data/generalizeFn.js +39 -2
- package/data/snippet.d.ts +5 -1
- package/data/snippet.js +1 -0
- package/data/wgslTypes.d.ts +1 -1
- package/errors.d.ts +7 -0
- package/errors.js +15 -0
- package/indexNamedExports.d.ts +5 -2
- package/indexNamedExports.js +2 -1
- package/internal.d.ts +1 -1
- package/internal.js +1 -1
- package/package.json +2 -2
- package/resolutionCtx.d.ts +5 -0
- package/resolutionCtx.js +11 -0
- package/serial/restore.d.ts +3 -1
- package/serial/restore.js +2 -0
- package/shared/meta.js +1 -1
- package/shared/symbols.js +1 -1
- package/shared/tseynit.d.ts +1 -0
- package/shared/tseynit.js +39 -7
- package/shared/utilityTypes.d.ts +1 -1
- package/std/atomic.d.ts +13 -0
- package/std/atomic.js +63 -29
- package/std/bitcast.js +4 -0
- package/std/boolean.d.ts +1 -1
- package/std/boolean.js +35 -21
- package/std/derivative.js +17 -12
- package/std/index.d.ts +4 -4
- package/std/index.js +5 -5
- package/std/numeric.d.ts +5 -1
- package/std/numeric.js +75 -8
- package/std/operators.d.ts +2 -2
- package/std/operators.js +29 -17
- package/std/packing.d.ts +61 -1
- package/std/packing.js +214 -25
- package/std/subgroup.d.ts +4 -0
- package/std/subgroup.js +34 -0
- package/std/texture.d.ts +13 -0
- package/std/texture.js +48 -0
- package/tgpuUnstable.d.ts +1 -0
- package/tgpuUnstable.js +1 -0
- package/tgsl/conversion.d.ts +2 -0
- package/tgsl/conversion.js +3 -0
- package/tgsl/shaderGenerator.d.ts +1 -1
- package/tgsl/wgslGenerator.d.ts +5 -0
- package/tgsl/wgslGenerator.js +218 -50
- package/types.d.ts +9 -1
- package/types.js +5 -0
package/bin.mjs
CHANGED
|
@@ -1,79 +1,51 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import { spawn } from 'node:child_process';
|
|
2
|
+
import { execFile, spawn } from 'node:child_process';
|
|
3
|
+
import { promisify } from 'node:util';
|
|
3
4
|
import pkg from './package.json' with { type: 'json' };
|
|
4
5
|
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
* trigger the CLI, which then allows us to download the latest
|
|
8
|
-
* version matching the major and minor of the `typegpu` package.
|
|
9
|
-
*/
|
|
10
|
-
const versionPattern = /^(\d+)\.(\d+)\.(\d+)/;
|
|
11
|
-
|
|
12
|
-
const result = versionPattern.exec(pkg.version);
|
|
13
|
-
const [_, major, minor] = result;
|
|
6
|
+
const [major, minor] = pkg.version.split('.');
|
|
7
|
+
const semver = `^${major}.${minor}.0`;
|
|
14
8
|
|
|
15
|
-
|
|
16
|
-
|
|
9
|
+
const windows = process.platform === 'win32';
|
|
10
|
+
const npm = windows ? 'npm.cmd' : 'npm';
|
|
11
|
+
const npx = windows ? 'npx.cmd' : 'npx';
|
|
12
|
+
const execFileAsync = promisify(execFile);
|
|
13
|
+
|
|
14
|
+
/** True only when the registry confirms no `@typegpu/cli` satisfies `semver` */
|
|
15
|
+
async function noMatchingCli() {
|
|
16
|
+
const args = ['view', `@typegpu/cli@${semver}`, 'version', '--json'];
|
|
17
|
+
try {
|
|
18
|
+
const { stdout } = await execFileAsync(npm, args, { shell: windows, timeout: 3_000 });
|
|
19
|
+
return ['', '[]'].includes(stdout.trim());
|
|
20
|
+
} catch (err) {
|
|
21
|
+
return /E404|ETARGET/.test(err.stderr ?? '');
|
|
22
|
+
}
|
|
17
23
|
}
|
|
18
24
|
|
|
19
|
-
/**
|
|
20
|
-
|
|
21
|
-
*/
|
|
22
|
-
const semver = `^${major}.${minor}.0`;
|
|
23
|
-
|
|
24
|
-
/**
|
|
25
|
-
* @returns {Promise<number | undefined>}
|
|
26
|
-
*/
|
|
27
|
-
function asyncSpawn(...args) {
|
|
25
|
+
/** Resolves with the child's exit code, re-raising a fatal signal on this process */
|
|
26
|
+
function run(command, args) {
|
|
28
27
|
return new Promise((resolve, reject) => {
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
resolve(code);
|
|
39
|
-
});
|
|
40
|
-
|
|
41
|
-
child.on('error', (err) => {
|
|
42
|
-
reject(err);
|
|
43
|
-
});
|
|
28
|
+
spawn(command, args, { stdio: 'inherit', shell: windows })
|
|
29
|
+
.on('error', reject)
|
|
30
|
+
.on('exit', (code, signal) => {
|
|
31
|
+
if (signal) {
|
|
32
|
+
process.kill(process.pid, signal);
|
|
33
|
+
} else {
|
|
34
|
+
resolve(code ?? 1);
|
|
35
|
+
}
|
|
36
|
+
});
|
|
44
37
|
});
|
|
45
38
|
}
|
|
46
39
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
function failedToRunErrHandler(label) {
|
|
51
|
-
return (err) => {
|
|
52
|
-
console.error(`Failed to run '${label}':`, err);
|
|
53
|
-
process.exit(1);
|
|
54
|
-
};
|
|
40
|
+
const fallback = await noMatchingCli();
|
|
41
|
+
if (fallback) {
|
|
42
|
+
console.warn(`Couldn't find @typegpu/cli version matching ${semver}, falling back to latest...`);
|
|
55
43
|
}
|
|
44
|
+
const spec = fallback ? '@typegpu/cli@latest' : `@typegpu/cli@${semver}`;
|
|
56
45
|
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
shell: windows, // needs to be ran through the shell on Windows
|
|
64
|
-
}).catch(failedToRunErrHandler(`npx @typegpu/cli@${semver}`));
|
|
65
|
-
|
|
66
|
-
if (code !== 0) {
|
|
67
|
-
console.warn(
|
|
68
|
-
`Couldn't find @typegpu/cli version matching ${semver}, falling back to latest...`,
|
|
69
|
-
);
|
|
70
|
-
// Fallback to latest
|
|
71
|
-
const code = await asyncSpawn(npxCommand, [`@typegpu/cli@latest`, ...process.argv.slice(2)], {
|
|
72
|
-
stdio: 'inherit',
|
|
73
|
-
shell: windows, // needs to be ran through the shell on Windows
|
|
74
|
-
}).catch(failedToRunErrHandler('npx @typegpu/cli@latest'));
|
|
75
|
-
process.exit(code ?? 0);
|
|
76
|
-
}
|
|
77
|
-
|
|
78
|
-
process.exit(code ?? 0);
|
|
79
|
-
})();
|
|
46
|
+
try {
|
|
47
|
+
process.exit(await run(npx, [spec, ...process.argv.slice(2)]));
|
|
48
|
+
} catch (err) {
|
|
49
|
+
console.error(`Failed to run 'npx ${spec}':`, err);
|
|
50
|
+
process.exit(2);
|
|
51
|
+
}
|
package/common/index.d.ts
CHANGED
package/common/index.js
CHANGED
|
@@ -0,0 +1,31 @@
|
|
|
1
|
+
import type { TgpuTexture } from '../core/texture/texture.ts';
|
|
2
|
+
import { type TextureChannel, type TextureWriteOptions } from '../core/texture/textureWrite.ts';
|
|
3
|
+
import type { RenderFlag } from '../core/texture/usageExtension.ts';
|
|
4
|
+
export type TextureChannelSource = {
|
|
5
|
+
source: GPUCopyExternalImageSource;
|
|
6
|
+
from: TextureChannel;
|
|
7
|
+
};
|
|
8
|
+
export type TextureChannels = {
|
|
9
|
+
[Channel in TextureChannel]?: TextureChannelSource | undefined;
|
|
10
|
+
};
|
|
11
|
+
/**
|
|
12
|
+
* Writes image sources into individual channels of `texture`. Each entry
|
|
13
|
+
* writes a single channel, with `from` selecting which channel of the
|
|
14
|
+
* source to read. Omitted channels are left untouched.
|
|
15
|
+
*
|
|
16
|
+
* Requires the `'render'` usage flag on the texture.
|
|
17
|
+
*
|
|
18
|
+
* @example
|
|
19
|
+
* ```ts
|
|
20
|
+
* common.writeChannels(material, {
|
|
21
|
+
* r: { source: roughnessMap, from: 'r' },
|
|
22
|
+
* g: { source: metalnessMap, from: 'r' },
|
|
23
|
+
* a: { source: maskMap, from: 'r' },
|
|
24
|
+
* });
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function writeChannels(texture: TgpuTexture & RenderFlag, channels: TextureChannels, options?: TextureWriteOptions): void;
|
|
28
|
+
export declare namespace writeChannels {
|
|
29
|
+
type Channels = TextureChannels;
|
|
30
|
+
type Source = TextureChannelSource;
|
|
31
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { writeImages } from "../core/texture/textureUtils.js";
|
|
2
|
+
import { resolveImageWrite, } from "../core/texture/textureWrite.js";
|
|
3
|
+
import { $internal, $soul } from "../shared/symbols.js";
|
|
4
|
+
/**
|
|
5
|
+
* Writes image sources into individual channels of `texture`. Each entry
|
|
6
|
+
* writes a single channel, with `from` selecting which channel of the
|
|
7
|
+
* source to read. Omitted channels are left untouched.
|
|
8
|
+
*
|
|
9
|
+
* Requires the `'render'` usage flag on the texture.
|
|
10
|
+
*
|
|
11
|
+
* @example
|
|
12
|
+
* ```ts
|
|
13
|
+
* common.writeChannels(material, {
|
|
14
|
+
* r: { source: roughnessMap, from: 'r' },
|
|
15
|
+
* g: { source: metalnessMap, from: 'r' },
|
|
16
|
+
* a: { source: maskMap, from: 'r' },
|
|
17
|
+
* });
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function writeChannels(texture, channels, options = {}) {
|
|
21
|
+
const writes = [];
|
|
22
|
+
for (const to of ['r', 'g', 'b', 'a']) {
|
|
23
|
+
const entry = channels[to];
|
|
24
|
+
if (entry) {
|
|
25
|
+
writes.push({
|
|
26
|
+
...resolveImageWrite(texture.props, entry.source, options),
|
|
27
|
+
channel: { from: entry.from, to },
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
writeImages(texture[$soul].device, texture[$internal].materialize(), writes);
|
|
32
|
+
}
|
|
@@ -1,8 +1,10 @@
|
|
|
1
1
|
import type { PrimitiveOffsetInfo } from '../../data/offsetUtils.ts';
|
|
2
2
|
import type { AnyWgslData } from '../../data/wgslTypes.ts';
|
|
3
|
+
import type { InferInput } from '../../shared/repr.ts';
|
|
3
4
|
import { $internal } from '../../shared/symbols.ts';
|
|
4
5
|
import type { TgpuBindGroup, TgpuBindGroupLayout, TgpuLayoutEntry } from '../../tgpuBindGroupLayout.ts';
|
|
5
6
|
import type { IndirectFlag, TgpuBuffer } from '../buffer/buffer.ts';
|
|
7
|
+
import type { TgpuImmediateVar } from '../immediate/immediateVar.ts';
|
|
6
8
|
import { ComputeDrawState } from '../pipeline/drawState.ts';
|
|
7
9
|
import type { TgpuComputePipeline } from '../pipeline/computePipeline.ts';
|
|
8
10
|
import type { ExperimentalTgpuRoot } from '../root/rootTypes.ts';
|
|
@@ -41,6 +43,17 @@ export interface TgpuComputePass {
|
|
|
41
43
|
setBindGroup(bindGroup: TgpuBindGroup): void;
|
|
42
44
|
/** Associates a bind group with the given layout */
|
|
43
45
|
setBindGroup<Entries extends Record<string, TgpuLayoutEntry | null>>(bindGroupLayout: TgpuBindGroupLayout<Entries>, bindGroup: TgpuBindGroup<Entries> | GPUBindGroup): void;
|
|
46
|
+
/**
|
|
47
|
+
* Provides a value for the given immediate variable, used by subsequent dispatches.
|
|
48
|
+
* The value is captured (copied) at call time; mutating it afterwards has no
|
|
49
|
+
* effect until it is set again. Takes precedence over values held by the
|
|
50
|
+
* pipeline (`pipeline.with(immediate, value)`) and persists across pipeline
|
|
51
|
+
* switches until set again, like any other pass-level state.
|
|
52
|
+
*
|
|
53
|
+
* Passing an `ArrayBuffer` or typed array skips serialization entirely; the bytes
|
|
54
|
+
* are copied verbatim and the caller guarantees they match the schema's layout.
|
|
55
|
+
*/
|
|
56
|
+
setImmediates<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): void;
|
|
44
57
|
dispatchWorkgroups(x: number, y?: number, z?: number): void;
|
|
45
58
|
/**
|
|
46
59
|
* Dispatches compute workgroups using parameters read from a buffer.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { $internal } from "../../shared/symbols.js";
|
|
2
2
|
import { isGPUBuffer } from "../../types.js";
|
|
3
3
|
import { DISPATCH_INDIRECT_SIZE, resolveIndirectOffset } from "../pipeline/pipelineUtils.js";
|
|
4
|
-
import { ComputeDrawState, emitComputeDispatch, recordBindGroup,
|
|
4
|
+
import { ComputeDrawState, emitComputeDispatch, recordBindGroup, selectPipeline, } from "../pipeline/drawState.js";
|
|
5
5
|
import { unwrapTimestampWrites } from "./attachments.js";
|
|
6
6
|
// --------------
|
|
7
7
|
// Implementation
|
|
@@ -44,11 +44,14 @@ class TgpuComputePassImpl {
|
|
|
44
44
|
emitComputeDispatch(this.#root, internals, pipeline, emit);
|
|
45
45
|
}
|
|
46
46
|
setPipeline(pipeline) {
|
|
47
|
-
|
|
47
|
+
selectPipeline(this[$internal].state, pipeline);
|
|
48
48
|
}
|
|
49
49
|
setBindGroup(first, bindGroup) {
|
|
50
50
|
recordBindGroup(this[$internal].state, first, bindGroup);
|
|
51
51
|
}
|
|
52
|
+
setImmediates(immediate, value) {
|
|
53
|
+
this[$internal].state.immediates.set(immediate, value);
|
|
54
|
+
}
|
|
52
55
|
dispatchWorkgroups(x, y, z) {
|
|
53
56
|
this.#emit((rawPass) => rawPass.dispatchWorkgroups(x, y, z));
|
|
54
57
|
}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import type { Disarray } from '../../data/dataTypes.ts';
|
|
2
2
|
import type { PrimitiveOffsetInfo } from '../../data/offsetUtils.ts';
|
|
3
|
-
import type { BaseData, WgslArray } from '../../data/wgslTypes.ts';
|
|
3
|
+
import type { AnyWgslData, BaseData, WgslArray } from '../../data/wgslTypes.ts';
|
|
4
|
+
import type { InferInput } from '../../shared/repr.ts';
|
|
4
5
|
import { $internal } from '../../shared/symbols.ts';
|
|
5
6
|
import type { TgpuBindGroup, TgpuBindGroupLayout, TgpuLayoutEntry } from '../../tgpuBindGroupLayout.ts';
|
|
6
7
|
import type { IndexFlag, IndirectFlag, TgpuBuffer, VertexFlag } from '../buffer/buffer.ts';
|
|
8
|
+
import type { TgpuImmediateVar } from '../immediate/immediateVar.ts';
|
|
7
9
|
import { RenderDrawState } from '../pipeline/drawState.ts';
|
|
8
10
|
import type { TgpuRenderPipeline } from '../pipeline/renderPipeline.ts';
|
|
9
11
|
import { type TgpuQuerySet } from '../querySet/querySet.ts';
|
|
@@ -58,6 +60,17 @@ export interface TgpuRenderCommands {
|
|
|
58
60
|
setVertexBuffer<TData extends WgslArray | Disarray>(vertexLayout: TgpuVertexLayout<TData>, buffer: (TgpuBuffer<TData> & VertexFlag) | GPUBuffer, offset?: number, size?: number): void;
|
|
59
61
|
/** Sets the current index buffer */
|
|
60
62
|
setIndexBuffer<TData extends WgslArray | Disarray>(buffer: (TgpuBuffer<TData> & IndexFlag) | GPUBuffer, indexFormat: GPUIndexFormat, offset?: number, size?: number): void;
|
|
63
|
+
/**
|
|
64
|
+
* Provides a value for the given immediate variable, used by subsequent draw calls.
|
|
65
|
+
* The value is captured (copied) at call time; mutating it afterwards has no
|
|
66
|
+
* effect until it is set again. Takes precedence over values held by the
|
|
67
|
+
* pipeline (`pipeline.with(immediate, value)`) and persists across pipeline
|
|
68
|
+
* switches until set again, like any other pass-level state.
|
|
69
|
+
*
|
|
70
|
+
* Passing an `ArrayBuffer` or typed array skips serialization entirely; the bytes
|
|
71
|
+
* are copied verbatim and the caller guarantees they match the schema's layout.
|
|
72
|
+
*/
|
|
73
|
+
setImmediates<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): void;
|
|
61
74
|
draw(vertexCount: number, instanceCount?: number, firstVertex?: number, firstInstance?: number): void;
|
|
62
75
|
drawIndexed(indexCount: number, instanceCount?: number, firstIndex?: number, baseVertex?: number, firstInstance?: number): void;
|
|
63
76
|
/**
|
|
@@ -108,9 +121,10 @@ export interface TgpuRenderPass extends TgpuRenderCommands {
|
|
|
108
121
|
endOcclusionQuery(): void;
|
|
109
122
|
/**
|
|
110
123
|
* Executes previously recorded {@link GPURenderBundle}s as part of this pass.
|
|
111
|
-
* As per the WebGPU spec, this resets the raw pass's pipeline, bind group
|
|
112
|
-
*
|
|
113
|
-
* re-applied on
|
|
124
|
+
* As per the WebGPU spec, this resets the raw pass's pipeline, bind group,
|
|
125
|
+
* vertex/index buffer and immediate data state. The state tracked by this
|
|
126
|
+
* typed pass (including values set via `setImmediates`) is re-applied on
|
|
127
|
+
* the next draw.
|
|
114
128
|
*/
|
|
115
129
|
executeBundles(bundles: Iterable<GPURenderBundle>): void;
|
|
116
130
|
/** Completes the recording of this render pass */
|
|
@@ -2,7 +2,7 @@ import { $internal } from "../../shared/symbols.js";
|
|
|
2
2
|
import { isGPUBuffer } from "../../types.js";
|
|
3
3
|
import { DRAW_INDEXED_INDIRECT_SIZE, DRAW_INDIRECT_SIZE, resolveIndirectOffset, } from "../pipeline/pipelineUtils.js";
|
|
4
4
|
import { isTexture, isTextureView } from "../texture/texture.js";
|
|
5
|
-
import { emitRenderDraw, recordBindGroup, RenderDrawState,
|
|
5
|
+
import { emitRenderDraw, recordBindGroup, RenderDrawState, selectPipeline, } from "../pipeline/drawState.js";
|
|
6
6
|
import { isQuerySet } from "../querySet/querySet.js";
|
|
7
7
|
import { unwrapAttachmentView, unwrapTimestampWrites, } from "./attachments.js";
|
|
8
8
|
// --------------
|
|
@@ -125,7 +125,7 @@ class TgpuRenderCommandsImpl {
|
|
|
125
125
|
emitRenderDraw(this.#root, internals, pipeline, usesIndexBuffer, emit);
|
|
126
126
|
}
|
|
127
127
|
setPipeline(pipeline) {
|
|
128
|
-
|
|
128
|
+
selectPipeline(this[$internal].state, pipeline);
|
|
129
129
|
}
|
|
130
130
|
setBindGroup(first, bindGroup) {
|
|
131
131
|
recordBindGroup(this[$internal].state, first, bindGroup);
|
|
@@ -140,6 +140,9 @@ class TgpuRenderCommandsImpl {
|
|
|
140
140
|
state.indexBuffer = { buffer, indexFormat, offsetBytes: offset, sizeBytes: size };
|
|
141
141
|
state.version++;
|
|
142
142
|
}
|
|
143
|
+
setImmediates(immediate, value) {
|
|
144
|
+
this[$internal].state.immediates.set(immediate, value);
|
|
145
|
+
}
|
|
143
146
|
draw(vertexCount, instanceCount, firstVertex, firstInstance) {
|
|
144
147
|
this.#emit(false, (rawPass) => rawPass.draw(vertexCount, instanceCount, firstVertex, firstInstance));
|
|
145
148
|
}
|
|
@@ -189,6 +192,7 @@ class TgpuRenderPassImpl extends TgpuRenderCommandsImpl {
|
|
|
189
192
|
const internals = this[$internal];
|
|
190
193
|
internals.rawPass.executeBundles(bundles);
|
|
191
194
|
internals.appliedVersion = undefined;
|
|
195
|
+
internals.state.immediates.invalidate();
|
|
192
196
|
}
|
|
193
197
|
end() {
|
|
194
198
|
this[$internal].rawPass.end();
|
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
import { type ResolvedSnippet } from '../../data/snippet.ts';
|
|
2
|
+
import { $internal, $resolve } from '../../shared/symbols.ts';
|
|
3
|
+
import type { ResolutionCtx, SelfResolvable } from '../../types.ts';
|
|
1
4
|
/**
|
|
2
5
|
* Extra declaration that shall be included in final WGSL code,
|
|
3
6
|
* when resolving objects that use it.
|
|
@@ -13,3 +16,11 @@ export interface TgpuDeclare {
|
|
|
13
16
|
* It was developed to ensure full compatibility of TypeGPU programs with current and future versions of WGSL.
|
|
14
17
|
*/
|
|
15
18
|
export declare function declare(declaration: string): TgpuDeclare;
|
|
19
|
+
export declare class TgpuDeclareImpl implements TgpuDeclare, SelfResolvable {
|
|
20
|
+
#private;
|
|
21
|
+
readonly [$internal] = true;
|
|
22
|
+
constructor(declaration: string);
|
|
23
|
+
$uses(dependencyMap: Record<string, unknown>): this;
|
|
24
|
+
[$resolve](ctx: ResolutionCtx): ResolvedSnippet;
|
|
25
|
+
toString(): string;
|
|
26
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { type BaseData, type MatData, type ScalarData, type VecData, type WgslStruct } from '../../data/wgslTypes.ts';
|
|
2
|
+
import type { TgpuNamable } from '../../shared/meta.ts';
|
|
3
|
+
import type { InferGPU, InferInput } from '../../shared/repr.ts';
|
|
4
|
+
import type { TgpuSoul } from '../../shared/soul.ts';
|
|
5
|
+
import { $gpuValueOf, $internal, $soul } from '../../shared/symbols.ts';
|
|
6
|
+
export type ImmediateData = ScalarData | VecData | MatData | WgslStruct;
|
|
7
|
+
export interface TgpuImmediateVarSoul<TDataType extends BaseData = BaseData> extends TgpuSoul<'immediate-var'> {
|
|
8
|
+
readonly dataType: TDataType;
|
|
9
|
+
readonly defaultValue: InferInput<TDataType> | undefined;
|
|
10
|
+
}
|
|
11
|
+
export interface TgpuImmediateVar<TDataType extends BaseData = BaseData> extends TgpuNamable {
|
|
12
|
+
readonly [$internal]: true;
|
|
13
|
+
readonly [$soul]: TgpuImmediateVarSoul<TDataType>;
|
|
14
|
+
readonly resourceType: 'immediate-var';
|
|
15
|
+
readonly dataType: TDataType;
|
|
16
|
+
readonly defaultValue: InferInput<TDataType> | undefined;
|
|
17
|
+
readonly [$gpuValueOf]: InferGPU<TDataType>;
|
|
18
|
+
readonly $: InferGPU<TDataType>;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Defines a variable in the 'immediate' address space, set from the CPU on a per-draw
|
|
22
|
+
* (or per-dispatch) basis without the overhead of a buffer.
|
|
23
|
+
*
|
|
24
|
+
* Only one immediate variable can be used in a single shader, and its size is limited
|
|
25
|
+
* by the device's `maxImmediateSize` limit. Requires the `immediate_address_space`
|
|
26
|
+
* WGSL language extension, check `root.enabledWgslLanguageFeatures` for support.
|
|
27
|
+
*
|
|
28
|
+
* @param dataType The schema of the held data's type. Cannot contain arrays, atomics or booleans.
|
|
29
|
+
* @param defaultValue The value used when no override is provided via `pipeline.with(immediate, value)`
|
|
30
|
+
* or `pass.setImmediates`. Captured (serialized) at creation time.
|
|
31
|
+
*/
|
|
32
|
+
export declare function immediateVar<TDataType extends ImmediateData>(dataType: TDataType, defaultValue?: InferInput<TDataType>): TgpuImmediateVar<TDataType>;
|
|
33
|
+
export declare function isImmediateVar(value: unknown): value is TgpuImmediateVar;
|
|
34
|
+
/**
|
|
35
|
+
* Validates that the given immediate variable can be used with the given root.
|
|
36
|
+
* @returns The size of the immediate data in bytes, to be passed as
|
|
37
|
+
* `immediateSize` to `device.createPipelineLayout`.
|
|
38
|
+
*/
|
|
39
|
+
export declare function validateImmediateUsage(immediate: TgpuImmediateVar, root: {
|
|
40
|
+
readonly enabledWgslLanguageFeatures: ReadonlySet<string>;
|
|
41
|
+
}): number;
|
|
42
|
+
/** Captured bytes of an immediate value, immutable once created */
|
|
43
|
+
export type ImmediateSnapshot = Uint8Array<ArrayBuffer>;
|
|
44
|
+
export declare function createImmediateSnapshot(immediate: TgpuImmediateVar, value: unknown): ImmediateSnapshot;
|
|
45
|
+
/** Captures pass overrides and tracks the last snapshot written to its encoder. */
|
|
46
|
+
export declare class ImmediatePassState {
|
|
47
|
+
#private;
|
|
48
|
+
set(immediate: TgpuImmediateVar, value: unknown): void;
|
|
49
|
+
invalidate(): void;
|
|
50
|
+
/** Pass overrides take precedence over pipeline values and the variable's default. */
|
|
51
|
+
write(pass: GPURenderPassEncoder | GPUComputePassEncoder | GPURenderBundleEncoder, immediate: TgpuImmediateVar, pipelineSnapshots: ReadonlyMap<TgpuImmediateVar, ImmediateSnapshot> | undefined, deduplicate: boolean): void;
|
|
52
|
+
}
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { writeToArrayBuffer } from "../../data/dataIO.js";
|
|
2
|
+
import { undecorate } from "../../data/dataTypes.js";
|
|
3
|
+
import { sizeOf } from "../../data/sizeOf.js";
|
|
4
|
+
import { snip } from "../../data/snippet.js";
|
|
5
|
+
import { isBool, isDecorated, isMat, isNumericSchema, isVec, isVecBool, isWgslStruct, } from "../../data/wgslTypes.js";
|
|
6
|
+
import { IllegalVarAccessError, MissingImmediatesError } from "../../errors.js";
|
|
7
|
+
import { isInsideTgpuFn } from "../../execMode.js";
|
|
8
|
+
import { getName, setName } from "../../shared/meta.js";
|
|
9
|
+
import { $gpuValueOf, $internal, $soul, isMarkedInternal } from "../../shared/symbols.js";
|
|
10
|
+
import { makeDereferenceable } from "../../tgsl/makeDereferenceable.js";
|
|
11
|
+
import { makeResolvable } from "../../tgsl/makeResolvable.js";
|
|
12
|
+
/**
|
|
13
|
+
* Defines a variable in the 'immediate' address space, set from the CPU on a per-draw
|
|
14
|
+
* (or per-dispatch) basis without the overhead of a buffer.
|
|
15
|
+
*
|
|
16
|
+
* Only one immediate variable can be used in a single shader, and its size is limited
|
|
17
|
+
* by the device's `maxImmediateSize` limit. Requires the `immediate_address_space`
|
|
18
|
+
* WGSL language extension, check `root.enabledWgslLanguageFeatures` for support.
|
|
19
|
+
*
|
|
20
|
+
* @param dataType The schema of the held data's type. Cannot contain arrays, atomics or booleans.
|
|
21
|
+
* @param defaultValue The value used when no override is provided via `pipeline.with(immediate, value)`
|
|
22
|
+
* or `pass.setImmediates`. Captured (serialized) at creation time.
|
|
23
|
+
*/
|
|
24
|
+
export function immediateVar(dataType, defaultValue) {
|
|
25
|
+
assertValidImmediateSchema(dataType);
|
|
26
|
+
return new TgpuImmediateVarImpl(dataType, defaultValue);
|
|
27
|
+
}
|
|
28
|
+
export function isImmediateVar(value) {
|
|
29
|
+
return value?.resourceType === 'immediate-var' && isMarkedInternal(value);
|
|
30
|
+
}
|
|
31
|
+
// --------------
|
|
32
|
+
// Implementation
|
|
33
|
+
// --------------
|
|
34
|
+
function assertValidImmediateSchema(schema, rootSchema = schema) {
|
|
35
|
+
if (isWgslStruct(schema)) {
|
|
36
|
+
for (const propType of Object.values(schema.propTypes)) {
|
|
37
|
+
assertValidImmediateSchema(undecorate(propType), rootSchema);
|
|
38
|
+
}
|
|
39
|
+
return;
|
|
40
|
+
}
|
|
41
|
+
if (isBool(schema) || isVecBool(schema)) {
|
|
42
|
+
throw new Error(`Invalid schema '${rootSchema.type}' for immediateVar: immediates cannot contain booleans (found '${schema.type}'), use u32 or i32 instead`);
|
|
43
|
+
}
|
|
44
|
+
if (isDecorated(schema)) {
|
|
45
|
+
throw new Error(`Invalid schema for immediateVar: immediates cannot be decorated types`);
|
|
46
|
+
}
|
|
47
|
+
if (!isNumericSchema(schema) && !isVec(schema) && !isMat(schema)) {
|
|
48
|
+
throw new Error(`Invalid schema '${rootSchema.type}' for immediateVar: immediates can only hold scalars, vectors, matrices and structs of those (found '${schema.type}')`);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
/**
|
|
52
|
+
* Validates that the given immediate variable can be used with the given root.
|
|
53
|
+
* @returns The size of the immediate data in bytes, to be passed as
|
|
54
|
+
* `immediateSize` to `device.createPipelineLayout`.
|
|
55
|
+
*/
|
|
56
|
+
export function validateImmediateUsage(immediate, root) {
|
|
57
|
+
if (!root.enabledWgslLanguageFeatures.has('immediate_address_space')) {
|
|
58
|
+
throw new Error(`Immediate variable '${getName(immediate) ?? '<unnamed>'}' cannot be used, because the 'immediate_address_space' WGSL language extension is not supported in this environment. Check support with root.enabledWgslLanguageFeatures and fall back to a uniform buffer (e.g. via tgpu.accessor) when unavailable.`);
|
|
59
|
+
}
|
|
60
|
+
return sizeOf(immediate.dataType);
|
|
61
|
+
}
|
|
62
|
+
const defaultSnapshots = new WeakMap();
|
|
63
|
+
export function createImmediateSnapshot(immediate, value) {
|
|
64
|
+
const bytes = new Uint8Array(sizeOf(immediate.dataType));
|
|
65
|
+
writeToArrayBuffer(bytes.buffer, immediate.dataType, value);
|
|
66
|
+
return bytes;
|
|
67
|
+
}
|
|
68
|
+
/** Captures pass overrides and tracks the last snapshot written to its encoder. */
|
|
69
|
+
export class ImmediatePassState {
|
|
70
|
+
#snapshots = new Map();
|
|
71
|
+
#lastWritten;
|
|
72
|
+
set(immediate, value) {
|
|
73
|
+
this.#snapshots.set(immediate, createImmediateSnapshot(immediate, value));
|
|
74
|
+
}
|
|
75
|
+
invalidate() {
|
|
76
|
+
this.#lastWritten = undefined;
|
|
77
|
+
}
|
|
78
|
+
/** Pass overrides take precedence over pipeline values and the variable's default. */
|
|
79
|
+
write(pass, immediate, pipelineSnapshots, deduplicate) {
|
|
80
|
+
const snapshot = this.#snapshots.get(immediate) ??
|
|
81
|
+
pipelineSnapshots?.get(immediate) ??
|
|
82
|
+
defaultSnapshots.get(immediate);
|
|
83
|
+
if (snapshot === undefined) {
|
|
84
|
+
throw new MissingImmediatesError(getName(immediate));
|
|
85
|
+
}
|
|
86
|
+
if (deduplicate && this.#lastWritten === snapshot) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
pass.setImmediates(0, snapshot.buffer);
|
|
90
|
+
this.#lastWritten = snapshot;
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
class TgpuImmediateVarImpl {
|
|
94
|
+
[$soul];
|
|
95
|
+
static {
|
|
96
|
+
const prototype = TgpuImmediateVarImpl.prototype;
|
|
97
|
+
prototype.resourceType = 'immediate-var';
|
|
98
|
+
makeDereferenceable(makeResolvable(prototype, {
|
|
99
|
+
asString() {
|
|
100
|
+
return `immediateVar:${getName(this) ?? '<unnamed>'}`;
|
|
101
|
+
},
|
|
102
|
+
resolve(ctx) {
|
|
103
|
+
ctx.registerImmediate(this);
|
|
104
|
+
const id = ctx.makeUniqueIdentifier(getName(this), 'global');
|
|
105
|
+
return ctx.gen.declareGlobalVar({
|
|
106
|
+
scope: 'immediate',
|
|
107
|
+
id,
|
|
108
|
+
dataType: this[$soul].dataType,
|
|
109
|
+
init: undefined,
|
|
110
|
+
});
|
|
111
|
+
},
|
|
112
|
+
}), {
|
|
113
|
+
codegenMode: {
|
|
114
|
+
getBaseSnippet(trackingProxy) {
|
|
115
|
+
return snip(trackingProxy, this[$soul].dataType, 'immediate',
|
|
116
|
+
/* possibleSideEffects */ false);
|
|
117
|
+
},
|
|
118
|
+
},
|
|
119
|
+
normalMode: {
|
|
120
|
+
get() {
|
|
121
|
+
throw new IllegalVarAccessError(isInsideTgpuFn()
|
|
122
|
+
? `Cannot access immediate variable '${getName(this) ?? '<unnamed>'}'. TypeGPU functions that depend on GPU resources need to be part of a compute dispatch or draw call`
|
|
123
|
+
: 'Immediate variables are inaccessible during normal JS execution');
|
|
124
|
+
},
|
|
125
|
+
},
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
constructor(dataType, defaultValue) {
|
|
129
|
+
this[$soul] = {
|
|
130
|
+
type: 'immediate-var',
|
|
131
|
+
dataType,
|
|
132
|
+
defaultValue,
|
|
133
|
+
label: undefined,
|
|
134
|
+
};
|
|
135
|
+
if (defaultValue !== undefined) {
|
|
136
|
+
defaultSnapshots.set(this, createImmediateSnapshot(this, defaultValue));
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
get dataType() {
|
|
140
|
+
return this[$soul].dataType;
|
|
141
|
+
}
|
|
142
|
+
get defaultValue() {
|
|
143
|
+
return this[$soul].defaultValue;
|
|
144
|
+
}
|
|
145
|
+
$name(label) {
|
|
146
|
+
setName(this, label);
|
|
147
|
+
return this;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
@@ -3,11 +3,13 @@ import type { TgpuQuerySet } from '../querySet/querySet.ts';
|
|
|
3
3
|
import { type ResolvedSnippet } from '../../data/snippet.ts';
|
|
4
4
|
import type { AnyWgslData } from '../../data/wgslTypes.ts';
|
|
5
5
|
import type { TgpuNamable } from '../../shared/meta.ts';
|
|
6
|
+
import type { InferInput } from '../../shared/repr.ts';
|
|
6
7
|
import type { TgpuDeviceOwningSoul } from '../../shared/soul.ts';
|
|
7
8
|
import { $getNameForward, $internal, $resolve, $soul } from '../../shared/symbols.ts';
|
|
8
9
|
import { type TgpuBindGroup, type TgpuBindGroupLayout, type TgpuLayoutEntry } from '../../tgpuBindGroupLayout.ts';
|
|
9
10
|
import { type TgpuCommandEncoder } from '../commandEncoder/commandEncoder.ts';
|
|
10
11
|
import { type TgpuComputePass } from '../commandEncoder/computePass.ts';
|
|
12
|
+
import { type ImmediateSnapshot, type TgpuImmediateVar } from '../immediate/immediateVar.ts';
|
|
11
13
|
import type { LogResources } from '../../tgsl/consoleLog/types.ts';
|
|
12
14
|
import { type ResolutionCtx, type SelfResolvable } from '../../types.ts';
|
|
13
15
|
import type { IORecord } from '../function/fnTypes.ts';
|
|
@@ -27,6 +29,8 @@ export interface ComputePipelineInternals {
|
|
|
27
29
|
export interface TgpuComputePipelineSoul extends TgpuDeviceOwningSoul<'compute-pipeline', GPUComputePipeline> {
|
|
28
30
|
usedBindGroupLayouts?: TgpuBindGroupLayout[] | undefined;
|
|
29
31
|
bindGroups?: [TgpuBindGroupLayout, TgpuBindGroup | GPUBindGroup][] | undefined;
|
|
32
|
+
usedImmediate?: TgpuImmediateVar | undefined;
|
|
33
|
+
immediates?: [TgpuImmediateVar, ImmediateSnapshot][] | undefined;
|
|
30
34
|
timestampWrites?: TimestampWritesPriors['timestampWrites'];
|
|
31
35
|
performanceCallback?: TimestampWritesPriors['performanceCallback'];
|
|
32
36
|
nonTransferablePriors?: string[] | undefined;
|
|
@@ -42,6 +46,16 @@ export interface TgpuComputePipeline extends TgpuNamable, SelfResolvable, Timeab
|
|
|
42
46
|
with<Entries extends Record<string, TgpuLayoutEntry | null>>(bindGroupLayout: TgpuBindGroupLayout<Entries>, bindGroup: TgpuBindGroup<Entries>): this;
|
|
43
47
|
with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
|
|
44
48
|
with(bindGroup: TgpuBindGroup): this;
|
|
49
|
+
/**
|
|
50
|
+
* Provides a value for the given immediate variable, applied on dispatch
|
|
51
|
+
* like the rest of the pipeline-held state and overridden by
|
|
52
|
+
* `pass.setImmediates`. The value is captured (copied) at call time;
|
|
53
|
+
* mutating it afterwards has no effect.
|
|
54
|
+
*
|
|
55
|
+
* Passing an `ArrayBuffer` or typed array skips serialization entirely; the bytes
|
|
56
|
+
* are copied verbatim and the caller guarantees they match the schema's layout.
|
|
57
|
+
*/
|
|
58
|
+
with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
|
|
45
59
|
/**
|
|
46
60
|
* Directs subsequent dispatches into the given compute pass, letting multiple
|
|
47
61
|
* pipelines share one pass (and one submission).
|
|
@@ -54,6 +68,11 @@ export interface TgpuComputePipeline extends TgpuNamable, SelfResolvable, Timeab
|
|
|
54
68
|
with(encoder: TgpuCommandEncoder): this;
|
|
55
69
|
with(encoder: GPUCommandEncoder): this;
|
|
56
70
|
with(pass: GPUComputePassEncoder): this;
|
|
71
|
+
/**
|
|
72
|
+
* Applies a transform to this pipeline, letting packages hand out reusable
|
|
73
|
+
* configuration steps, e.g. `pipeline.pipe(cache.inject())`.
|
|
74
|
+
*/
|
|
75
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
57
76
|
dispatchWorkgroups(x: number, y?: number, z?: number): void;
|
|
58
77
|
/**
|
|
59
78
|
* Immediately resolves the pipeline, then awaits `device.createComputePipelineAsync()`.
|
|
@@ -88,12 +107,14 @@ type TgpuComputePipelinePriors = {
|
|
|
88
107
|
readonly pass?: TgpuComputePass | undefined;
|
|
89
108
|
/** An encoder the pipeline records its own passes into, but does not submit */
|
|
90
109
|
readonly encoder?: TgpuCommandEncoder | undefined;
|
|
110
|
+
readonly immediatesMap?: Map<TgpuImmediateVar, ImmediateSnapshot> | undefined;
|
|
91
111
|
} & TimestampWritesPriors;
|
|
92
112
|
type Memo = {
|
|
93
113
|
pipeline: GPUComputePipeline;
|
|
94
114
|
usedBindGroupLayouts: TgpuBindGroupLayout[];
|
|
95
115
|
catchall: [number, TgpuBindGroup] | undefined;
|
|
96
116
|
logResources: LogResources | undefined;
|
|
117
|
+
usedImmediate: TgpuImmediateVar | undefined;
|
|
97
118
|
};
|
|
98
119
|
declare class TgpuComputePipelineImpl implements TgpuComputePipeline {
|
|
99
120
|
#private;
|
|
@@ -107,10 +128,12 @@ declare class TgpuComputePipelineImpl implements TgpuComputePipeline {
|
|
|
107
128
|
with<Entries extends Record<string, TgpuLayoutEntry | null>>(bindGroupLayout: TgpuBindGroupLayout<Entries>, bindGroup: TgpuBindGroup<Entries>): this;
|
|
108
129
|
with(bindGroupLayout: TgpuBindGroupLayout, bindGroup: GPUBindGroup): this;
|
|
109
130
|
with(bindGroup: TgpuBindGroup): this;
|
|
131
|
+
with<T extends AnyWgslData>(immediate: TgpuImmediateVar<T>, value: InferInput<T> | ArrayBuffer | ArrayBufferView): this;
|
|
110
132
|
with(pass: TgpuComputePass): this;
|
|
111
133
|
with(encoder: TgpuCommandEncoder): this;
|
|
112
134
|
with(encoder: GPUCommandEncoder): this;
|
|
113
135
|
with(pass: GPUComputePassEncoder): this;
|
|
136
|
+
pipe<T>(transform: (pipeline: this) => T): T;
|
|
114
137
|
withPerformanceCallback(callback: (start: bigint, end: bigint) => void | Promise<void>): this;
|
|
115
138
|
withTimestampWrites(options: {
|
|
116
139
|
querySet: TgpuQuerySet<'timestamp'> | GPUQuerySet;
|