@luma.gl/effects 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.
package/dist/dist.dev.js CHANGED
@@ -64,6 +64,7 @@ var __exports__ = (() => {
64
64
  DeviceFeatures: () => DeviceFeatures,
65
65
  DeviceLimits: () => DeviceLimits,
66
66
  ExternalTexture: () => ExternalTexture,
67
+ Fence: () => Fence,
67
68
  Framebuffer: () => Framebuffer,
68
69
  PipelineLayout: () => PipelineLayout,
69
70
  QuerySet: () => QuerySet,
@@ -82,20 +83,26 @@ var __exports__ = (() => {
82
83
  VertexArray: () => VertexArray,
83
84
  _getTextureFormatDefinition: () => getTextureFormatDefinition,
84
85
  _getTextureFormatTable: () => getTextureFormatTable,
86
+ assert: () => assert2,
87
+ assertDefined: () => assertDefined,
85
88
  getAttributeInfosFromLayouts: () => getAttributeInfosFromLayouts,
86
89
  getAttributeShaderTypeInfo: () => getAttributeShaderTypeInfo,
87
90
  getDataType: () => getDataType,
88
91
  getDataTypeInfo: () => getDataTypeInfo,
92
+ getExternalImageSize: () => getExternalImageSize,
89
93
  getNormalizedDataType: () => getNormalizedDataType,
90
94
  getScratchArray: () => getScratchArray,
95
+ getTextureImageView: () => getTextureImageView,
91
96
  getTypedArrayConstructor: () => getTypedArrayConstructor,
92
97
  getVariableShaderTypeInfo: () => getVariableShaderTypeInfo,
93
98
  getVertexFormatFromAttribute: () => getVertexFormatFromAttribute,
94
99
  getVertexFormatInfo: () => getVertexFormatInfo,
100
+ isExternalImage: () => isExternalImage,
95
101
  log: () => log,
96
102
  luma: () => luma,
97
103
  makeVertexFormat: () => makeVertexFormat,
98
104
  readPixel: () => readPixel,
105
+ setTextureImageData: () => setTextureImageData,
99
106
  textureFormatDecoder: () => textureFormatDecoder,
100
107
  writePixel: () => writePixel
101
108
  });
@@ -338,7 +345,139 @@ var __exports__ = (() => {
338
345
  }
339
346
 
340
347
  // ../../node_modules/@probe.gl/env/dist/index.js
341
- var VERSION = true ? "4.1.0" : "untranspiled source";
348
+ var VERSION = true ? "4.1.1" : "untranspiled source";
349
+
350
+ // ../../node_modules/@probe.gl/log/dist/utils/assert.js
351
+ function assert(condition, message) {
352
+ if (!condition) {
353
+ throw new Error(message || "Assertion failed");
354
+ }
355
+ }
356
+
357
+ // ../../node_modules/@probe.gl/log/dist/loggers/log-utils.js
358
+ function normalizeLogLevel(logLevel) {
359
+ if (!logLevel) {
360
+ return 0;
361
+ }
362
+ let resolvedLevel;
363
+ switch (typeof logLevel) {
364
+ case "number":
365
+ resolvedLevel = logLevel;
366
+ break;
367
+ case "object":
368
+ resolvedLevel = logLevel.logLevel || logLevel.priority || 0;
369
+ break;
370
+ default:
371
+ return 0;
372
+ }
373
+ assert(Number.isFinite(resolvedLevel) && resolvedLevel >= 0);
374
+ return resolvedLevel;
375
+ }
376
+ function normalizeArguments(opts) {
377
+ const { logLevel, message } = opts;
378
+ opts.logLevel = normalizeLogLevel(logLevel);
379
+ const args = opts.args ? Array.from(opts.args) : [];
380
+ while (args.length && args.shift() !== message) {
381
+ }
382
+ switch (typeof logLevel) {
383
+ case "string":
384
+ case "function":
385
+ if (message !== void 0) {
386
+ args.unshift(message);
387
+ }
388
+ opts.message = logLevel;
389
+ break;
390
+ case "object":
391
+ Object.assign(opts, logLevel);
392
+ break;
393
+ default:
394
+ }
395
+ if (typeof opts.message === "function") {
396
+ opts.message = opts.message();
397
+ }
398
+ const messageType = typeof opts.message;
399
+ assert(messageType === "string" || messageType === "object");
400
+ return Object.assign(opts, { args }, opts.opts);
401
+ }
402
+
403
+ // ../../node_modules/@probe.gl/log/dist/loggers/base-log.js
404
+ var noop = () => {
405
+ };
406
+ var BaseLog = class {
407
+ constructor({ level = 0 } = {}) {
408
+ this.userData = {};
409
+ this._onceCache = /* @__PURE__ */ new Set();
410
+ this._level = level;
411
+ }
412
+ set level(newLevel) {
413
+ this.setLevel(newLevel);
414
+ }
415
+ get level() {
416
+ return this.getLevel();
417
+ }
418
+ setLevel(level) {
419
+ this._level = level;
420
+ return this;
421
+ }
422
+ getLevel() {
423
+ return this._level;
424
+ }
425
+ // Unconditional logging
426
+ warn(message, ...args) {
427
+ return this._log("warn", 0, message, args, { once: true });
428
+ }
429
+ error(message, ...args) {
430
+ return this._log("error", 0, message, args);
431
+ }
432
+ // Conditional logging
433
+ log(logLevel, message, ...args) {
434
+ return this._log("log", logLevel, message, args);
435
+ }
436
+ info(logLevel, message, ...args) {
437
+ return this._log("info", logLevel, message, args);
438
+ }
439
+ once(logLevel, message, ...args) {
440
+ return this._log("once", logLevel, message, args, { once: true });
441
+ }
442
+ _log(type, logLevel, message, args, options = {}) {
443
+ const normalized = normalizeArguments({
444
+ logLevel,
445
+ message,
446
+ args: this._buildArgs(logLevel, message, args),
447
+ opts: options
448
+ });
449
+ return this._createLogFunction(type, normalized, options);
450
+ }
451
+ _buildArgs(logLevel, message, args) {
452
+ return [logLevel, message, ...args];
453
+ }
454
+ _createLogFunction(type, normalized, options) {
455
+ if (!this._shouldLog(normalized.logLevel)) {
456
+ return noop;
457
+ }
458
+ const tag = this._getOnceTag(options.tag ?? normalized.tag ?? normalized.message);
459
+ if ((options.once || normalized.once) && tag !== void 0) {
460
+ if (this._onceCache.has(tag)) {
461
+ return noop;
462
+ }
463
+ this._onceCache.add(tag);
464
+ }
465
+ return this._emit(type, normalized);
466
+ }
467
+ _shouldLog(logLevel) {
468
+ return this.getLevel() >= normalizeLogLevel(logLevel);
469
+ }
470
+ _getOnceTag(tag) {
471
+ if (tag === void 0) {
472
+ return void 0;
473
+ }
474
+ try {
475
+ return typeof tag === "string" ? tag : String(tag);
476
+ } catch {
477
+ return void 0;
478
+ }
479
+ }
480
+ };
342
481
 
343
482
  // ../../node_modules/@probe.gl/log/dist/utils/local-storage.js
344
483
  function getStorage(type) {
@@ -457,13 +596,6 @@ var __exports__ = (() => {
457
596
  }
458
597
  }
459
598
 
460
- // ../../node_modules/@probe.gl/log/dist/utils/assert.js
461
- function assert(condition, message) {
462
- if (!condition) {
463
- throw new Error(message || "Assertion failed");
464
- }
465
- }
466
-
467
599
  // ../../node_modules/@probe.gl/log/dist/utils/hi-res-timestamp.js
468
600
  function getHiResTimestamp2() {
469
601
  let timestamp;
@@ -478,7 +610,7 @@ var __exports__ = (() => {
478
610
  return timestamp;
479
611
  }
480
612
 
481
- // ../../node_modules/@probe.gl/log/dist/log.js
613
+ // ../../node_modules/@probe.gl/log/dist/loggers/probe-log.js
482
614
  var originalConsole = {
483
615
  debug: isBrowser() ? console.debug || console.log : console.log,
484
616
  log: console.log,
@@ -490,12 +622,9 @@ var __exports__ = (() => {
490
622
  enabled: true,
491
623
  level: 0
492
624
  };
493
- function noop() {
494
- }
495
- var cache = {};
496
- var ONCE = { once: true };
497
- var Log = class {
625
+ var ProbeLog = class extends BaseLog {
498
626
  constructor({ id } = { id: "" }) {
627
+ super({ level: 0 });
499
628
  this.VERSION = VERSION;
500
629
  this._startTs = getHiResTimestamp2();
501
630
  this._deltaTs = getHiResTimestamp2();
@@ -503,22 +632,16 @@ var __exports__ = (() => {
503
632
  this.LOG_THROTTLE_TIMEOUT = 0;
504
633
  this.id = id;
505
634
  this.userData = {};
506
- this._storage = new LocalStorage(`__probe-${this.id}__`, DEFAULT_LOG_CONFIGURATION);
635
+ this._storage = new LocalStorage(`__probe-${this.id}__`, { [this.id]: DEFAULT_LOG_CONFIGURATION });
507
636
  this.timeStamp(`${this.id} started`);
508
637
  autobind(this);
509
638
  Object.seal(this);
510
639
  }
511
- set level(newLevel) {
512
- this.setLevel(newLevel);
513
- }
514
- get level() {
515
- return this.getLevel();
516
- }
517
640
  isEnabled() {
518
- return this._storage.config.enabled;
641
+ return this._getConfiguration().enabled;
519
642
  }
520
643
  getLevel() {
521
- return this._storage.config.level;
644
+ return this._getConfiguration().level;
522
645
  }
523
646
  /** @return milliseconds, with fractions */
524
647
  getTotal() {
@@ -542,20 +665,20 @@ var __exports__ = (() => {
542
665
  }
543
666
  // Configure
544
667
  enable(enabled = true) {
545
- this._storage.setConfiguration({ enabled });
668
+ this._updateConfiguration({ enabled });
546
669
  return this;
547
670
  }
548
671
  setLevel(level) {
549
- this._storage.setConfiguration({ level });
672
+ this._updateConfiguration({ level });
550
673
  return this;
551
674
  }
552
675
  /** return the current status of the setting */
553
676
  get(setting) {
554
- return this._storage.config[setting];
677
+ return this._getConfiguration()[setting];
555
678
  }
556
679
  // update the status of the setting
557
680
  set(setting, value) {
558
- this._storage.setConfiguration({ [setting]: value });
681
+ this._updateConfiguration({ [setting]: value });
559
682
  }
560
683
  /** Logs the current settings as a table */
561
684
  settings() {
@@ -571,11 +694,16 @@ var __exports__ = (() => {
571
694
  throw new Error(message || "Assertion failed");
572
695
  }
573
696
  }
574
- warn(message) {
575
- return this._getLogFunction(0, message, originalConsole.warn, arguments, ONCE);
697
+ warn(message, ...args) {
698
+ return this._log("warn", 0, message, args, {
699
+ method: originalConsole.warn,
700
+ once: true
701
+ });
576
702
  }
577
- error(message) {
578
- return this._getLogFunction(0, message, originalConsole.error, arguments);
703
+ error(message, ...args) {
704
+ return this._log("error", 0, message, args, {
705
+ method: originalConsole.error
706
+ });
579
707
  }
580
708
  /** Print a deprecation warning */
581
709
  deprecated(oldUsage, newUsage) {
@@ -585,50 +713,63 @@ var __exports__ = (() => {
585
713
  removed(oldUsage, newUsage) {
586
714
  return this.error(`\`${oldUsage}\` has been removed. Use \`${newUsage}\` instead`);
587
715
  }
588
- probe(logLevel, message) {
589
- return this._getLogFunction(logLevel, message, originalConsole.log, arguments, {
716
+ probe(logLevel, message, ...args) {
717
+ return this._log("log", logLevel, message, args, {
718
+ method: originalConsole.log,
590
719
  time: true,
591
720
  once: true
592
721
  });
593
722
  }
594
- log(logLevel, message) {
595
- return this._getLogFunction(logLevel, message, originalConsole.debug, arguments);
723
+ log(logLevel, message, ...args) {
724
+ return this._log("log", logLevel, message, args, {
725
+ method: originalConsole.debug
726
+ });
596
727
  }
597
- info(logLevel, message) {
598
- return this._getLogFunction(logLevel, message, console.info, arguments);
728
+ info(logLevel, message, ...args) {
729
+ return this._log("info", logLevel, message, args, { method: console.info });
599
730
  }
600
- once(logLevel, message) {
601
- return this._getLogFunction(logLevel, message, originalConsole.debug || originalConsole.info, arguments, ONCE);
731
+ once(logLevel, message, ...args) {
732
+ return this._log("once", logLevel, message, args, {
733
+ method: originalConsole.debug || originalConsole.info,
734
+ once: true
735
+ });
602
736
  }
603
737
  /** Logs an object as a table */
604
738
  table(logLevel, table, columns) {
605
739
  if (table) {
606
- return this._getLogFunction(logLevel, table, console.table || noop, columns && [columns], {
740
+ return this._log("table", logLevel, table, columns && [columns] || [], {
741
+ method: console.table || noop,
607
742
  tag: getTableHeader(table)
608
743
  });
609
744
  }
610
745
  return noop;
611
746
  }
612
747
  time(logLevel, message) {
613
- return this._getLogFunction(logLevel, message, console.time ? console.time : console.info);
748
+ return this._log("time", logLevel, message, [], {
749
+ method: console.time ? console.time : console.info
750
+ });
614
751
  }
615
752
  timeEnd(logLevel, message) {
616
- return this._getLogFunction(logLevel, message, console.timeEnd ? console.timeEnd : console.info);
753
+ return this._log("time", logLevel, message, [], {
754
+ method: console.timeEnd ? console.timeEnd : console.info
755
+ });
617
756
  }
618
757
  timeStamp(logLevel, message) {
619
- return this._getLogFunction(logLevel, message, console.timeStamp || noop);
758
+ return this._log("time", logLevel, message, [], {
759
+ method: console.timeStamp || noop
760
+ });
620
761
  }
621
762
  group(logLevel, message, opts = { collapsed: false }) {
622
- const options = normalizeArguments({ logLevel, message, opts });
623
- const { collapsed } = opts;
624
- options.method = (collapsed ? console.groupCollapsed : console.group) || console.info;
625
- return this._getLogFunction(options);
763
+ const method = (opts.collapsed ? console.groupCollapsed : console.group) || console.info;
764
+ return this._log("group", logLevel, message, [], { method });
626
765
  }
627
766
  groupCollapsed(logLevel, message, opts = {}) {
628
767
  return this.group(logLevel, message, Object.assign({}, opts, { collapsed: true }));
629
768
  }
630
769
  groupEnd(logLevel) {
631
- return this._getLogFunction(logLevel, "", console.groupEnd || noop);
770
+ return this._log("groupEnd", logLevel, "", [], {
771
+ method: console.groupEnd || noop
772
+ });
632
773
  }
633
774
  // EXPERIMENTAL
634
775
  withGroup(logLevel, message, func) {
@@ -644,78 +785,34 @@ var __exports__ = (() => {
644
785
  console.trace();
645
786
  }
646
787
  }
647
- // PRIVATE METHODS
648
- /** Deduces log level from a variety of arguments */
649
788
  _shouldLog(logLevel) {
650
- return this.isEnabled() && this.getLevel() >= normalizeLogLevel(logLevel);
651
- }
652
- _getLogFunction(logLevel, message, method, args, opts) {
653
- if (this._shouldLog(logLevel)) {
654
- opts = normalizeArguments({ logLevel, message, args, opts });
655
- method = method || opts.method;
656
- assert(method);
657
- opts.total = this.getTotal();
658
- opts.delta = this.getDelta();
659
- this._deltaTs = getHiResTimestamp2();
660
- const tag = opts.tag || opts.message;
661
- if (opts.once && tag) {
662
- if (!cache[tag]) {
663
- cache[tag] = getHiResTimestamp2();
664
- } else {
665
- return noop;
666
- }
667
- }
668
- message = decorateMessage(this.id, opts.message, opts);
669
- return method.bind(console, message, ...opts.args);
670
- }
671
- return noop;
672
- }
673
- };
674
- Log.VERSION = VERSION;
675
- function normalizeLogLevel(logLevel) {
676
- if (!logLevel) {
677
- return 0;
678
- }
679
- let resolvedLevel;
680
- switch (typeof logLevel) {
681
- case "number":
682
- resolvedLevel = logLevel;
683
- break;
684
- case "object":
685
- resolvedLevel = logLevel.logLevel || logLevel.priority || 0;
686
- break;
687
- default:
688
- return 0;
789
+ return this.isEnabled() && super._shouldLog(logLevel);
689
790
  }
690
- assert(Number.isFinite(resolvedLevel) && resolvedLevel >= 0);
691
- return resolvedLevel;
692
- }
693
- function normalizeArguments(opts) {
694
- const { logLevel, message } = opts;
695
- opts.logLevel = normalizeLogLevel(logLevel);
696
- const args = opts.args ? Array.from(opts.args) : [];
697
- while (args.length && args.shift() !== message) {
791
+ _emit(_type, normalized) {
792
+ const method = normalized.method;
793
+ assert(method);
794
+ normalized.total = this.getTotal();
795
+ normalized.delta = this.getDelta();
796
+ this._deltaTs = getHiResTimestamp2();
797
+ const message = decorateMessage(this.id, normalized.message, normalized);
798
+ return method.bind(console, message, ...normalized.args);
698
799
  }
699
- switch (typeof logLevel) {
700
- case "string":
701
- case "function":
702
- if (message !== void 0) {
703
- args.unshift(message);
704
- }
705
- opts.message = logLevel;
706
- break;
707
- case "object":
708
- Object.assign(opts, logLevel);
709
- break;
710
- default:
800
+ _getConfiguration() {
801
+ if (!this._storage.config[this.id]) {
802
+ this._updateConfiguration(DEFAULT_LOG_CONFIGURATION);
803
+ }
804
+ return this._storage.config[this.id];
711
805
  }
712
- if (typeof opts.message === "function") {
713
- opts.message = opts.message();
806
+ _updateConfiguration(configuration) {
807
+ const currentConfiguration = this._storage.config[this.id] || {
808
+ ...DEFAULT_LOG_CONFIGURATION
809
+ };
810
+ this._storage.setConfiguration({
811
+ [this.id]: { ...currentConfiguration, ...configuration }
812
+ });
714
813
  }
715
- const messageType = typeof opts.message;
716
- assert(messageType === "string" || messageType === "object");
717
- return Object.assign(opts, { args }, opts.opts);
718
- }
814
+ };
815
+ ProbeLog.VERSION = VERSION;
719
816
  function decorateMessage(id, message, opts) {
720
817
  if (typeof message === "string") {
721
818
  const time = opts.time ? leftPad(formatTime(opts.total)) : "";
@@ -737,10 +834,10 @@ var __exports__ = (() => {
737
834
  globalThis.probe = {};
738
835
 
739
836
  // ../../node_modules/@probe.gl/log/dist/index.js
740
- var dist_default = new Log({ id: "@probe.gl/log" });
837
+ var dist_default = new ProbeLog({ id: "@probe.gl/log" });
741
838
 
742
839
  // ../core/src/utils/log.ts
743
- var log = new Log({ id: "luma.gl" });
840
+ var log = new ProbeLog({ id: "luma.gl" });
744
841
 
745
842
  // ../core/src/utils/uid.ts
746
843
  var uidCounters = {};
@@ -757,8 +854,11 @@ var __exports__ = (() => {
757
854
  }
758
855
  /** props.id, for debugging. */
759
856
  id;
857
+ /** The props that this resource was created with */
760
858
  props;
859
+ /** User data object, reserved for the application */
761
860
  userData = {};
861
+ /** The device that this resource is associated with - TODO can we remove this dup? */
762
862
  _device;
763
863
  /** Whether this resource has been destroyed */
764
864
  destroyed = false;
@@ -965,10 +1065,11 @@ var __exports__ = (() => {
965
1065
 
966
1066
  // ../core/src/shadertypes/data-types/decode-data-types.ts
967
1067
  function getDataTypeInfo(type) {
968
- const [signedType, primitiveType, byteLength] = NORMALIZED_TYPE_MAP[type];
969
1068
  const normalized = type.includes("norm");
970
1069
  const integer = !normalized && !type.startsWith("float");
971
1070
  const signed = type.startsWith("s");
1071
+ const typeInfo = NORMALIZED_TYPE_MAP[type];
1072
+ const [signedType, primitiveType, byteLength] = typeInfo || ["uint8 ", "i32", 1];
972
1073
  return {
973
1074
  signedType,
974
1075
  primitiveType,
@@ -1284,6 +1385,9 @@ var __exports__ = (() => {
1284
1385
  };
1285
1386
 
1286
1387
  // ../core/src/shadertypes/textures/texture-format-decoder.ts
1388
+ var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
1389
+ var COLOR_FORMAT_PREFIXES = ["rgb", "rgba", "bgra"];
1390
+ var DEPTH_FORMAT_PREFIXES = ["depth", "stencil"];
1287
1391
  var COMPRESSED_TEXTURE_FORMAT_PREFIXES = [
1288
1392
  "bc1",
1289
1393
  "bc2",
@@ -1299,49 +1403,73 @@ var __exports__ = (() => {
1299
1403
  "astc",
1300
1404
  "pvrtc"
1301
1405
  ];
1302
- var RGB_FORMAT_REGEX = /^(r|rg|rgb|rgba|bgra)([0-9]*)([a-z]*)(-srgb)?(-webgl)?$/;
1303
1406
  var TextureFormatDecoder = class {
1304
- /** Returns information about a texture format, e.g. attatchment type, components, byte length and flags (integer, signed, normalized) */
1305
- getInfo(format) {
1306
- return getTextureFormatInfo(format);
1307
- }
1308
1407
  /** Checks if a texture format is color */
1309
1408
  isColor(format) {
1310
- return format.startsWith("rgba") || format.startsWith("bgra") || format.startsWith("rgb");
1409
+ return COLOR_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1311
1410
  }
1312
1411
  /** Checks if a texture format is depth or stencil */
1313
1412
  isDepthStencil(format) {
1314
- return format.startsWith("depth") || format.startsWith("stencil");
1413
+ return DEPTH_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1315
1414
  }
1316
1415
  /** Checks if a texture format is compressed */
1317
1416
  isCompressed(format) {
1318
1417
  return COMPRESSED_TEXTURE_FORMAT_PREFIXES.some((prefix) => format.startsWith(prefix));
1319
1418
  }
1320
- /**
1321
- * Returns the "static" capabilities of a texture format.
1322
- * @note Needs to be checked against current device
1323
- */
1419
+ /** Returns information about a texture format, e.g. attachment type, components, byte length and flags (integer, signed, normalized) */
1420
+ getInfo(format) {
1421
+ return getTextureFormatInfo(format);
1422
+ }
1423
+ /** "static" capabilities of a texture format. @note Needs to be adjusted against current device */
1324
1424
  getCapabilities(format) {
1325
- const info = getTextureFormatDefinition(format);
1326
- const formatCapabilities = {
1327
- format,
1328
- create: info.f ?? true,
1329
- render: info.render ?? true,
1330
- filter: info.filter ?? true,
1331
- blend: info.blend ?? true,
1332
- store: info.store ?? true
1333
- };
1334
- const formatInfo = getTextureFormatInfo(format);
1335
- const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
1336
- const isSigned = formatInfo?.signed;
1337
- const isInteger = formatInfo?.integer;
1338
- const isWebGLSpecific = formatInfo?.webgl;
1339
- formatCapabilities.render &&= !isSigned;
1340
- formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
1341
- return formatCapabilities;
1425
+ return getTextureFormatCapabilities(format);
1426
+ }
1427
+ /** Computes the memory layout for a texture, in particular including row byte alignment */
1428
+ computeMemoryLayout(opts) {
1429
+ return computeTextureMemoryLayout(opts);
1342
1430
  }
1343
1431
  };
1344
1432
  var textureFormatDecoder = new TextureFormatDecoder();
1433
+ function computeTextureMemoryLayout({
1434
+ format,
1435
+ width,
1436
+ height,
1437
+ depth,
1438
+ byteAlignment
1439
+ }) {
1440
+ const { bytesPerPixel } = textureFormatDecoder.getInfo(format);
1441
+ const unpaddedBytesPerRow = width * bytesPerPixel;
1442
+ const bytesPerRow = Math.ceil(unpaddedBytesPerRow / byteAlignment) * byteAlignment;
1443
+ const rowsPerImage = height;
1444
+ const byteLength = bytesPerRow * rowsPerImage * depth;
1445
+ return {
1446
+ bytesPerPixel,
1447
+ bytesPerRow,
1448
+ rowsPerImage,
1449
+ depthOrArrayLayers: depth,
1450
+ bytesPerImage: bytesPerRow * rowsPerImage,
1451
+ byteLength
1452
+ };
1453
+ }
1454
+ function getTextureFormatCapabilities(format) {
1455
+ const info = getTextureFormatDefinition(format);
1456
+ const formatCapabilities = {
1457
+ format,
1458
+ create: info.f ?? true,
1459
+ render: info.render ?? true,
1460
+ filter: info.filter ?? true,
1461
+ blend: info.blend ?? true,
1462
+ store: info.store ?? true
1463
+ };
1464
+ const formatInfo = getTextureFormatInfo(format);
1465
+ const isDepthStencil = format.startsWith("depth") || format.startsWith("stencil");
1466
+ const isSigned = formatInfo?.signed;
1467
+ const isInteger = formatInfo?.integer;
1468
+ const isWebGLSpecific = formatInfo?.webgl;
1469
+ formatCapabilities.render &&= !isSigned;
1470
+ formatCapabilities.filter &&= !isDepthStencil && !isSigned && !isInteger && !isWebGLSpecific;
1471
+ return formatCapabilities;
1472
+ }
1345
1473
  function getTextureFormatInfo(format) {
1346
1474
  let formatInfo = getTextureFormatInfoUsingTable(format);
1347
1475
  if (textureFormatDecoder.isCompressed(format)) {
@@ -1362,7 +1490,7 @@ var __exports__ = (() => {
1362
1490
  const dataType = `${type}${length}`;
1363
1491
  const decodedType = getDataTypeInfo(dataType);
1364
1492
  const bits = decodedType.byteLength * 8;
1365
- const components = channels.length;
1493
+ const components = channels?.length ?? 1;
1366
1494
  const bitsPerChannel = [
1367
1495
  bits,
1368
1496
  components >= 2 ? bits : 0,
@@ -1379,7 +1507,7 @@ var __exports__ = (() => {
1379
1507
  signed: decodedType.signed,
1380
1508
  normalized: decodedType.normalized,
1381
1509
  bitsPerChannel,
1382
- bytesPerPixel: decodedType.byteLength * channels.length,
1510
+ bytesPerPixel: decodedType.byteLength * components,
1383
1511
  packed: formatInfo.packed,
1384
1512
  srgb: formatInfo.srgb
1385
1513
  };
@@ -1495,7 +1623,7 @@ var __exports__ = (() => {
1495
1623
  /** True if this device has been reused during device creation (app has multiple references) */
1496
1624
  _reused = false;
1497
1625
  /** Used by other luma.gl modules to store data on the device */
1498
- _lumaData = {};
1626
+ _moduleData = {};
1499
1627
  _textureCaps = {};
1500
1628
  constructor(props) {
1501
1629
  this.props = { ..._Device.defaultProps, ...props };
@@ -1592,7 +1720,13 @@ var __exports__ = (() => {
1592
1720
  reportError(error, context, ...args) {
1593
1721
  const isHandled = this.props.onError(error, context);
1594
1722
  if (!isHandled) {
1595
- return log.error(error.message, context, ...args);
1723
+ return log.error(
1724
+ this.type === "webgl" ? "%cWebGL" : "%cWebGPU",
1725
+ "color: white; background: red; padding: 2px 6px; border-radius: 3px;",
1726
+ error.message,
1727
+ context,
1728
+ ...args
1729
+ );
1596
1730
  }
1597
1731
  return () => {
1598
1732
  };
@@ -1614,6 +1748,10 @@ or create a device with the 'debug: true' prop.`;
1614
1748
  }
1615
1749
  return this.canvasContext;
1616
1750
  }
1751
+ /** Create a fence sync object */
1752
+ createFence() {
1753
+ throw new Error("createFence() not implemented");
1754
+ }
1617
1755
  /** Create a RenderPass using the default CommandEncoder */
1618
1756
  beginRenderPass(props) {
1619
1757
  return this.commandEncoder.beginRenderPass(props);
@@ -1657,6 +1795,12 @@ or create a device with the 'debug: true' prop.`;
1657
1795
  resetWebGL() {
1658
1796
  throw new Error("not implemented");
1659
1797
  }
1798
+ // INTERNAL LUMA.GL METHODS
1799
+ getModuleData(moduleName) {
1800
+ this._moduleData[moduleName] ||= {};
1801
+ return this._moduleData[moduleName];
1802
+ }
1803
+ // INTERNAL HELPERS
1660
1804
  // IMPLEMENTATION
1661
1805
  /** Helper to get the canvas context props */
1662
1806
  static _getCanvasContextProps(props) {
@@ -1933,6 +2077,19 @@ or create a device with the 'debug: true' prop.`;
1933
2077
  return { promise, resolve, reject };
1934
2078
  }
1935
2079
 
2080
+ // ../core/src/utils/assert.ts
2081
+ function assert2(condition, message) {
2082
+ if (!condition) {
2083
+ const error = new Error(message ?? "luma.gl assertion failed.");
2084
+ Error.captureStackTrace?.(error, assert2);
2085
+ throw error;
2086
+ }
2087
+ }
2088
+ function assertDefined(value, message) {
2089
+ assert2(value, message);
2090
+ return value;
2091
+ }
2092
+
1936
2093
  // ../core/src/adapter/canvas-context.ts
1937
2094
  var _CanvasContext = class {
1938
2095
  static isHTMLCanvas(canvas) {
@@ -1968,11 +2125,19 @@ or create a device with the 'debug: true' prop.`;
1968
2125
  drawingBufferWidth;
1969
2126
  /** Height of drawing buffer: automatically tracks this.pixelHeight if props.autoResize is true */
1970
2127
  drawingBufferHeight;
2128
+ /** Resolves when the canvas is initialized, i.e. when the ResizeObserver has updated the pixel size */
1971
2129
  _initializedResolvers = withResolvers();
2130
+ /** ResizeObserver to track canvas size changes */
1972
2131
  _resizeObserver;
2132
+ /** IntersectionObserver to track canvas visibility changes */
1973
2133
  _intersectionObserver;
1974
- _position;
2134
+ _observeDevicePixelRatioTimeout = null;
2135
+ /** Position of the canvas in the document, updated by a timer */
2136
+ _position = [0, 0];
2137
+ /** Whether this canvas context has been destroyed */
1975
2138
  destroyed = false;
2139
+ /** Whether the drawing buffer size needs to be resized (deferred resizing to avoid flicker) */
2140
+ _needsDrawingBufferResize = true;
1976
2141
  toString() {
1977
2142
  return `${this[Symbol.toStringTag]}(${this.id})`;
1978
2143
  }
@@ -2020,14 +2185,23 @@ or create a device with the 'debug: true' prop.`;
2020
2185
  } catch {
2021
2186
  this._resizeObserver.observe(this.canvas, { box: "content-box" });
2022
2187
  }
2023
- setTimeout(() => this._observeDevicePixelRatio(), 0);
2188
+ this._observeDevicePixelRatioTimeout = setTimeout(() => this._observeDevicePixelRatio(), 0);
2024
2189
  if (this.props.trackPosition) {
2025
2190
  this._trackPosition();
2026
2191
  }
2027
2192
  }
2028
2193
  }
2029
2194
  destroy() {
2030
- this.destroyed = true;
2195
+ if (!this.destroyed) {
2196
+ this.destroyed = true;
2197
+ if (this._observeDevicePixelRatioTimeout) {
2198
+ clearTimeout(this._observeDevicePixelRatioTimeout);
2199
+ this._observeDevicePixelRatioTimeout = null;
2200
+ }
2201
+ this.device = null;
2202
+ this._resizeObserver?.disconnect();
2203
+ this._intersectionObserver?.disconnect();
2204
+ }
2031
2205
  }
2032
2206
  setProps(props) {
2033
2207
  if ("useDevicePixels" in props) {
@@ -2036,6 +2210,11 @@ or create a device with the 'debug: true' prop.`;
2036
2210
  }
2037
2211
  return this;
2038
2212
  }
2213
+ /** Returns a framebuffer with properly resized current 'swap chain' textures */
2214
+ getCurrentFramebuffer(options) {
2215
+ this._resizeDrawingBufferIfNeeded();
2216
+ return this._getCurrentFramebuffer(options);
2217
+ }
2039
2218
  // SIZE METHODS
2040
2219
  /**
2041
2220
  * Returns the size covered by the canvas in CSS pixels
@@ -2065,12 +2244,21 @@ or create a device with the 'debug: true' prop.`;
2065
2244
  const maxTextureDimension = this.device.limits.maxTextureDimension2D;
2066
2245
  return [maxTextureDimension, maxTextureDimension];
2067
2246
  }
2068
- /** Update the canvas drawing buffer size. Called automatically if props.autoResize is true. */
2247
+ /**
2248
+ * Update the canvas drawing buffer size.
2249
+ * @note - Called automatically if props.autoResize is true.
2250
+ * @note - Defers update of drawing buffer size until framebuffer is requested to avoid flicker
2251
+ * (resizing clears the drawing buffer)!
2252
+ */
2069
2253
  setDrawingBufferSize(width, height) {
2070
- this.canvas.width = width;
2071
- this.canvas.height = height;
2254
+ width = Math.floor(width);
2255
+ height = Math.floor(height);
2256
+ if (this.drawingBufferWidth === width && this.drawingBufferHeight === height) {
2257
+ return;
2258
+ }
2072
2259
  this.drawingBufferWidth = width;
2073
2260
  this.drawingBufferHeight = height;
2261
+ this._needsDrawingBufferResize = true;
2074
2262
  }
2075
2263
  /**
2076
2264
  * Returns the current DPR (number of physical pixels per CSS pixel), if props.useDevicePixels is true
@@ -2125,6 +2313,9 @@ or create a device with the 'debug: true' prop.`;
2125
2313
  }
2126
2314
  /** reacts to an observed intersection */
2127
2315
  _handleIntersection(entries) {
2316
+ if (this.destroyed) {
2317
+ return;
2318
+ }
2128
2319
  const entry = entries.find((entry_) => entry_.target === this.canvas);
2129
2320
  if (!entry) {
2130
2321
  return;
@@ -2141,21 +2332,26 @@ or create a device with the 'debug: true' prop.`;
2141
2332
  * @see https://webgpufundamentals.org/webgpu/lessons/webgpu-resizing-the-canvas.html
2142
2333
  */
2143
2334
  _handleResize(entries) {
2335
+ if (this.destroyed) {
2336
+ return;
2337
+ }
2144
2338
  const entry = entries.find((entry_) => entry_.target === this.canvas);
2145
2339
  if (!entry) {
2146
2340
  return;
2147
2341
  }
2148
- this.cssWidth = entry.contentBoxSize[0].inlineSize;
2149
- this.cssHeight = entry.contentBoxSize[0].blockSize;
2342
+ const contentBoxSize = assertDefined(entry.contentBoxSize?.[0]);
2343
+ this.cssWidth = contentBoxSize.inlineSize;
2344
+ this.cssHeight = contentBoxSize.blockSize;
2150
2345
  const oldPixelSize = this.getDevicePixelSize();
2151
- const devicePixelWidth = entry.devicePixelContentBoxSize?.[0].inlineSize || entry.contentBoxSize[0].inlineSize * devicePixelRatio;
2152
- const devicePixelHeight = entry.devicePixelContentBoxSize?.[0].blockSize || entry.contentBoxSize[0].blockSize * devicePixelRatio;
2346
+ const devicePixelWidth = entry.devicePixelContentBoxSize?.[0]?.inlineSize || contentBoxSize.inlineSize * devicePixelRatio;
2347
+ const devicePixelHeight = entry.devicePixelContentBoxSize?.[0]?.blockSize || contentBoxSize.blockSize * devicePixelRatio;
2153
2348
  const [maxDevicePixelWidth, maxDevicePixelHeight] = this.getMaxDrawingBufferSize();
2154
2349
  this.devicePixelWidth = Math.max(1, Math.min(devicePixelWidth, maxDevicePixelWidth));
2155
2350
  this.devicePixelHeight = Math.max(1, Math.min(devicePixelHeight, maxDevicePixelHeight));
2156
2351
  this._updateDrawingBufferSize();
2157
2352
  this.device.props.onResize(this, { oldPixelSize });
2158
2353
  }
2354
+ /** Initiate a deferred update for the canvas drawing buffer size */
2159
2355
  _updateDrawingBufferSize() {
2160
2356
  if (this.props.autoResize) {
2161
2357
  if (typeof this.props.useDevicePixels === "number") {
@@ -2166,18 +2362,32 @@ or create a device with the 'debug: true' prop.`;
2166
2362
  } else {
2167
2363
  this.setDrawingBufferSize(this.cssWidth, this.cssHeight);
2168
2364
  }
2169
- this._updateDevice();
2170
2365
  }
2171
2366
  this._initializedResolvers.resolve();
2172
2367
  this.isInitialized = true;
2173
2368
  this.updatePosition();
2174
2369
  }
2370
+ /** Perform a deferred resize of the drawing buffer if needed */
2371
+ _resizeDrawingBufferIfNeeded() {
2372
+ if (this._needsDrawingBufferResize) {
2373
+ this._needsDrawingBufferResize = false;
2374
+ const sizeChanged = this.drawingBufferWidth !== this.canvas.width || this.drawingBufferHeight !== this.canvas.height;
2375
+ if (sizeChanged) {
2376
+ this.canvas.width = this.drawingBufferWidth;
2377
+ this.canvas.height = this.drawingBufferHeight;
2378
+ this._configureDevice();
2379
+ }
2380
+ }
2381
+ }
2175
2382
  /** Monitor DPR changes */
2176
2383
  _observeDevicePixelRatio() {
2384
+ if (this.destroyed) {
2385
+ return;
2386
+ }
2177
2387
  const oldRatio = this.devicePixelRatio;
2178
2388
  this.devicePixelRatio = window.devicePixelRatio;
2179
2389
  this.updatePosition();
2180
- this.device.props.onDevicePixelRatioChange(this, { oldRatio });
2390
+ this.device.props.onDevicePixelRatioChange?.(this, { oldRatio });
2181
2391
  matchMedia(`(resolution: ${this.devicePixelRatio}dppx)`).addEventListener(
2182
2392
  "change",
2183
2393
  () => this._observeDevicePixelRatio(),
@@ -2200,6 +2410,9 @@ or create a device with the 'debug: true' prop.`;
2200
2410
  * if called before browser has finished a reflow. Should not be the case here.
2201
2411
  */
2202
2412
  updatePosition() {
2413
+ if (this.destroyed) {
2414
+ return;
2415
+ }
2203
2416
  const newRect = this.htmlCanvas?.getBoundingClientRect();
2204
2417
  if (newRect) {
2205
2418
  const position = [newRect.left, newRect.top];
@@ -2347,7 +2560,13 @@ or create a device with the 'debug: true' prop.`;
2347
2560
  depth;
2348
2561
  /** mip levels in this texture */
2349
2562
  mipLevels;
2350
- /** "Time" of last update. Monotonically increasing timestamp. TODO move to AsyncTexture? */
2563
+ /** Rows are multiples of this length, padded with extra bytes if needed */
2564
+ byteAlignment;
2565
+ /** The ready promise is always resolved. It is provided for type compatibility with DynamicTexture. */
2566
+ ready = Promise.resolve(this);
2567
+ /** isReady is always true. It is provided for type compatibility with DynamicTexture. */
2568
+ isReady = true;
2569
+ /** "Time" of last update. Monotonically increasing timestamp. TODO move to DynamicTexture? */
2351
2570
  updateTimestamp;
2352
2571
  get [Symbol.toStringTag]() {
2353
2572
  return "Texture";
@@ -2356,7 +2575,7 @@ or create a device with the 'debug: true' prop.`;
2356
2575
  return `Texture(${this.id},${this.format},${this.width}x${this.height})`;
2357
2576
  }
2358
2577
  /** Do not use directly. Create with device.createTexture() */
2359
- constructor(device, props) {
2578
+ constructor(device, props, backendProps) {
2360
2579
  props = _Texture.normalizeProps(device, props);
2361
2580
  super(device, props, _Texture.defaultProps);
2362
2581
  this.dimension = this.props.dimension;
@@ -2366,6 +2585,9 @@ or create a device with the 'debug: true' prop.`;
2366
2585
  this.height = this.props.height;
2367
2586
  this.depth = this.props.depth;
2368
2587
  this.mipLevels = this.props.mipLevels;
2588
+ if (this.dimension === "cube") {
2589
+ this.depth = 6;
2590
+ }
2369
2591
  if (this.props.width === void 0 || this.props.height === void 0) {
2370
2592
  if (device.isExternalImage(props.data)) {
2371
2593
  const size = device.getExternalImageSize(props.data);
@@ -2376,17 +2598,14 @@ or create a device with the 'debug: true' prop.`;
2376
2598
  this.height = 1;
2377
2599
  if (this.props.width === void 0 || this.props.height === void 0) {
2378
2600
  log.warn(
2379
- `${this} created with undefined width or height. This is deprecated. Use AsyncTexture instead.`
2601
+ `${this} created with undefined width or height. This is deprecated. Use DynamicTexture instead.`
2380
2602
  )();
2381
2603
  }
2382
2604
  }
2383
2605
  }
2606
+ this.byteAlignment = backendProps?.byteAlignment || 1;
2384
2607
  this.updateTimestamp = device.incrementTimestamp();
2385
2608
  }
2386
- /** Set sampler props associated with this texture */
2387
- setSampler(sampler) {
2388
- this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
2389
- }
2390
2609
  /**
2391
2610
  * Create a new texture with the same parameters and optionally a different size
2392
2611
  * @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.
@@ -2395,6 +2614,79 @@ or create a device with the 'debug: true' prop.`;
2395
2614
  clone(size) {
2396
2615
  return this.device.createTexture({ ...this.props, ...size });
2397
2616
  }
2617
+ /** Set sampler props associated with this texture */
2618
+ setSampler(sampler) {
2619
+ this.sampler = sampler instanceof Sampler ? sampler : this.device.createSampler(sampler);
2620
+ }
2621
+ /**
2622
+ * Calculates the memory layout of the texture, required when reading and writing data.
2623
+ * @return the memory layout of the texture, in particular bytesPerRow which includes required padding
2624
+ */
2625
+ computeMemoryLayout(options_ = {}) {
2626
+ const options = this._normalizeTextureReadOptions(options_);
2627
+ const { width = this.width, height = this.height, depthOrArrayLayers = this.depth } = options;
2628
+ const { format, byteAlignment } = this;
2629
+ return textureFormatDecoder.computeMemoryLayout({
2630
+ format,
2631
+ width,
2632
+ height,
2633
+ depth: depthOrArrayLayers,
2634
+ byteAlignment
2635
+ });
2636
+ }
2637
+ /**
2638
+ * Read the contents of a texture into a GPU Buffer.
2639
+ * @returns A Buffer containing the texture data.
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
+ * @note The application can call Buffer.readAsync()
2644
+ * @note If not supplied a buffer will be created and the application needs to call Buffer.destroy
2645
+ */
2646
+ readBuffer(options, buffer) {
2647
+ throw new Error("readBuffer not implemented");
2648
+ }
2649
+ /**
2650
+ * Reads data from a texture into an ArrayBuffer.
2651
+ * @returns An ArrayBuffer containing the texture data.
2652
+ *
2653
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2654
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2655
+ */
2656
+ readDataAsync(options) {
2657
+ throw new Error("readBuffer not implemented");
2658
+ }
2659
+ /**
2660
+ * Writes an GPU Buffer into a texture.
2661
+ *
2662
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2663
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2664
+ */
2665
+ writeBuffer(buffer, options) {
2666
+ throw new Error("readBuffer not implemented");
2667
+ }
2668
+ /**
2669
+ * Writes an array buffer into a texture.
2670
+ *
2671
+ * @note The memory layout of the texture data is determined by the texture format and dimensions.
2672
+ * @note The application can call Texture.computeMemoryLayout() to compute the layout.
2673
+ */
2674
+ writeData(data, options) {
2675
+ throw new Error("readBuffer not implemented");
2676
+ }
2677
+ // IMPLEMENTATION SPECIFIC
2678
+ /**
2679
+ * WebGL can read data synchronously.
2680
+ * @note While it is convenient, the performance penalty is very significant
2681
+ */
2682
+ readDataSyncWebGL(options) {
2683
+ throw new Error("readDataSyncWebGL not available");
2684
+ }
2685
+ /** Generate mipmaps (WebGL only) */
2686
+ generateMipmapsWebGL() {
2687
+ throw new Error("generateMipmapsWebGL not available");
2688
+ }
2689
+ // HELPERS
2398
2690
  /** Ensure we have integer coordinates */
2399
2691
  static normalizeProps(device, props) {
2400
2692
  const newProps = { ...props };
@@ -2407,7 +2699,6 @@ or create a device with the 'debug: true' prop.`;
2407
2699
  }
2408
2700
  return newProps;
2409
2701
  }
2410
- // HELPERS
2411
2702
  /** Initialize texture with supplied props */
2412
2703
  // eslint-disable-next-line max-statements
2413
2704
  _initializeData(data) {
@@ -2458,6 +2749,20 @@ or create a device with the 'debug: true' prop.`;
2458
2749
  options.height = Math.min(options.height, this.height - options.y);
2459
2750
  return options;
2460
2751
  }
2752
+ _normalizeTextureReadOptions(options_) {
2753
+ const { width, height } = this;
2754
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2755
+ options.width = Math.min(options.width, this.width - options.x);
2756
+ options.height = Math.min(options.height, this.height - options.y);
2757
+ return options;
2758
+ }
2759
+ _normalizeTextureWriteOptions(options_) {
2760
+ const { width, height } = this;
2761
+ const options = { ..._Texture.defaultTextureReadOptions, width, height, ...options_ };
2762
+ options.width = Math.min(options.width, this.width - options.x);
2763
+ options.height = Math.min(options.height, this.height - options.y);
2764
+ return options;
2765
+ }
2461
2766
  };
2462
2767
  var Texture = _Texture;
2463
2768
  /** The texture can be bound for use as a sampled texture in a shader */
@@ -2474,13 +2779,12 @@ or create a device with the 'debug: true' prop.`;
2474
2779
  __publicField(Texture, "TEXTURE", 4);
2475
2780
  /** @deprecated Use Texture.RENDER */
2476
2781
  __publicField(Texture, "RENDER_ATTACHMENT", 16);
2477
- /** Default options */
2478
2782
  __publicField(Texture, "defaultProps", {
2479
2783
  ...Resource.defaultProps,
2480
2784
  data: null,
2481
2785
  dimension: "2d",
2482
2786
  format: "rgba8unorm",
2483
- usage: _Texture.TEXTURE | _Texture.RENDER_ATTACHMENT | _Texture.COPY_DST,
2787
+ usage: _Texture.SAMPLE | _Texture.RENDER | _Texture.COPY_DST,
2484
2788
  width: void 0,
2485
2789
  height: void 0,
2486
2790
  depth: 1,
@@ -2517,6 +2821,16 @@ or create a device with the 'debug: true' prop.`;
2517
2821
  premultipliedAlpha: false,
2518
2822
  flipY: false
2519
2823
  });
2824
+ __publicField(Texture, "defaultTextureReadOptions", {
2825
+ x: 0,
2826
+ y: 0,
2827
+ z: 0,
2828
+ width: void 0,
2829
+ height: void 0,
2830
+ depthOrArrayLayers: 1,
2831
+ mipLevel: 0,
2832
+ aspect: "all"
2833
+ });
2520
2834
 
2521
2835
  // ../core/src/adapter/resources/texture-view.ts
2522
2836
  var _TextureView = class extends Resource {
@@ -2563,24 +2877,32 @@ or create a device with the 'debug: true' prop.`;
2563
2877
  const log2 = shaderLog.slice().sort((a, b) => a.lineNum - b.lineNum);
2564
2878
  switch (options?.showSourceCode || "no") {
2565
2879
  case "all":
2566
- let currentMessage = 0;
2880
+ let currentMessageIndex = 0;
2567
2881
  for (let lineNum = 1; lineNum <= lines.length; lineNum++) {
2568
- formattedLog += getNumberedLine(lines[lineNum - 1], lineNum, options);
2569
- while (log2.length > currentMessage && log2[currentMessage].lineNum === lineNum) {
2570
- const message = log2[currentMessage++];
2571
- formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2882
+ const line = lines[lineNum - 1];
2883
+ const currentMessage = log2[currentMessageIndex];
2884
+ if (line && currentMessage) {
2885
+ formattedLog += getNumberedLine(line, lineNum, options);
2886
+ }
2887
+ while (log2.length > currentMessageIndex && currentMessage.lineNum === lineNum) {
2888
+ const message = log2[currentMessageIndex++];
2889
+ if (message) {
2890
+ formattedLog += formatCompilerMessage(message, lines, message.lineNum, {
2891
+ ...options,
2892
+ inlineSource: false
2893
+ });
2894
+ }
2895
+ }
2896
+ }
2897
+ while (log2.length > currentMessageIndex) {
2898
+ const message = log2[currentMessageIndex++];
2899
+ if (message) {
2900
+ formattedLog += formatCompilerMessage(message, [], 0, {
2572
2901
  ...options,
2573
2902
  inlineSource: false
2574
2903
  });
2575
2904
  }
2576
2905
  }
2577
- while (log2.length > currentMessage) {
2578
- const message = log2[currentMessage++];
2579
- formattedLog += formatCompilerMessage(message, [], 0, {
2580
- ...options,
2581
- inlineSource: false
2582
- });
2583
- }
2584
2906
  return formattedLog;
2585
2907
  case "issues":
2586
2908
  case "no":
@@ -2602,8 +2924,8 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
2602
2924
 
2603
2925
  `;
2604
2926
  }
2605
- const color = message.type === "error" ? "red" : "#8B4000";
2606
- 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}`;
2927
+ const color = message.type === "error" ? "red" : "orange";
2928
+ 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}`;
2607
2929
  }
2608
2930
  function getNumberedLines(lines, lineNum, options) {
2609
2931
  let numberedLines = "";
@@ -2689,29 +3011,34 @@ ${numberedLines}${positionIndicator}${message.type.toUpperCase()}: ${message.mes
2689
3011
  }
2690
3012
  const shaderName = shaderId;
2691
3013
  const shaderTitle = `${this.stage} shader "${shaderName}"`;
2692
- let htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
3014
+ const htmlLog = formatCompilerLog(messages, this.source, { showSourceCode: "all", html: true });
2693
3015
  const translatedSource = this.getTranslatedSource();
3016
+ const container = document.createElement("div");
3017
+ container.innerHTML = `<h1>Compilation error in ${shaderTitle}</h1>
3018
+ <div style="display:flex;position:fixed;top:10px;right:20px;gap:2px;">
3019
+ <button id="copy">Copy source</button><br/>
3020
+ <button id="close">Close</button>
3021
+ </div>
3022
+ <code><pre>${htmlLog}</pre></code>`;
2694
3023
  if (translatedSource) {
2695
- htmlLog += `<br /><br /><h1>Translated Source</h1><br /><br /><code style="user-select:text;"><pre>${translatedSource}</pre></code>`;
2696
- }
2697
- const button = document.createElement("Button");
2698
- button.innerHTML = `
2699
- <h1>Compilation error in ${shaderTitle}</h1><br /><br />
2700
- <code style="user-select:text;"><pre>
2701
- ${htmlLog}
2702
- </pre></code>`;
2703
- button.style.top = "10px";
2704
- button.style.left = "10px";
2705
- button.style.position = "absolute";
2706
- button.style.zIndex = "9999";
2707
- button.style.width = "100%";
2708
- button.style.textAlign = "left";
2709
- document.body.appendChild(button);
2710
- const errors = document.getElementsByClassName("luma-compiler-log-error");
2711
- errors[0]?.scrollIntoView();
2712
- button.onclick = () => {
2713
- const dataURI = `data:text/plain,${encodeURIComponent(this.source)}`;
2714
- navigator.clipboard.writeText(dataURI);
3024
+ container.innerHTML += `<br /><h1>Translated Source</h1><br /><br /><code><pre>${translatedSource}</pre></code>`;
3025
+ }
3026
+ container.style.top = "0";
3027
+ container.style.left = "0";
3028
+ container.style.background = "white";
3029
+ container.style.position = "fixed";
3030
+ container.style.zIndex = "9999";
3031
+ container.style.maxWidth = "100vw";
3032
+ container.style.maxHeight = "100vh";
3033
+ container.style.overflowY = "auto";
3034
+ document.body.appendChild(container);
3035
+ const error = container.querySelector(".luma-compiler-log-error");
3036
+ error?.scrollIntoView();
3037
+ container.querySelector("button#close").onclick = () => {
3038
+ container.remove();
3039
+ };
3040
+ container.querySelector("button#copy").onclick = () => {
3041
+ navigator.clipboard.writeText(this.source);
2715
3042
  };
2716
3043
  }
2717
3044
  };
@@ -2731,7 +3058,7 @@ ${htmlLog}
2731
3058
  function getShaderName(shader, defaultName = "unnamed") {
2732
3059
  const SHADER_NAME_REGEXP = /#define[\s*]SHADER_NAME[\s*]([A-Za-z0-9_-]+)[\s*]/;
2733
3060
  const match = SHADER_NAME_REGEXP.exec(shader);
2734
- return match ? match[1] : defaultName;
3061
+ return match?.[1] ?? defaultName;
2735
3062
  }
2736
3063
 
2737
3064
  // ../core/src/adapter/resources/framebuffer.ts
@@ -2757,7 +3084,12 @@ ${htmlLog}
2757
3084
  (colorAttachment) => colorAttachment.texture.clone(size)
2758
3085
  );
2759
3086
  const depthStencilAttachment = this.depthStencilAttachment && this.depthStencilAttachment.texture.clone(size);
2760
- return this.device.createFramebuffer({ ...this.props, colorAttachments, depthStencilAttachment });
3087
+ return this.device.createFramebuffer({
3088
+ ...this.props,
3089
+ ...size,
3090
+ colorAttachments,
3091
+ depthStencilAttachment
3092
+ });
2761
3093
  }
2762
3094
  resize(size) {
2763
3095
  let updateSize = !size;
@@ -2832,17 +3164,15 @@ ${htmlLog}
2832
3164
  * and destroys existing textures if owned
2833
3165
  */
2834
3166
  resizeAttachments(width, height) {
2835
- for (let i = 0; i < this.colorAttachments.length; ++i) {
2836
- if (this.colorAttachments[i]) {
2837
- const resizedTexture = this.colorAttachments[i].texture.clone({
2838
- width,
2839
- height
2840
- });
2841
- this.destroyAttachedResource(this.colorAttachments[i]);
2842
- this.colorAttachments[i] = resizedTexture.view;
2843
- this.attachResource(resizedTexture.view);
2844
- }
2845
- }
3167
+ this.colorAttachments.forEach((colorAttachment, i) => {
3168
+ const resizedTexture = colorAttachment.texture.clone({
3169
+ width,
3170
+ height
3171
+ });
3172
+ this.destroyAttachedResource(colorAttachment);
3173
+ this.colorAttachments[i] = resizedTexture.view;
3174
+ this.attachResource(resizedTexture.view);
3175
+ });
2846
3176
  if (this.depthStencilAttachment) {
2847
3177
  const resizedTexture = this.depthStencilAttachment.texture.clone({
2848
3178
  width,
@@ -3353,6 +3683,18 @@ ${htmlLog}
3353
3683
  count: void 0
3354
3684
  });
3355
3685
 
3686
+ // ../core/src/adapter/resources/fence.ts
3687
+ var _Fence = class extends Resource {
3688
+ [Symbol.toStringTag] = "WEBGLFence";
3689
+ constructor(device, props = {}) {
3690
+ super(device, props, _Fence.defaultProps);
3691
+ }
3692
+ };
3693
+ var Fence = _Fence;
3694
+ __publicField(Fence, "defaultProps", {
3695
+ ...Resource.defaultProps
3696
+ });
3697
+
3356
3698
  // ../core/src/adapter/resources/pipeline-layout.ts
3357
3699
  var _PipelineLayout = class extends Resource {
3358
3700
  get [Symbol.toStringTag]() {
@@ -3384,17 +3726,6 @@ ${htmlLog}
3384
3726
  return new Type(scratchArrayBuffer, 0, length);
3385
3727
  }
3386
3728
 
3387
- // ../core/src/utils/is-array.ts
3388
- function isTypedArray(value) {
3389
- return ArrayBuffer.isView(value) && !(value instanceof DataView);
3390
- }
3391
- function isNumberArray(value) {
3392
- if (Array.isArray(value)) {
3393
- return value.length === 0 || typeof value[0] === "number";
3394
- }
3395
- return isTypedArray(value);
3396
- }
3397
-
3398
3729
  // ../core/src/portable/uniform-buffer-layout.ts
3399
3730
  var minBufferSize = 1024;
3400
3731
  var UniformBufferLayout = class {
@@ -3405,67 +3736,114 @@ ${htmlLog}
3405
3736
  constructor(uniformTypes, uniformSizes = {}) {
3406
3737
  let size = 0;
3407
3738
  for (const [key, uniformType] of Object.entries(uniformTypes)) {
3408
- const typeAndComponents = getVariableShaderTypeInfo(uniformType);
3409
- const { type, components } = typeAndComponents;
3410
- const count = components * (uniformSizes?.[key] ?? 1);
3411
- size = alignTo(size, count);
3412
- const offset = size;
3413
- size += count;
3414
- this.layout[key] = { type, size: count, offset };
3739
+ size = this._addToLayout(key, uniformType, size, uniformSizes?.[key]);
3415
3740
  }
3416
3741
  size += (4 - size % 4) % 4;
3417
- const actualByteLength = size * 4;
3418
- this.byteLength = Math.max(actualByteLength, minBufferSize);
3742
+ this.byteLength = Math.max(size * 4, minBufferSize);
3743
+ }
3744
+ /** Does this layout have a field with specified name */
3745
+ has(name2) {
3746
+ return Boolean(this.layout[name2]);
3747
+ }
3748
+ /** Get offset and size for a field with specified name */
3749
+ get(name2) {
3750
+ const layout = this.layout[name2];
3751
+ return layout;
3419
3752
  }
3420
3753
  /** Get the data for the complete buffer */
3421
3754
  getData(uniformValues) {
3422
- const arrayBuffer2 = getScratchArrayBuffer(this.byteLength);
3755
+ const buffer = getScratchArrayBuffer(this.byteLength);
3423
3756
  const typedArrays = {
3424
- i32: new Int32Array(arrayBuffer2),
3425
- u32: new Uint32Array(arrayBuffer2),
3426
- f32: new Float32Array(arrayBuffer2),
3427
- // TODO not implemented
3428
- f16: new Uint16Array(arrayBuffer2)
3757
+ i32: new Int32Array(buffer),
3758
+ u32: new Uint32Array(buffer),
3759
+ f32: new Float32Array(buffer),
3760
+ f16: new Uint16Array(buffer)
3429
3761
  };
3430
3762
  for (const [name2, value] of Object.entries(uniformValues)) {
3431
- const uniformLayout = this.layout[name2];
3432
- if (!uniformLayout) {
3433
- log.warn(`Supplied uniform value ${name2} not present in uniform block layout`)();
3434
- continue;
3763
+ this._writeCompositeValue(typedArrays, name2, value);
3764
+ }
3765
+ return new Uint8Array(buffer, 0, this.byteLength);
3766
+ }
3767
+ // Recursively add a uniform to the layout
3768
+ _addToLayout(name2, type, offset, count = 1) {
3769
+ if (typeof type === "string") {
3770
+ const info = getVariableShaderTypeInfo(type);
3771
+ const sizeInSlots = info.components * count;
3772
+ const alignedOffset = alignTo(offset, info.components);
3773
+ this.layout[name2] = {
3774
+ offset: alignedOffset,
3775
+ size: sizeInSlots,
3776
+ type: info.type
3777
+ };
3778
+ return alignedOffset + sizeInSlots;
3779
+ }
3780
+ if (Array.isArray(type)) {
3781
+ const elementType = type[0];
3782
+ const length = count > 1 ? count : type.length > 1 ? type[1] : 1;
3783
+ let arrayOffset = alignTo(offset, 4);
3784
+ for (let i = 0; i < length; i++) {
3785
+ arrayOffset = this._addToLayout(`${name2}[${i}]`, elementType, arrayOffset);
3435
3786
  }
3436
- const { type, size, offset } = uniformLayout;
3437
- const typedArray = typedArrays[type];
3438
- if (size === 1) {
3439
- if (typeof value !== "number" && typeof value !== "boolean") {
3440
- log.warn(
3441
- `Supplied value for single component uniform ${name2} is not a number: ${value}`
3442
- )();
3443
- continue;
3444
- }
3445
- typedArray[offset] = Number(value);
3446
- } else {
3447
- if (!isNumberArray(value)) {
3448
- log.warn(
3449
- `Supplied value for multi component / array uniform ${name2} is not a numeric array: ${value}`
3450
- )();
3451
- continue;
3452
- }
3453
- typedArray.set(value, offset);
3787
+ return arrayOffset;
3788
+ }
3789
+ if (typeof type === "object") {
3790
+ let structOffset = alignTo(offset, 4);
3791
+ for (const [memberName, memberType] of Object.entries(type)) {
3792
+ structOffset = this._addToLayout(`${name2}.${memberName}`, memberType, structOffset);
3454
3793
  }
3794
+ return structOffset;
3455
3795
  }
3456
- return new Uint8Array(arrayBuffer2, 0, this.byteLength);
3796
+ throw new Error(`Unsupported CompositeShaderType for ${name2}`);
3457
3797
  }
3458
- /** Does this layout have a field with specified name */
3459
- has(name2) {
3460
- return Boolean(this.layout[name2]);
3798
+ _writeCompositeValue(typedArrays, baseName, value) {
3799
+ if (this.layout[baseName]) {
3800
+ this._writeToBuffer(typedArrays, baseName, value);
3801
+ return;
3802
+ }
3803
+ if (Array.isArray(value)) {
3804
+ for (let i = 0; i < value.length; i++) {
3805
+ const element = value[i];
3806
+ const indexedName = `${baseName}[${i}]`;
3807
+ this._writeCompositeValue(typedArrays, indexedName, element);
3808
+ }
3809
+ return;
3810
+ }
3811
+ if (typeof value === "object" && value !== null) {
3812
+ for (const [key, subValue] of Object.entries(value)) {
3813
+ const nestedName = `${baseName}.${key}`;
3814
+ this._writeCompositeValue(typedArrays, nestedName, subValue);
3815
+ }
3816
+ return;
3817
+ }
3818
+ log.warn(`Unsupported uniform value for ${baseName}:`, value)();
3461
3819
  }
3462
- /** Get offset and size for a field with specified name */
3463
- get(name2) {
3820
+ _writeToBuffer(typedArrays, name2, value) {
3464
3821
  const layout = this.layout[name2];
3465
- return layout;
3822
+ if (!layout) {
3823
+ log.warn(`Uniform ${name2} not found in layout`)();
3824
+ return;
3825
+ }
3826
+ const { type, size, offset } = layout;
3827
+ const array = typedArrays[type];
3828
+ if (size === 1) {
3829
+ array[offset] = Number(value);
3830
+ } else {
3831
+ array.set(value, offset);
3832
+ }
3466
3833
  }
3467
3834
  };
3468
3835
 
3836
+ // ../core/src/utils/is-array.ts
3837
+ function isTypedArray(value) {
3838
+ return ArrayBuffer.isView(value) && !(value instanceof DataView);
3839
+ }
3840
+ function isNumberArray(value) {
3841
+ if (Array.isArray(value)) {
3842
+ return value.length === 0 || typeof value[0] === "number";
3843
+ }
3844
+ return isTypedArray(value);
3845
+ }
3846
+
3469
3847
  // ../core/src/utils/array-equal.ts
3470
3848
  function arrayEqual(a, b, limit = 16) {
3471
3849
  if (a !== b) {
@@ -3657,6 +4035,44 @@ ${htmlLog}
3657
4035
  }
3658
4036
  };
3659
4037
 
4038
+ // ../core/src/shadertypes/textures/texture-layout.ts
4039
+ function getTextureImageView(arrayBuffer2, memoryLayout, format, image = 0) {
4040
+ const formatInfo = textureFormatDecoder.getInfo(format);
4041
+ const bytesPerComponent = formatInfo.bytesPerPixel / formatInfo.components;
4042
+ const { bytesPerImage } = memoryLayout;
4043
+ const offset = bytesPerImage * image;
4044
+ const totalPixels = memoryLayout.bytesPerImage / bytesPerComponent;
4045
+ switch (format) {
4046
+ case "rgba8unorm":
4047
+ case "bgra8unorm":
4048
+ case "rgba8uint":
4049
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
4050
+ case "r8unorm":
4051
+ return new Uint8Array(arrayBuffer2, offset, totalPixels);
4052
+ case "r16uint":
4053
+ case "rgba16uint":
4054
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
4055
+ case "r32uint":
4056
+ case "rgba32uint":
4057
+ return new Uint32Array(arrayBuffer2, offset, totalPixels);
4058
+ case "r32float":
4059
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
4060
+ case "rgba16float":
4061
+ return new Uint16Array(arrayBuffer2, offset, totalPixels);
4062
+ case "rgba32float":
4063
+ return new Float32Array(arrayBuffer2, offset, totalPixels);
4064
+ default:
4065
+ throw new Error(`Unsupported format: ${format}`);
4066
+ }
4067
+ }
4068
+ function setTextureImageData(arrayBuffer2, memoryLayout, format, data, image = 0) {
4069
+ const offset = 0;
4070
+ const totalPixels = memoryLayout.bytesPerImage / memoryLayout.bytesPerPixel;
4071
+ const subArray = data.subarray(0, totalPixels);
4072
+ const typedArray = getTextureImageView(arrayBuffer2, memoryLayout, format, image);
4073
+ typedArray.set(subArray, offset);
4074
+ }
4075
+
3660
4076
  // ../core/src/shadertypes/textures/pixel-utils.ts
3661
4077
  function readPixel(pixelData, x, y, bitsPerChannel) {
3662
4078
  if (x < 0 || x >= pixelData.width || y < 0 || y >= pixelData.height) {
@@ -3667,7 +4083,7 @@ ${htmlLog}
3667
4083
  let bitOffsetWithinPixel = 0;
3668
4084
  const channels = [];
3669
4085
  for (let i = 0; i < 4; i++) {
3670
- const bits = bitsPerChannel[i];
4086
+ const bits = bitsPerChannel[i] ?? 0;
3671
4087
  if (bits <= 0) {
3672
4088
  channels.push(0);
3673
4089
  } else {
@@ -3676,14 +4092,14 @@ ${htmlLog}
3676
4092
  bitOffsetWithinPixel += bits;
3677
4093
  }
3678
4094
  }
3679
- return [channels[0], channels[1], channels[2], channels[3]];
4095
+ return [channels[0] ?? 0, channels[1] ?? 0, channels[2] ?? 0, channels[3] ?? 0];
3680
4096
  }
3681
4097
  function writePixel(dataView, bitOffset, bitsPerChannel, pixel) {
3682
4098
  let currentBitOffset = bitOffset;
3683
4099
  for (let channel = 0; channel < 4; channel++) {
3684
- const bits = bitsPerChannel[channel];
4100
+ const bits = bitsPerChannel[channel] ?? 0;
3685
4101
  const maxValue = (1 << bits) - 1;
3686
- const channelValue = pixel[channel] & maxValue;
4102
+ const channelValue = (pixel[channel] ?? 0) & maxValue;
3687
4103
  writeBitsToDataView(dataView, currentBitOffset, bits, channelValue);
3688
4104
  currentBitOffset += bits;
3689
4105
  }