@plasius/gpu-renderer 0.2.5 → 0.2.6

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.6",
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,35 @@ 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
+
16
45
  export interface WavefrontCameraOptions {
17
46
  readonly position?: readonly [number, number, number] | readonly number[];
18
47
  readonly target?: readonly [number, number, number] | readonly number[];
@@ -21,6 +50,12 @@ export interface WavefrontCameraOptions {
21
50
  readonly fov?: number;
22
51
  }
23
52
 
53
+ export function createWavefrontAdaptiveSamplingLevels(options?: {
54
+ readonly samplesPerPixel?: number;
55
+ readonly frameTimeBudgetMs?: number;
56
+ readonly minimumSamplesPerPixel?: number;
57
+ }): CreateWavefrontAdaptiveSamplingLevelsResult;
58
+
24
59
  export interface WavefrontSceneObjectInput {
25
60
  readonly id?: number;
26
61
  readonly kind?: WavefrontSceneObjectKind;
@@ -48,6 +83,14 @@ export interface WavefrontSceneObjectInput {
48
83
  readonly metallic?: number;
49
84
  readonly opacity?: number;
50
85
  readonly ior?: number;
86
+ readonly sheen?: number;
87
+ readonly sheenTint?: number;
88
+ readonly sheenColor?: RendererColor | readonly number[];
89
+ readonly clearcoat?: number;
90
+ readonly clearcoatRoughness?: number;
91
+ readonly specular?: number;
92
+ readonly specularColor?: RendererColor | readonly number[];
93
+ readonly transmission?: number;
51
94
  readonly material?: {
52
95
  readonly kind?: WavefrontMaterialKind;
53
96
  readonly color?: RendererColor | readonly number[];
@@ -58,6 +101,14 @@ export interface WavefrontSceneObjectInput {
58
101
  readonly metallic?: number;
59
102
  readonly opacity?: number;
60
103
  readonly ior?: number;
104
+ readonly sheen?: number;
105
+ readonly sheenTint?: number;
106
+ readonly sheenColor?: RendererColor | readonly number[];
107
+ readonly clearcoat?: number;
108
+ readonly clearcoatRoughness?: number;
109
+ readonly specular?: number;
110
+ readonly specularColor?: RendererColor | readonly number[];
111
+ readonly transmission?: number;
61
112
  };
62
113
  }
63
114
 
@@ -74,6 +125,14 @@ export interface WavefrontSceneObject {
74
125
  readonly metallic: number;
75
126
  readonly opacity: number;
76
127
  readonly ior: number;
128
+ readonly sheen: number;
129
+ readonly sheenTint: number;
130
+ readonly sheenColor: readonly [number, number, number, number] | readonly number[];
131
+ readonly clearcoat: number;
132
+ readonly clearcoatRoughness: number;
133
+ readonly specular: number;
134
+ readonly specularColor: readonly [number, number, number, number] | readonly number[];
135
+ readonly transmission: number;
77
136
  }
78
137
 
79
138
  export interface WavefrontMeshInput {
@@ -99,6 +158,14 @@ export interface WavefrontMeshInput {
99
158
  readonly metallic?: number;
100
159
  readonly opacity?: number;
101
160
  readonly ior?: number;
161
+ readonly sheen?: number;
162
+ readonly sheenTint?: number;
163
+ readonly sheenColor?: RendererColor | readonly number[];
164
+ readonly clearcoat?: number;
165
+ readonly clearcoatRoughness?: number;
166
+ readonly specular?: number;
167
+ readonly specularColor?: RendererColor | readonly number[];
168
+ readonly transmission?: number;
102
169
  readonly material?: {
103
170
  readonly kind?: WavefrontMaterialKind;
104
171
  readonly color?: RendererColor | readonly number[];
@@ -109,8 +176,26 @@ export interface WavefrontMeshInput {
109
176
  readonly metallic?: number;
110
177
  readonly opacity?: number;
111
178
  readonly ior?: number;
179
+ readonly sheen?: number;
180
+ readonly sheenTint?: number;
181
+ readonly sheenColor?: RendererColor | readonly number[];
182
+ readonly clearcoat?: number;
183
+ readonly clearcoatRoughness?: number;
184
+ readonly specular?: number;
185
+ readonly specularColor?: RendererColor | readonly number[];
186
+ readonly transmission?: number;
112
187
  readonly id?: number;
188
+ readonly baseColorTexture?: WavefrontTextureSampleInput | null;
189
+ readonly metallicRoughnessTexture?: WavefrontTextureSampleInput | null;
190
+ readonly normalTexture?: WavefrontTextureSampleInput | null;
191
+ readonly occlusionTexture?: WavefrontTextureSampleInput | null;
192
+ readonly emissiveTexture?: WavefrontTextureSampleInput | null;
113
193
  };
194
+ readonly baseColorTexture?: WavefrontTextureSampleInput | null;
195
+ readonly metallicRoughnessTexture?: WavefrontTextureSampleInput | null;
196
+ readonly normalTexture?: WavefrontTextureSampleInput | null;
197
+ readonly occlusionTexture?: WavefrontTextureSampleInput | null;
198
+ readonly emissiveTexture?: WavefrontTextureSampleInput | null;
114
199
  readonly medium?: {
115
200
  readonly id?: number;
116
201
  };
@@ -123,6 +208,7 @@ export interface WavefrontTriangleRecord {
123
208
  readonly flags: number;
124
209
  readonly materialRefId: number;
125
210
  readonly mediumRefId: number;
211
+ readonly materialSlot?: number;
126
212
  readonly v0: readonly number[];
127
213
  readonly v1: readonly number[];
128
214
  readonly v2: readonly number[];
@@ -135,6 +221,15 @@ export interface WavefrontTriangleRecord {
135
221
  readonly color: readonly number[];
136
222
  readonly emission: readonly number[];
137
223
  readonly material: readonly number[];
224
+ readonly materialResponse: readonly number[];
225
+ readonly materialExtension?: readonly number[];
226
+ readonly specularColor?: readonly number[];
227
+ readonly baseColorAtlas?: readonly number[];
228
+ readonly metallicRoughnessAtlas?: readonly number[];
229
+ readonly normalAtlas?: readonly number[];
230
+ readonly occlusionAtlas?: readonly number[];
231
+ readonly emissiveAtlas?: readonly number[];
232
+ readonly textureSettings?: readonly number[];
138
233
  readonly bounds: Readonly<{ min: readonly number[]; max: readonly number[] }>;
139
234
  readonly centroid: readonly number[];
140
235
  }
@@ -214,6 +309,7 @@ export interface WavefrontReferenceHit {
214
309
  readonly color: readonly number[];
215
310
  readonly emission: readonly number[];
216
311
  readonly material: readonly number[];
312
+ readonly materialResponse: readonly number[];
217
313
  }
218
314
 
219
315
  export interface IntersectWavefrontReferenceTriangleOptions {
@@ -256,6 +352,19 @@ export interface WavefrontGpuMeshSource {
256
352
  readonly metallic: number;
257
353
  readonly opacity: number;
258
354
  readonly ior: number;
355
+ readonly sheen: number;
356
+ readonly sheenTint: number;
357
+ readonly sheenColor: readonly [number, number, number, number] | readonly number[];
358
+ readonly clearcoat: number;
359
+ readonly clearcoatRoughness: number;
360
+ readonly specular: number;
361
+ readonly specularColor: readonly [number, number, number, number] | readonly number[];
362
+ readonly transmission: number;
363
+ readonly baseColorTexture: WavefrontTextureSampleInput | null;
364
+ readonly metallicRoughnessTexture: WavefrontTextureSampleInput | null;
365
+ readonly normalTexture: WavefrontTextureSampleInput | null;
366
+ readonly occlusionTexture: WavefrontTextureSampleInput | null;
367
+ readonly emissiveTexture: WavefrontTextureSampleInput | null;
259
368
  }>[];
260
369
  readonly count: number;
261
370
  readonly recordBytes: number;
@@ -264,6 +373,24 @@ export interface WavefrontGpuMeshSource {
264
373
  readonly bvhNodeCapacity: number;
265
374
  }
266
375
 
376
+ export interface WavefrontGpuTextureAtlasSource {
377
+ readonly width: number;
378
+ readonly height: number;
379
+ readonly data: Uint8Array;
380
+ readonly defaultRect: readonly [number, number, number, number] | readonly number[];
381
+ }
382
+
383
+ export interface WavefrontGpuMaterialSource {
384
+ readonly buffer: ArrayBuffer;
385
+ readonly count: number;
386
+ readonly recordBytes: number;
387
+ readonly baseColorAtlas: WavefrontGpuTextureAtlasSource;
388
+ readonly metallicRoughnessAtlas: WavefrontGpuTextureAtlasSource;
389
+ readonly normalAtlas: WavefrontGpuTextureAtlasSource;
390
+ readonly occlusionAtlas: WavefrontGpuTextureAtlasSource;
391
+ readonly emissiveAtlas: WavefrontGpuTextureAtlasSource;
392
+ }
393
+
267
394
  export interface WavefrontEmissiveTriangleIndexSource {
268
395
  readonly buffer: ArrayBuffer;
269
396
  readonly indices: readonly number[];
@@ -312,6 +439,7 @@ export interface WavefrontEnvironmentMapInput {
312
439
  readonly enabled?: boolean;
313
440
  readonly width?: number;
314
441
  readonly height?: number;
442
+ readonly mipLevelCount?: number;
315
443
  readonly projection?: "equirectangular" | string;
316
444
  readonly format?: GPUTextureFormat | "rgba16float";
317
445
  readonly texture?: GPUTexture;
@@ -323,16 +451,19 @@ export interface WavefrontEnvironmentMapInput {
323
451
  readonly rotationRadians?: number;
324
452
  readonly rotation?: number;
325
453
  readonly ambientStrength?: number;
454
+ readonly hasImportanceData?: boolean;
326
455
  }
327
456
 
328
457
  export interface WavefrontEnvironmentMapSnapshot {
329
458
  readonly enabled: boolean;
330
459
  readonly width: number;
331
460
  readonly height: number;
461
+ readonly mipLevelCount: number;
332
462
  readonly projection: string;
333
463
  readonly intensity: number;
334
464
  readonly rotationRadians: number;
335
465
  readonly ambientStrength: number;
466
+ readonly hasImportanceData: boolean;
336
467
  }
337
468
 
338
469
  export interface WavefrontPathTracingComputeConfig {
@@ -350,6 +481,7 @@ export interface WavefrontPathTracingComputeConfig {
350
481
  readonly accelerationBuildMode: WavefrontAccelerationBuildMode;
351
482
  readonly gpuAccelerationBuildRequired: boolean;
352
483
  readonly gpuMeshSource: WavefrontGpuMeshSource;
484
+ readonly gpuMaterialSource: WavefrontGpuMaterialSource;
353
485
  readonly meshAcceleration: WavefrontMeshAcceleration;
354
486
  readonly emissiveTriangleIndices: WavefrontEmissiveTriangleIndexSource;
355
487
  readonly emissiveTriangleCount: number;
@@ -424,6 +556,7 @@ export interface WavefrontPathTracingMemoryEstimate {
424
556
  readonly pathVertexBytes: number;
425
557
  readonly sceneObjectBytes: number;
426
558
  readonly triangleBytes: number;
559
+ readonly materialTableBytes: number;
427
560
  readonly bvhNodeBytes: number;
428
561
  readonly bvhLeafReferenceBytes: number;
429
562
  readonly emissiveTriangleMetadataBytes: number;
@@ -525,6 +658,11 @@ export interface WavefrontPathTracingComputeRenderer {
525
658
  renderFrame(options?: {
526
659
  readStats?: boolean;
527
660
  readOutputProbe?: boolean;
661
+ awaitGPUCompletion?: boolean;
662
+ submittedWorkTimeoutMs?: number;
663
+ samplesPerPixel?: number;
664
+ minimumSamplesPerPixel?: number;
665
+ frameTimeBudgetMs?: number;
528
666
  probe?: { x?: number; y?: number };
529
667
  }): Promise<WavefrontPathTracingComputeFrameStats>;
530
668
  readOutputProbe(options?: { x?: number; y?: number }): Promise<
@@ -574,6 +712,9 @@ export interface WavefrontPathTracingComputeFrameStats {
574
712
  readonly tiles: number;
575
713
  readonly tileSize: number;
576
714
  readonly samplesPerPixel: number;
715
+ readonly renderedSamplesPerPixel: number;
716
+ readonly frameTimeBudgetMs: number | null;
717
+ readonly budgetConstrained: boolean;
577
718
  readonly maxFramePassesPerSubmission: number;
578
719
  readonly screenRays: number;
579
720
  readonly primaryRays: number;
@@ -592,6 +733,15 @@ export interface WavefrontPathTracingComputeFrameStats {
592
733
  readonly accelerationBuilt: boolean;
593
734
  readonly accelerationBuildCount: number;
594
735
  readonly commandSubmissions: number;
736
+ readonly gpuWorkerJobs: Readonly<{
737
+ completedPerFrame: number;
738
+ completedPerSecond: number | null;
739
+ completedPerSubmission: number;
740
+ directDispatchesCompleted: number;
741
+ indirectDispatchesCompleted: number;
742
+ frameTimeMs: number;
743
+ awaitedGpuCompletion: boolean;
744
+ }>;
595
745
  readonly frameConfigSlots: number;
596
746
  readonly gpuParallelism: WavefrontGpuParallelismDiagnostics;
597
747
  readonly memory: WavefrontPathTracingMemoryEstimate;
@@ -618,6 +768,9 @@ export function normalizeWavefrontSceneObject(
618
768
  input?: WavefrontSceneObjectInput,
619
769
  index?: number
620
770
  ): WavefrontSceneObject;
771
+ export function createWavefrontGpuMaterialSource(
772
+ meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
773
+ ): WavefrontGpuMaterialSource;
621
774
  export function createDefaultWavefrontSceneObjects(): readonly WavefrontSceneObject[];
622
775
  export function estimateWavefrontPathTracingMemory(options?: {
623
776
  tilePixelCapacity?: number;
@@ -646,12 +799,25 @@ export function normalizeWavefrontMesh(
646
799
  metallic: number;
647
800
  opacity: number;
648
801
  ior: number;
802
+ sheen: number;
803
+ sheenTint: number;
804
+ sheenColor: readonly [number, number, number, number] | readonly number[];
805
+ clearcoat: number;
806
+ clearcoatRoughness: number;
807
+ specular: number;
808
+ specularColor: readonly [number, number, number, number] | readonly number[];
809
+ transmission: number;
810
+ baseColorTexture: WavefrontTextureSampleInput | null;
811
+ metallicRoughnessTexture: WavefrontTextureSampleInput | null;
812
+ normalTexture: WavefrontTextureSampleInput | null;
813
+ occlusionTexture: WavefrontTextureSampleInput | null;
649
814
  }>;
650
815
  export function createWavefrontMeshAcceleration(
651
816
  meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
652
817
  ): WavefrontMeshAcceleration;
653
818
  export function createWavefrontGpuMeshSource(
654
- meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput
819
+ meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput,
820
+ gpuMaterialSource?: WavefrontGpuMaterialSource | null
655
821
  ): WavefrontGpuMeshSource;
656
822
  export function createWavefrontEmissiveTriangleIndexSource(
657
823
  meshes?: readonly WavefrontMeshInput[] | WavefrontMeshInput,
@@ -731,12 +897,14 @@ export const rendererWavefrontComputeStatsStride: 8;
731
897
 
732
898
  export const wavefrontPathTracingComputeLimits: Readonly<{
733
899
  workgroupSize: 64;
900
+ traceStorageBufferBindings: number;
734
901
  rayRecordBytes: 80;
735
- hitRecordBytes: 208;
736
- sceneObjectRecordBytes: 96;
902
+ hitRecordBytes: 256;
903
+ sceneObjectRecordBytes: 144;
737
904
  meshVertexRecordBytes: 48;
738
- meshRangeRecordBytes: 96;
739
- triangleRecordBytes: 208;
905
+ meshRangeRecordBytes: 240;
906
+ triangleRecordBytes: 352;
907
+ materialRecordBytes: 192;
740
908
  bvhNodeRecordBytes: 48;
741
909
  bvhLeafReferenceRecordBytes: 16;
742
910
  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,