mirror of
https://github.com/EsotericSoftware/spine-runtimes.git
synced 2026-02-06 23:34:53 +08:00
[ts][webcomponents] Sharable cache for AssetManager.
This commit is contained in:
parent
85de53624e
commit
2e5a099ef4
@ -35,17 +35,16 @@ export class AssetManagerBase implements Disposable {
|
||||
private pathPrefix: string = "";
|
||||
private textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture;
|
||||
private downloader: Downloader;
|
||||
private assets: StringMap<any> = {};
|
||||
private assetsRefCount: StringMap<number> = {};
|
||||
private assetsLoaded: StringMap<Promise<any>> = {};
|
||||
private cache: AssetCache;
|
||||
private errors: StringMap<string> = {};
|
||||
private toLoad = 0;
|
||||
private loaded = 0;
|
||||
|
||||
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||
constructor (textureLoader: (image: HTMLImageElement | ImageBitmap) => Texture, pathPrefix: string = "", downloader = new Downloader(), cache = new AssetCache()) {
|
||||
this.textureLoader = textureLoader;
|
||||
this.pathPrefix = pathPrefix;
|
||||
this.downloader = downloader;
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
private start (path: string): string {
|
||||
@ -56,8 +55,8 @@ export class AssetManagerBase implements Disposable {
|
||||
private success (callback: (path: string, data: any) => void, path: string, asset: any) {
|
||||
this.toLoad--;
|
||||
this.loaded++;
|
||||
this.assets[path] = asset;
|
||||
this.assetsRefCount[path] = (this.assetsRefCount[path] || 0) + 1;
|
||||
this.cache.assets[path] = asset;
|
||||
this.cache.assetsRefCount[path] = (this.cache.assetsRefCount[path] || 0) + 1;
|
||||
if (callback) callback(path, asset);
|
||||
}
|
||||
|
||||
@ -94,7 +93,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.downloader.downloadBinary(path, (data: Uint8Array): void => {
|
||||
this.success(success, path, data);
|
||||
resolve(data);
|
||||
@ -125,7 +124,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.downloader.downloadJson(path, (data: object): void => {
|
||||
this.success(success, path, data);
|
||||
resolve(data);
|
||||
@ -140,11 +139,17 @@ export class AssetManagerBase implements Disposable {
|
||||
reuseAssets (path: string,
|
||||
success: (path: string, data: any) => void = () => { },
|
||||
error: (path: string, message: string) => void = () => { }) {
|
||||
const loadedStatus = this.assetsLoaded[path];
|
||||
const loadedStatus = this.cache.assetsLoaded[path];
|
||||
const alreadyExistsOrLoading = loadedStatus !== undefined;
|
||||
if (alreadyExistsOrLoading) {
|
||||
loadedStatus
|
||||
.then(data => this.success(success, path, data))
|
||||
.then(data => {
|
||||
if (data instanceof Image) {
|
||||
data = this.textureLoader(data);
|
||||
this.cache.assetsLoaded[path] = Promise.resolve(data);
|
||||
}
|
||||
return this.success(success, path, data)
|
||||
})
|
||||
.catch(errorMsg => this.error(error, path, errorMsg));
|
||||
}
|
||||
return alreadyExistsOrLoading;
|
||||
@ -158,7 +163,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
let isBrowser = !!(typeof window !== 'undefined' && typeof navigator !== 'undefined' && window.document);
|
||||
let isWebWorker = !isBrowser; // && typeof importScripts !== 'undefined';
|
||||
if (isWebWorker) {
|
||||
@ -171,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.textureLoader(bitmap);
|
||||
this.success(success, path, texture);
|
||||
resolve(texture);
|
||||
};
|
||||
@ -180,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.textureLoader(image);
|
||||
this.success(success, path, texture);
|
||||
resolve(texture);
|
||||
};
|
||||
@ -206,7 +211,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||
try {
|
||||
let atlas = new TextureAtlas(atlasText);
|
||||
@ -224,7 +229,7 @@ export class AssetManagerBase implements Disposable {
|
||||
},
|
||||
(imagePath: string, message: string) => {
|
||||
if (!abort) {
|
||||
const errorMsg = `Couldn't load texture atlas ${path} page image: ${imagePath}`;
|
||||
const errorMsg = `Couldn't load texture ${path} page image: ${imagePath}`;
|
||||
this.error(error, path, errorMsg);
|
||||
reject(errorMsg);
|
||||
}
|
||||
@ -254,7 +259,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
if (this.reuseAssets(path, success, error)) return;
|
||||
|
||||
this.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.cache.assetsLoaded[path] = new Promise<any>((resolve, reject) => {
|
||||
this.downloader.downloadText(path, (atlasText: string): void => {
|
||||
try {
|
||||
const atlas = new TextureAtlas(atlasText);
|
||||
@ -319,13 +324,17 @@ export class AssetManagerBase implements Disposable {
|
||||
});
|
||||
}
|
||||
|
||||
setCache (cache: AssetCache) {
|
||||
this.cache = cache;
|
||||
}
|
||||
|
||||
get (path: string) {
|
||||
return this.assets[this.pathPrefix + path];
|
||||
return this.cache.assets[this.pathPrefix + path];
|
||||
}
|
||||
|
||||
require (path: string) {
|
||||
path = this.pathPrefix + path;
|
||||
let asset = this.assets[path];
|
||||
let asset = this.cache.assets[path];
|
||||
if (asset) return asset;
|
||||
let error = this.errors[path];
|
||||
throw Error("Asset not found: " + path + (error ? "\n" + error : ""));
|
||||
@ -333,22 +342,22 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
remove (path: string) {
|
||||
path = this.pathPrefix + path;
|
||||
let asset = this.assets[path];
|
||||
let asset = this.cache.assets[path];
|
||||
if (asset.dispose) asset.dispose();
|
||||
delete this.assets[path];
|
||||
delete this.assetsRefCount[path];
|
||||
delete this.assetsLoaded[path];
|
||||
delete this.cache.assets[path];
|
||||
delete this.cache.assetsRefCount[path];
|
||||
delete this.cache.assetsLoaded[path];
|
||||
return asset;
|
||||
}
|
||||
|
||||
removeAll () {
|
||||
for (let path in this.assets) {
|
||||
let asset = this.assets[path];
|
||||
for (let path in this.cache.assets) {
|
||||
let asset = this.cache.assets[path];
|
||||
if (asset.dispose) asset.dispose();
|
||||
}
|
||||
this.assets = {};
|
||||
this.assetsLoaded = {};
|
||||
this.assetsRefCount = {};
|
||||
this.cache.assets = {};
|
||||
this.cache.assetsLoaded = {};
|
||||
this.cache.assetsRefCount = {};
|
||||
}
|
||||
|
||||
isLoadingComplete (): boolean {
|
||||
@ -369,7 +378,7 @@ export class AssetManagerBase implements Disposable {
|
||||
|
||||
// dispose asset only if it's not used by others
|
||||
disposeAsset (path: string) {
|
||||
if (--this.assetsRefCount[path] === 0) {
|
||||
if (--this.cache.assetsRefCount[path] === 0) {
|
||||
this.remove(path)
|
||||
}
|
||||
}
|
||||
@ -383,6 +392,27 @@ export class AssetManagerBase implements Disposable {
|
||||
}
|
||||
}
|
||||
|
||||
export class AssetCache {
|
||||
public assets: StringMap<any> = {};
|
||||
public assetsRefCount: StringMap<number> = {};
|
||||
public assetsLoaded: StringMap<Promise<any>> = {};
|
||||
|
||||
static AVAILABLE_CACHES = new Map<string, AssetCache>();
|
||||
static getCache (id: string) {
|
||||
const cache = AssetCache.AVAILABLE_CACHES.get(id);
|
||||
if (cache) return cache;
|
||||
|
||||
const newCache = new AssetCache();
|
||||
AssetCache.AVAILABLE_CACHES.set(id, newCache);
|
||||
return newCache;
|
||||
}
|
||||
|
||||
async addAsset(path: string, asset: any) {
|
||||
this.assetsLoaded[path] = Promise.resolve(asset);
|
||||
this.assets[path] = await asset;
|
||||
}
|
||||
}
|
||||
|
||||
export class Downloader {
|
||||
private callbacks: StringMap<Array<Function>> = {};
|
||||
rawDataUris: StringMap<string> = {};
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { AssetManager, Color, Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
|
||||
import { AssetCache, AssetManager, Color, Disposable, Input, LoadingScreen, ManagedWebGLRenderingContext, Physics, SceneRenderer, TimeKeeper, Vector2, Vector3 } from "@esotericsoftware/spine-webgl"
|
||||
import { SpineWebComponentSkeleton } from "./SpineWebComponentSkeleton.js"
|
||||
import { AttributeTypes, castValue, Point, Rectangle } from "./wcUtils.js"
|
||||
|
||||
@ -48,10 +48,11 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
||||
* @internal
|
||||
*/
|
||||
static getOrCreateOverlay (overlayId: string | null): SpineWebComponentOverlay {
|
||||
let overlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId || SpineWebComponentOverlay.OVERLAY_ID);
|
||||
const id = overlayId || SpineWebComponentOverlay.OVERLAY_ID;
|
||||
let overlay = SpineWebComponentOverlay.OVERLAY_LIST.get(id);
|
||||
if (!overlay) {
|
||||
overlay = document.createElement('spine-overlay') as SpineWebComponentOverlay;
|
||||
overlay.setAttribute('overlay-id', SpineWebComponentOverlay.OVERLAY_ID);
|
||||
overlay.setAttribute('overlay-id', id);
|
||||
document.body.appendChild(overlay);
|
||||
}
|
||||
return overlay;
|
||||
@ -232,6 +233,9 @@ export class SpineWebComponentOverlay extends HTMLElement implements OverlayAttr
|
||||
overlayId = SpineWebComponentOverlay.OVERLAY_ID;
|
||||
this.setAttribute('overlay-id', overlayId);
|
||||
}
|
||||
|
||||
this.assetManager.setCache(AssetCache.getCache(overlayId));
|
||||
|
||||
const existingOverlay = SpineWebComponentOverlay.OVERLAY_LIST.get(overlayId);
|
||||
if (existingOverlay && existingOverlay !== this) {
|
||||
throw new Error(`"SpineWebComponentOverlay - You cannot have two spine-overlay with the same overlay-id: ${overlayId}"`);
|
||||
|
||||
@ -31,7 +31,6 @@ import { AssetManagerBase, Downloader } from "@esotericsoftware/spine-core"
|
||||
import { ManagedWebGLRenderingContext } from "./WebGL.js";
|
||||
import { GLTexture } from "./GLTexture.js";
|
||||
|
||||
|
||||
export class AssetManager extends AssetManagerBase {
|
||||
constructor (context: ManagedWebGLRenderingContext | WebGLRenderingContext, pathPrefix: string = "", downloader: Downloader = new Downloader()) {
|
||||
super((image: HTMLImageElement | ImageBitmap) => {
|
||||
|
||||
@ -27,7 +27,7 @@
|
||||
* THE SPINE RUNTIMES, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*****************************************************************************/
|
||||
|
||||
import { Restorable, BlendMode } from "@esotericsoftware/spine-core";
|
||||
import { Restorable } from "@esotericsoftware/spine-core";
|
||||
|
||||
export class ManagedWebGLRenderingContext {
|
||||
public canvas: HTMLCanvasElement | OffscreenCanvas;
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user