@woosh/meep-engine 3.31.0 → 3.32.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/build/bundle-worker-terrain.js +1 -1
- package/package.json +1 -1
- package/src/engine/graphics3/DebugDrawSystem.d.ts.map +1 -1
- package/src/engine/graphics3/DebugDrawSystem.js +6 -2
- package/src/engine/graphics3/PathDisplaySystem.d.ts.map +1 -1
- package/src/engine/graphics3/PathDisplaySystem.js +311 -307
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/RENDERER_CONTRACT.md +15 -2
- package/src/shade/device/IMMEDIATE_DATA_PLAN_2026_08_28.md +5 -0
- package/src/shade/device/ShadeGPUCommandContext.d.ts +15 -2
- package/src/shade/device/ShadeGPUCommandContext.d.ts.map +1 -1
- package/src/shade/device/ShadeGPUCommandContext.js +154 -25
- package/src/shade/renderer/GraphicsContext.d.ts +9 -1
- package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
- package/src/shade/renderer/GraphicsContext.js +54 -15
- package/src/shade/renderer/Renderer.d.ts +19 -4
- package/src/shade/renderer/Renderer.d.ts.map +1 -1
- package/src/shade/renderer/Renderer.js +113 -2
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts +10 -7
- package/src/shade/renderer/animation/GPUAnimationManager.d.ts.map +1 -1
- package/src/shade/renderer/animation/GPUAnimationManager.js +147 -45
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts +11 -2
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.d.ts.map +1 -1
- package/src/shade/renderer/buffer/GPUBufferStagingAllocator.js +95 -13
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts +60 -0
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.d.ts.map +1 -0
- package/src/shade/renderer/buffer/GPUBufferUploadAllocator.js +202 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.d.ts +68 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.d.ts.map +1 -0
- package/src/shade/renderer/buffer/GPUUploadBlock.js +97 -0
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts +20 -7
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.d.ts.map +1 -1
- package/src/shade/renderer/dynamic/GPUDynamicMeshRenderer.js +20 -7
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts +5 -0
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.d.ts.map +1 -1
- package/src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js +19 -0
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts +3 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js +3 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js +134 -88
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js +57 -26
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js +28 -20
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts +38 -0
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.d.ts.map +1 -0
- package/src/shade/renderer/rasterize/graph_clear_raster_targets.js +105 -0
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_partial_opaque.js +21 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.d.ts.map +1 -1
- package/src/shade/renderer/rasterize/standard/graph_rasterize_scene.js +12 -20
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts +2 -1
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.d.ts.map +1 -1
- package/src/shade/renderer/shader/graph/graph_import_typed_buffer.js +15 -28
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts +0 -73
- package/src/core/geom/3d/atlas/atlas_bench_lib.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts +0 -59
- package/src/engine/graphics3/terrain/GPUTerrainSplatRenderer.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts +0 -22
- package/src/engine/graphics3/terrain/pack_terrain_row_table.d.ts.map +0 -1
- package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts +0 -26
- package/src/engine/graphics3/terrain/shader_terrain_splat.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts +0 -25
- package/src/shade/renderer/animation/ANIMATION_BOUND_TRACK_GROUP_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts +0 -26
- package/src/shade/renderer/animation/ANIMATION_KEYFRAME_BLOCK_STRUCT.d.ts.map +0 -1
- package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts +0 -29
- package/src/shade/renderer/animation/ANIMATION_SKIN_JOINT_BLOCK_STRUCT.d.ts.map +0 -1
|
@@ -30,6 +30,11 @@ export class GPUDynamicMeshRenderer {
|
|
|
30
30
|
* Set before the first draw. Changing it later rebuilds the pipeline, which is cheap but not
|
|
31
31
|
* free, and the whole batch moves together — this is a property of the pass, not of a mesh.
|
|
32
32
|
*
|
|
33
|
+
* Solid content belongs at `FramePhase.AfterLighting`, with the depth {@link graph_draw} hands
|
|
34
|
+
* back published: transparency runs after that phase and tests against that depth, so glass in
|
|
35
|
+
* front of the batch covers it. At `AfterTransparency` the glass is already resolved, and the
|
|
36
|
+
* batch is drawn over it.
|
|
37
|
+
*
|
|
33
38
|
* @type {boolean}
|
|
34
39
|
*/
|
|
35
40
|
opaque = false;
|
|
@@ -52,11 +57,16 @@ export class GPUDynamicMeshRenderer {
|
|
|
52
57
|
/**
|
|
53
58
|
* Record the draws into `graph`, over the colour it is given.
|
|
54
59
|
*
|
|
55
|
-
*
|
|
56
|
-
*
|
|
57
|
-
*
|
|
58
|
-
*
|
|
59
|
-
*
|
|
60
|
+
* **Both handles come back, and the caller publishes both** — into `SceneColor` and
|
|
61
|
+
* `ViewTextures` from an extension. The colour is always replaced. The depth is replaced when
|
|
62
|
+
* {@link opaque} is set, since the pass writes it; otherwise it comes back as it was given.
|
|
63
|
+
*
|
|
64
|
+
* A written handle is a renamed clone of the same texture, so a pass that goes on reading the
|
|
65
|
+
* depth handle it held before this draw is reading a version this pass has superseded.
|
|
66
|
+
* `FrameGraph.validate` refuses that, and the frame fails to compile in a development build.
|
|
67
|
+
* A stripped build reads the right contents only because passes run in the order they were
|
|
68
|
+
* recorded. So every reader of the scene depth after this draw has to be handed the new handle:
|
|
69
|
+
* depth of field, motion blur, the NSS upscaler, the next extension along.
|
|
60
70
|
*
|
|
61
71
|
* @param {object} args
|
|
62
72
|
* @param {FrameGraph} args.graph
|
|
@@ -64,7 +74,7 @@ export class GPUDynamicMeshRenderer {
|
|
|
64
74
|
* @param {number} args.color colour resource handle to draw over
|
|
65
75
|
* @param {number} args.depth scene depth, written when {@link opaque} and otherwise only tested
|
|
66
76
|
* @param {GPUBuffer} args.camera
|
|
67
|
-
* @returns {number} the colour
|
|
77
|
+
* @returns {{color: number, depth: number}} the colour and depth handles after the draws
|
|
68
78
|
*/
|
|
69
79
|
graph_draw({ graph, batch, color, depth, camera }) {
|
|
70
80
|
assert.defined(graph, 'graph');
|
|
@@ -86,7 +96,10 @@ export class GPUDynamicMeshRenderer {
|
|
|
86
96
|
data.depth = data.opaque ? pass.write(depth) : pass.read(depth);
|
|
87
97
|
data.color = pass.write(pass.read(color));
|
|
88
98
|
|
|
89
|
-
return
|
|
99
|
+
return {
|
|
100
|
+
color: data.color,
|
|
101
|
+
depth: data.depth,
|
|
102
|
+
};
|
|
90
103
|
}
|
|
91
104
|
|
|
92
105
|
/**
|
|
@@ -1,4 +1,9 @@
|
|
|
1
1
|
/**
|
|
2
|
+
* Needs meshoptimizer's WASM, which instantiates asynchronously at import. A build before it has
|
|
3
|
+
* fails inside the vendored module, on an instance that is not there yet, with a message about
|
|
4
|
+
* `exports` that names nothing the caller did — so await `MeshoptClusterizer.ready` before the
|
|
5
|
+
* first call.
|
|
6
|
+
*
|
|
2
7
|
* @param {RowFirstTable} result_metadata
|
|
3
8
|
* @param {BinaryBuffer} result_data
|
|
4
9
|
* @param {number[]} input_index Index array
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"geometry_to_meshlets_meshoptimizer.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"geometry_to_meshlets_meshoptimizer.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/geometry/meshlet/build/meshoptimizer/geometry_to_meshlets_meshoptimizer.js"],"names":[],"mappings":"AAgBA;;;;;;;;;;;;;;GAcG;AACH,2HAPW,MAAM,EAAE,qBACR,MAAM,gBACN,MAAM,oBACN,YAAY,yBACZ,MAAM,2BACN,MAAM,QAmDhB"}
|
|
@@ -1,7 +1,25 @@
|
|
|
1
|
+
import { assert } from "../../../../../../core/assert.js";
|
|
1
2
|
import { MeshoptClusterizer } from "./meshopt_clusterizer.js";
|
|
2
3
|
import { meshopt_optimizeMeshlet } from "./meshopt_optimizeMeshlet.js";
|
|
3
4
|
|
|
4
5
|
/**
|
|
6
|
+
* Whether meshoptimizer's WASM has landed. The vendored module instantiates it asynchronously at
|
|
7
|
+
* import and exposes only the promise, so this holds the promise's outcome for the assertion below.
|
|
8
|
+
*
|
|
9
|
+
* @type {boolean}
|
|
10
|
+
*/
|
|
11
|
+
let clusterizer_ready = false;
|
|
12
|
+
|
|
13
|
+
MeshoptClusterizer.ready?.then(() => {
|
|
14
|
+
clusterizer_ready = true;
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Needs meshoptimizer's WASM, which instantiates asynchronously at import. A build before it has
|
|
19
|
+
* fails inside the vendored module, on an instance that is not there yet, with a message about
|
|
20
|
+
* `exports` that names nothing the caller did — so await `MeshoptClusterizer.ready` before the
|
|
21
|
+
* first call.
|
|
22
|
+
*
|
|
5
23
|
* @param {RowFirstTable} result_metadata
|
|
6
24
|
* @param {BinaryBuffer} result_data
|
|
7
25
|
* @param {number[]} input_index Index array
|
|
@@ -21,6 +39,7 @@ export function geometry_to_meshlets_meshoptimizer(
|
|
|
21
39
|
meshlet_vertex_limit = 64,
|
|
22
40
|
meshlet_triangle_limit = 64,
|
|
23
41
|
) {
|
|
42
|
+
assert.ok(clusterizer_ready, 'meshoptimizer is not ready; await MeshoptClusterizer.ready');
|
|
24
43
|
|
|
25
44
|
const meshlets = MeshoptClusterizer.buildMeshlets(input_index, vertex_positions, 3, meshlet_vertex_limit, meshlet_triangle_limit, 0.0);
|
|
26
45
|
|
|
@@ -13,7 +13,7 @@ export class TinyGltf {
|
|
|
13
13
|
* @readonly
|
|
14
14
|
* @type {TextureFormat}
|
|
15
15
|
*/
|
|
16
|
-
static readonly "__#
|
|
16
|
+
static readonly "__#251@#UNCOMPRESSED_TARGET": TextureFormat;
|
|
17
17
|
/**
|
|
18
18
|
* @param {AssetManager} assetManager
|
|
19
19
|
* @param {AssetRequestScope} [scope]
|
|
@@ -53,7 +53,7 @@ export class NSS {
|
|
|
53
53
|
* @returns {number}
|
|
54
54
|
*/
|
|
55
55
|
static recommended_jitter_sequence_size(upscale_ratio: number): number;
|
|
56
|
-
static "__#
|
|
56
|
+
static "__#185@#preprocess_tensor_descriptor"(render_w: any, render_h: any): TextureResourceDescriptor;
|
|
57
57
|
/**
|
|
58
58
|
* @param {GraphicsContext} graphics
|
|
59
59
|
*/
|
|
@@ -9,7 +9,9 @@
|
|
|
9
9
|
* and every pixel the alpha-tested pass did not cover keeping last frame's mesh and triangle ids for
|
|
10
10
|
* the deferred shading to resolve as real surfaces.
|
|
11
11
|
*
|
|
12
|
-
*
|
|
12
|
+
* The composers used this to hand the clear to the first alpha-tested draw, which left a scene with
|
|
13
|
+
* no draw of either kind uncleared. They no longer ask: the opaque loop knows whether it drew, and
|
|
14
|
+
* when it did not, `graph_clear_raster_targets` clears in its first draw's place.
|
|
13
15
|
*
|
|
14
16
|
* @param {Set<number>} active_buckets bucket ids any material in the scene asks for
|
|
15
17
|
* @returns {boolean}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"rasterization_has_opaque_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js"],"names":[],"mappings":"AAEA
|
|
1
|
+
{"version":3,"file":"rasterization_has_opaque_bucket.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/bucket/rasterization_has_opaque_bucket.js"],"names":[],"mappings":"AAEA;;;;;;;;;;;;;;;;;;;;GAoBG;AACH,gEANW,IAAI,MAAM,CAAC,GACT,OAAO,CAWnB"}
|
|
@@ -11,7 +11,9 @@ import { rasterization_opaque_buckets } from "./rasterization_opaque_buckets.js"
|
|
|
11
11
|
* and every pixel the alpha-tested pass did not cover keeping last frame's mesh and triangle ids for
|
|
12
12
|
* the deferred shading to resolve as real surfaces.
|
|
13
13
|
*
|
|
14
|
-
*
|
|
14
|
+
* The composers used this to hand the clear to the first alpha-tested draw, which left a scene with
|
|
15
|
+
* no draw of either kind uncleared. They no longer ask: the opaque loop knows whether it drew, and
|
|
16
|
+
* when it did not, `graph_clear_raster_targets` clears in its first draw's place.
|
|
15
17
|
*
|
|
16
18
|
* @param {Set<number>} active_buckets bucket ids any material in the scene asks for
|
|
17
19
|
* @returns {boolean}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_instances_rasterization_bucket_sort.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shader_instances_rasterization_bucket_sort.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/rasterize/expand/bucket/mesh/shader_instances_rasterization_bucket_sort.js"],"names":[],"mappings":"AAkBA,yEAGG;AA6GH,uEAIE;6BAnI2B,yCAAyC;8BAMxC,qCAAqC"}
|
|
@@ -1,89 +1,135 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import {
|
|
11
|
-
import {
|
|
12
|
-
import {
|
|
13
|
-
import {
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
resources
|
|
27
|
-
|
|
28
|
-
.addStorageBuffer("
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
.addStorageBuffer("
|
|
33
|
-
.addStorageBuffer("
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
1
|
+
import { assert } from "../../../../../../core/assert.js";
|
|
2
|
+
import { u32 } from "../../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
3
|
+
import { WebGPUArray } from "../../../../shader/type/WebGPUArray.js";
|
|
4
|
+
import { WebGPUStruct } from "../../../../shader/type/WebGPUStruct.js";
|
|
5
|
+
import { COLLECTION_U32_STRUCT_READ } from "../../../../gpu_primitive/COLLECTION_U32_STRUCT_READ.js";
|
|
6
|
+
import { MATERIAL_METADATA_STRUCT } from "../../../../material/MATERIAL_METADATA_STRUCT.js";
|
|
7
|
+
import { chunk_scene_database_access } from "../../../../scene/chunk_scene_database_access.js";
|
|
8
|
+
import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
|
|
9
|
+
import { COMPUTE_SHADER_DEFAULT_GROUP_SIZE } from "../../../../shader/COMPUTE_SHADER_DEFAULT_GROUP_SIZE.js";
|
|
10
|
+
import { ComputeShader } from "../../../../shader/ComputeShader.js";
|
|
11
|
+
import { ShaderResourceSetDescriptor } from "../../../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
12
|
+
import { chunk_rasterization_material_bucket } from "../chunk_rasterization_material_bucket.js";
|
|
13
|
+
import { RASTERIZATION_BUCKET_LIMIT } from "../RASTERIZATION_BUCKET_LIMIT.js";
|
|
14
|
+
import {
|
|
15
|
+
RASTERIZATION_BUCKET_SORT_OUTPUT_HEADER_STRUCT_WRITE
|
|
16
|
+
} from "../RASTERIZATION_BUCKET_SORT_OUTPUT_HEADER_STRUCT_WRITE.js";
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
export const RASTERIZATION_BUCKET_SORT_OUTPUT_STRUCT_WRITE = WebGPUStruct.from({
|
|
20
|
+
headers: RASTERIZATION_BUCKET_SORT_OUTPUT_HEADER_STRUCT_WRITE,
|
|
21
|
+
elements: WebGPUArray.u32
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
const OFFSETS_TYPE = WebGPUArray.from(u32, RASTERIZATION_BUCKET_LIMIT);
|
|
25
|
+
|
|
26
|
+
const resources = new ShaderResourceSetDescriptor();
|
|
27
|
+
resources.createGroup()
|
|
28
|
+
.addStorageBuffer("scene_database", WebGPUArray.from(u32))
|
|
29
|
+
.addStorageBuffer("materials", WebGPUArray.from(MATERIAL_METADATA_STRUCT))
|
|
30
|
+
|
|
31
|
+
resources.createGroup()
|
|
32
|
+
.addStorageBuffer("input", COLLECTION_U32_STRUCT_READ)
|
|
33
|
+
.addStorageBuffer("offsets", OFFSETS_TYPE)
|
|
34
|
+
.addStorageBuffer("output", RASTERIZATION_BUCKET_SORT_OUTPUT_STRUCT_WRITE, true)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
const GROUP_SIZE = COMPUTE_SHADER_DEFAULT_GROUP_SIZE;
|
|
38
|
+
|
|
39
|
+
// one lane per bucket makes the workgroup's claims
|
|
40
|
+
assert.lessThanOrEqual(RASTERIZATION_BUCKET_LIMIT, GROUP_SIZE);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* Every element used to claim its slot with an `atomicAdd(&output.headers[bucket].count, 1u)` of
|
|
44
|
+
* its own. The address comes from the material, so nothing aggregated it, and every survivor of a
|
|
45
|
+
* view queued on its bucket's one counter: 0.47-0.80 ns a survivor, the largest non-raster pass of
|
|
46
|
+
* a view.
|
|
47
|
+
*
|
|
48
|
+
* Instead each lane ranks itself within its workgroup, in workgroup memory, and one lane per bucket
|
|
49
|
+
* claims the workgroup's whole run with a single global atomic. Measured on a 4090 against
|
|
50
|
+
* aggregating per subgroup (a ballot per bit of the bucket index, one atomic per bucket a wave
|
|
51
|
+
* holds), at 2M rows: 62 against 63 us for 1.48M elements of one bucket, 13 against 22 us for
|
|
52
|
+
* the main view's 774K, and 62 against 107 us for the 1.48M with six buckets to a wave. Waves on
|
|
53
|
+
* mixed content hold several buckets, and a workgroup claims each of them a quarter as often.
|
|
54
|
+
*/
|
|
55
|
+
const body = CodeChunk.from(
|
|
56
|
+
//language=WGSL
|
|
57
|
+
`
|
|
58
|
+
var<workgroup> wg_count: array<atomic<u32>, ${RASTERIZATION_BUCKET_LIMIT}>;
|
|
59
|
+
var<workgroup> wg_base: array<u32, ${RASTERIZATION_BUCKET_LIMIT}>;
|
|
60
|
+
|
|
61
|
+
@compute @workgroup_size(${GROUP_SIZE})
|
|
62
|
+
fn main(
|
|
63
|
+
@builtin(global_invocation_id) global_id : vec3<u32>,
|
|
64
|
+
@builtin(local_invocation_index) local_index : u32,
|
|
65
|
+
){
|
|
66
|
+
|
|
67
|
+
let index = global_id.x;
|
|
68
|
+
|
|
69
|
+
let input_size = min(input.count, arrayLength(&input.elements));
|
|
70
|
+
|
|
71
|
+
// a lane past the input does not return: every lane of the workgroup has to reach the barriers
|
|
72
|
+
let valid = index < input_size;
|
|
73
|
+
|
|
74
|
+
var mesh_index = 0u;
|
|
75
|
+
var bucket_index = 0u;
|
|
76
|
+
var rank = 0u;
|
|
77
|
+
|
|
78
|
+
if(valid){
|
|
79
|
+
mesh_index = input.elements[index];
|
|
80
|
+
|
|
81
|
+
let mesh = scene_read_mesh(&scene_database, mesh_index);
|
|
82
|
+
|
|
83
|
+
let material = materials[mesh.material];
|
|
84
|
+
|
|
85
|
+
bucket_index = rasterization_material_bucket(
|
|
86
|
+
material.transparency_mode,
|
|
87
|
+
material.draw_mode,
|
|
88
|
+
material.draw_side
|
|
89
|
+
);
|
|
90
|
+
|
|
91
|
+
// this element's place in the workgroup's run of its bucket
|
|
92
|
+
rank = atomicAdd(&wg_count[bucket_index], 1u);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
workgroupBarrier();
|
|
96
|
+
|
|
97
|
+
// one lane per bucket claims the workgroup's whole run with one atomic
|
|
98
|
+
if(local_index < ${RASTERIZATION_BUCKET_LIMIT}u){
|
|
99
|
+
let run_length = atomicLoad(&wg_count[local_index]);
|
|
100
|
+
|
|
101
|
+
if(run_length > 0u){
|
|
102
|
+
let run_offset = atomicAdd(&output.headers[local_index].count, run_length);
|
|
103
|
+
|
|
104
|
+
if(run_offset == 0u){
|
|
105
|
+
// first element in bucket, write offset as well
|
|
106
|
+
output.headers[local_index].offset = offsets[local_index];
|
|
107
|
+
}
|
|
108
|
+
|
|
109
|
+
wg_base[local_index] = run_offset;
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
workgroupBarrier();
|
|
114
|
+
|
|
115
|
+
if(valid){
|
|
116
|
+
let output_offset = offsets[bucket_index] + wg_base[bucket_index] + rank;
|
|
117
|
+
|
|
118
|
+
if(output_offset < arrayLength(&output.elements)){
|
|
119
|
+
// guard against out-of-bounds access
|
|
120
|
+
output.elements[output_offset] = mesh_index;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
}
|
|
125
|
+
`, [
|
|
126
|
+
chunk_rasterization_material_bucket,
|
|
127
|
+
chunk_scene_database_access,
|
|
128
|
+
]
|
|
129
|
+
);
|
|
130
|
+
|
|
131
|
+
export const shader_instances_rasterization_bucket_sort = ComputeShader.from({
|
|
132
|
+
label: 'Rasterization bucket sort',
|
|
133
|
+
resources,
|
|
134
|
+
body,
|
|
89
135
|
})
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"shader_meshlets_rasterization_bucket_sort.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"shader_meshlets_rasterization_bucket_sort.d.ts","sourceRoot":"","sources":["../../../../../../../../src/shade/renderer/rasterize/expand/bucket/meshlet/shader_meshlets_rasterization_bucket_sort.js"],"names":[],"mappings":"AA0HA,sEAIE;8BApH4B,qCAAqC"}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { assert } from "../../../../../../core/assert.js";
|
|
1
2
|
import { u32 } from "../../../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
2
3
|
import { WebGPUArray } from "../../../../shader/type/WebGPUArray.js";
|
|
3
4
|
import { WebGPUStruct } from "../../../../shader/type/WebGPUStruct.js";
|
|
@@ -33,52 +34,82 @@ resources.createGroup()
|
|
|
33
34
|
.addStorageBuffer("output", RASTERIZATION_MESHLET_BUCKET_SORT_OUTPUT_STRUCT_WRITE, true)
|
|
34
35
|
|
|
35
36
|
|
|
37
|
+
const GROUP_SIZE = COMPUTE_SHADER_DEFAULT_GROUP_SIZE;
|
|
38
|
+
|
|
39
|
+
// one lane per bucket makes the workgroup's claims
|
|
40
|
+
assert.lessThanOrEqual(RASTERIZATION_BUCKET_LIMIT, GROUP_SIZE);
|
|
41
|
+
|
|
42
|
+
/**
|
|
43
|
+
* The meshlet twin of {@link shader_instances_rasterization_bucket_sort}: the same workgroup
|
|
44
|
+
* histogram, for the same reason.
|
|
45
|
+
*/
|
|
36
46
|
const body = CodeChunk.from(
|
|
37
47
|
//language=WGSL
|
|
38
48
|
`
|
|
39
|
-
|
|
49
|
+
var<workgroup> wg_count: array<atomic<u32>, ${RASTERIZATION_BUCKET_LIMIT}>;
|
|
50
|
+
var<workgroup> wg_base: array<u32, ${RASTERIZATION_BUCKET_LIMIT}>;
|
|
51
|
+
|
|
52
|
+
@compute @workgroup_size(${GROUP_SIZE})
|
|
40
53
|
fn main(
|
|
41
54
|
@builtin(workgroup_id) workgroup_id : vec3<u32>,
|
|
42
55
|
@builtin(local_invocation_index) local_index : u32,
|
|
43
56
|
){
|
|
44
|
-
|
|
57
|
+
|
|
45
58
|
// folded dispatch — see IndirectComputeCommandBuildPass
|
|
46
|
-
let index = folded_workgroup_index(workgroup_id) * ${
|
|
59
|
+
let index = folded_workgroup_index(workgroup_id) * ${GROUP_SIZE}u + local_index;
|
|
47
60
|
|
|
48
61
|
let input_size = min(input.count, arrayLength(&input.elements));
|
|
49
62
|
|
|
50
|
-
|
|
51
|
-
|
|
63
|
+
// a lane past the input does not return: every lane of the workgroup has to reach the barriers
|
|
64
|
+
let valid = index < input_size;
|
|
65
|
+
|
|
66
|
+
var bucket_index = 0u;
|
|
67
|
+
var rank = 0u;
|
|
68
|
+
|
|
69
|
+
if(valid){
|
|
70
|
+
let mesh_index = input.elements[index].mesh;
|
|
71
|
+
|
|
72
|
+
let mesh = scene_read_mesh(&scene_database, mesh_index);
|
|
73
|
+
|
|
74
|
+
let material = materials[mesh.material];
|
|
75
|
+
|
|
76
|
+
bucket_index = rasterization_material_bucket(
|
|
77
|
+
material.transparency_mode,
|
|
78
|
+
material.draw_mode,
|
|
79
|
+
material.draw_side
|
|
80
|
+
);
|
|
81
|
+
|
|
82
|
+
// this element's place in the workgroup's run of its bucket
|
|
83
|
+
rank = atomicAdd(&wg_count[bucket_index], 1u);
|
|
52
84
|
}
|
|
53
85
|
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
let mesh_index = meshlet_def.mesh;
|
|
57
|
-
|
|
58
|
-
let mesh = scene_read_mesh(&scene_database, mesh_index);
|
|
59
|
-
|
|
60
|
-
let material = materials[mesh.material];
|
|
86
|
+
workgroupBarrier();
|
|
61
87
|
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
material.draw_side
|
|
66
|
-
);
|
|
88
|
+
// one lane per bucket claims the workgroup's whole run with one atomic
|
|
89
|
+
if(local_index < ${RASTERIZATION_BUCKET_LIMIT}u){
|
|
90
|
+
let run_length = atomicLoad(&wg_count[local_index]);
|
|
67
91
|
|
|
68
|
-
|
|
92
|
+
if(run_length > 0u){
|
|
93
|
+
let run_offset = atomicAdd(&output.headers[local_index].count, run_length);
|
|
69
94
|
|
|
70
|
-
|
|
95
|
+
if(run_offset == 0u){
|
|
96
|
+
// first element in bucket, write offset as well
|
|
97
|
+
output.headers[local_index].offset = offsets[local_index];
|
|
98
|
+
}
|
|
71
99
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
output.headers[bucket_index].offset = bucket_offset;
|
|
100
|
+
wg_base[local_index] = run_offset;
|
|
101
|
+
}
|
|
75
102
|
}
|
|
76
103
|
|
|
77
|
-
|
|
104
|
+
workgroupBarrier();
|
|
105
|
+
|
|
106
|
+
if(valid){
|
|
107
|
+
let output_offset = offsets[bucket_index] + wg_base[bucket_index] + rank;
|
|
78
108
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
109
|
+
if(output_offset < arrayLength(&output.elements)){
|
|
110
|
+
// guard against out-of-bounds access
|
|
111
|
+
output.elements[output_offset] = input.elements[index];
|
|
112
|
+
}
|
|
82
113
|
}
|
|
83
114
|
|
|
84
115
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"graph_rasterize_scene_fast.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AACH;;;;;;;IAFa;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,
|
|
1
|
+
{"version":3,"file":"graph_rasterize_scene_fast.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/rasterize/fast/graph_rasterize_scene_fast.js"],"names":[],"mappings":"AAoBA;;;;;;;;;;GAUG;AACH;;;;;;;IAFa;IAAC,YAAY,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,EAAE,CAAA;CAAC,CAkK3D;AAED;;;;;;;;;;;;;;;;;;;GAmBG;AACH;IAJ+B,KAAK;IACC,GAAG;IACA,MAAM;SA8C7C"}
|
|
@@ -13,8 +13,8 @@ import { graph_vgeo_expand_if_enabled } from "../../geometry/virtual/runtime/gra
|
|
|
13
13
|
import { RasterizationCollectionType } from "../expand/RasterizationCollectionType.js";
|
|
14
14
|
import { construct_primitive_state } from "../bucket/construct_primitive_state.js";
|
|
15
15
|
import { rasterization_opaque_buckets } from "../bucket/rasterization_opaque_buckets.js";
|
|
16
|
-
import { rasterization_has_opaque_bucket } from "../bucket/rasterization_has_opaque_bucket.js";
|
|
17
16
|
import { graph_mesh_frustum_cull } from "../compute/graph_mesh_frustum_cull.js";
|
|
17
|
+
import { graph_clear_raster_targets } from "../graph_clear_raster_targets.js";
|
|
18
18
|
import { graph_rasterize_meshes_alpha_tested } from "../native/graph_rasterize_meshes_alpha_tested.js";
|
|
19
19
|
import { graph_extract_meshes_from_bucket } from "./graph_extract_meshes_from_bucket.js";
|
|
20
20
|
|
|
@@ -122,29 +122,39 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
122
122
|
is_first_draw = false;
|
|
123
123
|
}
|
|
124
124
|
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
let targets_cleared = rasterization_has_opaque_bucket(active_buckets);
|
|
125
|
+
if (is_first_draw) {
|
|
126
|
+
/*
|
|
127
|
+
No opaque bucket, so no draw above cleared the target, and the rectangle holds whatever was
|
|
128
|
+
there before: in the atlas, the shadow of whatever was in the scene then; in a point light's
|
|
129
|
+
cube, which is a pooled transient, whatever the pool's last user left. The clear goes in the
|
|
130
|
+
first draw's place, and everything after this loads.
|
|
131
|
+
*/
|
|
132
|
+
const cleared = graph_clear_raster_targets({
|
|
133
|
+
graph,
|
|
134
|
+
depth: target_depth,
|
|
135
|
+
color_targets: targets_color,
|
|
136
|
+
pass_descriptor: job.opaque_pass,
|
|
137
|
+
viewport,
|
|
138
|
+
});
|
|
140
139
|
|
|
140
|
+
target_depth = cleared.depth;
|
|
141
|
+
targets_color = cleared.color_targets;
|
|
142
|
+
}
|
|
141
143
|
|
|
144
|
+
// alpha-tested: reuse the existing native pass but skip HZB filtering by passing hzb=-1
|
|
145
|
+
if (job.alpha_tested_pass !== undefined) {
|
|
146
|
+
/*
|
|
147
|
+
Both alpha-tested passes load, never clear: the targets were cleared above, by the first
|
|
148
|
+
opaque draw or in its place. The clear used to be handed to the first alpha-tested draw, which
|
|
149
|
+
left a scene with neither kind of draw — nothing in it, or only transparent surfaces — with
|
|
150
|
+
nothing clearing at all.
|
|
151
|
+
*/
|
|
142
152
|
const alpha_tested_front = graph_rasterize_meshes_alpha_tested({
|
|
143
153
|
graph,
|
|
144
154
|
buckets: gr_render_buckets,
|
|
145
155
|
view_ctx: view_ctx,
|
|
146
156
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
147
|
-
clear_target:
|
|
157
|
+
clear_target: false,
|
|
148
158
|
side: ShadeDrawSide.Front,
|
|
149
159
|
cull_policy: job.cull_policy,
|
|
150
160
|
limits,
|
|
@@ -157,14 +167,13 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
157
167
|
|
|
158
168
|
target_depth = alpha_tested_front.depth;
|
|
159
169
|
targets_color = alpha_tested_front.color_targets;
|
|
160
|
-
targets_cleared = targets_cleared || alpha_tested_front.executed;
|
|
161
170
|
|
|
162
171
|
const alpha_tested_double = graph_rasterize_meshes_alpha_tested({
|
|
163
172
|
graph,
|
|
164
173
|
buckets: gr_render_buckets,
|
|
165
174
|
view_ctx: view_ctx,
|
|
166
175
|
render_pass_descriptor: job.alpha_tested_pass,
|
|
167
|
-
clear_target:
|
|
176
|
+
clear_target: false,
|
|
168
177
|
side: ShadeDrawSide.Double,
|
|
169
178
|
cull_policy: job.cull_policy,
|
|
170
179
|
limits,
|
|
@@ -177,7 +186,6 @@ export function graph_rasterize_scene_fast_raw({
|
|
|
177
186
|
|
|
178
187
|
target_depth = alpha_tested_double.depth;
|
|
179
188
|
targets_color = alpha_tested_double.color_targets;
|
|
180
|
-
targets_cleared = targets_cleared || alpha_tested_double.executed;
|
|
181
189
|
}
|
|
182
190
|
|
|
183
191
|
return {target_depth, targets_color};
|