@plasius/gpu-renderer 0.2.56 → 0.2.58

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.
@@ -159,7 +159,7 @@ function chooseSplitAxis(triangles) {
159
159
  return extent[1] >= extent[2] ? 1 : 2;
160
160
  }
161
161
 
162
- function buildBvh(triangles, maxLeafTriangles = 4) {
162
+ export function buildWavefrontBoundsBvh(triangles, maxLeafTriangles = 4) {
163
163
  if (triangles.length === 0) {
164
164
  return Object.freeze({ nodes: Object.freeze([]), triangles: Object.freeze([]) });
165
165
  }
@@ -217,7 +217,7 @@ export function createWavefrontMeshAcceleration(meshes = [], gpuMaterialSource =
217
217
  const source = Array.isArray(meshes) ? meshes : [meshes];
218
218
  const resolvedMaterialSource = gpuMaterialSource ?? createWavefrontGpuMaterialSource(source);
219
219
  const triangles = createMeshTriangleRecords(source, resolvedMaterialSource);
220
- return buildBvh(triangles);
220
+ return buildWavefrontBoundsBvh(triangles);
221
221
  }
222
222
 
223
223
  export function estimateMeshSourceShape(meshes) {
@@ -3,10 +3,10 @@ import {
3
3
  createComputePipeline,
4
4
  createRenderPipeline,
5
5
  } from "./wavefront-runtime-support.js";
6
- import { PRESENT_WGSL, WAVEFRONT_COMPUTE_WGSL } from "./wavefront-shaders.js";
6
+ import { PRESENT_WGSL, createWavefrontPathTracingComputeShaderSource } from "./wavefront-shaders.js";
7
7
  import { CONFIG_BUFFER_BYTES } from "./wavefront-core.js";
8
8
 
9
- export function createWavefrontBindGroupLayouts(device, constants) {
9
+ export function createWavefrontBindGroupLayouts(device, constants, instanced = false) {
10
10
  const trace = device.createBindGroupLayout({
11
11
  label: "plasius.wavefront.traceBindGroupLayout",
12
12
  entries: [
@@ -59,6 +59,7 @@ export function createWavefrontBindGroupLayouts(device, constants) {
59
59
  { binding: 42, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
60
60
  { binding: 43, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
61
61
  { binding: 44, visibility: constants.shader.COMPUTE, texture: { sampleType: "float" } },
62
+ ...(instanced ? [{binding:47, visibility:constants.shader.COMPUTE, texture:{sampleType:"uint"}}] : []),
62
63
  ],
63
64
  });
64
65
  const acceleration = device.createBindGroupLayout({
@@ -258,12 +259,12 @@ export async function createWavefrontPresentPipeline(device, presentBindGroupLay
258
259
  });
259
260
  }
260
261
 
261
- export async function createWavefrontPipelineResources({ device, constants, format }) {
262
- const bindGroupLayouts = createWavefrontBindGroupLayouts(device, constants);
262
+ export async function createWavefrontPipelineResources({ device, constants, format, instanced = false }) {
263
+ const bindGroupLayouts = createWavefrontBindGroupLayouts(device, constants, instanced);
263
264
  const pipelineLayouts = createWavefrontPipelineLayouts(device, bindGroupLayouts);
264
265
  const computeShader = device.createShaderModule({
265
266
  label: "plasius.wavefront.computeShader",
266
- code: WAVEFRONT_COMPUTE_WGSL,
267
+ code: createWavefrontPathTracingComputeShaderSource({instanced}),
267
268
  });
268
269
  await assertShaderModuleCompiles(computeShader, "plasius.wavefront.computeShader");
269
270
 
@@ -161,31 +161,33 @@ export function createWavefrontDeviceDescriptor(adapter, options = {}, memory =
161
161
  ...descriptorRequiredLimits,
162
162
  ...(options.requiredLimits ?? {}),
163
163
  };
164
+ const traceStorageBindings = TRACE_STORAGE_BUFFER_BINDINGS;
165
+ const traceTextureBindings = TRACE_SAMPLED_TEXTURE_BINDINGS + (options.instances !== undefined ? 1 : 0);
164
166
  const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
165
167
  if (Number.isFinite(exposedStorageBufferLimit)) {
166
- if (exposedStorageBufferLimit < TRACE_STORAGE_BUFFER_BINDINGS) {
168
+ if (exposedStorageBufferLimit < traceStorageBindings) {
167
169
  throw new Error(
168
- `Wavefront mesh tracing requires maxStorageBuffersPerShaderStage>=${TRACE_STORAGE_BUFFER_BINDINGS}, ` +
170
+ `Wavefront mesh tracing requires maxStorageBuffersPerShaderStage>=${traceStorageBindings}, ` +
169
171
  `but this adapter exposes ${exposedStorageBufferLimit}.`
170
172
  );
171
173
  }
172
174
  requiredLimits.maxStorageBuffersPerShaderStage = Math.max(
173
175
  Number(requiredLimits.maxStorageBuffersPerShaderStage ?? 0),
174
- TRACE_STORAGE_BUFFER_BINDINGS
176
+ traceStorageBindings
175
177
  );
176
178
  }
177
179
 
178
180
  const exposedSampledTextureLimit = Number(adapter?.limits?.maxSampledTexturesPerShaderStage);
179
181
  if (Number.isFinite(exposedSampledTextureLimit)) {
180
- if (exposedSampledTextureLimit < TRACE_SAMPLED_TEXTURE_BINDINGS) {
182
+ if (exposedSampledTextureLimit < traceTextureBindings) {
181
183
  throw new Error(
182
- `Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${TRACE_SAMPLED_TEXTURE_BINDINGS}, ` +
184
+ `Wavefront material tracing requires maxSampledTexturesPerShaderStage>=${traceTextureBindings}, ` +
183
185
  `but this adapter exposes ${exposedSampledTextureLimit}.`
184
186
  );
185
187
  }
186
188
  requiredLimits.maxSampledTexturesPerShaderStage = Math.max(
187
189
  Number(requiredLimits.maxSampledTexturesPerShaderStage ?? 0),
188
- TRACE_SAMPLED_TEXTURE_BINDINGS
190
+ traceTextureBindings
189
191
  );
190
192
  }
191
193
 
@@ -459,6 +459,7 @@ fn no_candidate() -> Candidate {
459
459
  1u,
460
460
  0xffffffffu,
461
461
  0xffffffffu,
462
+ 0xffffffffu,
462
463
  0u,
463
464
  0u
464
465
  );
@@ -485,6 +486,7 @@ fn surface_candidate(
485
486
  uv,
486
487
  frontFace,
487
488
  triangleIndex,
489
+ 0xffffffffu,
488
490
  primitiveId,
489
491
  materialRefId,
490
492
  mediumRefId
@@ -546,7 +548,7 @@ fn intersect_triangle(ray: RayRecord, triangle: TriangleRecord, triangleIndex: u
546
548
  );
547
549
  }
548
550
 
549
- fn intersect_bvh(ray: RayRecord, initialNearest: f32) -> Candidate {
551
+ fn intersect_geometry_bvh(ray: RayRecord, initialNearest: f32, root: u32) -> Candidate {
550
552
  var nearest = initialNearest;
551
553
  var best = no_candidate();
552
554
  if (config.bvhNodeCount == 0u || config.triangleCount == 0u) {
@@ -555,7 +557,7 @@ fn intersect_bvh(ray: RayRecord, initialNearest: f32) -> Candidate {
555
557
 
556
558
  var stack = array<u32, 64>();
557
559
  var stackSize = 1u;
558
- stack[0] = 0u;
560
+ stack[0] = root;
559
561
 
560
562
  loop {
561
563
  if (stackSize == 0u) {
@@ -245,7 +245,7 @@ fn intersectActiveQueue(@builtin(global_invocation_id) globalId: vec3<u32>) {
245
245
  if (meshCandidate.hit == 1u && meshCandidate.distance < nearest) {
246
246
  nearest = meshCandidate.distance;
247
247
  candidate = meshCandidate;
248
- hitTriangle = triangles[meshCandidate.triangleIndex];
248
+ hitTriangle = triangle_for_candidate(meshCandidate);
249
249
  }
250
250
 
251
251
  if (candidate.hit == 0u) {
@@ -283,6 +283,7 @@ struct Candidate {
283
283
  uv: vec2<f32>,
284
284
  frontFace: u32,
285
285
  triangleIndex: u32,
286
+ instanceIndex: u32,
286
287
  primitiveId: u32,
287
288
  materialRefId: u32,
288
289
  mediumRefId: u32,
@@ -1,3 +1,4 @@
1
+ import { WAVEFRONT_INSTANCE_SHADER_WGSL, WAVEFRONT_UNINSTANCED_SHADER_WGSL } from './wavefront-instance-shader.js';
1
2
  import { WAVEFRONT_COMPUTE_WORKGROUP_SIZE } from "./wavefront-workgroup.js";
2
3
  import { WAVEFRONT_SHADER_BVH_WGSL } from "./wavefront-shader-bvh.js";
3
4
  import { WAVEFRONT_SHADER_KERNELS_WGSL } from "./wavefront-shader-kernels.js";
@@ -22,6 +23,7 @@ export const WAVEFRONT_COMPUTE_WGSL = [
22
23
  WAVEFRONT_SHADER_MATERIALS_WGSL,
23
24
  WAVEFRONT_SHADER_LIGHTING_WGSL,
24
25
  WAVEFRONT_SHADER_BVH_WGSL,
26
+ WAVEFRONT_UNINSTANCED_SHADER_WGSL,
25
27
  WAVEFRONT_SHADER_KERNELS_WGSL,
26
28
  ].join("\n");
27
29
 
@@ -34,5 +36,7 @@ export function createWavefrontPathTracingComputeShaderSource(options = {}) {
34
36
  if (workgroupSize !== WAVEFRONT_COMPUTE_WORKGROUP_SIZE) {
35
37
  throw new Error(`wavefront mesh compute currently requires workgroupSize=${WAVEFRONT_COMPUTE_WORKGROUP_SIZE}`);
36
38
  }
37
- return WAVEFRONT_COMPUTE_WGSL;
39
+ return options.instanced === true
40
+ ? WAVEFRONT_COMPUTE_WGSL.replace(WAVEFRONT_UNINSTANCED_SHADER_WGSL, WAVEFRONT_INSTANCE_SHADER_WGSL)
41
+ : WAVEFRONT_COMPUTE_WGSL;
38
42
  }
@@ -0,0 +1,12 @@
1
+ import { waitForSubmittedGpuWork } from './wavefront-gpu-synchronization.js';
2
+
3
+ // Large writeBuffer calls need temporary driver staging storage in addition to
4
+ // the destination. Complete each bounded chunk before requesting the next one.
5
+ export async function uploadWavefrontBuffer(device, buffer, data, {chunkBytes=8*1024*1024}={}) {
6
+ if(!Number.isSafeInteger(chunkBytes)||chunkBytes<4||chunkBytes%4!==0) throw new Error('GPU upload chunk size must be a positive multiple of four.');
7
+ if(data.byteLength<=chunkBytes){device.queue.writeBuffer(buffer,0,data);return;}
8
+ for(let offset=0;offset<data.byteLength;offset+=chunkBytes){
9
+ device.queue.writeBuffer(buffer,offset,data,offset,Math.min(chunkBytes,data.byteLength-offset));
10
+ await waitForSubmittedGpuWork(device,{timeoutMs:10000,maxWaitMs:10000,allowTimeout:false});
11
+ }
12
+ }