@plasius/gpu-renderer 0.2.42 → 0.2.44
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +60 -1
- package/README.md +51 -3
- package/dist/index.cjs +381 -42
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +381 -42
- package/dist/index.js.map +1 -1
- package/package.json +7 -6
- package/src/feedback-diagnostics.js +190 -0
- package/src/index.d.ts +99 -13
- package/src/index.js +6 -0
- package/src/wavefront-compute.js +31 -46
- package/src/wavefront-config.js +161 -23
- package/src/wavefront-gpu-resources.js +8 -1
- package/src/wavefront-runtime-support.js +73 -3
package/src/wavefront-config.js
CHANGED
|
@@ -19,7 +19,6 @@ import {
|
|
|
19
19
|
EMISSIVE_TRIANGLE_INDEX_BYTES,
|
|
20
20
|
EMPTY_TERMINATION_METRICS,
|
|
21
21
|
ENVIRONMENT_PORTAL_RECORD_BYTES,
|
|
22
|
-
GPU_MATERIAL_RECORD_BYTES,
|
|
23
22
|
HIT_RECORD_BYTES,
|
|
24
23
|
INDIRECT_DISPATCH_ARGS_BYTES,
|
|
25
24
|
MAX_PATH_TRACING_DEPTH,
|
|
@@ -69,6 +68,16 @@ import {
|
|
|
69
68
|
resolveAccelerationBuildMode,
|
|
70
69
|
} from "./wavefront-scene-data.js";
|
|
71
70
|
|
|
71
|
+
const WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT = 256;
|
|
72
|
+
|
|
73
|
+
function allocatedRecordBytes(count, recordBytes) {
|
|
74
|
+
return Math.max(1, count) * recordBytes;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
function alignByteSize(value, alignment) {
|
|
78
|
+
return Math.ceil(value / alignment) * alignment;
|
|
79
|
+
}
|
|
80
|
+
|
|
72
81
|
function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
|
|
73
82
|
const source = input ?? {};
|
|
74
83
|
return Object.freeze({
|
|
@@ -257,6 +266,18 @@ function resolveCamera(input, width, height) {
|
|
|
257
266
|
}
|
|
258
267
|
|
|
259
268
|
export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
269
|
+
const width = readPositiveInteger("width", options.width, DEFAULT_WIDTH);
|
|
270
|
+
const height = readPositiveInteger("height", options.height, DEFAULT_HEIGHT);
|
|
271
|
+
const tileSize = readPositiveInteger("tileSize", options.tileSize, DEFAULT_TILE_SIZE);
|
|
272
|
+
const samplesPerPixel = clamp(
|
|
273
|
+
readPositiveInteger(
|
|
274
|
+
"samplesPerPixel",
|
|
275
|
+
options.samplesPerPixel,
|
|
276
|
+
DEFAULT_SAMPLES_PER_PIXEL
|
|
277
|
+
),
|
|
278
|
+
1,
|
|
279
|
+
MAX_SAMPLES_PER_PIXEL
|
|
280
|
+
);
|
|
260
281
|
const tilePixelCapacity = readPositiveInteger(
|
|
261
282
|
"tilePixelCapacity",
|
|
262
283
|
options.tilePixelCapacity,
|
|
@@ -289,20 +310,106 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
289
310
|
options.environmentPortalCapacity,
|
|
290
311
|
0
|
|
291
312
|
);
|
|
292
|
-
|
|
313
|
+
readNonNegativeInteger("materialCapacity", options.materialCapacity, 0);
|
|
314
|
+
const meshVertexCount = readNonNegativeInteger(
|
|
315
|
+
"meshVertexCount",
|
|
316
|
+
options.meshVertexCount,
|
|
317
|
+
0
|
|
318
|
+
);
|
|
319
|
+
const meshIndexCount = readNonNegativeInteger(
|
|
320
|
+
"meshIndexCount",
|
|
321
|
+
options.meshIndexCount,
|
|
322
|
+
0
|
|
323
|
+
);
|
|
324
|
+
const meshRangeCount = readNonNegativeInteger(
|
|
325
|
+
"meshRangeCount",
|
|
326
|
+
options.meshRangeCount,
|
|
327
|
+
0
|
|
328
|
+
);
|
|
329
|
+
const bvhSortStageCount = readNonNegativeInteger(
|
|
330
|
+
"bvhSortStageCount",
|
|
331
|
+
options.bvhSortStageCount,
|
|
332
|
+
0
|
|
333
|
+
);
|
|
334
|
+
const bvhBuildLevelCount = readNonNegativeInteger(
|
|
335
|
+
"bvhBuildLevelCount",
|
|
336
|
+
options.bvhBuildLevelCount,
|
|
337
|
+
0
|
|
338
|
+
);
|
|
339
|
+
const minUniformBufferOffsetAlignment = readPositiveInteger(
|
|
340
|
+
"minUniformBufferOffsetAlignment",
|
|
341
|
+
options.minUniformBufferOffsetAlignment,
|
|
342
|
+
WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT
|
|
343
|
+
);
|
|
344
|
+
const denoise = options.denoise !== false;
|
|
345
|
+
const deferredPathResolve = options.deferredPathResolve !== false;
|
|
293
346
|
const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
|
|
294
347
|
const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
|
|
295
348
|
const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
|
|
296
349
|
const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
|
|
297
350
|
const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
|
|
298
|
-
const triangleBytes = triangleCapacity
|
|
299
|
-
const materialTableBytes =
|
|
351
|
+
const triangleBytes = allocatedRecordBytes(triangleCapacity, TRIANGLE_RECORD_BYTES);
|
|
352
|
+
const materialTableBytes = 0;
|
|
300
353
|
const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
|
|
301
|
-
const bvhLeafReferenceBytes =
|
|
354
|
+
const bvhLeafReferenceBytes = allocatedRecordBytes(
|
|
355
|
+
bvhLeafSortCapacity,
|
|
356
|
+
BVH_LEAF_REF_RECORD_BYTES
|
|
357
|
+
);
|
|
302
358
|
const emissiveTriangleMetadataBytes =
|
|
303
359
|
emissiveTriangleCapacity * BVH_NODE_RECORD_BYTES;
|
|
304
|
-
const
|
|
305
|
-
|
|
360
|
+
const bvhCombinedBytes = allocatedRecordBytes(
|
|
361
|
+
bvhNodeCapacity + emissiveTriangleCapacity,
|
|
362
|
+
BVH_NODE_RECORD_BYTES
|
|
363
|
+
);
|
|
364
|
+
const meshVertexBytes = allocatedRecordBytes(meshVertexCount, MESH_VERTEX_RECORD_BYTES);
|
|
365
|
+
const meshIndexBytes = allocatedRecordBytes(meshIndexCount, 4);
|
|
366
|
+
const meshRangeBytes = allocatedRecordBytes(meshRangeCount, MESH_RANGE_RECORD_BYTES);
|
|
367
|
+
const environmentPortalBytes = allocatedRecordBytes(
|
|
368
|
+
environmentPortalCapacity,
|
|
369
|
+
ENVIRONMENT_PORTAL_RECORD_BYTES
|
|
370
|
+
);
|
|
371
|
+
const tileCount = Math.ceil(width / tileSize) * Math.ceil(height / tileSize);
|
|
372
|
+
const outputConfigSlotCount = deferredPathResolve ? 0 : tileCount;
|
|
373
|
+
const frameConfigSlotCount = Math.max(
|
|
374
|
+
1,
|
|
375
|
+
tileCount * samplesPerPixel + outputConfigSlotCount + (denoise ? 1 : 0)
|
|
376
|
+
);
|
|
377
|
+
const configBufferStride = alignByteSize(
|
|
378
|
+
CONFIG_BUFFER_BYTES,
|
|
379
|
+
minUniformBufferOffsetAlignment
|
|
380
|
+
);
|
|
381
|
+
const frameConfigBytes = frameConfigSlotCount * configBufferStride;
|
|
382
|
+
const bvhBuildConfigBytes = Math.max(
|
|
383
|
+
1,
|
|
384
|
+
1 + bvhSortStageCount + bvhBuildLevelCount
|
|
385
|
+
) * configBufferStride;
|
|
386
|
+
const sceneStorageBufferBindingBytes = Math.max(
|
|
387
|
+
sceneObjectBytes,
|
|
388
|
+
triangleBytes,
|
|
389
|
+
bvhCombinedBytes,
|
|
390
|
+
meshVertexBytes,
|
|
391
|
+
meshIndexBytes,
|
|
392
|
+
meshRangeBytes,
|
|
393
|
+
environmentPortalBytes,
|
|
394
|
+
bvhLeafReferenceBytes
|
|
395
|
+
);
|
|
396
|
+
const totalHotBufferBytes =
|
|
397
|
+
queueBytes * 2 +
|
|
398
|
+
hitBytes +
|
|
399
|
+
accumulationBytes +
|
|
400
|
+
pathVertexBytes +
|
|
401
|
+
sceneObjectBytes +
|
|
402
|
+
triangleBytes +
|
|
403
|
+
bvhCombinedBytes +
|
|
404
|
+
meshVertexBytes +
|
|
405
|
+
meshIndexBytes +
|
|
406
|
+
meshRangeBytes +
|
|
407
|
+
bvhLeafReferenceBytes +
|
|
408
|
+
environmentPortalBytes +
|
|
409
|
+
frameConfigBytes +
|
|
410
|
+
bvhBuildConfigBytes +
|
|
411
|
+
COUNTER_BUFFER_BYTES +
|
|
412
|
+
INDIRECT_DISPATCH_ARGS_BYTES;
|
|
306
413
|
|
|
307
414
|
return Object.freeze({
|
|
308
415
|
queueBytes,
|
|
@@ -314,27 +421,48 @@ export function estimateWavefrontPathTracingMemory(options = {}) {
|
|
|
314
421
|
triangleBytes,
|
|
315
422
|
materialTableBytes,
|
|
316
423
|
bvhNodeBytes,
|
|
424
|
+
bvhCombinedBytes,
|
|
317
425
|
bvhLeafReferenceBytes,
|
|
318
426
|
emissiveTriangleMetadataBytes,
|
|
427
|
+
meshVertexBytes,
|
|
428
|
+
meshIndexBytes,
|
|
429
|
+
meshRangeBytes,
|
|
319
430
|
environmentPortalBytes,
|
|
320
431
|
configBytes: CONFIG_BUFFER_BYTES,
|
|
432
|
+
configBufferStride,
|
|
433
|
+
frameConfigBytes,
|
|
434
|
+
bvhBuildConfigBytes,
|
|
321
435
|
counterBytes: COUNTER_BUFFER_BYTES,
|
|
322
436
|
indirectDispatchBytes: INDIRECT_DISPATCH_ARGS_BYTES,
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
437
|
+
sceneStorageBufferBindingBytes,
|
|
438
|
+
totalHotBufferBytes,
|
|
439
|
+
});
|
|
440
|
+
}
|
|
441
|
+
|
|
442
|
+
export function estimateWavefrontPathTracingMemoryForConfig(config, limits = {}) {
|
|
443
|
+
return estimateWavefrontPathTracingMemory({
|
|
444
|
+
width: config.width,
|
|
445
|
+
height: config.height,
|
|
446
|
+
tileSize: config.tileSize,
|
|
447
|
+
samplesPerPixel: config.samplesPerPixel,
|
|
448
|
+
tilePixelCapacity: config.tilePixelCapacity,
|
|
449
|
+
maxDepth: config.maxDepth,
|
|
450
|
+
sceneObjectCapacity: config.sceneObjectCapacity,
|
|
451
|
+
triangleCapacity: config.triangleCapacity,
|
|
452
|
+
bvhNodeCapacity: config.bvhNodeCapacity,
|
|
453
|
+
bvhLeafSortCapacity: config.bvhLeafSortCapacity,
|
|
454
|
+
emissiveTriangleCapacity: config.emissiveTriangleCapacity,
|
|
455
|
+
environmentPortalCapacity: config.environmentPortalCapacity,
|
|
456
|
+
meshVertexCount: config.gpuMeshSource.vertices.count,
|
|
457
|
+
meshIndexCount: config.gpuMeshSource.indices.count,
|
|
458
|
+
meshRangeCount: config.gpuMeshSource.meshes.count,
|
|
459
|
+
bvhSortStageCount: config.bvhSortStages.length,
|
|
460
|
+
bvhBuildLevelCount: config.bvhBuildLevels.length,
|
|
461
|
+
denoise: config.denoise,
|
|
462
|
+
deferredPathResolve: config.deferredPathResolve,
|
|
463
|
+
minUniformBufferOffsetAlignment:
|
|
464
|
+
limits.minUniformBufferOffsetAlignment ??
|
|
465
|
+
WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT,
|
|
338
466
|
});
|
|
339
467
|
}
|
|
340
468
|
|
|
@@ -509,15 +637,25 @@ export function createWavefrontPathTracingComputeConfig(options = {}) {
|
|
|
509
637
|
denoise: options.denoise !== false,
|
|
510
638
|
frameIndex: readNonNegativeInteger("frameIndex", options.frameIndex, 0),
|
|
511
639
|
memory: estimateWavefrontPathTracingMemory({
|
|
640
|
+
width,
|
|
641
|
+
height,
|
|
642
|
+
tileSize,
|
|
643
|
+
samplesPerPixel,
|
|
512
644
|
tilePixelCapacity,
|
|
513
645
|
maxDepth,
|
|
514
646
|
sceneObjectCapacity,
|
|
515
647
|
triangleCapacity,
|
|
516
|
-
materialCapacity: gpuMaterialSource.count,
|
|
517
648
|
bvhNodeCapacity,
|
|
518
649
|
bvhLeafSortCapacity,
|
|
519
650
|
emissiveTriangleCapacity: emissiveTriangleIndices.capacity,
|
|
520
651
|
environmentPortalCapacity,
|
|
652
|
+
meshVertexCount: gpuMeshSource.vertices.count,
|
|
653
|
+
meshIndexCount: gpuMeshSource.indices.count,
|
|
654
|
+
meshRangeCount: gpuMeshSource.meshes.count,
|
|
655
|
+
bvhSortStageCount: bvhSortStages.length,
|
|
656
|
+
bvhBuildLevelCount: bvhBuildLevels.length,
|
|
657
|
+
denoise: options.denoise !== false,
|
|
658
|
+
deferredPathResolve,
|
|
521
659
|
}),
|
|
522
660
|
});
|
|
523
661
|
}
|
|
@@ -4,6 +4,7 @@ import {
|
|
|
4
4
|
DEFAULT_BRDF_LUT_SIZE,
|
|
5
5
|
HIT_RECORD_BYTES,
|
|
6
6
|
MEDIUM_TABLE_ROWS,
|
|
7
|
+
PATH_VERTEX_RECORD_BYTES,
|
|
7
8
|
RAY_RECORD_BYTES,
|
|
8
9
|
} from "./wavefront-core.js";
|
|
9
10
|
import {
|
|
@@ -28,7 +29,13 @@ export function clampTileSizeForDevice(config, device) {
|
|
|
28
29
|
|
|
29
30
|
const maxPixelsByRay = Math.floor(limit / RAY_RECORD_BYTES);
|
|
30
31
|
const maxPixelsByHit = Math.floor(limit / HIT_RECORD_BYTES);
|
|
31
|
-
const
|
|
32
|
+
const maxPixelsByPathVertex = Math.floor(
|
|
33
|
+
limit / ((config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES)
|
|
34
|
+
);
|
|
35
|
+
const maxPixels = Math.max(
|
|
36
|
+
256,
|
|
37
|
+
Math.min(maxPixelsByRay, maxPixelsByHit, maxPixelsByPathVertex)
|
|
38
|
+
);
|
|
32
39
|
if (config.tilePixelCapacity <= maxPixels) {
|
|
33
40
|
return config.tileSize;
|
|
34
41
|
}
|
|
@@ -10,6 +10,37 @@ import {
|
|
|
10
10
|
readPositiveInteger,
|
|
11
11
|
} from "./wavefront-core.js";
|
|
12
12
|
|
|
13
|
+
const WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE = 128 * 1024 * 1024;
|
|
14
|
+
const WEBGPU_DEFAULT_MAX_BUFFER_SIZE = 256 * 1024 * 1024;
|
|
15
|
+
|
|
16
|
+
function readRequestedDeviceLimit(source, name) {
|
|
17
|
+
const rawValue = source?.[name];
|
|
18
|
+
if (rawValue === undefined || rawValue === null) {
|
|
19
|
+
return 0;
|
|
20
|
+
}
|
|
21
|
+
const value = Number(rawValue);
|
|
22
|
+
if (!Number.isSafeInteger(value) || value < 0) {
|
|
23
|
+
throw new Error(`${name} must be a non-negative safe integer.`);
|
|
24
|
+
}
|
|
25
|
+
return value;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
function assertAdapterSupportsRequiredLimit(adapter, name, requiredValue) {
|
|
29
|
+
const availableValue = Number(adapter?.limits?.[name]);
|
|
30
|
+
if (!Number.isFinite(availableValue)) {
|
|
31
|
+
throw new Error(
|
|
32
|
+
`Wavefront scene buffers require ${name}>=${requiredValue} bytes, ` +
|
|
33
|
+
"but this adapter does not expose that limit."
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
if (availableValue < requiredValue) {
|
|
37
|
+
throw new Error(
|
|
38
|
+
`Wavefront scene buffers require ${name}>=${requiredValue} bytes, ` +
|
|
39
|
+
`but this adapter exposes ${availableValue} bytes.`
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
|
|
13
44
|
export function getGpuUsageConstants() {
|
|
14
45
|
if (
|
|
15
46
|
typeof GPUBufferUsage === "undefined" ||
|
|
@@ -123,8 +154,13 @@ export async function createRenderPipeline(device, descriptor) {
|
|
|
123
154
|
return device.createRenderPipeline(descriptor);
|
|
124
155
|
}
|
|
125
156
|
|
|
126
|
-
export function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
127
|
-
const
|
|
157
|
+
export function createWavefrontDeviceDescriptor(adapter, options = {}, memory = null) {
|
|
158
|
+
const descriptor = { ...(options.deviceDescriptor ?? {}) };
|
|
159
|
+
const descriptorRequiredLimits = { ...(descriptor.requiredLimits ?? {}) };
|
|
160
|
+
const requiredLimits = {
|
|
161
|
+
...descriptorRequiredLimits,
|
|
162
|
+
...(options.requiredLimits ?? {}),
|
|
163
|
+
};
|
|
128
164
|
const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
|
|
129
165
|
if (Number.isFinite(exposedStorageBufferLimit)) {
|
|
130
166
|
if (exposedStorageBufferLimit < TRACE_STORAGE_BUFFER_BINDINGS) {
|
|
@@ -153,7 +189,40 @@ export function createWavefrontDeviceDescriptor(adapter, options = {}) {
|
|
|
153
189
|
);
|
|
154
190
|
}
|
|
155
191
|
|
|
156
|
-
const
|
|
192
|
+
const sceneStorageBufferBindingBytes = Number(
|
|
193
|
+
memory?.sceneStorageBufferBindingBytes ?? 0
|
|
194
|
+
);
|
|
195
|
+
const requestedStorageBufferBindingSize = Math.max(
|
|
196
|
+
readRequestedDeviceLimit(descriptorRequiredLimits, "maxStorageBufferBindingSize"),
|
|
197
|
+
readRequestedDeviceLimit(options.requiredLimits, "maxStorageBufferBindingSize"),
|
|
198
|
+
sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE
|
|
199
|
+
? sceneStorageBufferBindingBytes
|
|
200
|
+
: 0
|
|
201
|
+
);
|
|
202
|
+
if (requestedStorageBufferBindingSize > 0) {
|
|
203
|
+
assertAdapterSupportsRequiredLimit(
|
|
204
|
+
adapter,
|
|
205
|
+
"maxStorageBufferBindingSize",
|
|
206
|
+
requestedStorageBufferBindingSize
|
|
207
|
+
);
|
|
208
|
+
requiredLimits.maxStorageBufferBindingSize = requestedStorageBufferBindingSize;
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const requestedMaxBufferSize = Math.max(
|
|
212
|
+
readRequestedDeviceLimit(descriptorRequiredLimits, "maxBufferSize"),
|
|
213
|
+
readRequestedDeviceLimit(options.requiredLimits, "maxBufferSize"),
|
|
214
|
+
sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_BUFFER_SIZE
|
|
215
|
+
? sceneStorageBufferBindingBytes
|
|
216
|
+
: 0,
|
|
217
|
+
requestedStorageBufferBindingSize > WEBGPU_DEFAULT_MAX_BUFFER_SIZE
|
|
218
|
+
? requestedStorageBufferBindingSize
|
|
219
|
+
: 0
|
|
220
|
+
);
|
|
221
|
+
if (requestedMaxBufferSize > 0) {
|
|
222
|
+
assertAdapterSupportsRequiredLimit(adapter, "maxBufferSize", requestedMaxBufferSize);
|
|
223
|
+
requiredLimits.maxBufferSize = requestedMaxBufferSize;
|
|
224
|
+
}
|
|
225
|
+
|
|
157
226
|
const requiredFeatures = Array.from(descriptor.requiredFeatures ?? []);
|
|
158
227
|
const adapterFeatures = adapter?.features;
|
|
159
228
|
const timestampQuerySupported =
|
|
@@ -217,6 +286,7 @@ export function createGpuAdapterParallelismDiagnostics(adapter, device) {
|
|
|
217
286
|
maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
|
|
218
287
|
maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
|
|
219
288
|
maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize"),
|
|
289
|
+
maxBufferSize: readGpuLimit(adapter, device, "maxBufferSize"),
|
|
220
290
|
}),
|
|
221
291
|
configuredWorkgroupSize: WORKGROUP_SIZE,
|
|
222
292
|
});
|