@plasius/gpu-renderer 0.2.5 → 0.2.7

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@plasius/gpu-renderer",
3
- "version": "0.2.5",
3
+ "version": "0.2.7",
4
4
  "description": "Framework-agnostic WebGPU renderer runtime for Plasius projects.",
5
5
  "type": "module",
6
6
  "sideEffects": false,
@@ -27,7 +27,7 @@
27
27
  "scripts": {
28
28
  "build": "tsup",
29
29
  "demo": "python3 -m http.server --directory ..",
30
- "typecheck": "node --check src/index.js && node --check src/wavefront-compute.js",
30
+ "typecheck": "node --check src/index.js && node --check src/wavefront-frame-runtime.js && node --check src/wavefront-compute.js",
31
31
  "audit:eslint": "eslint . --max-warnings=0",
32
32
  "audit:deps": "npm ls --all --omit=optional --omit=peer > /dev/null 2>&1 || true",
33
33
  "audit:npm": "npm audit --audit-level=high --omit=dev",
package/src/index.d.ts CHANGED
@@ -13,6 +13,56 @@ export type WavefrontMaterialKind =
13
13
  | "light"
14
14
  | number;
15
15
 
16
+ export interface WavefrontAdaptiveSamplingLevelConfig {
17
+ readonly samplesPerPixel: number;
18
+ readonly frameTimeBudgetMs: number;
19
+ readonly minimumSamplesPerPixel: number;
20
+ }
21
+
22
+ export interface WavefrontAdaptiveSamplingLevel {
23
+ readonly id: string;
24
+ readonly label: string;
25
+ readonly estimatedCostMs: number;
26
+ readonly config: WavefrontAdaptiveSamplingLevelConfig;
27
+ }
28
+
29
+ export interface CreateWavefrontAdaptiveSamplingLevelsResult {
30
+ readonly requestedSamplesPerPixel: number;
31
+ readonly minimumSamplesPerPixel: number;
32
+ readonly frameTimeBudgetMs: number;
33
+ readonly levels: readonly WavefrontAdaptiveSamplingLevel[];
34
+ }
35
+
36
+ export interface WavefrontTextureSampleInput {
37
+ readonly texCoord?: number;
38
+ readonly scale?: number;
39
+ readonly strength?: number;
40
+ readonly width: number;
41
+ readonly height: number;
42
+ readonly data: Uint8ClampedArray | Uint8Array | readonly number[];
43
+ }
44
+
45
+ export interface WavefrontMediumInput {
46
+ readonly id?: number;
47
+ readonly mediumId?: number;
48
+ readonly phaseModel?: number | "isotropic";
49
+ readonly density?: number;
50
+ readonly attenuationColor?: RendererColor | readonly number[];
51
+ readonly attenuationDistance?: number;
52
+ readonly absorption?: readonly [number, number, number] | readonly number[];
53
+ readonly scattering?: readonly [number, number, number] | readonly number[];
54
+ }
55
+
56
+ export interface WavefrontVolumeInput {
57
+ readonly thickness?: number;
58
+ readonly phaseModel?: number | "isotropic";
59
+ readonly density?: number;
60
+ readonly attenuationColor?: RendererColor | readonly number[];
61
+ readonly attenuationDistance?: number;
62
+ readonly absorption?: readonly [number, number, number] | readonly number[];
63
+ readonly scattering?: readonly [number, number, number] | readonly number[];
64
+ }
65
+
16
66
  export interface WavefrontCameraOptions {
17
67
  readonly position?: readonly [number, number, number] | readonly number[];
18
68
  readonly target?: readonly [number, number, number] | readonly number[];
@@ -21,12 +71,21 @@ export interface WavefrontCameraOptions {
21
71
  readonly fov?: number;
22
72
  }
23
73
 
74
+ export function createWavefrontAdaptiveSamplingLevels(options?: {
75
+ readonly samplesPerPixel?: number;
76
+ readonly frameTimeBudgetMs?: number;
77
+ readonly minimumSamplesPerPixel?: number;
78
+ }): CreateWavefrontAdaptiveSamplingLevelsResult;
79
+
24
80
  export interface WavefrontSceneObjectInput {
25
81
  readonly id?: number;
26
82
  readonly kind?: WavefrontSceneObjectKind;
27
83
  readonly type?: WavefrontSceneObjectKind;
28
84
  readonly materialKind?: WavefrontMaterialKind;
29
85
  readonly flags?: number;
86
+ readonly mediumRefId?: number;
87
+ readonly mediumId?: number;
88
+ readonly medium?: WavefrontMediumInput | null;
30
89
  readonly center?: readonly [number, number, number] | readonly number[];
31
90
  readonly position?: readonly [number, number, number] | readonly number[];
32
91
  readonly radius?: number;
@@ -48,6 +107,16 @@ export interface WavefrontSceneObjectInput {
48
107
  readonly metallic?: number;
49
108
  readonly opacity?: number;
50
109
  readonly ior?: number;
110
+ readonly sheen?: number;
111
+ readonly sheenTint?: number;
112
+ readonly sheenColor?: RendererColor | readonly number[];
113
+ readonly clearcoat?: number;
114
+ readonly clearcoatRoughness?: number;
115
+ readonly specular?: number;
116
+ readonly specularColor?: RendererColor | readonly number[];
117
+ readonly thickness?: number;
118
+ readonly transmission?: number;
119
+ readonly volume?: WavefrontVolumeInput | null;
51
120
  readonly material?: {
52
121
  readonly kind?: WavefrontMaterialKind;
53
122
  readonly color?: RendererColor | readonly number[];
@@ -58,6 +127,18 @@ export interface WavefrontSceneObjectInput {
58
127
  readonly metallic?: number;
59
128
  readonly opacity?: number;
60
129
  readonly ior?: number;
130
+ readonly sheen?: number;
131
+ readonly sheenTint?: number;
132
+ readonly sheenColor?: RendererColor | readonly number[];
133
+ readonly clearcoat?: number;
134
+ readonly clearcoatRoughness?: number;
135
+ readonly specular?: number;
136
+ readonly specularColor?: RendererColor | readonly number[];
137
+ readonly thickness?: number;
138
+ readonly transmission?: number;
139
+ readonly medium?: WavefrontMediumInput | null;
140
+ readonly mediumId?: number;
141
+ readonly volume?: WavefrontVolumeInput | null;
61
142
  };
62
143
  }
63
144
 
@@ -66,6 +147,8 @@ export interface WavefrontSceneObject {
66
147
  readonly kind: number;
67
148
  readonly materialKind: number;
68
149
  readonly flags: number;
150
+ readonly mediumRefId: number;
151
+ readonly medium: WavefrontMediumInput | null;
69
152
  readonly center: readonly [number, number, number] | readonly number[];
70
153
  readonly halfExtent: readonly [number, number, number] | readonly number[];
71
154
  readonly color: readonly [number, number, number, number] | readonly number[];
@@ -74,6 +157,15 @@ export interface WavefrontSceneObject {
74
157
  readonly metallic: number;
75
158
  readonly opacity: number;
76
159
  readonly ior: number;
160
+ readonly sheen: number;
161
+ readonly sheenTint: number;
162
+ readonly sheenColor: readonly [number, number, number, number] | readonly number[];
163
+ readonly clearcoat: number;
164
+ readonly clearcoatRoughness: number;
165
+ readonly specular: number;
166
+ readonly specularColor: readonly [number, number, number, number] | readonly number[];
167
+ readonly thickness: number;
168
+ readonly transmission: number;
77
169
  }
78
170
 
79
171
  export interface WavefrontMeshInput {
@@ -99,6 +191,16 @@ export interface WavefrontMeshInput {
99
191
  readonly metallic?: number;
100
192
  readonly opacity?: number;
101
193
  readonly ior?: number;
194
+ readonly sheen?: number;
195
+ readonly sheenTint?: number;
196
+ readonly sheenColor?: RendererColor | readonly number[];
197
+ readonly clearcoat?: number;
198
+ readonly clearcoatRoughness?: number;
199
+ readonly specular?: number;
200
+ readonly specularColor?: RendererColor | readonly number[];
201
+ readonly thickness?: number;
202
+ readonly transmission?: number;
203
+ readonly volume?: WavefrontVolumeInput | null;
102
204
  readonly material?: {
103
205
  readonly kind?: WavefrontMaterialKind;
104
206
  readonly color?: RendererColor | readonly number[];
@@ -109,11 +211,31 @@ export interface WavefrontMeshInput {
109
211
  readonly metallic?: number;
110
212
  readonly opacity?: number;
111
213
  readonly ior?: number;
214
+ readonly sheen?: number;
215
+ readonly sheenTint?: number;
216
+ readonly sheenColor?: RendererColor | readonly number[];
217
+ readonly clearcoat?: number;
218
+ readonly clearcoatRoughness?: number;
219
+ readonly specular?: number;
220
+ readonly specularColor?: RendererColor | readonly number[];
221
+ readonly thickness?: number;
222
+ readonly transmission?: number;
112
223
  readonly id?: number;
224
+ readonly medium?: WavefrontMediumInput | null;
225
+ readonly mediumId?: number;
226
+ readonly volume?: WavefrontVolumeInput | null;
227
+ readonly baseColorTexture?: WavefrontTextureSampleInput | null;
228
+ readonly metallicRoughnessTexture?: WavefrontTextureSampleInput | null;
229
+ readonly normalTexture?: WavefrontTextureSampleInput | null;
230
+ readonly occlusionTexture?: WavefrontTextureSampleInput | null;
231
+ readonly emissiveTexture?: WavefrontTextureSampleInput | null;
113
232
  };
114
- readonly medium?: {
115
- readonly id?: number;
116
- };
233
+ readonly baseColorTexture?: WavefrontTextureSampleInput | null;
234
+ readonly metallicRoughnessTexture?: WavefrontTextureSampleInput | null;
235
+ readonly normalTexture?: WavefrontTextureSampleInput | null;
236
+ readonly occlusionTexture?: WavefrontTextureSampleInput | null;
237
+ readonly emissiveTexture?: WavefrontTextureSampleInput | null;
238
+ readonly medium?: WavefrontMediumInput | null;
117
239
  }
118
240
 
119
241
  export interface WavefrontTriangleRecord {
@@ -123,6 +245,7 @@ export interface WavefrontTriangleRecord {
123
245
  readonly flags: number;
124
246
  readonly materialRefId: number;
125
247
  readonly mediumRefId: number;
248
+ readonly materialSlot?: number;
126
249
  readonly v0: readonly number[];
127
250
  readonly v1: readonly number[];
128
251
  readonly v2: readonly number[];
@@ -135,6 +258,15 @@ export interface WavefrontTriangleRecord {
135
258
  readonly color: readonly number[];
136
259
  readonly emission: readonly number[];
137
260
  readonly material: readonly number[];
261
+ readonly materialResponse: readonly number[];
262
+ readonly materialExtension?: readonly number[];
263
+ readonly specularColor?: readonly number[];
264
+ readonly baseColorAtlas?: readonly number[];
265
+ readonly metallicRoughnessAtlas?: readonly number[];
266
+ readonly normalAtlas?: readonly number[];
267
+ readonly occlusionAtlas?: readonly number[];
268
+ readonly emissiveAtlas?: readonly number[];
269
+ readonly textureSettings?: readonly number[];
138
270
  readonly bounds: Readonly<{ min: readonly number[]; max: readonly number[] }>;
139
271
  readonly centroid: readonly number[];
140
272
  }
@@ -214,6 +346,7 @@ export interface WavefrontReferenceHit {
214
346
  readonly color: readonly number[];
215
347
  readonly emission: readonly number[];
216
348
  readonly material: readonly number[];
349
+ readonly materialResponse: readonly number[];
217
350
  }
218
351
 
219
352
  export interface IntersectWavefrontReferenceTriangleOptions {
@@ -250,12 +383,27 @@ export interface WavefrontGpuMeshSource {
250
383
  readonly flags: number;
251
384
  readonly materialRefId: number;
252
385
  readonly mediumRefId: number;
386
+ readonly medium: WavefrontMediumInput | null;
253
387
  readonly color: readonly number[];
254
388
  readonly emission: readonly number[];
255
389
  readonly roughness: number;
256
390
  readonly metallic: number;
257
391
  readonly opacity: number;
258
392
  readonly ior: number;
393
+ readonly sheen: number;
394
+ readonly sheenTint: number;
395
+ readonly sheenColor: readonly [number, number, number, number] | readonly number[];
396
+ readonly clearcoat: number;
397
+ readonly clearcoatRoughness: number;
398
+ readonly specular: number;
399
+ readonly specularColor: readonly [number, number, number, number] | readonly number[];
400
+ readonly thickness: number;
401
+ readonly transmission: number;
402
+ readonly baseColorTexture: WavefrontTextureSampleInput | null;
403
+ readonly metallicRoughnessTexture: WavefrontTextureSampleInput | null;
404
+ readonly normalTexture: WavefrontTextureSampleInput | null;
405
+ readonly occlusionTexture: WavefrontTextureSampleInput | null;
406
+ readonly emissiveTexture: WavefrontTextureSampleInput | null;
259
407
  }>[];
260
408
  readonly count: number;
261
409
  readonly recordBytes: number;
@@ -264,6 +412,24 @@ export interface WavefrontGpuMeshSource {
264
412
  readonly bvhNodeCapacity: number;
265
413
  }
266
414
 
415
+ export interface WavefrontGpuTextureAtlasSource {
416
+ readonly width: number;
417
+ readonly height: number;
418
+ readonly data: Uint8Array;
419
+ readonly defaultRect: readonly [number, number, number, number] | readonly number[];
420
+ }
421
+
422
+ export interface WavefrontGpuMaterialSource {
423
+ readonly buffer: ArrayBuffer;
424
+ readonly count: number;
425
+ readonly recordBytes: number;
426
+ readonly baseColorAtlas: WavefrontGpuTextureAtlasSource;
427
+ readonly metallicRoughnessAtlas: WavefrontGpuTextureAtlasSource;
428
+ readonly normalAtlas: WavefrontGpuTextureAtlasSource;
429
+ readonly occlusionAtlas: WavefrontGpuTextureAtlasSource;
430
+ readonly emissiveAtlas: WavefrontGpuTextureAtlasSource;
431
+ }
432
+
267
433
  export interface WavefrontEmissiveTriangleIndexSource {
268
434
  readonly buffer: ArrayBuffer;
269
435
  readonly indices: readonly number[];
@@ -312,6 +478,7 @@ export interface WavefrontEnvironmentMapInput {
312
478
  readonly enabled?: boolean;
313
479
  readonly width?: number;
314
480
  readonly height?: number;
481
+ readonly mipLevelCount?: number;
315
482
  readonly projection?: "equirectangular" | string;
316
483
  readonly format?: GPUTextureFormat | "rgba16float";
317
484
  readonly texture?: GPUTexture;
@@ -323,16 +490,19 @@ export interface WavefrontEnvironmentMapInput {
323
490
  readonly rotationRadians?: number;
324
491
  readonly rotation?: number;
325
492
  readonly ambientStrength?: number;
493
+ readonly hasImportanceData?: boolean;
326
494
  }
327
495
 
328
496
  export interface WavefrontEnvironmentMapSnapshot {
329
497
  readonly enabled: boolean;
330
498
  readonly width: number;
331
499
  readonly height: number;
500
+ readonly mipLevelCount: number;
332
501
  readonly projection: string;
333
502
  readonly intensity: number;
334
503
  readonly rotationRadians: number;
335
504
  readonly ambientStrength: number;
505
+ readonly hasImportanceData: boolean;
336
506
  }
337
507
 
338
508
  export interface WavefrontPathTracingComputeConfig {
@@ -347,9 +517,20 @@ export interface WavefrontPathTracingComputeConfig {
347
517
  readonly sceneObjects: readonly WavefrontSceneObject[];
348
518
  readonly sceneObjectCount: number;
349
519
  readonly sceneObjectCapacity: number;
520
+ readonly mediums: readonly Readonly<{
521
+ readonly id: number;
522
+ readonly phaseModel: number;
523
+ readonly density: number;
524
+ readonly attenuationColor: readonly [number, number, number, number] | readonly number[];
525
+ readonly attenuationDistance: number;
526
+ readonly absorption: readonly [number, number, number] | readonly number[];
527
+ readonly scattering: readonly [number, number, number] | readonly number[];
528
+ }>[];
529
+ readonly mediumCount: number;
350
530
  readonly accelerationBuildMode: WavefrontAccelerationBuildMode;
351
531
  readonly gpuAccelerationBuildRequired: boolean;
352
532
  readonly gpuMeshSource: WavefrontGpuMeshSource;
533
+ readonly gpuMaterialSource: WavefrontGpuMaterialSource;
353
534
  readonly meshAcceleration: WavefrontMeshAcceleration;
354
535
  readonly emissiveTriangleIndices: WavefrontEmissiveTriangleIndexSource;
355
536
  readonly emissiveTriangleCount: number;
@@ -424,6 +605,7 @@ export interface WavefrontPathTracingMemoryEstimate {
424
605
  readonly pathVertexBytes: number;
425
606
  readonly sceneObjectBytes: number;
426
607
  readonly triangleBytes: number;
608
+ readonly materialTableBytes: number;
427
609
  readonly bvhNodeBytes: number;
428
610
  readonly bvhLeafReferenceBytes: number;
429
611
  readonly emissiveTriangleMetadataBytes: number;
@@ -489,6 +671,7 @@ export interface CreateWavefrontPathTracingComputeRendererOptions {
489
671
  readonly tilePixelCapacity?: number;
490
672
  readonly sceneObjectCapacity?: number;
491
673
  readonly sceneObjects?: readonly WavefrontSceneObjectInput[];
674
+ readonly mediums?: readonly WavefrontMediumInput[];
492
675
  readonly mesh?: WavefrontMeshInput;
493
676
  readonly meshes?: readonly WavefrontMeshInput[];
494
677
  readonly triangleCapacity?: number;
@@ -525,6 +708,11 @@ export interface WavefrontPathTracingComputeRenderer {
525
708
  renderFrame(options?: {
526
709
  readStats?: boolean;
527
710
  readOutputProbe?: boolean;
711
+ awaitGPUCompletion?: boolean;
712
+ submittedWorkTimeoutMs?: number;
713
+ samplesPerPixel?: number;
714
+ minimumSamplesPerPixel?: number;
715
+ frameTimeBudgetMs?: number;
528
716
  probe?: { x?: number; y?: number };
529
717
  }): Promise<WavefrontPathTracingComputeFrameStats>;
530
718
  readOutputProbe(options?: { x?: number; y?: number }): Promise<
@@ -551,6 +739,7 @@ export interface WavefrontPathTracingComputeRenderer {
551
739
  emissiveTriangleCount: number;
552
740
  environmentPortalCount: number;
553
741
  environmentPortalMode: 0 | 1 | 2;
742
+ mediumCount: number;
554
743
  environmentMap: WavefrontEnvironmentMapSnapshot;
555
744
  deferredPathResolve: boolean;
556
745
  bvhNodeCount: number;
@@ -574,6 +763,9 @@ export interface WavefrontPathTracingComputeFrameStats {
574
763
  readonly tiles: number;
575
764
  readonly tileSize: number;
576
765
  readonly samplesPerPixel: number;
766
+ readonly renderedSamplesPerPixel: number;
767
+ readonly frameTimeBudgetMs: number | null;
768
+ readonly budgetConstrained: boolean;
577
769
  readonly maxFramePassesPerSubmission: number;
578
770
  readonly screenRays: number;
579
771
  readonly primaryRays: number;
@@ -582,6 +774,7 @@ export interface WavefrontPathTracingComputeFrameStats {
582
774
  readonly emissiveTriangleCount: number;
583
775
  readonly environmentPortalCount: number;
584
776
  readonly environmentPortalMode: 0 | 1 | 2;
777
+ readonly mediumCount: number;
585
778
  readonly environmentMap: WavefrontEnvironmentMapSnapshot;
586
779
  readonly deferredPathResolve: boolean;
587
780
  readonly bvhNodeCount: number;
@@ -592,6 +785,15 @@ export interface WavefrontPathTracingComputeFrameStats {
592
785
  readonly accelerationBuilt: boolean;
593
786
  readonly accelerationBuildCount: number;
594
787
  readonly commandSubmissions: number;
788
+ readonly gpuWorkerJobs: Readonly<{
789
+ completedPerFrame: number;
790
+ completedPerSecond: number | null;
791
+ completedPerSubmission: number;
792
+ directDispatchesCompleted: number;
793
+ indirectDispatchesCompleted: number;
794
+ frameTimeMs: number;
795
+ awaitedGpuCompletion: boolean;
796
+ }>;
595
797
  readonly frameConfigSlots: number;
596
798
  readonly gpuParallelism: WavefrontGpuParallelismDiagnostics;
597
799
  readonly memory: WavefrontPathTracingMemoryEstimate;
@@ -618,6 +820,9 @@ export function normalizeWavefrontSceneObject(
618
820
  input?: WavefrontSceneObjectInput,
619
821
  index?: number
620
822
  ): WavefrontSceneObject;
823
+ export function createWavefrontGpuMaterialSource(
824
+ meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
825
+ ): WavefrontGpuMaterialSource;
621
826
  export function createDefaultWavefrontSceneObjects(): readonly WavefrontSceneObject[];
622
827
  export function estimateWavefrontPathTracingMemory(options?: {
623
828
  tilePixelCapacity?: number;
@@ -646,12 +851,25 @@ export function normalizeWavefrontMesh(
646
851
  metallic: number;
647
852
  opacity: number;
648
853
  ior: number;
854
+ sheen: number;
855
+ sheenTint: number;
856
+ sheenColor: readonly [number, number, number, number] | readonly number[];
857
+ clearcoat: number;
858
+ clearcoatRoughness: number;
859
+ specular: number;
860
+ specularColor: readonly [number, number, number, number] | readonly number[];
861
+ transmission: number;
862
+ baseColorTexture: WavefrontTextureSampleInput | null;
863
+ metallicRoughnessTexture: WavefrontTextureSampleInput | null;
864
+ normalTexture: WavefrontTextureSampleInput | null;
865
+ occlusionTexture: WavefrontTextureSampleInput | null;
649
866
  }>;
650
867
  export function createWavefrontMeshAcceleration(
651
868
  meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
652
869
  ): WavefrontMeshAcceleration;
653
870
  export function createWavefrontGpuMeshSource(
654
- meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
871
+ meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput,
872
+ gpuMaterialSource?: WavefrontGpuMaterialSource | null
655
873
  ): WavefrontGpuMeshSource;
656
874
  export function createWavefrontEmissiveTriangleIndexSource(
657
875
  meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput,
@@ -731,12 +949,14 @@ export const rendererWavefrontComputeStatsStride: 8;
731
949
 
732
950
  export const wavefrontPathTracingComputeLimits: Readonly<{
733
951
  workgroupSize: 64;
952
+ traceStorageBufferBindings: number;
734
953
  rayRecordBytes: 80;
735
- hitRecordBytes: 208;
736
- sceneObjectRecordBytes: 96;
954
+ hitRecordBytes: 256;
955
+ sceneObjectRecordBytes: 160;
737
956
  meshVertexRecordBytes: 48;
738
- meshRangeRecordBytes: 96;
739
- triangleRecordBytes: 208;
957
+ meshRangeRecordBytes: 240;
958
+ triangleRecordBytes: 352;
959
+ materialRecordBytes: 192;
740
960
  bvhNodeRecordBytes: 48;
741
961
  bvhLeafReferenceRecordBytes: 16;
742
962
  emissiveTriangleIndexBytes: 4;
package/src/index.js CHANGED
@@ -5,6 +5,7 @@ export {
5
5
  createWavefrontBvhBuildLevels,
6
6
  createWavefrontBvhSortStages,
7
7
  createWavefrontEmissiveTriangleIndexSource,
8
+ createWavefrontGpuMaterialSource,
8
9
  createWavefrontGpuMeshSource,
9
10
  createWavefrontMeshAcceleration,
10
11
  createWavefrontPathTracingComputeConfig,
@@ -149,6 +150,57 @@ const rendererAccelerationStructurePolicies = Object.freeze(
149
150
  )
150
151
  );
151
152
 
153
+ function clampWavefrontAdaptiveSamplesPerPixel(value) {
154
+ if (!Number.isFinite(value)) {
155
+ return 1;
156
+ }
157
+ return Math.max(1, Math.min(256, Math.round(value)));
158
+ }
159
+
160
+ export function createWavefrontAdaptiveSamplingLevels(options = {}) {
161
+ const requestedSamplesPerPixel = clampWavefrontAdaptiveSamplesPerPixel(
162
+ options.samplesPerPixel ?? 1
163
+ );
164
+ const minimumSamplesPerPixel = Math.min(
165
+ requestedSamplesPerPixel,
166
+ clampWavefrontAdaptiveSamplesPerPixel(options.minimumSamplesPerPixel ?? 1)
167
+ );
168
+ const frameTimeBudgetMs = Number.isFinite(options.frameTimeBudgetMs)
169
+ ? Math.max(0, Number(options.frameTimeBudgetMs))
170
+ : 0;
171
+ const levels = new Set([minimumSamplesPerPixel, requestedSamplesPerPixel]);
172
+ let currentSamplesPerPixel = minimumSamplesPerPixel;
173
+
174
+ while (currentSamplesPerPixel < requestedSamplesPerPixel) {
175
+ levels.add(currentSamplesPerPixel);
176
+ currentSamplesPerPixel *= 2;
177
+ }
178
+
179
+ levels.add(Math.min(currentSamplesPerPixel, requestedSamplesPerPixel));
180
+
181
+ return Object.freeze({
182
+ requestedSamplesPerPixel,
183
+ minimumSamplesPerPixel,
184
+ frameTimeBudgetMs,
185
+ levels: Object.freeze(
186
+ [...levels]
187
+ .sort((left, right) => left - right)
188
+ .map((samplesPerPixel) =>
189
+ Object.freeze({
190
+ id: `${samplesPerPixel}spp`,
191
+ label: `${samplesPerPixel} spp`,
192
+ estimatedCostMs: samplesPerPixel,
193
+ config: Object.freeze({
194
+ samplesPerPixel,
195
+ frameTimeBudgetMs,
196
+ minimumSamplesPerPixel,
197
+ }),
198
+ })
199
+ )
200
+ ),
201
+ });
202
+ }
203
+
152
204
  function createWavefrontField(name, type, description) {
153
205
  return Object.freeze({
154
206
  name,