@woosh/meep-engine 3.60.8 → 3.60.9

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.
Files changed (124) hide show
  1. package/package.json +1 -1
  2. package/src/engine/graphics3/AnimationGraphSystem.d.ts +4 -1
  3. package/src/engine/graphics3/AnimationGraphSystem.d.ts.map +1 -1
  4. package/src/engine/graphics3/AnimationGraphSystem.js +35 -5
  5. package/src/engine/graphics3/AnimationSystem.d.ts +1 -1
  6. package/src/engine/graphics3/AnimationSystem.d.ts.map +1 -1
  7. package/src/engine/graphics3/AnimationSystem.js +34 -5
  8. package/src/engine/graphics3/FoliageWindSystem.d.ts +2 -1
  9. package/src/engine/graphics3/FoliageWindSystem.d.ts.map +1 -1
  10. package/src/engine/graphics3/FoliageWindSystem.js +50 -3
  11. package/src/engine/graphics3/GPUFluidSystem.d.ts.map +1 -1
  12. package/src/engine/graphics3/GPUFluidSystem.js +16 -14
  13. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts +2 -2
  14. package/src/engine/graphics3/GPUParticleEmitterSystem.d.ts.map +1 -1
  15. package/src/engine/graphics3/GPUParticleEmitterSystem.js +84 -32
  16. package/src/engine/graphics3/LightSystem.d.ts +4 -0
  17. package/src/engine/graphics3/LightSystem.d.ts.map +1 -1
  18. package/src/engine/graphics3/LightSystem.js +23 -6
  19. package/src/engine/graphics3/MeshSkinSystem.d.ts +1 -1
  20. package/src/engine/graphics3/MeshSkinSystem.d.ts.map +1 -1
  21. package/src/engine/graphics3/MeshSkinSystem.js +50 -5
  22. package/src/engine/graphics3/MeshSystem.d.ts +1 -0
  23. package/src/engine/graphics3/MeshSystem.d.ts.map +1 -1
  24. package/src/engine/graphics3/MeshSystem.js +22 -8
  25. package/src/engine/graphics3/ShadedGeometrySystem.d.ts +5 -1
  26. package/src/engine/graphics3/ShadedGeometrySystem.d.ts.map +1 -1
  27. package/src/engine/graphics3/ShadedGeometrySystem.js +56 -7
  28. package/src/engine/graphics3/WaterSystem.d.ts +3 -0
  29. package/src/engine/graphics3/WaterSystem.d.ts.map +1 -1
  30. package/src/engine/graphics3/WaterSystem.js +10 -2
  31. package/src/engine/graphics3/animation/AnimationClipsSink.d.ts +15 -7
  32. package/src/engine/graphics3/animation/AnimationClipsSink.d.ts.map +1 -1
  33. package/src/engine/graphics3/animation/AnimationClipsSink.js +42 -8
  34. package/src/engine/network/state/ReplicationSlotTable.d.ts +9 -5
  35. package/src/engine/network/state/ReplicationSlotTable.d.ts.map +1 -1
  36. package/src/engine/network/state/ReplicationSlotTable.js +36 -8
  37. package/src/engine/physics/fluid/ecs/FluidObstacleSystem.d.ts +4 -4
  38. package/src/shade/renderer/GraphicsContext.d.ts +8 -0
  39. package/src/shade/renderer/GraphicsContext.d.ts.map +1 -1
  40. package/src/shade/renderer/GraphicsContext.js +29 -0
  41. package/src/shade/renderer/Renderer.d.ts +10 -0
  42. package/src/shade/renderer/Renderer.d.ts.map +1 -1
  43. package/src/shade/renderer/Renderer.js +22 -2
  44. package/src/shade/renderer/fluid/GPUFluidCascade.d.ts +3 -3
  45. package/src/shade/renderer/fluid/GPUFluidCascadeStack.d.ts +1 -1
  46. package/src/shade/renderer/fluid/forcing/GPUFluidForcing.d.ts +6 -6
  47. package/src/shade/renderer/fluid/pressure/GPUFluidPressureSolve.d.ts +1 -1
  48. package/src/shade/renderer/fluid/query/GPUFluidQueries.d.ts +1 -1
  49. package/src/shade/renderer/global_illumination/probe_clipmap/GPUProbeClipmapGI.d.ts +1 -1
  50. package/src/shade/renderer/light/GPULightCollection.d.ts +3 -1
  51. package/src/shade/renderer/light/GPULightCollection.d.ts.map +1 -1
  52. package/src/shade/renderer/light/GPULightCollection.js +34 -9
  53. package/src/shade/renderer/light/LightCollection.d.ts +13 -3
  54. package/src/shade/renderer/light/LightCollection.d.ts.map +1 -1
  55. package/src/shade/renderer/light/LightCollection.js +19 -3
  56. package/src/shade/renderer/loader/gltf/tiny-gltf.d.ts +1 -1
  57. package/src/shade/renderer/material/GPUMaterialManager.d.ts +22 -0
  58. package/src/shade/renderer/material/GPUMaterialManager.d.ts.map +1 -1
  59. package/src/shade/renderer/material/GPUMaterialManager.js +30 -0
  60. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.d.ts +75 -0
  61. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.d.ts.map +1 -0
  62. package/src/shade/renderer/material/dispatch/GPUMaterialComputeDispatch.js +338 -0
  63. package/src/shade/renderer/material/dispatch/MATERIAL_DISPATCH_CONSTANTS.d.ts +50 -0
  64. package/src/shade/renderer/material/dispatch/MATERIAL_DISPATCH_CONSTANTS.d.ts.map +1 -0
  65. package/src/shade/renderer/material/dispatch/MATERIAL_DISPATCH_CONSTANTS.js +54 -0
  66. package/src/shade/renderer/material/dispatch/MaterialDispatch.d.ts +37 -0
  67. package/src/shade/renderer/material/dispatch/MaterialDispatch.d.ts.map +1 -0
  68. package/src/shade/renderer/material/dispatch/MaterialDispatch.js +31 -0
  69. package/src/shade/renderer/material/dispatch/MaterialDispatchRecords.d.ts +42 -0
  70. package/src/shade/renderer/material/dispatch/MaterialDispatchRecords.d.ts.map +1 -0
  71. package/src/shade/renderer/material/dispatch/MaterialDispatchRecords.js +132 -0
  72. package/src/shade/renderer/material/dispatch/chunk_material_mesh_key.d.ts +21 -0
  73. package/src/shade/renderer/material/dispatch/chunk_material_mesh_key.d.ts.map +1 -0
  74. package/src/shade/renderer/material/dispatch/chunk_material_mesh_key.js +46 -0
  75. package/src/shade/renderer/material/dispatch/chunk_material_quad_keys.d.ts +20 -0
  76. package/src/shade/renderer/material/dispatch/chunk_material_quad_keys.d.ts.map +1 -0
  77. package/src/shade/renderer/material/dispatch/chunk_material_quad_keys.js +111 -0
  78. package/src/shade/renderer/material/dispatch/chunk_material_quad_pack.d.ts +18 -0
  79. package/src/shade/renderer/material/dispatch/chunk_material_quad_pack.d.ts.map +1 -0
  80. package/src/shade/renderer/material/dispatch/chunk_material_quad_pack.js +33 -0
  81. package/src/shade/renderer/material/dispatch/gbuffer_compute.d.ts +116 -0
  82. package/src/shade/renderer/material/dispatch/gbuffer_compute.d.ts.map +1 -0
  83. package/src/shade/renderer/material/dispatch/gbuffer_compute.js +249 -0
  84. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.d.ts +56 -0
  85. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.d.ts.map +1 -0
  86. package/src/shade/renderer/material/dispatch/graph_material_dispatch_compute.js +315 -0
  87. package/src/shade/renderer/material/dispatch/shader_material_dispatch_commands.d.ts +16 -0
  88. package/src/shade/renderer/material/dispatch/shader_material_dispatch_commands.d.ts.map +1 -0
  89. package/src/shade/renderer/material/dispatch/shader_material_dispatch_commands.js +83 -0
  90. package/src/shade/renderer/material/dispatch/shader_material_pixel_count.d.ts +22 -0
  91. package/src/shade/renderer/material/dispatch/shader_material_pixel_count.d.ts.map +1 -0
  92. package/src/shade/renderer/material/dispatch/shader_material_pixel_count.js +133 -0
  93. package/src/shade/renderer/material/dispatch/shader_material_pixel_scatter.d.ts +14 -0
  94. package/src/shade/renderer/material/dispatch/shader_material_pixel_scatter.d.ts.map +1 -0
  95. package/src/shade/renderer/material/dispatch/shader_material_pixel_scatter.js +98 -0
  96. package/src/shade/renderer/material/standard/VT_MATERIAL_TEMPLATE.d.ts +1 -0
  97. package/src/shade/renderer/material/standard/VT_MATERIAL_TEMPLATE.d.ts.map +1 -1
  98. package/src/shade/renderer/material/standard/VT_MATERIAL_TEMPLATE.js +11 -0
  99. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts +41 -6
  100. package/src/shade/renderer/material/standard/fragment_gbuffer.d.ts.map +1 -1
  101. package/src/shade/renderer/material/standard/fragment_gbuffer.js +88 -29
  102. package/src/shade/renderer/material/template/ShadeMaterialTemplate.d.ts +13 -1
  103. package/src/shade/renderer/material/template/ShadeMaterialTemplate.d.ts.map +1 -1
  104. package/src/shade/renderer/material/template/ShadeMaterialTemplate.js +18 -1
  105. package/src/shade/renderer/postprocess/nss/NSS.d.ts +1 -1
  106. package/src/shade/renderer/scene/GPUSceneContext.d.ts +6 -0
  107. package/src/shade/renderer/scene/GPUSceneContext.d.ts.map +1 -1
  108. package/src/shade/renderer/scene/GPUSceneContext.js +17 -7
  109. package/src/shade/renderer/scene/Scene.d.ts +4 -3
  110. package/src/shade/renderer/scene/Scene.d.ts.map +1 -1
  111. package/src/shade/renderer/scene/Scene.js +54 -53
  112. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts +16 -0
  113. package/src/shade/renderer/scene/rows/GPUSceneRows.d.ts.map +1 -1
  114. package/src/shade/renderer/scene/rows/GPUSceneRows.js +34 -0
  115. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_anti_alias_roughness_kaplanyan.d.ts +19 -0
  116. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_anti_alias_roughness_kaplanyan.d.ts.map +1 -1
  117. package/src/shade/renderer/shader/chunk/geometry/normal/chunk_anti_alias_roughness_kaplanyan.js +55 -20
  118. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_keyed_append.d.ts +31 -0
  119. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_keyed_append.d.ts.map +1 -0
  120. package/src/shade/renderer/shader/chunk/subgroup/chunk_subgroup_keyed_append.js +77 -0
  121. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts +6 -0
  122. package/src/shade/renderer/texture/virtual/VirtualTextureManager.d.ts.map +1 -1
  123. package/src/shade/renderer/texture/virtual/VirtualTextureManager.js +15 -1
  124. package/src/shade/renderer/water/query/GPUWaterQueries.d.ts +1 -1
