@plasius/gpu-lighting 0.1.4 → 0.1.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/src/index.js CHANGED
@@ -128,6 +128,658 @@ export const lightingProfileNames = Object.freeze(Object.keys(lightingProfiles))
128
128
 
129
129
  export const defaultLightingProfile = "realtime";
130
130
 
131
+ export const lightingWorkerQueueClass = "lighting";
132
+ export const lightingDebugOwner = "lighting";
133
+
134
+ function buildWorkerBudgetLevels(jobType, queueClass, presets) {
135
+ return Object.freeze([
136
+ Object.freeze({
137
+ id: "low",
138
+ estimatedCostMs: presets.low.estimatedCostMs,
139
+ config: Object.freeze({
140
+ maxDispatchesPerFrame: presets.low.maxDispatchesPerFrame,
141
+ maxJobsPerDispatch: presets.low.maxJobsPerDispatch,
142
+ cadenceDivisor: presets.low.cadenceDivisor,
143
+ workgroupScale: presets.low.workgroupScale,
144
+ maxQueueDepth: presets.low.maxQueueDepth,
145
+ metadata: Object.freeze({
146
+ owner: lightingDebugOwner,
147
+ queueClass,
148
+ jobType,
149
+ quality: "low",
150
+ }),
151
+ }),
152
+ }),
153
+ Object.freeze({
154
+ id: "medium",
155
+ estimatedCostMs: presets.medium.estimatedCostMs,
156
+ config: Object.freeze({
157
+ maxDispatchesPerFrame: presets.medium.maxDispatchesPerFrame,
158
+ maxJobsPerDispatch: presets.medium.maxJobsPerDispatch,
159
+ cadenceDivisor: presets.medium.cadenceDivisor,
160
+ workgroupScale: presets.medium.workgroupScale,
161
+ maxQueueDepth: presets.medium.maxQueueDepth,
162
+ metadata: Object.freeze({
163
+ owner: lightingDebugOwner,
164
+ queueClass,
165
+ jobType,
166
+ quality: "medium",
167
+ }),
168
+ }),
169
+ }),
170
+ Object.freeze({
171
+ id: "high",
172
+ estimatedCostMs: presets.high.estimatedCostMs,
173
+ config: Object.freeze({
174
+ maxDispatchesPerFrame: presets.high.maxDispatchesPerFrame,
175
+ maxJobsPerDispatch: presets.high.maxJobsPerDispatch,
176
+ cadenceDivisor: presets.high.cadenceDivisor,
177
+ workgroupScale: presets.high.workgroupScale,
178
+ maxQueueDepth: presets.high.maxQueueDepth,
179
+ metadata: Object.freeze({
180
+ owner: lightingDebugOwner,
181
+ queueClass,
182
+ jobType,
183
+ quality: "high",
184
+ }),
185
+ }),
186
+ }),
187
+ ]);
188
+ }
189
+
190
+ const lightingWorkerSpecPresets = {
191
+ hybrid: {
192
+ suggestedAllocationIds: [
193
+ "lighting.hybrid.radiance-cache",
194
+ "lighting.hybrid.reflection-history",
195
+ "lighting.hybrid.shadow-atlas",
196
+ ],
197
+ jobs: {
198
+ directLighting: {
199
+ domain: "lighting",
200
+ importance: "high",
201
+ levels: buildWorkerBudgetLevels(
202
+ "lighting.hybrid.directLighting",
203
+ lightingWorkerQueueClass,
204
+ {
205
+ low: {
206
+ estimatedCostMs: 0.5,
207
+ maxDispatchesPerFrame: 1,
208
+ maxJobsPerDispatch: 64,
209
+ cadenceDivisor: 2,
210
+ workgroupScale: 0.5,
211
+ maxQueueDepth: 128,
212
+ },
213
+ medium: {
214
+ estimatedCostMs: 0.9,
215
+ maxDispatchesPerFrame: 1,
216
+ maxJobsPerDispatch: 128,
217
+ cadenceDivisor: 1,
218
+ workgroupScale: 0.75,
219
+ maxQueueDepth: 256,
220
+ },
221
+ high: {
222
+ estimatedCostMs: 1.2,
223
+ maxDispatchesPerFrame: 2,
224
+ maxJobsPerDispatch: 256,
225
+ cadenceDivisor: 1,
226
+ workgroupScale: 1,
227
+ maxQueueDepth: 512,
228
+ },
229
+ }
230
+ ),
231
+ suggestedAllocationIds: ["lighting.hybrid.shadow-atlas"],
232
+ },
233
+ screenTrace: {
234
+ domain: "reflections",
235
+ importance: "high",
236
+ levels: buildWorkerBudgetLevels(
237
+ "lighting.hybrid.screenTrace",
238
+ lightingWorkerQueueClass,
239
+ {
240
+ low: {
241
+ estimatedCostMs: 0.8,
242
+ maxDispatchesPerFrame: 1,
243
+ maxJobsPerDispatch: 32,
244
+ cadenceDivisor: 3,
245
+ workgroupScale: 0.4,
246
+ maxQueueDepth: 96,
247
+ },
248
+ medium: {
249
+ estimatedCostMs: 1.5,
250
+ maxDispatchesPerFrame: 1,
251
+ maxJobsPerDispatch: 96,
252
+ cadenceDivisor: 2,
253
+ workgroupScale: 0.7,
254
+ maxQueueDepth: 192,
255
+ },
256
+ high: {
257
+ estimatedCostMs: 2.2,
258
+ maxDispatchesPerFrame: 2,
259
+ maxJobsPerDispatch: 192,
260
+ cadenceDivisor: 1,
261
+ workgroupScale: 1,
262
+ maxQueueDepth: 384,
263
+ },
264
+ }
265
+ ),
266
+ suggestedAllocationIds: ["lighting.hybrid.reflection-history"],
267
+ },
268
+ radianceCache: {
269
+ domain: "lighting",
270
+ importance: "high",
271
+ levels: buildWorkerBudgetLevels(
272
+ "lighting.hybrid.radianceCache",
273
+ lightingWorkerQueueClass,
274
+ {
275
+ low: {
276
+ estimatedCostMs: 0.7,
277
+ maxDispatchesPerFrame: 1,
278
+ maxJobsPerDispatch: 32,
279
+ cadenceDivisor: 2,
280
+ workgroupScale: 0.5,
281
+ maxQueueDepth: 96,
282
+ },
283
+ medium: {
284
+ estimatedCostMs: 1.2,
285
+ maxDispatchesPerFrame: 1,
286
+ maxJobsPerDispatch: 64,
287
+ cadenceDivisor: 1,
288
+ workgroupScale: 0.75,
289
+ maxQueueDepth: 192,
290
+ },
291
+ high: {
292
+ estimatedCostMs: 1.8,
293
+ maxDispatchesPerFrame: 2,
294
+ maxJobsPerDispatch: 128,
295
+ cadenceDivisor: 1,
296
+ workgroupScale: 1,
297
+ maxQueueDepth: 256,
298
+ },
299
+ }
300
+ ),
301
+ suggestedAllocationIds: ["lighting.hybrid.radiance-cache"],
302
+ },
303
+ finalGather: {
304
+ domain: "lighting",
305
+ importance: "critical",
306
+ levels: buildWorkerBudgetLevels(
307
+ "lighting.hybrid.finalGather",
308
+ lightingWorkerQueueClass,
309
+ {
310
+ low: {
311
+ estimatedCostMs: 1,
312
+ maxDispatchesPerFrame: 1,
313
+ maxJobsPerDispatch: 48,
314
+ cadenceDivisor: 2,
315
+ workgroupScale: 0.5,
316
+ maxQueueDepth: 128,
317
+ },
318
+ medium: {
319
+ estimatedCostMs: 1.8,
320
+ maxDispatchesPerFrame: 1,
321
+ maxJobsPerDispatch: 96,
322
+ cadenceDivisor: 1,
323
+ workgroupScale: 0.75,
324
+ maxQueueDepth: 256,
325
+ },
326
+ high: {
327
+ estimatedCostMs: 2.6,
328
+ maxDispatchesPerFrame: 2,
329
+ maxJobsPerDispatch: 192,
330
+ cadenceDivisor: 1,
331
+ workgroupScale: 1,
332
+ maxQueueDepth: 384,
333
+ },
334
+ }
335
+ ),
336
+ suggestedAllocationIds: [
337
+ "lighting.hybrid.radiance-cache",
338
+ "lighting.hybrid.reflection-history",
339
+ ],
340
+ },
341
+ reflectionResolve: {
342
+ domain: "reflections",
343
+ importance: "high",
344
+ levels: buildWorkerBudgetLevels(
345
+ "lighting.hybrid.reflectionResolve",
346
+ lightingWorkerQueueClass,
347
+ {
348
+ low: {
349
+ estimatedCostMs: 0.4,
350
+ maxDispatchesPerFrame: 1,
351
+ maxJobsPerDispatch: 32,
352
+ cadenceDivisor: 2,
353
+ workgroupScale: 0.5,
354
+ maxQueueDepth: 96,
355
+ },
356
+ medium: {
357
+ estimatedCostMs: 0.8,
358
+ maxDispatchesPerFrame: 1,
359
+ maxJobsPerDispatch: 64,
360
+ cadenceDivisor: 1,
361
+ workgroupScale: 0.75,
362
+ maxQueueDepth: 192,
363
+ },
364
+ high: {
365
+ estimatedCostMs: 1.2,
366
+ maxDispatchesPerFrame: 1,
367
+ maxJobsPerDispatch: 128,
368
+ cadenceDivisor: 1,
369
+ workgroupScale: 1,
370
+ maxQueueDepth: 256,
371
+ },
372
+ }
373
+ ),
374
+ suggestedAllocationIds: ["lighting.hybrid.reflection-history"],
375
+ },
376
+ },
377
+ },
378
+ pathtracer: {
379
+ suggestedAllocationIds: [
380
+ "lighting.pathtracer.path-state",
381
+ "lighting.pathtracer.accumulation",
382
+ "lighting.pathtracer.denoise-history",
383
+ ],
384
+ jobs: {
385
+ pathTrace: {
386
+ domain: "lighting",
387
+ importance: "critical",
388
+ levels: buildWorkerBudgetLevels(
389
+ "lighting.pathtracer.pathTrace",
390
+ lightingWorkerQueueClass,
391
+ {
392
+ low: {
393
+ estimatedCostMs: 1.2,
394
+ maxDispatchesPerFrame: 1,
395
+ maxJobsPerDispatch: 16,
396
+ cadenceDivisor: 3,
397
+ workgroupScale: 0.45,
398
+ maxQueueDepth: 48,
399
+ },
400
+ medium: {
401
+ estimatedCostMs: 2.3,
402
+ maxDispatchesPerFrame: 1,
403
+ maxJobsPerDispatch: 32,
404
+ cadenceDivisor: 2,
405
+ workgroupScale: 0.7,
406
+ maxQueueDepth: 96,
407
+ },
408
+ high: {
409
+ estimatedCostMs: 3.8,
410
+ maxDispatchesPerFrame: 2,
411
+ maxJobsPerDispatch: 64,
412
+ cadenceDivisor: 1,
413
+ workgroupScale: 1,
414
+ maxQueueDepth: 128,
415
+ },
416
+ }
417
+ ),
418
+ suggestedAllocationIds: ["lighting.pathtracer.path-state"],
419
+ },
420
+ accumulate: {
421
+ domain: "lighting",
422
+ importance: "medium",
423
+ levels: buildWorkerBudgetLevels(
424
+ "lighting.pathtracer.accumulate",
425
+ lightingWorkerQueueClass,
426
+ {
427
+ low: {
428
+ estimatedCostMs: 0.4,
429
+ maxDispatchesPerFrame: 1,
430
+ maxJobsPerDispatch: 16,
431
+ cadenceDivisor: 2,
432
+ workgroupScale: 0.5,
433
+ maxQueueDepth: 32,
434
+ },
435
+ medium: {
436
+ estimatedCostMs: 0.8,
437
+ maxDispatchesPerFrame: 1,
438
+ maxJobsPerDispatch: 32,
439
+ cadenceDivisor: 1,
440
+ workgroupScale: 0.75,
441
+ maxQueueDepth: 64,
442
+ },
443
+ high: {
444
+ estimatedCostMs: 1.1,
445
+ maxDispatchesPerFrame: 1,
446
+ maxJobsPerDispatch: 64,
447
+ cadenceDivisor: 1,
448
+ workgroupScale: 1,
449
+ maxQueueDepth: 96,
450
+ },
451
+ }
452
+ ),
453
+ suggestedAllocationIds: ["lighting.pathtracer.accumulation"],
454
+ },
455
+ denoise: {
456
+ domain: "post-processing",
457
+ importance: "high",
458
+ levels: buildWorkerBudgetLevels(
459
+ "lighting.pathtracer.denoise",
460
+ lightingWorkerQueueClass,
461
+ {
462
+ low: {
463
+ estimatedCostMs: 0.4,
464
+ maxDispatchesPerFrame: 1,
465
+ maxJobsPerDispatch: 16,
466
+ cadenceDivisor: 2,
467
+ workgroupScale: 0.5,
468
+ maxQueueDepth: 32,
469
+ },
470
+ medium: {
471
+ estimatedCostMs: 0.9,
472
+ maxDispatchesPerFrame: 1,
473
+ maxJobsPerDispatch: 32,
474
+ cadenceDivisor: 1,
475
+ workgroupScale: 0.75,
476
+ maxQueueDepth: 64,
477
+ },
478
+ high: {
479
+ estimatedCostMs: 1.4,
480
+ maxDispatchesPerFrame: 1,
481
+ maxJobsPerDispatch: 64,
482
+ cadenceDivisor: 1,
483
+ workgroupScale: 1,
484
+ maxQueueDepth: 96,
485
+ },
486
+ }
487
+ ),
488
+ suggestedAllocationIds: ["lighting.pathtracer.denoise-history"],
489
+ },
490
+ },
491
+ },
492
+ volumetrics: {
493
+ suggestedAllocationIds: [
494
+ "lighting.volumetrics.froxel-grid",
495
+ "lighting.volumetrics.shadow-history",
496
+ ],
497
+ jobs: {
498
+ froxelIntegrate: {
499
+ domain: "volumetrics",
500
+ importance: "high",
501
+ levels: buildWorkerBudgetLevels(
502
+ "lighting.volumetrics.froxelIntegrate",
503
+ lightingWorkerQueueClass,
504
+ {
505
+ low: {
506
+ estimatedCostMs: 0.6,
507
+ maxDispatchesPerFrame: 1,
508
+ maxJobsPerDispatch: 32,
509
+ cadenceDivisor: 2,
510
+ workgroupScale: 0.5,
511
+ maxQueueDepth: 96,
512
+ },
513
+ medium: {
514
+ estimatedCostMs: 1.1,
515
+ maxDispatchesPerFrame: 1,
516
+ maxJobsPerDispatch: 64,
517
+ cadenceDivisor: 1,
518
+ workgroupScale: 0.75,
519
+ maxQueueDepth: 192,
520
+ },
521
+ high: {
522
+ estimatedCostMs: 1.7,
523
+ maxDispatchesPerFrame: 2,
524
+ maxJobsPerDispatch: 128,
525
+ cadenceDivisor: 1,
526
+ workgroupScale: 1,
527
+ maxQueueDepth: 256,
528
+ },
529
+ }
530
+ ),
531
+ suggestedAllocationIds: ["lighting.volumetrics.froxel-grid"],
532
+ },
533
+ volumetricShadow: {
534
+ domain: "volumetrics",
535
+ importance: "high",
536
+ levels: buildWorkerBudgetLevels(
537
+ "lighting.volumetrics.volumetricShadow",
538
+ lightingWorkerQueueClass,
539
+ {
540
+ low: {
541
+ estimatedCostMs: 0.5,
542
+ maxDispatchesPerFrame: 1,
543
+ maxJobsPerDispatch: 24,
544
+ cadenceDivisor: 2,
545
+ workgroupScale: 0.5,
546
+ maxQueueDepth: 64,
547
+ },
548
+ medium: {
549
+ estimatedCostMs: 0.9,
550
+ maxDispatchesPerFrame: 1,
551
+ maxJobsPerDispatch: 48,
552
+ cadenceDivisor: 1,
553
+ workgroupScale: 0.75,
554
+ maxQueueDepth: 128,
555
+ },
556
+ high: {
557
+ estimatedCostMs: 1.3,
558
+ maxDispatchesPerFrame: 1,
559
+ maxJobsPerDispatch: 96,
560
+ cadenceDivisor: 1,
561
+ workgroupScale: 1,
562
+ maxQueueDepth: 192,
563
+ },
564
+ }
565
+ ),
566
+ suggestedAllocationIds: ["lighting.volumetrics.shadow-history"],
567
+ },
568
+ },
569
+ },
570
+ hdri: {
571
+ suggestedAllocationIds: [
572
+ "lighting.hdri.cubemap",
573
+ "lighting.hdri.prefilter",
574
+ "lighting.hdri.brdf-lut",
575
+ ],
576
+ jobs: {
577
+ irradianceConvolution: {
578
+ domain: "lighting",
579
+ importance: "medium",
580
+ levels: buildWorkerBudgetLevels(
581
+ "lighting.hdri.irradianceConvolution",
582
+ lightingWorkerQueueClass,
583
+ {
584
+ low: {
585
+ estimatedCostMs: 0.3,
586
+ maxDispatchesPerFrame: 1,
587
+ maxJobsPerDispatch: 8,
588
+ cadenceDivisor: 3,
589
+ workgroupScale: 0.5,
590
+ maxQueueDepth: 16,
591
+ },
592
+ medium: {
593
+ estimatedCostMs: 0.5,
594
+ maxDispatchesPerFrame: 1,
595
+ maxJobsPerDispatch: 16,
596
+ cadenceDivisor: 2,
597
+ workgroupScale: 0.75,
598
+ maxQueueDepth: 32,
599
+ },
600
+ high: {
601
+ estimatedCostMs: 0.8,
602
+ maxDispatchesPerFrame: 1,
603
+ maxJobsPerDispatch: 32,
604
+ cadenceDivisor: 1,
605
+ workgroupScale: 1,
606
+ maxQueueDepth: 48,
607
+ },
608
+ }
609
+ ),
610
+ suggestedAllocationIds: ["lighting.hdri.cubemap"],
611
+ },
612
+ specularPrefilter: {
613
+ domain: "lighting",
614
+ importance: "medium",
615
+ levels: buildWorkerBudgetLevels(
616
+ "lighting.hdri.specularPrefilter",
617
+ lightingWorkerQueueClass,
618
+ {
619
+ low: {
620
+ estimatedCostMs: 0.4,
621
+ maxDispatchesPerFrame: 1,
622
+ maxJobsPerDispatch: 8,
623
+ cadenceDivisor: 3,
624
+ workgroupScale: 0.5,
625
+ maxQueueDepth: 16,
626
+ },
627
+ medium: {
628
+ estimatedCostMs: 0.7,
629
+ maxDispatchesPerFrame: 1,
630
+ maxJobsPerDispatch: 16,
631
+ cadenceDivisor: 2,
632
+ workgroupScale: 0.75,
633
+ maxQueueDepth: 32,
634
+ },
635
+ high: {
636
+ estimatedCostMs: 1,
637
+ maxDispatchesPerFrame: 1,
638
+ maxJobsPerDispatch: 32,
639
+ cadenceDivisor: 1,
640
+ workgroupScale: 1,
641
+ maxQueueDepth: 48,
642
+ },
643
+ }
644
+ ),
645
+ suggestedAllocationIds: ["lighting.hdri.prefilter"],
646
+ },
647
+ brdfLut: {
648
+ domain: "lighting",
649
+ importance: "low",
650
+ levels: buildWorkerBudgetLevels(
651
+ "lighting.hdri.brdfLut",
652
+ lightingWorkerQueueClass,
653
+ {
654
+ low: {
655
+ estimatedCostMs: 0.2,
656
+ maxDispatchesPerFrame: 1,
657
+ maxJobsPerDispatch: 4,
658
+ cadenceDivisor: 3,
659
+ workgroupScale: 0.5,
660
+ maxQueueDepth: 8,
661
+ },
662
+ medium: {
663
+ estimatedCostMs: 0.4,
664
+ maxDispatchesPerFrame: 1,
665
+ maxJobsPerDispatch: 8,
666
+ cadenceDivisor: 2,
667
+ workgroupScale: 0.75,
668
+ maxQueueDepth: 16,
669
+ },
670
+ high: {
671
+ estimatedCostMs: 0.6,
672
+ maxDispatchesPerFrame: 1,
673
+ maxJobsPerDispatch: 16,
674
+ cadenceDivisor: 1,
675
+ workgroupScale: 1,
676
+ maxQueueDepth: 24,
677
+ },
678
+ }
679
+ ),
680
+ suggestedAllocationIds: ["lighting.hdri.brdf-lut"],
681
+ },
682
+ },
683
+ },
684
+ };
685
+
686
+ const lightingWorkerDagSpecs = {
687
+ hybrid: {
688
+ directLighting: { priority: 4, dependencies: [] },
689
+ screenTrace: { priority: 3, dependencies: [] },
690
+ radianceCache: { priority: 4, dependencies: ["directLighting"] },
691
+ finalGather: { priority: 2, dependencies: ["radianceCache", "screenTrace"] },
692
+ reflectionResolve: {
693
+ priority: 1,
694
+ dependencies: ["screenTrace", "finalGather"],
695
+ },
696
+ },
697
+ pathtracer: {
698
+ pathTrace: { priority: 4, dependencies: [] },
699
+ accumulate: { priority: 3, dependencies: ["pathTrace"] },
700
+ denoise: { priority: 2, dependencies: ["accumulate"] },
701
+ },
702
+ volumetrics: {
703
+ volumetricShadow: { priority: 3, dependencies: [] },
704
+ froxelIntegrate: { priority: 2, dependencies: ["volumetricShadow"] },
705
+ },
706
+ hdri: {
707
+ irradianceConvolution: { priority: 3, dependencies: [] },
708
+ specularPrefilter: { priority: 3, dependencies: [] },
709
+ brdfLut: {
710
+ priority: 2,
711
+ dependencies: ["irradianceConvolution", "specularPrefilter"],
712
+ },
713
+ },
714
+ };
715
+
716
+ function buildWorkerManifestJob(techniqueName, job) {
717
+ const spec = lightingWorkerSpecPresets[techniqueName].jobs[job.key];
718
+ const dag = lightingWorkerDagSpecs[techniqueName][job.key];
719
+ const dependencies = dag.dependencies.map(
720
+ (dependency) => `lighting.${techniqueName}.${dependency}`
721
+ );
722
+
723
+ return Object.freeze({
724
+ key: job.key,
725
+ label: job.label,
726
+ worker: Object.freeze({
727
+ jobType: job.label,
728
+ queueClass: lightingWorkerQueueClass,
729
+ priority: dag.priority,
730
+ dependencies: Object.freeze(dependencies),
731
+ schedulerMode: "dag",
732
+ }),
733
+ performance: Object.freeze({
734
+ id: job.label,
735
+ jobType: job.label,
736
+ queueClass: lightingWorkerQueueClass,
737
+ domain: spec.domain,
738
+ authority: "visual",
739
+ importance: spec.importance,
740
+ levels: spec.levels,
741
+ }),
742
+ debug: Object.freeze({
743
+ owner: lightingDebugOwner,
744
+ queueClass: lightingWorkerQueueClass,
745
+ jobType: job.label,
746
+ tags: Object.freeze([
747
+ "lighting",
748
+ techniqueName,
749
+ job.key,
750
+ spec.domain,
751
+ ]),
752
+ suggestedAllocationIds: Object.freeze([...spec.suggestedAllocationIds]),
753
+ }),
754
+ });
755
+ }
756
+
757
+ function buildLightingWorkerManifest(name, technique) {
758
+ const spec = lightingWorkerSpecPresets[name];
759
+
760
+ return Object.freeze({
761
+ schemaVersion: 1,
762
+ owner: lightingDebugOwner,
763
+ technique: name,
764
+ description: technique.description,
765
+ queueClass: lightingWorkerQueueClass,
766
+ schedulerMode: "dag",
767
+ suggestedAllocationIds: Object.freeze([...spec.suggestedAllocationIds]),
768
+ jobs: Object.freeze(
769
+ technique.jobs.map((job) => buildWorkerManifestJob(name, job))
770
+ ),
771
+ });
772
+ }
773
+
774
+ export const lightingWorkerManifests = Object.freeze(
775
+ Object.fromEntries(
776
+ Object.entries(lightingTechniques).map(([name, technique]) => [
777
+ name,
778
+ buildLightingWorkerManifest(name, technique),
779
+ ])
780
+ )
781
+ );
782
+
131
783
  function getTechniqueJob(technique, key) {
132
784
  const job = technique.jobs.find((entry) => entry.key === key);
133
785
  if (!job) {
@@ -158,6 +810,38 @@ export function getLightingProfile(name = defaultLightingProfile) {
158
810
  return profile;
159
811
  }
160
812
 
813
+ export function getLightingTechniqueWorkerManifest(
814
+ name = defaultLightingTechnique
815
+ ) {
816
+ const manifest = lightingWorkerManifests[name];
817
+ if (!manifest) {
818
+ const available = lightingTechniqueNames.join(", ");
819
+ throw new Error(
820
+ `Unknown lighting technique "${name}". Available: ${available}.`
821
+ );
822
+ }
823
+ return manifest;
824
+ }
825
+
826
+ export function getLightingProfileWorkerManifest(
827
+ name = defaultLightingProfile
828
+ ) {
829
+ const profile = getLightingProfile(name);
830
+ const techniques = profile.techniques.map((techniqueName) =>
831
+ getLightingTechniqueWorkerManifest(techniqueName)
832
+ );
833
+
834
+ return Object.freeze({
835
+ schemaVersion: 1,
836
+ owner: lightingDebugOwner,
837
+ profile: profile.name,
838
+ description: profile.description,
839
+ schedulerMode: "dag",
840
+ techniques: Object.freeze(techniques),
841
+ jobs: Object.freeze(techniques.flatMap((technique) => technique.jobs)),
842
+ });
843
+ }
844
+
161
845
  const defaultTechnique = getLightingTechnique(defaultLightingTechnique);
162
846
 
163
847
  export const lightingPreludeWgslUrl = defaultTechnique.preludeUrl;
@@ -268,6 +952,24 @@ export async function loadLightingTechniqueJobs(techniqueName, options = {}) {
268
952
  return { preludeWgsl, jobs };
269
953
  }
270
954
 
955
+ export async function loadLightingTechniqueWorkerBundle(
956
+ techniqueName = defaultLightingTechnique,
957
+ options = {}
958
+ ) {
959
+ const technique = getLightingTechnique(techniqueName);
960
+ const { preludeWgsl, jobs } = await loadLightingTechniqueJobs(
961
+ technique.name,
962
+ options
963
+ );
964
+
965
+ return {
966
+ technique: technique.name,
967
+ preludeWgsl,
968
+ jobs,
969
+ workerManifest: getLightingTechniqueWorkerManifest(technique.name),
970
+ };
971
+ }
972
+
271
973
  export async function loadLightingPreludeWgsl(options = {}) {
272
974
  const { fetcher } = options ?? {};
273
975
  return loadTechniquePrelude(defaultTechnique, fetcher);
@@ -294,3 +996,21 @@ export async function loadLightingProfile(profileName, options = {}) {
294
996
  );
295
997
  return { profile, techniques };
296
998
  }
999
+
1000
+ export async function loadLightingProfileWorkerPlan(
1001
+ profileName = defaultLightingProfile,
1002
+ options = {}
1003
+ ) {
1004
+ const profile = getLightingProfile(profileName);
1005
+ const techniques = await Promise.all(
1006
+ profile.techniques.map((techniqueName) =>
1007
+ loadLightingTechniqueWorkerBundle(techniqueName, options)
1008
+ )
1009
+ );
1010
+
1011
+ return {
1012
+ profile,
1013
+ techniques,
1014
+ workerManifest: getLightingProfileWorkerManifest(profile.name),
1015
+ };
1016
+ }