/* ES Lint */ /* globals PIXI, console*/ const registeredTiles = new Map() /** Implements a baseTexture cache. The last textures are kept for reuse */ let keepTextures = 0 const keptTextures = [] const lateTextures = new Map() let lastSweepTime = 0 let sweepInterval = 2000.0 /** The current Tile implementation simply uses PIXI.Sprites. * * BTW: PIXI.extras.TilingSprite is not appropriate. It should be used for * repeating patterns. **/ export default class Tile extends PIXI.Sprite { constructor(texture, url) { super(texture) this.url = url this.register(url) } /** * Static method to enable keeping of base textures * * @static * @param {*} value * @memberof Tile */ static enableKeepTextures(value = 1000) { keepTextures = value } /** * Returns true iff the url is pending * * @static * @param {*} url * @returns * @memberof Tile */ /*static isPending(url) { return pendingTiles.has(url) && pendingTiles.get(url) > 0 } */ static isObsolete(url) { if (registeredTiles.has(url) && registeredTiles.get(url) > 0) { return false } return true } /** * Loads a tile from image using the PIXI.Texture.from method. * * @static * @param {*} imageId * @param {*} crossorigin * @param {*} scaleMode * @returns * @memberof Tile */ static fromImage(imageId, crossorigin, scaleMode) { return new Tile(PIXI.Texture.from(imageId, crossorigin, scaleMode), imageId) } /** * Registers the tile in the global reference counter for textures * * @param {*} url * @param {boolean} [debug=false] * @memberof Tile */ register(url, debug = false) { //Tile.unschedule(url) if (registeredTiles.has(url)) { let tiles = registeredTiles.get(url) tiles.add(this) if (debug) console.log('Tile.register', url, tiles.size) } else { registeredTiles.set(url, new Set([this])) if (debug) console.log('Tile.register', url, 1) } } /** * Unregisters the rile in the global reference counter for textures * * @returns {number} The number of how often a texture is used. * @memberof Tile */ unregister() { let tiles = registeredTiles.get(this.url) tiles.delete(this) if (tiles.size == 0) { registeredTiles.delete(this.url) return 0 } return tiles.size } /** * Destroys this sprite and optionally its texture and children * * @param {*} options Part of the PIXI API, but ignored in the implementation * @memberof Tile */ destroy(options, debug = false) { let count = this.unregister() if (keepTextures > 0) { keptTextures.push({ url: this.url, texture: this.texture }) let opts = { children: true, texture: false, baseTexture: false } if (debug) console.log('Tile.destroy', registeredTiles.size, opts) super.destroy(opts) while (keptTextures.length > keepTextures) { let { url, texture } = keptTextures.shift() if (Tile.isObsolete(url)) { texture.destroy(true) // Destroy base as well if (debug) console.log('Destroying texture and baseTexture', url) } } } else { // No longer registered and not pending if (count <= 0) { // && !Tile.isPending(this.url) let opts = { children: true, texture: true, baseTexture: true } super.destroy(opts) if (debug) console.log('Tile.destroy', registeredTiles.size, opts) } else { let opts = { children: true, texture: false, baseTexture: false } if (debug) console.log('Tile.destroy', registeredTiles.size, opts) super.destroy(opts) } if (this.parent != null) { // UO: Emit warning and remove console.warn('Destroying tile with parent. Hiding instead') this.visible = false } } } /** * Returns an available texture that can be reused * * @param {*} url * @returns * @memberof Tile */ static textureAvailable(url) { if (registeredTiles.has(url)) { let tiles = registeredTiles.get(url) for (let tile of tiles.values()) { //console.log("Reusing cached texture", tile.parent) return tile.texture } } return null } /** * Specialized renderWebGL to avoid freezing system * * @param {*} renderer * @memberof Tile */ renderWebGL(renderer) { try { super.renderWebGL(renderer) } catch (e) { // We want persistent logging here Logging.error('Error in Tile.renderWebGL: ' + e.message) } } /** * Removes lately arrived texture if they have not been touched in the meanwhile. * * @static * @memberof Tile */ static sweepLateTextures() { let now = performance.now() if (now > lastSweepTime + sweepInterval) { lastSweepTime = now let count = 0 for (let [url, texture] of lateTextures.entries()) { if (texture) { let base = texture.baseTexture if (base != null && base.touched == 0) { texture.destroy(true) //console.info("Sweeping ", url) count += 1 lateTextures.delete(url) } } } if (count > 0) console.log('Sweeping textures', count) } } /** * Texture received too late. We do not need it. * @param {*} url * @param {*} texture */ static lateTexture(url, texture) { lateTextures.set(url, texture) //console.warn("Tile.lateTexture") // We cannot destroy the texture since we got errors in t.renderWebGL. // Perhaps we can destroy unsed textures later on Tile.sweepLateTextures() } static printInfos() { let references = new Map() let multiples = 0 for (let [url, tiles] of registeredTiles.entries()) { let count = tiles.size references.set(url, count) if (count > 1) { multiples += 1 } } console.log({ multiples, references }) } }