@woosh/meep-engine 3.35.0 → 3.37.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/package.json +1 -1
- package/src/engine/Engine.d.ts +2 -0
- package/src/engine/Engine.d.ts.map +1 -1
- package/src/engine/Engine.js +5 -1
- package/src/engine/input/devices/XRInputDevice.d.ts +91 -0
- package/src/engine/input/devices/XRInputDevice.d.ts.map +1 -0
- package/src/engine/input/devices/XRInputDevice.js +479 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.d.ts +14 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRGamepadMapping.js +9 -0
- package/src/engine/input/devices/xr/XRHandedness.d.ts +15 -0
- package/src/engine/input/devices/xr/XRHandedness.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRHandedness.js +10 -0
- package/src/engine/input/devices/xr/XRInputAction.d.ts +50 -0
- package/src/engine/input/devices/xr/XRInputAction.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputAction.js +116 -0
- package/src/engine/input/devices/xr/XRInputButton.d.ts +26 -0
- package/src/engine/input/devices/xr/XRInputButton.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputButton.js +53 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.d.ts +23 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputCoordinates.js +35 -0
- package/src/engine/input/devices/xr/XRInputHandle.d.ts +93 -0
- package/src/engine/input/devices/xr/XRInputHandle.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputHandle.js +268 -0
- package/src/engine/input/devices/xr/XRInputPose.d.ts +59 -0
- package/src/engine/input/devices/xr/XRInputPose.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputPose.js +77 -0
- package/src/engine/input/devices/xr/XRInputTypes.d.ts +76 -0
- package/src/engine/input/devices/xr/XRInputTypes.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputTypes.js +31 -0
- package/src/engine/input/devices/xr/XRInputValueSource.d.ts +11 -0
- package/src/engine/input/devices/xr/XRInputValueSource.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRInputValueSource.js +11 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.d.ts +17 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRTargetRayMode.js +12 -0
- package/src/engine/input/devices/xr/XRVisibilityState.d.ts +15 -0
- package/src/engine/input/devices/xr/XRVisibilityState.d.ts.map +1 -0
- package/src/engine/input/devices/xr/XRVisibilityState.js +10 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts +15 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateChannel.js +9 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts +17 -4
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateDevice.js +26 -0
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts +17 -2
- package/src/engine/input/ecs/ism/coordinate/InputCoordinateSwitches.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/GamepadInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts +2 -0
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/InputDeviceAdapter.js +2 -0
- package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts +7 -0
- package/src/engine/input/ecs/ism/device/KeyboardInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/ManualInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts +5 -0
- package/src/engine/input/ecs/ism/device/PointerInputDeviceAdapter.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts +30 -0
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/device/XRInputDeviceAdapter.js +87 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts +15 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/device/XR_INPUT_DEVICE_IDS.js +22 -0
- package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts +11 -1
- package/src/engine/input/ecs/ism/trigger/InputTrigger.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTrigger.js +7 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts +16 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerChord.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts +22 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerHold.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts +16 -2
- package/src/engine/input/ecs/ism/trigger/InputTriggerRelease.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts +24 -4
- package/src/engine/input/ecs/ism/trigger/InputTriggerSequence.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts +6 -8
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.d.ts.map +1 -1
- package/src/engine/input/ecs/ism/trigger/InputTriggerSwitch.js +5 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts +15 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.d.ts.map +1 -0
- package/src/engine/input/ecs/ism/trigger/InputTriggerXRButton.js +25 -0
- package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
- package/src/shade/renderer/animation/chunk_pose_accumulator.js +14 -24
- package/src/shade/renderer/animation/skinning/bounds_refresh/shader_meshlet_bounds_reduce.js +16 -15
- package/src/shade/renderer/deferred/indirect/chunk_lpv_sample_light.js +1 -1
- package/src/shade/renderer/gpu_primitive/bvh/GPU_BVH_BUILD_PLAN.md +1123 -1122
- package/src/shade/renderer/gpu_primitive/bvh/PHASE1_HANDOVER.md +202 -201
- package/src/shade/renderer/light/environment/chunk_roughness_to_mip_ratio.js +24 -0
- package/src/shade/renderer/light/environment/chunk_sphere_probe_roughness_to_lod.js +2 -25
- package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts +2 -2
- package/src/shade/renderer/light/environment/shader_project_environment_sh.d.ts.map +1 -1
- package/src/shade/renderer/light/environment/shader_project_environment_sh.js +50 -19
- package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
- package/src/shade/renderer/particles/DESIGN.md +1 -1
- package/src/shade/renderer/particles/bounds/chunk_particle_bounds.js +10 -9
- package/src/shade/renderer/scene/shader_bvh_extract_instance_leaves.js +10 -8
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_add_f32_storage.js +25 -0
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_max_f32_storage.js +28 -0
- package/src/shade/renderer/shader/chunk/atomic/chunk_atomic_min_f32_storage.js +28 -0
- package/src/shade/renderer/shader/chunk/geometry/plane/{plane_ray_raycast.js → chunk_plane_ray_raycast.js} +1 -1
- package/src/shade/renderer/shadow/map/csm/shader/shader_csm_caster_range.js +3 -3
- package/src/shade/renderer/animation/skinning/bounds_refresh/chunk_aabb_atomic_f32.js +0 -59
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { WebGPUExtensionType } from "../../../descriptor/WebGPUExtensionType.js";
|
|
1
2
|
import { chunk_get_ibl_irradiance } from "../../deferred/indirect/chunk_get_ibl_irradiance.js";
|
|
2
3
|
import { PI_DECLARATION } from "../../shader/chunk/math/PI_DECLARATION.js";
|
|
3
4
|
import { chunk_sphere_fibonacci_point } from "../../shader/chunk/geometry/sphere/chunk_sphere_fibonacci_point.js";
|
|
@@ -21,12 +22,12 @@ export const ENVIRONMENT_SH_TYPE = WebGPUArray.from(vec4f, 4);
|
|
|
21
22
|
|
|
22
23
|
/**
|
|
23
24
|
* Directions the projection samples, spread evenly over the sphere (`sphere_fibonacci_point`).
|
|
24
|
-
* The field is
|
|
25
|
-
*
|
|
25
|
+
* The field is already prefiltered at the roughness diffuse reads it at; these samples project
|
|
26
|
+
* that smooth field into two bands. The pass runs only when the environment changes.
|
|
26
27
|
*
|
|
27
28
|
* @type {number}
|
|
28
29
|
*/
|
|
29
|
-
export const ENVIRONMENT_SH_SAMPLE_COUNT =
|
|
30
|
+
export const ENVIRONMENT_SH_SAMPLE_COUNT = 256;
|
|
30
31
|
|
|
31
32
|
const resources = new ShaderResourceSetDescriptor();
|
|
32
33
|
|
|
@@ -39,28 +40,56 @@ const body = CodeChunk.from(
|
|
|
39
40
|
//language=WGSL
|
|
40
41
|
`
|
|
41
42
|
const SAMPLE_COUNT : u32 = ${ENVIRONMENT_SH_SAMPLE_COUNT}u;
|
|
43
|
+
// The minimum subgroup size is four, so this holds every subgroup's partial sum.
|
|
44
|
+
const MAX_SUBGROUP_PARTIALS : u32 = SAMPLE_COUNT / 4u;
|
|
45
|
+
|
|
46
|
+
var<workgroup> wg_partial_count: atomic<u32>;
|
|
47
|
+
var<workgroup> wg_sh_0: array<vec3<f32>, MAX_SUBGROUP_PARTIALS>;
|
|
48
|
+
var<workgroup> wg_sh_1: array<vec3<f32>, MAX_SUBGROUP_PARTIALS>;
|
|
49
|
+
var<workgroup> wg_sh_2: array<vec3<f32>, MAX_SUBGROUP_PARTIALS>;
|
|
50
|
+
var<workgroup> wg_sh_3: array<vec3<f32>, MAX_SUBGROUP_PARTIALS>;
|
|
51
|
+
|
|
52
|
+
// One workgroup projects the sphere, with one invocation per sample.
|
|
53
|
+
@compute @workgroup_size(${ENVIRONMENT_SH_SAMPLE_COUNT})
|
|
54
|
+
fn main(@builtin(local_invocation_index) thread_index: u32) {
|
|
55
|
+
let direction = sphere_fibonacci_point(f32(thread_index), f32(SAMPLE_COUNT));
|
|
56
|
+
|
|
57
|
+
// exactly what the forward mesh path takes as a surface's diffuse irradiance under IBL
|
|
58
|
+
let irradiance = get_ibl_irradiance(direction);
|
|
59
|
+
let basis = sh2_basis_at(direction);
|
|
60
|
+
|
|
61
|
+
let sh_0_partial = subgroupAdd(irradiance * basis[0]);
|
|
62
|
+
let sh_1_partial = subgroupAdd(irradiance * basis[1]);
|
|
63
|
+
let sh_2_partial = subgroupAdd(irradiance * basis[2]);
|
|
64
|
+
let sh_3_partial = subgroupAdd(irradiance * basis[3]);
|
|
65
|
+
|
|
66
|
+
// Workgroup variables start zeroed. One elected lane claims a distinct slot per subgroup;
|
|
67
|
+
// local invocation indices need not map to subgroups in contiguous blocks.
|
|
68
|
+
if (subgroupElect()) {
|
|
69
|
+
let slot = atomicAdd(&wg_partial_count, 1u);
|
|
70
|
+
wg_sh_0[slot] = sh_0_partial;
|
|
71
|
+
wg_sh_1[slot] = sh_1_partial;
|
|
72
|
+
wg_sh_2[slot] = sh_2_partial;
|
|
73
|
+
wg_sh_3[slot] = sh_3_partial;
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
workgroupBarrier();
|
|
77
|
+
|
|
78
|
+
if (thread_index != 0u) {
|
|
79
|
+
return;
|
|
80
|
+
}
|
|
42
81
|
|
|
43
|
-
// One invocation: the projection is 1024 reads that happen when the environment changes and never
|
|
44
|
-
// otherwise, and a single lane needs no reduction and runs whole on the emulator.
|
|
45
|
-
@compute @workgroup_size(1)
|
|
46
|
-
fn main() {
|
|
47
82
|
var sh_0 = vec3<f32>(0.0);
|
|
48
83
|
var sh_1 = vec3<f32>(0.0);
|
|
49
84
|
var sh_2 = vec3<f32>(0.0);
|
|
50
85
|
var sh_3 = vec3<f32>(0.0);
|
|
86
|
+
let partial_count = atomicLoad(&wg_partial_count);
|
|
51
87
|
|
|
52
|
-
for (var i = 0u; i <
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
let basis = sh2_basis_at(direction);
|
|
59
|
-
|
|
60
|
-
sh_0 += irradiance * basis[0];
|
|
61
|
-
sh_1 += irradiance * basis[1];
|
|
62
|
-
sh_2 += irradiance * basis[2];
|
|
63
|
-
sh_3 += irradiance * basis[3];
|
|
88
|
+
for (var i = 0u; i < partial_count; i++) {
|
|
89
|
+
sh_0 += wg_sh_0[i];
|
|
90
|
+
sh_1 += wg_sh_1[i];
|
|
91
|
+
sh_2 += wg_sh_2[i];
|
|
92
|
+
sh_3 += wg_sh_3[i];
|
|
64
93
|
}
|
|
65
94
|
|
|
66
95
|
// each sample stands for an equal share of the sphere
|
|
@@ -83,6 +112,8 @@ fn main() {
|
|
|
83
112
|
PI_DECLARATION,
|
|
84
113
|
]);
|
|
85
114
|
|
|
115
|
+
body.addExtension(WebGPUExtensionType.Subgroups);
|
|
116
|
+
|
|
86
117
|
/**
|
|
87
118
|
* The environment as RGB SH2 of radiance ({@link ENVIRONMENT_SH_TYPE}): the form the renderer's other
|
|
88
119
|
* indirect lighting — Brick4, the light probe volume — already hands over, so the froxel GI stage
|
|
@@ -13,7 +13,7 @@ export class TinyGltf {
|
|
|
13
13
|
* @readonly
|
|
14
14
|
* @type {TextureFormat}
|
|
15
15
|
*/
|
|
16
|
-
static readonly "__#
|
|
16
|
+
static readonly "__#254@#UNCOMPRESSED_TARGET": TextureFormat;
|
|
17
17
|
/**
|
|
18
18
|
* @param {AssetManager} assetManager
|
|
19
19
|
* @param {AssetRequestScope} [scope]
|
|
@@ -353,7 +353,7 @@ finished appending to the alive list:
|
|
|
353
353
|
`particle_billboard_radius`, the largest offset `particle_billboard_offset` can produce for that
|
|
354
354
|
size, velocity and projection, over every corner, every roll and any camera. The box has to hold
|
|
355
355
|
the *quads*, not the centres, and it has to be camera-independent, because it is read on a later
|
|
356
|
-
frame. The merge is `
|
|
356
|
+
frame. The merge is `atomic_min_f32_storage` / `atomic_max_f32_storage`, the same CAS-spin the
|
|
357
357
|
skinned-mesh bounds refresh uses; both load first and return without a CAS once the box holds the
|
|
358
358
|
contribution, so what survives is load traffic, not exchanges. Dispatched indirectly from
|
|
359
359
|
`sim_dispatch_args`, the args the coherence reset wrote from the GPU's own alive count.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
|
|
2
|
-
import {
|
|
2
|
+
import { chunk_atomic_min_f32_storage } from "../../shader/chunk/atomic/chunk_atomic_min_f32_storage.js";
|
|
3
|
+
import { chunk_atomic_max_f32_storage } from "../../shader/chunk/atomic/chunk_atomic_max_f32_storage.js";
|
|
3
4
|
import { AABB3_STRUCT } from "../../shader/chunk/geometry/aabb3/AABB3_STRUCT.js";
|
|
4
5
|
import {
|
|
5
6
|
PARTICLE_BOUNDS_BASE,
|
|
@@ -26,7 +27,7 @@ import { chunk_particle_render_math } from "../shaders/chunk_particle_render_mat
|
|
|
26
27
|
*
|
|
27
28
|
* ## Why f32 in the buffer, and not an order-preserving encoding
|
|
28
29
|
*
|
|
29
|
-
* `
|
|
30
|
+
* `atomic_min_f32_storage` / `atomic_max_f32_storage` are the engine's, shared with the skinned-mesh
|
|
30
31
|
* bounds refresh, and they CAS-spin over the plain float so the words are readable as floats at
|
|
31
32
|
* every moment of the frame. The sign-flip encoding that would let `atomicMin` do it in one
|
|
32
33
|
* instruction leaves the buffer in a form only a decoder understands, and the publish pass reads
|
|
@@ -71,12 +72,12 @@ fn particle_bounds_contribute(row : u32, center : vec3<f32>, radius : f32) {
|
|
|
71
72
|
let lo = center - vec3<f32>(radius);
|
|
72
73
|
let hi = center + vec3<f32>(radius);
|
|
73
74
|
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
75
|
+
atomic_min_f32_storage(&counters[base + 0u], lo.x);
|
|
76
|
+
atomic_min_f32_storage(&counters[base + 1u], lo.y);
|
|
77
|
+
atomic_min_f32_storage(&counters[base + 2u], lo.z);
|
|
78
|
+
atomic_max_f32_storage(&counters[base + 3u], hi.x);
|
|
79
|
+
atomic_max_f32_storage(&counters[base + 4u], hi.y);
|
|
80
|
+
atomic_max_f32_storage(&counters[base + 5u], hi.z);
|
|
80
81
|
}
|
|
81
82
|
|
|
82
83
|
// What the frame accumulated for one row. Empty (see \`particle_bounds_is_empty\`) when nothing
|
|
@@ -117,7 +118,7 @@ fn particle_bounds_reset(row : u32) {
|
|
|
117
118
|
fn particle_bounds_is_empty(box : ${AABB3_STRUCT.wgsl_ref}) -> bool {
|
|
118
119
|
return any(box.min > box.max);
|
|
119
120
|
}
|
|
120
|
-
`, [
|
|
121
|
+
`, [chunk_atomic_min_f32_storage, chunk_atomic_max_f32_storage, AABB3_STRUCT.declaration_chunk]);
|
|
121
122
|
|
|
122
123
|
/**
|
|
123
124
|
* One live particle's contribution to its emitter's box: where its quad is, and how far the quad
|
|
@@ -1,7 +1,8 @@
|
|
|
1
1
|
import { WebGPUExtensionType } from "../../descriptor/WebGPUExtensionType.js";
|
|
2
2
|
import { atomic_u32, f32, u32 } from "../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
|
|
3
3
|
import { WebGPUArray } from "../shader/type/WebGPUArray.js";
|
|
4
|
-
import {
|
|
4
|
+
import { chunk_atomic_min_f32_storage } from "../shader/chunk/atomic/chunk_atomic_min_f32_storage.js";
|
|
5
|
+
import { chunk_atomic_max_f32_storage } from "../shader/chunk/atomic/chunk_atomic_max_f32_storage.js";
|
|
5
6
|
import { CodeChunk } from "../shader/compiler/CodeChunk.js";
|
|
6
7
|
import { ComputeShader } from "../shader/ComputeShader.js";
|
|
7
8
|
import { ShaderResourceSetDescriptor } from "../shader/resource/ShaderResourceSetDescriptor.js";
|
|
@@ -107,17 +108,18 @@ fn main(
|
|
|
107
108
|
group_high = max(group_high, wg_high[s]);
|
|
108
109
|
}
|
|
109
110
|
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
111
|
+
atomic_min_f32_storage(&scene_bounds[0], group_low.x);
|
|
112
|
+
atomic_min_f32_storage(&scene_bounds[1], group_low.y);
|
|
113
|
+
atomic_min_f32_storage(&scene_bounds[2], group_low.z);
|
|
114
|
+
atomic_max_f32_storage(&scene_bounds[3], group_high.x);
|
|
115
|
+
atomic_max_f32_storage(&scene_bounds[4], group_high.y);
|
|
116
|
+
atomic_max_f32_storage(&scene_bounds[5], group_high.z);
|
|
116
117
|
}
|
|
117
118
|
}
|
|
118
119
|
`, [
|
|
119
120
|
chunk_scene_database_access,
|
|
120
|
-
|
|
121
|
+
chunk_atomic_min_f32_storage,
|
|
122
|
+
chunk_atomic_max_f32_storage,
|
|
121
123
|
]);
|
|
122
124
|
|
|
123
125
|
body.addExtension(WebGPUExtensionType.Subgroups);
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Atomic f32 addition over a read-write storage atomic<u32> cell containing f32 bits.
|
|
5
|
+
*
|
|
6
|
+
* Each compare-exchange attempt adds to the current float value and writes the sum
|
|
7
|
+
* as plain f32 bits. Failed or spurious exchanges retry from a fresh atomic load,
|
|
8
|
+
* so concurrent additions are not lost.
|
|
9
|
+
*
|
|
10
|
+
* @type {CodeChunk}
|
|
11
|
+
*/
|
|
12
|
+
export const chunk_atomic_add_f32_storage = CodeChunk.from(
|
|
13
|
+
//language=WGSL
|
|
14
|
+
`
|
|
15
|
+
fn atomic_add_f32_storage(cell: ptr<storage, atomic<u32>, read_write>, value: f32) {
|
|
16
|
+
loop {
|
|
17
|
+
let old_u = atomicLoad(cell);
|
|
18
|
+
let old_f = bitcast<f32>(old_u);
|
|
19
|
+
let result = atomicCompareExchangeWeak(cell, old_u, bitcast<u32>(old_f + value));
|
|
20
|
+
if result.exchanged {
|
|
21
|
+
break;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
`);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Atomic f32 maximum over a read-write storage atomic<u32> cell containing f32 bits.
|
|
5
|
+
*
|
|
6
|
+
* Compare-exchange retries keep the cell in plain f32 representation. If the stored
|
|
7
|
+
* value already bounds the contribution, the function exits without an exchange.
|
|
8
|
+
* Infinities are supported; the stored value and contribution must not be NaN.
|
|
9
|
+
*
|
|
10
|
+
* @type {CodeChunk}
|
|
11
|
+
*/
|
|
12
|
+
export const chunk_atomic_max_f32_storage = CodeChunk.from(
|
|
13
|
+
//language=WGSL
|
|
14
|
+
`
|
|
15
|
+
fn atomic_max_f32_storage(cell: ptr<storage, atomic<u32>, read_write>, value: f32) {
|
|
16
|
+
loop {
|
|
17
|
+
let old_u = atomicLoad(cell);
|
|
18
|
+
let old_f = bitcast<f32>(old_u);
|
|
19
|
+
if value <= old_f {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
let result = atomicCompareExchangeWeak(cell, old_u, bitcast<u32>(value));
|
|
23
|
+
if result.exchanged {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import { CodeChunk } from "../../compiler/CodeChunk.js";
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* Atomic f32 minimum over a read-write storage atomic<u32> cell containing f32 bits.
|
|
5
|
+
*
|
|
6
|
+
* Compare-exchange retries keep the cell in plain f32 representation. If the stored
|
|
7
|
+
* value already bounds the contribution, the function exits without an exchange.
|
|
8
|
+
* Infinities are supported; the stored value and contribution must not be NaN.
|
|
9
|
+
*
|
|
10
|
+
* @type {CodeChunk}
|
|
11
|
+
*/
|
|
12
|
+
export const chunk_atomic_min_f32_storage = CodeChunk.from(
|
|
13
|
+
//language=WGSL
|
|
14
|
+
`
|
|
15
|
+
fn atomic_min_f32_storage(cell: ptr<storage, atomic<u32>, read_write>, value: f32) {
|
|
16
|
+
loop {
|
|
17
|
+
let old_u = atomicLoad(cell);
|
|
18
|
+
let old_f = bitcast<f32>(old_u);
|
|
19
|
+
if value >= old_f {
|
|
20
|
+
break;
|
|
21
|
+
}
|
|
22
|
+
let result = atomicCompareExchangeWeak(cell, old_u, bitcast<u32>(value));
|
|
23
|
+
if result.exchanged {
|
|
24
|
+
break;
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
`);
|
|
@@ -6,7 +6,7 @@ import { CodeChunk } from "../../../../shader/compiler/CodeChunk.js";
|
|
|
6
6
|
import { COMPUTE_SHADER_DEFAULT_GROUP_SIZE } from "../../../../shader/COMPUTE_SHADER_DEFAULT_GROUP_SIZE.js";
|
|
7
7
|
import { ComputeShader } from "../../../../shader/ComputeShader.js";
|
|
8
8
|
import { ShaderResourceSetDescriptor } from "../../../../shader/resource/ShaderResourceSetDescriptor.js";
|
|
9
|
-
import {
|
|
9
|
+
import { chunk_atomic_min_f32_storage } from "../../../../shader/chunk/atomic/chunk_atomic_min_f32_storage.js";
|
|
10
10
|
import { LIGHT_DATABASE_SPEC } from "../../../../light/LIGHT_DATABASE_SPEC.js";
|
|
11
11
|
import { MESHES_TABLE } from "../../../../scene/chunk_scene_database_access.js";
|
|
12
12
|
import { CSM_CASTER_RANGE_UNKNOWN } from "../CSM_CASTER_RANGE_STRUCT.js";
|
|
@@ -124,13 +124,13 @@ fn main(
|
|
|
124
124
|
|
|
125
125
|
// one atomic per subgroup rather than one per row, and none where no row was inside
|
|
126
126
|
if lane_id == 0u && near < RANGE_UNKNOWN {
|
|
127
|
-
|
|
127
|
+
atomic_min_f32_storage(&ranges[settings.base + c], near);
|
|
128
128
|
}
|
|
129
129
|
}
|
|
130
130
|
}
|
|
131
131
|
`, [
|
|
132
132
|
MESHES_TABLE.chunk_iterate(WG),
|
|
133
|
-
|
|
133
|
+
chunk_atomic_min_f32_storage,
|
|
134
134
|
]);
|
|
135
135
|
|
|
136
136
|
accumulate_body.addExtension(WebGPUExtensionType.Subgroups);
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
import { CodeChunk } from "../../../shader/compiler/CodeChunk.js";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Per-element f32 atomic min/max via CAS-spin over a `atomic<u32>`
|
|
5
|
-
* storage cell, bitcast through f32 for comparison.
|
|
6
|
-
*
|
|
7
|
-
* WGSL has native u32/i32 atomics only. For float min/max the
|
|
8
|
-
* sign-bit-flip encoding (cheaper, only `atomicMin`/`atomicMax`
|
|
9
|
-
* needed) is a tempting alternative, but it leaves the buffer in
|
|
10
|
-
* "encoded" form during the dispatch chain — readers in the same
|
|
11
|
-
* frame would need to know whether the value is encoded or decoded.
|
|
12
|
-
* The CAS-spin keeps the storage in plain f32 the whole time, which
|
|
13
|
-
* matches what the rasterizer's meshlet-culling shader already
|
|
14
|
-
* expects in `meshlet.bounds_box`.
|
|
15
|
-
*
|
|
16
|
-
* Contention model: every contributor reads → compares → conditionally
|
|
17
|
-
* writes. When the existing value already bounds the contribution
|
|
18
|
-
* (typical for late contributors as the box settles), the comparison
|
|
19
|
-
* exits early without any CAS. The worst case is many threads
|
|
20
|
-
* hammering the same slot in the first few iterations; the loop is
|
|
21
|
-
* bounded by collisions, not by thread count.
|
|
22
|
-
*
|
|
23
|
-
* `value` is treated as a normal f32 — NaNs are not handled (vertex
|
|
24
|
-
* positions are never NaN in correctly-loaded geometry). +inf / -inf
|
|
25
|
-
* are valid (they're our scatter sentinels).
|
|
26
|
-
*
|
|
27
|
-
* @type {CodeChunk}
|
|
28
|
-
*/
|
|
29
|
-
export const chunk_aabb_atomic_f32 = CodeChunk.from(
|
|
30
|
-
//language=WGSL
|
|
31
|
-
`
|
|
32
|
-
fn aabb_atomic_min_f32(cell: ptr<storage, atomic<u32>, read_write>, value: f32) {
|
|
33
|
-
loop {
|
|
34
|
-
let old_u = atomicLoad(cell);
|
|
35
|
-
let old_f = bitcast<f32>(old_u);
|
|
36
|
-
if value >= old_f {
|
|
37
|
-
break;
|
|
38
|
-
}
|
|
39
|
-
let result = atomicCompareExchangeWeak(cell, old_u, bitcast<u32>(value));
|
|
40
|
-
if result.exchanged {
|
|
41
|
-
break;
|
|
42
|
-
}
|
|
43
|
-
}
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
fn aabb_atomic_max_f32(cell: ptr<storage, atomic<u32>, read_write>, value: f32) {
|
|
47
|
-
loop {
|
|
48
|
-
let old_u = atomicLoad(cell);
|
|
49
|
-
let old_f = bitcast<f32>(old_u);
|
|
50
|
-
if value <= old_f {
|
|
51
|
-
break;
|
|
52
|
-
}
|
|
53
|
-
let result = atomicCompareExchangeWeak(cell, old_u, bitcast<u32>(value));
|
|
54
|
-
if result.exchanged {
|
|
55
|
-
break;
|
|
56
|
-
}
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
`);
|