[ts] Fixed AssetManager dispose invalidating textures too early. Close #2905.

This commit is contained in:
Davide Tantillo 2025-09-29 18:18:29 +02:00
parent aded292be3
commit e899a99cbb
2 changed files with 46 additions and 17 deletions

View File

@ -176,7 +176,7 @@ export class AssetManagerBase implements Disposable {
return blob ? createImageBitmap(blob, { premultiplyAlpha: "none", colorSpaceConversion: "none" }) : null;
}).then((bitmap) => {
if (bitmap) {
const texture = this.textureLoader(bitmap);
const texture = this.createTexture(path, bitmap);
this.success(success, path, texture);
resolve(texture);
};
@ -185,7 +185,7 @@ export class AssetManagerBase implements Disposable {
let image = new Image();
image.crossOrigin = "anonymous";
image.onload = () => {
const texture = this.textureLoader(image);
const texture = this.createTexture(path, image);
this.success(success, path, texture);
resolve(texture);
};
@ -214,7 +214,7 @@ export class AssetManagerBase implements Disposable {
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
this.downloader.downloadText(path, (atlasText: string): void => {
try {
let atlas = new TextureAtlas(atlasText);
const atlas = this.createTextureAtlas(path, atlasText);
let toLoad = atlas.pages.length, abort = false;
for (let page of atlas.pages) {
this.loadTexture(!fileAlias ? parent + page.name : fileAlias[page.name!],
@ -262,7 +262,7 @@ export class AssetManagerBase implements Disposable {
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
this.downloader.downloadText(path, (atlasText: string): void => {
try {
const atlas = new TextureAtlas(atlasText);
const atlas = this.createTextureAtlas(path, atlasText);
this.success(success, path, atlas);
resolve(atlas);
} catch (e) {
@ -378,9 +378,12 @@ export class AssetManagerBase implements Disposable {
// dispose asset only if it's not used by others
disposeAsset (path: string) {
if (--this.cache.assetsRefCount[path] === 0) {
this.remove(path)
const asset = this.cache.assets[path];
if (asset instanceof TextureAtlas) {
asset.dispose();
return;
}
this.disposeAssetInternal(path);
}
hasErrors () {
@ -390,6 +393,33 @@ export class AssetManagerBase implements Disposable {
getErrors () {
return this.errors;
}
private disposeAssetInternal (path: string) {
if (this.cache.assetsRefCount[path] > 0 && --this.cache.assetsRefCount[path] === 0) {
return this.remove(path);
}
}
private createTextureAtlas (path: string, atlasText: string): TextureAtlas {
const atlas = new TextureAtlas(atlasText);
atlas.dispose = () => {
if (this.cache.assetsRefCount[path] <= 0) return;
this.disposeAssetInternal(path);
for (const page of atlas.pages) {
page.texture?.dispose();
}
}
return atlas;
}
private createTexture (path: string, image: HTMLImageElement | ImageBitmap): Texture {
const texture = this.textureLoader(image);
const textureDispose = texture.dispose.bind(texture);
texture.dispose = () => {
if (this.disposeAssetInternal(path)) textureDispose();
}
return texture;
}
}
export class AssetCache {

View File

@ -32,27 +32,27 @@ import {
AnimationState,
AnimationStateData,
AtlasAttachmentLoader,
Bone,
Disposable,
LoadingScreen,
MeshAttachment,
MixBlend,
MixDirection,
NumberArrayLike,
Physics,
RegionAttachment,
Skeleton,
SkeletonBinary,
SkeletonData,
SkeletonJson,
Skeleton,
TextureAtlas,
Vector2,
Utils,
NumberArrayLike,
Slot,
RegionAttachment,
MeshAttachment,
Bone,
Skin,
Slot,
TextureAtlas,
Utils,
Vector2,
} from "@esotericsoftware/spine-webgl";
import { AttributeTypes, castValue, isBase64, Rectangle } from "./wcUtils.js";
import { SpineWebComponentOverlay } from "./SpineWebComponentOverlay.js";
import { AttributeTypes, castValue, isBase64, Rectangle } from "./wcUtils.js";
type UpdateSpineWidgetFunction = (delta: number, skeleton: Skeleton, state: AnimationState) => void;
@ -1318,7 +1318,6 @@ export class SpineWebComponentSkeleton extends HTMLElement implements Disposable
const { assetManager } = this.overlay;
if (this.lastAtlasPath) assetManager.disposeAsset(this.lastAtlasPath);
if (this.lastSkelPath) assetManager.disposeAsset(this.lastSkelPath);
for (const texturePath of this.lastTexturePaths) assetManager.disposeAsset(texturePath);
}
}