@luma.gl/core 9.2.6 → 9.3.0-alpha.4

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.
Files changed (86) hide show
  1. package/dist/adapter/canvas-context.d.ts +23 -3
  2. package/dist/adapter/canvas-context.d.ts.map +1 -1
  3. package/dist/adapter/canvas-context.js +77 -15
  4. package/dist/adapter/canvas-context.js.map +1 -1
  5. package/dist/adapter/device.d.ts +5 -5
  6. package/dist/adapter/device.d.ts.map +1 -1
  7. package/dist/adapter/device.js +12 -2
  8. package/dist/adapter/device.js.map +1 -1
  9. package/dist/adapter/luma.js +1 -1
  10. package/dist/adapter/luma.js.map +1 -1
  11. package/dist/adapter/resources/command-encoder.d.ts +5 -5
  12. package/dist/adapter/resources/command-encoder.d.ts.map +1 -1
  13. package/dist/adapter/resources/fence.d.ts +16 -0
  14. package/dist/adapter/resources/fence.d.ts.map +1 -0
  15. package/dist/adapter/resources/fence.js +15 -0
  16. package/dist/adapter/resources/fence.js.map +1 -0
  17. package/dist/adapter/resources/framebuffer.d.ts.map +1 -1
  18. package/dist/adapter/resources/framebuffer.js +15 -12
  19. package/dist/adapter/resources/framebuffer.js.map +1 -1
  20. package/dist/adapter/resources/resource.d.ts +5 -0
  21. package/dist/adapter/resources/resource.d.ts.map +1 -1
  22. package/dist/adapter/resources/resource.js +3 -0
  23. package/dist/adapter/resources/resource.js.map +1 -1
  24. package/dist/adapter/resources/shader.js +27 -25
  25. package/dist/adapter/resources/shader.js.map +1 -1
  26. package/dist/adapter/resources/texture.d.ts +97 -24
  27. package/dist/adapter/resources/texture.d.ts.map +1 -1
  28. package/dist/adapter/resources/texture.js +116 -10
  29. package/dist/adapter/resources/texture.js.map +1 -1
  30. package/dist/adapter-utils/format-compiler-log.d.ts.map +1 -1
  31. package/dist/adapter-utils/format-compiler-log.js +23 -15
  32. package/dist/adapter-utils/format-compiler-log.js.map +1 -1
  33. package/dist/dist.dev.js +695 -279
  34. package/dist/dist.min.js +10 -9
  35. package/dist/index.cjs +481 -161
  36. package/dist/index.cjs.map +4 -4
  37. package/dist/index.d.ts +6 -2
  38. package/dist/index.d.ts.map +1 -1
  39. package/dist/index.js +5 -0
  40. package/dist/index.js.map +1 -1
  41. package/dist/portable/uniform-buffer-layout.d.ts +13 -4
  42. package/dist/portable/uniform-buffer-layout.d.ts.map +1 -1
  43. package/dist/portable/uniform-buffer-layout.js +88 -55
  44. package/dist/portable/uniform-buffer-layout.js.map +1 -1
  45. package/dist/shadertypes/data-types/decode-data-types.d.ts.map +1 -1
  46. package/dist/shadertypes/data-types/decode-data-types.js +2 -1
  47. package/dist/shadertypes/data-types/decode-data-types.js.map +1 -1
  48. package/dist/shadertypes/data-types/shader-types.d.ts +5 -7
  49. package/dist/shadertypes/data-types/shader-types.d.ts.map +1 -1
  50. package/dist/shadertypes/textures/pixel-utils.js +4 -4
  51. package/dist/shadertypes/textures/pixel-utils.js.map +1 -1
  52. package/dist/shadertypes/textures/texture-format-decoder.d.ts +25 -8
  53. package/dist/shadertypes/textures/texture-format-decoder.d.ts.map +1 -1
  54. package/dist/shadertypes/textures/texture-format-decoder.js +60 -34
  55. package/dist/shadertypes/textures/texture-format-decoder.js.map +1 -1
  56. package/dist/shadertypes/textures/texture-formats.d.ts +43 -16
  57. package/dist/shadertypes/textures/texture-formats.d.ts.map +1 -1
  58. package/dist/shadertypes/textures/texture-formats.js.map +1 -1
  59. package/dist/shadertypes/textures/texture-layout.d.ts +5 -0
  60. package/dist/shadertypes/textures/texture-layout.d.ts.map +1 -0
  61. package/dist/shadertypes/textures/texture-layout.js +41 -0
  62. package/dist/shadertypes/textures/texture-layout.js.map +1 -0
  63. package/dist/utils/assert.d.ts +5 -0
  64. package/dist/utils/assert.d.ts.map +1 -0
  65. package/dist/utils/assert.js +17 -0
  66. package/dist/utils/assert.js.map +1 -0
  67. package/package.json +5 -5
  68. package/src/adapter/canvas-context.ts +87 -20
  69. package/src/adapter/device.ts +23 -5
  70. package/src/adapter/resources/command-buffer.ts +1 -1
  71. package/src/adapter/resources/command-encoder.ts +7 -7
  72. package/src/adapter/resources/fence.ts +30 -0
  73. package/src/adapter/resources/framebuffer.ts +15 -12
  74. package/src/adapter/resources/resource.ts +5 -0
  75. package/src/adapter/resources/shader.ts +28 -28
  76. package/src/adapter/resources/texture.ts +176 -28
  77. package/src/adapter-utils/format-compiler-log.ts +23 -15
  78. package/src/index.ts +12 -2
  79. package/src/portable/uniform-buffer-layout.ts +122 -63
  80. package/src/shadertypes/data-types/decode-data-types.ts +2 -1
  81. package/src/shadertypes/data-types/shader-types.ts +14 -7
  82. package/src/shadertypes/textures/pixel-utils.ts +4 -4
  83. package/src/shadertypes/textures/texture-format-decoder.ts +97 -42
  84. package/src/shadertypes/textures/texture-formats.ts +54 -15
  85. package/src/shadertypes/textures/texture-layout.ts +60 -0
  86. package/src/utils/assert.ts +18 -0
