diff --git a/src/core/CoreNode.ts b/src/core/CoreNode.ts index cab56795..64e60a07 100644 --- a/src/core/CoreNode.ts +++ b/src/core/CoreNode.ts @@ -1017,7 +1017,7 @@ export class CoreNode extends EventEmitter { if (this.updateType & UpdateType.RenderTexture && this.rtt) { // Only the RTT node itself triggers `renderToTexture` this.hasRTTupdates = true; - this.stage.renderer?.renderToTexture(this); + this.loadRenderTexture(); } if (this.updateType & UpdateType.Global) { @@ -1225,11 +1225,7 @@ export class CoreNode extends EventEmitter { //check if CoreNode is renderable based on props hasRenderableProperties(): boolean { if (this.texture !== null) { - if (this.texture.state === 'loaded') { - return true; - } - - return false; + return true; } if (!this.props.width || !this.props.height) { @@ -1401,22 +1397,12 @@ export class CoreNode extends EventEmitter { */ updateIsRenderable() { let newIsRenderable: boolean; - if (this.worldAlpha === 0 || !this.hasRenderableProperties()) { + if (this.worldAlpha === 0 || this.hasRenderableProperties() === false) { newIsRenderable = false; } else { newIsRenderable = this.renderState > CoreNodeRenderState.OutOfBounds; } - // If the texture is not loaded and the node is renderable, load the texture - // this only needs to happen once or until the texture is no longer loaded - if ( - this.texture !== null && - this.texture.state === 'freed' && - this.renderState > CoreNodeRenderState.OutOfBounds - ) { - this.stage.txManager.loadTexture(this.texture); - } - if (this.isRenderable !== newIsRenderable) { this.isRenderable = newIsRenderable; this.onChangeIsRenderable(newIsRenderable); @@ -2045,10 +2031,25 @@ export class CoreNode extends EventEmitter { height: this.height, }); + this.loadRenderTexture(); + } + + private loadRenderTexture() { + if (this.texture === null) { + return; + } + + // If the texture is already loaded, render to it immediately + if (this.texture.state === 'loaded') { + this.stage.renderer?.renderToTexture(this); + return; + } + // call load immediately to ensure the texture is created this.stage.txManager.loadTexture(this.texture, true); - - this.stage.renderer?.renderToTexture(this); // Only this RTT node + this.texture.once('loaded', () => { + this.stage.renderer?.renderToTexture(this); // Only this RTT node + }); } private cleanupRenderTexture() { diff --git a/src/core/Stage.ts b/src/core/Stage.ts index a3c09cb1..bfeb1955 100644 --- a/src/core/Stage.ts +++ b/src/core/Stage.ts @@ -454,7 +454,12 @@ export class Stage { addQuads(node: CoreNode) { assertTruthy(this.renderer); - if (node.isRenderable === true) { + // If the node is renderable and has a loaded texture, render it + if ( + node.isRenderable === true && + node.texture !== null && + node.texture.state === 'loaded' + ) { node.renderQuads(this.renderer); } diff --git a/src/core/textures/Texture.ts b/src/core/textures/Texture.ts index 52a02cbd..282f152d 100644 --- a/src/core/textures/Texture.ts +++ b/src/core/textures/Texture.ts @@ -101,7 +101,12 @@ export interface TextureData { premultiplyAlpha?: boolean | null; } -export type TextureState = 'freed' | 'loading' | 'loaded' | 'failed'; +export type TextureState = + | 'initial' + | 'freed' + | 'loading' + | 'loaded' + | 'failed'; export enum TextureType { 'generic' = 0, @@ -155,11 +160,11 @@ export abstract class Texture extends EventEmitter { readonly error: Error | null = null; // aggregate state - public state: TextureState = 'freed'; + public state: TextureState = 'initial'; // texture source state - private sourceState: TextureState = 'freed'; + private sourceState: TextureState = 'initial'; // texture (gpu) state - private coreCtxState: TextureState = 'freed'; + private coreCtxState: TextureState = 'initial'; readonly renderableOwners = new Set(); @@ -195,13 +200,19 @@ export abstract class Texture extends EventEmitter { */ setRenderableOwner(owner: unknown, renderable: boolean): void { const oldSize = this.renderableOwners.size; - if (renderable) { + if (renderable === true) { this.renderableOwners.add(owner); const newSize = this.renderableOwners.size; + if (newSize > oldSize && newSize === 1) { (this.renderable as boolean) = true; (this.lastRenderableChangeTime as number) = this.txManager.frameTime; this.onChangeIsRenderable?.(true); + + // Check if the texture needs to be added to the loading queue + if (this.state === 'freed' || this.state === 'initial') { + this.txManager.loadTexture(this); + } } } else { this.renderableOwners.delete(owner); @@ -249,6 +260,7 @@ export abstract class Texture extends EventEmitter { this.ctxTexture?.free(); if (this.textureData !== null) { this.textureData = null; + this.setSourceState('freed'); } }