@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/dist/index.cjs CHANGED
@@ -24,8 +24,12 @@ __export(index_exports, {
24
24
  applyMediumTransmittance: () => applyMediumTransmittance,
25
25
  beerLambertTransmittance: () => beerLambertTransmittance,
26
26
  bindRendererToXrManager: () => bindRendererToXrManager,
27
+ bucketFeedbackGameFrameRate: () => bucketFeedbackGameFrameRate,
28
+ bucketFeedbackGameFrameTime: () => bucketFeedbackGameFrameTime,
29
+ bucketFeedbackGameViewport: () => bucketFeedbackGameViewport,
27
30
  createAnimatedSceneRenderer: () => createAnimatedSceneRenderer,
28
31
  createDefaultWavefrontSceneObjects: () => createDefaultWavefrontSceneObjects,
32
+ createFeedbackGameDiagnosticSnapshot: () => createFeedbackGameDiagnosticSnapshot,
29
33
  createGpuRenderer: () => createGpuRenderer,
30
34
  createMediumStack: () => createMediumStack,
31
35
  createProfessionalAnimatedSceneRenderer: () => createProfessionalAnimatedSceneRenderer,
@@ -2294,6 +2298,140 @@ async function createProfessionalAnimatedSceneRenderer(options = {}) {
2294
2298
  };
2295
2299
  }
2296
2300
 
2301
+ // src/feedback-diagnostics.js
2302
+ var import_feedback_diagnostics = require("@plasius/gpu-shared/feedback-diagnostics");
2303
+ var inputKeys = Object.freeze([
2304
+ "featureEnabled",
2305
+ "capabilityGranted",
2306
+ "consentConfirmed",
2307
+ "surfaceId",
2308
+ "renderer",
2309
+ "backend",
2310
+ "viewportWidth",
2311
+ "viewportHeight",
2312
+ "frameRate",
2313
+ "frameTimeMs",
2314
+ "featureIds",
2315
+ "counters",
2316
+ "errorCodes"
2317
+ ]);
2318
+ function bucketFeedbackGameViewport(width, height) {
2319
+ if (typeof width !== "number" || typeof height !== "number" || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
2320
+ return "unknown";
2321
+ }
2322
+ const shorterEdge = Math.min(width, height);
2323
+ const longerEdge = Math.max(width, height);
2324
+ const size = shorterEdge < 600 ? "small" : shorterEdge < 900 || longerEdge < 1440 ? "medium" : "large";
2325
+ const orientation = width > height ? "landscape" : "portrait";
2326
+ return `${size}-${orientation}`;
2327
+ }
2328
+ function bucketFeedbackGameFrameRate(frameRate) {
2329
+ if (typeof frameRate !== "number" || !Number.isFinite(frameRate) || frameRate < 0) {
2330
+ return "unknown";
2331
+ }
2332
+ if (frameRate < 15) {
2333
+ return "under-15";
2334
+ }
2335
+ if (frameRate < 30) {
2336
+ return "15-29";
2337
+ }
2338
+ if (frameRate < 60) {
2339
+ return "30-59";
2340
+ }
2341
+ return "60-plus";
2342
+ }
2343
+ function bucketFeedbackGameFrameTime(frameTimeMs) {
2344
+ if (typeof frameTimeMs !== "number" || !Number.isFinite(frameTimeMs) || frameTimeMs < 0) {
2345
+ return "unknown";
2346
+ }
2347
+ if (frameTimeMs < 17) {
2348
+ return "under-17ms";
2349
+ }
2350
+ if (frameTimeMs < 34) {
2351
+ return "17-33ms";
2352
+ }
2353
+ if (frameTimeMs < 67) {
2354
+ return "34-66ms";
2355
+ }
2356
+ return "over-66ms";
2357
+ }
2358
+ function createFeedbackGameDiagnosticSnapshot(input) {
2359
+ try {
2360
+ return createFeedbackGameDiagnosticSnapshotInternal(input);
2361
+ } catch {
2362
+ throw new TypeError("Invalid privacy-safe renderer diagnostics input.");
2363
+ }
2364
+ }
2365
+ function createFeedbackGameDiagnosticSnapshotInternal(input) {
2366
+ const snapshot = snapshotInputIfEnabled(input);
2367
+ if (snapshot === null) {
2368
+ return null;
2369
+ }
2370
+ const registration = typeof snapshot.surfaceId === "string" && Object.hasOwn(
2371
+ import_feedback_diagnostics.feedbackGameDiagnosticSurfaceRegistrations,
2372
+ snapshot.surfaceId
2373
+ ) ? import_feedback_diagnostics.feedbackGameDiagnosticSurfaceRegistrations[snapshot.surfaceId] : void 0;
2374
+ return (0, import_feedback_diagnostics.parseFeedbackGameDiagnostics)({
2375
+ type: "feedback-game-diagnostics",
2376
+ version: import_feedback_diagnostics.FEEDBACK_GAME_DIAGNOSTICS_CONTRACT_VERSION,
2377
+ surfaceId: snapshot.surfaceId,
2378
+ consentConfirmed: true,
2379
+ provenanceContractId: registration?.provenanceContractId,
2380
+ renderer: snapshot.renderer,
2381
+ backend: snapshot.backend,
2382
+ viewportBucket: bucketFeedbackGameViewport(
2383
+ snapshot.viewportWidth,
2384
+ snapshot.viewportHeight
2385
+ ),
2386
+ frameRateBucket: bucketFeedbackGameFrameRate(snapshot.frameRate),
2387
+ frameTimeBucket: bucketFeedbackGameFrameTime(snapshot.frameTimeMs),
2388
+ featureIds: snapshot.featureIds,
2389
+ counters: snapshot.counters,
2390
+ errorCodes: snapshot.errorCodes
2391
+ });
2392
+ }
2393
+ function snapshotInputIfEnabled(input) {
2394
+ assertValid(
2395
+ input !== null && typeof input === "object" && !Array.isArray(input) && (Object.getPrototypeOf(input) === Object.prototype || Object.getPrototypeOf(input) === null)
2396
+ );
2397
+ const gateKeys = [
2398
+ "featureEnabled",
2399
+ "capabilityGranted",
2400
+ "consentConfirmed"
2401
+ ];
2402
+ const snapshot = /* @__PURE__ */ Object.create(null);
2403
+ for (const key of gateKeys) {
2404
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
2405
+ assertValid(
2406
+ descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value") && typeof descriptor.value === "boolean"
2407
+ );
2408
+ snapshot[key] = descriptor.value;
2409
+ }
2410
+ const { featureEnabled, capabilityGranted, consentConfirmed } = snapshot;
2411
+ if (!featureEnabled || !capabilityGranted || !consentConfirmed) {
2412
+ return null;
2413
+ }
2414
+ const actualKeys = Reflect.ownKeys(input);
2415
+ assertValid(
2416
+ actualKeys.length === inputKeys.length && inputKeys.every((key) => actualKeys.includes(key)) && actualKeys.every(
2417
+ (key) => typeof key === "string" && inputKeys.includes(key)
2418
+ )
2419
+ );
2420
+ for (const key of inputKeys.filter((key2) => !gateKeys.includes(key2))) {
2421
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
2422
+ assertValid(
2423
+ descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value")
2424
+ );
2425
+ snapshot[key] = descriptor.value;
2426
+ }
2427
+ return Object.freeze(snapshot);
2428
+ }
2429
+ function assertValid(condition) {
2430
+ if (!condition) {
2431
+ throw new TypeError("Invalid privacy-safe renderer diagnostics input.");
2432
+ }
2433
+ }
2434
+
2297
2435
  // src/wavefront-frame-runtime.js
2298
2436
  function createGpuParallelismCounters() {
2299
2437
  return {
@@ -4527,6 +4665,13 @@ function dispatchWavefrontGpuAccelerationBuild({
4527
4665
  }
4528
4666
 
4529
4667
  // src/wavefront-config.js
4668
+ var WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT = 256;
4669
+ function allocatedRecordBytes(count, recordBytes) {
4670
+ return Math.max(1, count) * recordBytes;
4671
+ }
4672
+ function alignByteSize(value, alignment) {
4673
+ return Math.ceil(value / alignment) * alignment;
4674
+ }
4530
4675
  function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
4531
4676
  const source = input ?? {};
4532
4677
  return Object.freeze({
@@ -4703,6 +4848,18 @@ function resolveCamera(input, width, height) {
4703
4848
  });
4704
4849
  }
4705
4850
  function estimateWavefrontPathTracingMemory(options = {}) {
4851
+ const width = readPositiveInteger2("width", options.width, DEFAULT_WIDTH);
4852
+ const height = readPositiveInteger2("height", options.height, DEFAULT_HEIGHT);
4853
+ const tileSize = readPositiveInteger2("tileSize", options.tileSize, DEFAULT_TILE_SIZE);
4854
+ const samplesPerPixel = clamp2(
4855
+ readPositiveInteger2(
4856
+ "samplesPerPixel",
4857
+ options.samplesPerPixel,
4858
+ DEFAULT_SAMPLES_PER_PIXEL
4859
+ ),
4860
+ 1,
4861
+ MAX_SAMPLES_PER_PIXEL
4862
+ );
4706
4863
  const tilePixelCapacity = readPositiveInteger2(
4707
4864
  "tilePixelCapacity",
4708
4865
  options.tilePixelCapacity,
@@ -4735,18 +4892,89 @@ function estimateWavefrontPathTracingMemory(options = {}) {
4735
4892
  options.environmentPortalCapacity,
4736
4893
  0
4737
4894
  );
4738
- const materialCapacity = readNonNegativeInteger2("materialCapacity", options.materialCapacity, 0);
4895
+ readNonNegativeInteger2("materialCapacity", options.materialCapacity, 0);
4896
+ const meshVertexCount = readNonNegativeInteger2(
4897
+ "meshVertexCount",
4898
+ options.meshVertexCount,
4899
+ 0
4900
+ );
4901
+ const meshIndexCount = readNonNegativeInteger2(
4902
+ "meshIndexCount",
4903
+ options.meshIndexCount,
4904
+ 0
4905
+ );
4906
+ const meshRangeCount = readNonNegativeInteger2(
4907
+ "meshRangeCount",
4908
+ options.meshRangeCount,
4909
+ 0
4910
+ );
4911
+ const bvhSortStageCount = readNonNegativeInteger2(
4912
+ "bvhSortStageCount",
4913
+ options.bvhSortStageCount,
4914
+ 0
4915
+ );
4916
+ const bvhBuildLevelCount = readNonNegativeInteger2(
4917
+ "bvhBuildLevelCount",
4918
+ options.bvhBuildLevelCount,
4919
+ 0
4920
+ );
4921
+ const minUniformBufferOffsetAlignment = readPositiveInteger2(
4922
+ "minUniformBufferOffsetAlignment",
4923
+ options.minUniformBufferOffsetAlignment,
4924
+ WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT
4925
+ );
4926
+ const denoise = options.denoise !== false;
4927
+ const deferredPathResolve = options.deferredPathResolve !== false;
4739
4928
  const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
4740
4929
  const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
4741
4930
  const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
4742
4931
  const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
4743
4932
  const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
4744
- const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
4745
- const materialTableBytes = materialCapacity * GPU_MATERIAL_RECORD_BYTES;
4933
+ const triangleBytes = allocatedRecordBytes(triangleCapacity, TRIANGLE_RECORD_BYTES);
4934
+ const materialTableBytes = 0;
4746
4935
  const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
4747
- const bvhLeafReferenceBytes = bvhLeafSortCapacity * BVH_LEAF_REF_RECORD_BYTES;
4936
+ const bvhLeafReferenceBytes = allocatedRecordBytes(
4937
+ bvhLeafSortCapacity,
4938
+ BVH_LEAF_REF_RECORD_BYTES
4939
+ );
4748
4940
  const emissiveTriangleMetadataBytes = emissiveTriangleCapacity * BVH_NODE_RECORD_BYTES;
4749
- const environmentPortalBytes = environmentPortalCapacity * ENVIRONMENT_PORTAL_RECORD_BYTES;
4941
+ const bvhCombinedBytes = allocatedRecordBytes(
4942
+ bvhNodeCapacity + emissiveTriangleCapacity,
4943
+ BVH_NODE_RECORD_BYTES
4944
+ );
4945
+ const meshVertexBytes = allocatedRecordBytes(meshVertexCount, MESH_VERTEX_RECORD_BYTES);
4946
+ const meshIndexBytes = allocatedRecordBytes(meshIndexCount, 4);
4947
+ const meshRangeBytes = allocatedRecordBytes(meshRangeCount, MESH_RANGE_RECORD_BYTES);
4948
+ const environmentPortalBytes = allocatedRecordBytes(
4949
+ environmentPortalCapacity,
4950
+ ENVIRONMENT_PORTAL_RECORD_BYTES
4951
+ );
4952
+ const tileCount = Math.ceil(width / tileSize) * Math.ceil(height / tileSize);
4953
+ const outputConfigSlotCount = deferredPathResolve ? 0 : tileCount;
4954
+ const frameConfigSlotCount = Math.max(
4955
+ 1,
4956
+ tileCount * samplesPerPixel + outputConfigSlotCount + (denoise ? 1 : 0)
4957
+ );
4958
+ const configBufferStride = alignByteSize(
4959
+ CONFIG_BUFFER_BYTES,
4960
+ minUniformBufferOffsetAlignment
4961
+ );
4962
+ const frameConfigBytes = frameConfigSlotCount * configBufferStride;
4963
+ const bvhBuildConfigBytes = Math.max(
4964
+ 1,
4965
+ 1 + bvhSortStageCount + bvhBuildLevelCount
4966
+ ) * configBufferStride;
4967
+ const sceneStorageBufferBindingBytes = Math.max(
4968
+ sceneObjectBytes,
4969
+ triangleBytes,
4970
+ bvhCombinedBytes,
4971
+ meshVertexBytes,
4972
+ meshIndexBytes,
4973
+ meshRangeBytes,
4974
+ environmentPortalBytes,
4975
+ bvhLeafReferenceBytes
4976
+ );
4977
+ const totalHotBufferBytes = queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + bvhCombinedBytes + meshVertexBytes + meshIndexBytes + meshRangeBytes + bvhLeafReferenceBytes + environmentPortalBytes + frameConfigBytes + bvhBuildConfigBytes + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES;
4750
4978
  return Object.freeze({
4751
4979
  queueBytes,
4752
4980
  queuePairBytes: queueBytes * 2,
@@ -4757,13 +4985,45 @@ function estimateWavefrontPathTracingMemory(options = {}) {
4757
4985
  triangleBytes,
4758
4986
  materialTableBytes,
4759
4987
  bvhNodeBytes,
4988
+ bvhCombinedBytes,
4760
4989
  bvhLeafReferenceBytes,
4761
4990
  emissiveTriangleMetadataBytes,
4991
+ meshVertexBytes,
4992
+ meshIndexBytes,
4993
+ meshRangeBytes,
4762
4994
  environmentPortalBytes,
4763
4995
  configBytes: CONFIG_BUFFER_BYTES,
4996
+ configBufferStride,
4997
+ frameConfigBytes,
4998
+ bvhBuildConfigBytes,
4764
4999
  counterBytes: COUNTER_BUFFER_BYTES,
4765
5000
  indirectDispatchBytes: INDIRECT_DISPATCH_ARGS_BYTES,
4766
- totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + materialTableBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
5001
+ sceneStorageBufferBindingBytes,
5002
+ totalHotBufferBytes
5003
+ });
5004
+ }
5005
+ function estimateWavefrontPathTracingMemoryForConfig(config, limits = {}) {
5006
+ return estimateWavefrontPathTracingMemory({
5007
+ width: config.width,
5008
+ height: config.height,
5009
+ tileSize: config.tileSize,
5010
+ samplesPerPixel: config.samplesPerPixel,
5011
+ tilePixelCapacity: config.tilePixelCapacity,
5012
+ maxDepth: config.maxDepth,
5013
+ sceneObjectCapacity: config.sceneObjectCapacity,
5014
+ triangleCapacity: config.triangleCapacity,
5015
+ bvhNodeCapacity: config.bvhNodeCapacity,
5016
+ bvhLeafSortCapacity: config.bvhLeafSortCapacity,
5017
+ emissiveTriangleCapacity: config.emissiveTriangleCapacity,
5018
+ environmentPortalCapacity: config.environmentPortalCapacity,
5019
+ meshVertexCount: config.gpuMeshSource.vertices.count,
5020
+ meshIndexCount: config.gpuMeshSource.indices.count,
5021
+ meshRangeCount: config.gpuMeshSource.meshes.count,
5022
+ bvhSortStageCount: config.bvhSortStages.length,
5023
+ bvhBuildLevelCount: config.bvhBuildLevels.length,
5024
+ denoise: config.denoise,
5025
+ deferredPathResolve: config.deferredPathResolve,
5026
+ minUniformBufferOffsetAlignment: limits.minUniformBufferOffsetAlignment ?? WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT
4767
5027
  });
4768
5028
  }
4769
5029
  function createWavefrontPathTracingComputeConfig(options = {}) {
@@ -4908,15 +5168,25 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
4908
5168
  denoise: options.denoise !== false,
4909
5169
  frameIndex: readNonNegativeInteger2("frameIndex", options.frameIndex, 0),
4910
5170
  memory: estimateWavefrontPathTracingMemory({
5171
+ width,
5172
+ height,
5173
+ tileSize,
5174
+ samplesPerPixel,
4911
5175
  tilePixelCapacity,
4912
5176
  maxDepth,
4913
5177
  sceneObjectCapacity,
4914
5178
  triangleCapacity,
4915
- materialCapacity: gpuMaterialSource.count,
4916
5179
  bvhNodeCapacity,
4917
5180
  bvhLeafSortCapacity,
4918
5181
  emissiveTriangleCapacity: emissiveTriangleIndices.capacity,
4919
- environmentPortalCapacity
5182
+ environmentPortalCapacity,
5183
+ meshVertexCount: gpuMeshSource.vertices.count,
5184
+ meshIndexCount: gpuMeshSource.indices.count,
5185
+ meshRangeCount: gpuMeshSource.meshes.count,
5186
+ bvhSortStageCount: bvhSortStages.length,
5187
+ bvhBuildLevelCount: bvhBuildLevels.length,
5188
+ denoise: options.denoise !== false,
5189
+ deferredPathResolve
4920
5190
  })
4921
5191
  });
4922
5192
  }
@@ -5007,7 +5277,13 @@ function clampTileSizeForDevice(config, device) {
5007
5277
  }
5008
5278
  const maxPixelsByRay = Math.floor(limit / RAY_RECORD_BYTES);
5009
5279
  const maxPixelsByHit = Math.floor(limit / HIT_RECORD_BYTES);
5010
- const maxPixels = Math.max(256, Math.min(maxPixelsByRay, maxPixelsByHit));
5280
+ const maxPixelsByPathVertex = Math.floor(
5281
+ limit / ((config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES)
5282
+ );
5283
+ const maxPixels = Math.max(
5284
+ 256,
5285
+ Math.min(maxPixelsByRay, maxPixelsByHit, maxPixelsByPathVertex)
5286
+ );
5011
5287
  if (config.tilePixelCapacity <= maxPixels) {
5012
5288
  return config.tileSize;
5013
5289
  }
@@ -5761,6 +6037,32 @@ function createWavefrontPresentBindGroup({
5761
6037
  }
5762
6038
 
5763
6039
  // src/wavefront-runtime-support.js
6040
+ var WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE = 128 * 1024 * 1024;
6041
+ var WEBGPU_DEFAULT_MAX_BUFFER_SIZE = 256 * 1024 * 1024;
6042
+ function readRequestedDeviceLimit(source, name) {
6043
+ const rawValue = source?.[name];
6044
+ if (rawValue === void 0 || rawValue === null) {
6045
+ return 0;
6046
+ }
6047
+ const value = Number(rawValue);
6048
+ if (!Number.isSafeInteger(value) || value < 0) {
6049
+ throw new Error(`${name} must be a non-negative safe integer.`);
6050
+ }
6051
+ return value;
6052
+ }
6053
+ function assertAdapterSupportsRequiredLimit(adapter, name, requiredValue) {
6054
+ const availableValue = Number(adapter?.limits?.[name]);
6055
+ if (!Number.isFinite(availableValue)) {
6056
+ throw new Error(
6057
+ `Wavefront scene buffers require ${name}>=${requiredValue} bytes, but this adapter does not expose that limit.`
6058
+ );
6059
+ }
6060
+ if (availableValue < requiredValue) {
6061
+ throw new Error(
6062
+ `Wavefront scene buffers require ${name}>=${requiredValue} bytes, but this adapter exposes ${availableValue} bytes.`
6063
+ );
6064
+ }
6065
+ }
5764
6066
  function getGpuUsageConstants() {
5765
6067
  if (typeof GPUBufferUsage === "undefined" || typeof GPUTextureUsage === "undefined" || typeof GPUShaderStage === "undefined") {
5766
6068
  throw new Error("WebGPU runtime unavailable. Required GPU constants are missing.");
@@ -5858,8 +6160,13 @@ async function createRenderPipeline(device, descriptor) {
5858
6160
  }
5859
6161
  return device.createRenderPipeline(descriptor);
5860
6162
  }
5861
- function createWavefrontDeviceDescriptor(adapter, options = {}) {
5862
- const requiredLimits = { ...options.requiredLimits ?? {} };
6163
+ function createWavefrontDeviceDescriptor(adapter, options = {}, memory = null) {
6164
+ const descriptor = { ...options.deviceDescriptor ?? {} };
6165
+ const descriptorRequiredLimits = { ...descriptor.requiredLimits ?? {} };
6166
+ const requiredLimits = {
6167
+ ...descriptorRequiredLimits,
6168
+ ...options.requiredLimits ?? {}
6169
+ };
5863
6170
  const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
5864
6171
  if (Number.isFinite(exposedStorageBufferLimit)) {
5865
6172
  if (exposedStorageBufferLimit < TRACE_STORAGE_BUFFER_BINDINGS) {
@@ -5884,7 +6191,32 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
5884
6191
  TRACE_SAMPLED_TEXTURE_BINDINGS
5885
6192
  );
5886
6193
  }
5887
- const descriptor = { ...options.deviceDescriptor ?? {} };
6194
+ const sceneStorageBufferBindingBytes = Number(
6195
+ memory?.sceneStorageBufferBindingBytes ?? 0
6196
+ );
6197
+ const requestedStorageBufferBindingSize = Math.max(
6198
+ readRequestedDeviceLimit(descriptorRequiredLimits, "maxStorageBufferBindingSize"),
6199
+ readRequestedDeviceLimit(options.requiredLimits, "maxStorageBufferBindingSize"),
6200
+ sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE ? sceneStorageBufferBindingBytes : 0
6201
+ );
6202
+ if (requestedStorageBufferBindingSize > 0) {
6203
+ assertAdapterSupportsRequiredLimit(
6204
+ adapter,
6205
+ "maxStorageBufferBindingSize",
6206
+ requestedStorageBufferBindingSize
6207
+ );
6208
+ requiredLimits.maxStorageBufferBindingSize = requestedStorageBufferBindingSize;
6209
+ }
6210
+ const requestedMaxBufferSize = Math.max(
6211
+ readRequestedDeviceLimit(descriptorRequiredLimits, "maxBufferSize"),
6212
+ readRequestedDeviceLimit(options.requiredLimits, "maxBufferSize"),
6213
+ sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_BUFFER_SIZE ? sceneStorageBufferBindingBytes : 0,
6214
+ requestedStorageBufferBindingSize > WEBGPU_DEFAULT_MAX_BUFFER_SIZE ? requestedStorageBufferBindingSize : 0
6215
+ );
6216
+ if (requestedMaxBufferSize > 0) {
6217
+ assertAdapterSupportsRequiredLimit(adapter, "maxBufferSize", requestedMaxBufferSize);
6218
+ requiredLimits.maxBufferSize = requestedMaxBufferSize;
6219
+ }
5888
6220
  const requiredFeatures = Array.from(descriptor.requiredFeatures ?? []);
5889
6221
  const adapterFeatures = adapter?.features;
5890
6222
  const timestampQuerySupported = typeof adapterFeatures?.has === "function" ? adapterFeatures.has("timestamp-query") : adapterFeatures && typeof adapterFeatures[Symbol.iterator] === "function" ? Array.from(adapterFeatures).includes("timestamp-query") : false;
@@ -5935,7 +6267,8 @@ function createGpuAdapterParallelismDiagnostics(adapter, device) {
5935
6267
  maxComputeWorkgroupSizeZ: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeZ"),
5936
6268
  maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
5937
6269
  maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
5938
- maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize")
6270
+ maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize"),
6271
+ maxBufferSize: readGpuLimit(adapter, device, "maxBufferSize")
5939
6272
  }),
5940
6273
  configuredWorkgroupSize: WORKGROUP_SIZE
5941
6274
  });
@@ -11241,7 +11574,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11241
11574
  if (!adapter) {
11242
11575
  throw new Error("Unable to acquire a WebGPU adapter for wavefront path tracing.");
11243
11576
  }
11244
- const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
11577
+ const device = await adapter.requestDevice(
11578
+ createWavefrontDeviceDescriptor(adapter, options, initialConfig.memory)
11579
+ );
11245
11580
  const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
11246
11581
  const context = canvas.getContext("webgpu");
11247
11582
  if (!context || typeof context.configure !== "function") {
@@ -11256,9 +11591,17 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11256
11591
  canvas,
11257
11592
  width: config.width,
11258
11593
  height: config.height,
11259
- tileSize: safeTileSize
11594
+ tileSize: safeTileSize,
11595
+ tilePixelCapacity: Math.min(
11596
+ config.tilePixelCapacity,
11597
+ safeTileSize * safeTileSize
11598
+ )
11260
11599
  });
11261
11600
  }
11601
+ config = Object.freeze({
11602
+ ...config,
11603
+ memory: estimateWavefrontPathTracingMemoryForConfig(config, device.limits)
11604
+ });
11262
11605
  canvas.width = config.width;
11263
11606
  canvas.height = config.height;
11264
11607
  context.configure({
@@ -11267,79 +11610,72 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11267
11610
  alphaMode: options.alpha === true ? "premultiplied" : "opaque"
11268
11611
  });
11269
11612
  const bufferUsage = constants.buffer.STORAGE | constants.buffer.COPY_DST;
11270
- const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
11271
- const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
11272
- const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
11273
- const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
11274
- const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
11275
- const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
11276
- const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
11613
+ const memory = config.memory;
11614
+ const activeQueue = createBuffer(device, bufferUsage, memory.queueBytes, "plasius.wavefront.activeQueue");
11615
+ const nextQueue = createBuffer(device, bufferUsage, memory.queueBytes, "plasius.wavefront.nextQueue");
11616
+ const hitBuffer = createBuffer(device, bufferUsage, memory.hitBytes, "plasius.wavefront.hitBuffer");
11277
11617
  const accumulationBuffer = createBuffer(
11278
11618
  device,
11279
11619
  bufferUsage,
11280
- accumulationBytes,
11620
+ memory.accumulationBytes,
11281
11621
  "plasius.wavefront.accumulation"
11282
11622
  );
11283
11623
  const pathVertexBuffer = createBuffer(
11284
11624
  device,
11285
11625
  bufferUsage,
11286
- pathVertexBytes,
11626
+ memory.pathVertexBytes,
11287
11627
  "plasius.wavefront.pathVertices"
11288
11628
  );
11289
11629
  const sceneObjectBuffer = createBuffer(
11290
11630
  device,
11291
11631
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11292
- config.sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES,
11632
+ memory.sceneObjectBytes,
11293
11633
  "plasius.wavefront.sceneObjects"
11294
11634
  );
11295
11635
  const triangleBuffer = createBuffer(
11296
11636
  device,
11297
11637
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11298
- Math.max(1, config.triangleCapacity) * TRIANGLE_RECORD_BYTES,
11638
+ memory.triangleBytes,
11299
11639
  "plasius.wavefront.triangles"
11300
11640
  );
11301
11641
  const bvhNodeBuffer = createBuffer(
11302
11642
  device,
11303
11643
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11304
- Math.max(1, config.bvhNodeCapacity + config.emissiveTriangleCapacity) * BVH_NODE_RECORD_BYTES,
11644
+ memory.bvhCombinedBytes,
11305
11645
  "plasius.wavefront.bvhNodes"
11306
11646
  );
11307
11647
  const meshVertexBuffer = createBuffer(
11308
11648
  device,
11309
11649
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11310
- Math.max(1, config.gpuMeshSource.vertices.count) * MESH_VERTEX_RECORD_BYTES,
11650
+ memory.meshVertexBytes,
11311
11651
  "plasius.wavefront.meshVertices"
11312
11652
  );
11313
11653
  const meshIndexBuffer = createBuffer(
11314
11654
  device,
11315
11655
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11316
- Math.max(1, config.gpuMeshSource.indices.count) * 4,
11656
+ memory.meshIndexBytes,
11317
11657
  "plasius.wavefront.meshIndices"
11318
11658
  );
11319
11659
  const meshRangeBuffer = createBuffer(
11320
11660
  device,
11321
11661
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11322
- Math.max(1, config.gpuMeshSource.meshes.count) * MESH_RANGE_RECORD_BYTES,
11662
+ memory.meshRangeBytes,
11323
11663
  "plasius.wavefront.meshRanges"
11324
11664
  );
11325
11665
  const environmentPortalBuffer = createBuffer(
11326
11666
  device,
11327
11667
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11328
- Math.max(1, config.environmentPortalCapacity) * ENVIRONMENT_PORTAL_RECORD_BYTES,
11668
+ memory.environmentPortalBytes,
11329
11669
  "plasius.wavefront.environmentPortals"
11330
11670
  );
11331
11671
  const bvhLeafRefBuffer = createBuffer(
11332
11672
  device,
11333
11673
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11334
- Math.max(1, config.bvhLeafSortCapacity) * BVH_LEAF_REF_RECORD_BYTES,
11674
+ memory.bvhLeafReferenceBytes,
11335
11675
  "plasius.wavefront.bvhLeafRefs"
11336
11676
  );
11337
11677
  const tiles = createTiles(config.width, config.height, config.tileSize);
11338
- const uniformOffsetAlignment = Number(device?.limits?.minUniformBufferOffsetAlignment);
11339
- const configBufferStride = alignTo(
11340
- CONFIG_BUFFER_BYTES,
11341
- Number.isFinite(uniformOffsetAlignment) && uniformOffsetAlignment > 0 ? uniformOffsetAlignment : CONFIG_BUFFER_BYTES
11342
- );
11678
+ const configBufferStride = memory.configBufferStride;
11343
11679
  const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
11344
11680
  const frameConfigSlotCount = Math.max(
11345
11681
  1,
@@ -11348,26 +11684,25 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11348
11684
  const configBuffer = createBuffer(
11349
11685
  device,
11350
11686
  constants.buffer.UNIFORM | constants.buffer.COPY_DST,
11351
- frameConfigSlotCount * configBufferStride,
11687
+ memory.frameConfigBytes,
11352
11688
  "plasius.wavefront.frameConfig"
11353
11689
  );
11354
- const bvhBuildConfigSlots = 1 + config.bvhSortStages.length + config.bvhBuildLevels.length;
11355
11690
  const bvhBuildConfigBuffer = createBuffer(
11356
11691
  device,
11357
11692
  constants.buffer.UNIFORM | constants.buffer.COPY_DST,
11358
- Math.max(1, bvhBuildConfigSlots) * configBufferStride,
11693
+ memory.bvhBuildConfigBytes,
11359
11694
  "plasius.wavefront.bvhBuildConfig"
11360
11695
  );
11361
11696
  const counterBuffer = createBuffer(
11362
11697
  device,
11363
11698
  constants.buffer.STORAGE | constants.buffer.COPY_DST | constants.buffer.COPY_SRC,
11364
- COUNTER_BUFFER_BYTES,
11699
+ memory.counterBytes,
11365
11700
  "plasius.wavefront.counters"
11366
11701
  );
11367
11702
  const activeDispatchBuffer = createBuffer(
11368
11703
  device,
11369
11704
  constants.buffer.INDIRECT | constants.buffer.COPY_DST,
11370
- INDIRECT_DISPATCH_ARGS_BYTES,
11705
+ memory.indirectDispatchBytes,
11371
11706
  "plasius.wavefront.activeDispatchArgs"
11372
11707
  );
11373
11708
  let packedScene = packWavefrontSceneObjects(config.sceneObjects, config.sceneObjectCapacity);
@@ -13078,8 +13413,12 @@ function createRayTracingRenderPlan(options = {}) {
13078
13413
  applyMediumTransmittance,
13079
13414
  beerLambertTransmittance,
13080
13415
  bindRendererToXrManager,
13416
+ bucketFeedbackGameFrameRate,
13417
+ bucketFeedbackGameFrameTime,
13418
+ bucketFeedbackGameViewport,
13081
13419
  createAnimatedSceneRenderer,
13082
13420
  createDefaultWavefrontSceneObjects,
13421
+ createFeedbackGameDiagnosticSnapshot,
13083
13422
  createGpuRenderer,
13084
13423
  createMediumStack,
13085
13424
  createProfessionalAnimatedSceneRenderer,