@driftengine/core 4.9.0 → 4.10.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 +62 -11
- package/README.md +43 -38
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -3
- 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 +90 -2
- package/dist/render/backend/webgl2/renderer.js +253 -24
- 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 +19 -4
- package/dist/render/backend/webgpu/buffers.js +18 -11
- 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 +18 -4
- package/dist/render/backend/webgpu/drawCommand.js +10 -3
- package/dist/render/backend/webgpu/flatPass.d.ts +85 -42
- package/dist/render/backend/webgpu/flatPass.js +136 -20
- package/dist/render/backend/webgpu/instanced.d.ts +14 -1
- package/dist/render/backend/webgpu/instanced.js +24 -1
- package/dist/render/backend/webgpu/materialStore.d.ts +101 -0
- package/dist/render/backend/webgpu/materialStore.js +265 -0
- package/dist/render/backend/webgpu/pipelineCache.d.ts +6 -0
- package/dist/render/backend/webgpu/pipelineCache.js +7 -0
- package/dist/render/backend/webgpu/renderer.d.ts +152 -35
- package/dist/render/backend/webgpu/renderer.js +787 -142
- 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 +241 -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/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/instances.d.ts +26 -0
- package/dist/render/lightBudget.d.ts +10 -0
- package/dist/render/lightBudget.js +10 -0
- package/dist/render/lightmap.d.ts +1 -1
- package/dist/render/mesh.d.ts +5 -0
- package/dist/render/mesh.js +6 -0
- package/dist/render/pointLightSelection.d.ts +4 -4
- package/dist/render/screenSpaceReflection.d.ts +2 -1
- 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/index.d.ts +6 -0
- package/dist/render/shaders/flat/index.js +27 -5
- package/dist/render/shaders/flat/main.d.ts +1 -1
- package/dist/render/shaders/flat/main.js +15 -5
- package/dist/render/shaders/flat/models.js +1 -1
- package/dist/render/shaders/flat/overlay.js +1 -1
- package/dist/render/shaders/flat/pointShadow.js +2 -2
- package/dist/render/shaders/flat/preamble.js +38 -24
- 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 +3077 -2903
- package/dist/render/shaders/generated/flat.wgsl.js +3100 -2925
- package/dist/render/shaders/outputTransform.d.ts +7 -0
- package/dist/render/shaders/outputTransform.js +28 -14
- package/dist/render/shaders/vertexChannel.d.ts +7 -0
- package/dist/render/shaders/vertexChannel.js +26 -0
- package/dist/render/staticDraws.d.ts +47 -0
- package/dist/render/staticDraws.js +43 -0
- package/dist/render/surfaceTexture.d.ts +38 -7
- package/dist/render/surfaceTexture.js +56 -17
- package/package.json +6 -6
- package/src/index.ts +8 -2
- 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 +315 -24
- 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 +42 -16
- 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 +22 -4
- package/src/render/backend/webgpu/flatPass.ts +187 -14
- package/src/render/backend/webgpu/instanced.ts +32 -1
- package/src/render/backend/webgpu/materialStore.ts +276 -0
- package/src/render/backend/webgpu/pipelineCache.ts +7 -0
- package/src/render/backend/webgpu/renderer.ts +928 -155
- 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 +363 -0
- package/src/render/backend/webgpu/surfaceTexturePass.ts +29 -3
- package/src/render/backend/webgpu/uniformRing.ts +10 -1
- 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/instances.ts +27 -0
- package/src/render/lightBudget.ts +11 -0
- package/src/render/lightmap.ts +1 -1
- package/src/render/mesh.ts +6 -0
- package/src/render/pointLightSelection.ts +4 -4
- package/src/render/screenSpaceReflection.ts +2 -1
- package/src/render/shaders/boneAnimation.ts +63 -0
- package/src/render/shaders/depth.ts +45 -10
- package/src/render/shaders/flat/index.ts +33 -5
- package/src/render/shaders/flat/main.ts +15 -5
- package/src/render/shaders/flat/models.ts +1 -1
- package/src/render/shaders/flat/overlay.ts +1 -1
- package/src/render/shaders/flat/pointShadow.ts +2 -2
- package/src/render/shaders/flat/preamble.ts +38 -24
- package/src/render/shaders/generated/depth.wgsl.ts +95 -0
- package/src/render/shaders/generated/flat.wgsl.ts +3100 -2925
- package/src/render/shaders/outputTransform.ts +30 -14
- package/src/render/shaders/vertexChannel.ts +27 -0
- package/src/render/staticDraws.ts +95 -0
- package/src/render/surfaceTexture.ts +94 -27
|
@@ -258,18 +258,37 @@ export interface GpuMesh {
|
|
|
258
258
|
device: GPUDevice,
|
|
259
259
|
positions: Float32Array,
|
|
260
260
|
normals?: Float32Array,
|
|
261
|
-
|
|
261
|
+
staged?: StagedRewrite | null,
|
|
262
262
|
) => void)
|
|
263
263
|
| null;
|
|
264
264
|
/**
|
|
265
|
-
* A dynamic mesh's motion state, which the renderer owns: a buffer for last frame's positions
|
|
266
|
-
*
|
|
267
|
-
*
|
|
265
|
+
* A dynamic mesh's motion state, which the renderer owns: a buffer for last frame's positions and
|
|
266
|
+
* one the new positions are staged in, each made the first time a reconstructing renderer
|
|
267
|
+
* rewrites the mesh, and the frame of the last rewrite. Null on a mesh created without
|
|
268
|
+
* `{ dynamic: true }`, which never moves a vertex.
|
|
268
269
|
*/
|
|
269
|
-
readonly motion: {
|
|
270
|
+
readonly motion: {
|
|
271
|
+
previous: GPUBuffer | null;
|
|
272
|
+
staging: GPUBuffer | null;
|
|
273
|
+
changed: number;
|
|
274
|
+
} | null;
|
|
270
275
|
dispose(): void;
|
|
271
276
|
}
|
|
272
277
|
|
|
278
|
+
/**
|
|
279
|
+
* A rewrite a reconstructing renderer stages rather than writes, so last frame's positions can be
|
|
280
|
+
* kept with no submit of the rewrite's own: the new positions are written into `staging`, and two
|
|
281
|
+
* copies are recorded into `encoder` — the live positions into `previous` first, where the frame's
|
|
282
|
+
* first rewrite keeps them, then the staged ones in. The renderer submits that encoder at the head
|
|
283
|
+
* of the frame's next submit, which is what keeps both copies ahead of every draw that reads the
|
|
284
|
+
* mesh. A `writeBuffer` straight to the live positions would land before the copy aside could run.
|
|
285
|
+
*/
|
|
286
|
+
export interface StagedRewrite {
|
|
287
|
+
encoder: GPUCommandEncoder;
|
|
288
|
+
staging: GPUBuffer;
|
|
289
|
+
previous: GPUBuffer | null;
|
|
290
|
+
}
|
|
291
|
+
|
|
273
292
|
/** A mesh on its way to the device, and the iterator that gets it there. */
|
|
274
293
|
export interface IncrementalGpuMesh {
|
|
275
294
|
/**
|
|
@@ -658,7 +677,10 @@ function gpuMeshOf(parts: {
|
|
|
658
677
|
readonly morph: MorphTexture | null;
|
|
659
678
|
readonly update: GpuMesh['update'];
|
|
660
679
|
}): GpuMesh {
|
|
661
|
-
const motion =
|
|
680
|
+
const motion =
|
|
681
|
+
parts.update === null
|
|
682
|
+
? null
|
|
683
|
+
: { previous: null as GPUBuffer | null, staging: null as GPUBuffer | null, changed: -1 };
|
|
662
684
|
const { vertices, constants, streams, indexBuffer, progress } = parts;
|
|
663
685
|
return {
|
|
664
686
|
vertexBuffers:
|
|
@@ -684,6 +706,7 @@ function gpuMeshOf(parts: {
|
|
|
684
706
|
motion,
|
|
685
707
|
dispose(): void {
|
|
686
708
|
motion?.previous?.destroy();
|
|
709
|
+
motion?.staging?.destroy();
|
|
687
710
|
streams?.positions.destroy();
|
|
688
711
|
streams?.normals.destroy();
|
|
689
712
|
vertices.destroy();
|
|
@@ -700,9 +723,9 @@ function isStreamed(attribute: VertexAttribute): boolean {
|
|
|
700
723
|
|
|
701
724
|
/**
|
|
702
725
|
* A dynamic mesh's `update`: the caller's positions, and its normals where it has them, each
|
|
703
|
-
* written to its own buffer as it came
|
|
704
|
-
*
|
|
705
|
-
* of
|
|
726
|
+
* written to its own buffer as it came — or, where a reconstruction keeps last frame's positions,
|
|
727
|
+
* the positions staged and copied in on the device behind the copy aside (`StagedRewrite`), so no
|
|
728
|
+
* CPU copy of anything is kept and the rewrite submits nothing of its own.
|
|
706
729
|
*/
|
|
707
730
|
function streamUpdate(
|
|
708
731
|
positionsBuffer: GPUBuffer,
|
|
@@ -715,7 +738,7 @@ function streamUpdate(
|
|
|
715
738
|
target: GPUDevice,
|
|
716
739
|
positions: Float32Array,
|
|
717
740
|
normals?: Float32Array,
|
|
718
|
-
|
|
741
|
+
staged: StagedRewrite | null = null,
|
|
719
742
|
): void => {
|
|
720
743
|
for (const [name, values] of [
|
|
721
744
|
['update', positions],
|
|
@@ -728,15 +751,18 @@ function streamUpdate(
|
|
|
728
751
|
'every other attribute are sized against it.',
|
|
729
752
|
);
|
|
730
753
|
}
|
|
731
|
-
if (previous !== null) {
|
|
732
|
-
const encoder = target.createCommandEncoder({ label: 'mesh.previousPositions' });
|
|
733
|
-
encoder.copyBufferToBuffer(positionsBuffer, 0, previous, 0, streamBytes);
|
|
734
|
-
target.queue.submit([encoder.finish()]);
|
|
735
|
-
}
|
|
736
754
|
/* Everything that decides whether this is on screen starts from the bounds, so a cloth
|
|
737
755
|
that blew sideways out of its original box would be culled while still visible. */
|
|
738
756
|
boundsOfPositions(positions, bounds);
|
|
739
|
-
|
|
757
|
+
if (staged === null) {
|
|
758
|
+
target.queue.writeBuffer(positionsBuffer, 0, positions);
|
|
759
|
+
} else {
|
|
760
|
+
target.queue.writeBuffer(staged.staging, 0, positions);
|
|
761
|
+
if (staged.previous !== null) {
|
|
762
|
+
staged.encoder.copyBufferToBuffer(positionsBuffer, 0, staged.previous, 0, streamBytes);
|
|
763
|
+
}
|
|
764
|
+
staged.encoder.copyBufferToBuffer(staged.staging, 0, positionsBuffer, 0, streamBytes);
|
|
765
|
+
}
|
|
740
766
|
if (normals !== undefined) target.queue.writeBuffer(normalsBuffer, 0, normals);
|
|
741
767
|
};
|
|
742
768
|
}
|
|
@@ -12,7 +12,8 @@
|
|
|
12
12
|
* the level nominally has.
|
|
13
13
|
*/
|
|
14
14
|
import type { CompressedTextureFormat, CompressedTextureSource } from '../../compressedSource.ts';
|
|
15
|
-
import {
|
|
15
|
+
import { BLOCK_SHAPES, blockFamily } from '../../compressedSource.ts';
|
|
16
|
+
import type { BlockFormat } from '../../compressedSource.ts';
|
|
16
17
|
|
|
17
18
|
const GPU_FORMAT: Readonly<Record<CompressedTextureFormat, GPUTextureFormat>> = {
|
|
18
19
|
bc1: 'bc1-rgba-unorm',
|
|
@@ -25,13 +26,61 @@ const GPU_FORMAT: Readonly<Record<CompressedTextureFormat, GPUTextureFormat>> =
|
|
|
25
26
|
bc5: 'bc5-rg-unorm',
|
|
26
27
|
bc7: 'bc7-rgba-unorm',
|
|
27
28
|
'bc7-srgb': 'bc7-rgba-unorm-srgb',
|
|
29
|
+
'etc2-rgb8': 'etc2-rgb8unorm',
|
|
30
|
+
'etc2-rgb8-srgb': 'etc2-rgb8unorm-srgb',
|
|
31
|
+
'etc2-rgb8a1': 'etc2-rgb8a1unorm',
|
|
32
|
+
'etc2-rgb8a1-srgb': 'etc2-rgb8a1unorm-srgb',
|
|
33
|
+
'etc2-rgba8': 'etc2-rgba8unorm',
|
|
34
|
+
'etc2-rgba8-srgb': 'etc2-rgba8unorm-srgb',
|
|
35
|
+
'eac-r11': 'eac-r11unorm',
|
|
36
|
+
'eac-rg11': 'eac-rg11unorm',
|
|
37
|
+
'astc-4x4': 'astc-4x4-unorm',
|
|
38
|
+
'astc-4x4-srgb': 'astc-4x4-unorm-srgb',
|
|
39
|
+
'astc-5x4': 'astc-5x4-unorm',
|
|
40
|
+
'astc-5x4-srgb': 'astc-5x4-unorm-srgb',
|
|
41
|
+
'astc-5x5': 'astc-5x5-unorm',
|
|
42
|
+
'astc-5x5-srgb': 'astc-5x5-unorm-srgb',
|
|
43
|
+
'astc-6x5': 'astc-6x5-unorm',
|
|
44
|
+
'astc-6x5-srgb': 'astc-6x5-unorm-srgb',
|
|
45
|
+
'astc-6x6': 'astc-6x6-unorm',
|
|
46
|
+
'astc-6x6-srgb': 'astc-6x6-unorm-srgb',
|
|
47
|
+
'astc-8x5': 'astc-8x5-unorm',
|
|
48
|
+
'astc-8x5-srgb': 'astc-8x5-unorm-srgb',
|
|
49
|
+
'astc-8x6': 'astc-8x6-unorm',
|
|
50
|
+
'astc-8x6-srgb': 'astc-8x6-unorm-srgb',
|
|
51
|
+
'astc-8x8': 'astc-8x8-unorm',
|
|
52
|
+
'astc-8x8-srgb': 'astc-8x8-unorm-srgb',
|
|
53
|
+
'astc-10x5': 'astc-10x5-unorm',
|
|
54
|
+
'astc-10x5-srgb': 'astc-10x5-unorm-srgb',
|
|
55
|
+
'astc-10x6': 'astc-10x6-unorm',
|
|
56
|
+
'astc-10x6-srgb': 'astc-10x6-unorm-srgb',
|
|
57
|
+
'astc-10x8': 'astc-10x8-unorm',
|
|
58
|
+
'astc-10x8-srgb': 'astc-10x8-unorm-srgb',
|
|
59
|
+
'astc-10x10': 'astc-10x10-unorm',
|
|
60
|
+
'astc-10x10-srgb': 'astc-10x10-unorm-srgb',
|
|
61
|
+
'astc-12x10': 'astc-12x10-unorm',
|
|
62
|
+
'astc-12x10-srgb': 'astc-12x10-unorm-srgb',
|
|
63
|
+
'astc-12x12': 'astc-12x12-unorm',
|
|
64
|
+
'astc-12x12-srgb': 'astc-12x12-unorm-srgb',
|
|
28
65
|
};
|
|
29
66
|
|
|
30
67
|
/** Every BC format, where the device carries the feature; none where it does not. */
|
|
68
|
+
/** The feature that grants each family, which `select.ts` asks for wherever the adapter offers it. */
|
|
69
|
+
const FEATURE: Readonly<Record<'bc' | 'etc2' | 'astc', string>> = {
|
|
70
|
+
bc: 'texture-compression-bc',
|
|
71
|
+
etc2: 'texture-compression-etc2',
|
|
72
|
+
astc: 'texture-compression-astc',
|
|
73
|
+
};
|
|
74
|
+
|
|
31
75
|
export function gpuCompressedFormats(device: GPUDevice): CompressedTextureFormat[] {
|
|
32
|
-
return
|
|
33
|
-
|
|
34
|
-
|
|
76
|
+
return (Object.keys(GPU_FORMAT) as CompressedTextureFormat[]).filter((name) =>
|
|
77
|
+
device.features.has(FEATURE[blockFamily(baseOf(name))]),
|
|
78
|
+
);
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/** The format a name is of, without its colour space. */
|
|
82
|
+
function baseOf(name: CompressedTextureFormat): BlockFormat {
|
|
83
|
+
return (name.endsWith('-srgb') ? name.slice(0, -'-srgb'.length) : name) as BlockFormat;
|
|
35
84
|
}
|
|
36
85
|
|
|
37
86
|
/** A texture holding `layers` as `name`, `levels` levels of each, written as they are. */
|
|
@@ -43,21 +92,22 @@ export function createCompressedTexture(
|
|
|
43
92
|
): GPUTexture {
|
|
44
93
|
const { format, width, height } = layers[0] as CompressedTextureSource;
|
|
45
94
|
const texture = device.createTexture({
|
|
46
|
-
label:
|
|
95
|
+
label: `surface.texture.${format}`,
|
|
47
96
|
size: [width, height, layers.length],
|
|
48
97
|
format: GPU_FORMAT[name],
|
|
49
98
|
mipLevelCount: levels,
|
|
50
99
|
usage: 0x2 | 0x4, // COPY_DST | TEXTURE_BINDING
|
|
51
100
|
});
|
|
101
|
+
const block = BLOCK_SHAPES[format];
|
|
52
102
|
for (let level = 0; level < levels; level++) {
|
|
53
|
-
const across = Math.ceil(Math.max(1, width >> level) /
|
|
54
|
-
const down = Math.ceil(Math.max(1, height >> level) /
|
|
103
|
+
const across = Math.ceil(Math.max(1, width >> level) / block.width);
|
|
104
|
+
const down = Math.ceil(Math.max(1, height >> level) / block.height);
|
|
55
105
|
for (let layer = 0; layer < layers.length; layer++) {
|
|
56
106
|
device.queue.writeTexture(
|
|
57
107
|
{ texture, mipLevel: level, origin: [0, 0, layer] },
|
|
58
108
|
(layers[layer] as CompressedTextureSource).levels[level] as Uint8Array<ArrayBuffer>,
|
|
59
|
-
{ bytesPerRow: across *
|
|
60
|
-
{ width: across *
|
|
109
|
+
{ bytesPerRow: across * block.bytes, rowsPerImage: down },
|
|
110
|
+
{ width: across * block.width, height: down * block.height, depthOrArrayLayers: 1 },
|
|
61
111
|
);
|
|
62
112
|
}
|
|
63
113
|
}
|
|
@@ -3,6 +3,8 @@ import {
|
|
|
3
3
|
DEPTH_CUTOUT_FRAG_WGSL,
|
|
4
4
|
DEPTH_CUTOUT_VERT_WGSL,
|
|
5
5
|
DEPTH_FRAG_WGSL,
|
|
6
|
+
DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT_WGSL,
|
|
7
|
+
DEPTH_INSTANCED_ANIMATED_VERT_WGSL,
|
|
6
8
|
DEPTH_INSTANCED_CUTOUT_VERT_WGSL,
|
|
7
9
|
DEPTH_INSTANCED_VERT_WGSL,
|
|
8
10
|
DEPTH_SKINNED_VERT_WGSL,
|
|
@@ -13,6 +15,7 @@ import { vertexConstants } from './flatPass.ts';
|
|
|
13
15
|
import type { PipelineCache } from './pipelineCache.ts';
|
|
14
16
|
import { shaderModule } from './shaderModules.ts';
|
|
15
17
|
import type { ClothViews } from './clothTextures.ts';
|
|
18
|
+
import type { AnimationViews } from './flatPass.ts';
|
|
16
19
|
|
|
17
20
|
/**
|
|
18
21
|
* The shadow pass: the world drawn from the light, into a depth map.
|
|
@@ -52,6 +55,17 @@ const CLOTH_TEXTURES = [
|
|
|
52
55
|
DEPTH_BINDINGS.DEPTH_SKINNED_VERT.textures.uClothRest,
|
|
53
56
|
] as const;
|
|
54
57
|
|
|
58
|
+
/** A bone animation's three textures, at the bindings both animated depth variants declare them. */
|
|
59
|
+
const ANIMATION_TEXTURES = [
|
|
60
|
+
DEPTH_BINDINGS.DEPTH_INSTANCED_ANIMATED_VERT.textures.uBonePlaces,
|
|
61
|
+
DEPTH_BINDINGS.DEPTH_INSTANCED_ANIMATED_VERT.textures.uBoneTurns,
|
|
62
|
+
DEPTH_BINDINGS.DEPTH_INSTANCED_ANIMATED_VERT.textures.uInstanceClocks,
|
|
63
|
+
] as const;
|
|
64
|
+
export const DEPTH_INSTANCED_ANIMATED_VERT_FIELDS =
|
|
65
|
+
DEPTH_BINDINGS.DEPTH_INSTANCED_ANIMATED_VERT.fields;
|
|
66
|
+
export const DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT_FIELDS =
|
|
67
|
+
DEPTH_BINDINGS.DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT.fields;
|
|
68
|
+
|
|
55
69
|
const VISIBILITY_VERTEX = 0x1;
|
|
56
70
|
const VISIBILITY_FRAGMENT = 0x2;
|
|
57
71
|
|
|
@@ -82,10 +96,19 @@ export function createDepthBindGroupLayout(
|
|
|
82
96
|
* no cutout caster pays nothing.
|
|
83
97
|
*/
|
|
84
98
|
cutout = false,
|
|
99
|
+
/** An instanced caster playing a bone animation: its three vertex textures. */
|
|
100
|
+
animated = false,
|
|
85
101
|
): GPUBindGroupLayout {
|
|
86
102
|
return device.createBindGroupLayout({
|
|
87
|
-
label: skinned ? 'depth.layout.skinned' : cutout ? 'depth.layout.cutout' : 'depth.layout'
|
|
103
|
+
label: `${skinned ? 'depth.layout.skinned' : cutout ? 'depth.layout.cutout' : 'depth.layout'}${animated ? '.animated' : ''}`,
|
|
88
104
|
entries: [
|
|
105
|
+
...(animated
|
|
106
|
+
? ANIMATION_TEXTURES.map((binding) => ({
|
|
107
|
+
binding: binding.texture,
|
|
108
|
+
visibility: VISIBILITY_VERTEX,
|
|
109
|
+
texture: { sampleType: 'unfilterable-float' as GPUTextureSampleType },
|
|
110
|
+
}))
|
|
111
|
+
: []),
|
|
89
112
|
/* The texture and no sampler: the shader reads the palette with `textureLoad`, an integer
|
|
90
113
|
coordinate with nothing to filter, which is what `flatPass.ts` binds for the same
|
|
91
114
|
palette. naga declares a sampler for it and nothing ever reads one. */
|
|
@@ -174,6 +197,8 @@ export function createDepthBindGroup(
|
|
|
174
197
|
cutout: { readonly view: GPUTextureView; readonly sampler: GPUSampler } | null = null,
|
|
175
198
|
/** A skinned group's cloth views: the caster's, or the stand-in where it has none. */
|
|
176
199
|
cloth: ClothViews | null = null,
|
|
200
|
+
/** An animated caster's clip and clocks. See `AnimationViews`. */
|
|
201
|
+
animation: AnimationViews | null = null,
|
|
177
202
|
): GPUBindGroup {
|
|
178
203
|
return device.createBindGroup({
|
|
179
204
|
label:
|
|
@@ -208,6 +233,13 @@ export function createDepthBindGroup(
|
|
|
208
233
|
{ binding: CUTOUT_MAP.sampler, resource: cutout.sampler },
|
|
209
234
|
]
|
|
210
235
|
: []),
|
|
236
|
+
...(animation !== null
|
|
237
|
+
? [
|
|
238
|
+
{ binding: ANIMATION_TEXTURES[0].texture, resource: animation.places },
|
|
239
|
+
{ binding: ANIMATION_TEXTURES[1].texture, resource: animation.turns },
|
|
240
|
+
{ binding: ANIMATION_TEXTURES[2].texture, resource: animation.clocks },
|
|
241
|
+
]
|
|
242
|
+
: []),
|
|
211
243
|
],
|
|
212
244
|
});
|
|
213
245
|
}
|
|
@@ -256,6 +288,8 @@ export function depthPipeline(
|
|
|
256
288
|
skinEight = false,
|
|
257
289
|
/** Whether a skinned caster is placed by a cloth binding, as its visible draw is. The key carries it. */
|
|
258
290
|
cloth = false,
|
|
291
|
+
/** An instanced caster playing a bone animation. The key must carry it. */
|
|
292
|
+
animated = false,
|
|
259
293
|
): GPURenderPipeline {
|
|
260
294
|
return cache.get(key, () => ({
|
|
261
295
|
label: key,
|
|
@@ -263,15 +297,22 @@ export function depthPipeline(
|
|
|
263
297
|
vertex: {
|
|
264
298
|
module: shaderModule(
|
|
265
299
|
device,
|
|
266
|
-
|
|
267
|
-
?
|
|
268
|
-
? {
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
:
|
|
273
|
-
|
|
274
|
-
|
|
300
|
+
animated
|
|
301
|
+
? cutout
|
|
302
|
+
? {
|
|
303
|
+
label: 'depth.instanced.animated.cutout.vert',
|
|
304
|
+
code: DEPTH_INSTANCED_ANIMATED_CUTOUT_VERT_WGSL,
|
|
305
|
+
}
|
|
306
|
+
: { label: 'depth.instanced.animated.vert', code: DEPTH_INSTANCED_ANIMATED_VERT_WGSL }
|
|
307
|
+
: cutout
|
|
308
|
+
? instanced
|
|
309
|
+
? { label: 'depth.instanced.cutout.vert', code: DEPTH_INSTANCED_CUTOUT_VERT_WGSL }
|
|
310
|
+
: { label: 'depth.cutout.vert', code: DEPTH_CUTOUT_VERT_WGSL }
|
|
311
|
+
: instanced
|
|
312
|
+
? { label: 'depth.instanced.vert', code: DEPTH_INSTANCED_VERT_WGSL }
|
|
313
|
+
: skinned
|
|
314
|
+
? { label: 'depth.skinned.vert', code: DEPTH_SKINNED_VERT_WGSL }
|
|
315
|
+
: { label: 'depth.vert', code: DEPTH_VERT_WGSL },
|
|
275
316
|
),
|
|
276
317
|
entryPoint: 'main',
|
|
277
318
|
buffers: vertexBufferLayouts(present, instanced),
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
* One recorded draw, in the only shape this backend ever issues.
|
|
3
3
|
*
|
|
4
4
|
* Sampled across three unrelated verbs — panels, particles, wind streaks — the sequence is the
|
|
5
|
-
* same every time: a pipeline, a bind group with up to
|
|
5
|
+
* same every time: a pipeline, a bind group with up to four dynamic offsets, some vertex
|
|
6
6
|
* buffers, perhaps an index buffer, then a draw. Nothing in it is specific to what is being
|
|
7
7
|
* drawn except the values.
|
|
8
8
|
*
|
|
@@ -21,9 +21,14 @@ const VERTEX_BUFFER_SLOTS = 8;
|
|
|
21
21
|
export interface DrawCommand {
|
|
22
22
|
pipeline: GPURenderPipeline | null;
|
|
23
23
|
bindGroup: GPUBindGroup | null;
|
|
24
|
-
/**
|
|
24
|
+
/**
|
|
25
|
+
* Dynamic offsets, held as numbers rather than an array so a reset stays free. Four only for a
|
|
26
|
+
* lit mesh: its draw's slot, its pass's, its material's and its view's, in binding order.
|
|
27
|
+
*/
|
|
25
28
|
offsetA: number;
|
|
26
29
|
offsetB: number;
|
|
30
|
+
offsetC: number;
|
|
31
|
+
offsetD: number;
|
|
27
32
|
offsetCount: number;
|
|
28
33
|
/** Fixed length; `vertexCount` says how many slots are live. */
|
|
29
34
|
readonly vertexBuffers: (GPUBuffer | null)[];
|
|
@@ -37,6 +42,12 @@ export interface DrawCommand {
|
|
|
37
42
|
* cull's survivors, counted on the device. Null for every other draw.
|
|
38
43
|
*/
|
|
39
44
|
indirect: GPUBuffer | null;
|
|
45
|
+
/**
|
|
46
|
+
* Render bundles to execute instead of everything above: a static list recorded once, replayed
|
|
47
|
+
* as one command. Held as the array `executeBundles` takes, kept by the list, so issuing it
|
|
48
|
+
* allocates nothing. Null for every other command, and put back to null by every take.
|
|
49
|
+
*/
|
|
50
|
+
bundles: readonly GPURenderBundle[] | null;
|
|
40
51
|
}
|
|
41
52
|
|
|
42
53
|
export interface CommandPool {
|
|
@@ -52,6 +63,8 @@ function emptyCommand(): DrawCommand {
|
|
|
52
63
|
bindGroup: null,
|
|
53
64
|
offsetA: 0,
|
|
54
65
|
offsetB: 0,
|
|
66
|
+
offsetC: 0,
|
|
67
|
+
offsetD: 0,
|
|
55
68
|
offsetCount: 0,
|
|
56
69
|
vertexBuffers: new Array<GPUBuffer | null>(VERTEX_BUFFER_SLOTS).fill(null),
|
|
57
70
|
vertexCount: 0,
|
|
@@ -60,6 +73,7 @@ function emptyCommand(): DrawCommand {
|
|
|
60
73
|
count: 0,
|
|
61
74
|
instances: 1,
|
|
62
75
|
indirect: null,
|
|
76
|
+
bundles: null,
|
|
63
77
|
};
|
|
64
78
|
}
|
|
65
79
|
|
|
@@ -72,14 +86,18 @@ export function createCommandPool(capacity: number): CommandPool {
|
|
|
72
86
|
/**
|
|
73
87
|
* Hand out the next command, growing if a frame overruns.
|
|
74
88
|
*
|
|
75
|
-
* The entry is **not cleared
|
|
76
|
-
* be a write per field per draw for no reader — the same reasoning the node
|
|
89
|
+
* The entry is **not cleared**, but for `bundles`. Every caller fills every field it uses, and
|
|
90
|
+
* clearing here would be a write per field per draw for no reader — the same reasoning the node
|
|
91
|
+
* arena uses. `bundles` is the exception because almost no caller knows it exists: a pooled command
|
|
92
|
+
* that once carried a static list's bundles and was then taken for an ordinary draw would replay the
|
|
93
|
+
* list instead, so the one field nobody fills is the one field put back here.
|
|
77
94
|
*/
|
|
78
95
|
export function takeCommand(pool: CommandPool): number {
|
|
79
96
|
const at = pool.taken;
|
|
80
97
|
while (at >= pool.commands.length) pool.commands.push(emptyCommand());
|
|
81
98
|
pool.taken = at + 1;
|
|
82
99
|
if (pool.taken > pool.highWater) pool.highWater = pool.taken;
|
|
100
|
+
(pool.commands[at] as DrawCommand).bundles = null;
|
|
83
101
|
return at;
|
|
84
102
|
}
|
|
85
103
|
|