@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.js CHANGED
@@ -2205,6 +2205,144 @@ async function createProfessionalAnimatedSceneRenderer(options = {}) {
2205
2205
  };
2206
2206
  }
2207
2207
 
2208
+ // src/feedback-diagnostics.js
2209
+ import {
2210
+ FEEDBACK_GAME_DIAGNOSTICS_CONTRACT_VERSION,
2211
+ feedbackGameDiagnosticSurfaceRegistrations,
2212
+ parseFeedbackGameDiagnostics
2213
+ } from "@plasius/gpu-shared/feedback-diagnostics";
2214
+ var inputKeys = Object.freeze([
2215
+ "featureEnabled",
2216
+ "capabilityGranted",
2217
+ "consentConfirmed",
2218
+ "surfaceId",
2219
+ "renderer",
2220
+ "backend",
2221
+ "viewportWidth",
2222
+ "viewportHeight",
2223
+ "frameRate",
2224
+ "frameTimeMs",
2225
+ "featureIds",
2226
+ "counters",
2227
+ "errorCodes"
2228
+ ]);
2229
+ function bucketFeedbackGameViewport(width, height) {
2230
+ if (typeof width !== "number" || typeof height !== "number" || !Number.isFinite(width) || !Number.isFinite(height) || width <= 0 || height <= 0) {
2231
+ return "unknown";
2232
+ }
2233
+ const shorterEdge = Math.min(width, height);
2234
+ const longerEdge = Math.max(width, height);
2235
+ const size = shorterEdge < 600 ? "small" : shorterEdge < 900 || longerEdge < 1440 ? "medium" : "large";
2236
+ const orientation = width > height ? "landscape" : "portrait";
2237
+ return `${size}-${orientation}`;
2238
+ }
2239
+ function bucketFeedbackGameFrameRate(frameRate) {
2240
+ if (typeof frameRate !== "number" || !Number.isFinite(frameRate) || frameRate < 0) {
2241
+ return "unknown";
2242
+ }
2243
+ if (frameRate < 15) {
2244
+ return "under-15";
2245
+ }
2246
+ if (frameRate < 30) {
2247
+ return "15-29";
2248
+ }
2249
+ if (frameRate < 60) {
2250
+ return "30-59";
2251
+ }
2252
+ return "60-plus";
2253
+ }
2254
+ function bucketFeedbackGameFrameTime(frameTimeMs) {
2255
+ if (typeof frameTimeMs !== "number" || !Number.isFinite(frameTimeMs) || frameTimeMs < 0) {
2256
+ return "unknown";
2257
+ }
2258
+ if (frameTimeMs < 17) {
2259
+ return "under-17ms";
2260
+ }
2261
+ if (frameTimeMs < 34) {
2262
+ return "17-33ms";
2263
+ }
2264
+ if (frameTimeMs < 67) {
2265
+ return "34-66ms";
2266
+ }
2267
+ return "over-66ms";
2268
+ }
2269
+ function createFeedbackGameDiagnosticSnapshot(input) {
2270
+ try {
2271
+ return createFeedbackGameDiagnosticSnapshotInternal(input);
2272
+ } catch {
2273
+ throw new TypeError("Invalid privacy-safe renderer diagnostics input.");
2274
+ }
2275
+ }
2276
+ function createFeedbackGameDiagnosticSnapshotInternal(input) {
2277
+ const snapshot = snapshotInputIfEnabled(input);
2278
+ if (snapshot === null) {
2279
+ return null;
2280
+ }
2281
+ const registration = typeof snapshot.surfaceId === "string" && Object.hasOwn(
2282
+ feedbackGameDiagnosticSurfaceRegistrations,
2283
+ snapshot.surfaceId
2284
+ ) ? feedbackGameDiagnosticSurfaceRegistrations[snapshot.surfaceId] : void 0;
2285
+ return parseFeedbackGameDiagnostics({
2286
+ type: "feedback-game-diagnostics",
2287
+ version: FEEDBACK_GAME_DIAGNOSTICS_CONTRACT_VERSION,
2288
+ surfaceId: snapshot.surfaceId,
2289
+ consentConfirmed: true,
2290
+ provenanceContractId: registration?.provenanceContractId,
2291
+ renderer: snapshot.renderer,
2292
+ backend: snapshot.backend,
2293
+ viewportBucket: bucketFeedbackGameViewport(
2294
+ snapshot.viewportWidth,
2295
+ snapshot.viewportHeight
2296
+ ),
2297
+ frameRateBucket: bucketFeedbackGameFrameRate(snapshot.frameRate),
2298
+ frameTimeBucket: bucketFeedbackGameFrameTime(snapshot.frameTimeMs),
2299
+ featureIds: snapshot.featureIds,
2300
+ counters: snapshot.counters,
2301
+ errorCodes: snapshot.errorCodes
2302
+ });
2303
+ }
2304
+ function snapshotInputIfEnabled(input) {
2305
+ assertValid(
2306
+ input !== null && typeof input === "object" && !Array.isArray(input) && (Object.getPrototypeOf(input) === Object.prototype || Object.getPrototypeOf(input) === null)
2307
+ );
2308
+ const gateKeys = [
2309
+ "featureEnabled",
2310
+ "capabilityGranted",
2311
+ "consentConfirmed"
2312
+ ];
2313
+ const snapshot = /* @__PURE__ */ Object.create(null);
2314
+ for (const key of gateKeys) {
2315
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
2316
+ assertValid(
2317
+ descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value") && typeof descriptor.value === "boolean"
2318
+ );
2319
+ snapshot[key] = descriptor.value;
2320
+ }
2321
+ const { featureEnabled, capabilityGranted, consentConfirmed } = snapshot;
2322
+ if (!featureEnabled || !capabilityGranted || !consentConfirmed) {
2323
+ return null;
2324
+ }
2325
+ const actualKeys = Reflect.ownKeys(input);
2326
+ assertValid(
2327
+ actualKeys.length === inputKeys.length && inputKeys.every((key) => actualKeys.includes(key)) && actualKeys.every(
2328
+ (key) => typeof key === "string" && inputKeys.includes(key)
2329
+ )
2330
+ );
2331
+ for (const key of inputKeys.filter((key2) => !gateKeys.includes(key2))) {
2332
+ const descriptor = Object.getOwnPropertyDescriptor(input, key);
2333
+ assertValid(
2334
+ descriptor !== void 0 && descriptor.enumerable === true && Object.hasOwn(descriptor, "value")
2335
+ );
2336
+ snapshot[key] = descriptor.value;
2337
+ }
2338
+ return Object.freeze(snapshot);
2339
+ }
2340
+ function assertValid(condition) {
2341
+ if (!condition) {
2342
+ throw new TypeError("Invalid privacy-safe renderer diagnostics input.");
2343
+ }
2344
+ }
2345
+
2208
2346
  // src/wavefront-frame-runtime.js
