Skip to content

Commit 422d51e

Browse files
authored
🎨 Refactor CoreNode Renderability Logic 🚀 (#476)
### What’s New? ✨ This PR refactors Texture Throttling in the following ways: - 🕒 Async RTT Texture Loading: When an RTT node is set, wait for the texture to load asynchronously. - 🗑️ Improved Texture Cleanup: When a texture is freed, its source texture is now also marked as freed. Fixes a bug where textures weren't returning after memory cleanup. - 🔄 Auto-Trigger Texture Loading: Refactored isRenderable owner changes to automatically call texture loading if the texture state is freed or initial. ## How to Test? 🧪 RTT Changes: Use `test=rtt-dimension`. 🖼️ You should see race conditions occurring prior to this PR. Texture Freed Fix: Add a rocko.png to the `test=texture-cleanup-critical` test and move it in and out of the screen to validate. 🎯 (Not the prettiest, but it works!). Might create an automated test for this in the future. ## What’s Next? 🚀 More Testing! 🛠️
2 parents bd2186d + e31f190 commit 422d51e

File tree

3 files changed

+43
-25
lines changed

3 files changed

+43
-25
lines changed

src/core/CoreNode.ts

Lines changed: 20 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1017,7 +1017,7 @@ export class CoreNode extends EventEmitter {
10171017
if (this.updateType & UpdateType.RenderTexture && this.rtt) {
10181018
// Only the RTT node itself triggers `renderToTexture`
10191019
this.hasRTTupdates = true;
1020-
this.stage.renderer?.renderToTexture(this);
1020+
this.loadRenderTexture();
10211021
}
10221022

10231023
if (this.updateType & UpdateType.Global) {
@@ -1225,11 +1225,7 @@ export class CoreNode extends EventEmitter {
12251225
//check if CoreNode is renderable based on props
12261226
hasRenderableProperties(): boolean {
12271227
if (this.texture !== null) {
1228-
if (this.texture.state === 'loaded') {
1229-
return true;
1230-
}
1231-
1232-
return false;
1228+
return true;
12331229
}
12341230

12351231
if (!this.props.width || !this.props.height) {
@@ -1401,22 +1397,12 @@ export class CoreNode extends EventEmitter {
14011397
*/
14021398
updateIsRenderable() {
14031399
let newIsRenderable: boolean;
1404-
if (this.worldAlpha === 0 || !this.hasRenderableProperties()) {
1400+
if (this.worldAlpha === 0 || this.hasRenderableProperties() === false) {
14051401
newIsRenderable = false;
14061402
} else {
14071403
newIsRenderable = this.renderState > CoreNodeRenderState.OutOfBounds;
14081404
}
14091405

1410-
// If the texture is not loaded and the node is renderable, load the texture
1411-
// this only needs to happen once or until the texture is no longer loaded
1412-
if (
1413-
this.texture !== null &&
1414-
this.texture.state === 'freed' &&
1415-
this.renderState > CoreNodeRenderState.OutOfBounds
1416-
) {
1417-
this.stage.txManager.loadTexture(this.texture);
1418-
}
1419-
14201406
if (this.isRenderable !== newIsRenderable) {
14211407
this.isRenderable = newIsRenderable;
14221408
this.onChangeIsRenderable(newIsRenderable);
@@ -2045,10 +2031,25 @@ export class CoreNode extends EventEmitter {
20452031
height: this.height,
20462032
});
20472033

2034+
this.loadRenderTexture();
2035+
}
2036+
2037+
private loadRenderTexture() {
2038+
if (this.texture === null) {
2039+
return;
2040+
}
2041+
2042+
// If the texture is already loaded, render to it immediately
2043+
if (this.texture.state === 'loaded') {
2044+
this.stage.renderer?.renderToTexture(this);
2045+
return;
2046+
}
2047+
20482048
// call load immediately to ensure the texture is created
20492049
this.stage.txManager.loadTexture(this.texture, true);
2050-
2051-
this.stage.renderer?.renderToTexture(this); // Only this RTT node
2050+
this.texture.once('loaded', () => {
2051+
this.stage.renderer?.renderToTexture(this); // Only this RTT node
2052+
});
20522053
}
20532054

20542055
private cleanupRenderTexture() {

src/core/Stage.ts

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -454,7 +454,12 @@ export class Stage {
454454
addQuads(node: CoreNode) {
455455
assertTruthy(this.renderer);
456456

457-
if (node.isRenderable === true) {
457+
// If the node is renderable and has a loaded texture, render it
458+
if (
459+
node.isRenderable === true &&
460+
node.texture !== null &&
461+
node.texture.state === 'loaded'
462+
) {
458463
node.renderQuads(this.renderer);
459464
}
460465

src/core/textures/Texture.ts

Lines changed: 17 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,12 @@ export interface TextureData {
101101
premultiplyAlpha?: boolean | null;
102102
}
103103

104-
export type TextureState = 'freed' | 'loading' | 'loaded' | 'failed';
104+
export type TextureState =
105+
| 'initial'
106+
| 'freed'
107+
| 'loading'
108+
| 'loaded'
109+
| 'failed';
105110

106111
export enum TextureType {
107112
'generic' = 0,
@@ -155,11 +160,11 @@ export abstract class Texture extends EventEmitter {
155160
readonly error: Error | null = null;
156161

157162
// aggregate state
158-
public state: TextureState = 'freed';
163+
public state: TextureState = 'initial';
159164
// texture source state
160-
private sourceState: TextureState = 'freed';
165+
private sourceState: TextureState = 'initial';
161166
// texture (gpu) state
162-
private coreCtxState: TextureState = 'freed';
167+
private coreCtxState: TextureState = 'initial';
163168

164169
readonly renderableOwners = new Set<unknown>();
165170

@@ -195,13 +200,19 @@ export abstract class Texture extends EventEmitter {
195200
*/
196201
setRenderableOwner(owner: unknown, renderable: boolean): void {
197202
const oldSize = this.renderableOwners.size;
198-
if (renderable) {
203+
if (renderable === true) {
199204
this.renderableOwners.add(owner);
200205
const newSize = this.renderableOwners.size;
206+
201207
if (newSize > oldSize && newSize === 1) {
202208
(this.renderable as boolean) = true;
203209
(this.lastRenderableChangeTime as number) = this.txManager.frameTime;
204210
this.onChangeIsRenderable?.(true);
211+
212+
// Check if the texture needs to be added to the loading queue
213+
if (this.state === 'freed' || this.state === 'initial') {
214+
this.txManager.loadTexture(this);
215+
}
205216
}
206217
} else {
207218
this.renderableOwners.delete(owner);
@@ -249,6 +260,7 @@ export abstract class Texture extends EventEmitter {
249260
this.ctxTexture?.free();
250261
if (this.textureData !== null) {
251262
this.textureData = null;
263+
this.setSourceState('freed');
252264
}
253265
}
254266

0 commit comments

Comments
 (0)