@driftengine/core 4.9.0 → 4.11.0
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/CHANGELOG.json +133 -11
- package/README.md +43 -38
- package/dist/index.d.ts +7 -4
- package/dist/index.js +5 -4
- package/dist/render/ambientHarmonics.d.ts +1 -1
- package/dist/render/ambientHarmonics.js +1 -1
- package/dist/render/areaLights.d.ts +4 -4
- package/dist/render/areaLights.js +1 -1
- package/dist/render/backend/api.d.ts +10 -3
- package/dist/render/backend/select.js +11 -5
- package/dist/render/backend/webgl2/boneAnimations.d.ts +29 -0
- package/dist/render/backend/webgl2/boneAnimations.js +80 -0
- package/dist/render/backend/webgl2/instanced.d.ts +5 -1
- package/dist/render/backend/webgl2/instanced.js +13 -1
- package/dist/render/backend/webgl2/renderer.d.ts +189 -4
- package/dist/render/backend/webgl2/renderer.js +614 -66
- package/dist/render/backend/webgl2/skinPaletteTexture.d.ts +5 -3
- package/dist/render/backend/webgl2/skinPaletteTexture.js +6 -4
- package/dist/render/backend/webgpu/boneAnimations.d.ts +34 -0
- package/dist/render/backend/webgpu/boneAnimations.js +76 -0
- package/dist/render/backend/webgpu/buffers.d.ts +24 -4
- package/dist/render/backend/webgpu/buffers.js +116 -59
- package/dist/render/backend/webgpu/clothKernels.d.ts +8 -0
- package/dist/render/backend/webgpu/clothKernels.js +76 -0
- package/dist/render/backend/webgpu/clothOps.d.ts +39 -0
- package/dist/render/backend/webgpu/clothOps.js +78 -0
- package/dist/render/backend/webgpu/clothSetLayout.d.ts +25 -0
- package/dist/render/backend/webgpu/clothSetLayout.js +84 -0
- package/dist/render/backend/webgpu/clothSetPack.d.ts +33 -0
- package/dist/render/backend/webgpu/clothSetPack.js +144 -0
- package/dist/render/backend/webgpu/compressedUpload.d.ts +0 -1
- package/dist/render/backend/webgpu/compressedUpload.js +53 -8
- package/dist/render/backend/webgpu/depthPass.d.ts +59 -3
- package/dist/render/backend/webgpu/depthPass.js +49 -14
- package/dist/render/backend/webgpu/drawCommand.d.ts +20 -4
- package/dist/render/backend/webgpu/drawCommand.js +11 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +137 -20
- package/dist/render/backend/webgpu/gpuClothSet.d.ts +80 -0
- package/dist/render/backend/webgpu/gpuClothSet.js +462 -0
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/latePass.js +1 -0
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/motionPass.js +15 -6
- package/dist/render/backend/webgpu/pipelineCache.d.ts +28 -1
- package/dist/render/backend/webgpu/pipelineCache.js +46 -1
- package/dist/render/backend/webgpu/postPass.d.ts +1 -1
- package/dist/render/backend/webgpu/renderer.d.ts +200 -35
- package/dist/render/backend/webgpu/renderer.js +993 -168
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.d.ts +9 -2
- package/dist/render/backend/webgpu/shaders/clothSolve.wgsl.js +121 -47
- package/dist/render/backend/webgpu/skinPaletteRing.d.ts +7 -3
- package/dist/render/backend/webgpu/skinPaletteRing.js +13 -7
- package/dist/render/backend/webgpu/skinPaletteTexture.d.ts +6 -0
- package/dist/render/backend/webgpu/skinPaletteTexture.js +7 -1
- package/dist/render/backend/webgpu/staticBundles.d.ts +125 -0
- package/dist/render/backend/webgpu/staticBundles.js +242 -0
- package/dist/render/backend/webgpu/surfaceTexturePass.d.ts +10 -3
- package/dist/render/backend/webgpu/surfaceTexturePass.js +20 -2
- package/dist/render/backend/webgpu/uniformRing.d.ts +21 -0
- package/dist/render/backend/webgpu/uniformRing.js +9 -1
- package/dist/render/backend/webgpu/vertexFormats.d.ts +22 -0
- package/dist/render/backend/webgpu/vertexFormats.js +97 -0
- package/dist/render/bloomChain.d.ts +3 -3
- package/dist/render/boneAnimation.d.ts +92 -0
- package/dist/render/boneAnimation.js +133 -0
- package/dist/render/compressedSource.d.ts +33 -13
- package/dist/render/compressedSource.js +70 -25
- package/dist/render/glCompressed.js +83 -0
- package/dist/render/globalMediumPass.d.ts +2 -0
- package/dist/render/globalMediumPass.js +1 -0
- package/dist/render/indexWidth.d.ts +17 -0
- package/dist/render/indexWidth.js +23 -0
- package/dist/render/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightMatrix.d.ts +11 -0
- package/dist/render/lightMatrix.js +14 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +14 -0
- package/dist/render/mesh.js +44 -14
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/renderQuality.d.ts +30 -0
- package/dist/render/renderQuality.js +5 -0
- package/dist/render/rendererHarness.d.ts +7 -0
- package/dist/render/rendererHarness.js +12 -0
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- package/dist/render/shader.d.ts +15 -0
- package/dist/render/shader.js +41 -0
- package/dist/render/shaders/boneAnimation.d.ts +19 -0
- package/dist/render/shaders/boneAnimation.js +63 -0
- package/dist/render/shaders/depth.d.ts +7 -0
- package/dist/render/shaders/depth.js +35 -10
- package/dist/render/shaders/flat/directionalShadow.js +84 -10
- package/dist/render/shaders/flat/glassTint.d.ts +1 -1
- package/dist/render/shaders/flat/glassTint.js +3 -0
- package/dist/render/shaders/flat/index.d.ts +21 -0
- package/dist/render/shaders/flat/index.js +42 -7
- package/dist/render/shaders/flat/layered.d.ts +19 -0
- package/dist/render/shaders/flat/layered.js +115 -0
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +58 -21
- package/dist/render/shaders/flat/mapReads.d.ts +10 -0
- package/dist/render/shaders/flat/mapReads.js +38 -0
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/movingSun.d.ts +21 -0
- package/dist/render/shaders/flat/movingSun.js +46 -0
- package/dist/render/shaders/flat/normalMap.d.ts +6 -2
- package/dist/render/shaders/flat/normalMap.js +14 -3
- package/dist/render/shaders/flat/overlay.d.ts +3 -2
- package/dist/render/shaders/flat/overlay.js +42 -20
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +59 -27
- package/dist/render/shaders/flat/worldUv.d.ts +22 -0
- package/dist/render/shaders/flat/worldUv.js +113 -0
- package/dist/render/shaders/generated/depth.wgsl.d.ts +93 -0
- package/dist/render/shaders/generated/depth.wgsl.js +93 -0
- package/dist/render/shaders/generated/flat.wgsl.d.ts +3616 -3042
- package/dist/render/shaders/generated/flat.wgsl.js +3688 -3113
- package/dist/render/shaders/generated/globalMedium.wgsl.d.ts +8 -3
- package/dist/render/shaders/generated/globalMedium.wgsl.js +8 -3
- package/dist/render/shaders/generated/lightVolume.wgsl.d.ts +15 -10
- package/dist/render/shaders/generated/lightVolume.wgsl.js +16 -11
- package/dist/render/shaders/globalMedium.js +9 -1
- package/dist/render/shaders/lightVolume.js +9 -1
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/tangentFrame.d.ts +1 -1
- package/dist/render/shaders/tangentFrame.js +16 -3
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/skinnedClothRun.d.ts +28 -0
- package/dist/render/skinnedClothRun.js +103 -35
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceLayers.d.ts +39 -0
- package/dist/render/surfaceLayers.js +26 -0
- package/dist/render/surfaceOverlay.d.ts +16 -3
- package/dist/render/surfaceOverlay.js +11 -6
- package/dist/render/surfaceProjection.d.ts +34 -0
- package/dist/render/surfaceProjection.js +30 -0
- package/dist/render/surfaceTexture.d.ts +68 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/dist/render/vertexPacking.d.ts +48 -0
- package/dist/render/vertexPacking.js +68 -0
- package/package.json +6 -6
- package/src/index.ts +12 -4
- package/src/render/ambientHarmonics.ts +1 -1
- package/src/render/areaLights.ts +4 -4
- package/src/render/backend/api.ts +13 -3
- package/src/render/backend/select.ts +10 -5
- package/src/render/backend/webgl2/boneAnimations.ts +105 -0
- package/src/render/backend/webgl2/instanced.ts +20 -1
- package/src/render/backend/webgl2/renderer.ts +723 -79
- package/src/render/backend/webgl2/skinPaletteTexture.ts +6 -4
- package/src/render/backend/webgpu/boneAnimations.ts +97 -0
- package/src/render/backend/webgpu/buffers.ts +195 -70
- package/src/render/backend/webgpu/clothKernels.ts +86 -0
- package/src/render/backend/webgpu/clothOps.ts +86 -0
- package/src/render/backend/webgpu/clothSetLayout.ts +102 -0
- package/src/render/backend/webgpu/clothSetPack.ts +197 -0
- package/src/render/backend/webgpu/compressedUpload.ts +59 -9
- package/src/render/backend/webgpu/depthPass.ts +51 -10
- package/src/render/backend/webgpu/drawCommand.ts +25 -4
- package/src/render/backend/webgpu/flatPass.ts +212 -41
- package/src/render/backend/webgpu/gpuClothSet.ts +564 -0
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/latePass.ts +1 -0
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/motionPass.ts +18 -7
- package/src/render/backend/webgpu/pipelineCache.ts +53 -7
- package/src/render/backend/webgpu/renderer.ts +1201 -202
- package/src/render/backend/webgpu/shaders/clothSolve.wgsl.ts +121 -47
- package/src/render/backend/webgpu/skinPaletteRing.ts +13 -7
- package/src/render/backend/webgpu/skinPaletteTexture.ts +7 -1
- package/src/render/backend/webgpu/staticBundles.ts +364 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- package/src/render/backend/webgpu/vertexFormats.ts +113 -0
- package/src/render/bloomChain.ts +3 -3
- package/src/render/boneAnimation.ts +223 -0
- package/src/render/compressedSource.ts +121 -29
- package/src/render/glCompressed.ts +83 -0
- package/src/render/globalMediumPass.ts +7 -0
- package/src/render/indexWidth.ts +27 -0
- package/src/render/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightMatrix.ts +15 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +72 -14
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/renderQuality.ts +38 -0
- package/src/render/rendererHarness.ts +15 -0
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shader.ts +59 -0
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/directionalShadow.ts +84 -10
- package/src/render/shaders/flat/glassTint.ts +7 -1
- package/src/render/shaders/flat/index.ts +63 -7
- package/src/render/shaders/flat/layered.ts +115 -0
- package/src/render/shaders/flat/main.ts +58 -21
- package/src/render/shaders/flat/mapReads.ts +38 -0
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/movingSun.ts +46 -0
- package/src/render/shaders/flat/normalMap.ts +14 -3
- package/src/render/shaders/flat/overlay.ts +42 -20
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +59 -27
- package/src/render/shaders/flat/worldUv.ts +113 -0
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3688 -3113
- package/src/render/shaders/generated/globalMedium.wgsl.ts +8 -3
- package/src/render/shaders/generated/lightVolume.wgsl.ts +16 -11
- package/src/render/shaders/globalMedium.ts +9 -1
- package/src/render/shaders/lightVolume.ts +9 -1
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/tangentFrame.ts +16 -3
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/skinnedClothRun.ts +138 -38
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceLayers.ts +57 -0
- package/src/render/surfaceOverlay.ts +24 -7
- package/src/render/surfaceProjection.ts +57 -0
- package/src/render/surfaceTexture.ts +124 -27
- package/src/render/vertexPacking.ts +109 -0
- package/dist/render/backend/webgpu/gpuSkinnedCloth.d.ts +0 -74
- package/dist/render/backend/webgpu/gpuSkinnedCloth.js +0 -452
- package/src/render/backend/webgpu/gpuSkinnedCloth.ts +0 -502
|
@@ -19,9 +19,11 @@ export declare class SkinPaletteTexture {
|
|
|
19
19
|
/**
|
|
20
20
|
* Upload a palette, reallocating only when the joint count changes.
|
|
21
21
|
*
|
|
22
|
-
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
22
|
+
* **The texture grows and never shrinks**, so the steady state is one `texSubImage2D` whatever
|
|
23
|
+
* mix of rigs a frame draws. It was rebuilt whenever the joint count changed, which with one
|
|
24
|
+
* texture for every skinned draw meant a character and a prop drawn in turn reallocated it at
|
|
25
|
+
* every draw: a GPU allocation in the frame loop. The shader reads joints by index, so a texture
|
|
26
|
+
* wider than the rig is read exactly as one that fits.
|
|
25
27
|
*/
|
|
26
28
|
update(gl: WebGL2RenderingContext, palette: Float32Array): void;
|
|
27
29
|
/** Bind to a **vertex-stage** unit. See `SKIN_PALETTE_TEXTURE_UNIT` for why that is free. */
|
|
@@ -20,15 +20,17 @@ export class SkinPaletteTexture {
|
|
|
20
20
|
/**
|
|
21
21
|
* Upload a palette, reallocating only when the joint count changes.
|
|
22
22
|
*
|
|
23
|
-
*
|
|
24
|
-
*
|
|
25
|
-
*
|
|
23
|
+
* **The texture grows and never shrinks**, so the steady state is one `texSubImage2D` whatever
|
|
24
|
+
* mix of rigs a frame draws. It was rebuilt whenever the joint count changed, which with one
|
|
25
|
+
* texture for every skinned draw meant a character and a prop drawn in turn reallocated it at
|
|
26
|
+
* every draw: a GPU allocation in the frame loop. The shader reads joints by index, so a texture
|
|
27
|
+
* wider than the rig is read exactly as one that fits.
|
|
26
28
|
*/
|
|
27
29
|
update(gl, palette) {
|
|
28
30
|
validateSkinPalette(palette);
|
|
29
31
|
const joints = palette.length / 16;
|
|
30
32
|
const width = paletteTextureWidth(joints);
|
|
31
|
-
if (this.texture === null ||
|
|
33
|
+
if (this.texture === null || joints > this.joints) {
|
|
32
34
|
if (this.texture !== null)
|
|
33
35
|
gl.deleteTexture(this.texture);
|
|
34
36
|
const created = gl.createTexture();
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bone animation's textures on this backend, and a batch's clocks: what
|
|
3
|
+
* `shaders/boneAnimation.ts` reads by index, made once and never filtered.
|
|
4
|
+
*
|
|
5
|
+
* **`rgba32float` places and turns, and `rg32float` clocks**, all read with `textureLoad` and bound
|
|
6
|
+
* `unfilterable-float`, as the joint palette is: a turn in half floats moves a crowd's hands by a
|
|
7
|
+
* thousandth of their reach, a jitter at the frame's rate, and the bytes are a clip's, once.
|
|
8
|
+
*/
|
|
9
|
+
import { type BoneAnimationHandle, type BoneAnimationTexels } from '../../boneAnimation.ts';
|
|
10
|
+
/** A clip on the device, from `createBoneAnimation`. */
|
|
11
|
+
export declare class GpuBoneAnimation implements BoneAnimationHandle {
|
|
12
|
+
readonly bones: number;
|
|
13
|
+
readonly frames: number;
|
|
14
|
+
readonly framesPerSecond: number;
|
|
15
|
+
readonly boneScale: number;
|
|
16
|
+
readonly placesView: GPUTextureView;
|
|
17
|
+
readonly turnsView: GPUTextureView;
|
|
18
|
+
disposed: boolean;
|
|
19
|
+
private readonly places;
|
|
20
|
+
private readonly turns;
|
|
21
|
+
constructor(device: GPUDevice, texels: BoneAnimationTexels);
|
|
22
|
+
dispose(): void;
|
|
23
|
+
}
|
|
24
|
+
/** A batch's clocks: two floats an instance, wrapped onto rows `CLOCK_TEXTURE_WIDTH` wide. */
|
|
25
|
+
export declare class GpuInstanceClocks {
|
|
26
|
+
readonly view: GPUTextureView;
|
|
27
|
+
readonly staging: Float32Array;
|
|
28
|
+
private readonly texture;
|
|
29
|
+
private readonly width;
|
|
30
|
+
constructor(device: GPUDevice, capacity: number, label: string);
|
|
31
|
+
/** The rows that hold the first `count` instances, from `staging`. */
|
|
32
|
+
upload(queue: GPUQueue, count: number): void;
|
|
33
|
+
dispose(): void;
|
|
34
|
+
}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A bone animation's textures on this backend, and a batch's clocks: what
|
|
3
|
+
* `shaders/boneAnimation.ts` reads by index, made once and never filtered.
|
|
4
|
+
*
|
|
5
|
+
* **`rgba32float` places and turns, and `rg32float` clocks**, all read with `textureLoad` and bound
|
|
6
|
+
* `unfilterable-float`, as the joint palette is: a turn in half floats moves a crowd's hands by a
|
|
7
|
+
* thousandth of their reach, a jitter at the frame's rate, and the bytes are a clip's, once.
|
|
8
|
+
*/
|
|
9
|
+
import { CLOCK_TEXTURE_WIDTH, } from '../../boneAnimation.js';
|
|
10
|
+
const USAGE_TEXTURE = 0x4 | 0x2; // TEXTURE_BINDING | COPY_DST
|
|
11
|
+
/** A clip on the device, from `createBoneAnimation`. */
|
|
12
|
+
export class GpuBoneAnimation {
|
|
13
|
+
bones;
|
|
14
|
+
frames;
|
|
15
|
+
framesPerSecond;
|
|
16
|
+
boneScale;
|
|
17
|
+
placesView;
|
|
18
|
+
turnsView;
|
|
19
|
+
disposed = false;
|
|
20
|
+
places;
|
|
21
|
+
turns;
|
|
22
|
+
constructor(device, texels) {
|
|
23
|
+
this.bones = texels.bones;
|
|
24
|
+
this.frames = texels.frames;
|
|
25
|
+
this.framesPerSecond = texels.framesPerSecond;
|
|
26
|
+
this.boneScale = texels.boneScale;
|
|
27
|
+
const make = (label, data) => {
|
|
28
|
+
const texture = device.createTexture({
|
|
29
|
+
label,
|
|
30
|
+
size: [texels.bones, texels.frames],
|
|
31
|
+
format: 'rgba32float',
|
|
32
|
+
usage: USAGE_TEXTURE,
|
|
33
|
+
});
|
|
34
|
+
device.queue.writeTexture({ texture }, data, { bytesPerRow: texels.bones * 16, rowsPerImage: texels.frames }, [texels.bones, texels.frames]);
|
|
35
|
+
return texture;
|
|
36
|
+
};
|
|
37
|
+
this.places = make('bones.places', texels.places);
|
|
38
|
+
this.turns = make('bones.turns', texels.turns);
|
|
39
|
+
this.placesView = this.places.createView();
|
|
40
|
+
this.turnsView = this.turns.createView();
|
|
41
|
+
}
|
|
42
|
+
dispose() {
|
|
43
|
+
this.disposed = true;
|
|
44
|
+
this.places.destroy();
|
|
45
|
+
this.turns.destroy();
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/** A batch's clocks: two floats an instance, wrapped onto rows `CLOCK_TEXTURE_WIDTH` wide. */
|
|
49
|
+
export class GpuInstanceClocks {
|
|
50
|
+
view;
|
|
51
|
+
staging;
|
|
52
|
+
texture;
|
|
53
|
+
width;
|
|
54
|
+
constructor(device, capacity, label) {
|
|
55
|
+
this.width = Math.max(1, Math.min(capacity, CLOCK_TEXTURE_WIDTH));
|
|
56
|
+
const rows = Math.max(1, Math.ceil(capacity / this.width));
|
|
57
|
+
this.staging = new Float32Array(this.width * rows * 2);
|
|
58
|
+
this.texture = device.createTexture({
|
|
59
|
+
label,
|
|
60
|
+
size: [this.width, rows],
|
|
61
|
+
format: 'rg32float',
|
|
62
|
+
usage: USAGE_TEXTURE,
|
|
63
|
+
});
|
|
64
|
+
this.view = this.texture.createView();
|
|
65
|
+
}
|
|
66
|
+
/** The rows that hold the first `count` instances, from `staging`. */
|
|
67
|
+
upload(queue, count) {
|
|
68
|
+
if (count <= 0)
|
|
69
|
+
return;
|
|
70
|
+
const rows = Math.ceil(count / this.width);
|
|
71
|
+
queue.writeTexture({ texture: this.texture }, this.staging, { bytesPerRow: this.width * 8, rowsPerImage: rows }, [this.width, rows]);
|
|
72
|
+
}
|
|
73
|
+
dispose() {
|
|
74
|
+
this.texture.destroy();
|
|
75
|
+
}
|
|
76
|
+
}
|
|
@@ -75,8 +75,13 @@ export interface GpuMesh {
|
|
|
75
75
|
/** The second four influences' offsets, or -1 for a mesh with four. */
|
|
76
76
|
readonly joints2: number;
|
|
77
77
|
readonly weights2: number;
|
|
78
|
+
/** How the weights are read: floats, or sixteen-bit fixed point (`vertexPacking.ts`). */
|
|
79
|
+
readonly weightsFormat: GPUVertexFormat;
|
|
80
|
+
readonly weights2Format: GPUVertexFormat;
|
|
78
81
|
} | null;
|
|
79
82
|
readonly indexBuffer: GPUBuffer;
|
|
83
|
+
/** Sixteen or thirty-two bits an index: every pass that binds `indexBuffer` says which. */
|
|
84
|
+
readonly indexFormat: GPUIndexFormat;
|
|
80
85
|
/**
|
|
81
86
|
* How many indices the mesh draws. **Zero is a mesh with nothing to draw, and no pass draws it**:
|
|
82
87
|
* a level can hold none of some kind of geometry, and a draw of zero indices does nothing but
|
|
@@ -156,18 +161,33 @@ export interface GpuMesh {
|
|
|
156
161
|
* changed, reported at 2.8 MB a frame for one character. What the split costs is a pipeline per
|
|
157
162
|
* layout, keyed by `:dynamic`, and two more buffers bound per draw of such a mesh.
|
|
158
163
|
*/
|
|
159
|
-
readonly update: ((device: GPUDevice, positions: Float32Array, normals?: Float32Array,
|
|
164
|
+
readonly update: ((device: GPUDevice, positions: Float32Array, normals?: Float32Array, staged?: StagedRewrite | null) => void) | null;
|
|
160
165
|
/**
|
|
161
|
-
* A dynamic mesh's motion state, which the renderer owns: a buffer for last frame's positions
|
|
162
|
-
*
|
|
163
|
-
*
|
|
166
|
+
* A dynamic mesh's motion state, which the renderer owns: a buffer for last frame's positions and
|
|
167
|
+
* one the new positions are staged in, each made the first time a reconstructing renderer
|
|
168
|
+
* rewrites the mesh, and the frame of the last rewrite. Null on a mesh created without
|
|
169
|
+
* `{ dynamic: true }`, which never moves a vertex.
|
|
164
170
|
*/
|
|
165
171
|
readonly motion: {
|
|
166
172
|
previous: GPUBuffer | null;
|
|
173
|
+
staging: GPUBuffer | null;
|
|
167
174
|
changed: number;
|
|
168
175
|
} | null;
|
|
169
176
|
dispose(): void;
|
|
170
177
|
}
|
|
178
|
+
/**
|
|
179
|
+
* A rewrite a reconstructing renderer stages rather than writes, so last frame's positions can be
|
|
180
|
+
* kept with no submit of the rewrite's own: the new positions are written into `staging`, and two
|
|
181
|
+
* copies are recorded into `encoder` — the live positions into `previous` first, where the frame's
|
|
182
|
+
* first rewrite keeps them, then the staged ones in. The renderer submits that encoder at the head
|
|
183
|
+
* of the frame's next submit, which is what keeps both copies ahead of every draw that reads the
|
|
184
|
+
* mesh. A `writeBuffer` straight to the live positions would land before the copy aside could run.
|
|
185
|
+
*/
|
|
186
|
+
export interface StagedRewrite {
|
|
187
|
+
encoder: GPUCommandEncoder;
|
|
188
|
+
staging: GPUBuffer;
|
|
189
|
+
previous: GPUBuffer | null;
|
|
190
|
+
}
|
|
171
191
|
/** A mesh on its way to the device, and the iterator that gets it there. */
|
|
172
192
|
export interface IncrementalGpuMesh {
|
|
173
193
|
/**
|
|
@@ -4,6 +4,9 @@ import { validateMeshData } from '../../mesh.js';
|
|
|
4
4
|
import { ABSENT_ATTRIBUTE } from '../../vertexDefaults.js';
|
|
5
5
|
import { UPLOAD_BYTES_PER_STEP } from '../../uploadStep.js';
|
|
6
6
|
import { INSTANCE_STRIDE } from '../../instances.js';
|
|
7
|
+
import { compactIndices } from '../../indexWidth.js';
|
|
8
|
+
import { SNORM16_ONE, UNORM16_ONE, packingFor, sharedValue, vertexPackingOf, } from '../../vertexPacking.js';
|
|
9
|
+
import { formatBytes, interleaveFixed, packedFormat, packingIn } from './vertexFormats.js';
|
|
7
10
|
/**
|
|
8
11
|
* Mesh geometry as WebGPU buffers.
|
|
9
12
|
*
|
|
@@ -160,13 +163,33 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
160
163
|
const bounds = boundsOfPositions(data.positions, createBounds());
|
|
161
164
|
const vertexCount = data.positions.length / 3;
|
|
162
165
|
const layered = data.layers !== undefined;
|
|
163
|
-
|
|
164
|
-
|
|
166
|
+
/* How each attribute travels, which the pipeline key says too: `vertexPacking.ts`. */
|
|
167
|
+
const packing = vertexPackingOf(data, dynamic);
|
|
165
168
|
/* Interleaved: one stride holding every attribute the mesh actually supplied — less a dynamic
|
|
166
|
-
mesh's positions and normals, which are buffers of their own
|
|
167
|
-
|
|
168
|
-
const
|
|
169
|
-
|
|
169
|
+
mesh's positions and normals, which are buffers of their own, and any attribute every vertex
|
|
170
|
+
shares, which is a constant. */
|
|
171
|
+
const fields = [];
|
|
172
|
+
let stride = 0;
|
|
173
|
+
for (const declared of VERTEX_LAYOUT) {
|
|
174
|
+
if (declared.optional && data[declared.name] === undefined)
|
|
175
|
+
continue;
|
|
176
|
+
if (dynamic && isStreamed(declared))
|
|
177
|
+
continue;
|
|
178
|
+
const attribute = laidOut(declared, layered);
|
|
179
|
+
const how = packingFor(packing, attribute.name);
|
|
180
|
+
if (how === 'constant')
|
|
181
|
+
continue;
|
|
182
|
+
const format = packedFormat(attribute.format, how);
|
|
183
|
+
fields.push({ attribute, packing: how, format, offset: stride });
|
|
184
|
+
stride += formatBytes(format);
|
|
185
|
+
}
|
|
186
|
+
const buffer = new ArrayBuffer(stride * vertexCount);
|
|
187
|
+
const rows = {
|
|
188
|
+
floats: new Float32Array(buffer),
|
|
189
|
+
signed: new Int16Array(buffer),
|
|
190
|
+
unsigned: new Uint16Array(buffer),
|
|
191
|
+
bytes: new Uint8Array(buffer),
|
|
192
|
+
};
|
|
170
193
|
/*
|
|
171
194
|
* **Refused here, in one sentence, rather than by the driver a hundred times.**
|
|
172
195
|
*
|
|
@@ -181,7 +204,7 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
181
204
|
* whose adapter caps at the default, and it deserves a sentence naming the model's size and
|
|
182
205
|
* the device's rather than a wall of validation.
|
|
183
206
|
*/
|
|
184
|
-
const byteLength = align4(
|
|
207
|
+
const byteLength = align4(buffer.byteLength);
|
|
185
208
|
const ceiling = device.limits.maxBufferSize;
|
|
186
209
|
if (typeof ceiling === 'number' && byteLength > ceiling) {
|
|
187
210
|
throw new Error(`this mesh needs a ${Math.round(byteLength / 1024 / 1024)} MB vertex buffer and this GPU ` +
|
|
@@ -216,10 +239,20 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
216
239
|
continue;
|
|
217
240
|
absent.set(value, slot * (CONSTANT_SLOT / 4));
|
|
218
241
|
}
|
|
242
|
+
/* And what every vertex shares, where a mesh's attribute is one value: `vertexPacking.ts`. */
|
|
243
|
+
for (let slot = 0; slot < VERTEX_LAYOUT.length; slot++) {
|
|
244
|
+
const attribute = VERTEX_LAYOUT[slot];
|
|
245
|
+
if (packingFor(packing, attribute.name) !== 'constant')
|
|
246
|
+
continue;
|
|
247
|
+
const values = data[attribute.name];
|
|
248
|
+
absent.set(sharedValue(values, attribute.components), slot * (CONSTANT_SLOT / 4));
|
|
249
|
+
}
|
|
219
250
|
device.queue.writeBuffer(constants, 0, absent);
|
|
251
|
+
/* Sixteen bits where every vertex fits, which every draw of this mesh says: indexWidth.ts. */
|
|
252
|
+
const indices = compactIndices(data.indices, vertexCount);
|
|
220
253
|
const indexBuffer = device.createBuffer({
|
|
221
254
|
label: 'mesh.indices',
|
|
222
|
-
size: align4(
|
|
255
|
+
size: align4(indices.byteLength),
|
|
223
256
|
usage: USAGE.INDEX | USAGE.COPY_DST,
|
|
224
257
|
});
|
|
225
258
|
/* A dynamic mesh's two moving streams: copied from as well as written, for last frame's positions. */
|
|
@@ -243,10 +276,11 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
243
276
|
* buffer never holds a torn one even for the frame between two steps.
|
|
244
277
|
*/
|
|
245
278
|
const verticesPerStep = Math.max(1, Math.floor(UPLOAD_BYTES_PER_STEP / (stride + (dynamic ? 24 : 0))));
|
|
246
|
-
|
|
247
|
-
const
|
|
279
|
+
/* Whole four-byte words a step, which is all a queue writes: two sixteen-bit indices to one. */
|
|
280
|
+
const indexWidth = indices.BYTES_PER_ELEMENT;
|
|
281
|
+
const indicesPerStep = Math.max(1, Math.floor(UPLOAD_BYTES_PER_STEP / 4)) * (4 / indexWidth);
|
|
248
282
|
const totalSteps = Math.max(1, Math.ceil(vertexCount / verticesPerStep)) +
|
|
249
|
-
Math.max(1, Math.ceil(
|
|
283
|
+
Math.max(1, Math.ceil(indices.length / indicesPerStep));
|
|
250
284
|
/*
|
|
251
285
|
* **The mesh is assembled out here, by functions of its own, and that is the fix for a copy
|
|
252
286
|
* nobody named.** It was an object literal in this scope, and its `complete` getter shared this
|
|
@@ -262,6 +296,7 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
262
296
|
constants,
|
|
263
297
|
streams,
|
|
264
298
|
indexBuffer,
|
|
299
|
+
indexFormat: indices instanceof Uint16Array ? 'uint16' : 'uint32',
|
|
265
300
|
stride: streams === null ? stride : 12,
|
|
266
301
|
indexCount: data.indices.length,
|
|
267
302
|
bounds,
|
|
@@ -271,7 +306,7 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
271
306
|
isSkinnedEight: data.joints !== undefined && data.joints2 !== undefined,
|
|
272
307
|
vertexCount,
|
|
273
308
|
hasUvs: data.uvs !== undefined,
|
|
274
|
-
skinOffsets: streams === null ? skinOffsetsOf(
|
|
309
|
+
skinOffsets: streams === null ? skinOffsetsOf(fields) : null,
|
|
275
310
|
hasChannel: data.channel !== undefined,
|
|
276
311
|
morph: data.morphTargets === undefined || data.morphTargetCount === undefined
|
|
277
312
|
? null
|
|
@@ -282,14 +317,14 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
282
317
|
});
|
|
283
318
|
const job = {
|
|
284
319
|
data,
|
|
285
|
-
|
|
320
|
+
indices,
|
|
321
|
+
rows,
|
|
286
322
|
device,
|
|
287
|
-
|
|
323
|
+
fields,
|
|
288
324
|
streams,
|
|
289
325
|
vertices,
|
|
290
326
|
indexBuffer,
|
|
291
327
|
vertexCount,
|
|
292
|
-
step,
|
|
293
328
|
stride,
|
|
294
329
|
verticesPerStep,
|
|
295
330
|
indicesPerStep,
|
|
@@ -299,26 +334,35 @@ export function createGpuMeshIncremental(device, data, dynamic = false) {
|
|
|
299
334
|
return { mesh, upload: uploadSteps(job) };
|
|
300
335
|
}
|
|
301
336
|
function* uploadSteps(job) {
|
|
302
|
-
const { device,
|
|
337
|
+
const { device, fields, vertices, indexBuffer, vertexCount, stride } = job;
|
|
338
|
+
/* Floats and halves a vertex: every field is whole words, so both divide. */
|
|
339
|
+
const step = stride / 4;
|
|
340
|
+
const halves = stride / 2;
|
|
303
341
|
let taken = 0;
|
|
304
342
|
for (let from = 0; from < vertexCount; from += job.verticesPerStep) {
|
|
305
343
|
const data = job.data;
|
|
306
|
-
const
|
|
344
|
+
const rows = job.rows;
|
|
307
345
|
const to = Math.min(from + job.verticesPerStep, vertexCount);
|
|
308
|
-
|
|
309
|
-
|
|
346
|
+
for (const field of fields) {
|
|
347
|
+
const attribute = field.attribute;
|
|
310
348
|
const source = data[attribute.name];
|
|
311
|
-
if (
|
|
349
|
+
if (field.packing === 'snorm16') {
|
|
350
|
+
interleaveFixed(rows.signed, source, attribute.components, halves, field.offset / 2, from, to, SNORM16_ONE);
|
|
351
|
+
}
|
|
352
|
+
else if (field.packing === 'unorm16') {
|
|
353
|
+
interleaveFixed(rows.unsigned, source, attribute.components, halves, field.offset / 2, from, to, UNORM16_ONE);
|
|
354
|
+
}
|
|
355
|
+
else if (attribute === LAYERED_UVS) {
|
|
312
356
|
/* (u, v) from the coordinates and the layer after them, one field of three. */
|
|
313
|
-
|
|
314
|
-
interleaveField(
|
|
357
|
+
const at = field.offset / 4;
|
|
358
|
+
interleaveField(rows.floats, source, 2, step, at, from, to);
|
|
359
|
+
interleaveField(rows.floats, data.layers, 1, step, at + 2, from, to);
|
|
315
360
|
}
|
|
316
361
|
else {
|
|
317
|
-
interleaveField(
|
|
362
|
+
interleaveField(rows.floats, source, attribute.components, step, field.offset / 4, from, to);
|
|
318
363
|
}
|
|
319
|
-
fieldOffset += attribute.components;
|
|
320
364
|
}
|
|
321
|
-
device.queue.writeBuffer(vertices, from * stride,
|
|
365
|
+
device.queue.writeBuffer(vertices, from * stride, rows.bytes, from * stride, (to - from) * stride);
|
|
322
366
|
if (job.streams !== null) {
|
|
323
367
|
const count = (to - from) * 3;
|
|
324
368
|
device.queue.writeBuffer(job.streams.positions, from * 12, data.positions, from * 3, count);
|
|
@@ -328,7 +372,7 @@ function* uploadSteps(job) {
|
|
|
328
372
|
if (taken < job.totalSteps)
|
|
329
373
|
yield;
|
|
330
374
|
}
|
|
331
|
-
const indices = job.
|
|
375
|
+
const indices = job.indices;
|
|
332
376
|
const indexWidth = indices.BYTES_PER_ELEMENT;
|
|
333
377
|
for (let from = 0; from < indices.length; from += job.indicesPerStep) {
|
|
334
378
|
const to = Math.min(from + job.indicesPerStep, indices.length);
|
|
@@ -338,7 +382,8 @@ function* uploadSteps(job) {
|
|
|
338
382
|
yield;
|
|
339
383
|
}
|
|
340
384
|
job.data = null;
|
|
341
|
-
job.
|
|
385
|
+
job.indices = null;
|
|
386
|
+
job.rows = null;
|
|
342
387
|
job.progress.uploaded = true;
|
|
343
388
|
}
|
|
344
389
|
/**
|
|
@@ -383,32 +428,39 @@ function interleaveField(rows, source, width, step, fieldOffset, from, to) {
|
|
|
383
428
|
* Where the joints and weights sit in a vertex of `supplied`, the attributes a mesh interleaves in
|
|
384
429
|
* layout order; null unless it supplied both.
|
|
385
430
|
*/
|
|
386
|
-
function skinOffsetsOf(
|
|
387
|
-
let floats = 0;
|
|
431
|
+
function skinOffsetsOf(fields) {
|
|
388
432
|
let joints = -1;
|
|
389
433
|
let weights = -1;
|
|
390
434
|
let joints2 = -1;
|
|
391
435
|
let weights2 = -1;
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
436
|
+
let weightsFormat = 'float32x4';
|
|
437
|
+
let weights2Format = 'float32x4';
|
|
438
|
+
for (const field of fields) {
|
|
439
|
+
const name = field.attribute.name;
|
|
440
|
+
if (name === 'joints')
|
|
441
|
+
joints = field.offset;
|
|
442
|
+
if (name === 'joints2')
|
|
443
|
+
joints2 = field.offset;
|
|
444
|
+
if (name === 'weights') {
|
|
445
|
+
weights = field.offset;
|
|
446
|
+
weightsFormat = field.format;
|
|
447
|
+
}
|
|
448
|
+
if (name === 'weights2') {
|
|
449
|
+
weights2 = field.offset;
|
|
450
|
+
weights2Format = field.format;
|
|
451
|
+
}
|
|
402
452
|
}
|
|
403
453
|
if (joints < 0 || weights < 0)
|
|
404
454
|
return null;
|
|
405
455
|
return joints2 >= 0 && weights2 >= 0
|
|
406
|
-
? { joints, weights, joints2, weights2 }
|
|
407
|
-
: { joints, weights, joints2: -1, weights2: -1 };
|
|
456
|
+
? { joints, weights, joints2, weights2, weightsFormat, weights2Format }
|
|
457
|
+
: { joints, weights, joints2: -1, weights2: -1, weightsFormat, weights2Format };
|
|
408
458
|
}
|
|
409
459
|
/** The handle, made where it can see only what it hands out. */
|
|
410
460
|
function gpuMeshOf(parts) {
|
|
411
|
-
const motion = parts.update === null
|
|
461
|
+
const motion = parts.update === null
|
|
462
|
+
? null
|
|
463
|
+
: { previous: null, staging: null, changed: -1 };
|
|
412
464
|
const { vertices, constants, streams, indexBuffer, progress } = parts;
|
|
413
465
|
return {
|
|
414
466
|
vertexBuffers: streams === null
|
|
@@ -417,6 +469,7 @@ function gpuMeshOf(parts) {
|
|
|
417
469
|
vertexStride: parts.stride,
|
|
418
470
|
skinOffsets: parts.skinOffsets,
|
|
419
471
|
indexBuffer,
|
|
472
|
+
indexFormat: parts.indexFormat,
|
|
420
473
|
indexCount: parts.indexCount,
|
|
421
474
|
bounds: parts.bounds,
|
|
422
475
|
get complete() {
|
|
@@ -433,6 +486,7 @@ function gpuMeshOf(parts) {
|
|
|
433
486
|
motion,
|
|
434
487
|
dispose() {
|
|
435
488
|
motion?.previous?.destroy();
|
|
489
|
+
motion?.staging?.destroy();
|
|
436
490
|
streams?.positions.destroy();
|
|
437
491
|
streams?.normals.destroy();
|
|
438
492
|
vertices.destroy();
|
|
@@ -447,12 +501,12 @@ function isStreamed(attribute) {
|
|
|
447
501
|
}
|
|
448
502
|
/**
|
|
449
503
|
* A dynamic mesh's `update`: the caller's positions, and its normals where it has them, each
|
|
450
|
-
* written to its own buffer as it came
|
|
451
|
-
*
|
|
452
|
-
* of
|
|
504
|
+
* written to its own buffer as it came — or, where a reconstruction keeps last frame's positions,
|
|
505
|
+
* the positions staged and copied in on the device behind the copy aside (`StagedRewrite`), so no
|
|
506
|
+
* CPU copy of anything is kept and the rewrite submits nothing of its own.
|
|
453
507
|
*/
|
|
454
508
|
function streamUpdate(positionsBuffer, normalsBuffer, streamBytes, bounds, vertexCount) {
|
|
455
|
-
return (target, positions, normals,
|
|
509
|
+
return (target, positions, normals, staged = null) => {
|
|
456
510
|
for (const [name, values] of [
|
|
457
511
|
['update', positions],
|
|
458
512
|
['normals', normals],
|
|
@@ -463,15 +517,19 @@ function streamUpdate(positionsBuffer, normalsBuffer, streamBytes, bounds, verte
|
|
|
463
517
|
"A mesh's vertex count is fixed at creation: the index buffer, the pipeline and " +
|
|
464
518
|
'every other attribute are sized against it.');
|
|
465
519
|
}
|
|
466
|
-
if (previous !== null) {
|
|
467
|
-
const encoder = target.createCommandEncoder({ label: 'mesh.previousPositions' });
|
|
468
|
-
encoder.copyBufferToBuffer(positionsBuffer, 0, previous, 0, streamBytes);
|
|
469
|
-
target.queue.submit([encoder.finish()]);
|
|
470
|
-
}
|
|
471
520
|
/* Everything that decides whether this is on screen starts from the bounds, so a cloth
|
|
472
521
|
that blew sideways out of its original box would be culled while still visible. */
|
|
473
522
|
boundsOfPositions(positions, bounds);
|
|
474
|
-
|
|
523
|
+
if (staged === null) {
|
|
524
|
+
target.queue.writeBuffer(positionsBuffer, 0, positions);
|
|
525
|
+
}
|
|
526
|
+
else {
|
|
527
|
+
target.queue.writeBuffer(staged.staging, 0, positions);
|
|
528
|
+
if (staged.previous !== null) {
|
|
529
|
+
staged.encoder.copyBufferToBuffer(positionsBuffer, 0, staged.previous, 0, streamBytes);
|
|
530
|
+
}
|
|
531
|
+
staged.encoder.copyBufferToBuffer(staged.staging, 0, positionsBuffer, 0, streamBytes);
|
|
532
|
+
}
|
|
475
533
|
if (normals !== undefined)
|
|
476
534
|
target.queue.writeBuffer(normalsBuffer, 0, normals);
|
|
477
535
|
};
|
|
@@ -546,7 +604,9 @@ instanced = false) {
|
|
|
546
604
|
* in the interleaved one. A *skinned* mesh interleaves them, so removing them would shift
|
|
547
605
|
* the stride under every other attribute — which is why `createInstanced` refuses one.
|
|
548
606
|
*/
|
|
549
|
-
|
|
607
|
+
/* One value every vertex shares sits with the absent ones: `vertexPacking.ts`. */
|
|
608
|
+
const packing = packingIn(present, String(attribute.name));
|
|
609
|
+
const supplied = (!attribute.optional || present[String(attribute.name)] === true) && packing !== 'constant';
|
|
550
610
|
if (instanced && INSTANCE_LOCATIONS.has(attribute.shaderLocation)) {
|
|
551
611
|
/*
|
|
552
612
|
* **Free to drop only because the mesh does not supply it.** An absent optional attribute
|
|
@@ -566,12 +626,9 @@ instanced = false) {
|
|
|
566
626
|
return;
|
|
567
627
|
}
|
|
568
628
|
if (supplied) {
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
format: attribute.format,
|
|
573
|
-
});
|
|
574
|
-
offset += attribute.components * 4;
|
|
629
|
+
const format = packedFormat(attribute.format, packing);
|
|
630
|
+
interleaved.push({ shaderLocation: attribute.shaderLocation, offset, format });
|
|
631
|
+
offset += formatBytes(format);
|
|
575
632
|
return;
|
|
576
633
|
}
|
|
577
634
|
constants.push({
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
export declare const CLOTH_ENTRIES: readonly ['clothPose', 'clothRest', 'clothBegin', 'clothPredict', 'clothDistance', 'clothBending', 'clothTether', 'clothLimit', 'clothFinish', 'clothBlend', 'clothPublish'];
|
|
2
|
+
export type ClothEntry = (typeof CLOTH_ENTRIES)[number];
|
|
3
|
+
export interface ClothKernels {
|
|
4
|
+
readonly layout: GPUBindGroupLayout;
|
|
5
|
+
readonly outputLayout: GPUBindGroupLayout;
|
|
6
|
+
readonly pipelines: Readonly<Record<ClothEntry, GPUComputePipeline>>;
|
|
7
|
+
}
|
|
8
|
+
export declare function clothKernelsFor(device: GPUDevice): ClothKernels;
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The cloth solver's layouts and one compute pipeline an entry point of `shaders/clothSolve.wgsl.ts`,
|
|
3
|
+
* built once a device and shared by every set of garments on it.
|
|
4
|
+
*/
|
|
5
|
+
import { CLOTH_SOLVE_WGSL } from './shaders/clothSolve.wgsl.js';
|
|
6
|
+
import { shaderModule } from './shaderModules.js';
|
|
7
|
+
const COMPUTE = 0x4;
|
|
8
|
+
export const CLOTH_ENTRIES = [
|
|
9
|
+
'clothPose',
|
|
10
|
+
'clothRest',
|
|
11
|
+
'clothBegin',
|
|
12
|
+
'clothPredict',
|
|
13
|
+
'clothDistance',
|
|
14
|
+
'clothBending',
|
|
15
|
+
'clothTether',
|
|
16
|
+
'clothLimit',
|
|
17
|
+
'clothFinish',
|
|
18
|
+
'clothBlend',
|
|
19
|
+
'clothPublish',
|
|
20
|
+
];
|
|
21
|
+
const kernelsByDevice = new WeakMap();
|
|
22
|
+
export function clothKernelsFor(device) {
|
|
23
|
+
const held = kernelsByDevice.get(device);
|
|
24
|
+
if (held !== undefined)
|
|
25
|
+
return held;
|
|
26
|
+
const uniform = (binding, dynamic) => ({
|
|
27
|
+
binding,
|
|
28
|
+
visibility: COMPUTE,
|
|
29
|
+
buffer: { type: 'uniform', hasDynamicOffset: dynamic },
|
|
30
|
+
});
|
|
31
|
+
const storage = (binding, type) => ({
|
|
32
|
+
binding,
|
|
33
|
+
visibility: COMPUTE,
|
|
34
|
+
buffer: { type },
|
|
35
|
+
});
|
|
36
|
+
/* Seven storage buffers of the eight a stage is granted: the shader's header says why so few. */
|
|
37
|
+
const layout = device.createBindGroupLayout({
|
|
38
|
+
label: 'cloth.solve',
|
|
39
|
+
entries: [
|
|
40
|
+
uniform(0, false),
|
|
41
|
+
uniform(1, true),
|
|
42
|
+
uniform(2, true),
|
|
43
|
+
storage(3, 'storage'),
|
|
44
|
+
storage(4, 'storage'),
|
|
45
|
+
storage(5, 'read-only-storage'),
|
|
46
|
+
storage(6, 'read-only-storage'),
|
|
47
|
+
storage(7, 'storage'),
|
|
48
|
+
storage(8, 'read-only-storage'),
|
|
49
|
+
storage(9, 'read-only-storage'),
|
|
50
|
+
],
|
|
51
|
+
});
|
|
52
|
+
const outputLayout = device.createBindGroupLayout({
|
|
53
|
+
label: 'cloth.publish',
|
|
54
|
+
entries: [
|
|
55
|
+
{
|
|
56
|
+
binding: 0,
|
|
57
|
+
visibility: COMPUTE,
|
|
58
|
+
storageTexture: { access: 'write-only', format: 'rgba32float' },
|
|
59
|
+
},
|
|
60
|
+
],
|
|
61
|
+
});
|
|
62
|
+
const module = shaderModule(device, { label: 'cloth.solve', code: CLOTH_SOLVE_WGSL });
|
|
63
|
+
const solve = device.createPipelineLayout({ bindGroupLayouts: [layout] });
|
|
64
|
+
const publish = device.createPipelineLayout({ bindGroupLayouts: [layout, outputLayout] });
|
|
65
|
+
const pipelines = {};
|
|
66
|
+
for (const entry of CLOTH_ENTRIES) {
|
|
67
|
+
pipelines[entry] = device.createComputePipeline({
|
|
68
|
+
label: `cloth.${entry}`,
|
|
69
|
+
layout: entry === 'clothPublish' ? publish : solve,
|
|
70
|
+
compute: { module, entryPoint: entry },
|
|
71
|
+
});
|
|
72
|
+
}
|
|
73
|
+
const kernels = { layout, outputLayout, pipelines };
|
|
74
|
+
kernelsByDevice.set(device, kernels);
|
|
75
|
+
return kernels;
|
|
76
|
+
}
|