2209
2347
  function createGpuParallelismCounters() {
2210
2348
  return {
@@ -4438,6 +4576,13 @@ function dispatchWavefrontGpuAccelerationBuild({
4438
4576
  }
4439
4577
 
4440
4578
  // src/wavefront-config.js
4579
+ var WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT = 256;
4580
+ function allocatedRecordBytes(count, recordBytes) {
4581
+ return Math.max(1, count) * recordBytes;
4582
+ }
4583
+ function alignByteSize(value, alignment) {
4584
+ return Math.ceil(value / alignment) * alignment;
4585
+ }
4441
4586
  function resolveEnvironmentLighting(input, environmentColor, ambientColor) {
4442
4587
  const source = input ?? {};
4443
4588
  return Object.freeze({
@@ -4614,6 +4759,18 @@ function resolveCamera(input, width, height) {
4614
4759
  });
4615
4760
  }
4616
4761
  function estimateWavefrontPathTracingMemory(options = {}) {
4762
+ const width = readPositiveInteger2("width", options.width, DEFAULT_WIDTH);
4763
+ const height = readPositiveInteger2("height", options.height, DEFAULT_HEIGHT);
4764
+ const tileSize = readPositiveInteger2("tileSize", options.tileSize, DEFAULT_TILE_SIZE);
4765
+ const samplesPerPixel = clamp2(
4766
+ readPositiveInteger2(
4767
+ "samplesPerPixel",
4768
+ options.samplesPerPixel,
4769
+ DEFAULT_SAMPLES_PER_PIXEL
4770
+ ),
4771
+ 1,
4772
+ MAX_SAMPLES_PER_PIXEL
4773
+ );
4617
4774
  const tilePixelCapacity = readPositiveInteger2(
4618
4775
  "tilePixelCapacity",
4619
4776
  options.tilePixelCapacity,
@@ -4646,18 +4803,89 @@ function estimateWavefrontPathTracingMemory(options = {}) {
4646
4803
  options.environmentPortalCapacity,
4647
4804
  0
4648
4805
  );
4649
- const materialCapacity = readNonNegativeInteger2("materialCapacity", options.materialCapacity, 0);
4806
+ readNonNegativeInteger2("materialCapacity", options.materialCapacity, 0);
4807
+ const meshVertexCount = readNonNegativeInteger2(
4808
+ "meshVertexCount",
4809
+ options.meshVertexCount,
4810
+ 0
4811
+ );
4812
+ const meshIndexCount = readNonNegativeInteger2(
4813
+ "meshIndexCount",
4814
+ options.meshIndexCount,
4815
+ 0
4816
+ );
4817
+ const meshRangeCount = readNonNegativeInteger2(
4818
+ "meshRangeCount",
4819
+ options.meshRangeCount,
4820
+ 0
4821
+ );
4822
+ const bvhSortStageCount = readNonNegativeInteger2(
4823
+ "bvhSortStageCount",
4824
+ options.bvhSortStageCount,
4825
+ 0
4826
+ );
4827
+ const bvhBuildLevelCount = readNonNegativeInteger2(
4828
+ "bvhBuildLevelCount",
4829
+ options.bvhBuildLevelCount,
4830
+ 0
4831
+ );
4832
+ const minUniformBufferOffsetAlignment = readPositiveInteger2(
4833
+ "minUniformBufferOffsetAlignment",
4834
+ options.minUniformBufferOffsetAlignment,
4835
+ WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT
4836
+ );
4837
+ const denoise = options.denoise !== false;
4838
+ const deferredPathResolve = options.deferredPathResolve !== false;
4650
4839
  const queueBytes = tilePixelCapacity * RAY_RECORD_BYTES;
4651
4840
  const hitBytes = tilePixelCapacity * HIT_RECORD_BYTES;
4652
4841
  const accumulationBytes = tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
4653
4842
  const pathVertexBytes = tilePixelCapacity * (maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
4654
4843
  const sceneObjectBytes = sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES;
4655
- const triangleBytes = triangleCapacity * TRIANGLE_RECORD_BYTES;
4656
- const materialTableBytes = materialCapacity * GPU_MATERIAL_RECORD_BYTES;
4844
+ const triangleBytes = allocatedRecordBytes(triangleCapacity, TRIANGLE_RECORD_BYTES);
4845
+ const materialTableBytes = 0;
4657
4846
  const bvhNodeBytes = bvhNodeCapacity * BVH_NODE_RECORD_BYTES;
4658
- const bvhLeafReferenceBytes = bvhLeafSortCapacity * BVH_LEAF_REF_RECORD_BYTES;
4847
+ const bvhLeafReferenceBytes = allocatedRecordBytes(
4848
+ bvhLeafSortCapacity,
4849
+ BVH_LEAF_REF_RECORD_BYTES
4850
+ );
4659
4851
  const emissiveTriangleMetadataBytes = emissiveTriangleCapacity * BVH_NODE_RECORD_BYTES;
4660
- const environmentPortalBytes = environmentPortalCapacity * ENVIRONMENT_PORTAL_RECORD_BYTES;
4852
+ const bvhCombinedBytes = allocatedRecordBytes(
4853
+ bvhNodeCapacity + emissiveTriangleCapacity,
4854
+ BVH_NODE_RECORD_BYTES
4855
+ );
4856
+ const meshVertexBytes = allocatedRecordBytes(meshVertexCount, MESH_VERTEX_RECORD_BYTES);
4857
+ const meshIndexBytes = allocatedRecordBytes(meshIndexCount, 4);
4858
+ const meshRangeBytes = allocatedRecordBytes(meshRangeCount, MESH_RANGE_RECORD_BYTES);
4859
+ const environmentPortalBytes = allocatedRecordBytes(
4860
+ environmentPortalCapacity,
4861
+ ENVIRONMENT_PORTAL_RECORD_BYTES
4862
+ );
4863
+ const tileCount = Math.ceil(width / tileSize) * Math.ceil(height / tileSize);
4864
+ const outputConfigSlotCount = deferredPathResolve ? 0 : tileCount;
4865
+ const frameConfigSlotCount = Math.max(
4866
+ 1,
4867
+ tileCount * samplesPerPixel + outputConfigSlotCount + (denoise ? 1 : 0)
4868
+ );
4869
+ const configBufferStride = alignByteSize(
4870
+ CONFIG_BUFFER_BYTES,
4871
+ minUniformBufferOffsetAlignment
4872
+ );
4873
+ const frameConfigBytes = frameConfigSlotCount * configBufferStride;
4874
+ const bvhBuildConfigBytes = Math.max(
4875
+ 1,
4876
+ 1 + bvhSortStageCount + bvhBuildLevelCount
4877
+ ) * configBufferStride;
4878
+ const sceneStorageBufferBindingBytes = Math.max(
4879
+ sceneObjectBytes,
4880
+ triangleBytes,
4881
+ bvhCombinedBytes,
4882
+ meshVertexBytes,
4883
+ meshIndexBytes,
4884
+ meshRangeBytes,
4885
+ environmentPortalBytes,
4886
+ bvhLeafReferenceBytes
4887
+ );
4888
+ const totalHotBufferBytes = queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + bvhCombinedBytes + meshVertexBytes + meshIndexBytes + meshRangeBytes + bvhLeafReferenceBytes + environmentPortalBytes + frameConfigBytes + bvhBuildConfigBytes + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES;
4661
4889
  return Object.freeze({
4662
4890
  queueBytes,
4663
4891
  queuePairBytes: queueBytes * 2,
@@ -4668,13 +4896,45 @@ function estimateWavefrontPathTracingMemory(options = {}) {
4668
4896
  triangleBytes,
4669
4897
  materialTableBytes,
4670
4898
  bvhNodeBytes,
4899
+ bvhCombinedBytes,
4671
4900
  bvhLeafReferenceBytes,
4672
4901
  emissiveTriangleMetadataBytes,
4902
+ meshVertexBytes,
4903
+ meshIndexBytes,
4904
+ meshRangeBytes,
4673
4905
  environmentPortalBytes,
4674
4906
  configBytes: CONFIG_BUFFER_BYTES,
4907
+ configBufferStride,
4908
+ frameConfigBytes,
4909
+ bvhBuildConfigBytes,
4675
4910
  counterBytes: COUNTER_BUFFER_BYTES,
4676
4911
  indirectDispatchBytes: INDIRECT_DISPATCH_ARGS_BYTES,
4677
- totalHotBufferBytes: queueBytes * 2 + hitBytes + accumulationBytes + pathVertexBytes + sceneObjectBytes + triangleBytes + materialTableBytes + bvhNodeBytes + bvhLeafReferenceBytes + emissiveTriangleMetadataBytes + environmentPortalBytes + CONFIG_BUFFER_BYTES + COUNTER_BUFFER_BYTES + INDIRECT_DISPATCH_ARGS_BYTES
4912
+ sceneStorageBufferBindingBytes,
4913
+ totalHotBufferBytes
4914
+ });
4915
+ }
4916
+ function estimateWavefrontPathTracingMemoryForConfig(config, limits = {}) {
4917
+ return estimateWavefrontPathTracingMemory({
4918
+ width: config.width,
4919
+ height: config.height,
4920
+ tileSize: config.tileSize,
4921
+ samplesPerPixel: config.samplesPerPixel,
4922
+ tilePixelCapacity: config.tilePixelCapacity,
4923
+ maxDepth: config.maxDepth,
4924
+ sceneObjectCapacity: config.sceneObjectCapacity,
4925
+ triangleCapacity: config.triangleCapacity,
4926
+ bvhNodeCapacity: config.bvhNodeCapacity,
4927
+ bvhLeafSortCapacity: config.bvhLeafSortCapacity,
4928
+ emissiveTriangleCapacity: config.emissiveTriangleCapacity,
4929
+ environmentPortalCapacity: config.environmentPortalCapacity,
4930
+ meshVertexCount: config.gpuMeshSource.vertices.count,
4931
+ meshIndexCount: config.gpuMeshSource.indices.count,
4932
+ meshRangeCount: config.gpuMeshSource.meshes.count,
4933
+ bvhSortStageCount: config.bvhSortStages.length,
4934
+ bvhBuildLevelCount: config.bvhBuildLevels.length,
4935
+ denoise: config.denoise,
4936
+ deferredPathResolve: config.deferredPathResolve,
4937
+ minUniformBufferOffsetAlignment: limits.minUniformBufferOffsetAlignment ?? WEBGPU_DEFAULT_MIN_UNIFORM_BUFFER_OFFSET_ALIGNMENT
4678
4938
  });
4679
4939
  }
4680
4940
  function createWavefrontPathTracingComputeConfig(options = {}) {
@@ -4819,15 +5079,25 @@ function createWavefrontPathTracingComputeConfig(options = {}) {
4819
5079
  denoise: options.denoise !== false,
4820
5080
  frameIndex: readNonNegativeInteger2("frameIndex", options.frameIndex, 0),
4821
5081
  memory: estimateWavefrontPathTracingMemory({
5082
+ width,
5083
+ height,
5084
+ tileSize,
5085
+ samplesPerPixel,
4822
5086
  tilePixelCapacity,
4823
5087
  maxDepth,
4824
5088
  sceneObjectCapacity,
4825
5089
  triangleCapacity,
4826
- materialCapacity: gpuMaterialSource.count,
4827
5090
  bvhNodeCapacity,
4828
5091
  bvhLeafSortCapacity,
4829
5092
  emissiveTriangleCapacity: emissiveTriangleIndices.capacity,
4830
- environmentPortalCapacity
5093
+ environmentPortalCapacity,
5094
+ meshVertexCount: gpuMeshSource.vertices.count,
5095
+ meshIndexCount: gpuMeshSource.indices.count,
5096
+ meshRangeCount: gpuMeshSource.meshes.count,
5097
+ bvhSortStageCount: bvhSortStages.length,
5098
+ bvhBuildLevelCount: bvhBuildLevels.length,
5099
+ denoise: options.denoise !== false,
5100
+ deferredPathResolve
4831
5101
  })
4832
5102
  });
4833
5103
  }
@@ -4918,7 +5188,13 @@ function clampTileSizeForDevice(config, device) {
4918
5188
  }
4919
5189
  const maxPixelsByRay = Math.floor(limit / RAY_RECORD_BYTES);
4920
5190
  const maxPixelsByHit = Math.floor(limit / HIT_RECORD_BYTES);
4921
- const maxPixels = Math.max(256, Math.min(maxPixelsByRay, maxPixelsByHit));
5191
+ const maxPixelsByPathVertex = Math.floor(
5192
+ limit / ((config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES)
5193
+ );
5194
+ const maxPixels = Math.max(
5195
+ 256,
5196
+ Math.min(maxPixelsByRay, maxPixelsByHit, maxPixelsByPathVertex)
5197
+ );
4922
5198
  if (config.tilePixelCapacity <= maxPixels) {
4923
5199
  return config.tileSize;
4924
5200
  }
@@ -5672,6 +5948,32 @@ function createWavefrontPresentBindGroup({
5672
5948
  }
5673
5949
 
5674
5950
  // src/wavefront-runtime-support.js
5951
+ var WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE = 128 * 1024 * 1024;
5952
+ var WEBGPU_DEFAULT_MAX_BUFFER_SIZE = 256 * 1024 * 1024;
5953
+ function readRequestedDeviceLimit(source, name) {
5954
+ const rawValue = source?.[name];
5955
+ if (rawValue === void 0 || rawValue === null) {
5956
+ return 0;
5957
+ }
5958
+ const value = Number(rawValue);
5959
+ if (!Number.isSafeInteger(value) || value < 0) {
5960
+ throw new Error(`${name} must be a non-negative safe integer.`);
5961
+ }
5962
+ return value;
5963
+ }
5964
+ function assertAdapterSupportsRequiredLimit(adapter, name, requiredValue) {
5965
+ const availableValue = Number(adapter?.limits?.[name]);
5966
+ if (!Number.isFinite(availableValue)) {
5967
+ throw new Error(
5968
+ `Wavefront scene buffers require ${name}>=${requiredValue} bytes, but this adapter does not expose that limit.`
5969
+ );
5970
+ }
5971
+ if (availableValue < requiredValue) {
5972
+ throw new Error(
5973
+ `Wavefront scene buffers require ${name}>=${requiredValue} bytes, but this adapter exposes ${availableValue} bytes.`
5974
+ );
5975
+ }
5976
+ }
5675
5977
  function getGpuUsageConstants() {
5676
5978
  if (typeof GPUBufferUsage === "undefined" || typeof GPUTextureUsage === "undefined" || typeof GPUShaderStage === "undefined") {
5677
5979
  throw new Error("WebGPU runtime unavailable. Required GPU constants are missing.");
@@ -5769,8 +6071,13 @@ async function createRenderPipeline(device, descriptor) {
5769
6071
  }
5770
6072
  return device.createRenderPipeline(descriptor);
5771
6073
  }
5772
- function createWavefrontDeviceDescriptor(adapter, options = {}) {
5773
- const requiredLimits = { ...options.requiredLimits ?? {} };
6074
+ function createWavefrontDeviceDescriptor(adapter, options = {}, memory = null) {
6075
+ const descriptor = { ...options.deviceDescriptor ?? {} };
6076
+ const descriptorRequiredLimits = { ...descriptor.requiredLimits ?? {} };
6077
+ const requiredLimits = {
6078
+ ...descriptorRequiredLimits,
6079
+ ...options.requiredLimits ?? {}
6080
+ };
5774
6081
  const exposedStorageBufferLimit = Number(adapter?.limits?.maxStorageBuffersPerShaderStage);
5775
6082
  if (Number.isFinite(exposedStorageBufferLimit)) {
5776
6083
  if (exposedStorageBufferLimit < TRACE_STORAGE_BUFFER_BINDINGS) {
@@ -5795,7 +6102,32 @@ function createWavefrontDeviceDescriptor(adapter, options = {}) {
5795
6102
  TRACE_SAMPLED_TEXTURE_BINDINGS
5796
6103
  );
5797
6104
  }
5798
- const descriptor = { ...options.deviceDescriptor ?? {} };
6105
+ const sceneStorageBufferBindingBytes = Number(
6106
+ memory?.sceneStorageBufferBindingBytes ?? 0
6107
+ );
6108
+ const requestedStorageBufferBindingSize = Math.max(
6109
+ readRequestedDeviceLimit(descriptorRequiredLimits, "maxStorageBufferBindingSize"),
6110
+ readRequestedDeviceLimit(options.requiredLimits, "maxStorageBufferBindingSize"),
6111
+ sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_STORAGE_BUFFER_BINDING_SIZE ? sceneStorageBufferBindingBytes : 0
6112
+ );
6113
+ if (requestedStorageBufferBindingSize > 0) {
6114
+ assertAdapterSupportsRequiredLimit(
6115
+ adapter,
6116
+ "maxStorageBufferBindingSize",
6117
+ requestedStorageBufferBindingSize
6118
+ );
6119
+ requiredLimits.maxStorageBufferBindingSize = requestedStorageBufferBindingSize;
6120
+ }
6121
+ const requestedMaxBufferSize = Math.max(
6122
+ readRequestedDeviceLimit(descriptorRequiredLimits, "maxBufferSize"),
6123
+ readRequestedDeviceLimit(options.requiredLimits, "maxBufferSize"),
6124
+ sceneStorageBufferBindingBytes > WEBGPU_DEFAULT_MAX_BUFFER_SIZE ? sceneStorageBufferBindingBytes : 0,
6125
+ requestedStorageBufferBindingSize > WEBGPU_DEFAULT_MAX_BUFFER_SIZE ? requestedStorageBufferBindingSize : 0
6126
+ );
6127
+ if (requestedMaxBufferSize > 0) {
6128
+ assertAdapterSupportsRequiredLimit(adapter, "maxBufferSize", requestedMaxBufferSize);
6129
+ requiredLimits.maxBufferSize = requestedMaxBufferSize;
6130
+ }
5799
6131
  const requiredFeatures = Array.from(descriptor.requiredFeatures ?? []);
5800
6132
  const adapterFeatures = adapter?.features;
5801
6133
  const timestampQuerySupported = typeof adapterFeatures?.has === "function" ? adapterFeatures.has("timestamp-query") : adapterFeatures && typeof adapterFeatures[Symbol.iterator] === "function" ? Array.from(adapterFeatures).includes("timestamp-query") : false;
@@ -5846,7 +6178,8 @@ function createGpuAdapterParallelismDiagnostics(adapter, device) {
5846
6178
  maxComputeWorkgroupSizeZ: readGpuLimit(adapter, device, "maxComputeWorkgroupSizeZ"),
5847
6179
  maxComputeWorkgroupsPerDimension: readGpuLimit(adapter, device, "maxComputeWorkgroupsPerDimension"),
5848
6180
  maxStorageBuffersPerShaderStage: readGpuLimit(adapter, device, "maxStorageBuffersPerShaderStage"),
5849
- maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize")
6181
+ maxStorageBufferBindingSize: readGpuLimit(adapter, device, "maxStorageBufferBindingSize"),
6182
+ maxBufferSize: readGpuLimit(adapter, device, "maxBufferSize")
5850
6183
  }),
5851
6184
  configuredWorkgroupSize: WORKGROUP_SIZE
5852
6185
  });
@@ -11152,7 +11485,9 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11152
11485
  if (!adapter) {
11153
11486
  throw new Error("Unable to acquire a WebGPU adapter for wavefront path tracing.");
11154
11487
  }
11155
- const device = await adapter.requestDevice(createWavefrontDeviceDescriptor(adapter, options));
11488
+ const device = await adapter.requestDevice(
11489
+ createWavefrontDeviceDescriptor(adapter, options, initialConfig.memory)
11490
+ );
11156
11491
  const gpuAdapterParallelism = createGpuAdapterParallelismDiagnostics(adapter, device);
11157
11492
  const context = canvas.getContext("webgpu");
11158
11493
  if (!context || typeof context.configure !== "function") {
@@ -11167,9 +11502,17 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11167
11502
  canvas,
11168
11503
  width: config.width,
11169
11504
  height: config.height,
11170
- tileSize: safeTileSize
11505
+ tileSize: safeTileSize,
11506
+ tilePixelCapacity: Math.min(
11507
+ config.tilePixelCapacity,
11508
+ safeTileSize * safeTileSize
11509
+ )
11171
11510
  });
11172
11511
  }
11512
+ config = Object.freeze({
11513
+ ...config,
11514
+ memory: estimateWavefrontPathTracingMemoryForConfig(config, device.limits)
11515
+ });
11173
11516
  canvas.width = config.width;
11174
11517
  canvas.height = config.height;
11175
11518
  context.configure({
@@ -11178,79 +11521,72 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11178
11521
  alphaMode: options.alpha === true ? "premultiplied" : "opaque"
11179
11522
  });
11180
11523
  const bufferUsage = constants.buffer.STORAGE | constants.buffer.COPY_DST;
11181
- const rayQueueBytes = config.tilePixelCapacity * RAY_RECORD_BYTES;
11182
- const hitBytes = config.tilePixelCapacity * HIT_RECORD_BYTES;
11183
- const accumulationBytes = config.tilePixelCapacity * ACCUMULATION_RECORD_BYTES;
11184
- const pathVertexBytes = config.tilePixelCapacity * (config.maxDepth + 1) * PATH_VERTEX_RECORD_BYTES;
11185
- const activeQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.activeQueue");
11186
- const nextQueue = createBuffer(device, bufferUsage, rayQueueBytes, "plasius.wavefront.nextQueue");
11187
- const hitBuffer = createBuffer(device, bufferUsage, hitBytes, "plasius.wavefront.hitBuffer");
11524
+ const memory = config.memory;
11525
+ const activeQueue = createBuffer(device, bufferUsage, memory.queueBytes, "plasius.wavefront.activeQueue");
11526
+ const nextQueue = createBuffer(device, bufferUsage, memory.queueBytes, "plasius.wavefront.nextQueue");
11527
+ const hitBuffer = createBuffer(device, bufferUsage, memory.hitBytes, "plasius.wavefront.hitBuffer");
11188
11528
  const accumulationBuffer = createBuffer(
11189
11529
  device,
11190
11530
  bufferUsage,
11191
- accumulationBytes,
11531
+ memory.accumulationBytes,
11192
11532
  "plasius.wavefront.accumulation"
11193
11533
  );
11194
11534
  const pathVertexBuffer = createBuffer(
11195
11535
  device,
11196
11536
  bufferUsage,
11197
- pathVertexBytes,
11537
+ memory.pathVertexBytes,
11198
11538
  "plasius.wavefront.pathVertices"
11199
11539
  );
11200
11540
  const sceneObjectBuffer = createBuffer(
11201
11541
  device,
11202
11542
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11203
- config.sceneObjectCapacity * SCENE_OBJECT_RECORD_BYTES,
11543
+ memory.sceneObjectBytes,
11204
11544
  "plasius.wavefront.sceneObjects"
11205
11545
  );
11206
11546
  const triangleBuffer = createBuffer(
11207
11547
  device,
11208
11548
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11209
- Math.max(1, config.triangleCapacity) * TRIANGLE_RECORD_BYTES,
11549
+ memory.triangleBytes,
11210
11550
  "plasius.wavefront.triangles"
11211
11551
  );
11212
11552
  const bvhNodeBuffer = createBuffer(
11213
11553
  device,
11214
11554
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11215
- Math.max(1, config.bvhNodeCapacity + config.emissiveTriangleCapacity) * BVH_NODE_RECORD_BYTES,
11555
+ memory.bvhCombinedBytes,
11216
11556
  "plasius.wavefront.bvhNodes"
11217
11557
  );
11218
11558
  const meshVertexBuffer = createBuffer(
11219
11559
  device,
11220
11560
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11221
- Math.max(1, config.gpuMeshSource.vertices.count) * MESH_VERTEX_RECORD_BYTES,
11561
+ memory.meshVertexBytes,
11222
11562
  "plasius.wavefront.meshVertices"
11223
11563
  );
11224
11564
  const meshIndexBuffer = createBuffer(
11225
11565
  device,
11226
11566
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11227
- Math.max(1, config.gpuMeshSource.indices.count) * 4,
11567
+ memory.meshIndexBytes,
11228
11568
  "plasius.wavefront.meshIndices"
11229
11569
  );
11230
11570
  const meshRangeBuffer = createBuffer(
11231
11571
  device,
11232
11572
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11233
- Math.max(1, config.gpuMeshSource.meshes.count) * MESH_RANGE_RECORD_BYTES,
11573
+ memory.meshRangeBytes,
11234
11574
  "plasius.wavefront.meshRanges"
11235
11575
  );
11236
11576
  const environmentPortalBuffer = createBuffer(
11237
11577
  device,
11238
11578
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11239
- Math.max(1, config.environmentPortalCapacity) * ENVIRONMENT_PORTAL_RECORD_BYTES,
11579
+ memory.environmentPortalBytes,
11240
11580
  "plasius.wavefront.environmentPortals"
11241
11581
  );
11242
11582
  const bvhLeafRefBuffer = createBuffer(
11243
11583
  device,
11244
11584
  constants.buffer.STORAGE | constants.buffer.COPY_DST,
11245
- Math.max(1, config.bvhLeafSortCapacity) * BVH_LEAF_REF_RECORD_BYTES,
11585
+ memory.bvhLeafReferenceBytes,
11246
11586
  "plasius.wavefront.bvhLeafRefs"
11247
11587
  );
11248
11588
  const tiles = createTiles(config.width, config.height, config.tileSize);
11249
- const uniformOffsetAlignment = Number(device?.limits?.minUniformBufferOffsetAlignment);
11250
- const configBufferStride = alignTo(
11251
- CONFIG_BUFFER_BYTES,
11252
- Number.isFinite(uniformOffsetAlignment) && uniformOffsetAlignment > 0 ? uniformOffsetAlignment : CONFIG_BUFFER_BYTES
11253
- );
11589
+ const configBufferStride = memory.configBufferStride;
11254
11590
  const outputConfigSlotCount = config.deferredPathResolve ? 0 : tiles.length;
11255
11591
  const frameConfigSlotCount = Math.max(
11256
11592
  1,
@@ -11259,26 +11595,25 @@ async function createWavefrontPathTracingComputeRenderer(options = {}) {
11259
11595
  const configBuffer = createBuffer(
11260
11596
  device,
11261
11597
  constants.buffer.UNIFORM | constants.buffer.COPY_DST,
11262
- frameConfigSlotCount * configBufferStride,
11598
+ memory.frameConfigBytes,
11263
11599
  "plasius.wavefront.frameConfig"
11264
11600
  );
11265
- const bvhBuildConfigSlots = 1 + config.bvhSortStages.length + config.bvhBuildLevels.length;
11266
11601
  const bvhBuildConfigBuffer = createBuffer(
11267
11602
  device,
11268
11603
  constants.buffer.UNIFORM | constants.buffer.COPY_DST,
11269
- Math.max(1, bvhBuildConfigSlots) * configBufferStride,
11604
+ memory.bvhBuildConfigBytes,
11270
11605
  "plasius.wavefront.bvhBuildConfig"
11271
11606
  );
11272
11607
  const counterBuffer = createBuffer(
11273
11608
  device,
11274
11609
  constants.buffer.STORAGE | constants.buffer.COPY_DST | constants.buffer.COPY_SRC,
11275
- COUNTER_BUFFER_BYTES,
11610
+ memory.counterBytes,
11276
11611
  "plasius.wavefront.counters"
11277
11612
  );
11278
11613
  const activeDispatchBuffer = createBuffer(
11279
11614
  device,
11280
11615
  constants.buffer.INDIRECT | constants.buffer.COPY_DST,
11281
- INDIRECT_DISPATCH_ARGS_BYTES,
11616
+ memory.indirectDispatchBytes,
11282
11617
  "plasius.wavefront.activeDispatchArgs"
11283
11618
  );
11284
11619
  let packedScene = packWavefrontSceneObjects(config.sceneObjects, config.sceneObjectCapacity);
@@ -12988,8 +13323,12 @@ export {
12988
13323
  applyMediumTransmittance,
12989
13324
  beerLambertTransmittance,
12990
13325
  bindRendererToXrManager,
13326
+ bucketFeedbackGameFrameRate,
13327
+ bucketFeedbackGameFrameTime,
13328
+ bucketFeedbackGameViewport,
12991
13329
  createAnimatedSceneRenderer,
12992
13330
  createDefaultWavefrontSceneObjects,
13331
+ createFeedbackGameDiagnosticSnapshot,
12993
13332
  createGpuRenderer,
12994
13333
  createMediumStack,
12995
13334
  createProfessionalAnimatedSceneRenderer,