lighteningcards 2.1.0 → 2.1.2

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/index.cjs.js CHANGED
@@ -2959,8 +2959,8 @@ const _TextureStyle = class _TextureStyle extends EventEmitter {
2959
2959
  return this._sharedResourceId || this._generateResourceId();
2960
2960
  }
2961
2961
  update() {
2962
- this.emit("change", this);
2963
2962
  this._sharedResourceId = null;
2963
+ this.emit("change", this);
2964
2964
  }
2965
2965
  _generateResourceId() {
2966
2966
  const bigKey = `${this.addressModeU}-${this.addressModeV}-${this.addressModeW}-${this.magFilter}-${this.minFilter}-${this.mipmapFilter}-${this.lodMinClamp}-${this.lodMaxClamp}-${this.compare}-${this._maxAnisotropy}`;
@@ -2989,6 +2989,10 @@ const _TextureSource = class _TextureSource extends EventEmitter {
2989
2989
  constructor(options = {}) {
2990
2990
  super();
2991
2991
  this.options = options;
2992
+ /** @internal */
2993
+ this._gpuData = /* @__PURE__ */ Object.create(null);
2994
+ /** @internal */
2995
+ this._gcLastUsed = -1;
2992
2996
  /** unique id for this Texture source */
2993
2997
  this.uid = uid$1("textureSource");
2994
2998
  /**
@@ -3184,8 +3188,8 @@ const _TextureSource = class _TextureSource extends EventEmitter {
3184
3188
  /** Destroys this texture source */
3185
3189
  destroy() {
3186
3190
  this.destroyed = true;
3191
+ this.unload();
3187
3192
  this.emit("destroy", this);
3188
- this.emit("change", this);
3189
3193
  if (this._style) {
3190
3194
  this._style.destroy();
3191
3195
  this._style = null;
@@ -3202,6 +3206,10 @@ const _TextureSource = class _TextureSource extends EventEmitter {
3202
3206
  this._resourceId = uid$1("resource");
3203
3207
  this.emit("change", this);
3204
3208
  this.emit("unload", this);
3209
+ for (const key in this._gpuData) {
3210
+ this._gpuData[key]?.destroy?.();
3211
+ }
3212
+ this._gpuData = /* @__PURE__ */ Object.create(null);
3205
3213
  }
3206
3214
  /** the width of the resource. This is the REAL pure number, not accounting resolution */
3207
3215
  get resourceWidth() {
@@ -3568,6 +3576,7 @@ class Texture extends EventEmitter {
3568
3576
  */
3569
3577
  destroy(destroySource = false) {
3570
3578
  if (this._source) {
3579
+ this._source.off("resize", this.update, this);
3571
3580
  if (destroySource) {
3572
3581
  this._source.destroy();
3573
3582
  this._source = null;
@@ -5403,6 +5412,7 @@ class Pool {
5403
5412
  item = this._pool[--this._index];
5404
5413
  } else {
5405
5414
  item = new this._classType();
5415
+ this._count++;
5406
5416
  }
5407
5417
  item.init?.(data);
5408
5418
  return item;
@@ -6860,6 +6870,13 @@ class Container extends EventEmitter {
6860
6870
  this.measurable = true;
6861
6871
  /** @private */
6862
6872
  this.isSimple = true;
6873
+ /**
6874
+ * The RenderLayer this container belongs to, if any.
6875
+ * If it belongs to a RenderLayer, it will be rendered from the RenderLayer's position in the scene.
6876
+ * @readonly
6877
+ * @advanced
6878
+ */
6879
+ this.parentRenderLayer = null;
6863
6880
  // / /////////////Transform related props//////////////
6864
6881
  // used by the transform system to check if a container needs to be updated that frame
6865
6882
  // if the tick matches the current transform system tick, it is not updated again
@@ -7935,7 +7952,6 @@ extensions.mixin(
7935
7952
  );
7936
7953
 
7937
7954
  class ViewContainer extends Container {
7938
- // eslint-disable-next-line @typescript-eslint/no-useless-constructor
7939
7955
  constructor(options) {
7940
7956
  super(options);
7941
7957
  /** @internal */
@@ -7948,8 +7964,13 @@ class ViewContainer extends Container {
7948
7964
  this._lastUsed = -1;
7949
7965
  /** @internal */
7950
7966
  this._gpuData = /* @__PURE__ */ Object.create(null);
7967
+ /** If set to true, the resource will be garbage collected automatically when it is not used. */
7968
+ this.autoGarbageCollect = true;
7969
+ /** @internal */
7970
+ this._gcLastUsed = -1;
7951
7971
  this._bounds = new Bounds(0, 1, 0, 0);
7952
7972
  this._boundsDirty = true;
7973
+ this.autoGarbageCollect = options.autoGarbageCollect ?? true;
7953
7974
  }
7954
7975
  /**
7955
7976
  * The local bounds of the view in its own coordinate space.
@@ -8018,13 +8039,19 @@ class ViewContainer extends Container {
8018
8039
  renderGroup.onChildViewUpdate(this);
8019
8040
  }
8020
8041
  }
8042
+ /** Unloads the GPU data from the view. */
8043
+ unload() {
8044
+ this.emit("unload", this);
8045
+ for (const key in this._gpuData) {
8046
+ this._gpuData[key]?.destroy();
8047
+ }
8048
+ this._gpuData = /* @__PURE__ */ Object.create(null);
8049
+ this.onViewUpdate();
8050
+ }
8021
8051
  destroy(options) {
8052
+ this.unload();
8022
8053
  super.destroy(options);
8023
8054
  this._bounds = null;
8024
- for (const key in this._gpuData) {
8025
- this._gpuData[key].destroy?.();
8026
- }
8027
- this._gpuData = null;
8028
8055
  }
8029
8056
  /**
8030
8057
  * Collects renderables for the view container.
@@ -8216,7 +8243,6 @@ class Sprite extends ViewContainer {
8216
8243
  this._visualBounds = null;
8217
8244
  this._bounds = null;
8218
8245
  this._anchor = null;
8219
- this._gpuData = null;
8220
8246
  }
8221
8247
  /**
8222
8248
  * The anchor sets the origin point of the sprite. The default value is taken from the {@link Texture}
@@ -8429,6 +8455,8 @@ class AlphaMask {
8429
8455
  this.mask.measurable = false;
8430
8456
  }
8431
8457
  reset() {
8458
+ if (this.mask === null)
8459
+ return;
8432
8460
  this.mask.measurable = true;
8433
8461
  this.mask = null;
8434
8462
  }
@@ -8486,6 +8514,8 @@ class StencilMask {
8486
8514
  this.mask.measurable = false;
8487
8515
  }
8488
8516
  reset() {
8517
+ if (this.mask === null)
8518
+ return;
8489
8519
  this.mask.measurable = true;
8490
8520
  this.mask.includeInBuild = true;
8491
8521
  this.mask = null;
@@ -8742,7 +8772,6 @@ const _Ticker = class _Ticker {
8742
8772
  *
8743
8773
  * This is NOT in milliseconds - it's a scalar multiplier for frame-independent animations.
8744
8774
  * For actual milliseconds, use {@link Ticker#deltaMS}.
8745
- * @member {number}
8746
8775
  * @example
8747
8776
  * ```ts
8748
8777
  * // Frame-independent animation using deltaTime scalar
@@ -8758,7 +8787,6 @@ const _Ticker = class _Ticker {
8758
8787
  * Similar to performance.now() timestamp format.
8759
8788
  *
8760
8789
  * Used internally for calculating time deltas between frames.
8761
- * @member {number}
8762
8790
  * @example
8763
8791
  * ```ts
8764
8792
  * ticker.add((ticker) => {
@@ -10776,7 +10804,12 @@ class Resolver {
10776
10804
  const assetArray = convertToList(assets);
10777
10805
  assetArray.forEach((asset) => {
10778
10806
  const { src } = asset;
10779
- let { data, format, loadParser: userDefinedLoadParser, parser: userDefinedParser } = asset;
10807
+ let {
10808
+ data,
10809
+ format,
10810
+ loadParser: userDefinedLoadParser,
10811
+ parser: userDefinedParser
10812
+ } = asset;
10780
10813
  const srcsToUse = convertToList(src).map((src2) => {
10781
10814
  if (typeof src2 === "string") {
10782
10815
  return createStringVariations(src2);
@@ -10786,18 +10819,18 @@ class Resolver {
10786
10819
  const aliasesToUse = this.getAlias(asset);
10787
10820
  Array.isArray(aliasesToUse) ? aliasesToUse.forEach(keyCheck) : keyCheck(aliasesToUse);
10788
10821
  const resolvedAssets = [];
10822
+ const parseUrl = (url) => {
10823
+ const parser = this._parsers.find((p) => p.test(url));
10824
+ return {
10825
+ ...parser?.parse(url),
10826
+ src: url
10827
+ };
10828
+ };
10789
10829
  srcsToUse.forEach((srcs) => {
10790
10830
  srcs.forEach((src2) => {
10791
10831
  let formattedAsset = {};
10792
10832
  if (typeof src2 !== "object") {
10793
- formattedAsset.src = src2;
10794
- for (let i = 0; i < this._parsers.length; i++) {
10795
- const parser = this._parsers[i];
10796
- if (parser.test(src2)) {
10797
- formattedAsset = parser.parse(src2);
10798
- break;
10799
- }
10800
- }
10833
+ formattedAsset = parseUrl(src2);
10801
10834
  } else {
10802
10835
  data = src2.data ?? data;
10803
10836
  format = src2.format ?? format;
@@ -10806,7 +10839,7 @@ class Resolver {
10806
10839
  userDefinedParser = src2.parser ?? userDefinedParser;
10807
10840
  }
10808
10841
  formattedAsset = {
10809
- ...formattedAsset,
10842
+ ...parseUrl(src2.src),
10810
10843
  ...src2
10811
10844
  };
10812
10845
  }
@@ -10818,7 +10851,8 @@ class Resolver {
10818
10851
  data,
10819
10852
  format,
10820
10853
  loadParser: userDefinedLoadParser,
10821
- parser: userDefinedParser
10854
+ parser: userDefinedParser,
10855
+ progressSize: asset.progressSize
10822
10856
  });
10823
10857
  resolvedAssets.push(formattedAsset);
10824
10858
  });
@@ -10978,7 +11012,7 @@ class Resolver {
10978
11012
  return `${url}${paramConnector}${this._defaultSearchParams}`;
10979
11013
  }
10980
11014
  _buildResolvedAsset(formattedAsset, data) {
10981
- const { aliases, data: assetData, loadParser, parser, format } = data;
11015
+ const { aliases, data: assetData, loadParser, parser, format, progressSize } = data;
10982
11016
  if (this._basePath || this._rootPath) {
10983
11017
  formattedAsset.src = path.toAbsolute(formattedAsset.src, this._basePath, this._rootPath);
10984
11018
  }
@@ -10988,6 +11022,9 @@ class Resolver {
10988
11022
  formattedAsset.loadParser = loadParser ?? formattedAsset.loadParser;
10989
11023
  formattedAsset.parser = parser ?? formattedAsset.parser;
10990
11024
  formattedAsset.format = format ?? formattedAsset.format ?? getUrlExtension(formattedAsset.src);
11025
+ if (progressSize !== void 0) {
11026
+ formattedAsset.progressSize = progressSize;
11027
+ }
10991
11028
  return formattedAsset;
10992
11029
  }
10993
11030
  }
@@ -11764,7 +11801,7 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11764
11801
  /** This is the dom element that will sit over the PixiJS element. This is where the div overlays will go. */
11765
11802
  this._div = null;
11766
11803
  /** A simple pool for storing divs. */
11767
- this._pool = [];
11804
+ this._pools = {};
11768
11805
  /** This is a tick used to check if an object is no longer being rendered. */
11769
11806
  this._renderId = 0;
11770
11807
  /** The array of currently active accessible items. */
@@ -11773,6 +11810,11 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11773
11810
  this._androidUpdateCount = 0;
11774
11811
  /** The frequency to update the div elements. */
11775
11812
  this._androidUpdateFrequency = 500;
11813
+ // eslint-disable-next-line @typescript-eslint/prefer-readonly
11814
+ this._isRunningTests = false;
11815
+ /** Bound function references for proper event listener removal */
11816
+ this._boundOnKeyDown = this._onKeyDown.bind(this);
11817
+ this._boundOnMouseMove = this._onMouseMove.bind(this);
11776
11818
  this._hookDiv = null;
11777
11819
  if (_mobileInfo.tablet || _mobileInfo.phone) {
11778
11820
  this._createTouchHook();
@@ -11796,12 +11838,19 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11796
11838
  return this._isMobileAccessibility;
11797
11839
  }
11798
11840
  /**
11799
- * The DOM element that will sit over the PixiJS element. This is where the div overlays will go.
11841
+ * Button element for handling touch hooks.
11800
11842
  * @readonly
11801
11843
  */
11802
11844
  get hookDiv() {
11803
11845
  return this._hookDiv;
11804
11846
  }
11847
+ /**
11848
+ * The DOM element that will sit over the PixiJS element. This is where the div overlays will go.
11849
+ * @readonly
11850
+ */
11851
+ get div() {
11852
+ return this._div;
11853
+ }
11805
11854
  /**
11806
11855
  * Creates the touch hooks.
11807
11856
  * @private
@@ -11858,12 +11907,10 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11858
11907
  });
11859
11908
  }
11860
11909
  if (this._activateOnTab) {
11861
- this._onKeyDown = this._onKeyDown.bind(this);
11862
- globalThis.addEventListener("keydown", this._onKeyDown, false);
11910
+ globalThis.addEventListener("keydown", this._boundOnKeyDown, false);
11863
11911
  }
11864
11912
  if (this._deactivateOnMouseMove) {
11865
- this._onMouseMove = this._onMouseMove.bind(this);
11866
- globalThis.document.addEventListener("mousemove", this._onMouseMove, true);
11913
+ globalThis.document.addEventListener("mousemove", this._boundOnMouseMove, true);
11867
11914
  }
11868
11915
  const canvas = this._renderer.view.canvas;
11869
11916
  if (!canvas.parentNode) {
@@ -11896,27 +11943,31 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11896
11943
  return;
11897
11944
  }
11898
11945
  this._isActive = false;
11899
- globalThis.document.removeEventListener("mousemove", this._onMouseMove, true);
11946
+ globalThis.document.removeEventListener("mousemove", this._boundOnMouseMove, true);
11900
11947
  if (this._activateOnTab) {
11901
- globalThis.addEventListener("keydown", this._onKeyDown, false);
11948
+ globalThis.addEventListener("keydown", this._boundOnKeyDown, false);
11902
11949
  }
11903
11950
  this._renderer.runners.postrender.remove(this);
11904
11951
  for (const child of this._children) {
11905
- if (child._accessibleDiv && child._accessibleDiv.parentNode) {
11952
+ if (child._accessibleDiv?.parentNode) {
11906
11953
  child._accessibleDiv.parentNode.removeChild(child._accessibleDiv);
11907
11954
  child._accessibleDiv = null;
11908
11955
  }
11909
11956
  child._accessibleActive = false;
11910
11957
  }
11911
- this._pool.forEach((div) => {
11912
- if (div.parentNode) {
11913
- div.parentNode.removeChild(div);
11914
- }
11915
- });
11916
- if (this._div && this._div.parentNode) {
11958
+ for (const accessibleType in this._pools) {
11959
+ const pool = this._pools[accessibleType];
11960
+ pool.forEach((div) => {
11961
+ if (div.parentNode) {
11962
+ div.parentNode.removeChild(div);
11963
+ }
11964
+ });
11965
+ delete this._pools[accessibleType];
11966
+ }
11967
+ if (this._div?.parentNode) {
11917
11968
  this._div.parentNode.removeChild(this._div);
11918
11969
  }
11919
- this._pool = [];
11970
+ this._pools = {};
11920
11971
  this._children = [];
11921
11972
  }
11922
11973
  /**
@@ -11958,9 +12009,6 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11958
12009
  this._deactivateOnMouseMove = mergedOptions.accessibilityOptions.deactivateOnMouseMove;
11959
12010
  if (mergedOptions.accessibilityOptions.enabledByDefault) {
11960
12011
  this._activate();
11961
- } else if (this._activateOnTab) {
11962
- this._onKeyDown = this._onKeyDown.bind(this);
11963
- globalThis.addEventListener("keydown", this._onKeyDown, false);
11964
12012
  }
11965
12013
  this._renderer.runners.postrender.remove(this);
11966
12014
  }
@@ -11978,7 +12026,7 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11978
12026
  return;
11979
12027
  }
11980
12028
  this._androidUpdateCount = now + this._androidUpdateFrequency;
11981
- if (!this._renderer.renderingToScreen || !this._renderer.view.canvas) {
12029
+ if ((!this._renderer.renderingToScreen || !this._renderer.view.canvas) && !this._isRunningTests) {
11982
12030
  return;
11983
12031
  }
11984
12032
  const activeIds = /* @__PURE__ */ new Set();
@@ -11995,7 +12043,8 @@ const _AccessibilitySystem = class _AccessibilitySystem {
11995
12043
  if (!activeIds.has(i)) {
11996
12044
  if (child._accessibleDiv && child._accessibleDiv.parentNode) {
11997
12045
  child._accessibleDiv.parentNode.removeChild(child._accessibleDiv);
11998
- this._pool.push(child._accessibleDiv);
12046
+ const pool = this._getPool(child.accessibleType);
12047
+ pool.push(child._accessibleDiv);
11999
12048
  child._accessibleDiv = null;
12000
12049
  }
12001
12050
  child._accessibleActive = false;
@@ -12064,8 +12113,14 @@ const _AccessibilitySystem = class _AccessibilitySystem {
12064
12113
  * @param {Container} container - The child to make accessible.
12065
12114
  */
12066
12115
  _addChild(container) {
12067
- let div = this._pool.pop();
12068
- if (!div) {
12116
+ const pool = this._getPool(container.accessibleType);
12117
+ let div = pool.pop();
12118
+ if (div) {
12119
+ div.innerHTML = "";
12120
+ div.removeAttribute("title");
12121
+ div.removeAttribute("aria-label");
12122
+ div.tabIndex = 0;
12123
+ } else {
12069
12124
  if (container.accessibleType === "button") {
12070
12125
  div = document.createElement("button");
12071
12126
  } else {
@@ -12208,12 +12263,14 @@ const _AccessibilitySystem = class _AccessibilitySystem {
12208
12263
  this._canvasObserver?.destroy();
12209
12264
  this._canvasObserver = null;
12210
12265
  this._div = null;
12211
- this._pool = null;
12266
+ this._pools = null;
12212
12267
  this._children = null;
12213
12268
  this._renderer = null;
12214
- if (this._activateOnTab) {
12215
- globalThis.removeEventListener("keydown", this._onKeyDown);
12216
- }
12269
+ this._hookDiv = null;
12270
+ globalThis.removeEventListener("keydown", this._boundOnKeyDown);
12271
+ this._boundOnKeyDown = null;
12272
+ globalThis.document.removeEventListener("mousemove", this._boundOnMouseMove, true);
12273
+ this._boundOnMouseMove = null;
12217
12274
  }
12218
12275
  /**
12219
12276
  * Enables or disables the accessibility system.
@@ -12231,6 +12288,12 @@ const _AccessibilitySystem = class _AccessibilitySystem {
12231
12288
  this._deactivate();
12232
12289
  }
12233
12290
  }
12291
+ _getPool(accessibleType) {
12292
+ if (!this._pools[accessibleType]) {
12293
+ this._pools[accessibleType] = [];
12294
+ }
12295
+ return this._pools[accessibleType];
12296
+ }
12234
12297
  };
12235
12298
  /** @ignore */
12236
12299
  _AccessibilitySystem.extension = {
@@ -12957,12 +13020,14 @@ class BindGroup {
12957
13020
  /**
12958
13021
  * Used internally to 'touch' each resource, to ensure that the GC
12959
13022
  * knows that all resources in this bind group are still being used.
13023
+ * @param now - The current time in milliseconds.
12960
13024
  * @param tick - The current tick.
12961
13025
  * @internal
12962
13026
  */
12963
- _touch(tick) {
13027
+ _touch(now, tick) {
12964
13028
  const resources = this.resources;
12965
13029
  for (const i in resources) {
13030
+ resources[i]._gcLastUsed = now;
12966
13031
  resources[i]._touched = tick;
12967
13032
  }
12968
13033
  }
@@ -13009,6 +13074,8 @@ class Shader extends EventEmitter {
13009
13074
  */
13010
13075
  this._uniformBindMap = /* @__PURE__ */ Object.create(null);
13011
13076
  this._ownedBindGroups = [];
13077
+ /** @internal */
13078
+ this._destroyed = false;
13012
13079
  let {
13013
13080
  gpuProgram,
13014
13081
  glProgram,
@@ -13136,6 +13203,9 @@ class Shader extends EventEmitter {
13136
13203
  * Make sure its not being used by other shaders!
13137
13204
  */
13138
13205
  destroy(destroyPrograms = false) {
13206
+ if (this._destroyed)
13207
+ return;
13208
+ this._destroyed = true;
13139
13209
  this.emit("destroy", this);
13140
13210
  if (destroyPrograms) {
13141
13211
  this.gpuProgram?.destroy();
@@ -13350,6 +13420,9 @@ const _Filter = class _Filter extends Shader {
13350
13420
  this.blendRequired = options.blendRequired;
13351
13421
  this.clipToViewport = options.clipToViewport;
13352
13422
  this.addResource("uTexture", 0, 1);
13423
+ if (options.blendRequired) {
13424
+ this.addResource("uBackTexture", 0, 3);
13425
+ }
13353
13426
  }
13354
13427
  /**
13355
13428
  * Applies the filter
@@ -14191,6 +14264,8 @@ const _AbstractRenderer = class _AbstractRenderer extends EventEmitter {
14191
14264
  */
14192
14265
  constructor(config) {
14193
14266
  super();
14267
+ /** The current tick of the renderer. */
14268
+ this.tick = 0;
14194
14269
  /** @internal */
14195
14270
  this.uid = uid$1("renderer");
14196
14271
  /** @internal */
@@ -14228,6 +14303,7 @@ const _AbstractRenderer = class _AbstractRenderer extends EventEmitter {
14228
14303
  this._initOptions = options;
14229
14304
  }
14230
14305
  render(args, deprecated) {
14306
+ this.tick++;
14231
14307
  let options = args;
14232
14308
  if (options instanceof Container) {
14233
14309
  options = { container: options };
@@ -14403,12 +14479,12 @@ const _AbstractRenderer = class _AbstractRenderer extends EventEmitter {
14403
14479
  destroy(options = false) {
14404
14480
  this.runners.destroy.items.reverse();
14405
14481
  this.runners.destroy.emit(options);
14406
- Object.values(this.runners).forEach((runner) => {
14407
- runner.destroy();
14408
- });
14409
14482
  if (options === true || typeof options === "object" && options.releaseGlobalResources) {
14410
14483
  GlobalResourceRegistry.release();
14411
14484
  }
14485
+ Object.values(this.runners).forEach((runner) => {
14486
+ runner.destroy();
14487
+ });
14412
14488
  this._systemsHash = null;
14413
14489
  this.renderPipes = null;
14414
14490
  }
@@ -14597,7 +14673,7 @@ async function autoDetectRenderer(options) {
14597
14673
  return renderer;
14598
14674
  }
14599
14675
 
14600
- const VERSION = "8.13.2";
14676
+ const VERSION = "8.15.0";
14601
14677
 
14602
14678
  class ApplicationInitHook {
14603
14679
  static init() {
@@ -14671,6 +14747,7 @@ const _Application = class _Application {
14671
14747
  */
14672
14748
  async init(options) {
14673
14749
  options = { ...options };
14750
+ this.stage || (this.stage = new Container());
14674
14751
  this.renderer = await autoDetectRenderer(options);
14675
14752
  _Application._plugins.forEach((plugin) => {
14676
14753
  plugin.init.call(this, options);
@@ -14836,6 +14913,7 @@ class ResizePlugin {
14836
14913
  this,
14837
14914
  "resizeTo",
14838
14915
  {
14916
+ configurable: true,
14839
14917
  set(dom) {
14840
14918
  globalThis.removeEventListener("resize", this.queueResize);
14841
14919
  this._resizeTo = dom;
@@ -14915,6 +14993,7 @@ class TickerPlugin {
14915
14993
  this,
14916
14994
  "ticker",
14917
14995
  {
14996
+ configurable: true,
14918
14997
  set(ticker) {
14919
14998
  if (this._ticker) {
14920
14999
  this._ticker.remove(this.render, this);
@@ -17840,13 +17919,15 @@ class ViewableBuffer {
17840
17919
  /** Destroys all buffer references. Do not use after calling this. */
17841
17920
  destroy() {
17842
17921
  this.rawBinaryData = null;
17922
+ this.uint32View = null;
17923
+ this.float32View = null;
17924
+ this.uint16View = null;
17843
17925
  this._int8View = null;
17844
17926
  this._uint8View = null;
17845
17927
  this._int16View = null;
17846
- this.uint16View = null;
17847
17928
  this._int32View = null;
17848
- this.uint32View = null;
17849
- this.float32View = null;
17929
+ this._float64Array = null;
17930
+ this._bigUint64Array = null;
17850
17931
  }
17851
17932
  /**
17852
17933
  * Returns the size of the given type in bytes.
@@ -18309,13 +18390,24 @@ const _Batcher = class _Batcher {
18309
18390
  indexBuffer[index++] = indicesOffset + indices[i + indexOffset] - attributeOffset;
18310
18391
  }
18311
18392
  }
18312
- destroy() {
18393
+ /**
18394
+ * Destroys the batch and its resources.
18395
+ * @param options - destruction options
18396
+ * @param options.shader - whether to destroy the associated shader
18397
+ */
18398
+ destroy(options = {}) {
18313
18399
  if (this.batches === null)
18314
18400
  return;
18315
- for (let i = 0; i < this.batches.length; i++) {
18401
+ for (let i = 0; i < this.batchIndex; i++) {
18316
18402
  returnBatchToPool(this.batches[i]);
18317
18403
  }
18318
18404
  this.batches = null;
18405
+ this.geometry.destroy(true);
18406
+ this.geometry = null;
18407
+ if (options.shader) {
18408
+ this.shader?.destroy();
18409
+ this.shader = null;
18410
+ }
18319
18411
  for (let i = 0; i < this._elements.length; i++) {
18320
18412
  if (this._elements[i])
18321
18413
  this._elements[i]._batch = null;
@@ -18371,6 +18463,12 @@ class Buffer extends EventEmitter {
18371
18463
  * emits when the buffer is destroyed. letting the renderer know that it needs to destroy the buffer on the GPU
18372
18464
  * @event destroy
18373
18465
  */
18466
+ /** @internal */
18467
+ this._gpuData = /* @__PURE__ */ Object.create(null);
18468
+ /** @internal */
18469
+ this._gcLastUsed = -1;
18470
+ /** If set to true, the buffer will be garbage collected automatically when it is not used. */
18471
+ this.autoGarbageCollect = true;
18374
18472
  /** a unique id for this uniform group used through the renderer */
18375
18473
  this.uid = uid$1("buffer");
18376
18474
  /**
@@ -18486,9 +18584,18 @@ class Buffer extends EventEmitter {
18486
18584
  this._updateID++;
18487
18585
  this.emit("update", this);
18488
18586
  }
18587
+ /** Unloads the buffer from the GPU */
18588
+ unload() {
18589
+ this.emit("unload", this);
18590
+ for (const key in this._gpuData) {
18591
+ this._gpuData[key]?.destroy();
18592
+ }
18593
+ this._gpuData = /* @__PURE__ */ Object.create(null);
18594
+ }
18489
18595
  /** Destroys the buffer */
18490
18596
  destroy() {
18491
18597
  this.destroyed = true;
18598
+ this.unload();
18492
18599
  this.emit("destroy", this);
18493
18600
  this.emit("change", this);
18494
18601
  this._data = null;
@@ -18570,6 +18677,12 @@ class Geometry extends EventEmitter {
18570
18677
  */
18571
18678
  constructor(options = {}) {
18572
18679
  super();
18680
+ /** @internal */
18681
+ this._gpuData = /* @__PURE__ */ Object.create(null);
18682
+ /** If set to true, the resource will be garbage collected automatically when it is not used. */
18683
+ this.autoGarbageCollect = true;
18684
+ /** @internal */
18685
+ this._gcLastUsed = -1;
18573
18686
  /** The unique id of the geometry. */
18574
18687
  this.uid = uid$1("geometry");
18575
18688
  /**
@@ -18665,6 +18778,14 @@ class Geometry extends EventEmitter {
18665
18778
  this._boundsDirty = false;
18666
18779
  return getGeometryBounds(this, "aPosition", this._bounds);
18667
18780
  }
18781
+ /** Unloads the geometry from the GPU. */
18782
+ unload() {
18783
+ this.emit("unload", this);
18784
+ for (const key in this._gpuData) {
18785
+ this._gpuData[key]?.destroy();
18786
+ }
18787
+ this._gpuData = /* @__PURE__ */ Object.create(null);
18788
+ }
18668
18789
  /**
18669
18790
  * destroys the geometry.
18670
18791
  * @param destroyBuffers - destroy the buffers associated with this geometry
@@ -18675,6 +18796,8 @@ class Geometry extends EventEmitter {
18675
18796
  if (destroyBuffers) {
18676
18797
  this.buffers.forEach((buffer) => buffer.destroy());
18677
18798
  }
18799
+ this.unload();
18800
+ this.indexBuffer?.destroy();
18678
18801
  this.attributes = null;
18679
18802
  this.buffers = null;
18680
18803
  this.indexBuffer = null;
@@ -19350,6 +19473,7 @@ class DefaultShader extends Shader {
19350
19473
  batchSamplers: getBatchSamplersUniformGroup(maxTextures)
19351
19474
  }
19352
19475
  });
19476
+ this.maxTextures = maxTextures;
19353
19477
  }
19354
19478
  }
19355
19479
 
@@ -19447,6 +19571,21 @@ const _DefaultBatcher = class _DefaultBatcher extends Batcher {
19447
19571
  uint32View[index + 22] = argb;
19448
19572
  uint32View[index + 23] = textureIdAndRound;
19449
19573
  }
19574
+ /**
19575
+ * Updates the maximum number of textures that can be used in the shader.
19576
+ * @param maxTextures - The maximum number of textures that can be used in the shader.
19577
+ * @internal
19578
+ */
19579
+ _updateMaxTextures(maxTextures) {
19580
+ if (this.shader.maxTextures === maxTextures)
19581
+ return;
19582
+ defaultShader = new DefaultShader(maxTextures);
19583
+ this.shader = defaultShader;
19584
+ }
19585
+ destroy() {
19586
+ this.shader = null;
19587
+ super.destroy();
19588
+ }
19450
19589
  };
19451
19590
  /** @ignore */
19452
19591
  _DefaultBatcher.extension = {
@@ -19457,6 +19596,51 @@ _DefaultBatcher.extension = {
19457
19596
  };
19458
19597
  let DefaultBatcher = _DefaultBatcher;
19459
19598
 
19599
+ class GCManagedHash {
19600
+ constructor(options) {
19601
+ // Exposed directly for GC system access
19602
+ this.items = /* @__PURE__ */ Object.create(null);
19603
+ const { renderer, type, onUnload, priority, name } = options;
19604
+ this._renderer = renderer;
19605
+ renderer.gc.addResourceHash(this, "items", type, priority ?? 0);
19606
+ this._onUnload = onUnload;
19607
+ this.name = name;
19608
+ }
19609
+ /**
19610
+ * Add an item to the hash. No-op if already added.
19611
+ * @param item
19612
+ * @returns true if the item was added, false if it was already in the hash
19613
+ */
19614
+ add(item) {
19615
+ if (this.items[item.uid])
19616
+ return false;
19617
+ this.items[item.uid] = item;
19618
+ item.once("unload", this.remove, this);
19619
+ item._gcLastUsed = this._renderer.gc.now;
19620
+ return true;
19621
+ }
19622
+ remove(item, ...args) {
19623
+ if (!this.items[item.uid])
19624
+ return;
19625
+ const gpuData = item._gpuData[this._renderer.uid];
19626
+ if (!gpuData)
19627
+ return;
19628
+ this._onUnload?.(item, ...args);
19629
+ gpuData.destroy();
19630
+ item._gpuData[this._renderer.uid] = null;
19631
+ this.items[item.uid] = null;
19632
+ }
19633
+ removeAll(...args) {
19634
+ Object.values(this.items).forEach((item) => item && this.remove(item, ...args));
19635
+ }
19636
+ destroy(...args) {
19637
+ this.removeAll(...args);
19638
+ this.items = /* @__PURE__ */ Object.create(null);
19639
+ this._renderer = null;
19640
+ this._onUnload = null;
19641
+ }
19642
+ }
19643
+
19460
19644
  function buildUvs(vertices, verticesStride, verticesOffset, uvs, uvsOffset, uvsStride, size, matrix = null) {
19461
19645
  let index = 0;
19462
19646
  verticesOffset *= verticesStride;
@@ -20424,15 +20608,36 @@ class GpuGraphicsContext {
20424
20608
  indices: []
20425
20609
  };
20426
20610
  }
20611
+ reset() {
20612
+ if (this.batches) {
20613
+ this.batches.forEach((batch) => {
20614
+ BigPool.return(batch);
20615
+ });
20616
+ }
20617
+ if (this.graphicsData) {
20618
+ BigPool.return(this.graphicsData);
20619
+ }
20620
+ this.isBatchable = false;
20621
+ this.context = null;
20622
+ this.batches.length = 0;
20623
+ this.geometryData.indices.length = 0;
20624
+ this.geometryData.vertices.length = 0;
20625
+ this.geometryData.uvs.length = 0;
20626
+ this.graphicsData = null;
20627
+ }
20628
+ destroy() {
20629
+ this.reset();
20630
+ this.batches = null;
20631
+ this.geometryData = null;
20632
+ }
20427
20633
  }
20428
20634
  class GraphicsContextRenderData {
20429
20635
  constructor() {
20430
20636
  this.instructions = new InstructionSet();
20431
20637
  }
20432
- init(maxTextures) {
20433
- this.batcher = new DefaultBatcher({
20434
- maxTextures
20435
- });
20638
+ init(options) {
20639
+ const maxTextures = options.maxTextures;
20640
+ this.batcher ? this.batcher._updateMaxTextures(maxTextures) : this.batcher = new DefaultBatcher({ maxTextures });
20436
20641
  this.instructions.reset();
20437
20642
  }
20438
20643
  /**
@@ -20453,14 +20658,8 @@ class GraphicsContextRenderData {
20453
20658
  }
20454
20659
  const _GraphicsContextSystem = class _GraphicsContextSystem {
20455
20660
  constructor(renderer) {
20456
- // the root context batches, used to either make a batch or geometry
20457
- // all graphics use this as a base
20458
- this._gpuContextHash = {};
20459
- // used for non-batchable graphics
20460
- this._graphicsDataContextHash = /* @__PURE__ */ Object.create(null);
20461
20661
  this._renderer = renderer;
20462
- renderer.renderableGC.addManagedHash(this, "_gpuContextHash");
20463
- renderer.renderableGC.addManagedHash(this, "_graphicsDataContextHash");
20662
+ this._managedContexts = new GCManagedHash({ renderer, type: "resource", name: "graphicsContext" });
20464
20663
  }
20465
20664
  /**
20466
20665
  * Runner init called, update the default options
@@ -20475,7 +20674,7 @@ const _GraphicsContextSystem = class _GraphicsContextSystem {
20475
20674
  * @internal
20476
20675
  */
20477
20676
  getContextRenderData(context) {
20478
- return this._graphicsDataContextHash[context.uid] || this._initContextRenderData(context);
20677
+ return context._gpuData[this._renderer.uid].graphicsData || this._initContextRenderData(context);
20479
20678
  }
20480
20679
  /**
20481
20680
  * Updates the GPU context for a given GraphicsContext.
@@ -20485,12 +20684,11 @@ const _GraphicsContextSystem = class _GraphicsContextSystem {
20485
20684
  * @internal
20486
20685
  */
20487
20686
  updateGpuContext(context) {
20488
- let gpuContext = this._gpuContextHash[context.uid] || this._initContext(context);
20489
- if (context.dirty) {
20490
- if (gpuContext) {
20491
- this._cleanGraphicsContextData(context);
20492
- } else {
20493
- gpuContext = this._initContext(context);
20687
+ const hasContext = !!context._gpuData[this._renderer.uid];
20688
+ const gpuContext = context._gpuData[this._renderer.uid] || this._initContext(context);
20689
+ if (context.dirty || !hasContext) {
20690
+ if (hasContext) {
20691
+ gpuContext.reset();
20494
20692
  }
20495
20693
  buildContextBatches(context, gpuContext);
20496
20694
  const batchMode = context.batchMode;
@@ -20513,13 +20711,15 @@ const _GraphicsContextSystem = class _GraphicsContextSystem {
20513
20711
  * @internal
20514
20712
  */
20515
20713
  getGpuContext(context) {
20516
- return this._gpuContextHash[context.uid] || this._initContext(context);
20714
+ return context._gpuData[this._renderer.uid] || this._initContext(context);
20517
20715
  }
20518
20716
  _initContextRenderData(context) {
20519
20717
  const graphicsData = BigPool.get(GraphicsContextRenderData, {
20520
20718
  maxTextures: this._renderer.limits.maxBatchableTextures
20521
20719
  });
20522
- const { batches, geometryData } = this._gpuContextHash[context.uid];
20720
+ const gpuContext = context._gpuData[this._renderer.uid];
20721
+ const { batches, geometryData } = gpuContext;
20722
+ gpuContext.graphicsData = graphicsData;
20523
20723
  const vertexSize = geometryData.vertices.length;
20524
20724
  const indexSize = geometryData.indices.length;
20525
20725
  for (let i = 0; i < batches.length; i++) {
@@ -20546,41 +20746,18 @@ const _GraphicsContextSystem = class _GraphicsContextSystem {
20546
20746
  this._renderer.limits.maxBatchableTextures
20547
20747
  );
20548
20748
  }
20549
- this._graphicsDataContextHash[context.uid] = graphicsData;
20550
20749
  return graphicsData;
20551
20750
  }
20552
20751
  _initContext(context) {
20553
20752
  const gpuContext = new GpuGraphicsContext();
20554
20753
  gpuContext.context = context;
20555
- this._gpuContextHash[context.uid] = gpuContext;
20556
- context.on("destroy", this.onGraphicsContextDestroy, this);
20557
- return this._gpuContextHash[context.uid];
20558
- }
20559
- onGraphicsContextDestroy(context) {
20560
- this._cleanGraphicsContextData(context);
20561
- context.off("destroy", this.onGraphicsContextDestroy, this);
20562
- this._gpuContextHash[context.uid] = null;
20563
- }
20564
- _cleanGraphicsContextData(context) {
20565
- const gpuContext = this._gpuContextHash[context.uid];
20566
- if (!gpuContext.isBatchable) {
20567
- if (this._graphicsDataContextHash[context.uid]) {
20568
- BigPool.return(this.getContextRenderData(context));
20569
- this._graphicsDataContextHash[context.uid] = null;
20570
- }
20571
- }
20572
- if (gpuContext.batches) {
20573
- gpuContext.batches.forEach((batch) => {
20574
- BigPool.return(batch);
20575
- });
20576
- }
20754
+ context._gpuData[this._renderer.uid] = gpuContext;
20755
+ this._managedContexts.add(context);
20756
+ return gpuContext;
20577
20757
  }
20578
20758
  destroy() {
20579
- for (const i in this._gpuContextHash) {
20580
- if (this._gpuContextHash[i]) {
20581
- this.onGraphicsContextDestroy(this._gpuContextHash[i].context);
20582
- }
20583
- }
20759
+ this._managedContexts.destroy();
20760
+ this._renderer = null;
20584
20761
  }
20585
20762
  };
20586
20763
  /** @ignore */
@@ -22403,7 +22580,7 @@ function renderChildren(svg, session, fillStyle, strokeStyle) {
22403
22580
  break;
22404
22581
  case "polygon":
22405
22582
  pointsString = svg.getAttribute("points");
22406
- points = pointsString.match(/\d+/g).map((n) => parseInt(n, 10));
22583
+ points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10));
22407
22584
  session.context.poly(points, true);
22408
22585
  if (fillStyle)
22409
22586
  session.context.fill(fillStyle);
@@ -22412,7 +22589,7 @@ function renderChildren(svg, session, fillStyle, strokeStyle) {
22412
22589
  break;
22413
22590
  case "polyline":
22414
22591
  pointsString = svg.getAttribute("points");
22415
- points = pointsString.match(/\d+/g).map((n) => parseInt(n, 10));
22592
+ points = pointsString.match(/-?\d+/g).map((n) => parseInt(n, 10));
22416
22593
  session.context.poly(points, false);
22417
22594
  if (strokeStyle)
22418
22595
  session.context.stroke(strokeStyle);
@@ -22522,6 +22699,12 @@ const tempMatrix$2 = new Matrix();
22522
22699
  const _GraphicsContext = class _GraphicsContext extends EventEmitter {
22523
22700
  constructor() {
22524
22701
  super(...arguments);
22702
+ /** @internal */
22703
+ this._gpuData = /* @__PURE__ */ Object.create(null);
22704
+ /** If set to true, the resource will be garbage collected automatically when it is not used. */
22705
+ this.autoGarbageCollect = true;
22706
+ /** @internal */
22707
+ this._gcLastUsed = -1;
22525
22708
  /**
22526
22709
  * unique id for this graphics context
22527
22710
  * @internal
@@ -22536,6 +22719,8 @@ const _GraphicsContext = class _GraphicsContext extends EventEmitter {
22536
22719
  this.batchMode = "auto";
22537
22720
  /** @internal */
22538
22721
  this.instructions = [];
22722
+ /** Whether the graphics context has been destroyed. */
22723
+ this.destroyed = false;
22539
22724
  this._activePath = new GraphicsPath();
22540
22725
  this._transform = new Matrix();
22541
22726
  this._fillStyle = { ..._GraphicsContext.defaultFillStyle };
@@ -22632,7 +22817,7 @@ const _GraphicsContext = class _GraphicsContext extends EventEmitter {
22632
22817
  fill(style, alpha) {
22633
22818
  let path;
22634
22819
  const lastInstruction = this.instructions[this.instructions.length - 1];
22635
- if (this._tick === 0 && lastInstruction && lastInstruction.action === "stroke") {
22820
+ if (this._tick === 0 && lastInstruction?.action === "stroke") {
22636
22821
  path = lastInstruction.data.path;
22637
22822
  } else {
22638
22823
  path = this._activePath.clone();
@@ -22670,7 +22855,7 @@ const _GraphicsContext = class _GraphicsContext extends EventEmitter {
22670
22855
  stroke(style) {
22671
22856
  let path;
22672
22857
  const lastInstruction = this.instructions[this.instructions.length - 1];
22673
- if (this._tick === 0 && lastInstruction && lastInstruction.action === "fill") {
22858
+ if (this._tick === 0 && lastInstruction?.action === "fill") {
22674
22859
  path = lastInstruction.data.path;
22675
22860
  } else {
22676
22861
  path = this._activePath.clone();
@@ -23252,6 +23437,14 @@ const _GraphicsContext = class _GraphicsContext extends EventEmitter {
23252
23437
  }
23253
23438
  return hasHit;
23254
23439
  }
23440
+ /** Unloads the GPU data from the graphics context. */
23441
+ unload() {
23442
+ this.emit("unload", this);
23443
+ for (const key in this._gpuData) {
23444
+ this._gpuData[key]?.destroy();
23445
+ }
23446
+ this._gpuData = /* @__PURE__ */ Object.create(null);
23447
+ }
23255
23448
  /**
23256
23449
  * Destroys the GraphicsData object.
23257
23450
  * @param options - Options parameter. A boolean will act as if all options
@@ -23262,8 +23455,12 @@ const _GraphicsContext = class _GraphicsContext extends EventEmitter {
23262
23455
  * context.destroy({ texture: true, textureSource: true });
23263
23456
  */
23264
23457
  destroy(options = false) {
23458
+ if (this.destroyed)
23459
+ return;
23460
+ this.destroyed = true;
23265
23461
  this._stateStack.length = 0;
23266
23462
  this._transform = null;
23463
+ this.unload();
23267
23464
  this.emit("destroy", this);
23268
23465
  this.removeAllListeners();
23269
23466
  const destroyTexture = typeof options === "boolean" ? options : options?.texture;
@@ -23362,6 +23559,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23362
23559
  return this._align;
23363
23560
  }
23364
23561
  set align(value) {
23562
+ if (this._align === value)
23563
+ return;
23365
23564
  this._align = value;
23366
23565
  this.update();
23367
23566
  }
@@ -23370,6 +23569,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23370
23569
  return this._breakWords;
23371
23570
  }
23372
23571
  set breakWords(value) {
23572
+ if (this._breakWords === value)
23573
+ return;
23373
23574
  this._breakWords = value;
23374
23575
  this.update();
23375
23576
  }
@@ -23378,6 +23579,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23378
23579
  return this._dropShadow;
23379
23580
  }
23380
23581
  set dropShadow(value) {
23582
+ if (this._dropShadow === value)
23583
+ return;
23381
23584
  if (value !== null && typeof value === "object") {
23382
23585
  this._dropShadow = this._createProxy({ ..._TextStyle.defaultDropShadow, ...value });
23383
23586
  } else {
@@ -23390,6 +23593,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23390
23593
  return this._fontFamily;
23391
23594
  }
23392
23595
  set fontFamily(value) {
23596
+ if (this._fontFamily === value)
23597
+ return;
23393
23598
  this._fontFamily = value;
23394
23599
  this.update();
23395
23600
  }
@@ -23398,6 +23603,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23398
23603
  return this._fontSize;
23399
23604
  }
23400
23605
  set fontSize(value) {
23606
+ if (this._fontSize === value)
23607
+ return;
23401
23608
  if (typeof value === "string") {
23402
23609
  this._fontSize = parseInt(value, 10);
23403
23610
  } else {
@@ -23413,6 +23620,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23413
23620
  return this._fontStyle;
23414
23621
  }
23415
23622
  set fontStyle(value) {
23623
+ if (this._fontStyle === value)
23624
+ return;
23416
23625
  this._fontStyle = value.toLowerCase();
23417
23626
  this.update();
23418
23627
  }
@@ -23424,6 +23633,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23424
23633
  return this._fontVariant;
23425
23634
  }
23426
23635
  set fontVariant(value) {
23636
+ if (this._fontVariant === value)
23637
+ return;
23427
23638
  this._fontVariant = value;
23428
23639
  this.update();
23429
23640
  }
@@ -23435,6 +23646,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23435
23646
  return this._fontWeight;
23436
23647
  }
23437
23648
  set fontWeight(value) {
23649
+ if (this._fontWeight === value)
23650
+ return;
23438
23651
  this._fontWeight = value;
23439
23652
  this.update();
23440
23653
  }
@@ -23443,6 +23656,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23443
23656
  return this._leading;
23444
23657
  }
23445
23658
  set leading(value) {
23659
+ if (this._leading === value)
23660
+ return;
23446
23661
  this._leading = value;
23447
23662
  this.update();
23448
23663
  }
@@ -23451,6 +23666,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23451
23666
  return this._letterSpacing;
23452
23667
  }
23453
23668
  set letterSpacing(value) {
23669
+ if (this._letterSpacing === value)
23670
+ return;
23454
23671
  this._letterSpacing = value;
23455
23672
  this.update();
23456
23673
  }
@@ -23459,6 +23676,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23459
23676
  return this._lineHeight;
23460
23677
  }
23461
23678
  set lineHeight(value) {
23679
+ if (this._lineHeight === value)
23680
+ return;
23462
23681
  this._lineHeight = value;
23463
23682
  this.update();
23464
23683
  }
@@ -23471,6 +23690,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23471
23690
  return this._padding;
23472
23691
  }
23473
23692
  set padding(value) {
23693
+ if (this._padding === value)
23694
+ return;
23474
23695
  this._padding = value;
23475
23696
  this.update();
23476
23697
  }
@@ -23484,6 +23705,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23484
23705
  return this._filters;
23485
23706
  }
23486
23707
  set filters(value) {
23708
+ if (this._filters === value)
23709
+ return;
23487
23710
  this._filters = Object.freeze(value);
23488
23711
  this.update();
23489
23712
  }
@@ -23497,6 +23720,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23497
23720
  return this._trim;
23498
23721
  }
23499
23722
  set trim(value) {
23723
+ if (this._trim === value)
23724
+ return;
23500
23725
  this._trim = value;
23501
23726
  this.update();
23502
23727
  }
@@ -23508,6 +23733,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23508
23733
  return this._textBaseline;
23509
23734
  }
23510
23735
  set textBaseline(value) {
23736
+ if (this._textBaseline === value)
23737
+ return;
23511
23738
  this._textBaseline = value;
23512
23739
  this.update();
23513
23740
  }
@@ -23526,6 +23753,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23526
23753
  return this._whiteSpace;
23527
23754
  }
23528
23755
  set whiteSpace(value) {
23756
+ if (this._whiteSpace === value)
23757
+ return;
23529
23758
  this._whiteSpace = value;
23530
23759
  this.update();
23531
23760
  }
@@ -23534,6 +23763,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23534
23763
  return this._wordWrap;
23535
23764
  }
23536
23765
  set wordWrap(value) {
23766
+ if (this._wordWrap === value)
23767
+ return;
23537
23768
  this._wordWrap = value;
23538
23769
  this.update();
23539
23770
  }
@@ -23542,6 +23773,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23542
23773
  return this._wordWrapWidth;
23543
23774
  }
23544
23775
  set wordWrapWidth(value) {
23776
+ if (this._wordWrapWidth === value)
23777
+ return;
23545
23778
  this._wordWrapWidth = value;
23546
23779
  this.update();
23547
23780
  }
@@ -23707,6 +23940,8 @@ const _TextStyle = class _TextStyle extends EventEmitter {
23707
23940
  _createProxy(value, cb) {
23708
23941
  return new Proxy(value, {
23709
23942
  set: (target, property, newValue) => {
23943
+ if (target[property] === newValue)
23944
+ return true;
23710
23945
  target[property] = newValue;
23711
23946
  cb?.(property, newValue);
23712
23947
  this.update();
@@ -24887,7 +25122,7 @@ const bitmapFontXMLParser = {
24887
25122
 
24888
25123
  const bitmapFontXMLStringParser = {
24889
25124
  test(data) {
24890
- if (typeof data === "string" && data.includes("<font>")) {
25125
+ if (typeof data === "string" && data.match(/<font(\s|>)/)) {
24891
25126
  return bitmapFontXMLParser.test(DOMAdapter.get().parseXML(data));
24892
25127
  }
24893
25128
  return false;
@@ -25186,8 +25421,26 @@ const detectWebp = {
25186
25421
  remove: async (formats) => formats.filter((f) => f !== "webp")
25187
25422
  };
25188
25423
 
25189
- class Loader {
25424
+ const _Loader = class _Loader {
25190
25425
  constructor() {
25426
+ /**
25427
+ * Options for loading assets with the loader.
25428
+ * These options will be used as defaults for all load calls made with this loader instance.
25429
+ * They can be overridden by passing options directly to the load method.
25430
+ * @example
25431
+ * ```ts
25432
+ * // Create a loader with custom default options
25433
+ * const loader = new Loader();
25434
+ * loader.loadOptions = {
25435
+ * strategy: 'skip', // Default strategy to 'skip'
25436
+ * retryCount: 5, // Default retry count to 5
25437
+ * retryDelay: 500, // Default retry delay to 500ms
25438
+ * };
25439
+ *
25440
+ * // This load call will use the loader's default options
25441
+ * await loader.load('image1.png');
25442
+ */
25443
+ this.loadOptions = { ..._Loader.defaultOptions };
25191
25444
  this._parsers = [];
25192
25445
  this._parsersValidated = false;
25193
25446
  /**
@@ -25264,10 +25517,12 @@ class Loader {
25264
25517
  })();
25265
25518
  return result;
25266
25519
  }
25267
- async load(assetsToLoadIn, onProgress) {
25520
+ async load(assetsToLoadIn, onProgressOrOptions) {
25268
25521
  if (!this._parsersValidated) {
25269
25522
  this._validateParsers();
25270
25523
  }
25524
+ const options = typeof onProgressOrOptions === "function" ? { ..._Loader.defaultOptions, ...this.loadOptions, onProgress: onProgressOrOptions } : { ..._Loader.defaultOptions, ...this.loadOptions, ...onProgressOrOptions || {} };
25525
+ const { onProgress, onError, strategy, retryCount, retryDelay } = options;
25271
25526
  let count = 0;
25272
25527
  const assets = {};
25273
25528
  const singleAsset = isSingleItem(assetsToLoadIn);
@@ -25276,24 +25531,15 @@ class Loader {
25276
25531
  src: item,
25277
25532
  data: {}
25278
25533
  }));
25279
- const total = assetsToLoad.length;
25534
+ const total = assetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0);
25280
25535
  const promises = assetsToLoad.map(async (asset) => {
25281
25536
  const url = path.toAbsolute(asset.src);
25282
- if (!assets[asset.src]) {
25283
- try {
25284
- if (!this.promiseCache[url]) {
25285
- this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset);
25286
- }
25287
- assets[asset.src] = await this.promiseCache[url].promise;
25288
- if (onProgress)
25289
- onProgress(++count / total);
25290
- } catch (e) {
25291
- delete this.promiseCache[url];
25292
- delete assets[asset.src];
25293
- throw new Error(`[Loader.load] Failed to load ${url}.
25294
- ${e}`);
25295
- }
25296
- }
25537
+ if (assets[asset.src])
25538
+ return;
25539
+ await this._loadAssetWithRetry(url, asset, { onProgress, onError, strategy, retryCount, retryDelay }, assets);
25540
+ count += asset.progressSize || 1;
25541
+ if (onProgress)
25542
+ onProgress(count / total);
25297
25543
  });
25298
25544
  await Promise.all(promises);
25299
25545
  return singleAsset ? assets[assetsToLoad[0].src] : assets;
@@ -25341,7 +25587,65 @@ ${e}`);
25341
25587
  return hash;
25342
25588
  }, {});
25343
25589
  }
25344
- }
25590
+ async _loadAssetWithRetry(url, asset, options, assets) {
25591
+ let attempt = 0;
25592
+ const { onError, strategy, retryCount, retryDelay } = options;
25593
+ const wait = (ms) => new Promise((r) => setTimeout(r, ms));
25594
+ while (true) {
25595
+ try {
25596
+ if (!this.promiseCache[url]) {
25597
+ this.promiseCache[url] = this._getLoadPromiseAndParser(url, asset);
25598
+ }
25599
+ assets[asset.src] = await this.promiseCache[url].promise;
25600
+ return;
25601
+ } catch (e) {
25602
+ delete this.promiseCache[url];
25603
+ delete assets[asset.src];
25604
+ attempt++;
25605
+ const isLast = strategy !== "retry" || attempt > retryCount;
25606
+ if (strategy === "retry" && !isLast) {
25607
+ if (onError)
25608
+ onError(e, asset);
25609
+ await wait(retryDelay);
25610
+ continue;
25611
+ }
25612
+ if (strategy === "skip") {
25613
+ if (onError)
25614
+ onError(e, asset);
25615
+ return;
25616
+ }
25617
+ if (onError)
25618
+ onError(e, asset);
25619
+ const error = new Error(`[Loader.load] Failed to load ${url}.
25620
+ ${e}`);
25621
+ if (e instanceof Error && e.stack) {
25622
+ error.stack = e.stack;
25623
+ }
25624
+ throw error;
25625
+ }
25626
+ }
25627
+ }
25628
+ };
25629
+ /**
25630
+ * Default options for loading assets
25631
+ * @example
25632
+ * ```ts
25633
+ * // Change default load options globally
25634
+ * Loader.defaultOptions = {
25635
+ * strategy: 'skip', // Change default strategy to 'skip'
25636
+ * retryCount: 5, // Change default retry count to 5
25637
+ * retryDelay: 500, // Change default retry delay to 500ms
25638
+ * };
25639
+ * ```
25640
+ */
25641
+ _Loader.defaultOptions = {
25642
+ onProgress: void 0,
25643
+ onError: void 0,
25644
+ strategy: "throw",
25645
+ retryCount: 3,
25646
+ retryDelay: 250
25647
+ };
25648
+ let Loader = _Loader;
25345
25649
 
25346
25650
  function checkDataUrl(url, mimes) {
25347
25651
  if (Array.isArray(mimes)) {
@@ -25740,12 +26044,15 @@ class WorkerManagerClass {
25740
26044
  * @param data - Result data from the worker containing uuid, data, and optional error
25741
26045
  */
25742
26046
  _complete(data) {
26047
+ if (!this._resolveHash[data.uuid]) {
26048
+ return;
26049
+ }
25743
26050
  if (data.error !== void 0) {
25744
26051
  this._resolveHash[data.uuid].reject(data.error);
25745
26052
  } else {
25746
26053
  this._resolveHash[data.uuid].resolve(data.data);
25747
26054
  }
25748
- this._resolveHash[data.uuid] = null;
26055
+ delete this._resolveHash[data.uuid];
25749
26056
  }
25750
26057
  /**
25751
26058
  * Executes a task using the worker pool system.
@@ -25806,7 +26113,7 @@ class WorkerManagerClass {
25806
26113
  this._workerPool.forEach((worker) => worker.terminate());
25807
26114
  this._workerPool.length = 0;
25808
26115
  Object.values(this._resolveHash).forEach(({ reject }) => {
25809
- reject?.(new Error("WorkerManager destroyed"));
26116
+ reject?.(new Error("WorkerManager has been reset before completion"));
25810
26117
  });
25811
26118
  this._resolveHash = {};
25812
26119
  this._queue.length = 0;
@@ -26145,6 +26452,12 @@ class AssetsClass {
26145
26452
  if (options.preferences) {
26146
26453
  this.setPreferences(options.preferences);
26147
26454
  }
26455
+ if (options.loadOptions) {
26456
+ this.loader.loadOptions = {
26457
+ ...this.loader.loadOptions,
26458
+ ...options.loadOptions
26459
+ };
26460
+ }
26148
26461
  }
26149
26462
  /**
26150
26463
  * Registers assets with the Assets resolver. This method maps keys (aliases) to asset sources,
@@ -26364,17 +26677,22 @@ class AssetsClass {
26364
26677
  const resolveResults = this.resolver.resolveBundle(bundleIds);
26365
26678
  const out = {};
26366
26679
  const keys = Object.keys(resolveResults);
26367
- let count = 0;
26368
26680
  let total = 0;
26681
+ const counts = [];
26369
26682
  const _onProgress = () => {
26370
- onProgress?.(++count / total);
26683
+ onProgress?.(counts.reduce((a, b) => a + b, 0) / total);
26371
26684
  };
26372
- const promises = keys.map((bundleId) => {
26685
+ const promises = keys.map((bundleId, i) => {
26373
26686
  const resolveResult = resolveResults[bundleId];
26374
26687
  const values = Object.values(resolveResult);
26375
26688
  const totalAssetsToLoad = [...new Set(values.flat())];
26376
- total += totalAssetsToLoad.length;
26377
- return this._mapLoadToResolve(resolveResult, _onProgress).then((resolveResult2) => {
26689
+ const progressSize = totalAssetsToLoad.reduce((sum, asset) => sum + (asset.progressSize || 1), 0);
26690
+ counts.push(0);
26691
+ total += progressSize;
26692
+ return this._mapLoadToResolve(resolveResult, (e) => {
26693
+ counts[i] = e * progressSize;
26694
+ _onProgress();
26695
+ }).then((resolveResult2) => {
26378
26696
  out[bundleId] = resolveResult2;
26379
26697
  });
26380
26698
  });
@@ -26526,12 +26844,12 @@ class AssetsClass {
26526
26844
  /**
26527
26845
  * helper function to map resolved assets back to loaded assets
26528
26846
  * @param resolveResults - the resolve results from the resolver
26529
- * @param onProgress - the progress callback
26847
+ * @param progressOrLoadOptions - the progress callback or load options
26530
26848
  */
26531
- async _mapLoadToResolve(resolveResults, onProgress) {
26849
+ async _mapLoadToResolve(resolveResults, progressOrLoadOptions) {
26532
26850
  const resolveArray = [...new Set(Object.values(resolveResults))];
26533
26851
  this._backgroundLoader.active = false;
26534
- const loadedAssets = await this.loader.load(resolveArray, onProgress);
26852
+ const loadedAssets = await this.loader.load(resolveArray, progressOrLoadOptions);
26535
26853
  this._backgroundLoader.active = true;
26536
26854
  const out = {};
26537
26855
  resolveArray.forEach((resolveResult) => {
@@ -28816,6 +29134,31 @@ const FederatedContainer = {
28816
29134
  }
28817
29135
  };
28818
29136
 
29137
+ var vertex$2 = "in vec2 aPosition;\nout vec2 vTextureCoord;\n\nuniform vec4 uInputSize;\nuniform vec4 uOutputFrame;\nuniform vec4 uOutputTexture;\n\nvec4 filterVertexPosition( void )\n{\n vec2 position = aPosition * uOutputFrame.zw + uOutputFrame.xy;\n \n position.x = position.x * (2.0 / uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0*uOutputTexture.z / uOutputTexture.y) - uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nvec2 filterTextureCoord( void )\n{\n return aPosition * (uOutputFrame.zw * uInputSize.zw);\n}\n\nvoid main(void)\n{\n gl_Position = filterVertexPosition();\n vTextureCoord = filterTextureCoord();\n}\n";
29138
+
29139
+ var fragment$2 = "in vec2 vTextureCoord;\nout vec4 finalColor;\nuniform sampler2D uTexture;\nvoid main() {\n finalColor = texture(uTexture, vTextureCoord);\n}\n";
29140
+
29141
+ var source$1 = "struct GlobalFilterUniforms {\n uInputSize: vec4<f32>,\n uInputPixel: vec4<f32>,\n uInputClamp: vec4<f32>,\n uOutputFrame: vec4<f32>,\n uGlobalFrame: vec4<f32>,\n uOutputTexture: vec4<f32>,\n};\n\n@group(0) @binding(0) var <uniform> gfu: GlobalFilterUniforms;\n@group(0) @binding(1) var uTexture: texture_2d<f32>;\n@group(0) @binding(2) var uSampler: sampler;\n\nstruct VSOutput {\n @builtin(position) position: vec4<f32>,\n @location(0) uv: vec2<f32>\n};\n\nfn filterVertexPosition(aPosition: vec2<f32>) -> vec4<f32>\n{\n var position = aPosition * gfu.uOutputFrame.zw + gfu.uOutputFrame.xy;\n\n position.x = position.x * (2.0 / gfu.uOutputTexture.x) - 1.0;\n position.y = position.y * (2.0 * gfu.uOutputTexture.z / gfu.uOutputTexture.y) - gfu.uOutputTexture.z;\n\n return vec4(position, 0.0, 1.0);\n}\n\nfn filterTextureCoord(aPosition: vec2<f32>) -> vec2<f32>\n{\n return aPosition * (gfu.uOutputFrame.zw * gfu.uInputSize.zw);\n}\n\n@vertex\nfn mainVertex(\n @location(0) aPosition: vec2<f32>,\n) -> VSOutput {\n return VSOutput(\n filterVertexPosition(aPosition),\n filterTextureCoord(aPosition)\n );\n}\n\n@fragment\nfn mainFragment(\n @location(0) uv: vec2<f32>,\n) -> @location(0) vec4<f32> {\n return textureSample(uTexture, uSampler, uv);\n}\n";
29142
+
29143
+ class PassthroughFilter extends Filter {
29144
+ constructor() {
29145
+ const gpuProgram = GpuProgram.from({
29146
+ vertex: { source: source$1, entryPoint: "mainVertex" },
29147
+ fragment: { source: source$1, entryPoint: "mainFragment" },
29148
+ name: "passthrough-filter"
29149
+ });
29150
+ const glProgram = GlProgram.from({
29151
+ vertex: vertex$2,
29152
+ fragment: fragment$2,
29153
+ name: "passthrough-filter"
29154
+ });
29155
+ super({
29156
+ gpuProgram,
29157
+ glProgram
29158
+ });
29159
+ }
29160
+ }
29161
+
28819
29162
  class FilterPipe {
28820
29163
  constructor(renderer) {
28821
29164
  this._renderer = renderer;
@@ -28940,6 +29283,10 @@ class FilterData {
28940
29283
  * @type {{ x: number, y: number, width: number, height: number }}
28941
29284
  */
28942
29285
  this.globalFrame = { x: 0, y: 0, width: 0, height: 0 };
29286
+ /** The first enabled filter index in the current filter list. */
29287
+ this.firstEnabledIndex = -1;
29288
+ /** The last enabled filter index in the current filter list. */
29289
+ this.lastEnabledIndex = -1;
28943
29290
  }
28944
29291
  }
28945
29292
  class FilterSystem {
@@ -28980,7 +29327,7 @@ class FilterSystem {
28980
29327
  const colorTextureSource = renderer.renderTarget.renderTarget.colorTexture.source;
28981
29328
  const rootResolution = colorTextureSource.resolution;
28982
29329
  const rootAntialias = colorTextureSource.antialias;
28983
- if (filters.length === 0) {
29330
+ if (filters.every((filter) => !filter.enabled)) {
28984
29331
  filterData.skip = true;
28985
29332
  return;
28986
29333
  }
@@ -29043,7 +29390,7 @@ class FilterSystem {
29043
29390
  const colorTextureSource = texture.source;
29044
29391
  const rootResolution = colorTextureSource.resolution;
29045
29392
  const rootAntialias = colorTextureSource.antialias;
29046
- if (filters.length === 0) {
29393
+ if (filters.every((filter) => !filter.enabled)) {
29047
29394
  filterData.skip = true;
29048
29395
  return texture;
29049
29396
  }
@@ -29150,7 +29497,8 @@ class FilterSystem {
29150
29497
  offsetY = offset.y;
29151
29498
  }
29152
29499
  this._updateFilterUniforms(input, output, filterData, offsetX, offsetY, resolution, isFinalTarget, clear);
29153
- this._setupBindGroupsAndRender(filter, input, renderer);
29500
+ const filterToApply = filter.enabled ? filter : this._getPassthroughFilter();
29501
+ this._setupBindGroupsAndRender(filterToApply, input, renderer);
29154
29502
  }
29155
29503
  /**
29156
29504
  * Multiply _input normalized coordinates_ to this matrix to get _sprite texture normalized coordinates_.
@@ -29185,6 +29533,12 @@ class FilterSystem {
29185
29533
  return mappedMatrix;
29186
29534
  }
29187
29535
  destroy() {
29536
+ this._passthroughFilter?.destroy(true);
29537
+ this._passthroughFilter = null;
29538
+ }
29539
+ _getPassthroughFilter() {
29540
+ this._passthroughFilter ?? (this._passthroughFilter = new PassthroughFilter());
29541
+ return this._passthroughFilter;
29188
29542
  }
29189
29543
  /**
29190
29544
  * Sets up the bind groups and renders the filter.
@@ -29370,10 +29724,12 @@ class FilterSystem {
29370
29724
  const inputTexture = filterData.inputTexture;
29371
29725
  const bounds = filterData.bounds;
29372
29726
  const filters = filterData.filters;
29727
+ const firstEnabled = filterData.firstEnabledIndex;
29728
+ const lastEnabled = filterData.lastEnabledIndex;
29373
29729
  this._globalFilterBindGroup.setResource(inputTexture.source.style, 2);
29374
29730
  this._globalFilterBindGroup.setResource(filterData.backTexture.source, 3);
29375
- if (filters.length === 1) {
29376
- filters[0].apply(this, inputTexture, filterData.outputRenderSurface, clear);
29731
+ if (firstEnabled === lastEnabled) {
29732
+ filters[firstEnabled].apply(this, inputTexture, filterData.outputRenderSurface, clear);
29377
29733
  } else {
29378
29734
  let flip = filterData.inputTexture;
29379
29735
  const tempTexture = TexturePool.getOptimalTexture(
@@ -29383,15 +29739,16 @@ class FilterSystem {
29383
29739
  false
29384
29740
  );
29385
29741
  let flop = tempTexture;
29386
- let i = 0;
29387
- for (i = 0; i < filters.length - 1; ++i) {
29742
+ for (let i = firstEnabled; i < lastEnabled; i++) {
29388
29743
  const filter = filters[i];
29744
+ if (!filter.enabled)
29745
+ continue;
29389
29746
  filter.apply(this, flip, flop, true);
29390
29747
  const t = flip;
29391
29748
  flip = flop;
29392
29749
  flop = t;
29393
29750
  }
29394
- filters[i].apply(this, flip, filterData.outputRenderSurface, clear);
29751
+ filters[lastEnabled].apply(this, flip, filterData.outputRenderSurface, clear);
29395
29752
  TexturePool.returnTexture(tempTexture);
29396
29753
  }
29397
29754
  }
@@ -29405,8 +29762,15 @@ class FilterSystem {
29405
29762
  let blendRequired = false;
29406
29763
  let enabled = false;
29407
29764
  let clipToViewport = true;
29765
+ let firstEnabledIndex = -1;
29766
+ let lastEnabledIndex = -1;
29408
29767
  for (let i = 0; i < filters.length; i++) {
29409
29768
  const filter = filters[i];
29769
+ if (!filter.enabled)
29770
+ continue;
29771
+ if (firstEnabledIndex === -1)
29772
+ firstEnabledIndex = i;
29773
+ lastEnabledIndex = i;
29410
29774
  resolution = Math.min(resolution, filter.resolution === "inherit" ? rootResolution : filter.resolution);
29411
29775
  padding += filter.padding;
29412
29776
  if (filter.antialias === "off") {
@@ -29427,7 +29791,7 @@ class FilterSystem {
29427
29791
  enabled = false;
29428
29792
  break;
29429
29793
  }
29430
- enabled = filter.enabled || enabled;
29794
+ enabled = true;
29431
29795
  blendRequired || (blendRequired = filter.blendRequired);
29432
29796
  }
29433
29797
  if (!enabled) {
@@ -29445,6 +29809,8 @@ class FilterSystem {
29445
29809
  filterData.antialias = antialias;
29446
29810
  filterData.resolution = resolution;
29447
29811
  filterData.blendRequired = blendRequired;
29812
+ filterData.firstEnabledIndex = firstEnabledIndex;
29813
+ filterData.lastEnabledIndex = lastEnabledIndex;
29448
29814
  }
29449
29815
  _popFilterData() {
29450
29816
  this._filterStackIndex--;
@@ -29558,11 +29924,11 @@ class Graphics extends ViewContainer {
29558
29924
  /** @internal */
29559
29925
  this.renderPipeId = "graphics";
29560
29926
  if (!context) {
29561
- this._context = this._ownedContext = new GraphicsContext();
29927
+ this.context = this._ownedContext = new GraphicsContext();
29928
+ this.context.autoGarbageCollect = this.autoGarbageCollect;
29562
29929
  } else {
29563
- this._context = context;
29930
+ this.context = context;
29564
29931
  }
29565
- this._context.on("update", this.onViewUpdate, this);
29566
29932
  this.didViewUpdate = true;
29567
29933
  this.allowChildren = false;
29568
29934
  this.roundPixels = roundPixels ?? false;
@@ -29570,9 +29936,13 @@ class Graphics extends ViewContainer {
29570
29936
  set context(context) {
29571
29937
  if (context === this._context)
29572
29938
  return;
29573
- this._context.off("update", this.onViewUpdate, this);
29939
+ if (this._context) {
29940
+ this._context.off("update", this.onViewUpdate, this);
29941
+ this._context.off("unload", this.unload, this);
29942
+ }
29574
29943
  this._context = context;
29575
29944
  this._context.on("update", this.onViewUpdate, this);
29945
+ this._context.on("unload", this.unload, this);
29576
29946
  this.onViewUpdate();
29577
29947
  }
29578
29948
  /**
@@ -29686,6 +30056,14 @@ class Graphics extends ViewContainer {
29686
30056
  this._context = null;
29687
30057
  super.destroy(options);
29688
30058
  }
30059
+ /**
30060
+ * @param now - The current time in milliseconds.
30061
+ * @internal
30062
+ */
30063
+ _onTouch(now) {
30064
+ this._gcLastUsed = now;
30065
+ this._context._gcLastUsed = now;
30066
+ }
29689
30067
  _callContextMethod(method, args) {
29690
30068
  this.context[method](...args);
29691
30069
  return this;
@@ -31802,7 +32180,7 @@ class GpuBatchAdaptor {
31802
32180
  tempState,
31803
32181
  batch.topology
31804
32182
  );
31805
- batch.bindGroup._touch(renderer.textureGC.count);
32183
+ batch.bindGroup._touch(renderer.gc.now, renderer.tick);
31806
32184
  encoder.setPipeline(pipeline);
31807
32185
  encoder.renderPassEncoder.setBindGroup(1, gpuBindGroup);
31808
32186
  encoder.renderPassEncoder.drawIndexed(batch.size, 1, batch.start);
@@ -32408,6 +32786,14 @@ class GlBuffer {
32408
32786
  this.byteLength = -1;
32409
32787
  this.type = type;
32410
32788
  }
32789
+ destroy() {
32790
+ this.buffer = null;
32791
+ this.updateID = -1;
32792
+ this.byteLength = -1;
32793
+ this.type = -1;
32794
+ this._lastBindBaseLocation = -1;
32795
+ this._lastBindCallId = -1;
32796
+ }
32411
32797
  }
32412
32798
 
32413
32799
  class GlBufferSystem {
@@ -32415,30 +32801,35 @@ class GlBufferSystem {
32415
32801
  * @param {Renderer} renderer - The renderer this System works for.
32416
32802
  */
32417
32803
  constructor(renderer) {
32418
- this._gpuBuffers = /* @__PURE__ */ Object.create(null);
32419
32804
  /** Cache keeping track of the base bound buffer bases */
32420
32805
  this._boundBufferBases = /* @__PURE__ */ Object.create(null);
32421
32806
  this._minBaseLocation = 0;
32422
32807
  this._nextBindBaseIndex = this._minBaseLocation;
32423
32808
  this._bindCallId = 0;
32424
32809
  this._renderer = renderer;
32425
- this._renderer.renderableGC.addManagedHash(this, "_gpuBuffers");
32810
+ this._managedBuffers = new GCManagedHash({
32811
+ renderer,
32812
+ type: "resource",
32813
+ onUnload: this.onBufferUnload.bind(this),
32814
+ name: "glBuffer"
32815
+ });
32426
32816
  }
32427
32817
  /** @ignore */
32428
32818
  destroy() {
32819
+ this._managedBuffers.destroy();
32429
32820
  this._renderer = null;
32430
32821
  this._gl = null;
32431
- this._gpuBuffers = null;
32432
- this._boundBufferBases = null;
32822
+ this._boundBufferBases = {};
32433
32823
  }
32434
32824
  /** Sets up the renderer context and necessary buffers. */
32435
32825
  contextChange() {
32436
32826
  this._gl = this._renderer.gl;
32437
- this._gpuBuffers = /* @__PURE__ */ Object.create(null);
32827
+ this.destroyAll(true);
32438
32828
  this._maxBindings = this._renderer.limits.maxUniformBindings;
32439
32829
  }
32440
32830
  getGlBuffer(buffer) {
32441
- return this._gpuBuffers[buffer.uid] || this.createGLBuffer(buffer);
32831
+ buffer._gcLastUsed = this._renderer.gc.now;
32832
+ return buffer._gpuData[this._renderer.uid] || this.createGLBuffer(buffer);
32442
32833
  }
32443
32834
  /**
32444
32835
  * This binds specified buffer. On first run, it will create the webGL buffers for the context too
@@ -32553,26 +32944,19 @@ class GlBufferSystem {
32553
32944
  }
32554
32945
  return glBuffer;
32555
32946
  }
32556
- /** dispose all WebGL resources of all managed buffers */
32557
- destroyAll() {
32558
- const gl = this._gl;
32559
- for (const id in this._gpuBuffers) {
32560
- gl.deleteBuffer(this._gpuBuffers[id].buffer);
32561
- }
32562
- this._gpuBuffers = /* @__PURE__ */ Object.create(null);
32563
- }
32564
32947
  /**
32565
- * Disposes buffer
32566
- * @param {Buffer} buffer - buffer with data
32567
- * @param {boolean} [contextLost=false] - If context was lost, we suppress deleteVertexArray
32948
+ * dispose all WebGL resources of all managed buffers
32949
+ * @param contextLost
32568
32950
  */
32569
- onBufferDestroy(buffer, contextLost) {
32570
- const glBuffer = this._gpuBuffers[buffer.uid];
32571
- const gl = this._gl;
32572
- if (!contextLost) {
32573
- gl.deleteBuffer(glBuffer.buffer);
32574
- }
32575
- this._gpuBuffers[buffer.uid] = null;
32951
+ destroyAll(contextLost = false) {
32952
+ this._managedBuffers.removeAll(contextLost);
32953
+ }
32954
+ onBufferUnload(buffer, contextLost = false) {
32955
+ const glBuffer = buffer._gpuData[this._renderer.uid];
32956
+ if (!glBuffer)
32957
+ return;
32958
+ if (!contextLost)
32959
+ this._gl.deleteBuffer(glBuffer.buffer);
32576
32960
  }
32577
32961
  /**
32578
32962
  * creates and attaches a GLBuffer object tied to the current context.
@@ -32588,8 +32972,8 @@ class GlBufferSystem {
32588
32972
  type = BUFFER_TYPE.UNIFORM_BUFFER;
32589
32973
  }
32590
32974
  const glBuffer = new GlBuffer(gl.createBuffer(), type);
32591
- this._gpuBuffers[buffer.uid] = glBuffer;
32592
- buffer.on("destroy", this.onBufferDestroy, this);
32975
+ buffer._gpuData[this._renderer.uid] = glBuffer;
32976
+ this._managedBuffers.add(buffer);
32593
32977
  return glBuffer;
32594
32978
  }
32595
32979
  resetState() {
@@ -33001,16 +33385,28 @@ const topologyToGlMap = {
33001
33385
  "triangle-list": 4,
33002
33386
  "triangle-strip": 5
33003
33387
  };
33388
+ class GlGeometryGpuData {
33389
+ constructor() {
33390
+ this.vaoCache = /* @__PURE__ */ Object.create(null);
33391
+ }
33392
+ destroy() {
33393
+ this.vaoCache = /* @__PURE__ */ Object.create(null);
33394
+ }
33395
+ }
33004
33396
  class GlGeometrySystem {
33005
33397
  /** @param renderer - The renderer this System works for. */
33006
33398
  constructor(renderer) {
33007
- this._geometryVaoHash = /* @__PURE__ */ Object.create(null);
33008
33399
  this._renderer = renderer;
33009
33400
  this._activeGeometry = null;
33010
33401
  this._activeVao = null;
33011
33402
  this.hasVao = true;
33012
33403
  this.hasInstance = true;
33013
- this._renderer.renderableGC.addManagedHash(this, "_geometryVaoHash");
33404
+ this._managedGeometries = new GCManagedHash({
33405
+ renderer,
33406
+ type: "resource",
33407
+ onUnload: this.onGeometryUnload.bind(this),
33408
+ name: "glGeometry"
33409
+ });
33014
33410
  }
33015
33411
  /** Sets up the renderer context and necessary buffers. */
33016
33412
  contextChange() {
@@ -33018,6 +33414,7 @@ class GlGeometrySystem {
33018
33414
  if (!this._renderer.context.supports.vertexArrayObject) {
33019
33415
  throw new Error("[PixiJS] Vertex Array Objects are not supported on this device");
33020
33416
  }
33417
+ this.destroyAll(true);
33021
33418
  const nativeVaoExtension = this._renderer.context.extensions.vertexArrayObject;
33022
33419
  if (nativeVaoExtension) {
33023
33420
  gl.createVertexArray = () => nativeVaoExtension.createVertexArrayOES();
@@ -33036,7 +33433,6 @@ class GlGeometrySystem {
33036
33433
  }
33037
33434
  this._activeGeometry = null;
33038
33435
  this._activeVao = null;
33039
- this._geometryVaoHash = /* @__PURE__ */ Object.create(null);
33040
33436
  }
33041
33437
  /**
33042
33438
  * Binds geometry so that is can be drawn. Creating a Vao if required
@@ -33065,6 +33461,7 @@ class GlGeometrySystem {
33065
33461
  const buffer = geometry.buffers[i];
33066
33462
  bufferSystem.updateBuffer(buffer);
33067
33463
  }
33464
+ geometry._gcLastUsed = this._renderer.gc.now;
33068
33465
  }
33069
33466
  /**
33070
33467
  * Check compatibility between a geometry and a program
@@ -33098,7 +33495,7 @@ class GlGeometrySystem {
33098
33495
  return strings.join("-");
33099
33496
  }
33100
33497
  getVao(geometry, program) {
33101
- return this._geometryVaoHash[geometry.uid]?.[program._key] || this.initGeometryVao(geometry, program);
33498
+ return geometry._gpuData[this._renderer.uid]?.vaoCache[program._key] || this.initGeometryVao(geometry, program);
33102
33499
  }
33103
33500
  /**
33104
33501
  * Creates or gets Vao with the same structure as the geometry and stores it on the geometry.
@@ -33114,11 +33511,10 @@ class GlGeometrySystem {
33114
33511
  this._renderer.shader._getProgramData(program);
33115
33512
  this.checkCompatibility(geometry, program);
33116
33513
  const signature = this.getSignature(geometry, program);
33117
- if (!this._geometryVaoHash[geometry.uid]) {
33118
- this._geometryVaoHash[geometry.uid] = /* @__PURE__ */ Object.create(null);
33119
- geometry.on("destroy", this.onGeometryDestroy, this);
33120
- }
33121
- const vaoObjectHash = this._geometryVaoHash[geometry.uid];
33514
+ const gpuData = new GlGeometryGpuData();
33515
+ geometry._gpuData[this._renderer.uid] = gpuData;
33516
+ this._managedGeometries.add(geometry);
33517
+ const vaoObjectHash = gpuData.vaoCache;
33122
33518
  let vao = vaoObjectHash[signature];
33123
33519
  if (vao) {
33124
33520
  vaoObjectHash[program._key] = vao;
@@ -33138,24 +33534,18 @@ class GlGeometrySystem {
33138
33534
  gl.bindVertexArray(null);
33139
33535
  return vao;
33140
33536
  }
33141
- /**
33142
- * Disposes geometry.
33143
- * @param geometry - Geometry with buffers. Only VAO will be disposed
33144
- * @param [contextLost=false] - If context was lost, we suppress deleteVertexArray
33145
- */
33146
- onGeometryDestroy(geometry, contextLost) {
33147
- const vaoObjectHash = this._geometryVaoHash[geometry.uid];
33148
- const gl = this.gl;
33149
- if (vaoObjectHash) {
33150
- if (contextLost) {
33151
- for (const i in vaoObjectHash) {
33152
- if (this._activeVao !== vaoObjectHash[i]) {
33153
- this.unbind();
33154
- }
33155
- gl.deleteVertexArray(vaoObjectHash[i]);
33537
+ onGeometryUnload(geometry, contextLost = false) {
33538
+ const gpuData = geometry._gpuData[this._renderer.uid];
33539
+ if (!gpuData)
33540
+ return;
33541
+ const vaoCache = gpuData.vaoCache;
33542
+ if (!contextLost) {
33543
+ for (const i in vaoCache) {
33544
+ if (this._activeVao !== vaoCache[i]) {
33545
+ this.resetState();
33156
33546
  }
33547
+ this.gl.deleteVertexArray(vaoCache[i]);
33157
33548
  }
33158
- this._geometryVaoHash[geometry.uid] = null;
33159
33549
  }
33160
33550
  }
33161
33551
  /**
@@ -33163,19 +33553,7 @@ class GlGeometrySystem {
33163
33553
  * @param [contextLost=false] - If context was lost, we suppress `gl.delete` calls
33164
33554
  */
33165
33555
  destroyAll(contextLost = false) {
33166
- const gl = this.gl;
33167
- for (const i in this._geometryVaoHash) {
33168
- if (contextLost) {
33169
- for (const j in this._geometryVaoHash[i]) {
33170
- const vaoObjectHash = this._geometryVaoHash[i];
33171
- if (this._activeVao !== vaoObjectHash) {
33172
- this.unbind();
33173
- }
33174
- gl.deleteVertexArray(vaoObjectHash[j]);
33175
- }
33176
- }
33177
- this._geometryVaoHash[i] = null;
33178
- }
33556
+ this._managedGeometries.removeAll(contextLost);
33179
33557
  }
33180
33558
  /**
33181
33559
  * Activate vertex array object.
@@ -33252,12 +33630,12 @@ class GlGeometrySystem {
33252
33630
  if (geometry.indexBuffer) {
33253
33631
  const byteSize = geometry.indexBuffer.data.BYTES_PER_ELEMENT;
33254
33632
  const glType = byteSize === 2 ? gl.UNSIGNED_SHORT : gl.UNSIGNED_INT;
33255
- if (instanceCount > 1) {
33633
+ if (instanceCount !== 1) {
33256
33634
  gl.drawElementsInstanced(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize, instanceCount);
33257
33635
  } else {
33258
33636
  gl.drawElements(glTopology, size || geometry.indexBuffer.data.length, glType, (start || 0) * byteSize);
33259
33637
  }
33260
- } else if (instanceCount > 1) {
33638
+ } else if (instanceCount !== 1) {
33261
33639
  gl.drawArraysInstanced(glTopology, start || 0, size || geometry.getSize(), instanceCount);
33262
33640
  } else {
33263
33641
  gl.drawArrays(glTopology, start || 0, size || geometry.getSize());
@@ -33271,11 +33649,11 @@ class GlGeometrySystem {
33271
33649
  this._activeGeometry = null;
33272
33650
  }
33273
33651
  destroy() {
33652
+ this._managedGeometries.destroy();
33274
33653
  this._renderer = null;
33275
33654
  this.gl = null;
33276
33655
  this._activeVao = null;
33277
33656
  this._activeGeometry = null;
33278
- this._geometryVaoHash = null;
33279
33657
  }
33280
33658
  }
33281
33659
  /** @ignore */
@@ -34076,6 +34454,11 @@ class GlRenderTargetAdaptor {
34076
34454
  contextChange() {
34077
34455
  this._clearColorCache = [0, 0, 0, 0];
34078
34456
  this._viewPortCache = new Rectangle();
34457
+ const gl = this._renderer.gl;
34458
+ this._drawBuffersCache = [];
34459
+ for (let i = 1; i <= 16; i++) {
34460
+ this._drawBuffersCache[i] = Array.from({ length: i }, (_, j) => gl.COLOR_ATTACHMENT0 + j);
34461
+ }
34079
34462
  }
34080
34463
  copyToTexture(sourceRenderSurfaceTexture, destinationTexture, originSrc, size, originDest) {
34081
34464
  const renderTargetSystem = this._renderTargetSystem;
@@ -34103,13 +34486,16 @@ class GlRenderTargetAdaptor {
34103
34486
  const gpuRenderTarget = renderTargetSystem.getGpuRenderTarget(renderTarget);
34104
34487
  let viewPortY = viewport.y;
34105
34488
  if (renderTarget.isRoot) {
34106
- viewPortY = source.pixelHeight - viewport.height;
34489
+ viewPortY = source.pixelHeight - viewport.height - viewport.y;
34107
34490
  }
34108
34491
  renderTarget.colorTextures.forEach((texture) => {
34109
34492
  this._renderer.texture.unbind(texture);
34110
34493
  });
34111
34494
  const gl = this._renderer.gl;
34112
34495
  gl.bindFramebuffer(gl.FRAMEBUFFER, gpuRenderTarget.framebuffer);
34496
+ if (renderTarget.colorTextures.length > 1) {
34497
+ this._setDrawBuffers(renderTarget, gl);
34498
+ }
34113
34499
  const viewPortCache = this._viewPortCache;
34114
34500
  if (viewPortCache.x !== viewport.x || viewPortCache.y !== viewPortY || viewPortCache.width !== viewport.width || viewPortCache.height !== viewport.height) {
34115
34501
  viewPortCache.x = viewport.x;
@@ -34223,7 +34609,8 @@ class GlRenderTargetAdaptor {
34223
34609
  gl.bindFramebuffer(gl.FRAMEBUFFER, resolveTargetFramebuffer);
34224
34610
  glRenderTarget.width = renderTarget.colorTexture.source.pixelWidth;
34225
34611
  glRenderTarget.height = renderTarget.colorTexture.source.pixelHeight;
34226
- renderTarget.colorTextures.forEach((colorTexture, i) => {
34612
+ const colorTextures = renderTarget.colorTextures;
34613
+ colorTextures.forEach((colorTexture, i) => {
34227
34614
  const source = colorTexture.source;
34228
34615
  if (source.antialias) {
34229
34616
  if (renderer.context.supports.msaa) {
@@ -34357,6 +34744,20 @@ class GlRenderTargetAdaptor {
34357
34744
  );
34358
34745
  }
34359
34746
  }
34747
+ _setDrawBuffers(renderTarget, gl) {
34748
+ const count = renderTarget.colorTextures.length;
34749
+ const bufferArray = this._drawBuffersCache[count];
34750
+ if (this._renderer.context.webGLVersion === 1) {
34751
+ const ext = this._renderer.context.extensions.drawBuffers;
34752
+ if (!ext) {
34753
+ warn("[RenderTexture] This WebGL1 context does not support rendering to multiple targets");
34754
+ } else {
34755
+ ext.drawBuffersWEBGL(bufferArray);
34756
+ }
34757
+ } else {
34758
+ gl.drawBuffers(bufferArray);
34759
+ }
34760
+ }
34360
34761
  }
34361
34762
 
34362
34763
  function calculateProjection(pm, x, y, width, height, flipY) {
@@ -34901,6 +35302,7 @@ class BufferResource extends EventEmitter {
34901
35302
  }
34902
35303
  this.emit("change", this);
34903
35304
  this.buffer = null;
35305
+ this.removeAllListeners();
34904
35306
  }
34905
35307
  }
34906
35308
 
@@ -35315,7 +35717,6 @@ class GlShaderSystem {
35315
35717
  this._programDataHash = /* @__PURE__ */ Object.create(null);
35316
35718
  this._shaderSyncFunctions = /* @__PURE__ */ Object.create(null);
35317
35719
  this._renderer = renderer;
35318
- this._renderer.renderableGC.addManagedHash(this, "_programDataHash");
35319
35720
  }
35320
35721
  contextChange(gl) {
35321
35722
  this._gl = gl;
@@ -35402,9 +35803,7 @@ class GlShaderSystem {
35402
35803
  }
35403
35804
  destroy() {
35404
35805
  for (const key of Object.keys(this._programDataHash)) {
35405
- const programData = this._programDataHash[key];
35406
- programData.destroy();
35407
- this._programDataHash[key] = null;
35806
+ this._programDataHash[key].destroy();
35408
35807
  }
35409
35808
  this._programDataHash = null;
35410
35809
  this._shaderSyncFunctions = null;
@@ -35948,6 +36347,8 @@ class GlTexture {
35948
36347
  this.format = GL_FORMATS.RGBA;
35949
36348
  this.samplerType = 0;
35950
36349
  }
36350
+ destroy() {
36351
+ }
35951
36352
  }
35952
36353
 
35953
36354
  const glUploadBufferImageResource = {
@@ -36487,8 +36888,6 @@ function mapFormatToGlType(gl) {
36487
36888
  const BYTES_PER_PIXEL = 4;
36488
36889
  class GlTextureSystem {
36489
36890
  constructor(renderer) {
36490
- this.managedTextures = [];
36491
- this._glTextures = /* @__PURE__ */ Object.create(null);
36492
36891
  this._glSamplers = /* @__PURE__ */ Object.create(null);
36493
36892
  this._boundTextures = [];
36494
36893
  this._activeTextureLocation = -1;
@@ -36503,8 +36902,18 @@ class GlTextureSystem {
36503
36902
  // TODO - separate samplers will be a cool thing to add, but not right now!
36504
36903
  this._useSeparateSamplers = false;
36505
36904
  this._renderer = renderer;
36506
- this._renderer.renderableGC.addManagedHash(this, "_glTextures");
36507
- this._renderer.renderableGC.addManagedHash(this, "_glSamplers");
36905
+ this._managedTextures = new GCManagedHash({
36906
+ renderer,
36907
+ type: "resource",
36908
+ onUnload: this.onSourceUnload.bind(this),
36909
+ name: "glTexture"
36910
+ });
36911
+ }
36912
+ /**
36913
+ * @deprecated since 8.15.0
36914
+ */
36915
+ get managedTextures() {
36916
+ return Object.values(this._managedTextures.items);
36508
36917
  }
36509
36918
  contextChange(gl) {
36510
36919
  this._gl = gl;
@@ -36513,7 +36922,7 @@ class GlTextureSystem {
36513
36922
  this._mapFormatToType = mapFormatToGlType(gl);
36514
36923
  this._mapFormatToFormat = mapFormatToGlFormat(gl);
36515
36924
  }
36516
- this._glTextures = /* @__PURE__ */ Object.create(null);
36925
+ this._managedTextures.removeAll(true);
36517
36926
  this._glSamplers = /* @__PURE__ */ Object.create(null);
36518
36927
  this._boundSamplers = /* @__PURE__ */ Object.create(null);
36519
36928
  this._premultiplyAlpha = false;
@@ -36545,7 +36954,7 @@ class GlTextureSystem {
36545
36954
  }
36546
36955
  bindSource(source, location = 0) {
36547
36956
  const gl = this._gl;
36548
- source._touched = this._renderer.textureGC.count;
36957
+ source._gcLastUsed = this._renderer.gc.now;
36549
36958
  if (this._boundTextures[location] !== source) {
36550
36959
  this._boundTextures[location] = source;
36551
36960
  this._activateLocation(location);
@@ -36596,15 +37005,13 @@ class GlTextureSystem {
36596
37005
  const biggestDimension = Math.max(source.width, source.height);
36597
37006
  source.mipLevelCount = Math.floor(Math.log2(biggestDimension)) + 1;
36598
37007
  }
36599
- this._glTextures[source.uid] = glTexture;
36600
- if (!this.managedTextures.includes(source)) {
37008
+ source._gpuData[this._renderer.uid] = glTexture;
37009
+ const added = this._managedTextures.add(source);
37010
+ if (added) {
36601
37011
  source.on("update", this.onSourceUpdate, this);
36602
37012
  source.on("resize", this.onSourceUpdate, this);
36603
37013
  source.on("styleChange", this.onStyleChange, this);
36604
- source.on("destroy", this.onSourceDestroy, this);
36605
- source.on("unload", this.onSourceUnload, this);
36606
37014
  source.on("updateMipmaps", this.onUpdateMipmaps, this);
36607
- this.managedTextures.push(source);
36608
37015
  }
36609
37016
  this.onSourceUpdate(source);
36610
37017
  this.updateStyle(source, false);
@@ -36630,13 +37037,18 @@ class GlTextureSystem {
36630
37037
  firstCreation
36631
37038
  );
36632
37039
  }
36633
- onSourceUnload(source) {
36634
- const glTexture = this._glTextures[source.uid];
37040
+ onSourceUnload(source, contextLost = false) {
37041
+ const glTexture = source._gpuData[this._renderer.uid];
36635
37042
  if (!glTexture)
36636
37043
  return;
36637
- this.unbind(source);
36638
- this._glTextures[source.uid] = null;
36639
- this._gl.deleteTexture(glTexture.texture);
37044
+ if (!contextLost) {
37045
+ this.unbind(source);
37046
+ this._gl.deleteTexture(glTexture.texture);
37047
+ }
37048
+ source.off("update", this.onSourceUpdate, this);
37049
+ source.off("resize", this.onSourceUpdate, this);
37050
+ source.off("styleChange", this.onStyleChange, this);
37051
+ source.off("updateMipmaps", this.onUpdateMipmaps, this);
36640
37052
  }
36641
37053
  onSourceUpdate(source) {
36642
37054
  const gl = this._gl;
@@ -36651,7 +37063,17 @@ class GlTextureSystem {
36651
37063
  if (this._uploads[source.uploadMethodId]) {
36652
37064
  this._uploads[source.uploadMethodId].upload(source, glTexture, gl, this._renderer.context.webGLVersion);
36653
37065
  } else {
36654
- gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, source.pixelWidth, source.pixelHeight, 0, gl.RGBA, gl.UNSIGNED_BYTE, null);
37066
+ gl.texImage2D(
37067
+ gl.TEXTURE_2D,
37068
+ 0,
37069
+ glTexture.internalFormat,
37070
+ source.pixelWidth,
37071
+ source.pixelHeight,
37072
+ 0,
37073
+ glTexture.format,
37074
+ glTexture.type,
37075
+ null
37076
+ );
36655
37077
  }
36656
37078
  if (source.autoGenerateMipmaps && source.mipLevelCount > 1) {
36657
37079
  this.onUpdateMipmaps(source, false);
@@ -36663,16 +37085,6 @@ class GlTextureSystem {
36663
37085
  const glTexture = this.getGlSource(source);
36664
37086
  this._gl.generateMipmap(glTexture.target);
36665
37087
  }
36666
- onSourceDestroy(source) {
36667
- source.off("destroy", this.onSourceDestroy, this);
36668
- source.off("update", this.onSourceUpdate, this);
36669
- source.off("resize", this.onSourceUpdate, this);
36670
- source.off("unload", this.onSourceUnload, this);
36671
- source.off("styleChange", this.onStyleChange, this);
36672
- source.off("updateMipmaps", this.onUpdateMipmaps, this);
36673
- this.managedTextures.splice(this.managedTextures.indexOf(source), 1);
36674
- this.onSourceUnload(source);
36675
- }
36676
37088
  _initSampler(style) {
36677
37089
  const gl = this._gl;
36678
37090
  const glSampler = this._gl.createSampler();
@@ -36693,7 +37105,8 @@ class GlTextureSystem {
36693
37105
  return this._glSamplers[sampler._resourceId] || this._initSampler(sampler);
36694
37106
  }
36695
37107
  getGlSource(source) {
36696
- return this._glTextures[source.uid] || this._initSource(source);
37108
+ source._gcLastUsed = this._renderer.gc.now;
37109
+ return source._gpuData[this._renderer.uid] || this._initSource(source);
36697
37110
  }
36698
37111
  generateCanvas(texture) {
36699
37112
  const { pixels, width, height } = this.getPixels(texture);
@@ -36731,9 +37144,7 @@ class GlTextureSystem {
36731
37144
  return { pixels: new Uint8ClampedArray(pixels.buffer), width, height };
36732
37145
  }
36733
37146
  destroy() {
36734
- this.managedTextures.slice().forEach((source) => this.onSourceDestroy(source));
36735
- this.managedTextures = null;
36736
- this._glTextures = null;
37147
+ this._managedTextures.destroy();
36737
37148
  this._glSamplers = null;
36738
37149
  this._boundTextures = null;
36739
37150
  this._boundSamplers = null;
@@ -36937,6 +37348,7 @@ class BatchableSprite {
36937
37348
  this.bounds = null;
36938
37349
  }
36939
37350
  destroy() {
37351
+ this.reset();
36940
37352
  }
36941
37353
  }
36942
37354
 
@@ -37149,9 +37561,9 @@ function updateColorBlendVisibility(container, parent, updateFlags) {
37149
37561
  }
37150
37562
 
37151
37563
  function validateRenderables(renderGroup, renderPipes) {
37152
- const { list, index } = renderGroup.childrenRenderablesToUpdate;
37564
+ const { list } = renderGroup.childrenRenderablesToUpdate;
37153
37565
  let rebuildRequired = false;
37154
- for (let i = 0; i < index; i++) {
37566
+ for (let i = 0; i < renderGroup.childrenRenderablesToUpdate.index; i++) {
37155
37567
  const container = list[i];
37156
37568
  const renderable = container;
37157
37569
  const pipe = renderPipes[renderable.renderPipeId];
@@ -37963,9 +38375,19 @@ _ExtractSystem.defaultImageOptions = {
37963
38375
  let ExtractSystem = _ExtractSystem;
37964
38376
 
37965
38377
  class RenderTexture extends Texture {
38378
+ /**
38379
+ * Creates a RenderTexture. Pass `dynamic: true` in options to allow resizing after creation.
38380
+ * @param options - Options for the RenderTexture, including width, height, and dynamic.
38381
+ * @returns A new RenderTexture instance.
38382
+ * @example
38383
+ * const rt = RenderTexture.create({ width: 100, height: 100, dynamic: true });
38384
+ * rt.resize(500, 500);
38385
+ */
37966
38386
  static create(options) {
38387
+ const { dynamic, ...rest } = options;
37967
38388
  return new RenderTexture({
37968
- source: new TextureSource(options)
38389
+ source: new TextureSource(rest),
38390
+ dynamic: dynamic ?? false
37969
38391
  });
37970
38392
  }
37971
38393
  /**
@@ -38076,6 +38498,278 @@ GenerateTextureSystem.extension = {
38076
38498
  name: "textureGenerator"
38077
38499
  };
38078
38500
 
38501
+ const _GCSystem = class _GCSystem {
38502
+ /**
38503
+ * Creates a new GCSystem instance.
38504
+ * @param renderer - The renderer this garbage collection system works for
38505
+ */
38506
+ constructor(renderer) {
38507
+ /** Array of resources being tracked for garbage collection */
38508
+ this._managedResources = [];
38509
+ this._managedResourceHashes = [];
38510
+ this._ready = false;
38511
+ this._renderer = renderer;
38512
+ }
38513
+ /**
38514
+ * Initializes the garbage collection system with the provided options.
38515
+ * @param options - Configuration options
38516
+ */
38517
+ init(options) {
38518
+ options = { ..._GCSystem.defaultOptions, ...options };
38519
+ this.maxUnusedTime = options.gcMaxUnusedTime;
38520
+ this._frequency = options.gcFrequency;
38521
+ this.enabled = options.gcActive;
38522
+ this.now = performance.now();
38523
+ }
38524
+ /**
38525
+ * Gets whether the garbage collection system is currently enabled.
38526
+ * @returns True if GC is enabled, false otherwise
38527
+ */
38528
+ get enabled() {
38529
+ return !!this._handler;
38530
+ }
38531
+ /**
38532
+ * Enables or disables the garbage collection system.
38533
+ * When enabled, schedules periodic cleanup of resources.
38534
+ * When disabled, cancels all scheduled cleanups.
38535
+ */
38536
+ set enabled(value) {
38537
+ if (this.enabled === value)
38538
+ return;
38539
+ if (value) {
38540
+ this._handler = this._renderer.scheduler.repeat(
38541
+ () => {
38542
+ this._ready = true;
38543
+ },
38544
+ this._frequency,
38545
+ false
38546
+ );
38547
+ } else {
38548
+ this._renderer.scheduler.cancel(this._handler);
38549
+ this._handler = 0;
38550
+ }
38551
+ }
38552
+ /**
38553
+ * Called before rendering. Updates the current timestamp.
38554
+ * @param options - The render options
38555
+ * @param options.container - The container to render
38556
+ */
38557
+ prerender({ container }) {
38558
+ this.now = performance.now();
38559
+ container.renderGroup.gcTick = this._renderer.tick++;
38560
+ this._updateInstructionGCTick(container.renderGroup, container.renderGroup.gcTick);
38561
+ }
38562
+ /** Performs garbage collection after rendering. */
38563
+ postrender() {
38564
+ if (!this._ready || !this.enabled)
38565
+ return;
38566
+ this.run();
38567
+ this._ready = false;
38568
+ }
38569
+ /**
38570
+ * Updates the GC tick counter for a render group and its children.
38571
+ * @param renderGroup - The render group to update
38572
+ * @param gcTick - The new tick value
38573
+ */
38574
+ _updateInstructionGCTick(renderGroup, gcTick) {
38575
+ renderGroup.instructionSet.gcTick = gcTick;
38576
+ for (const child of renderGroup.renderGroupChildren) {
38577
+ this._updateInstructionGCTick(child, gcTick);
38578
+ }
38579
+ }
38580
+ /**
38581
+ * Registers a resource for garbage collection tracking.
38582
+ * @param resource - The resource to track
38583
+ * @param type - The type of resource to track
38584
+ */
38585
+ addResource(resource, type) {
38586
+ if (resource._gcLastUsed !== -1) {
38587
+ resource._gcLastUsed = this.now;
38588
+ resource._onTouch?.(this.now);
38589
+ return;
38590
+ }
38591
+ const index = this._managedResources.length;
38592
+ resource._gcData = {
38593
+ index,
38594
+ type
38595
+ };
38596
+ resource._gcLastUsed = this.now;
38597
+ resource._onTouch?.(this.now);
38598
+ resource.once("unload", this.removeResource, this);
38599
+ this._managedResources.push(resource);
38600
+ }
38601
+ /**
38602
+ * Removes a resource from garbage collection tracking.
38603
+ * Call this when manually destroying a resource.
38604
+ * @param resource - The resource to stop tracking
38605
+ */
38606
+ removeResource(resource) {
38607
+ const gcData = resource._gcData;
38608
+ if (!gcData)
38609
+ return;
38610
+ const index = gcData.index;
38611
+ const last = this._managedResources.length - 1;
38612
+ if (index !== last) {
38613
+ const lastResource = this._managedResources[last];
38614
+ this._managedResources[index] = lastResource;
38615
+ lastResource._gcData.index = index;
38616
+ }
38617
+ this._managedResources.length--;
38618
+ resource._gcData = null;
38619
+ resource._gcLastUsed = -1;
38620
+ }
38621
+ /**
38622
+ * Registers a hash-based resource collection for garbage collection tracking.
38623
+ * Resources in the hash will be automatically tracked and cleaned up when unused.
38624
+ * @param context - The object containing the hash property
38625
+ * @param hash - The property name on context that holds the resource hash
38626
+ * @param type - The type of resources in the hash ('resource' or 'renderable')
38627
+ * @param priority - Processing priority (lower values are processed first)
38628
+ */
38629
+ addResourceHash(context, hash, type, priority = 0) {
38630
+ this._managedResourceHashes.push({
38631
+ context,
38632
+ hash,
38633
+ type,
38634
+ priority
38635
+ });
38636
+ this._managedResourceHashes.sort((a, b) => a.priority - b.priority);
38637
+ }
38638
+ /**
38639
+ * Performs garbage collection by cleaning up unused resources.
38640
+ * Removes resources that haven't been used for longer than maxUnusedTime.
38641
+ */
38642
+ run() {
38643
+ const now = performance.now();
38644
+ const managedResourceHashes = this._managedResourceHashes;
38645
+ for (const hashEntry of managedResourceHashes) {
38646
+ this.runOnHash(hashEntry, now);
38647
+ }
38648
+ let writeIndex = 0;
38649
+ for (let i = 0; i < this._managedResources.length; i++) {
38650
+ const resource = this._managedResources[i];
38651
+ writeIndex = this.runOnResource(resource, now, writeIndex);
38652
+ }
38653
+ this._managedResources.length = writeIndex;
38654
+ }
38655
+ updateRenderableGCTick(renderable, now) {
38656
+ const renderGroup = renderable.renderGroup ?? renderable.parentRenderGroup;
38657
+ const currentTick = renderGroup?.instructionSet?.gcTick ?? -1;
38658
+ if ((renderGroup?.gcTick ?? 0) === currentTick) {
38659
+ renderable._gcLastUsed = now;
38660
+ renderable._onTouch?.(now);
38661
+ }
38662
+ }
38663
+ runOnResource(resource, now, writeIndex) {
38664
+ const gcData = resource._gcData;
38665
+ if (gcData.type === "renderable") {
38666
+ this.updateRenderableGCTick(resource, now);
38667
+ }
38668
+ const isRecentlyUsed = now - resource._gcLastUsed < this.maxUnusedTime;
38669
+ if (isRecentlyUsed || !resource.autoGarbageCollect) {
38670
+ this._managedResources[writeIndex] = resource;
38671
+ gcData.index = writeIndex;
38672
+ writeIndex++;
38673
+ } else {
38674
+ resource.unload();
38675
+ resource._gcData = null;
38676
+ resource._gcLastUsed = -1;
38677
+ resource.off("unload", this.removeResource, this);
38678
+ }
38679
+ return writeIndex;
38680
+ }
38681
+ /**
38682
+ * Creates a clone of the hash, copying all non-null entries up to (but not including) the stop key.
38683
+ * @param hashValue - The original hash to clone from
38684
+ * @param stopKey - The key to stop at (exclusive)
38685
+ * @returns A new hash object with copied entries
38686
+ */
38687
+ _createHashClone(hashValue, stopKey) {
38688
+ const hashClone = /* @__PURE__ */ Object.create(null);
38689
+ for (const k in hashValue) {
38690
+ if (k === stopKey)
38691
+ break;
38692
+ if (hashValue[k] !== null)
38693
+ hashClone[k] = hashValue[k];
38694
+ }
38695
+ return hashClone;
38696
+ }
38697
+ runOnHash(hashEntry, now) {
38698
+ const { context, hash, type } = hashEntry;
38699
+ const hashValue = context[hash];
38700
+ let hashClone = null;
38701
+ let nullCount = 0;
38702
+ for (const key in hashValue) {
38703
+ const resource = hashValue[key];
38704
+ if (resource === null) {
38705
+ nullCount++;
38706
+ if (nullCount === 1e4 && !hashClone) {
38707
+ hashClone = this._createHashClone(hashValue, key);
38708
+ }
38709
+ continue;
38710
+ }
38711
+ if (resource._gcLastUsed === -1) {
38712
+ resource._gcLastUsed = now;
38713
+ resource._onTouch?.(now);
38714
+ if (hashClone)
38715
+ hashClone[key] = resource;
38716
+ continue;
38717
+ }
38718
+ if (type === "renderable") {
38719
+ this.updateRenderableGCTick(resource, now);
38720
+ }
38721
+ const isRecentlyUsed = now - resource._gcLastUsed < this.maxUnusedTime;
38722
+ if (!isRecentlyUsed && resource.autoGarbageCollect) {
38723
+ if (!hashClone) {
38724
+ if (nullCount + 1 !== 1e4) {
38725
+ hashValue[key] = null;
38726
+ nullCount++;
38727
+ } else {
38728
+ hashClone = this._createHashClone(hashValue, key);
38729
+ }
38730
+ }
38731
+ resource.unload();
38732
+ resource._gcData = null;
38733
+ resource._gcLastUsed = -1;
38734
+ } else if (hashClone) {
38735
+ hashClone[key] = resource;
38736
+ }
38737
+ }
38738
+ if (hashClone) {
38739
+ context[hash] = hashClone;
38740
+ }
38741
+ }
38742
+ /** Cleans up the garbage collection system. Disables GC and removes all tracked resources. */
38743
+ destroy() {
38744
+ this.enabled = false;
38745
+ this._managedResources.forEach((resource) => {
38746
+ resource.off("unload", this.removeResource, this);
38747
+ });
38748
+ this._managedResources.length = 0;
38749
+ this._managedResourceHashes.length = 0;
38750
+ this._renderer = null;
38751
+ }
38752
+ };
38753
+ /** @ignore */
38754
+ _GCSystem.extension = {
38755
+ type: [
38756
+ ExtensionType.WebGLSystem,
38757
+ ExtensionType.WebGPUSystem
38758
+ ],
38759
+ name: "gc",
38760
+ priority: 0
38761
+ };
38762
+ /** Default options for the GCSystem */
38763
+ _GCSystem.defaultOptions = {
38764
+ /** Enable/disable the garbage collector */
38765
+ gcActive: true,
38766
+ /** Time in ms before an unused resource is collected (default 1 minute) */
38767
+ gcMaxUnusedTime: 6e4,
38768
+ /** How often to run garbage collection in ms (default 30 seconds) */
38769
+ gcFrequency: 3e4
38770
+ };
38771
+ let GCSystem = _GCSystem;
38772
+
38079
38773
  function color32BitToUniform(abgr, out, offset) {
38080
38774
  const alpha = (abgr >> 24 & 255) / 255;
38081
38775
  out[offset++] = (abgr & 255) / 255 * alpha;
@@ -38582,48 +39276,86 @@ _RenderableGCSystem.defaultOptions = {
38582
39276
  let RenderableGCSystem = _RenderableGCSystem;
38583
39277
 
38584
39278
  const _TextureGCSystem = class _TextureGCSystem {
39279
+ /**
39280
+ * Frame count since started.
39281
+ * @readonly
39282
+ * @deprecated since 8.15.0
39283
+ */
39284
+ get count() {
39285
+ return this._renderer.tick;
39286
+ }
39287
+ /**
39288
+ * Frame count since last garbage collection.
39289
+ * @readonly
39290
+ * @deprecated since 8.15.0
39291
+ */
39292
+ get checkCount() {
39293
+ return this._checkCount;
39294
+ }
39295
+ set checkCount(value) {
39296
+ deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead.");
39297
+ this._checkCount = value;
39298
+ }
39299
+ /**
39300
+ * Maximum idle frames before a texture is destroyed by garbage collection.
39301
+ * @see TextureGCSystem.defaultMaxIdle
39302
+ * @deprecated since 8.15.0
39303
+ */
39304
+ get maxIdle() {
39305
+ return this._renderer.gc.maxUnusedTime / 1e3 * 60;
39306
+ }
39307
+ set maxIdle(value) {
39308
+ deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead.");
39309
+ this._renderer.gc.maxUnusedTime = value / 60 * 1e3;
39310
+ }
39311
+ /**
39312
+ * Frames between two garbage collections.
39313
+ * @see TextureGCSystem.defaultCheckCountMax
39314
+ * @deprecated since 8.15.0
39315
+ */
39316
+ // eslint-disable-next-line dot-notation
39317
+ get checkCountMax() {
39318
+ return Math.floor(this._renderer.gc["_frequency"] / 1e3);
39319
+ }
39320
+ set checkCountMax(_value) {
39321
+ deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead.");
39322
+ }
39323
+ /**
39324
+ * Current garbage collection mode.
39325
+ * @see TextureGCSystem.defaultMode
39326
+ * @deprecated since 8.15.0
39327
+ */
39328
+ get active() {
39329
+ return this._renderer.gc.enabled;
39330
+ }
39331
+ set active(value) {
39332
+ deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead.");
39333
+ this._renderer.gc.enabled = value;
39334
+ }
38585
39335
  /** @param renderer - The renderer this System works for. */
38586
39336
  constructor(renderer) {
38587
39337
  this._renderer = renderer;
38588
- this.count = 0;
38589
- this.checkCount = 0;
39338
+ this._checkCount = 0;
38590
39339
  }
38591
39340
  init(options) {
38592
- options = { ..._TextureGCSystem.defaultOptions, ...options };
38593
- this.checkCountMax = options.textureGCCheckCountMax;
38594
- this.maxIdle = options.textureGCAMaxIdle ?? options.textureGCMaxIdle;
38595
- this.active = options.textureGCActive;
38596
- }
38597
- /**
38598
- * Checks to see when the last time a texture was used.
38599
- * If the texture has not been used for a specified amount of time, it will be removed from the GPU.
38600
- */
38601
- postrender() {
38602
- if (!this._renderer.renderingToScreen) {
38603
- return;
39341
+ if (options.textureGCActive !== _TextureGCSystem.defaultOptions.textureGCActive) {
39342
+ this.active = options.textureGCActive;
38604
39343
  }
38605
- this.count++;
38606
- if (!this.active)
38607
- return;
38608
- this.checkCount++;
38609
- if (this.checkCount > this.checkCountMax) {
38610
- this.checkCount = 0;
38611
- this.run();
39344
+ if (options.textureGCMaxIdle !== _TextureGCSystem.defaultOptions.textureGCMaxIdle) {
39345
+ this.maxIdle = options.textureGCMaxIdle;
39346
+ }
39347
+ if (options.textureGCCheckCountMax !== _TextureGCSystem.defaultOptions.textureGCCheckCountMax) {
39348
+ this.checkCountMax = options.textureGCCheckCountMax;
38612
39349
  }
38613
39350
  }
38614
39351
  /**
38615
39352
  * Checks to see when the last time a texture was used.
38616
39353
  * If the texture has not been used for a specified amount of time, it will be removed from the GPU.
39354
+ * @deprecated since 8.15.0
38617
39355
  */
38618
39356
  run() {
38619
- const managedTextures = this._renderer.texture.managedTextures;
38620
- for (let i = 0; i < managedTextures.length; i++) {
38621
- const texture = managedTextures[i];
38622
- if (texture.autoGarbageCollect && texture.resource && texture._touched > -1 && this.count - texture._touched > this.maxIdle) {
38623
- texture._touched = -1;
38624
- texture.unload();
38625
- }
38626
- }
39357
+ deprecation("8.15.0", "TextureGCSystem.run is deprecated, please use the GCSystem instead.");
39358
+ this._renderer.gc.run();
38627
39359
  }
38628
39360
  destroy() {
38629
39361
  this._renderer = null;
@@ -38637,7 +39369,10 @@ _TextureGCSystem.extension = {
38637
39369
  ],
38638
39370
  name: "textureGC"
38639
39371
  };
38640
- /** default options for the TextureGCSystem */
39372
+ /**
39373
+ * Default options for the TextureGCSystem
39374
+ * @deprecated since 8.15.0
39375
+ */
38641
39376
  _TextureGCSystem.defaultOptions = {
38642
39377
  /**
38643
39378
  * If set to true, this will enable the garbage collector on the GPU.
@@ -38778,6 +39513,7 @@ const SharedSystems = [
38778
39513
  HelloSystem,
38779
39514
  ViewSystem,
38780
39515
  RenderGroupSystem,
39516
+ GCSystem,
38781
39517
  TextureGCSystem,
38782
39518
  GenerateTextureSystem,
38783
39519
  ExtractSystem,
@@ -38844,7 +39580,6 @@ class BindGroupSystem {
38844
39580
  constructor(renderer) {
38845
39581
  this._hash = /* @__PURE__ */ Object.create(null);
38846
39582
  this._renderer = renderer;
38847
- this._renderer.renderableGC.addManagedHash(this, "_hash");
38848
39583
  }
38849
39584
  contextChange(gpu) {
38850
39585
  this._gpu = gpu;
@@ -38890,7 +39625,7 @@ class BindGroupSystem {
38890
39625
  gpuResource = renderer.texture.getGpuSampler(sampler);
38891
39626
  } else if (resource._resourceType === "textureSource") {
38892
39627
  const texture = resource;
38893
- gpuResource = renderer.texture.getGpuSource(texture).createView({});
39628
+ gpuResource = renderer.texture.getGpuSource(texture).createView();
38894
39629
  }
38895
39630
  entries.push({
38896
39631
  binding: groupLayout[j],
@@ -38906,9 +39641,6 @@ class BindGroupSystem {
38906
39641
  return gpuBindGroup;
38907
39642
  }
38908
39643
  destroy() {
38909
- for (const key of Object.keys(this._hash)) {
38910
- this._hash[key] = null;
38911
- }
38912
39644
  this._hash = null;
38913
39645
  this._renderer = null;
38914
39646
  }
@@ -38921,20 +39653,34 @@ BindGroupSystem.extension = {
38921
39653
  name: "bindGroup"
38922
39654
  };
38923
39655
 
39656
+ class GpuBufferData {
39657
+ constructor(gpuBuffer) {
39658
+ this.gpuBuffer = gpuBuffer;
39659
+ }
39660
+ destroy() {
39661
+ this.gpuBuffer.destroy();
39662
+ this.gpuBuffer = null;
39663
+ }
39664
+ }
38924
39665
  class GpuBufferSystem {
38925
39666
  constructor(renderer) {
38926
- this._gpuBuffers = /* @__PURE__ */ Object.create(null);
38927
- this._managedBuffers = [];
38928
- renderer.renderableGC.addManagedHash(this, "_gpuBuffers");
39667
+ this._renderer = renderer;
39668
+ this._managedBuffers = new GCManagedHash({
39669
+ renderer,
39670
+ type: "resource",
39671
+ onUnload: this.onBufferUnload.bind(this),
39672
+ name: "gpuBuffer"
39673
+ });
38929
39674
  }
38930
39675
  contextChange(gpu) {
38931
39676
  this._gpu = gpu;
38932
39677
  }
38933
39678
  getGPUBuffer(buffer) {
38934
- return this._gpuBuffers[buffer.uid] || this.createGPUBuffer(buffer);
39679
+ buffer._gcLastUsed = this._renderer.gc.now;
39680
+ return buffer._gpuData[this._renderer.uid]?.gpuBuffer || this.createGPUBuffer(buffer);
38935
39681
  }
38936
39682
  updateBuffer(buffer) {
38937
- const gpuBuffer = this._gpuBuffers[buffer.uid] || this.createGPUBuffer(buffer);
39683
+ const gpuBuffer = this.getGPUBuffer(buffer);
38938
39684
  const data = buffer.data;
38939
39685
  if (buffer._updateID && data) {
38940
39686
  buffer._updateID = 0;
@@ -38951,53 +39697,36 @@ class GpuBufferSystem {
38951
39697
  }
38952
39698
  /** dispose all WebGL resources of all managed buffers */
38953
39699
  destroyAll() {
38954
- for (const id in this._gpuBuffers) {
38955
- this._gpuBuffers[id].destroy();
38956
- }
38957
- this._gpuBuffers = {};
39700
+ this._managedBuffers.removeAll();
39701
+ }
39702
+ onBufferUnload(buffer) {
39703
+ buffer.off("update", this.updateBuffer, this);
39704
+ buffer.off("change", this.onBufferChange, this);
38958
39705
  }
38959
39706
  createGPUBuffer(buffer) {
38960
- if (!this._gpuBuffers[buffer.uid]) {
38961
- buffer.on("update", this.updateBuffer, this);
38962
- buffer.on("change", this.onBufferChange, this);
38963
- buffer.on("destroy", this.onBufferDestroy, this);
38964
- this._managedBuffers.push(buffer);
38965
- }
38966
39707
  const gpuBuffer = this._gpu.device.createBuffer(buffer.descriptor);
38967
39708
  buffer._updateID = 0;
39709
+ buffer._resourceId = uid$1("resource");
38968
39710
  if (buffer.data) {
38969
39711
  fastCopy(buffer.data.buffer, gpuBuffer.getMappedRange());
38970
39712
  gpuBuffer.unmap();
38971
39713
  }
38972
- this._gpuBuffers[buffer.uid] = gpuBuffer;
39714
+ buffer._gpuData[this._renderer.uid] = new GpuBufferData(gpuBuffer);
39715
+ if (this._managedBuffers.add(buffer)) {
39716
+ buffer.on("update", this.updateBuffer, this);
39717
+ buffer.on("change", this.onBufferChange, this);
39718
+ }
38973
39719
  return gpuBuffer;
38974
39720
  }
38975
39721
  onBufferChange(buffer) {
38976
- const gpuBuffer = this._gpuBuffers[buffer.uid];
38977
- gpuBuffer.destroy();
39722
+ this._managedBuffers.remove(buffer);
38978
39723
  buffer._updateID = 0;
38979
- this._gpuBuffers[buffer.uid] = this.createGPUBuffer(buffer);
38980
- }
38981
- /**
38982
- * Disposes buffer
38983
- * @param buffer - buffer with data
38984
- */
38985
- onBufferDestroy(buffer) {
38986
- this._managedBuffers.splice(this._managedBuffers.indexOf(buffer), 1);
38987
- this._destroyBuffer(buffer);
39724
+ this.createGPUBuffer(buffer);
38988
39725
  }
38989
39726
  destroy() {
38990
- this._managedBuffers.forEach((buffer) => this._destroyBuffer(buffer));
38991
- this._managedBuffers = null;
38992
- this._gpuBuffers = null;
38993
- }
38994
- _destroyBuffer(buffer) {
38995
- const gpuBuffer = this._gpuBuffers[buffer.uid];
38996
- gpuBuffer.destroy();
38997
- buffer.off("update", this.updateBuffer, this);
38998
- buffer.off("change", this.onBufferChange, this);
38999
- buffer.off("destroy", this.onBufferDestroy, this);
39000
- this._gpuBuffers[buffer.uid] = null;
39727
+ this._managedBuffers.destroy();
39728
+ this._renderer = null;
39729
+ this._gpu = null;
39001
39730
  }
39002
39731
  }
39003
39732
  /** @ignore */
@@ -39194,7 +39923,7 @@ class GpuEncoderSystem {
39194
39923
  if (this._boundBindGroup[index] === bindGroup)
39195
39924
  return;
39196
39925
  this._boundBindGroup[index] = bindGroup;
39197
- bindGroup._touch(this._renderer.textureGC.count);
39926
+ bindGroup._touch(this._renderer.gc.now, this._renderer.tick);
39198
39927
  const gpuBindGroup = this._renderer.bindGroup.getBindGroup(bindGroup, program, index);
39199
39928
  this.renderPassEncoder.setBindGroup(index, gpuBindGroup);
39200
39929
  }
@@ -39480,7 +40209,6 @@ class GpuUniformBatchPipe {
39480
40209
  this._bindGroups = [];
39481
40210
  this._bufferResources = [];
39482
40211
  this._renderer = renderer;
39483
- this._renderer.renderableGC.addManagedHash(this, "_bindGroupHash");
39484
40212
  this._batchBuffer = new UboBatch({ minUniformOffsetAlignment });
39485
40213
  const totalBuffers = 256 / minUniformOffsetAlignment;
39486
40214
  for (let i = 0; i < totalBuffers; i++) {
@@ -39498,9 +40226,7 @@ class GpuUniformBatchPipe {
39498
40226
  this._resetBindGroups();
39499
40227
  }
39500
40228
  _resetBindGroups() {
39501
- for (const i in this._bindGroupHash) {
39502
- this._bindGroupHash[i] = null;
39503
- }
40229
+ this._bindGroupHash = /* @__PURE__ */ Object.create(null);
39504
40230
  this._batchBuffer.clear();
39505
40231
  }
39506
40232
  // just works for single bind groups for now
@@ -39583,7 +40309,6 @@ class GpuUniformBatchPipe {
39583
40309
  }
39584
40310
  this._bufferResources = null;
39585
40311
  this._batchBuffer.destroy();
39586
- this._bindGroupHash = null;
39587
40312
  this._renderer = null;
39588
40313
  }
39589
40314
  }
@@ -39605,8 +40330,8 @@ const topologyStringToId = {
39605
40330
  function getGraphicsStateKey(geometryLayout, shaderKey, state, blendMode, topology) {
39606
40331
  return geometryLayout << 24 | shaderKey << 16 | state << 10 | blendMode << 5 | topology;
39607
40332
  }
39608
- function getGlobalStateKey(stencilStateId, multiSampleCount, colorMask, renderTarget) {
39609
- return colorMask << 6 | stencilStateId << 3 | renderTarget << 1 | multiSampleCount;
40333
+ function getGlobalStateKey(stencilStateId, multiSampleCount, colorMask, renderTarget, colorTargetCount) {
40334
+ return colorMask << 8 | stencilStateId << 5 | renderTarget << 3 | colorTargetCount << 1 | multiSampleCount;
39610
40335
  }
39611
40336
  class PipelineSystem {
39612
40337
  constructor(renderer) {
@@ -39617,6 +40342,7 @@ class PipelineSystem {
39617
40342
  this._pipeStateCaches = /* @__PURE__ */ Object.create(null);
39618
40343
  this._colorMask = 15;
39619
40344
  this._multisampleCount = 1;
40345
+ this._colorTargetCount = 1;
39620
40346
  this._renderer = renderer;
39621
40347
  }
39622
40348
  contextChange(gpu) {
@@ -39633,6 +40359,7 @@ class PipelineSystem {
39633
40359
  setRenderTarget(renderTarget) {
39634
40360
  this._multisampleCount = renderTarget.msaaSamples;
39635
40361
  this._depthStencilAttachment = renderTarget.descriptor.depthStencilAttachment ? 1 : 0;
40362
+ this._colorTargetCount = renderTarget.colorTargetCount;
39636
40363
  this._updatePipeHash();
39637
40364
  }
39638
40365
  setColorMask(colorMask) {
@@ -39673,8 +40400,11 @@ class PipelineSystem {
39673
40400
  _createPipeline(geometry, program, state, topology) {
39674
40401
  const device = this._gpu.device;
39675
40402
  const buffers = this._createVertexBufferLayouts(geometry, program);
39676
- const blendModes = this._renderer.state.getColorTargets(state);
39677
- blendModes[0].writeMask = this._stencilMode === STENCIL_MODES.RENDERING_MASK_ADD ? 0 : this._colorMask;
40403
+ const blendModes = this._renderer.state.getColorTargets(state, this._colorTargetCount);
40404
+ const writeMask = this._stencilMode === STENCIL_MODES.RENDERING_MASK_ADD ? 0 : this._colorMask;
40405
+ for (let i = 0; i < blendModes.length; i++) {
40406
+ blendModes[i].writeMask = writeMask;
40407
+ }
39678
40408
  const layout = this._renderer.shader.getProgramData(program).pipeline;
39679
40409
  const descriptor = {
39680
40410
  // TODO later check if its helpful to create..
@@ -39818,7 +40548,8 @@ class PipelineSystem {
39818
40548
  this._stencilMode,
39819
40549
  this._multisampleCount,
39820
40550
  this._colorMask,
39821
- this._depthStencilAttachment
40551
+ this._depthStencilAttachment,
40552
+ this._colorTargetCount
39822
40553
  );
39823
40554
  if (!this._pipeStateCaches[key]) {
39824
40555
  this._pipeStateCaches[key] = /* @__PURE__ */ Object.create(null);
@@ -39978,6 +40709,7 @@ class GpuRenderTargetAdaptor {
39978
40709
  initGpuRenderTarget(renderTarget) {
39979
40710
  renderTarget.isRoot = true;
39980
40711
  const gpuRenderTarget = new GpuRenderTarget();
40712
+ gpuRenderTarget.colorTargetCount = renderTarget.colorTextures.length;
39981
40713
  renderTarget.colorTextures.forEach((colorTexture, i) => {
39982
40714
  if (colorTexture instanceof CanvasSource) {
39983
40715
  const context = colorTexture.resource.getContext(
@@ -40250,16 +40982,20 @@ class GpuStateSystem {
40250
40982
  /**
40251
40983
  * Gets the blend mode data for the current state
40252
40984
  * @param state - The state to get the blend mode from
40985
+ * @param count - The number of color targets to create
40253
40986
  */
40254
- getColorTargets(state) {
40987
+ getColorTargets(state, count) {
40255
40988
  const blend = GpuBlendModesToPixi[state.blendMode] || GpuBlendModesToPixi.normal;
40256
- return [
40257
- {
40258
- format: "bgra8unorm",
40259
- writeMask: 0,
40260
- blend
40261
- }
40262
- ];
40989
+ const targets = [];
40990
+ const target = {
40991
+ format: "bgra8unorm",
40992
+ writeMask: 0,
40993
+ blend
40994
+ };
40995
+ for (let i = 0; i < count; i++) {
40996
+ targets[i] = target;
40997
+ }
40998
+ return targets;
40263
40999
  }
40264
41000
  destroy() {
40265
41001
  this.gpu = null;
@@ -40285,7 +41021,7 @@ const gpuUploadBufferImageResource = {
40285
41021
  {
40286
41022
  offset: 0,
40287
41023
  rowsPerImage: source.pixelHeight,
40288
- bytesPerRow: source.pixelHeight * bytesPerPixel
41024
+ bytesPerRow: source.pixelWidth * bytesPerPixel
40289
41025
  },
40290
41026
  {
40291
41027
  width: source.pixelWidth,
@@ -40524,13 +41260,22 @@ class GpuMipmapGenerator {
40524
41260
  }
40525
41261
  }
40526
41262
 
41263
+ class GPUTextureGpuData {
41264
+ constructor(gpuTexture) {
41265
+ this.textureView = null;
41266
+ this.gpuTexture = gpuTexture;
41267
+ }
41268
+ /** Destroys this GPU data instance. */
41269
+ destroy() {
41270
+ this.gpuTexture.destroy();
41271
+ this.textureView = null;
41272
+ this.gpuTexture = null;
41273
+ }
41274
+ }
40527
41275
  class GpuTextureSystem {
40528
41276
  constructor(renderer) {
40529
- this.managedTextures = [];
40530
- this._gpuSources = /* @__PURE__ */ Object.create(null);
40531
41277
  this._gpuSamplers = /* @__PURE__ */ Object.create(null);
40532
41278
  this._bindGroupHash = /* @__PURE__ */ Object.create(null);
40533
- this._textureViewHash = /* @__PURE__ */ Object.create(null);
40534
41279
  this._uploads = {
40535
41280
  image: gpuUploadImageResource,
40536
41281
  buffer: gpuUploadBufferImageResource,
@@ -40538,10 +41283,19 @@ class GpuTextureSystem {
40538
41283
  compressed: gpuUploadCompressedTextureResource
40539
41284
  };
40540
41285
  this._renderer = renderer;
40541
- renderer.renderableGC.addManagedHash(this, "_gpuSources");
40542
- renderer.renderableGC.addManagedHash(this, "_gpuSamplers");
40543
41286
  renderer.renderableGC.addManagedHash(this, "_bindGroupHash");
40544
- renderer.renderableGC.addManagedHash(this, "_textureViewHash");
41287
+ this._managedTextures = new GCManagedHash({
41288
+ renderer,
41289
+ type: "resource",
41290
+ onUnload: this.onSourceUnload.bind(this),
41291
+ name: "gpuTextureSource"
41292
+ });
41293
+ }
41294
+ /**
41295
+ * @deprecated since 8.15.0
41296
+ */
41297
+ get managedTextures() {
41298
+ return Object.values(this._managedTextures.items);
40545
41299
  }
40546
41300
  contextChange(gpu) {
40547
41301
  this._gpu = gpu;
@@ -40552,10 +41306,7 @@ class GpuTextureSystem {
40552
41306
  * @returns The initialized texture source.
40553
41307
  */
40554
41308
  initSource(source) {
40555
- if (this._gpuSources[source.uid]) {
40556
- return this._gpuSources[source.uid];
40557
- }
40558
- return this._initSource(source);
41309
+ return source._gpuData[this._renderer.uid]?.gpuTexture || this._initSource(source);
40559
41310
  }
40560
41311
  _initSource(source) {
40561
41312
  if (source.autoGenerateMipmaps) {
@@ -40579,14 +41330,13 @@ class GpuTextureSystem {
40579
41330
  dimension: source.dimension,
40580
41331
  usage
40581
41332
  };
40582
- const gpuTexture = this._gpuSources[source.uid] = this._gpu.device.createTexture(textureDescriptor);
40583
- if (!this.managedTextures.includes(source)) {
41333
+ const gpuTexture = this._gpu.device.createTexture(textureDescriptor);
41334
+ source._gpuData[this._renderer.uid] = new GPUTextureGpuData(gpuTexture);
41335
+ const added = this._managedTextures.add(source);
41336
+ if (added) {
40584
41337
  source.on("update", this.onSourceUpdate, this);
40585
41338
  source.on("resize", this.onSourceResize, this);
40586
- source.on("destroy", this.onSourceDestroy, this);
40587
- source.on("unload", this.onSourceUnload, this);
40588
41339
  source.on("updateMipmaps", this.onUpdateMipmaps, this);
40589
- this.managedTextures.push(source);
40590
41340
  }
40591
41341
  this.onSourceUpdate(source);
40592
41342
  return gpuTexture;
@@ -40602,13 +41352,6 @@ class GpuTextureSystem {
40602
41352
  this.onUpdateMipmaps(source);
40603
41353
  }
40604
41354
  }
40605
- onSourceUnload(source) {
40606
- const gpuTexture = this._gpuSources[source.uid];
40607
- if (gpuTexture) {
40608
- this._gpuSources[source.uid] = null;
40609
- gpuTexture.destroy();
40610
- }
40611
- }
40612
41355
  onUpdateMipmaps(source) {
40613
41356
  if (!this._mipmapGenerator) {
40614
41357
  this._mipmapGenerator = new GpuMipmapGenerator(this._gpu.device);
@@ -40616,23 +41359,21 @@ class GpuTextureSystem {
40616
41359
  const gpuTexture = this.getGpuSource(source);
40617
41360
  this._mipmapGenerator.generateMipmap(gpuTexture);
40618
41361
  }
40619
- onSourceDestroy(source) {
41362
+ onSourceUnload(source) {
40620
41363
  source.off("update", this.onSourceUpdate, this);
40621
- source.off("unload", this.onSourceUnload, this);
40622
- source.off("destroy", this.onSourceDestroy, this);
40623
41364
  source.off("resize", this.onSourceResize, this);
40624
41365
  source.off("updateMipmaps", this.onUpdateMipmaps, this);
40625
- this.managedTextures.splice(this.managedTextures.indexOf(source), 1);
40626
- this.onSourceUnload(source);
40627
41366
  }
40628
41367
  onSourceResize(source) {
40629
- const gpuTexture = this._gpuSources[source.uid];
41368
+ source._gcLastUsed = this._renderer.gc.now;
41369
+ const gpuData = source._gpuData[this._renderer.uid];
41370
+ const gpuTexture = gpuData?.gpuTexture;
40630
41371
  if (!gpuTexture) {
40631
41372
  this.initSource(source);
40632
41373
  } else if (gpuTexture.width !== source.pixelWidth || gpuTexture.height !== source.pixelHeight) {
40633
- this._textureViewHash[source.uid] = null;
41374
+ gpuData.destroy();
40634
41375
  this._bindGroupHash[source.uid] = null;
40635
- this.onSourceUnload(source);
41376
+ source._gpuData[this._renderer.uid] = null;
40636
41377
  this.initSource(source);
40637
41378
  }
40638
41379
  }
@@ -40644,7 +41385,8 @@ class GpuTextureSystem {
40644
41385
  return this._gpuSamplers[sampler._resourceId] || this._initSampler(sampler);
40645
41386
  }
40646
41387
  getGpuSource(source) {
40647
- return this._gpuSources[source.uid] || this.initSource(source);
41388
+ source._gcLastUsed = this._renderer.gc.now;
41389
+ return source._gpuData[this._renderer.uid]?.gpuTexture || this.initSource(source);
40648
41390
  }
40649
41391
  /**
40650
41392
  * this returns s bind group for a specific texture, the bind group contains
@@ -40656,7 +41398,7 @@ class GpuTextureSystem {
40656
41398
  * @returns the bind group for the texture
40657
41399
  */
40658
41400
  getTextureBindGroup(texture) {
40659
- return this._bindGroupHash[texture.uid] ?? this._createTextureBindGroup(texture);
41401
+ return this._bindGroupHash[texture.uid] || this._createTextureBindGroup(texture);
40660
41402
  }
40661
41403
  _createTextureBindGroup(texture) {
40662
41404
  const source = texture.source;
@@ -40671,11 +41413,15 @@ class GpuTextureSystem {
40671
41413
  }
40672
41414
  getTextureView(texture) {
40673
41415
  const source = texture.source;
40674
- return this._textureViewHash[source.uid] ?? this._createTextureView(source);
40675
- }
40676
- _createTextureView(texture) {
40677
- this._textureViewHash[texture.uid] = this.getGpuSource(texture).createView();
40678
- return this._textureViewHash[texture.uid];
41416
+ source._gcLastUsed = this._renderer.gc.now;
41417
+ let gpuData = source._gpuData[this._renderer.uid];
41418
+ let textureView = null;
41419
+ if (!gpuData) {
41420
+ this.initSource(source);
41421
+ gpuData = source._gpuData[this._renderer.uid];
41422
+ }
41423
+ textureView = gpuData.textureView || gpuData.gpuTexture.createView();
41424
+ return textureView;
40679
41425
  }
40680
41426
  generateCanvas(texture) {
40681
41427
  const renderer = this._renderer;
@@ -40717,20 +41463,17 @@ class GpuTextureSystem {
40717
41463
  return { pixels, width, height };
40718
41464
  }
40719
41465
  destroy() {
40720
- this.managedTextures.slice().forEach((source) => this.onSourceDestroy(source));
40721
- this.managedTextures = null;
41466
+ this._managedTextures.destroy();
40722
41467
  for (const k of Object.keys(this._bindGroupHash)) {
40723
41468
  const key = Number(k);
40724
41469
  const bindGroup = this._bindGroupHash[key];
40725
41470
  bindGroup?.destroy();
40726
- this._bindGroupHash[key] = null;
40727
41471
  }
41472
+ this._renderer = null;
40728
41473
  this._gpu = null;
40729
41474
  this._mipmapGenerator = null;
40730
- this._gpuSources = null;
40731
- this._bindGroupHash = null;
40732
- this._textureViewHash = null;
40733
41475
  this._gpuSamplers = null;
41476
+ this._bindGroupHash = null;
40734
41477
  }
40735
41478
  }
40736
41479
  /** @ignore */
@@ -40947,6 +41690,7 @@ class GraphicsPipe {
40947
41690
  this.renderer = renderer;
40948
41691
  this._adaptor = adaptor;
40949
41692
  this.renderer.runners.contextChange.add(this);
41693
+ this._managedGraphics = new GCManagedHash({ renderer, type: "renderable", priority: -1, name: "graphics" });
40950
41694
  }
40951
41695
  contextChange() {
40952
41696
  this._adaptor.contextChange(this.renderer);
@@ -41023,6 +41767,7 @@ class GraphicsPipe {
41023
41767
  _initGpuDataForRenderable(graphics) {
41024
41768
  const gpuData = new GraphicsGpuData();
41025
41769
  graphics._gpuData[this.renderer.uid] = gpuData;
41770
+ this._managedGraphics.add(graphics);
41026
41771
  return gpuData;
41027
41772
  }
41028
41773
  _updateBatchesForRenderable(graphics, gpuData) {
@@ -41038,6 +41783,7 @@ class GraphicsPipe {
41038
41783
  });
41039
41784
  }
41040
41785
  destroy() {
41786
+ this._managedGraphics.destroy();
41041
41787
  this.renderer = null;
41042
41788
  this._adaptor.destroy();
41043
41789
  this._adaptor = null;
@@ -41610,6 +42356,7 @@ class ParticleContainerPipe {
41610
42356
  this.adaptor = adaptor;
41611
42357
  this.defaultShader = new ParticleShader();
41612
42358
  this.state = State.for2d();
42359
+ this._managedContainers = new GCManagedHash({ renderer, type: "renderable", name: "particleContainer" });
41613
42360
  }
41614
42361
  validateRenderable(_renderable) {
41615
42362
  return false;
@@ -41626,6 +42373,7 @@ class ParticleContainerPipe {
41626
42373
  size: renderable.particleChildren.length,
41627
42374
  properties: renderable._properties
41628
42375
  });
42376
+ this._managedContainers.add(renderable);
41629
42377
  return renderable._gpuData[this.renderer.uid];
41630
42378
  }
41631
42379
  updateRenderable(_renderable) {
@@ -41657,6 +42405,7 @@ class ParticleContainerPipe {
41657
42405
  }
41658
42406
  /** Destroys the ParticleRenderer. */
41659
42407
  destroy() {
42408
+ this._managedContainers.destroy();
41660
42409
  this.renderer = null;
41661
42410
  if (this.defaultShader) {
41662
42411
  this.defaultShader.destroy();
@@ -41799,6 +42548,7 @@ class NineSliceSpriteGpuData extends BatchableMesh {
41799
42548
  class NineSliceSpritePipe {
41800
42549
  constructor(renderer) {
41801
42550
  this._renderer = renderer;
42551
+ this._managedSprites = new GCManagedHash({ renderer, type: "renderable", name: "nineSliceSprite" });
41802
42552
  }
41803
42553
  addRenderable(sprite, instructionSet) {
41804
42554
  const gpuSprite = this._getGpuSprite(sprite);
@@ -41833,12 +42583,14 @@ class NineSliceSpritePipe {
41833
42583
  batchableMesh.transform = sprite.groupTransform;
41834
42584
  batchableMesh.texture = sprite._texture;
41835
42585
  batchableMesh.roundPixels = this._renderer._roundPixels | sprite._roundPixels;
42586
+ this._managedSprites.add(sprite);
41836
42587
  if (!sprite.didViewUpdate) {
41837
42588
  this._updateBatchableSprite(sprite, batchableMesh);
41838
42589
  }
41839
42590
  return gpuData;
41840
42591
  }
41841
42592
  destroy() {
42593
+ this._managedSprites.destroy();
41842
42594
  this._renderer = null;
41843
42595
  }
41844
42596
  }
@@ -42121,6 +42873,7 @@ class TilingSpritePipe {
42121
42873
  constructor(renderer) {
42122
42874
  this._state = State.default2d;
42123
42875
  this._renderer = renderer;
42876
+ this._managedTilingSprites = new GCManagedHash({ renderer, type: "renderable", name: "tilingSprite" });
42124
42877
  }
42125
42878
  validateRenderable(renderable) {
42126
42879
  const tilingSpriteData = this._getTilingSpriteData(renderable);
@@ -42205,6 +42958,7 @@ class TilingSpritePipe {
42205
42958
  const gpuData = new TilingSpriteGpuData();
42206
42959
  gpuData.renderable = tilingSprite;
42207
42960
  tilingSprite._gpuData[this._renderer.uid] = gpuData;
42961
+ this._managedTilingSprites.add(tilingSprite);
42208
42962
  return gpuData;
42209
42963
  }
42210
42964
  _updateBatchableMesh(tilingSprite) {
@@ -42219,6 +42973,7 @@ class TilingSpritePipe {
42219
42973
  setPositions(tilingSprite, geometry.positions);
42220
42974
  }
42221
42975
  destroy() {
42976
+ this._managedTilingSprites.destroy();
42222
42977
  this._renderer = null;
42223
42978
  }
42224
42979
  _updateCanBatch(tilingSprite) {
@@ -42465,6 +43220,7 @@ class BitmapTextGraphics extends Graphics {
42465
43220
  class BitmapTextPipe {
42466
43221
  constructor(renderer) {
42467
43222
  this._renderer = renderer;
43223
+ this._managedBitmapTexts = new GCManagedHash({ renderer, type: "renderable", priority: -2, name: "bitmapText" });
42468
43224
  }
42469
43225
  validateRenderable(bitmapText) {
42470
43226
  const graphicsRenderable = this._getGpuBitmapText(bitmapText);
@@ -42550,6 +43306,7 @@ class BitmapTextPipe {
42550
43306
  const proxyRenderable = new BitmapTextGraphics();
42551
43307
  bitmapText._gpuData[this._renderer.uid] = proxyRenderable;
42552
43308
  this._updateContext(bitmapText, proxyRenderable);
43309
+ this._managedBitmapTexts.add(bitmapText);
42553
43310
  return proxyRenderable;
42554
43311
  }
42555
43312
  _updateDistanceField(bitmapText) {
@@ -42565,7 +43322,9 @@ class BitmapTextPipe {
42565
43322
  context.customShader.resources.localUniforms.uniforms.uDistance = distance;
42566
43323
  }
42567
43324
  destroy() {
43325
+ this._managedBitmapTexts.destroy();
42568
43326
  this._renderer = null;
43327
+ this._managedBitmapTexts = null;
42569
43328
  }
42570
43329
  }
42571
43330
  /** @ignore */
@@ -42590,31 +43349,17 @@ function syncWithProxy(container, proxy) {
42590
43349
  }
42591
43350
 
42592
43351
  class BatchableHTMLText extends BatchableSprite {
42593
- /**
42594
- * Creates an instance of BatchableHTMLText.
42595
- * @param renderer - The renderer instance to be used.
42596
- */
42597
- constructor(renderer) {
42598
- super();
43352
+ constructor() {
43353
+ super(...arguments);
42599
43354
  this.generatingTexture = false;
42600
43355
  this.currentKey = "--";
42601
- this._renderer = renderer;
42602
- renderer.runners.resolutionChange.add(this);
42603
- }
42604
- /** Handles resolution changes for the HTML text. If the text has auto resolution enabled, it triggers a view update. */
42605
- resolutionChange() {
42606
- const text = this.renderable;
42607
- if (text._autoResolution) {
42608
- text.onViewUpdate();
42609
- }
42610
43356
  }
42611
43357
  /** Destroys the BatchableHTMLText instance. Returns the texture promise to the renderer and cleans up references. */
42612
43358
  destroy() {
42613
- const { htmlText } = this._renderer;
42614
- htmlText.getReferenceCount(this.currentKey) === null ? htmlText.returnTexturePromise(this.texturePromise) : htmlText.decreaseReferenceCount(this.currentKey);
42615
- this._renderer.runners.resolutionChange.remove(this);
42616
43359
  this.texturePromise = null;
42617
- this._renderer = null;
43360
+ this.generatingTexture = false;
43361
+ this.currentKey = "--";
43362
+ super.destroy();
42618
43363
  }
42619
43364
  }
42620
43365
 
@@ -42633,6 +43378,21 @@ function updateTextBounds(batchableSprite, text) {
42633
43378
  class HTMLTextPipe {
42634
43379
  constructor(renderer) {
42635
43380
  this._renderer = renderer;
43381
+ renderer.runners.resolutionChange.add(this);
43382
+ this._managedTexts = new GCManagedHash({
43383
+ renderer,
43384
+ type: "renderable",
43385
+ onUnload: this.onTextUnload.bind(this),
43386
+ name: "htmlText"
43387
+ });
43388
+ }
43389
+ resolutionChange() {
43390
+ for (const key in this._managedTexts.items) {
43391
+ const text = this._managedTexts.items[key];
43392
+ if (text?._autoResolution) {
43393
+ text.onViewUpdate();
43394
+ }
43395
+ }
42636
43396
  }
42637
43397
  validateRenderable(htmlText) {
42638
43398
  const gpuText = this._getGpuText(htmlText);
@@ -42690,7 +43450,7 @@ class HTMLTextPipe {
42690
43450
  return htmlText._gpuData[this._renderer.uid] || this.initGpuText(htmlText);
42691
43451
  }
42692
43452
  initGpuText(htmlText) {
42693
- const batchableHTMLText = new BatchableHTMLText(this._renderer);
43453
+ const batchableHTMLText = new BatchableHTMLText();
42694
43454
  batchableHTMLText.renderable = htmlText;
42695
43455
  batchableHTMLText.transform = htmlText.groupTransform;
42696
43456
  batchableHTMLText.texture = Texture.EMPTY;
@@ -42698,9 +43458,18 @@ class HTMLTextPipe {
42698
43458
  batchableHTMLText.roundPixels = this._renderer._roundPixels | htmlText._roundPixels;
42699
43459
  htmlText._resolution = htmlText._autoResolution ? this._renderer.resolution : htmlText.resolution;
42700
43460
  htmlText._gpuData[this._renderer.uid] = batchableHTMLText;
43461
+ this._managedTexts.add(htmlText);
42701
43462
  return batchableHTMLText;
42702
43463
  }
43464
+ onTextUnload(text) {
43465
+ const gpuData = text._gpuData[this._renderer.uid];
43466
+ if (!gpuData)
43467
+ return;
43468
+ const { htmlText } = this._renderer;
43469
+ htmlText.getReferenceCount(gpuData.currentKey) === null ? htmlText.returnTexturePromise(gpuData.texturePromise) : htmlText.decreaseReferenceCount(gpuData.currentKey);
43470
+ }
42703
43471
  destroy() {
43472
+ this._managedTexts.destroy();
42704
43473
  this._renderer = null;
42705
43474
  }
42706
43475
  }
@@ -43008,34 +43777,26 @@ HTMLTextSystem.extension = {
43008
43777
  };
43009
43778
 
43010
43779
  class BatchableText extends BatchableSprite {
43780
+ }
43781
+
43782
+ class CanvasTextPipe {
43011
43783
  constructor(renderer) {
43012
- super();
43013
43784
  this._renderer = renderer;
43014
43785
  renderer.runners.resolutionChange.add(this);
43786
+ this._managedTexts = new GCManagedHash({
43787
+ renderer,
43788
+ type: "renderable",
43789
+ onUnload: this.onTextUnload.bind(this),
43790
+ name: "canvasText"
43791
+ });
43015
43792
  }
43016
43793
  resolutionChange() {
43017
- const text = this.renderable;
43018
- if (text._autoResolution) {
43019
- text.onViewUpdate();
43794
+ for (const key in this._managedTexts.items) {
43795
+ const text = this._managedTexts.items[key];
43796
+ if (text?._autoResolution)
43797
+ text.onViewUpdate();
43020
43798
  }
43021
43799
  }
43022
- destroy() {
43023
- const { canvasText } = this._renderer;
43024
- const refCount = canvasText.getReferenceCount(this.currentKey);
43025
- if (refCount > 0) {
43026
- canvasText.decreaseReferenceCount(this.currentKey);
43027
- } else if (this.texture) {
43028
- canvasText.returnTexture(this.texture);
43029
- }
43030
- this._renderer.runners.resolutionChange.remove(this);
43031
- this._renderer = null;
43032
- }
43033
- }
43034
-
43035
- class CanvasTextPipe {
43036
- constructor(renderer) {
43037
- this._renderer = renderer;
43038
- }
43039
43800
  validateRenderable(text) {
43040
43801
  const gpuText = this._getGpuText(text);
43041
43802
  const newKey = text.styleKey;
@@ -43051,6 +43812,7 @@ class CanvasTextPipe {
43051
43812
  this._updateGpuText(text);
43052
43813
  }
43053
43814
  text._didTextUpdate = false;
43815
+ updateTextBounds(batchableText, text);
43054
43816
  }
43055
43817
  this._renderer.renderPipes.batch.addToBatch(batchableText, instructionSet);
43056
43818
  }
@@ -43066,22 +43828,35 @@ class CanvasTextPipe {
43066
43828
  text._resolution = text._autoResolution ? this._renderer.resolution : text.resolution;
43067
43829
  batchableText.texture = this._renderer.canvasText.getManagedTexture(text);
43068
43830
  batchableText.currentKey = text.styleKey;
43069
- updateTextBounds(batchableText, text);
43070
43831
  }
43071
43832
  _getGpuText(text) {
43072
43833
  return text._gpuData[this._renderer.uid] || this.initGpuText(text);
43073
43834
  }
43074
43835
  initGpuText(text) {
43075
- const batchableText = new BatchableText(this._renderer);
43836
+ const batchableText = new BatchableText();
43076
43837
  batchableText.currentKey = "--";
43077
43838
  batchableText.renderable = text;
43078
43839
  batchableText.transform = text.groupTransform;
43079
43840
  batchableText.bounds = { minX: 0, maxX: 1, minY: 0, maxY: 0 };
43080
43841
  batchableText.roundPixels = this._renderer._roundPixels | text._roundPixels;
43081
43842
  text._gpuData[this._renderer.uid] = batchableText;
43843
+ this._managedTexts.add(text);
43082
43844
  return batchableText;
43083
43845
  }
43846
+ onTextUnload(text) {
43847
+ const gpuData = text._gpuData[this._renderer.uid];
43848
+ if (!gpuData)
43849
+ return;
43850
+ const { canvasText } = this._renderer;
43851
+ const refCount = canvasText.getReferenceCount(gpuData.currentKey);
43852
+ if (refCount > 0) {
43853
+ canvasText.decreaseReferenceCount(gpuData.currentKey);
43854
+ } else if (gpuData.texture) {
43855
+ canvasText.returnTexture(gpuData.texture);
43856
+ }
43857
+ }
43084
43858
  destroy() {
43859
+ this._managedTexts.destroy();
43085
43860
  this._renderer = null;
43086
43861
  }
43087
43862
  }
@@ -43422,12 +44197,6 @@ const useLightening = () => {
43422
44197
  animatedSprite.scale.set(spriteScale);
43423
44198
  animatedSprite.anchor.set(0.5);
43424
44199
  animatedSprite.allowChildren = true;
43425
-
43426
- // Position horizontally, centered
43427
- const totalWidth = (numCards - 1) * spacing;
43428
- animatedSprite.x = -totalWidth / 2 + i * spacing;
43429
- animatedSprite.y = 0; // Centered in container
43430
-
43431
44200
  animatedSprite.play();
43432
44201
 
43433
44202
  // Add multiplier text
@@ -43450,16 +44219,22 @@ const useLightening = () => {
43450
44219
 
43451
44220
  // Suit icon
43452
44221
  let suit_url = "";
43453
- switch ((_card$suit = card.suit) === null || _card$suit === void 0 ? void 0 : _card$suit.toLowerCase()) {
44222
+ const suit = (_card$suit = card.suit) === null || _card$suit === void 0 ? void 0 : _card$suit.toLowerCase();
44223
+ switch (suit) {
43454
44224
  case "hearts":
44225
+ case "heart":
43455
44226
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/SuitHearts.svg/60px-SuitHearts.svg.png";
43456
44227
  break;
43457
44228
  case "clubs":
44229
+ case "club":
43458
44230
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/SuitClubs.svg/60px-SuitClubs.svg.png";
43459
44231
  break;
43460
44232
  case "diamonds":
44233
+ case "diamond":
43461
44234
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/SuitDiamonds.svg/60px-SuitDiamonds.svg.png";
43462
44235
  break;
44236
+ case "spades":
44237
+ case "spade":
43463
44238
  default:
43464
44239
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/SuitSpades.svg/60px-SuitSpades.svg.png";
43465
44240
  break;
@@ -43469,21 +44244,56 @@ const useLightening = () => {
43469
44244
  const card_suit_tex = await Assets.load(suit_url);
43470
44245
  const card_suit_sprite = new Sprite(card_suit_tex);
43471
44246
  card_suit_sprite.anchor.set(0.5);
43472
- card_suit_sprite.scale.set(1.2);
43473
- card_suit_sprite.position.set(0, 35);
43474
- animatedSprite.addChild(card_suit_sprite);
43475
- return animatedSprite;
44247
+ card_suit_sprite.scale.set(1.0);
44248
+
44249
+ // Create white card-like background with rounded corners for suit symbol
44250
+ const suit_bg = new Graphics();
44251
+ const bgWidth = 95;
44252
+ const bgHeight = 95;
44253
+ const cornerRadius = 10;
44254
+ suit_bg.roundRect(-bgWidth / 2, -bgHeight / 2, bgWidth, bgHeight, cornerRadius);
44255
+ suit_bg.fill(0xffffff);
44256
+ suit_bg.stroke({
44257
+ color: 0xffd700,
44258
+ width: 2
44259
+ });
44260
+
44261
+ // Create container for suit symbol with background
44262
+ const suitContainer = new Container();
44263
+ suitContainer.addChild(suit_bg);
44264
+ suitContainer.addChild(card_suit_sprite);
44265
+ suitContainer.position.set(0, 35);
44266
+ animatedSprite.addChild(suitContainer);
44267
+
44268
+ // Create container for this card with background
44269
+ const cardContainer = new Container();
44270
+
44271
+ // Load and add background sprite for this card
44272
+ try {
44273
+ const bg_tex = await Assets.load("/card_bg.svg");
44274
+ const bg_sprite = new Sprite(bg_tex);
44275
+ bg_sprite.scale.set(window.innerWidth < 768 ? 0.65 : 0.75);
44276
+ bg_sprite.anchor.set(0.5);
44277
+ bg_sprite.x = 0;
44278
+ bg_sprite.y = 0;
44279
+ cardContainer.addChild(bg_sprite);
44280
+ } catch (bgError) {
44281
+ // Background is optional, continue without it
44282
+ console.log("Background not found for card, continuing without it");
44283
+ }
44284
+
44285
+ // Add the animated sprite on top of background
44286
+ cardContainer.addChild(animatedSprite);
44287
+
44288
+ // Position the card container horizontally, centered
44289
+ const totalWidth = (numCards - 1) * spacing;
44290
+ cardContainer.x = -totalWidth / 2 + i * spacing;
44291
+ cardContainer.y = 0;
44292
+ return cardContainer;
43476
44293
  });
43477
44294
 
43478
44295
  // After all sprites are loaded, add them to the container and stage
43479
44296
  Promise.all(spritePromises).then(async sprites => {
43480
- const bg_tex = await Assets.load("/card_bg.svg");
43481
- const bg_sprite = new Sprite(bg_tex);
43482
- bg_sprite.scale.set(window.innerWidth < 768 ? 0.65 : 0.75);
43483
- bg_sprite.anchor.set(0.5);
43484
- bg_sprite.x = 0;
43485
- bg_sprite.y = 0;
43486
- cardsContainer.addChild(bg_sprite);
43487
44297
  cardsContainer.label = "card_container";
43488
44298
  sprites.forEach(sprite => cardsContainer.addChild(sprite));
43489
44299
  cardsContainer.x = this.appRef.current.renderer.width / 2;
@@ -43611,12 +44421,6 @@ const useLighteningStart = () => {
43611
44421
  animatedSprite.scale.set(spriteScale);
43612
44422
  animatedSprite.anchor.set(0.5);
43613
44423
  animatedSprite.allowChildren = true;
43614
-
43615
- // Position horizontally, centered
43616
- const totalWidth = (numCards - 1) * spacing;
43617
- animatedSprite.x = -totalWidth / 2 + i * spacing;
43618
- animatedSprite.y = 0; // Centered in container
43619
-
43620
44424
  animatedSprite.play();
43621
44425
 
43622
44426
  // Add multiplier text
@@ -43639,16 +44443,22 @@ const useLighteningStart = () => {
43639
44443
 
43640
44444
  // Suit icon
43641
44445
  let suit_url = "";
43642
- switch ((_card$suit = card.suit) === null || _card$suit === void 0 ? void 0 : _card$suit.toLowerCase()) {
44446
+ const suit = (_card$suit = card.suit) === null || _card$suit === void 0 ? void 0 : _card$suit.toLowerCase();
44447
+ switch (suit) {
43643
44448
  case "hearts":
44449
+ case "heart":
43644
44450
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/f/f0/SuitHearts.svg/60px-SuitHearts.svg.png";
43645
44451
  break;
43646
44452
  case "clubs":
44453
+ case "club":
43647
44454
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/8/8a/SuitClubs.svg/60px-SuitClubs.svg.png";
43648
44455
  break;
43649
44456
  case "diamonds":
44457
+ case "diamond":
43650
44458
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/d/db/SuitDiamonds.svg/60px-SuitDiamonds.svg.png";
43651
44459
  break;
44460
+ case "spades":
44461
+ case "spade":
43652
44462
  default:
43653
44463
  suit_url = "https://upload.wikimedia.org/wikipedia/commons/thumb/5/5b/SuitSpades.svg/60px-SuitSpades.svg.png";
43654
44464
  break;
@@ -43658,21 +44468,56 @@ const useLighteningStart = () => {
43658
44468
  const card_suit_tex = await Assets.load(suit_url);
43659
44469
  const card_suit_sprite = new Sprite(card_suit_tex);
43660
44470
  card_suit_sprite.anchor.set(0.5);
43661
- card_suit_sprite.scale.set(1.2);
43662
- card_suit_sprite.position.set(0, 35);
43663
- animatedSprite.addChild(card_suit_sprite);
43664
- return animatedSprite;
44471
+ card_suit_sprite.scale.set(1.0);
44472
+
44473
+ // Create white card-like background with rounded corners for suit symbol
44474
+ const suit_bg = new Graphics();
44475
+ const bgWidth = 95;
44476
+ const bgHeight = 95;
44477
+ const cornerRadius = 10;
44478
+ suit_bg.roundRect(-bgWidth / 2, -bgHeight / 2, bgWidth, bgHeight, cornerRadius);
44479
+ suit_bg.fill(0xffffff);
44480
+ suit_bg.stroke({
44481
+ color: 0xffd700,
44482
+ width: 2
44483
+ });
44484
+
44485
+ // Create container for suit symbol with background
44486
+ const suitContainer = new Container();
44487
+ suitContainer.addChild(suit_bg);
44488
+ suitContainer.addChild(card_suit_sprite);
44489
+ suitContainer.position.set(0, 35);
44490
+ animatedSprite.addChild(suitContainer);
44491
+
44492
+ // Create container for this card with background
44493
+ const cardContainer = new Container();
44494
+
44495
+ // Load and add background sprite for this card
44496
+ try {
44497
+ const bg_tex = await Assets.load("/card_bg.svg");
44498
+ const bg_sprite = new Sprite(bg_tex);
44499
+ bg_sprite.scale.set(window.innerWidth < 768 ? 0.65 : 0.75);
44500
+ bg_sprite.anchor.set(0.5);
44501
+ bg_sprite.x = 0;
44502
+ bg_sprite.y = 0;
44503
+ cardContainer.addChild(bg_sprite);
44504
+ } catch (bgError) {
44505
+ // Background is optional, continue without it
44506
+ console.log("Background not found for card, continuing without it");
44507
+ }
44508
+
44509
+ // Add the animated sprite on top of background
44510
+ cardContainer.addChild(animatedSprite);
44511
+
44512
+ // Position the card container horizontally, centered
44513
+ const totalWidth = (numCards - 1) * spacing;
44514
+ cardContainer.x = -totalWidth / 2 + i * spacing;
44515
+ cardContainer.y = 0;
44516
+ return cardContainer;
43665
44517
  });
43666
44518
 
43667
44519
  // After all sprites are loaded, add them to the container and stage
43668
44520
  Promise.all(spritePromises).then(async sprites => {
43669
- const bg_tex = await Assets.load("/card_bg.svg");
43670
- const bg_sprite = new Sprite(bg_tex);
43671
- bg_sprite.scale.set(window.innerWidth < 768 ? 0.65 : 0.75);
43672
- bg_sprite.anchor.set(0.5);
43673
- bg_sprite.x = 0;
43674
- bg_sprite.y = 0;
43675
- cardsContainer.addChild(bg_sprite);
43676
44521
  cardsContainer.label = "card_container";
43677
44522
  sprites.forEach(sprite => cardsContainer.addChild(sprite));
43678
44523
  // Center the container in the canvas
@@ -43687,6 +44532,540 @@ const useLighteningStart = () => {
43687
44532
  };
43688
44533
  };
43689
44534
 
44535
+ /**
44536
+ * Generates SVG code for a baccarat card
44537
+ * @param {string} rank - The card rank (e.g., "A", "K", "Q", "J", "10", "9", etc.)
44538
+ * @param {string} suit - The card suit (e.g., "hearts", "diamonds", "clubs", "spades")
44539
+ * @param {number} width - Card width (default: 200)
44540
+ * @param {number} height - Card height (default: 280)
44541
+ * @returns {string} SVG code as a string
44542
+ */
44543
+ const generateBaccaratCardSVG = (rank, suit, width = 200, height = 280) => {
44544
+ // Format rank for display
44545
+ const formatRank = r => {
44546
+ if (!r) return "";
44547
+ const rankUpper = r.toUpperCase();
44548
+ const rankMap = {
44549
+ ACE: "A",
44550
+ KING: "K",
44551
+ QUEEN: "Q",
44552
+ JACK: "J",
44553
+ JOKER: "J"
44554
+ };
44555
+ return rankMap[rankUpper] || r;
44556
+ };
44557
+ const displayRank = formatRank(rank);
44558
+ const suitLower = (suit || "").toLowerCase();
44559
+
44560
+ // Get suit symbol/color
44561
+ const getSuitInfo = s => {
44562
+ switch (s) {
44563
+ case "hearts":
44564
+ case "heart":
44565
+ return {
44566
+ symbol: "♥",
44567
+ color: "#DC143C"
44568
+ };
44569
+ case "diamonds":
44570
+ case "diamond":
44571
+ return {
44572
+ symbol: "♦",
44573
+ color: "#DC143C"
44574
+ };
44575
+ case "clubs":
44576
+ case "club":
44577
+ return {
44578
+ symbol: "♣",
44579
+ color: "#000000"
44580
+ };
44581
+ case "spades":
44582
+ case "spade":
44583
+ return {
44584
+ symbol: "♠",
44585
+ color: "#000000"
44586
+ };
44587
+ default:
44588
+ return {
44589
+ symbol: "♠",
44590
+ color: "#000000"
44591
+ };
44592
+ }
44593
+ };
44594
+ const suitInfo = getSuitInfo(suitLower);
44595
+ const cornerRadius = 12; // Rounded corners
44596
+ const padding = 12; // Padding for corner elements
44597
+
44598
+ // Font sizes - set to 86 pixels to match the example size
44599
+ const rankFontSize = 86; // Fixed size: 86 pixels
44600
+ const suitFontSize = 86; // Fixed size: 86 pixels
44601
+
44602
+ return `
44603
+ <svg width="${width}" height="${height}" xmlns="http://www.w3.org/2000/svg">
44604
+ <defs>
44605
+ <!-- Glow effect for border -->
44606
+ <filter id="glow">
44607
+ <feGaussianBlur stdDeviation="3" result="coloredBlur"/>
44608
+ <feMerge>
44609
+ <feMergeNode in="coloredBlur"/>
44610
+ <feMergeNode in="SourceGraphic"/>
44611
+ </feMerge>
44612
+ </filter>
44613
+ </defs>
44614
+ <!-- Card background with rounded corners - transparent background -->
44615
+ <rect
44616
+ x="0"
44617
+ y="0"
44618
+ width="${width}"
44619
+ height="${height}"
44620
+ rx="${cornerRadius}"
44621
+ ry="${cornerRadius}"
44622
+ fill="none"
44623
+ stroke="#D4AF37"
44624
+ stroke-width="3"
44625
+ filter="url(#glow)"
44626
+ />
44627
+
44628
+ <!-- Inner border for card frame effect -->
44629
+ <rect
44630
+ x="2"
44631
+ y="2"
44632
+ width="${width - 4}"
44633
+ height="${height - 4}"
44634
+ rx="${cornerRadius - 2}"
44635
+ ry="${cornerRadius - 2}"
44636
+ fill="none"
44637
+ stroke="#F5E6D3"
44638
+ stroke-width="1"
44639
+ />
44640
+
44641
+ <!-- Rank at top left corner -->
44642
+ <text
44643
+ x="${padding}"
44644
+ y="${padding + rankFontSize * 0.8}"
44645
+ font-family="Arial, sans-serif"
44646
+ font-size="${rankFontSize}"
44647
+ font-weight="bold"
44648
+ fill="#FFFFFF"
44649
+ text-anchor="start"
44650
+ dominant-baseline="hanging"
44651
+ stroke="#000000"
44652
+ stroke-width="1.5"
44653
+ >
44654
+ ${displayRank}
44655
+ </text>
44656
+
44657
+ <!-- Rank at bottom right corner (inverted) -->
44658
+ <text
44659
+ x="${width - padding}"
44660
+ y="${height - padding}"
44661
+ font-family="Arial, sans-serif"
44662
+ font-size="${rankFontSize}"
44663
+ font-weight="bold"
44664
+ fill="#FFFFFF"
44665
+ text-anchor="end"
44666
+ dominant-baseline="baseline"
44667
+ stroke="#000000"
44668
+ stroke-width="1.5"
44669
+ transform="rotate(180 ${width - padding} ${height - padding})"
44670
+ >
44671
+ ${displayRank}
44672
+ </text>
44673
+
44674
+ <!-- Suit at top right corner -->
44675
+ <text
44676
+ x="${width - padding}"
44677
+ y="${padding + suitFontSize * 0.8}"
44678
+ font-family="Arial, sans-serif"
44679
+ font-size="${suitFontSize}"
44680
+ font-weight="bold"
44681
+ fill="${suitInfo.color}"
44682
+ text-anchor="end"
44683
+ dominant-baseline="hanging"
44684
+ stroke="#000000"
44685
+ stroke-width="1"
44686
+ >
44687
+ ${suitInfo.symbol}
44688
+ </text>
44689
+
44690
+ <!-- Suit at bottom left corner (inverted) -->
44691
+ <text
44692
+ x="${padding}"
44693
+ y="${height - padding}"
44694
+ font-family="Arial, sans-serif"
44695
+ font-size="${suitFontSize}"
44696
+ font-weight="bold"
44697
+ fill="${suitInfo.color}"
44698
+ text-anchor="start"
44699
+ dominant-baseline="baseline"
44700
+ stroke="#000000"
44701
+ stroke-width="1"
44702
+ transform="rotate(180 ${padding} ${height - padding})"
44703
+ >
44704
+ ${suitInfo.symbol}
44705
+ </text>
44706
+ </svg>
44707
+ `.trim();
44708
+ };
44709
+
44710
+ const useBaccaratCard = () => {
44711
+ class BaccaratCard {
44712
+ constructor(cards, appRef) {
44713
+ this.cards = cards;
44714
+ this.appRef = appRef;
44715
+ this.initiateAnimation();
44716
+ }
44717
+ async initiateAnimation() {
44718
+ // Validate cards array
44719
+ if (!this.cards || !Array.isArray(this.cards) || this.cards.length === 0) {
44720
+ console.warn("BaccaratCard: No cards provided or cards array is empty");
44721
+ return;
44722
+ }
44723
+
44724
+ // Create a container to hold all cards
44725
+ const cardsContainer = new Container();
44726
+
44727
+ // Calculate layout: arrange cards horizontally, centered
44728
+ const numCards = this.cards.length;
44729
+ // Smaller SVG dimensions to fit within card_bg.svg
44730
+ const cardWidth = 120;
44731
+ const cardHeight = 160;
44732
+ const spacing = 77.6 * 0.5 + 20; // space between cards (match other hooks)
44733
+ const rowSpacing = 200; // Vertical spacing between rows
44734
+ // Scale for the card_bg.svg background
44735
+ const bgScale = window.innerWidth < 768 ? 0.65 : 0.75;
44736
+ // Smaller scale for the baccarat SVG to fit within the frame
44737
+ const cardScale = window.innerWidth < 768 ? 0.25 : 0.3;
44738
+
44739
+ // Load background texture once for all cards
44740
+ let bgTexture;
44741
+ try {
44742
+ bgTexture = await Assets.load("/card_bg.svg");
44743
+ } catch (bgError) {
44744
+ console.log("Background not found, continuing without it");
44745
+ }
44746
+
44747
+ // Create SVG cards with individual backgrounds
44748
+ const cardPromises = this.cards.map(async (card, i) => {
44749
+ try {
44750
+ var _card$multiplier;
44751
+ // Generate SVG for this card (without multiplier)
44752
+ const svgString = generateBaccaratCardSVG(card.rank, card.suit, cardWidth, cardHeight);
44753
+
44754
+ // Convert SVG string to data URL
44755
+ const svgDataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
44756
+
44757
+ // Create texture from data URL using Image
44758
+ const img = new Image();
44759
+ img.src = svgDataUrl;
44760
+ await new Promise((resolve, reject) => {
44761
+ img.onload = resolve;
44762
+ img.onerror = reject;
44763
+ });
44764
+
44765
+ // Create texture from the loaded image
44766
+ const cardTexture = Texture.from(img);
44767
+ const cardSprite = new Sprite(cardTexture);
44768
+
44769
+ // Create container for this card with background
44770
+ const cardContainer = new Container();
44771
+
44772
+ // Add background sprite if available (card_bg.svg frame)
44773
+ let bg_sprite = null;
44774
+ if (bgTexture) {
44775
+ bg_sprite = new Sprite(bgTexture);
44776
+ bg_sprite.scale.set(bgScale);
44777
+ bg_sprite.anchor.set(0.5);
44778
+ bg_sprite.x = 0;
44779
+ bg_sprite.y = 0;
44780
+ cardContainer.addChild(bg_sprite);
44781
+ }
44782
+
44783
+ // Add the card sprite on top of background (smaller, fits within frame)
44784
+ // Position it downward in the frame
44785
+ cardSprite.scale.set(cardScale);
44786
+ cardSprite.anchor.set(0.5);
44787
+ cardSprite.x = 0;
44788
+ // Move card sprite downward
44789
+ if (bg_sprite) {
44790
+ const bgHeight = bg_sprite.height;
44791
+ cardSprite.y = bgHeight * 0.1; // Move down from center
44792
+ } else {
44793
+ cardSprite.y = cardHeight * cardScale * 0.15; // Fallback position
44794
+ }
44795
+ cardContainer.addChild(cardSprite);
44796
+
44797
+ // Add multiplier text separately at top center with small font size
44798
+ const multiplier = (_card$multiplier = card.multiplier) !== null && _card$multiplier !== void 0 ? _card$multiplier : card.multplier;
44799
+ if (multiplier !== null && multiplier !== undefined) {
44800
+ const multiplierText = new Text({
44801
+ text: `${multiplier}x`,
44802
+ style: {
44803
+ fontFamily: "Arial, sans-serif",
44804
+ fontSize: 14,
44805
+ // Small font size
44806
+ fontWeight: "bold",
44807
+ fill: "#D4AF37",
44808
+ // Golden color
44809
+ align: "center",
44810
+ stroke: "#000000",
44811
+ strokeThickness: 1
44812
+ }
44813
+ });
44814
+ multiplierText.anchor.set(0.5);
44815
+ // Position at very top center of the card container
44816
+ multiplierText.x = 0;
44817
+ if (bg_sprite) {
44818
+ // Use the sprite's scaled height to position at the very top
44819
+ const bgHeight = bg_sprite.height;
44820
+ multiplierText.y = -bgHeight / 2 + 8; // Closer to the top edge
44821
+ } else {
44822
+ // Fallback if no background
44823
+ multiplierText.y = -cardHeight * cardScale / 2 + 5;
44824
+ }
44825
+ cardContainer.addChild(multiplierText);
44826
+ }
44827
+
44828
+ // Calculate multi-row layout for 5 cards: 3 in first row, 2 in second row
44829
+ let rowIndex = 0;
44830
+ let colIndex = i;
44831
+ let cardsInRow = numCards;
44832
+ if (numCards === 5) {
44833
+ // First 3 cards in row 0, remaining 2 in row 1
44834
+ if (i < 3) {
44835
+ rowIndex = 0;
44836
+ colIndex = i;
44837
+ cardsInRow = 3;
44838
+ } else {
44839
+ rowIndex = 1;
44840
+ colIndex = i - 3;
44841
+ cardsInRow = 2;
44842
+ }
44843
+ }
44844
+
44845
+ // Calculate horizontal position within the row
44846
+ const rowWidth = (cardsInRow - 1) * spacing;
44847
+ cardContainer.x = -rowWidth / 2 + colIndex * spacing;
44848
+
44849
+ // Calculate vertical position based on row
44850
+ cardContainer.y = rowIndex * rowSpacing;
44851
+ return cardContainer;
44852
+ } catch (error) {
44853
+ console.error(`Error creating baccarat card ${i}:`, error);
44854
+ return null;
44855
+ }
44856
+ });
44857
+
44858
+ // After all cards are loaded, add them to the container and stage
44859
+ Promise.all(cardPromises).then(async sprites => {
44860
+ // Filter out any null sprites (from failed card creation)
44861
+ const validSprites = sprites.filter(sprite => sprite !== null);
44862
+ if (validSprites.length === 0) {
44863
+ console.error("BaccaratCard: No valid cards were created");
44864
+ return;
44865
+ }
44866
+ try {
44867
+ cardsContainer.label = "baccarat_card_container";
44868
+ validSprites.forEach(sprite => cardsContainer.addChild(sprite));
44869
+
44870
+ // Center the container in the canvas
44871
+ cardsContainer.x = this.appRef.current.renderer.width / 2;
44872
+ // Adjust vertical centering for multi-row layout
44873
+ const verticalOffset = numCards === 5 ? -rowSpacing / 2 : 0;
44874
+ cardsContainer.y = this.appRef.current.renderer.height / 2 + verticalOffset;
44875
+ this.appRef.current.stage.addChild(cardsContainer);
44876
+ } catch (error) {
44877
+ console.error("Error adding cards to stage:", error);
44878
+ }
44879
+ }).catch(error => {
44880
+ console.error("Error in card promises:", error);
44881
+ });
44882
+ }
44883
+ }
44884
+ return {
44885
+ BaccaratCard
44886
+ };
44887
+ };
44888
+
44889
+ const useBaccaratCardStart = () => {
44890
+ class BaccaratCardStart {
44891
+ constructor(cards, appRef) {
44892
+ this.cards = cards;
44893
+ this.appRef = appRef;
44894
+ this.initiateAnimation();
44895
+ }
44896
+ async initiateAnimation() {
44897
+ // Validate cards array
44898
+ if (!this.cards || !Array.isArray(this.cards) || this.cards.length === 0) {
44899
+ console.warn("BaccaratCardStart: No cards provided or cards array is empty");
44900
+ return;
44901
+ }
44902
+
44903
+ // Create a container to hold all cards
44904
+ const cardsContainer = new Container();
44905
+
44906
+ // Calculate layout: arrange cards horizontally, centered
44907
+ const numCards = this.cards.length;
44908
+ // Smaller SVG dimensions to fit within card_bg.svg
44909
+ const cardWidth = 120;
44910
+ const cardHeight = 160;
44911
+ const spacing = 77.6 * 0.5 + 20; // space between cards (match other hooks)
44912
+ const rowSpacing = 200; // Vertical spacing between rows
44913
+ // Scale for the card_bg.svg background
44914
+ const bgScale = window.innerWidth < 768 ? 0.65 : 0.75;
44915
+ // Smaller scale for the baccarat SVG to fit within the frame
44916
+ const cardScale = window.innerWidth < 768 ? 0.25 : 0.3;
44917
+
44918
+ // Load background texture once for all cards
44919
+ let bgTexture;
44920
+ try {
44921
+ bgTexture = await Assets.load("/card_bg.svg");
44922
+ } catch (bgError) {
44923
+ console.log("Background not found, continuing without it");
44924
+ }
44925
+
44926
+ // Create SVG cards with individual backgrounds
44927
+ const cardPromises = this.cards.map(async (card, i) => {
44928
+ try {
44929
+ var _card$multiplier;
44930
+ // Generate SVG for this card (without multiplier)
44931
+ const svgString = generateBaccaratCardSVG(card.rank, card.suit, cardWidth, cardHeight);
44932
+
44933
+ // Convert SVG string to data URL
44934
+ const svgDataUrl = `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svgString)}`;
44935
+
44936
+ // Create texture from data URL using Image
44937
+ const img = new Image();
44938
+ img.src = svgDataUrl;
44939
+ await new Promise((resolve, reject) => {
44940
+ img.onload = resolve;
44941
+ img.onerror = reject;
44942
+ });
44943
+
44944
+ // Create texture from the loaded image
44945
+ const cardTexture = Texture.from(img);
44946
+ const cardSprite = new Sprite(cardTexture);
44947
+ cardSprite.label = "starting_baccarat_card";
44948
+
44949
+ // Create container for this card with background
44950
+ const cardContainer = new Container();
44951
+
44952
+ // Add background sprite if available (card_bg.svg frame)
44953
+ let bg_sprite = null;
44954
+ if (bgTexture) {
44955
+ bg_sprite = new Sprite(bgTexture);
44956
+ bg_sprite.scale.set(bgScale);
44957
+ bg_sprite.anchor.set(0.5);
44958
+ bg_sprite.x = 0;
44959
+ bg_sprite.y = 0;
44960
+ cardContainer.addChild(bg_sprite);
44961
+ }
44962
+
44963
+ // Add the card sprite on top of background (smaller, fits within frame)
44964
+ // Position it downward in the frame
44965
+ cardSprite.scale.set(cardScale);
44966
+ cardSprite.anchor.set(0.5);
44967
+ cardSprite.x = 0;
44968
+ // Move card sprite downward
44969
+ if (bg_sprite) {
44970
+ const bgHeight = bg_sprite.height;
44971
+ cardSprite.y = bgHeight * 0.1; // Move down from center
44972
+ } else {
44973
+ cardSprite.y = cardHeight * cardScale * 0.15; // Fallback position
44974
+ }
44975
+ cardContainer.addChild(cardSprite);
44976
+
44977
+ // Add multiplier text separately at top center with small font size
44978
+ const multiplier = (_card$multiplier = card.multiplier) !== null && _card$multiplier !== void 0 ? _card$multiplier : card.multplier;
44979
+ if (multiplier !== null && multiplier !== undefined) {
44980
+ const multiplierText = new Text({
44981
+ text: `${multiplier}x`,
44982
+ style: {
44983
+ fontFamily: "Arial, sans-serif",
44984
+ fontSize: 14,
44985
+ // Small font size
44986
+ fontWeight: "bold",
44987
+ fill: "#D4AF37",
44988
+ // Golden color
44989
+ align: "center",
44990
+ stroke: "#000000",
44991
+ strokeThickness: 1
44992
+ }
44993
+ });
44994
+ multiplierText.anchor.set(0.5);
44995
+ // Position at very top center of the card container
44996
+ multiplierText.x = 0;
44997
+ if (bg_sprite) {
44998
+ // Use the sprite's scaled height to position at the very top
44999
+ const bgHeight = bg_sprite.height;
45000
+ multiplierText.y = -bgHeight / 2 + 8; // Closer to the top edge
45001
+ } else {
45002
+ // Fallback if no background
45003
+ multiplierText.y = -cardHeight * cardScale / 2 + 5;
45004
+ }
45005
+ cardContainer.addChild(multiplierText);
45006
+ }
45007
+
45008
+ // Calculate multi-row layout for 5 cards: 3 in first row, 2 in second row
45009
+ let rowIndex = 0;
45010
+ let colIndex = i;
45011
+ let cardsInRow = numCards;
45012
+ if (numCards === 5) {
45013
+ // First 3 cards in row 0, remaining 2 in row 1
45014
+ if (i < 3) {
45015
+ rowIndex = 0;
45016
+ colIndex = i;
45017
+ cardsInRow = 3;
45018
+ } else {
45019
+ rowIndex = 1;
45020
+ colIndex = i - 3;
45021
+ cardsInRow = 2;
45022
+ }
45023
+ }
45024
+
45025
+ // Calculate horizontal position within the row
45026
+ const rowWidth = (cardsInRow - 1) * spacing;
45027
+ cardContainer.x = -rowWidth / 2 + colIndex * spacing;
45028
+
45029
+ // Calculate vertical position based on row
45030
+ cardContainer.y = rowIndex * rowSpacing;
45031
+ return cardContainer;
45032
+ } catch (error) {
45033
+ console.error(`Error creating baccarat card ${i}:`, error);
45034
+ return null;
45035
+ }
45036
+ });
45037
+
45038
+ // After all cards are loaded, add them to the container and stage
45039
+ Promise.all(cardPromises).then(async sprites => {
45040
+ // Filter out any null sprites (from failed card creation)
45041
+ const validSprites = sprites.filter(sprite => sprite !== null);
45042
+ if (validSprites.length === 0) {
45043
+ console.error("BaccaratCardStart: No valid cards were created");
45044
+ return;
45045
+ }
45046
+ try {
45047
+ cardsContainer.label = "baccarat_card_start_container";
45048
+ validSprites.forEach(sprite => cardsContainer.addChild(sprite));
45049
+
45050
+ // Center the container in the canvas
45051
+ cardsContainer.x = this.appRef.current.renderer.width / 2;
45052
+ // Adjust vertical centering for multi-row layout
45053
+ const verticalOffset = numCards === 5 ? -rowSpacing / 2 : 0;
45054
+ cardsContainer.y = this.appRef.current.renderer.height / 2 + verticalOffset;
45055
+ this.appRef.current.stage.addChild(cardsContainer);
45056
+ } catch (error) {
45057
+ console.error("Error adding cards to stage:", error);
45058
+ }
45059
+ }).catch(error => {
45060
+ console.error("Error in card promises:", error);
45061
+ });
45062
+ }
45063
+ }
45064
+ return {
45065
+ BaccaratCardStart
45066
+ };
45067
+ };
45068
+
43690
45069
  // CSS is provided separately via the style field in package.json
43691
45070
 
43692
45071
  const thunder_left = "https://babylonbetst.evo-games.com/frontend/evo/mini/images/lightnings.f9848fb7.webp";
@@ -43696,7 +45075,9 @@ const border_start = "https://babylonbetst.evo-games.com/frontend/evo/mini/image
43696
45075
  const border_loop = "https://babylonbetst.evo-games.com/frontend/evo/mini/images/borderloop.c55570cc.webp";
43697
45076
  const LighteningCard = ({
43698
45077
  roundStatus,
43699
- cards
45078
+ cards,
45079
+ showRank = false,
45080
+ gameType
43700
45081
  }) => {
43701
45082
  const containerRef = React.useRef(null);
43702
45083
  const appRef = React.useRef(null);
@@ -43715,6 +45096,12 @@ const LighteningCard = ({
43715
45096
  const {
43716
45097
  LighteningStart
43717
45098
  } = useLighteningStart();
45099
+ const {
45100
+ BaccaratCard
45101
+ } = useBaccaratCard();
45102
+ const {
45103
+ BaccaratCardStart
45104
+ } = useBaccaratCardStart();
43718
45105
  const initiatePixiApplication = async () => {
43719
45106
  appRef.current = new Application();
43720
45107
  await appRef.current.init({
@@ -43729,8 +45116,28 @@ const LighteningCard = ({
43729
45116
  ["NO_MORE_BETS", "LIGHTNING", "NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new LeftThunder(thunder_left, appRef);
43730
45117
  ["NO_MORE_BETS", "LIGHTNING", "NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new RightThunder(thunder_right, appRef);
43731
45118
  ["NO_MORE_BETS", "NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new MiddleThunder(thunder_middle, appRef);
43732
- ["LIGHTNING"].includes(roundStatus) && new LighteningStart(cards, border_start, appRef);
43733
- ["NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new Lightening(cards, border_loop, appRef);
45119
+
45120
+ // Debug logging
45121
+ console.log("LighteningCard render:", {
45122
+ roundStatus,
45123
+ showRank,
45124
+ gameType,
45125
+ cardsCount: (cards === null || cards === void 0 ? void 0 : cards.length) || 0,
45126
+ cards: cards
45127
+ });
45128
+
45129
+ // Check if this is baccarat game - use SVG cards
45130
+ // Use baccarat SVG cards when gameType is baccarat OR when showRank is true (baccarat-style display)
45131
+ const isBaccarat = (gameType === null || gameType === void 0 ? void 0 : gameType.toLowerCase()) === "baccarat" || showRank === true;
45132
+ if (isBaccarat) {
45133
+ // Use baccarat SVG cards (no PNGs, uses SVG suit symbols)
45134
+ ["LIGHTNING"].includes(roundStatus) && new BaccaratCardStart(cards, appRef);
45135
+ ["NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new BaccaratCard(cards, appRef);
45136
+ } else {
45137
+ // Use suit-only display (with PNG suit images)
45138
+ ["LIGHTNING"].includes(roundStatus) && new LighteningStart(cards, border_start, appRef);
45139
+ ["NEW_CARD", "ROUND_END", "BETTING_STARTED"].includes(roundStatus) && new Lightening(cards, border_loop, appRef);
45140
+ }
43734
45141
  };
43735
45142
  React.useEffect(() => {
43736
45143
  if (!containerRef.current) return;
@@ -43743,7 +45150,7 @@ const LighteningCard = ({
43743
45150
  appRef.current = null;
43744
45151
  }
43745
45152
  };
43746
- }, [roundStatus]);
45153
+ }, [roundStatus, showRank, cards, gameType]);
43747
45154
  return /*#__PURE__*/React.createElement("div", {
43748
45155
  className: "lightening-card",
43749
45156
  ref: containerRef
@@ -43756,12 +45163,17 @@ LighteningCard.propTypes = {
43756
45163
  rank: PropTypes.string,
43757
45164
  suit: PropTypes.string
43758
45165
  })),
45166
+ showRank: PropTypes.bool,
45167
+ // If true, displays rank at center and suit at top-left. If false, displays suit only.
45168
+ gameType: PropTypes.string,
45169
+ // Game type (e.g., "baccarat") - if "baccarat", uses SVG cards
43759
45170
  assetPath: PropTypes.string,
43760
45171
  // Path to the animation asset
43761
45172
  onAnimationReady: PropTypes.func // Callback when animation is ready
43762
45173
  };
43763
45174
  LighteningCard.defaultProps = {
43764
- cards: []
45175
+ cards: [],
45176
+ showRank: false // Default to suit-only display
43765
45177
  };
43766
45178
 
43767
45179
  extensions.add(AccessibilitySystem);