Skip to content

Commit ad55881

Browse files
authored
Refactor isRenderable detection (#479)
✨ What changed? ✨ * Decouple **isRenderable** from **hasRenderableOwner** as textures can be loaded asynchronously. * Refactored the `isRenderable` detection: - If a node has `alpha = 0` or is off screen its not renderable - If a node has dimensions and color properties, its marked renderable and we assume its a `ColorTexture`. - If a node has a texture, it will be marked as a renderable owner. Which will trigger loading of the texture (if its the first owner) - If the texture is loaded, it is marked renderable * On `renderQuads` we assume everything has a texture if not we set the defaultTexture for 1x1 sampling. This is implicit but matches the old behaviour. * `CoreTextNode` overwrites behaviour and checks for text !== ''. * Also fixed a bug where the default texture was still loading but already referenced. * Added a `CoreNode` unit test to validate this more rapidly. 🔧 Addresses the following Fixes #477
2 parents 6174898 + a168da4 commit ad55881

File tree

6 files changed

+260
-125
lines changed

6 files changed

+260
-125
lines changed

src/core/CoreNode.test.ts

Lines changed: 146 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -17,55 +17,73 @@
1717
* limitations under the License.
1818
*/
1919

20-
import { describe, expect, it } from 'vitest';
20+
import { describe, expect, it, vi } from 'vitest';
2121
import { CoreNode, type CoreNodeProps, UpdateType } from './CoreNode.js';
2222
import { Stage } from './Stage.js';
2323
import { mock } from 'vitest-mock-extended';
2424
import { type TextureOptions } from './CoreTextureManager.js';
2525
import { type BaseShaderController } from '../main-api/ShaderController';
26+
import { createBound } from './lib/utils.js';
27+
import { ImageTexture } from './textures/ImageTexture.js';
2628

27-
describe('set color()', () => {
28-
const defaultProps: CoreNodeProps = {
29-
alpha: 0,
30-
autosize: false,
31-
clipping: false,
32-
color: 0,
33-
colorBl: 0,
34-
colorBottom: 0,
35-
colorBr: 0,
36-
colorLeft: 0,
37-
colorRight: 0,
38-
colorTl: 0,
39-
colorTop: 0,
40-
colorTr: 0,
41-
height: 0,
42-
mount: 0,
43-
mountX: 0,
44-
mountY: 0,
45-
parent: null,
46-
pivot: 0,
47-
pivotX: 0,
48-
pivotY: 0,
49-
rotation: 0,
50-
rtt: false,
51-
scale: 0,
52-
scaleX: 0,
53-
scaleY: 0,
54-
shader: mock<BaseShaderController>(),
55-
src: '',
56-
texture: null,
57-
textureOptions: {} as TextureOptions,
58-
width: 0,
59-
x: 0,
60-
y: 0,
61-
zIndex: 0,
62-
zIndexLocked: 0,
63-
preventCleanup: false,
64-
strictBounds: false,
65-
};
29+
const defaultProps: CoreNodeProps = {
30+
alpha: 0,
31+
autosize: false,
32+
clipping: false,
33+
color: 0,
34+
colorBl: 0,
35+
colorBottom: 0,
36+
colorBr: 0,
37+
colorLeft: 0,
38+
colorRight: 0,
39+
colorTl: 0,
40+
colorTop: 0,
41+
colorTr: 0,
42+
height: 0,
43+
mount: 0,
44+
mountX: 0,
45+
mountY: 0,
46+
parent: null,
47+
pivot: 0,
48+
pivotX: 0,
49+
pivotY: 0,
50+
rotation: 0,
51+
rtt: false,
52+
scale: 0,
53+
scaleX: 0,
54+
scaleY: 0,
55+
shader: mock<BaseShaderController>(),
56+
src: '',
57+
texture: null,
58+
textureOptions: {} as TextureOptions,
59+
width: 0,
60+
x: 0,
61+
y: 0,
62+
zIndex: 0,
63+
zIndexLocked: 0,
64+
preventCleanup: false,
65+
strictBounds: false,
66+
};
67+
68+
const clippingRect = {
69+
x: 0,
70+
y: 0,
71+
width: 200,
72+
height: 200,
73+
valid: false,
74+
};
75+
76+
const stage = mock<Stage>({
77+
strictBound: createBound(0, 0, 200, 200),
78+
preloadBound: createBound(0, 0, 200, 200),
79+
defaultTexture: {
80+
state: 'loaded',
81+
},
82+
});
6683

84+
describe('set color()', () => {
6785
it('should set all color subcomponents.', () => {
68-
const node = new CoreNode(mock<Stage>(), defaultProps);
86+
const node = new CoreNode(stage, defaultProps);
6987
node.colorBl = 0x99aabbff;
7088
node.colorBr = 0xaabbccff;
7189
node.colorTl = 0xbbcceeff;
@@ -85,11 +103,97 @@ describe('set color()', () => {
85103
});
86104

87105
it('should set update type.', () => {
88-
const node = new CoreNode(mock<Stage>(), defaultProps);
106+
const node = new CoreNode(stage, defaultProps);
89107
node.updateType = 0;
90108

91109
node.color = 0xffffffff;
92110

93111
expect(node.updateType).toBe(UpdateType.PremultipliedColors);
94112
});
95113
});
114+
115+
describe('isRenderable checks', () => {
116+
it('should return false if node is not renderable', () => {
117+
const node = new CoreNode(stage, defaultProps);
118+
expect(node.isRenderable).toBe(false);
119+
});
120+
121+
it('visible node that is a color texture', () => {
122+
const node = new CoreNode(stage, defaultProps);
123+
node.alpha = 1;
124+
node.x = 0;
125+
node.y = 0;
126+
node.width = 100;
127+
node.height = 100;
128+
node.color = 0xffffffff;
129+
130+
node.update(0, clippingRect);
131+
expect(node.isRenderable).toBe(true);
132+
});
133+
134+
it('visible node that is a texture', () => {
135+
const node = new CoreNode(stage, defaultProps);
136+
node.alpha = 1;
137+
node.x = 0;
138+
node.y = 0;
139+
node.width = 100;
140+
node.height = 100;
141+
node.texture = mock<ImageTexture>({
142+
state: 'initial',
143+
});
144+
145+
node.update(0, clippingRect);
146+
expect(node.isRenderable).toBe(false);
147+
148+
node.texture.state = 'loaded';
149+
node.setUpdateType(UpdateType.IsRenderable);
150+
node.update(1, clippingRect);
151+
152+
expect(node.isRenderable).toBe(true);
153+
});
154+
155+
it('a node with a texture with alpha 0 should not be renderable', () => {
156+
const node = new CoreNode(stage, defaultProps);
157+
node.alpha = 0;
158+
node.x = 0;
159+
node.y = 0;
160+
node.width = 100;
161+
node.height = 100;
162+
node.texture = mock<ImageTexture>({
163+
state: 'loaded',
164+
});
165+
166+
node.update(0, clippingRect);
167+
expect(node.isRenderable).toBe(false);
168+
});
169+
170+
it('a node with a texture that is OutOfBounds should not be renderable', () => {
171+
const node = new CoreNode(stage, defaultProps);
172+
node.alpha = 1;
173+
node.x = 300;
174+
node.y = 300;
175+
node.width = 100;
176+
node.height = 100;
177+
node.texture = mock<ImageTexture>({
178+
state: 'loaded',
179+
});
180+
181+
node.update(0, clippingRect);
182+
expect(node.isRenderable).toBe(false);
183+
});
184+
185+
it('a node with a freed texture should not be renderable', () => {
186+
const node = new CoreNode(stage, defaultProps);
187+
node.alpha = 1;
188+
node.x = 0;
189+
node.y = 0;
190+
node.width = 100;
191+
node.height = 100;
192+
node.texture = mock<ImageTexture>({
193+
state: 'freed',
194+
});
195+
196+
node.update(0, clippingRect);
197+
expect(node.isRenderable).toBe(false);
198+
});
199+
});

0 commit comments

Comments
 (0)