@@ -0,0 +1,249 @@
1
+ import { GPUShaderStage } from "../../../descriptor/GPUShaderStage.js";
2
+ import { ComputePipelineDescriptor } from "../../../descriptor/pipeline/compute/ComputePipelineDescriptor.js";
3
+ import { PipelineLayoutDescriptor } from "../../../descriptor/pipeline/PipelineLayoutDescriptor.js";
4
+ import { WebGPUExtensionType } from "../../../descriptor/WebGPUExtensionType.js";
5
+ import { G_BUFFER_COLOR_TARGETS } from "../../gbuffer/G_BUFFER_COLOR_TARGETS.js";
6
+ import { chunk_subgroup_reserve_workgroup } from "../../shader/chunk/subgroup/chunk_subgroup_reserve_workgroup.js";
7
+ import { chunk_folded_workgroup_index } from "../../shader/chunk/grid/chunk_folded_workgroup_index.js";
8
+ import { CodeChunk } from "../../shader/compiler/CodeChunk.js";
9
+ import { ShaderResourceGroupDescriptor } from "../../shader/resource/ShaderResourceGroupDescriptor.js";
10
+ import { ShaderResourceSetDescriptor } from "../../shader/resource/ShaderResourceSetDescriptor.js";
11
+ import { ShaderDescriptor } from "../../shader/ShaderDescriptor.js";
12
+ import { u32 } from "../../shader/type/WEBGPU_WGSL_PRIMITIVE_TYPES.js";
13
+ import { WebGPUArray } from "../../shader/type/WebGPUArray.js";
14
+ import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
15
+ import {
16
+ add_g_buffer_viz_resources,
17
+ build_gbuffer_laydown,
18
+ g_buffer_scene_resources
19
+ } from "../standard/fragment_gbuffer.js";
20
+ import { chunk_material_quad_pack } from "./chunk_material_quad_pack.js";
21
+ import { MATERIAL_DISPATCH_GROUP_SIZE, MATERIAL_DISPATCH_QUADS_PER_GROUP } from "./MATERIAL_DISPATCH_CONSTANTS.js";
22
+
23
+ /**
24
+ * What one material's compute dispatch is for: its indirect workgroup counts, which the dispatch is
25
+ * issued with, and the run of the pixel list it shades. Written by
26
+ * {@link shader_material_dispatch_commands}, one per material at
27
+ * {@link MATERIAL_DISPATCH_RECORD_STRIDE}, and bound to the dispatch as its own uniform.
28
+ *
29
+ * @type {WebGPUStruct}
30
+ */
31
+ export const MATERIAL_DISPATCH_RECORD_STRUCT = WebGPUStruct.from({
32
+ groups_x: u32,
33
+ groups_y: u32,
34
+ groups_z: u32,
35
+ /**
36
+ * Index of the material's first quad in the list.
37
+ */
38
+ start: u32,
39
+ /**
40
+ * How many quads the material has — 2×2 quads of pixels holding at least one of its pixels.
41
+ */
42
+ count: u32,
43
+ }, "MaterialDispatchRecord");
44
+
45
+ /**
46
+ * @typedef {import("../../../descriptor/binding/BindGroupLayoutDescriptor.js").BindGroupLayoutDescriptor} BindGroupLayoutDescriptor
47
+ */
48
+
49
+ /**
50
+ * Group 1 of every template's compute G-buffer pipeline: what {@link build_gbuffer_laydown} reads
51
+ * of the visibility buffer and the view, under the names the laydown reads them by, then the quad
52
+ * lists the dispatch walks and the G-buffer it writes.
53
+ *
54
+ * One bind group a frame serves every template.
55
+ *
56
+ * @type {ShaderResourceGroupDescriptor}
57
+ */
58
+ export const g_buffer_viz_compute_resources = new ShaderResourceGroupDescriptor();
59
+
60
+ g_buffer_viz_compute_resources.label = "viz buffer / compute";
61
+
62
+ add_g_buffer_viz_resources(g_buffer_viz_compute_resources)
63
+ // every material's quads, each as chunk_material_quad_pack packs it
64
+ .addStorageBuffer("material_quads", WebGPUArray.u32)
65
+ .addStorageTexture("g_buffer_pbr", G_BUFFER_COLOR_TARGETS[0].format, "2d", "write-only")
66
+ .addStorageTexture("g_buffer_normal", G_BUFFER_COLOR_TARGETS[1].format, "2d", "write-only")
67
+ .addStorageTexture("g_buffer_albedo", G_BUFFER_COLOR_TARGETS[2].format, "2d", "write-only")
68
+ .addStorageTexture("g_buffer_emissive", G_BUFFER_COLOR_TARGETS[3].format, "2d", "write-only")
69
+ ;
70
+
71
+ g_buffer_viz_compute_resources.addVisibility(GPUShaderStage.COMPUTE);
72
+
73
+ /**
74
+ * Group 3 of every template's compute G-buffer pipeline: the dispatch's own record. A bind group
75
+ * per material, over that material's slice of the record buffer, so the shader learns which run of
76
+ * the pixel list is its own from the one thing that differs between the dispatches.
77
+ *
78
+ * @type {ShaderResourceGroupDescriptor}
79
+ */
80
+ export const g_buffer_dispatch_resources = new ShaderResourceGroupDescriptor();
81
+
82
+ g_buffer_dispatch_resources.label = "material dispatch";
83
+
84
+ g_buffer_dispatch_resources.addUniform("material_dispatch", MATERIAL_DISPATCH_RECORD_STRUCT);
85
+
86
+ g_buffer_dispatch_resources.addVisibility(GPUShaderStage.COMPUTE);
87
+
88
+ /**
89
+ * Group 1 as every compute G-buffer pipeline lays it out.
90
+ *
91
+ * @type {BindGroupLayoutDescriptor}
92
+ */
93
+ export const GBUFFER_COMPUTE_VIZ_LAYOUT = g_buffer_viz_compute_resources.generateBindGroupLayoutDescriptor(GPUShaderStage.COMPUTE);
94
+
95
+ /**
96
+ * Group 2 as every compute G-buffer pipeline lays it out: the fragment pipelines' scene group,
97
+ * visible to compute.
98
+ *
99
+ * @type {BindGroupLayoutDescriptor}
100
+ */
101
+ export const GBUFFER_COMPUTE_SCENE_LAYOUT = g_buffer_scene_resources.generateBindGroupLayoutDescriptor(GPUShaderStage.COMPUTE);
102
+
103
+ /**
104
+ * Group 3 as every compute G-buffer pipeline lays it out.
105
+ *
106
+ * @type {BindGroupLayoutDescriptor}
107
+ */
108
+ export const GBUFFER_COMPUTE_DISPATCH_LAYOUT = g_buffer_dispatch_resources.generateBindGroupLayoutDescriptor(GPUShaderStage.COMPUTE);
109
+
110
+ /**
111
+ * The resource set of a compute G-buffer pipeline whose group 0 is `material_group`: the template's
112
+ * own group 0 and the fragment pipelines' group 2, which this code reads under the same names, and
113
+ * groups 1 and 3 of its own.
114
+ *
115
+ * @param {ShaderResourceGroupDescriptor} material_group
116
+ * @returns {ShaderResourceSetDescriptor}
117
+ */
118
+ export function build_gbuffer_compute_shader_resources(material_group) {
119
+ const resources = new ShaderResourceSetDescriptor();
120
+
121
+ resources.addGroup(material_group);
122
+ resources.addGroup(g_buffer_viz_compute_resources);
123
+ resources.addGroup(g_buffer_scene_resources);
124
+ resources.addGroup(g_buffer_dispatch_resources);
125
+
126
+ return resources;
127
+ }
128
+
129
+ /**
130
+ * `fn gbuffer_fwidth(v: vec2<f32>) -> vec2<f32>` for {@link build_gbuffer_laydown} in the compute
131
+ * material dispatch, whose quad of lanes is a 2×2 quad of pixels: `fwidth` taken coarse, across the
132
+ * quad's top row and left column. That is what a fragment's `fwidth` is on D3D12; WGSL lets a backend
133
+ * take it fine instead, and there the two paths' filtered roughness differs where the normal varies
134
+ * between a quad's rows.
135
+ *
136
+ * Called from uniform control flow, with every lane of the quad running the laydown.
137
+ *
138
+ * @type {CodeChunk}
139
+ */
140
+ export const chunk_gbuffer_fwidth_quad = CodeChunk.from(
141
+ // language=WGSL
142
+ `
143
+ @diagnostic(off, subgroup_uniformity)
144
+ fn gbuffer_fwidth(v: vec2<f32>) -> vec2<f32> {
145
+ let top_left = quadBroadcast(v, 0u);
146
+
147
+ return abs(quadBroadcast(v, 1u) - top_left) + abs(quadBroadcast(v, 2u) - top_left);
148
+ }
149
+ `);
150
+
151
+ chunk_gbuffer_fwidth_quad.addExtension(WebGPUExtensionType.Subgroups);
152
+
153
+ /**
154
+ * The compute entry of a template's G-buffer pipeline: a quad of lanes per 2×2 quad of pixels in
155
+ * the material's run of the list, {@link build_gbuffer_laydown} at each pixel's centre, and the
156
+ * result stored, for the pixels of the quad that are the material's, where the fragment dispatch's
157
+ * colour targets would have taken it.
158
+ *
159
+ * The quad's other pixels run the laydown as a fragment quad's helper lanes do, so the derivative
160
+ * the laydown takes is a fragment quad's — see {@link chunk_gbuffer_fwidth_quad}. So does the tail
161
+ * of the material's last workgroup, on its last quad again; every lane of a workgroup that holds any
162
+ * of the list runs the laydown, which keeps the quad operations in uniform control flow, and a
163
+ * workgroup wholly past the list leaves at once.
164
+ *
165
+ * Lanes take their places in subgroup order, through {@link chunk_subgroup_reserve_workgroup}, so a
166
+ * quad of places is a quad of lanes however subgroups are laid over the workgroup.
167
+ *
168
+ * Dispatched with a folded command — see {@link chunk_folded_workgroup_index} — so one material can
169
+ * cover more of the screen than one dispatch dimension reaches.
170
+ *
171
+ * @param {CodeChunk} surface defines `fn material_surface(s: MaterialSurfaceInput) -> MaterialSurfaceOutput`
172
+ * @returns {CodeChunk}
173
+ */
174
+ export function build_gbuffer_compute_body(surface) {
175
+ return CodeChunk.from(
176
+ // language=WGSL
177
+ `
178
+ var<workgroup> material_dispatch_places: atomic<u32>;
179
+
180
+ @compute @workgroup_size(${MATERIAL_DISPATCH_GROUP_SIZE})
181
+ fn main(
182
+ @builtin(workgroup_id) workgroup_id : vec3<u32>,
183
+ ) {
184
+ let first_quad = folded_workgroup_index(workgroup_id) * ${MATERIAL_DISPATCH_QUADS_PER_GROUP}u;
185
+ let count = material_dispatch.count;
186
+
187
+ // a folded grid's last row runs past the list; the whole workgroup leaves together, so what
188
+ // follows stays in uniform control flow
189
+ if (first_quad >= count) {
190
+ return;
191
+ }
192
+
193
+ let place = subgroup_reserve_workgroup(&material_dispatch_places, 1u);
194
+
195
+ let quad_index = first_quad + place / 4u;
196
+ let corner = place & 3u;
197
+
198
+ let in_list = quad_index < count;
199
+
200
+ let entry = material_quad_unpack(material_quads[material_dispatch.start + min(quad_index, count - 1u)]);
201
+
202
+ let pixel = entry.quad * 2u + vec2<u32>(corner & 1u, corner >> 1u);
203
+
204
+ // the pixel's centre, which is what a fragment's position holds
205
+ let g = gbuffer_laydown(vec2<f32>(pixel) + vec2<f32>(0.5));
206
+
207
+ if (in_list && ((entry.mask >> corner) & 1u) != 0u) {
208
+ textureStore(g_buffer_pbr, pixel, vec4<f32>(g.pbr, 0.0, 0.0));
209
+ textureStore(g_buffer_normal, pixel, g.normal);
210
+ textureStore(g_buffer_albedo, pixel, g.albedo);
211
+ textureStore(g_buffer_emissive, pixel, vec4<u32>(g.emissive, 0u, 0u, 0u));
212
+ }
213
+ }`, [
214
+ build_gbuffer_laydown(surface, chunk_gbuffer_fwidth_quad),
215
+ chunk_folded_workgroup_index,
216
+ chunk_subgroup_reserve_workgroup,
217
+ chunk_material_quad_pack,
218
+ ]
219
+ );
220
+ }
221
+
222
+ /**
223
+ * The compute G-buffer pipeline of a template: its group 0 and surface function around the shared
224
+ * laydown. Every group is laid out for the compute stage, whatever stages its declaration names, so
225
+ * no template has to declare anything for this pipeline: a material's group 0 for it is a bind group
226
+ * of its own, made by the template's own hook over this layout.
227
+ *
228
+ * @param {Object} options
229
+ * @param {string} options.label
230
+ * @param {ShaderResourceGroupDescriptor} options.material_group the template's group 0
231
+ * @param {CodeChunk} options.surface defines `material_surface`
232
+ * @returns {ComputePipelineDescriptor}
233
+ */
234
+ export function build_gbuffer_compute_pipeline_descriptor({ label, material_group, surface }) {
235
+ const shader = ShaderDescriptor.from({
236
+ label,
237
+ resources: build_gbuffer_compute_shader_resources(material_group),
238
+ body: build_gbuffer_compute_body(surface),
239
+ });
240
+
241
+ const layout = PipelineLayoutDescriptor.from([
242
+ material_group.generateBindGroupLayoutDescriptor(GPUShaderStage.COMPUTE),
243
+ GBUFFER_COMPUTE_VIZ_LAYOUT,
244
+ GBUFFER_COMPUTE_SCENE_LAYOUT,
245
+ GBUFFER_COMPUTE_DISPATCH_LAYOUT,
246
+ ]);
247
+
248
+ return ComputePipelineDescriptor.from(label, shader.source, layout);
249
+ }
@@ -0,0 +1,56 @@
1
+ /**
2
+ * Lay the G-buffer down with the compute material dispatch — see {@link MaterialDispatch.Compute}:
3
+ *
4
+ * 1. **count** every 2×2 quad into the (material, tile) list of each material it holds, and clear
5
+ * the pixels no material shades;
6
+ * 2. **scan** the counts, so each list knows where it starts;
7
+ * 3. write each material's **dispatch record** — its run of the lists and its workgroup counts;
8
+ * 4. **scatter** every quad into its lists;
9
+ * 5. **shade**: one compute pass, one indirect dispatch per material over its own run, with the
10
+ * material's compute bind group, its template's compute pipeline, and its record as group 3.
11
+ *
12
+ * The material lists are ordered as `contexts` is, which is by template, so pipeline switches in the
13
+ * shade pass are one per template; within a material its quads run tile by tile.
14
+ *
15
+ * @param {Object} options
16
+ * @param {FrameGraph} options.graph
17
+ * @param {GPUViewContext} options.view
18
+ * @param {GPUMaterialContext[]} options.contexts the G-buffer materials, as the fragment dispatch
19
+ * draws them
20
+ * @param {Uint32Array} options.material_slots a {@link cpu_hash_table_create} table from each
21
+ * context's material id to its place in `contexts`
22
+ * @param {MaterialDispatchRecords} options.records with room for every context
23
+ * @param {BindGroupManager} options.bindings
24
+ * @param {function(GPUMaterialContext):GPUComputePipeline} options.pipeline_of the context's
25
+ * compute G-buffer pipeline
26
+ * @param {function(GPUMaterialContext):GPUBindGroup} options.group_of the context's group 0 for
27
+ * that pipeline
28
+ * @param {number} options.texture_viz_mesh
29
+ * @param {number} options.texture_viz_triangle
30
+ * @param {number} options.texture_normal
31
+ * @returns {{g_pbr: number, g_normal: number, g_albedo: number, g_emissive: number}}
32
+ */
33
+ export function graph_material_dispatch_compute({ graph, view, contexts, material_slots, records, bindings, pipeline_of, group_of, texture_viz_mesh, texture_viz_triangle, texture_normal, }: {
34
+ graph: FrameGraph;
35
+ view: GPUViewContext;
36
+ contexts: GPUMaterialContext[];
37
+ material_slots: Uint32Array;
38
+ records: MaterialDispatchRecords;
39
+ bindings: BindGroupManager;
40
+ pipeline_of: (arg0: GPUMaterialContext) => GPUComputePipeline;
41
+ group_of: (arg0: GPUMaterialContext) => GPUBindGroup;
42
+ texture_viz_mesh: number;
43
+ texture_viz_triangle: number;
44
+ texture_normal: number;
45
+ }): {
46
+ g_pbr: number;
47
+ g_normal: number;
48
+ g_albedo: number;
49
+ g_emissive: number;
50
+ };
51
+ export type FrameGraph = import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph;
52
+ export type BindGroupManager = import("../../binding/BindGroupManager.js").BindGroupManager;
53
+ export type GPUViewContext = import("../../view/GPUViewContext.js").GPUViewContext;
54
+ export type GPUMaterialContext = import("../GPUMaterialContext.js").GPUMaterialContext;
55
+ export type MaterialDispatchRecords = import("./MaterialDispatchRecords.js").MaterialDispatchRecords;
56
+ //# sourceMappingURL=graph_material_dispatch_compute.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"graph_material_dispatch_compute.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/material/dispatch/graph_material_dispatch_compute.js"],"names":[],"mappings":"AAkDA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH;IAjB+B,KAAK,EAAzB,UAAU;IACc,IAAI,EAA5B,cAAc;IACgB,QAAQ,EAAtC,kBAAkB,EAAE;IAEC,cAAc,EAAnC,WAAW;IAEsB,OAAO,EAAxC,uBAAuB;IACG,QAAQ,EAAlC,gBAAgB;IACyC,WAAW,SAA3D,kBAAkB;IAEwB,QAAQ,SAAlD,kBAAkB;IAEX,gBAAgB,EAAhC,MAAM;IACU,oBAAoB,EAApC,MAAM;IACU,cAAc,EAA9B,MAAM;IACJ;IAAC,KAAK,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,QAAQ,EAAE,MAAM,CAAC;IAAC,UAAU,EAAE,MAAM,CAAA;CAAC,CA0OnF;yBA/RY,OAAO,8DAA8D,EAAE,UAAU;+BACjF,OAAO,mCAAmC,EAAE,gBAAgB;6BAC5D,OAAO,8BAA8B,EAAE,cAAc;iCACrD,OAAO,0BAA0B,EAAE,kBAAkB;sCACrD,OAAO,8BAA8B,EAAE,uBAAuB"}
@@ -0,0 +1,315 @@
1
+ import { assert } from "../../../../core/assert.js";
2
+ import { round_up } from "../../../../core/math/round_up.js";
3
+ import { BufferResourceDescriptor } from "../../../device/graph/BufferResourceDescriptor.js";
4
+ import { TextureResourceDescriptor } from "../../../device/graph/TextureResourceDescriptor.js";
5
+ import { BindGroupDescriptor } from "../../binding/BindGroupDescriptor.js";
6
+ import { G_BUFFER_COLOR_TARGETS } from "../../gbuffer/G_BUFFER_COLOR_TARGETS.js";
7
+ import {
8
+ graph_prefix_scan_csdldf,
9
+ prefix_scan_csdldf_output_descriptor
10
+ } from "../../gpu_primitive/prefix_sum/v1/graph_prefix_scan_csdldf.js";
11
+ import { graph_create_buffer } from "../../shader/graph/graph_create_buffer.js";
12
+ import { graph_import_buffer } from "../../shader/graph/graph_import_buffer.js";
13
+ import { graph_import_raw_buffer } from "../../shader/graph/graph_import_raw_buffer.js";
14
+ import { GBUFFER_COMPUTE_SCENE_LAYOUT, GBUFFER_COMPUTE_VIZ_LAYOUT } from "./gbuffer_compute.js";
15
+ import {
16
+ MATERIAL_CLASSIFY_BLOCK_SIZE,
17
+ MATERIAL_DISPATCH_RECORD_STRIDE,
18
+ MATERIAL_DISPATCH_TILE_SIZE
19
+ } from "./MATERIAL_DISPATCH_CONSTANTS.js";
20
+ import {
21
+ MATERIAL_DISPATCH_COMMANDS_SETTINGS_STRUCT,
22
+ shader_material_dispatch_commands
23
+ } from "./shader_material_dispatch_commands.js";
24
+ import { MATERIAL_CLASSIFY_SETTINGS_STRUCT, shader_material_pixel_count } from "./shader_material_pixel_count.js";
25
+ import { shader_material_pixel_scatter } from "./shader_material_pixel_scatter.js";
26
+
27
+ /**
28
+ * @typedef {import("../../../../engine/graphics/render/frame_graph/FrameGraph.js").FrameGraph} FrameGraph
29
+ * @typedef {import("../../binding/BindGroupManager.js").BindGroupManager} BindGroupManager
30
+ * @typedef {import("../../view/GPUViewContext.js").GPUViewContext} GPUViewContext
31
+ * @typedef {import("../GPUMaterialContext.js").GPUMaterialContext} GPUMaterialContext
32
+ * @typedef {import("./MaterialDispatchRecords.js").MaterialDispatchRecords} MaterialDispatchRecords
33
+ */
34
+
35
+ /**
36
+ * The G-buffer targets the compute dispatch creates, in the formats every G-buffer pass writes, and
37
+ * written as storage. They stay render attachments for whatever composites into them later.
38
+ *
39
+ * @param {GPUTextureFormat} format
40
+ * @param {number[]} resolution
41
+ * @returns {TextureResourceDescriptor}
42
+ */
43
+ function storage_target(format, resolution) {
44
+ return TextureResourceDescriptor.from({
45
+ format,
46
+ resolution,
47
+ usage: GPUTextureUsage.RENDER_ATTACHMENT | GPUTextureUsage.TEXTURE_BINDING | GPUTextureUsage.STORAGE_BINDING,
48
+ });
49
+ }
50
+
51
+ /**
52
+ * Lay the G-buffer down with the compute material dispatch — see {@link MaterialDispatch.Compute}:
53
+ *
54
+ * 1. **count** every 2×2 quad into the (material, tile) list of each material it holds, and clear
55
+ * the pixels no material shades;
56
+ * 2. **scan** the counts, so each list knows where it starts;
57
+ * 3. write each material's **dispatch record** — its run of the lists and its workgroup counts;
58
+ * 4. **scatter** every quad into its lists;
59
+ * 5. **shade**: one compute pass, one indirect dispatch per material over its own run, with the
60
+ * material's compute bind group, its template's compute pipeline, and its record as group 3.
61
+ *
62
+ * The material lists are ordered as `contexts` is, which is by template, so pipeline switches in the
63
+ * shade pass are one per template; within a material its quads run tile by tile.
64
+ *
65
+ * @param {Object} options
66
+ * @param {FrameGraph} options.graph
67
+ * @param {GPUViewContext} options.view
68
+ * @param {GPUMaterialContext[]} options.contexts the G-buffer materials, as the fragment dispatch
69
+ * draws them
70
+ * @param {Uint32Array} options.material_slots a {@link cpu_hash_table_create} table from each
71
+ * context's material id to its place in `contexts`
72
+ * @param {MaterialDispatchRecords} options.records with room for every context
73
+ * @param {BindGroupManager} options.bindings
74
+ * @param {function(GPUMaterialContext):GPUComputePipeline} options.pipeline_of the context's
75
+ * compute G-buffer pipeline
76
+ * @param {function(GPUMaterialContext):GPUBindGroup} options.group_of the context's group 0 for
77
+ * that pipeline
78
+ * @param {number} options.texture_viz_mesh
79
+ * @param {number} options.texture_viz_triangle
80
+ * @param {number} options.texture_normal
81
+ * @returns {{g_pbr: number, g_normal: number, g_albedo: number, g_emissive: number}}
82
+ */
83
+ export function graph_material_dispatch_compute({
84
+ graph,
85
+ view,
86
+ contexts,
87
+ material_slots,
88
+ records,
89
+ bindings,
90
+ pipeline_of,
91
+ group_of,
92
+ texture_viz_mesh,
93
+ texture_viz_triangle,
94
+ texture_normal,
95
+ }) {
96
+ assert.defined(graph, 'graph');
97
+ assert.defined(view, 'view');
98
+ assert.isArray(contexts, 'contexts');
99
+
100
+ const resolution = view.resolution;
101
+
102
+ const width = resolution[0];
103
+ const height = resolution[1];
104
+
105
+ const tiles_x = Math.ceil(width / MATERIAL_DISPATCH_TILE_SIZE);
106
+ const tile_count = tiles_x * Math.ceil(height / MATERIAL_DISPATCH_TILE_SIZE);
107
+
108
+ const slot_count = contexts.length;
109
+ // never empty: an empty list still clears the G-buffer, and the scan wants room for one
110
+ const list_count = Math.max(1, slot_count * tile_count);
111
+
112
+ const classify_settings = {
113
+ resolution,
114
+ tiles_x,
115
+ tile_count,
116
+ slot_count,
117
+ };
118
+
119
+ const block_count_x = Math.ceil(width / MATERIAL_CLASSIFY_BLOCK_SIZE);
120
+ const block_count_y = Math.ceil(height / MATERIAL_CLASSIFY_BLOCK_SIZE);
121
+
122
+ // at least one, so there is a buffer to name in the graph when the list is empty
123
+ records.reserve(Math.max(1, slot_count));
124
+
125
+ const gr_material_slots = graph_import_raw_buffer({
126
+ graph,
127
+ value: material_slots.buffer,
128
+ offset: material_slots.byteOffset,
129
+ size: material_slots.byteLength,
130
+ label: "material slots",
131
+ usage: GPUBufferUsage.STORAGE,
132
+ });
133
+
134
+ // 1. count
135
+ const count_data = {};
136
+
137
+ const count_pass = graph.add("Material dispatch / count", count_data, (data, resources, context) => {
138
+ const encoder = context.encoder;
139
+
140
+ shader_material_pixel_count.dispatch({
141
+ encoder,
142
+ bindings: {
143
+ viz_buffer_mesh: resources.get(data.viz_mesh).obtainView(),
144
+ material_slots: resources.get(data.material_slots),
145
+ counts: resources.get(data.counts),
146
+ settings: encoder.allocateTransientValueBuffer(MATERIAL_CLASSIFY_SETTINGS_STRUCT, classify_settings, GPUBufferUsage.UNIFORM),
147
+ g_buffer_pbr: resources.get(data.g_pbr).obtainView(),
148
+ g_buffer_normal: resources.get(data.g_normal).obtainView(),
149
+ g_buffer_albedo: resources.get(data.g_albedo).obtainView(),
150
+ g_buffer_emissive: resources.get(data.g_emissive).obtainView(),
151
+ scene_database: view.scene.scene_database_buffer,
152
+ },
153
+ group_count_x: block_count_x,
154
+ group_count_y: block_count_y,
155
+ });
156
+ });
157
+
158
+ count_data.viz_mesh = count_pass.read(texture_viz_mesh);
159
+ count_data.material_slots = count_pass.read(gr_material_slots);
160
+ count_data.counts = count_pass.create("material pixel counts", BufferResourceDescriptor.empty(
161
+ round_up(16 + list_count * 4, 16),
162
+ GPUBufferUsage.STORAGE
163
+ ));
164
+ count_data.g_pbr = count_pass.create("g-buffer / PBR", storage_target(G_BUFFER_COLOR_TARGETS[0].format, resolution));
165
+ count_data.g_normal = count_pass.write(texture_normal);
166
+ count_data.g_albedo = count_pass.create("g-buffer / Albedo", storage_target(G_BUFFER_COLOR_TARGETS[2].format, resolution));
167
+ count_data.g_emissive = count_pass.create("g-buffer / Emissive", storage_target(G_BUFFER_COLOR_TARGETS[3].format, resolution));
168
+
169
+ // 2. scan
170
+ const gr_offsets = graph_prefix_scan_csdldf({
171
+ graph,
172
+ input: count_data.counts,
173
+ output: graph_create_buffer(graph, "material pixel offsets", prefix_scan_csdldf_output_descriptor(list_count)),
174
+ element_max_count: list_count,
175
+ });
176
+
177
+ // 3. dispatch records
178
+ const commands_settings = { tile_count, slot_count };
179
+
180
+ const commands_data = {};
181
+
182
+ const commands_pass = graph.add("Material dispatch / commands", commands_data, (data, resources, context) => {
183
+ if (slot_count === 0) {
184
+ return;
185
+ }
186
+
187
+ const encoder = context.encoder;
188
+
189
+ shader_material_dispatch_commands.dispatchThreads({
190
+ encoder,
191
+ bindings: {
192
+ offsets: resources.get(data.offsets),
193
+ records: resources.get(data.records),
194
+ settings: encoder.allocateTransientValueBuffer(MATERIAL_DISPATCH_COMMANDS_SETTINGS_STRUCT, commands_settings, GPUBufferUsage.UNIFORM),
195
+ },
196
+ thread_count_x: slot_count,
197
+ });
198
+ });
199
+
200
+ commands_data.offsets = commands_pass.read(gr_offsets);
201
+ commands_data.records = commands_pass.write(graph_import_buffer(graph, records.buffer, "material dispatch records"));
202
+
203
+ // 4. scatter
204
+ const scatter_data = {};
205
+
206
+ const scatter_pass = graph.add("Material dispatch / scatter", scatter_data, (data, resources, context) => {
207
+ const encoder = context.encoder;
208
+
209
+ shader_material_pixel_scatter.dispatch({
210
+ encoder,
211
+ bindings: {
212
+ viz_buffer_mesh: resources.get(data.viz_mesh).obtainView(),
213
+ material_slots: resources.get(data.material_slots),
214
+ settings: encoder.allocateTransientValueBuffer(MATERIAL_CLASSIFY_SETTINGS_STRUCT, classify_settings, GPUBufferUsage.UNIFORM),
215
+ offsets: resources.get(data.offsets),
216
+ cursors: resources.get(data.cursors),
217
+ material_quads: resources.get(data.material_quads),
218
+ scene_database: view.scene.scene_database_buffer,
219
+ },
220
+ group_count_x: block_count_x,
221
+ group_count_y: block_count_y,
222
+ });
223
+ });
224
+
225
+ scatter_data.viz_mesh = scatter_pass.read(texture_viz_mesh);
226
+ scatter_data.material_slots = scatter_pass.read(gr_material_slots);
227
+ scatter_data.offsets = scatter_pass.read(gr_offsets);
228
+ scatter_data.cursors = scatter_pass.create("material pixel cursors", BufferResourceDescriptor.empty(
229
+ round_up(list_count * 4, 16),
230
+ GPUBufferUsage.STORAGE
231
+ ));
232
+ scatter_data.material_quads = scatter_pass.create("material quads", BufferResourceDescriptor.from(
233
+ round_up(width * height * 4, 16),
234
+ GPUBufferUsage.STORAGE
235
+ ));
236
+
237
+ // 5. shade
238
+ const shade_data = {};
239
+
240
+ const shade_pass = graph.add("Material dispatch / shade", shade_data, (data, resources, context) => {
241
+ const encoder = context.encoder;
242
+
243
+ const g_pbr = resources.get(data.g_pbr);
244
+ const g_normal = resources.get(data.g_normal);
245
+ const g_albedo = resources.get(data.g_albedo);
246
+ const g_emissive = resources.get(data.g_emissive);
247
+
248
+ const record_buffer = resources.get(data.records);
249
+
250
+ // the group 3s are made over the records' current buffer; a growth since this frame was
251
+ // recorded would leave them on a different one than the commands
252
+ assert.equal(record_buffer, records.buffer, 'the dispatch records were replaced between recording and executing the frame');
253
+
254
+ const bind_1 = bindings.obtain(BindGroupDescriptor.from(GBUFFER_COMPUTE_VIZ_LAYOUT, [
255
+ resources.get(data.viz_triangle).obtainView(),
256
+ resources.get(data.viz_mesh).obtainView(),
257
+ { buffer: view.uniform_buffer.buffer },
258
+ { buffer: view.camera.buffer.buffer },
259
+ { buffer: resources.get(data.material_quads) },
260
+ g_pbr.obtainView(),
261
+ g_normal.obtainView(),
262
+ g_albedo.obtainView(),
263
+ g_emissive.obtainView(),
264
+ ]));
265
+
266
+ const bind_2 = bindings.obtain(BindGroupDescriptor.from(GBUFFER_COMPUTE_SCENE_LAYOUT, [
267
+ { buffer: view.scene.scene_database_buffer },
268
+ { buffer: view.scene.geometries.buffer_metadata },
269
+ { buffer: view.scene.geometries.meshlets.buffer_metadata },
270
+ { buffer: view.scene.geometries.meshlets.buffer_data },
271
+ ]));
272
+
273
+ const pass = encoder.beginComputePass({ label: "Material dispatch / shade" });
274
+
275
+ pass.setBindGroup(1, bind_1);
276
+ pass.setBindGroup(2, bind_2);
277
+
278
+ let bound_pipeline = null;
279
+
280
+ for (let slot = 0; slot < slot_count; slot++) {
281
+ const material_ctx = contexts[slot];
282
+
283
+ const pipeline = pipeline_of(material_ctx);
284
+
285
+ if (pipeline !== bound_pipeline) {
286
+ pass.setPipeline(pipeline);
287
+
288
+ bound_pipeline = pipeline;
289
+ }
290
+
291
+ pass.setBindGroup(0, group_of(material_ctx));
292
+ pass.setBindGroup(3, records.bind_group(slot));
293
+
294
+ pass.dispatchWorkgroupsIndirect(record_buffer, slot * MATERIAL_DISPATCH_RECORD_STRIDE);
295
+ }
296
+
297
+ pass.end();
298
+ });
299
+
300
+ shade_data.viz_triangle = shade_pass.read(texture_viz_triangle);
301
+ shade_data.viz_mesh = shade_pass.read(texture_viz_mesh);
302
+ shade_data.material_quads = shade_pass.read(scatter_data.material_quads);
303
+ shade_data.records = shade_pass.read(commands_data.records);
304
+ shade_data.g_pbr = shade_pass.write(shade_pass.read(count_data.g_pbr));
305
+ shade_data.g_normal = shade_pass.write(shade_pass.read(count_data.g_normal));
306
+ shade_data.g_albedo = shade_pass.write(shade_pass.read(count_data.g_albedo));
307
+ shade_data.g_emissive = shade_pass.write(shade_pass.read(count_data.g_emissive));
308
+
309
+ return {
310
+ g_pbr: shade_data.g_pbr,
311
+ g_normal: shade_data.g_normal,
312
+ g_albedo: shade_data.g_albedo,
313
+ g_emissive: shade_data.g_emissive,
314
+ };
315
+ }
@@ -0,0 +1,16 @@
1
+ /**
2
+ * @type {WebGPUStruct}
3
+ */
4
+ export const MATERIAL_DISPATCH_COMMANDS_SETTINGS_STRUCT: WebGPUStruct;
5
+ /**
6
+ * Write each material's {@link MATERIAL_DISPATCH_RECORD_STRUCT}: its run of the pixel list, from
7
+ * the inclusive sums of the (material, tile) counts, and the folded workgroup counts its dispatch is
8
+ * issued with — `(0, 1, 1)` for a material with no pixel on screen. One invocation per material,
9
+ * dispatched directly over the G-buffer pass's list.
10
+ *
11
+ * @type {ComputeShader}
12
+ */
13
+ export const shader_material_dispatch_commands: ComputeShader;
14
+ import { WebGPUStruct } from "../../shader/type/WebGPUStruct.js";
15
+ import { ComputeShader } from "../../shader/ComputeShader.js";
16
+ //# sourceMappingURL=shader_material_dispatch_commands.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"shader_material_dispatch_commands.d.ts","sourceRoot":"","sources":["../../../../../../src/shade/renderer/material/dispatch/shader_material_dispatch_commands.js"],"names":[],"mappings":"AAWA;;GAEG;AACH,yDAFU,YAAY,CAKiB;AAqDvC;;;;;;;GAOG;AACH,gDAFU,aAAa,CAMpB;6BA1E0B,mCAAmC;8BAJlC,+BAA+B"}