package/dist/dist.dev.js CHANGED
@@ -43,6 +43,7 @@ var __exports__ = (() => {
43
43
  DeviceFeatures: () => DeviceFeatures,
44
44
  DeviceLimits: () => DeviceLimits,
45
45
  ExternalTexture: () => ExternalTexture,
46
+ Fence: () => Fence,
46
47
  Framebuffer: () => Framebuffer,
47
48
  PipelineLayout: () => PipelineLayout,
48
49
  QuerySet: () => QuerySet,
@@ -61,20 +62,26 @@ var __exports__ = (() => {
61
62
  VertexArray: () => VertexArray,
62
63
  _getTextureFormatDefinition: () => getTextureFormatDefinition,
63
64
  _getTextureFormatTable: () => getTextureFormatTable,
65
+ assert: () => assert2,
66
+ assertDefined: () => assertDefined,
64
67
  getAttributeInfosFromLayouts: () => getAttributeInfosFromLayouts,
65
68
  getAttributeShaderTypeInfo: () => getAttributeShaderTypeInfo,
66
69
  getDataType: () => getDataType,
67
70
  getDataTypeInfo: () => getDataTypeInfo,
71
+ getExternalImageSize: () => getExternalImageSize,
68
72
  getNormalizedDataType: () => getNormalizedDataType,
69
73
  getScratchArray: () => getScratchArray,
74
+ getTextureImageView: () => getTextureImageView,
70
75
  getTypedArrayConstructor: () => getTypedArrayConstructor,
71
76
  getVariableShaderTypeInfo: () => getVariableShaderTypeInfo,
72
77
  getVertexFormatFromAttribute: () => getVertexFormatFromAttribute,
73
78
  getVertexFormatInfo: () => getVertexFormatInfo,
79
+ isExternalImage: () => isExternalImage,
74
80
  log: () => log,
75
81
  luma: () => luma,
76
82
  makeVertexFormat: () => makeVertexFormat,
77
83
  readPixel: () => readPixel,
84
+ setTextureImageData: () => setTextureImageData,
78
85
  textureFormatDecoder: () => textureFormatDecoder,
79
86
  writePixel: () => writePixel
80
87
  });
@@ -317,7 +324,139 @@ var __exports__ = (() => {
317
324
  }
318
325
 
319
326
  // ../../node_modules/@probe.gl/env/dist/index.js
320
- var VERSION = true ? "4.1.0" : "untranspiled source";
327
+ var VERSION = true ? "4.1.1" : "untranspiled source";
328
+
329
+ // ../../node_modules/@probe.gl/log/dist/utils/assert.js
330
+ function assert(condition, message) {
331
+ if (!condition) {
332
+ throw new Error(message || "Assertion failed");
333
+ }
334
+ }
335
+
336
+ // ../../node_modules/@probe.gl/log/dist/loggers/log-utils.js
337
+ function normalizeLogLevel(logLevel) {
338
+ if (!logLevel) {
339
+ return 0;
340
+ }
341
+ let resolvedLevel;
342
+ switch (typeof logLevel) {
343
+ case "number":
344
+ resolvedLevel = logLevel;
345
+ break;
346
+ case "object":
347
+ resolvedLevel = logLevel.logLevel || logLevel.priority || 0;
348
+ break;
349
+ default:
350
+ return 0;
351
+ }
352
+ assert(Number.isFinite(resolvedLevel) && resolvedLevel >= 0);
353
+ return resolvedLevel;
354
+ }
355
+ function normalizeArguments(opts) {
356
+ const { logLevel, message } = opts;
357
+ opts.logLevel = normalizeLogLevel(logLevel);
358
+ const args = opts.args ? Array.from(opts.args) : [];
359
+ while (args.length && args.shift() !== message) {
360
+ }
361
+ switch (typeof logLevel) {
362
+ case "string":
363
+ case "function":
364
+ if (message !== void 0) {
365
+ args.unshift(message);
366
+ }
367
+ opts.message = logLevel;
368
+ break;
369
+ case "object":
370
+ Object.assign(opts, logLevel);
371
+ break;
372
+ default:
373
+ }
374
+ if (typeof opts.message === "function") {
375
+ opts.message = opts.message();
376
+ }
377
+ const messageType = typeof opts.message;
378
+ assert(messageType === "string" || messageType === "object");
379
+ return Object.assign(opts, { args }, opts.opts);
380
+ }
381
+
382
+ // ../../node_modules/@probe.gl/log/dist/loggers/base-log.js
383
+ var noop = () => {
384
+ };
385
+ var BaseLog = class {
386
+ constructor({ level = 0 } = {}) {
387
+ this.userData = {};
388
+ this._onceCache = /* @__PURE__ */ new Set();
389
+ this._level = level;
390
+ }
391
+ set level(newLevel) {
392
+ this.setLevel(newLevel);
393
+ }
394
+ get level() {
395
+ return this.getLevel();
396
+ }
397
+ setLevel(level) {
398
+ this._level = level;
399
+ return this;
400
+ }
401
+ getLevel() {
402
+ return this._level;
403
+ }
404
+ // Unconditional logging
405
+ warn(message, ...args) {
406
+ return this._log("warn", 0, message, args, { once: true });
407
+ }
408
+ error(message, ...args) {
409
+ return this._log("error", 0, message, args);
410
+ }
411
+ // Conditional logging
412
+ log(logLevel, message, ...args) {
413
+ return this._log("log", logLevel, message, args);
414
+ }
415
+ info(logLevel, message, ...args) {
416
+ return this._log("info", logLevel, message, args);
417
+ }
418
+ once(logLevel, message, ...args) {
419
+ return this._log("once", logLevel, message, args, { once: true });
420
+ }
421
+ _log(type, logLevel, message, args, options = {}) {
422
+ const normalized = normalizeArguments({
423
+ logLevel,
424
+ message,
425
+ args: this._buildArgs(logLevel, message, args),
426
+ opts: options
427
+ });
428
+ return this._createLogFunction(type, normalized, options);
429
+ }
430
+ _buildArgs(logLevel, message, args) {
431
+ return [logLevel, message, ...args];
432
+ }
433
+ _createLogFunction(type, normalized, options) {
434
+ if (!this._shouldLog(normalized.logLevel)) {
435
+ return noop;
436
+ }
437
+ const tag = this._getOnceTag(options.tag ?? normalized.tag ?? normalized.message);
438
+ if ((options.once || normalized.once) && tag !== void 0) {
439
+ if (this._onceCache.has(tag)) {
440
+ return noop;
441
+ }
442
+ this._onceCache.add(tag);
443
+ }
444
+ return this._emit(type, normalized);
445
+ }
446
+ _shouldLog(logLevel) {
447
+ return this.getLevel() >= normalizeLogLevel(logLevel);
448
+ }
449
+ _getOnceTag(tag) {
450
+ if (tag === void 0) {
451
+ return void 0;
452
+ }
453
+ try {
454
+ return typeof tag === "string" ? tag : String(tag);
455
+ } catch {
456
+ return void 0;
457
+ }
458
+ }
459
+ };
321
460
 
322
461
  // ../../node_modules/@probe.gl/log/dist/utils/local-storage.js
323
462
  function getStorage(type) {
@@ -436,13 +575,6 @@ var __exports__ = (() => {
436
575
  }
437
576
  }
438
577
 
439
- // ../../node_modules/@probe.gl/log/dist/utils/assert.js
440
- function assert(condition, message) {
441
- if (!condition) {
442
- throw new Error(message || "Assertion failed");
443
- }
444
- }
445
-
446
578
  // ../../node_modules/@probe.gl/log/dist/utils/hi-res-timestamp.js
447
579
  function getHiResTimestamp2() {
448
580
  let timestamp;
@@ -457,7 +589,7 @@ var __exports__ = (() => {
457
589
  return timestamp;
458
590
  }
459
591
 
460
- // ../../node_modules/@probe.gl/log/dist/log.js
592
+ // ../../node_modules/@probe.gl/log/dist/loggers/probe-log.js
461
593
  var originalConsole = {
462
594
  debug: isBrowser() ? console.debug || console.log : console.log,
463
595
  log: console.log,
@@ -469,12 +601,9 @@ var __exports__ = (() => {
469
601
  enabled: true,
470
602
  level: 0
471
603
  };
472
- function noop() {
473
- }
474
- var cache = {};
475
- var ONCE = { once: true };
476
- var Log = class {
604
+ var ProbeLog = class extends BaseLog {
477
605
  constructor({ id } = { id: "" }) {
606
+ super({ level: 0 });
478
607
  this.VERSION = VERSION;
479
608
  this._startTs = getHiResTimestamp2();
480
609
  this._deltaTs = getHiResTimestamp2();
@@ -482,22 +611,16 @@ var __exports__ = (() => {
482
611
  this.LOG_THROTTLE_TIMEOUT = 0;
483
612
  this.id = id;
484
613
  this.userData = {};
485
- this._storage = new LocalStorage(`__probe-${this.id}__`, DEFAULT_LOG_CONFIGURATION);
614
+ this._storage = new LocalStorage(`__probe-${this.id}__`, { [this.id]: DEFAULT_LOG_CONFIGURATION });
486
615
  this.timeStamp(`${this.id} started`);
487
616
  autobind(this);
488
617
  Object.seal(this);
489
618
  }
490
- set level(newLevel) {
491
- this.setLevel(newLevel);
492
- }
493
- get level() {
494
- return this.getLevel();
495
- }
496
619
  isEnabled() {
497
- return this._storage.config.enabled;
620
+ return this._getConfiguration().enabled;
498
621
  }
499
622
  getLevel() {
500
- return this._storage.config.level;
623
+ return this._getConfiguration().level;
501
624
  }
502
625
  /** @return milliseconds, with fractions */
503
626
  getTotal() {
@@ -521,20 +644,20 @@ var __exports__ = (() => {
521
644
  }
522
645
  // Configure
523
646
  enable(enabled = true) {
524
- this._storage.setConfiguration({ enabled });
647
+ this._updateConfiguration({ enabled });
525
648
  return this;
526
649
  }
527
650
  setLevel(level) {
528
- this._storage.setConfiguration({ level });
651
+ this._updateConfiguration({ level });
529
652
  return this;
530
653
  }
531
654
  /** return the current status of the setting */
532
655
  get(setting) {
533
- return this._storage.config[setting];
656
+ return this._getConfiguration()[setting];
534
657
  }
535
658
  // update the status of the setting
536
659
  set(setting, value) {
537
- this._storage.setConfiguration({ [setting]: value });
660
+ this._updateConfiguration({ [setting]: value });
538
661
  }
539
662
  /** Logs the current settings as a table */
540
663
  settings() {
@@ -550,11 +673,16 @@ var __exports__ = (() => {
550
673
  throw new Error(message || "Assertion failed");
551
674
  }
552
675
  }
553
- warn(message) {
554
- return this._getLogFunction(0, message, originalConsole.warn, arguments, ONCE);
676
+ warn(message, ...args) {
677
+ return this._log("warn", 0, message, args, {
678
+ method: originalConsole.warn,
679
+ once: true
680
+ });
555
681
  }
556
- error(message) {
557
- return this._getLogFunction(0, message, originalConsole.error, arguments);
682
+ error(message, ...args) {
683
+ return this._log("error", 0, message, args, {
684
+ method: originalConsole.error
685
+ });
558
686
  }
559
687
  /** Print a deprecation warning */
560
688
  deprecated(oldUsage, newUsage) {
@@ -564,50 +692,63 @@ var __exports__ = (() => {
564
692
  removed(oldUsage, newUsage) {
565
693
  return this.error(`\`${oldUsage}\` has been removed. Use \`${newUsage}\` instead`);
566
694
  }
567
- probe(logLevel, message) {
568
- return this._getLogFunction(logLevel, message, originalConsole.log, arguments, {
695
+ probe(logLevel, message, ...args) {
696
+ return this._log("log", logLevel, message, args, {
697
+ method: originalConsole.log,
569
698
  time: true,
570
699
  once: true
571
700
  });
572
701
  }
573
- log(logLevel, message) {
574
- return this._getLogFunction(logLevel, message, originalConsole.debug, arguments);
702
+ log(logLevel, message, ...args) {
703
+ return this._log("log", logLevel, message, args, {
704
+ method: originalConsole.debug
705
+ });
575
706
  }
576
- info(logLevel, message) {
577
- return this._getLogFunction(logLevel, message, console.info, arguments);
707
+ info(logLevel, message, ...args) {
708
+ return this._log("info", logLevel, message, args, { method: console.info });
578
709
  }
579
- once(logLevel, message) {
580
- return this._getLogFunction(logLevel, message, originalConsole.debug || originalConsole.info, arguments, ONCE);
710
+ once(logLevel, message, ...args) {
711
+ return this._log("once", logLevel, message, args, {
712
+ method: originalConsole.debug || originalConsole.info,
713
+ once: true
714
+ });
581
715
  }
582
716
  /** Logs an object as a table */
583
717
  table(logLevel, table, columns) {
584
718
  if (table) {
585
- return this._getLogFunction(logLevel, table, console.table || noop, columns && [columns], {
719
+ return this._log("table", logLevel, table, columns && [columns] || [], {
720
+ method: console.table || noop,
586
721
  tag: getTableHeader(table)
587
722
  });
588
723
  }
589
724
  return noop;
590
725
  }
591
726
  time(logLevel, message) {
592
- return this._getLogFunction(logLevel, message, console.time ? console.time : console.info);
727
+ return this._log("time", logLevel, message, [], {
728
+ method: console.time ? console.time : console.info
729
+ });
593
730
  }
594
731
  timeEnd(logLevel, message) {
595
- return this._getLogFunction(logLevel, message, console.timeEnd ? console.timeEnd : console.info);
732
+ return this._log("time", logLevel, message, [], {
733
+ method: console.timeEnd ? console.timeEnd : console.info
734
+ });
596
735
  }
597
736
  timeStamp(logLevel, message) {
598
- return this._getLogFunction(logLevel, message, console.timeStamp || noop);
737
+ return this._log("time", logLevel, message, [], {
738
+ method: console.timeStamp || noop
739
+ });
599
740
  }
600
741
  group(logLevel, message, opts = { collapsed: false }) {
601
- const options = normalizeArguments({ logLevel, message, opts });
602
- const { collapsed } = opts;
603
- options.method = (collapsed ? console.groupCollapsed : console.group) || console.info;
604
- return this._getLogFunction(options);
742
+ const method = (opts.collapsed ? console.groupCollapsed : console.group) || console.info;
743
+ return this._log("group", logLevel, message, [], { method });
605
744
  }
606
745
  groupCollapsed(logLevel, message, opts = {}) {
607
746
  return this.group(logLevel, message, Object.assign({}, opts, { collapsed: true }));
608
747
  }
609
748
  groupEnd(logLevel) {
610
- return this._getLogFunction(logLevel, "", console.groupEnd || noop);
749
+ return this._log("groupEnd", logLevel, "", [], {
750
+ method: console.groupEnd || noop
751
+ });
611
752
  }
612
753
  // EXPERIMENTAL
613
754
  withGroup(logLevel, message, func) {
@@ -623,78 +764,34 @@ var __exports__ = (() => {
623
764
  console.trace();
624
765
  }
625
766
  }
626
- // PRIVATE METHODS
627
- /** Deduces log level from a variety of arguments */
628
767
  _shouldLog(logLevel) {
629
- return this.isEnabled() && this.getLevel() >= normalizeLogLevel(logLevel);
630
- }
631
- _getLogFunction(logLevel, message, method, args, opts) {
632
- if (this._shouldLog(logLevel)) {
633
- opts = normalizeArguments({ logLevel, message, args, opts });
634
- method = method || opts.method;
635
- assert(method);
636
- opts.total = this.getTotal();
637
- opts.delta = this.getDelta();
638
- this._deltaTs = getHiResTimestamp2();
639
- const tag = opts.tag || opts.message;
640
- if (opts.once && tag) {
641
- if (!cache[tag]) {
642
- cache[tag] = getHiResTimestamp2();
643
- } else {
644
- return noop;
645
- }
646
- }
647
- message = decorateMessage(this.id, opts.message, opts);
648
- return method.bind(console, message, ...opts.args);
649
- }
650
- return noop;
651
- }
652
- };
653
- Log.VERSION = VERSION;
654
- function normalizeLogLevel(logLevel) {
655
- if (!logLevel) {
656
- return 0;
657
- }
658
- let resolvedLevel;
659
- switch (typeof logLevel) {
660
- case "number":
661
- resolvedLevel = logLevel;
662
- break;
663
- case "object":
664
- resolvedLevel = logLevel.logLevel || logLevel.priority || 0;
665
- break;
666
- default:
667
- return 0;
768
+ return this.isEnabled() && super._shouldLog(logLevel);
668
769
  }
669
- assert(Number.isFinite(resolvedLevel) && resolvedLevel >= 0);
670
- return resolvedLevel;
671
- }
672
- function normalizeArguments(opts) {
673
- const { logLevel, message } = opts;
674
- opts.logLevel = normalizeLogLevel(logLevel);
675
- const args = opts.args ? Array.from(opts.args) : [];
676
- while (args.length && args.shift() !== message) {
770
+ _emit(_type, normalized) {
771
+ const method = normalized.method;
772
+ assert(method);
773
+ normalized.total = this.getTotal();
774
+ normalized.delta = this.getDelta();
775
+ this._deltaTs = getHiResTimestamp2();
776
+ const message = decorateMessage(this.id, normalized.message, normalized);
777
+ return method.bind(console, message, ...normalized.args);
677
778
  }
678
- switch (typeof logLevel) {
679
- case "string":
680
- case "function":
681
- if (message !== void 0) {
682
- args.unshift(message);
683
- }
684
- opts.message = logLevel;
685
- break;
686
- case "object":
687
- Object.assign(opts, logLevel);
688
- break;
689
- default:
779
+ _getConfiguration() {
780
+ if (!this._storage.config[this.id]) {
781
+ this._updateConfiguration(DEFAULT_LOG_CONFIGURATION);
782
+ }
783
+ return this._storage.config[this.id];
690
784
  }
691
- if (typeof opts.message === "function") {
692
- opts.message = opts.message();
785
+ _updateConfiguration(configuration) {
786
+ const currentConfiguration = this._storage.config[this.id] || {
787
+ ...DEFAULT_LOG_CONFIGURATION
788
+ };
789
+ this._storage.setConfiguration({
790
+ [this.id]: { ...currentConfiguration, ...configuration }
791
+ });
693
792
  }
694
- const messageType = typeof opts.message;
695
- assert(messageType === "string" || messageType === "object");
696
- return Object.assign(opts, { args }, opts.opts);
697
- }
793
+ };
794
+ ProbeLog.VERSION = VERSION;
698
795
  function decorateMessage(id, message, opts) {
699
796
  if (typeof message === "string") {
700
797
  const time = opts.time ? leftPad(formatTime(opts.total)) : "";
@@ -716,10 +813,10 @@ var __exports__ = (() => {
716
813
  globalThis.probe = {};
717
814
 
718
815
  // ../../node_modules/@probe.gl/log/dist/index.js
719
- var dist_default = new Log({ id: "@probe.gl/log" });
816
+ var dist_default = new ProbeLog({ id: "@probe.gl/log" });
720
817
 
721
818
  // src/utils/log.ts
722
- var log = new Log({ id: "luma.gl" });
819
+ var log = new ProbeLog({ id: "luma.gl" });
723
820
 
724
821
  // src/utils/uid.ts
725
822
  var uidCounters = {};
@@ -736,8 +833,11 @@ var __exports__ = (() => {
736
833
  }
737
834
  /** props.id, for debugging. */
738
835
  id;
836
+ /** The props that this resource was created with */
739
837
  props;
838
+ /** User data object, reserved for the application */
740
839
  userData = {};
840
+ /** The device that this resource is associated with - TODO can we remove this dup? */
741
841
  _device;
742
842
  /** Whether this resource has been destroyed */
743
843
  destroyed = false;
@@ -944,10 +1044,11 @@ var __exports__ = (() => {
944
1044
 
945
1045
  // src/shadertypes/data-types/decode-data-types.ts
946
1046
  function getDataTypeInfo(type) {
947
- const [signedType, primitiveType, byteLength] = NORMALIZED_TYPE_MAP[type];
948
1047
  const normalized = type.includes("norm");
949
1048
  const integer = !normalized && !type.startsWith("float");
950
1049
  const signed = type.startsWith("s");
1050
+ const typeInfo = NORMALIZED_TYPE_MAP[type];
1051
+ const [signedType, primitiveType, byteLength] = typeInfo || ["uint8 ", "i32", 1];
951
1052
  return {
952
1053
  signedType,
953
1054
  primitiveType,
@@ -1263,6 +1364,9 @@ var __exports__ = (() => {
1263
1364
  };
1264
1365
 
1265
1366
  // src/shadertypes/textures/texture-format-decoder.ts
1367
+ var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
1368
+ var COLOR_FORMAT_PREFIXES = ["rgb", "rgba", "bgra"];
1369
+ var DEPTH_FORMAT_PREFIXES = ["depth", "stencil"];
1266
1370
  var COMPRESSED_TEXTURE_FORMAT_PREFIXES = [
1267
1371
  "bc1",
1268
1372
  "bc2",
@@ -1278,49 +1382,73 @@ var __exports__ = (() => {
1278
1382
  "astc",
1279
1383
  "pvrtc"
1280
1384
  ];
1281
- var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
1282
1385
  var TextureFormatDecoder = class {
1283
- /** Returns information about a texture format, e.g. attatchment type, components, byte length and flags (integer, signed, normalized) */
1284
- getInfo(format) {
1285
- return getTextureFormatInfo(format);
1286
- }
1287
1386
  /** Checks if a texture format is color */
1288
1387
  isColor(format) {
1289
- return format.startsWith("rgba") || format.startsWith("bgra") || format.startsWith("rgb");
1388
+ return COLOR_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1290
1389
  }
1291
1390
  /** Checks if a texture format is depth or stencil */
1292
1391
  isDepthStencil(format) {
1293
- return format.startsWith("depth") || format.startsWith("stencil");
1392
+ return DEPTH_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1294
1393
  }
1295
1394
  /** Checks if a texture format is compressed */
1296
1395
  isCompressed(format) {
1297
1396
  return COMPRESSED_TEXTURE_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1298
1397
  }
1299
- /**
1300
- * Returns the "static" capabilities of a texture format.
1301
- * @note Needs to be checked against current device
1302
- */
1398
+ /** Returns information about a texture format, e.g. attachment type, components, byte length and flags (integer, signed, normalized) */
1399
+ getInfo(format) {
1400
+ return getTextureFormatInfo(format);
1401
+ }
1402
+ /** "static" capabilities of a texture format. @note Needs to be adjusted against current device */
1303
1403
  getCapabilities(format) {
1304
- const info = getTextureFormatDefinition(format);
1305
- const formatCapabilities = {
1306
- format,
1307
- create: info.f ?? true,
1308
- render: info.render ?? true,
1309
- filter: info.filter ?? true,
1310
- blend: info.blend ?? true,
1311
- store: info.store ?? true
1312
- };
1313
- const formatInfo = getTextureFormatInfo(format);
1314
- const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
1315
- const isSigned = formatInfo?.signed;
1316
- const isInteger = formatInfo?.integer;
1317
- const isWebGLSpecific = formatInfo?.webgl;
1318
- formatCapabilities.render &&= !isSigned;
1319
- formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
1320
- return formatCapabilities;
1404
+ return getTextureFormatCapabilities(format);
1405
+ }
1406
+ /** Computes the memory layout for a texture, in particular including row byte alignment */
1407
+ computeMemoryLayout(opts) {
1408
+ return computeTextureMemoryLayout(opts);
1321
1409
  }
1322
1410
  };
1323
1411
  var textureFormatDecoder = new TextureFormatDecoder();
1412
+ function computeTextureMemoryLayout({
1413
+ format,
1414
+ width,
1415
+ height,
1416
+ depth,
1417
+ byteAlignment
1418
+ }) {
1419
+ const { bytesPerPixel } = textureFormatDecoder.getInfo(format);
1420
+ const unpaddedBytesPerRow = width * bytesPerPixel;
1421
+ const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment;
1422
+ const rowsPerImage = height;
1423
+ const byteLength = bytesPerRow * rowsPerImage * depth;
1424
+ return {
1425
+ bytesPerPixel,
1426
+ bytesPerRow,
1427
+ rowsPerImage,
1428
+ depthOrArrayLayers: depth,
1429
+ bytesPerImage: bytesPerRow * rowsPerImage,
1430
+ byteLength
1431
+ };
1432
+ }
1433
+ function getTextureFormatCapabilities(format) {
1434
+ const info = getTextureFormatDefinition(format);
1435
+ const formatCapabilities = {
1436
+ format,
1437
+ create: info.f ?? true,
1438
+ render: info.render ?? true,
1439
+ filter: info.filter ?? true,
1440
+ blend: info.blend ?? true,
1441
+ store: info.store ?? true
1442
+ };
1443
+ const formatInfo = getTextureFormatInfo(format);
1444
+ const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
1445
+ const isSigned = formatInfo?.signed;
1446
+ const isInteger = formatInfo?.integer;
1447
+ const isWebGLSpecific = formatInfo?.webgl;
1448
+ formatCapabilities.render &&= !isSigned;
1449
+ formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
1450
+ return formatCapabilities;
1451
+ }
1324
1452
  function getTextureFormatInfo(format) {
1325
1453
  let formatInfo = getTextureFormatInfoUsingTable(format);
1326
1454
  if (textureFormatDecoder.isCompressed(format)) {
@@ -1341,7 +1469,7 @@ var __exports__ = (() => {
1341
1469
  const dataType = `${type}${length}`;
1342
1470
  const decodedType = getDataTypeInfo(dataType);
1343
1471
  const bits = decodedType.byteLength * 8;
1344
- const components = channels.length;
1472
+ const components = channels?.length ?? 1;
1345
1473
  const bitsPerChannel = [
1346
1474
  bits,
1347
1475
  components >= 2 ? bits : 0,
@@ -1358,7 +1486,7 @@ var __exports__ = (() => {
1358
1486
  signed: decodedType.signed,
1359
1487
  normalized: decodedType.normalized,
1360
1488
  bitsPerChannel,
1361
- bytesPerPixel: decodedType.byteLength * channels.length,
1489
+ bytesPerPixel: decodedType.byteLength * components,
1362
1490
  packed: formatInfo.packed,
1363
1491
  srgb: formatInfo.srgb
1364
1492
  };
@@ -1474,7 +1602,7 @@ var __exports__ = (() => {
1474
1602
  /** True if this device has been reused during device creation (app has multiple references) */
1475
1603
  _reused = false;
1476
1604
  /** Used by other luma.gl modules to store data on the device */
1477
- _lumaData = {};
1605
+ _moduleData = {};
1478
1606
  _textureCaps = {};
1479
1607
  constructor(props) {
1480
1608
  this.props = { ..._Device.defaultProps, ...props };
@@ -1571,7 +1699,13 @@ var __exports__ = (() => {
1571
1699
  reportError(error, context, ...args) {
1572
1700
  const isHandled = this.props.onError(error, context);
1573
1701
  if (!isHandled) {
1574
- return log.error(error.message, context, ...args);
1702
+ return log.error(
1703
+ this.type === "webgl" ? "%cWebGL" : "%cWebGPU",
1704
+ "color: white; background: red; padding: 2px 6px; border-radius: 3px;",
1705
+ error.message,
1706
+ context,
1707
+ ...args
1708
+ );
1575
1709
  }
1576
1710
  return () => {
1577
1711
  };
@@ -1593,6 +1727,10 @@ or create a device with the 'debug: true' prop.`;
1593
1727
  }
1594
1728
  return this.canvasContext;
1595
1729
  }
1730
+ /** Create a fence sync object */
1731
+ createFence() {
1732
+ throw new Error("createFence() not implemented");
1733
+ }
1596
1734
  /** Create a RenderPass using the default CommandEncoder */
1597
1735
  beginRenderPass(props) {
1598
1736
  return this.commandEncoder.beginRenderPass(props);
@@ -1636,6 +1774,12 @@ or create a device with the 'debug: true' prop.`;
1636
1774
  resetWebGL() {
1637
1775
  throw new Error("not implemented");
1638
1776
  }
1777
+ // INTERNAL LUMA.GL METHODS
1778
+ getModuleData(moduleName) {
1779
+ this._moduleData[moduleName] ||= {};
1780
+ return this._moduleData[moduleName];
1781
+ }
1782
+ // INTERNAL HELPERS
1639
1783
  // IMPLEMENTATION
1640
1784
  /** Helper to get the canvas context props */
1641
1785
  static _getCanvasContextProps(props) {
@@ -1912,6 +2056,19 @@ or create a device with the 'debug: true' prop.`;
1912
2056
  return { promise, resolve, reject };
1913
2057
  }
1914
2058
 
2059
+ // src/utils/assert.ts
2060
+ function assert2(condition, message) {
2061
+ if (!condition) {
2062
+ const error = new Error(message ?? "luma.gl assertion failed.");
2063
+ Error.captureStackTrace?.(error, assert2);
2064
+ throw error;
2065
+ }
2066
+ }
2067
+ function assertDefined(value, message) {
2068
+ assert2(value, message);
2069
+ return value;
2070
+ }
2071
+
1915
2072
  // src/adapter/canvas-context.ts
1916
2073
  var _CanvasContext = class {
1917
2074
  static isHTMLCanvas(canvas) {
@@ -1947,11 +2104,19 @@ or create a device with the 'debug: true' prop.`;
1947
2104
  drawingBufferWidth;
1948
2105
  /** Height of drawing buffer: automatically tracks this.pixelHeight if props.autoResize is true */
1949
2106
  drawingBufferHeight;
2107
+ /** Resolves when the canvas is initialized, i.e. when the ResizeObserver has updated the pixel size */
1950
2108
  _initializedResolvers = withResolvers();
2109
+ /** ResizeObserver to track canvas size changes */
1951
2110
  _resizeObserver;
2111
+ /** IntersectionObserver to track canvas visibility changes */
1952
2112
  _intersectionObserver;
1953
- _position;
2113
+ _observeDevicePixelRatioTimeout = null;
2114
+ /** Position of the canvas in the document, updated by a timer */
2115
+ _position = [0, 0];
2116
+ /** Whether this canvas context has been destroyed */
1954
2117
  destroyed = false;
2118
+ /** Whether the drawing buffer size needs to be resized (deferred resizing to avoid flicker) */
2119
+ _needsDrawingBufferResize = true;
1955
2120
  toString() {
1956
2121
  return `${this[Symbol.toStringTag]}(${this.id})`;
1957
2122
  }
@@ -1999,14 +2164,23 @@ or create a device with the 'debug: true' prop.`;
1999
2164
  } catch {
2000
2165
  this._resizeObserver.observe(this.canvas, { box: "content-box" });
2001
2166
  }
2002
- setTimeout(() => this._observeDevicePixelRatio(), 0);
2167
+ this._observeDevicePixelRatioTimeout = setTimeout(() => this._observeDevicePixelRatio(), 0);
2003
2168
  if (this.props.trackPosition) {
2004
2169
  this._trackPosition();
2005
2170
  }
2006
2171
  }
2007
2172
  }
2008
2173
  destroy() {
2009
- this.destroyed = true;
2174
+ if (!this.destroyed) {
2175
+ this.destroyed = true;
2176
+ if (this._observeDevicePixelRatioTimeout) {
2177
+ clearTimeout(this._observeDevicePixelRatioTimeout);
2178
+ this._observeDevicePixelRatioTimeout = null;
2179
+ }
2180
+ this.device = null;
2181
+ this._resizeObserver?.disconnect();
2182
+ this._intersectionObserver?.disconnect();
2183
+ }
2010
2184
  }
2011
2185
  setProps(props) {
2012
2186
  if ("useDevicePixels" in props) {
@@ -2015,6 +2189,11 @@ or create a device with the 'debug: true' prop.`;
2015
2189
  }
2016
2190
  return this;
2017
2191
  }
2192
+ /** Returns a framebuffer with properly resized current 'swap chain' textures */
2193
+ getCurrentFramebuffer(options) {
2194
+ this._resizeDrawingBufferIfNeeded();
2195
+ return this._getCurrentFramebuffer(options);
2196
+ }
2018
2197
  // SIZE METHODS
2019
2198
  /**
2020
2199
  * Returns the size covered by the canvas in CSS pixels
@@ -2044,12 +2223,21 @@ or create a device with the 'debug: true' prop.`;
2044
2223
  const maxTextureDimension = this.device.limits.maxTextureDimension2D;
2045
2224
  return [maxTextureDimension, maxTextureDimension];
2046
2225
  }
2047
- /** Update the canvas drawing buffer size. Called automatically if props.autoResize is true. */
2226
+ /**
2227
+ * Update the canvas drawing buffer size.
2228
+ * @note - Called automatically if props.autoResize is true.
2229
+ * @note - Defers update of drawing buffer size until framebuffer is requested to avoid flicker
2230
+ * (resizing clears the drawing buffer)!
2231
+ */
2048
2232
  setDrawingBufferSize(width, height) {
2049
- this.canvas.width = width;
2050
- this.canvas.height = height;
2233
+ width = Math.floor(width);
2234
+ height = Math.floor(height);
2235
+ if (this.drawingBufferWidth === width && this.drawingBufferHeight === height) {
2236
+ return;
2237
+ }
2051
2238
  this.drawingBufferWidth = width;
2052
2239
  this.drawingBufferHeight = height;
2240
+ this._needsDrawingBufferResize = true;
2053
2241
  }
2054
2242
  /**
2055
2243
  * Returns the current DPR (number of physical pixels per CSS pixel), if props.useDevicePixels is true
@@ -2104,6 +2292,9 @@ or create a device with the 'debug: true' prop.`;
2104
2292
  }
2105
2293
  /** reacts to an observed intersection */
2106
2294
  _handleIntersection(entries) {
2295
+ if (this.destroyed) {
2296
+ return;
2297
+ }
2107
2298
  const entry = entries.find((entry_) => entry_.target === this.canvas);
2108
2299
  if (!entry) {
2109
2300
  return;
@@ -2120,21 +2311,26 @@ or create a device with the 'debug: true' prop.`;
2120
2311
  * @see https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
2121
2312
  */
2122
2313
  _handleResize(entries) {
2314
+ if (this.destroyed) {
2315
+ return;
2316
+ }
2123
2317
  const entry = entries.find((entry_) => entry_.target === this.canvas);
2124
2318
  if (!entry) {
2125
2319
  return;
2126
2320
  }
2127
- this.cssWidth = entry.contentBoxSize[0].inlineSize;
2128
- this.cssHeight = entry.contentBoxSize[0].blockSize;
2321
+ const contentBoxSize = assertDefined(entry.contentBoxSize?.[0]);
2322
+ this.cssWidth = contentBoxSize.inlineSize;
2323
+ this.cssHeight = contentBoxSize.blockSize;
2129
2324
  const oldPixelSize = this.getDevicePixelSize();
2130
- const devicePixelWidth = entry.devicePixelContentBoxSize?.[0].inlineSize || entry.contentBoxSize[0].inlineSize * devicePixelRatio;
2131
- const devicePixelHeight = entry.devicePixelContentBoxSize?.[0].blockSize || entry.contentBoxSize[0].blockSize * devicePixelRatio;
2325
+ const devicePixelWidth = entry.devicePixelContentBoxSize?.[0]?.inlineSize || contentBoxSize.inlineSize * devicePixelRatio;
2326
+ const devicePixelHeight = entry.devicePixelContentBoxSize?.[0]?.blockSize || contentBoxSize.blockSize * devicePixelRatio;
2132
2327
  const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
2133
2328
  this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
2134
2329
  this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
2135
2330
  this._updateDrawingBufferSize();
2136
2331
  this.device.props.onResize(this, { oldPixelSize });
2137
2332
  }
2333
+ /** Initiate a deferred update for the canvas drawing buffer size */
2138
2334
  _updateDrawingBufferSize() {
2139
2335
  if (this.props.autoResize) {
2140
2336
  if (typeof this.props.useDevicePixels === "number") {
@@ -2145,18 +2341,32 @@ or create a device with the 'debug: true' prop.`;
2145
2341
  } else {
2146
2342
  this.setDrawingBufferSize(this.cssWidth, this.cssHeight);
2147
2343
  }
2148
- this._updateDevice();
2149
2344
  }
2150
2345
  this._initializedResolvers.resolve();
2151
2346
  this.isInitialized = true;
2152
2347
  this.updatePosition();
2153
2348
  }
2349
+ /** Perform a deferred resize of the drawing buffer if needed */
2350
+ _resizeDrawingBufferIfNeeded() {
2351
+ if (this._needsDrawingBufferResize) {
2352
+ this._needsDrawingBufferResize = false;
2353
+ const sizeChanged = this.drawingBufferWidth !== this.canvas.width || this.drawingBufferHeight !== this.canvas.height;
2354
+ if (sizeChanged) {
2355
+ this.canvas.width = this.drawingBufferWidth;
2356
+ this.canvas.height = this.drawingBufferHeight;
2357
+ this._configureDevice();
2358
+ }
2359
+ }
2360
+ }
2154
2361
  /** Monitor DPR changes */
2155
2362
  _observeDevicePixelRatio() {
2363
+ if (this.destroyed) {
2364
+ return;
2365
+ }
2156
2366
  const oldRatio = this.devicePixelRatio;
2157
2367
  this.devicePixelRatio = window.devicePixelRatio;
2158
2368
  this.updatePosition();
2159
- this.device.props.onDevicePixelRatioChange(this, { oldRatio });
2369
+ this.device.props.onDevicePixelRatioChange?.(this, { oldRatio });
2160
2370
  matchMedia(`(resolution: ${this.devicePixelRatio}dppx)`).addEventListener(
2161
2371
  "change",
2162
2372
  () => this._observeDevicePixelRatio(),
@@ -2179,6 +2389,9 @@ or create a device with the 'debug: true' prop.`;
2179
2389
  * if called before browser has finished a reflow. Should not be the case here.
2180
2390
  */
2181
2391
  updatePosition() {
2392
+ if (this.destroyed) {
2393
+ return;
2394
+ }
2182
2395
  const newRect = this.htmlCanvas?.getBoundingClientRect();
2183
2396
  if (newRect) {
2184
2397
  const position = [newRect.left, newRect.top];
@@ -2326,7 +2539,13 @@ or create a device with the 'debug: true' prop.`;
2326
2539
  depth;
2327
2540
  /** mip levels in this texture */
2328
2541
  mipLevels;
2329
- /** "Time" of last update. Monotonically increasing timestamp. TODO move to AsyncTexture? */
2542
+ /** Rows are multiples of this length, padded with extra bytes if needed */
2543
+ byteAlignment;
2544
+ /** The ready promise is always resolved. It is provided for type compatibility with DynamicTexture. */
2545
+ ready = Promise.resolve(this);
2546
+ /** isReady is always true. It is provided for type compatibility with DynamicTexture. */
2547
+ isReady = true;
2548
+ /** "Time" of last update. Monotonically increasing timestamp. TODO move to DynamicTexture? */
2330
2549
  updateTimestamp;
2331
2550
  get [Symbol.toStringTag]() {
2332
2551
  return "Texture";
@@ -2335,7 +2554,7 @@ or create a device with the 'debug: true' prop.`;
2335
2554
  return `Texture(${this.id},${this.format},${this.width}x${this.height})`;
2336
2555
  }
2337
2556
  /** Do not use directly. Create with device.createTexture() */
2338
- constructor(device, props) {
2557
+ constructor(device, props, backendProps) {
2339
2558
  props = _Texture.normalizeProps(device, props);
2340
2559
  super(device, props, _Texture.defaultProps);
2341
2560
  this.dimension = this.props.dimension;
@@ -2345,6 +2564,9 @@ or create a device with the 'debug: true' prop.`;
2345
2564
  this.height = this.props.height;
2346
2565
  this.depth = this.props.depth;
2347
2566
  this.mipLevels = this.props.mipLevels;
2567
+ if (this.dimension === "cube") {
2568
+ this.depth = 6;
2569
+ }
2348
2570
  if (this.props.width === void 0 || this.props.height === void 0) {
2349
2571
  if (device.isExternalImage(props.data)) {
2350
2572
  const size = device.getExternalImageSize(props.data);
@@ -2355,17 +2577,14 @@ or create a device with the 'debug: true' prop.`;
2355
2577
  this.height = 1;
2356
2578
  if (this.props.width === void 0 || this.props.height === void 0) {
2357
2579
  log.warn(
2358
- `${this} created with undefined width or height. This is deprecated. Use AsyncTexture instead.`
2580
+ `${this} created with undefined width or height. This is deprecated. Use DynamicTexture instead.`
2359
2581
  )();
2360
2582
  }
2361
2583
  }
2362
2584
  }
2585
+ this.byteAlignment = backendProps?.byteAlignment || 1;
2363
2586
  this.updateTimestamp = device.incrementTimestamp();
2364
2587
  }
2365
- /** Set sampler props associated with this texture */
2366
- setSampler(sampler) {
2367
- this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
2368
- }
2369
2588
  /**
2370
2589
  * Create a new texture with the same parameters and optionally a different size
2371
2590
  * @note Textures are immutable and cannot be resized after creation, but we can create a similar texture with the same parameters but a new size.
@@ -2374,6 +2593,79 @@ or create a device with the 'debug: true' prop.`;
2374
2593
  clone(size) {
2375
2594
  return this.device.createTexture({ ...this.props, ...size });
2376
2595
  }
2596
+ /** Set sampler props associated with this texture */
2597
+ setSampler(sampler) {
2598
+ this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
2599
+ }
2600
+ /**
2601
+ * Calculates the memory layout of the texture, required when reading and writing data.
2602
+ * @return the memory layout of the texture, in particular bytesPerRow which includes required padding
2603
+ */
2604
+ computeMemoryLayout(options_ = {}) {
2605
+ const options = this._normalizeTextureReadOptions(options_);
2606
+ const { width = this.width, height = this.height, depthOrArrayLayers = this.depth } = options;
2607
+ const { format, byteAlignment } = this;
2608
+ return textureFormatDecoder.computeMemoryLayout({
2609
+ format,
2610
+ width,
2611
+ height,
2612
+ depth: depthOrArrayLayers,
2613
+ byteAlignment
2614
+ });
2615
+ }
2616
+ /**
2617
+ * Read the contents of a texture into a GPU Buffer.
2618
+ * @returns A Buffer containing the texture data.
2619
+ *
2620
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2621
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2622
+ * @note The application can call Buffer.readAsync()
2623
+ * @note If not supplied a buffer will be created and the application needs to call Buffer.destroy
2624
+ */
2625
+ readBuffer(options, buffer) {
2626
+ throw new Error("readBuffer not implemented");
2627
+ }
2628
+ /**
2629
+ * Reads data from a texture into an ArrayBuffer.
2630
+ * @returns An ArrayBuffer containing the texture data.
2631
+ *
2632
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2633
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2634
+ */
2635
+ readDataAsync(options) {
2636
+ throw new Error("readBuffer not implemented");
2637
+ }
2638
+ /**
2639
+ * Writes an GPU Buffer into a texture.
2640
+ *
2641
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2642
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2643
+ */
2644
+ writeBuffer(buffer, options) {
2645
+ throw new Error("readBuffer not implemented");
2646
+ }
2647
+ /**
2648
+ * Writes an array buffer into a texture.
2649
+ *
2650
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2651
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2652
+ */
2653
+ writeData(data, options) {
2654
+ throw new Error("readBuffer not implemented");
2655
+ }
2656
+ // IMPLEMENTATION SPECIFIC
2657
+ /**
2658
+ * WebGL can read data synchronously.
2659
+ * @note While it is convenient, the performance penalty is very significant
2660
+ */
2661
+ readDataSyncWebGL(options) {
2662
+ throw new Error("readDataSyncWebGL not available");
2663
+ }
2664
+ /** Generate mipmaps (WebGL only) */
2665
+ generateMipmapsWebGL() {
2666
+ throw new Error("generateMipmapsWebGL not available");
2667
+ }
2668
+ // HELPERS
2377
2669
  /** Ensure we have integer coordinates */
2378
2670
  static normalizeProps(device, props) {
2379
2671
  const newProps = { ...props };
@@ -2386,7 +2678,6 @@ or create a device with the 'debug: true' prop.`;
2386
2678
  }
2387
2679
  return newProps;
2388
2680
  }
2389
- // HELPERS
2390
2681
  /** Initialize texture with supplied props */
2391
2682
  // eslint-disable-next-line max-statements
2392
2683
  _initializeData(data) {
@@ -2437,6 +2728,20 @@ or create a device with the 'debug: true' prop.`;
2437
2728
  options.height = Math.min(options.height, this.height - options.y);
2438
2729
  return options;
2439
2730
  }
2731
+ _normalizeTextureReadOptions(options_) {
2732
+ const { width, height } = this;
2733
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2734
+ options.width = Math.min(options.width, this.width - options.x);
2735
+ options.height = Math.min(options.height, this.height - options.y);
2736
+ return options;
2737
+ }
2738
+ _normalizeTextureWriteOptions(options_) {
2739
+ const { width, height } = this;
2740
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2741
+ options.width = Math.min(options.width, this.width - options.x);
2742
+ options.height = Math.min(options.height, this.height - options.y);
2743
+ return options;
2744
+ }
2440
2745
  };
2441
2746
  var Texture = _Texture;
2442
2747
  /** The texture can be bound for use as a sampled texture in a shader */
@@ -2453,13 +2758,12 @@ or create a device with the 'debug: true' prop.`;
2453
2758
  __publicField(Texture, "TEXTURE", 4);
2454
2759
  /** @deprecated Use Texture.RENDER */
2455
2760
  __publicField(Texture, "RENDER_ATTACHMENT", 16);
2456
- /** Default options */
2457
2761
  __publicField(Texture, "defaultProps", {
2458
2762
  ...Resource.defaultProps,
2459
2763
  data: null,
2460
2764
  dimension: "2d",
2461
2765
  format: "rgba8unorm",
2462
- usage: _Texture.TEXTURE | _Texture.RENDER_ATTACHMENT | _Texture.COPY_DST,
2766
+ usage: _Texture.SAMPLE | _Texture.RENDER | _Texture.COPY_DST,
2463
2767
  width: void 0,
2464
2768
  height: void 0,
2465
2769
  depth: 1,
@@ -2496,6 +2800,16 @@ or create a device with the 'debug: true' prop.`;
2496
2800
  premultipliedAlpha: false,
2497
2801
  flipY: false
2498
2802
  });
2803
+ __publicField(Texture, "defaultTextureReadOptions", {
2804
+ x: 0,
2805
+ y: 0,
2806
+ z: 0,
2807
+ width: void 0,
2808
+ height: void 0,
2809
+ depthOrArrayLayers: 1,
2810
+ mipLevel: 0,
2811
+ aspect: "all"
2812
+ });
2499
2813
 
2500
2814
  // src/adapter/resources/texture-view.ts
2501
2815
  var _TextureView = class extends Resource {
@@ -2542,24 +2856,32 @@ or create a device with the 'debug: true' prop.`;
2542
2856
  const log2 = shaderLog.slice().sort((a, b) => a.lineNum - b.lineNum);
2543
2857
  switch (options?.showSourceCode || "no") {
2544
2858
  case "all":
2545
- let currentMessage = 0;
2859
+ let currentMessageIndex = 0;
2546
2860
  for (let lineNum = 1; lineNum <= lines.length; lineNum++) {
2547
- formattedLog += getNumberedLine(lines[lineNum - 1], lineNum, options);
2548
- while (log2.length > currentMessage && log2[currentMessage].lineNum === lineNum) {
2549
- const message = log2[currentMessage++];
2550
- formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2861
+ const line = lines[lineNum - 1];
2862
+ const currentMessage = log2[currentMessageIndex];
2863
+ if (line && currentMessage) {
2864
+ formattedLog += getNumberedLine(line, lineNum, options);
2865
+ }
2866
+ while (log2.length > currentMessageIndex && currentMessage.lineNum === lineNum) {
2867
+ const message = log2[currentMessageIndex++];
2868
+ if (message) {
2869
+ formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2870
+ ...options,
2871
+ inlineSource: false
2872
+ });
2873
+ }
2874
+ }
2875
+ }
2876
+ while (log2.length > currentMessageIndex) {
2877
+ const message = log2[currentMessageIndex++];
2878
+ if (message) {
2879
+ formattedLog += formatCompilerMessage(message, [], 0, {
2551
2880
  ...options,
2552
2881
  inlineSource: false
2553
2882
  });
2554
2883
  }
2555
2884
  }
2556
- while (log2.length > currentMessage) {
2557
- const message = log2[currentMessage++];
2558
- formattedLog += formatCompilerMessage(message, [], 0, {
2559
- ...options,
2560
- inlineSource: false
2561
- });
2562
- }
2563
2885
  return formattedLog;
2564
2886
  case "issues":
2565
2887
  case "no":
@@ -2581,8 +2903,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
2581
2903
 
2582
2904
  `;
2583
2905
  }
2584
- const color = message.type === "error" ? "red" : "#8B4000";
2585
- return options?.html ? `<div class='luma-compiler-log-error' style="color:${color};"><b> ${message.type.toUpperCase()}: ${message.message}</b></div>` : `${message.type.toUpperCase()}: ${message.message}`;
2906
+ const color = message.type === "error" ? "red" : "orange";
2907
+ return options?.html ? `<div class='luma-compiler-log-${message.type}' style="color:${color};"><b> ${message.type.toUpperCase()}: ${message.message}</b></div>` : `${message.type.toUpperCase()}: ${message.message}`;
2586
2908
  }
2587
2909
  function getNumberedLines(lines, lineNum, options) {
2588
2910
  let numberedLines = "";
@@ -2668,29 +2990,34 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
2668
2990
  }
2669
2991
  const shaderName = shaderId;
2670
2992
  const shaderTitle = `${this.stage} shader "${shaderName}"`;
2671
- let htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2993
+ const htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2672
2994
  const translatedSource = this.getTranslatedSource();
2995
+ const container = document.createElement("div");
2996
+ container.innerHTML = `<h1>Compilation error in ${shaderTitle}</h1>
2997
+ <div style="display:flex;position:fixed;top:10px;right:20px;gap:2px;">
2998
+ <button id="copy">Copy source</button><br/>
2999
+ <button id="close">Close</button>
3000
+ </div>
3001
+ <code><pre>${htmlLog}</pre></code>`;
2673
3002
  if (translatedSource) {
2674
- htmlLog += `<br /><br /><h1>Translated Source</h1><br /><br /><code style="user-select:text;"><pre>${translatedSource}</pre></code>`;
2675
- }
2676
- const button = document.createElement("Button");
2677
- button.innerHTML = `
2678
- <h1>Compilation error in ${shaderTitle}</h1><br /><br />
2679
- <code style="user-select:text;"><pre>
2680
- ${htmlLog}
2681
- </pre></code>`;
2682
- button.style.top = "10px";
2683
- button.style.left = "10px";
2684
- button.style.position = "absolute";
2685
- button.style.zIndex = "9999";
2686
- button.style.width = "100%";
2687
- button.style.textAlign = "left";
2688
- document.body.appendChild(button);
2689
- const errors = document.getElementsByClassName("luma-compiler-log-error");
2690
- errors[0]?.scrollIntoView();
2691
- button.onclick = () => {
2692
- const dataURI = `data:text/plain,${encodeURIComponent(this.source)}`;
2693
- navigator.clipboard.writeText(dataURI);
3003
+ container.innerHTML += `<br /><h1>Translated Source</h1><br /><br /><code><pre>${translatedSource}</pre></code>`;
3004
+ }
3005
+ container.style.top = "0";
3006
+ container.style.left = "0";
3007
+ container.style.background = "white";
3008
+ container.style.position = "fixed";
3009
+ container.style.zIndex = "9999";
3010
+ container.style.maxWidth = "100vw";
3011
+ container.style.maxHeight = "100vh";
3012
+ container.style.overflowY = "auto";
3013
+ document.body.appendChild(container);
3014
+ const error = container.querySelector(".luma-compiler-log-error");
3015
+ error?.scrollIntoView();
3016
+ container.querySelector("button#close").onclick = () => {
3017
+ container.remove();
3018
+ };
3019
+ container.querySelector("button#copy").onclick = () => {
3020
+ navigator.clipboard.writeText(this.source);
2694
3021
  };
2695
3022
  }
2696
3023
  };
@@ -2710,7 +3037,7 @@ ${htmlLog}
2710
3037
  function getShaderName(shader, defaultName = "unnamed") {
2711
3038
  const SHADER_NAME_REGEXP = /#define[\s*]SHADER_NAME[\s*]([A-Za-z0-9_-]+)[\s*]/;
2712
3039
  const match = SHADER_NAME_REGEXP.exec(shader);
2713
- return match ? match[1] : defaultName;
3040
+ return match?.[1] ?? defaultName;
2714
3041
  }
2715
3042
 
2716
3043
  // src/adapter/resources/framebuffer.ts
@@ -2736,7 +3063,12 @@ ${htmlLog}
2736
3063
  (colorAttachment) => colorAttachment.texture.clone(size)
2737
3064
  );
2738
3065
  const depthStencilAttachment = this.depthStencilAttachment && this.depthStencilAttachment.texture.clone(size);
2739
- return this.device.createFramebuffer({ ...this.props, colorAttachments, depthStencilAttachment });
3066
+ return this.device.createFramebuffer({
3067
+ ...this.props,
3068
+ ...size,
3069
+ colorAttachments,
3070
+ depthStencilAttachment
3071
+ });
2740
3072
  }
2741
3073
  resize(size) {
2742
3074
  let updateSize = !size;
@@ -2811,17 +3143,15 @@ ${htmlLog}
2811
3143
  * and destroys existing textures if owned
2812
3144
  */
2813
3145
  resizeAttachments(width, height) {
2814
- for (let i = 0; i < this.colorAttachments.length; ++i) {
2815
- if (this.colorAttachments[i]) {
2816
- const resizedTexture = this.colorAttachments[i].texture.clone({
2817
- width,
2818
- height
2819
- });
2820
- this.destroyAttachedResource(this.colorAttachments[i]);
2821
- this.colorAttachments[i] = resizedTexture.view;
2822
- this.attachResource(resizedTexture.view);
2823
- }
2824
- }
3146
+ this.colorAttachments.forEach((colorAttachment, i) => {
3147
+ const resizedTexture = colorAttachment.texture.clone({
3148
+ width,
3149
+ height
3150
+ });
3151
+ this.destroyAttachedResource(colorAttachment);
3152
+ this.colorAttachments[i] = resizedTexture.view;
3153
+ this.attachResource(resizedTexture.view);
3154
+ });
2825
3155
  if (this.depthStencilAttachment) {
2826
3156
  const resizedTexture = this.depthStencilAttachment.texture.clone({
2827
3157
  width,
@@ -3332,6 +3662,18 @@ ${htmlLog}
3332
3662
  count: void 0
3333
3663
  });
3334
3664
 
3665
+ // src/adapter/resources/fence.ts
3666
+ var _Fence = class extends Resource {
3667
+ [Symbol.toStringTag] = "WEBGLFence";
3668
+ constructor(device, props = {}) {
3669
+ super(device, props, _Fence.defaultProps);
3670
+ }
3671
+ };
3672
+ var Fence = _Fence;
3673
+ __publicField(Fence, "defaultProps", {
3674
+ ...Resource.defaultProps
3675
+ });
3676
+
3335
3677
  // src/adapter/resources/pipeline-layout.ts
3336
3678
  var _PipelineLayout = class extends Resource {
3337
3679
  get [Symbol.toStringTag]() {
@@ -3363,17 +3705,6 @@ ${htmlLog}
3363
3705
  return new Type(scratchArrayBuffer, 0, length);
3364
3706
  }
3365
3707
 
3366
- // src/utils/is-array.ts
3367
- function isTypedArray(value) {
3368
- return ArrayBuffer.isView(value) && !(value instanceof DataView);
3369
- }
3370
- function isNumberArray(value) {
3371
- if (Array.isArray(value)) {
3372
- return value.length === 0 || typeof value[0] === "number";
3373
- }
3374
- return isTypedArray(value);
3375
- }
3376
-
3377
3708
  // src/portable/uniform-buffer-layout.ts
3378
3709
  var minBufferSize = 1024;
3379
3710
  var UniformBufferLayout = class {
@@ -3384,67 +3715,114 @@ ${htmlLog}
3384
3715
  constructor(uniformTypes, uniformSizes = {}) {
3385
3716
  let size = 0;
3386
3717
  for (const [key, uniformType] of Object.entries(uniformTypes)) {
3387
- const typeAndComponents = getVariableShaderTypeInfo(uniformType);
3388
- const { type, components } = typeAndComponents;
3389
- const count = components * (uniformSizes?.[key] ?? 1);
3390
- size = alignTo(size, count);
3391
- const offset = size;
3392
- size += count;
3393
- this.layout[key] = { type, size: count, offset };
3718
+ size = this._addToLayout(key, uniformType, size, uniformSizes?.[key]);
3394
3719
  }
3395
3720
  size += (4 - size % 4) % 4;
3396
- const actualByteLength = size * 4;
3397
- this.byteLength = Math.max(actualByteLength, minBufferSize);
3721
+ this.byteLength = Math.max(size * 4, minBufferSize);
3722
+ }
3723
+ /** Does this layout have a field with specified name */
3724
+ has(name2) {
3725
+ return Boolean(this.layout[name2]);
3726
+ }
3727
+ /** Get offset and size for a field with specified name */
3728
+ get(name2) {
3729
+ const layout = this.layout[name2];
3730
+ return layout;
3398
3731
  }
3399
3732
  /** Get the data for the complete buffer */
3400
3733
  getData(uniformValues) {
3401
- const arrayBuffer2 = getScratchArrayBuffer(this.byteLength);
3734
+ const buffer = getScratchArrayBuffer(this.byteLength);
3402
3735
  const typedArrays = {
3403
- i32: new Int32Array(arrayBuffer2),
3404
- u32: new Uint32Array(arrayBuffer2),
3405
- f32: new Float32Array(arrayBuffer2),
3406
- // TODO not implemented
3407
- f16: new Uint16Array(arrayBuffer2)
3736
+ i32: new Int32Array(buffer),
3737
+ u32: new Uint32Array(buffer),
3738
+ f32: new Float32Array(buffer),
3739
+ f16: new Uint16Array(buffer)
3408
3740
  };
3409
3741
  for (const [name2, value] of Object.entries(uniformValues)) {
3410
- const uniformLayout = this.layout[name2];
3411
- if (!uniformLayout) {
3412
- log.warn(`Supplied uniform value ${name2} not present in uniform block layout`)();
3413
- continue;
3742
+ this._writeCompositeValue(typedArrays, name2, value);
3743
+ }
3744
+ return new Uint8Array(buffer, 0, this.byteLength);
3745
+ }
3746
+ // Recursively add a uniform to the layout
3747
+ _addToLayout(name2, type, offset, count = 1) {
3748
+ if (typeof type === "string") {
3749
+ const info = getVariableShaderTypeInfo(type);
3750
+ const sizeInSlots = info.components * count;
3751
+ const alignedOffset = alignTo(offset, info.components);
3752
+ this.layout[name2] = {
3753
+ offset: alignedOffset,
3754
+ size: sizeInSlots,
3755
+ type: info.type
3756
+ };
3757
+ return alignedOffset + sizeInSlots;
3758
+ }
3759
+ if (Array.isArray(type)) {
3760
+ const elementType = type[0];
3761
+ const length = count > 1 ? count : type.length > 1 ? type[1] : 1;
3762
+ let arrayOffset = alignTo(offset, 4);
3763
+ for (let i = 0; i < length; i++) {
3764
+ arrayOffset = this._addToLayout(`${name2}[${i}]`, elementType, arrayOffset);
3414
3765
  }
3415
- const { type, size, offset } = uniformLayout;
3416
- const typedArray = typedArrays[type];
3417
- if (size === 1) {
3418
- if (typeof value !== "number" && typeof value !== "boolean") {
3419
- log.warn(
3420
- `Supplied value for single component uniform ${name2} is not a number: ${value}`
3421
- )();
3422
- continue;
3423
- }
3424
- typedArray[offset] = Number(value);
3425
- } else {
3426
- if (!isNumberArray(value)) {
3427
- log.warn(
3428
- `Supplied value for multi component / array uniform ${name2} is not a numeric array: ${value}`
3429
- )();
3430
- continue;
3431
- }
3432
- typedArray.set(value, offset);
3766
+ return arrayOffset;
3767
+ }
3768
+ if (typeof type === "object") {
3769
+ let structOffset = alignTo(offset, 4);
3770
+ for (const [memberName, memberType] of Object.entries(type)) {
3771
+ structOffset = this._addToLayout(`${name2}.${memberName}`, memberType, structOffset);
3433
3772
  }
3773
+ return structOffset;
3434
3774
  }
3435
- return new Uint8Array(arrayBuffer2, 0, this.byteLength);
3775
+ throw new Error(`Unsupported CompositeShaderType for ${name2}`);
3436
3776
  }
3437
- /** Does this layout have a field with specified name */
3438
- has(name2) {
3439
- return Boolean(this.layout[name2]);
3777
+ _writeCompositeValue(typedArrays, baseName, value) {
3778
+ if (this.layout[baseName]) {
3779
+ this._writeToBuffer(typedArrays, baseName, value);
3780
+ return;
3781
+ }
3782
+ if (Array.isArray(value)) {
3783
+ for (let i = 0; i < value.length; i++) {
3784
+ const element = value[i];
3785
+ const indexedName = `${baseName}[${i}]`;
3786
+ this._writeCompositeValue(typedArrays, indexedName, element);
3787
+ }
3788
+ return;
3789
+ }
3790
+ if (typeof value === "object" && value !== null) {
3791
+ for (const [key, subValue] of Object.entries(value)) {
3792
+ const nestedName = `${baseName}.${key}`;
3793
+ this._writeCompositeValue(typedArrays, nestedName, subValue);
3794
+ }
3795
+ return;
3796
+ }
3797
+ log.warn(`Unsupported uniform value for ${baseName}:`, value)();
3440
3798
  }
3441
- /** Get offset and size for a field with specified name */
3442
- get(name2) {
3799
+ _writeToBuffer(typedArrays, name2, value) {
3443
3800
  const layout = this.layout[name2];
3444
- return layout;
3801
+ if (!layout) {
3802
+ log.warn(`Uniform ${name2} not found in layout`)();
3803
+ return;
3804
+ }
3805
+ const { type, size, offset } = layout;
3806
+ const array = typedArrays[type];
3807
+ if (size === 1) {
3808
+ array[offset] = Number(value);
3809
+ } else {
3810
+ array.set(value, offset);
3811
+ }
3445
3812
  }
3446
3813
  };
3447
3814
 
3815
+ // src/utils/is-array.ts
3816
+ function isTypedArray(value) {
3817
+ return ArrayBuffer.isView(value) && !(value instanceof DataView);
3818
+ }
3819
+ function isNumberArray(value) {
3820
+ if (Array.isArray(value)) {
3821
+ return value.length === 0 || typeof value[0] === "number";
3822
+ }
3823
+ return isTypedArray(value);
3824
+ }
3825
+
3448
3826
  // src/utils/array-equal.ts
3449
3827
  function arrayEqual(a, b, limit = 16) {
3450
3828
  if (a !== b) {
@@ -3636,6 +4014,44 @@ ${htmlLog}
3636
4014
  }
3637
4015
  };
3638
4016
 
4017
+ // src/shadertypes/textures/texture-layout.ts
4018
+ function getTextureImageView(arrayBuffer2, memoryLayout, format, image = 0) {
4019
+ const formatInfo = textureFormatDecoder.getInfo(format);
4020
+ const bytesPerComponent = formatInfo.bytesPerPixel / formatInfo.components;
4021
+ const { bytesPerImage } = memoryLayout;
4022
+ const offset = bytesPerImage * image;
4023
+ const totalPixels = memoryLayout.bytesPerImage / bytesPerComponent;
4024
+ switch (format) {
4025
+ case "rgba8unorm":
4026
+ case "bgra8unorm":
4027
+ case "rgba8uint":
4028
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
4029
+ case "r8unorm":
4030
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
4031
+ case "r16uint":
4032
+ case "rgba16uint":
4033
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
4034
+ case "r32uint":
4035
+ case "rgba32uint":
4036
+ return new Uint32Array(arrayBuffer2, offset, totalPixels);
4037
+ case "r32float":
4038
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
4039
+ case "rgba16float":
4040
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
4041
+ case "rgba32float":
4042
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
4043
+ default:
4044
+ throw new Error(`Unsupported format: ${format}`);
4045
+ }
4046
+ }
4047
+ function setTextureImageData(arrayBuffer2, memoryLayout, format, data, image = 0) {
4048
+ const offset = 0;
4049
+ const totalPixels = memoryLayout.bytesPerImage / memoryLayout.bytesPerPixel;
4050
+ const subArray = data.subarray(0, totalPixels);
4051
+ const typedArray = getTextureImageView(arrayBuffer2, memoryLayout, format, image);
4052
+ typedArray.set(subArray, offset);
4053
+ }
4054
+
3639
4055
  // src/shadertypes/textures/pixel-utils.ts
3640
4056
  function readPixel(pixelData, x, y, bitsPerChannel) {
3641
4057
  if (x < 0 || x >= pixelData.width || y < 0 || y >= pixelData.height) {
@@ -3646,7 +4062,7 @@ ${htmlLog}
3646
4062
  let bitOffsetWithinPixel = 0;
3647
4063
  const channels = [];
3648
4064
  for (let i = 0; i < 4; i++) {
3649
- const bits = bitsPerChannel[i];
4065
+ const bits = bitsPerChannel[i] ?? 0;
3650
4066
  if (bits <= 0) {
3651
4067
  channels.push(0);
3652
4068
  } else {
@@ -3655,14 +4071,14 @@ ${htmlLog}
3655
4071
  bitOffsetWithinPixel += bits;
3656
4072
  }
3657
4073
  }
3658
- return [channels[0], channels[1], channels[2], channels[3]];
4074
+ return [channels[0] ?? 0, channels[1] ?? 0, channels[2] ?? 0, channels[3] ?? 0];
3659
4075
  }
3660
4076
  function writePixel(dataView, bitOffset, bitsPerChannel, pixel) {
3661
4077
  let currentBitOffset = bitOffset;
3662
4078
  for (let channel = 0; channel < 4; channel++) {
3663
- const bits = bitsPerChannel[channel];
4079
+ const bits = bitsPerChannel[channel] ?? 0;
3664
4080
  const maxValue = (1 << bits) - 1;
3665
- const channelValue = pixel[channel] & maxValue;
4081
+ const channelValue = (pixel[channel] ?? 0) & maxValue;
3666
4082
  writeBitsToDataView(dataView, currentBitOffset, bits, channelValue);
3667
4083
  currentBitOffset += bits;
3668
4084
  }