@lightningjs/renderer 3.0.0-beta17 → 3.0.0-beta19
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/Autosizer.d.ts +29 -0
- package/dist/src/core/Autosizer.js +178 -0
- package/dist/src/core/Autosizer.js.map +1 -0
- package/dist/src/core/CoreNode.d.ts +44 -26
- package/dist/src/core/CoreNode.js +253 -150
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +1 -0
- package/dist/src/core/CoreTextNode.js +8 -4
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.d.ts +16 -2
- package/dist/src/core/CoreTextureManager.js +38 -51
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.d.ts +18 -2
- package/dist/src/core/Stage.js +46 -15
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureError.d.ts +11 -0
- package/dist/src/core/TextureError.js +37 -0
- package/dist/src/core/TextureError.js.map +1 -0
- package/dist/src/core/TextureMemoryManager.d.ts +2 -4
- package/dist/src/core/TextureMemoryManager.js +80 -95
- package/dist/src/core/TextureMemoryManager.js.map +1 -1
- package/dist/src/core/lib/WebGlContextWrapper.d.ts +11 -0
- package/dist/src/core/lib/WebGlContextWrapper.js +34 -0
- package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
- package/dist/src/core/lib/textureCompression.d.ts +14 -2
- package/dist/src/core/lib/textureCompression.js +320 -67
- package/dist/src/core/lib/textureCompression.js.map +1 -1
- package/dist/src/core/platforms/web/WebPlatform.js +2 -2
- package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
- package/dist/src/core/renderers/CoreContextTexture.d.ts +1 -0
- package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
- package/dist/src/core/renderers/CoreRenderer.d.ts +3 -1
- package/dist/src/core/renderers/CoreRenderer.js +1 -0
- package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/CoreShaderNode.d.ts +6 -0
- package/dist/src/core/renderers/CoreShaderNode.js +2 -0
- package/dist/src/core/renderers/CoreShaderNode.js.map +1 -1
- package/dist/src/core/renderers/canvas/CanvasTexture.d.ts +1 -0
- package/dist/src/core/renderers/canvas/CanvasTexture.js +4 -1
- package/dist/src/core/renderers/canvas/CanvasTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.d.ts +2 -0
- package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js +6 -0
- package/dist/src/core/renderers/webgl/WebGlCtxRenderTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.d.ts +11 -0
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js +51 -14
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderOp.d.ts +2 -1
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js +2 -0
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +57 -61
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +1 -0
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +12 -0
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
- package/dist/src/core/shaders/canvas/RadialGradient.js +1 -1
- package/dist/src/core/shaders/canvas/RadialGradient.js.map +1 -1
- package/dist/src/core/shaders/templates/RadialGradientTemplate.d.ts +6 -4
- package/dist/src/core/shaders/templates/RadialGradientTemplate.js.map +1 -1
- package/dist/src/core/shaders/webgl/LinearGradient.js +2 -1
- package/dist/src/core/shaders/webgl/LinearGradient.js.map +1 -1
- package/dist/src/core/shaders/webgl/RadialGradient.js +8 -7
- package/dist/src/core/shaders/webgl/RadialGradient.js.map +1 -1
- package/dist/src/core/shaders/webgl/Rounded.js +3 -1
- package/dist/src/core/shaders/webgl/Rounded.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithBorder.js +3 -2
- package/dist/src/core/shaders/webgl/RoundedWithBorder.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js +3 -2
- package/dist/src/core/shaders/webgl/RoundedWithBorderAndShadow.js.map +1 -1
- package/dist/src/core/shaders/webgl/RoundedWithShadow.js +2 -1
- package/dist/src/core/shaders/webgl/RoundedWithShadow.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.js +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/textures/ColorTexture.d.ts +1 -1
- package/dist/src/core/textures/ColorTexture.js +2 -3
- package/dist/src/core/textures/ColorTexture.js.map +1 -1
- package/dist/src/core/textures/ImageTexture.d.ts +7 -1
- package/dist/src/core/textures/ImageTexture.js +20 -36
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/src/core/textures/NoiseTexture.d.ts +1 -1
- package/dist/src/core/textures/NoiseTexture.js +2 -2
- package/dist/src/core/textures/NoiseTexture.js.map +1 -1
- package/dist/src/core/textures/RenderTexture.d.ts +1 -1
- package/dist/src/core/textures/RenderTexture.js +2 -2
- package/dist/src/core/textures/RenderTexture.js.map +1 -1
- package/dist/src/core/textures/SubTexture.d.ts +2 -4
- package/dist/src/core/textures/SubTexture.js +13 -31
- package/dist/src/core/textures/SubTexture.js.map +1 -1
- package/dist/src/core/textures/Texture.d.ts +67 -8
- package/dist/src/core/textures/Texture.js +127 -15
- package/dist/src/core/textures/Texture.js.map +1 -1
- package/dist/src/core/utils.d.ts +1 -1
- package/dist/src/main-api/Renderer.d.ts +15 -2
- package/dist/src/main-api/Renderer.js +19 -12
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/CoreNode.test.ts +0 -1
- package/src/core/CoreNode.ts +296 -177
- package/src/core/CoreTextNode.ts +9 -4
- package/src/core/CoreTextureManager.ts +69 -57
- package/src/core/Stage.ts +54 -18
- package/src/core/TextureError.ts +46 -0
- package/src/core/TextureMemoryManager.ts +95 -122
- package/src/core/lib/WebGlContextWrapper.ts +40 -0
- package/src/core/lib/collectionUtils.ts +118 -0
- package/src/core/lib/textureCompression.ts +433 -77
- package/src/core/platforms/web/WebPlatform.ts +2 -2
- package/src/core/renderers/CoreContextTexture.ts +1 -0
- package/src/core/renderers/CoreRenderer.ts +3 -2
- package/src/core/renderers/CoreShaderNode.ts +7 -0
- package/src/core/renderers/canvas/CanvasTexture.ts +5 -1
- package/src/core/renderers/webgl/WebGlCtxRenderTexture.ts +8 -0
- package/src/core/renderers/webgl/WebGlCtxTexture.ts +58 -15
- package/src/core/renderers/webgl/WebGlRenderOp.ts +4 -1
- package/src/core/renderers/webgl/WebGlRenderer.ts +66 -68
- package/src/core/renderers/webgl/WebGlShaderProgram.ts +16 -0
- package/src/core/shaders/canvas/RadialGradient.ts +1 -1
- package/src/core/shaders/templates/RadialGradientTemplate.ts +6 -4
- package/src/core/shaders/webgl/LinearGradient.ts +2 -1
- package/src/core/shaders/webgl/RadialGradient.ts +8 -7
- package/src/core/shaders/webgl/Rounded.ts +3 -1
- package/src/core/shaders/webgl/RoundedWithBorder.ts +3 -2
- package/src/core/shaders/webgl/RoundedWithBorderAndShadow.ts +3 -2
- package/src/core/shaders/webgl/RoundedWithShadow.ts +2 -1
- package/src/core/text-rendering/CanvasTextRenderer.ts +1 -1
- package/src/core/text-rendering/SdfFontHandler.ts +1 -1
- package/src/core/textures/ColorTexture.ts +6 -4
- package/src/core/textures/ImageTexture.ts +35 -42
- package/src/core/textures/NoiseTexture.ts +6 -4
- package/src/core/textures/RenderTexture.ts +6 -4
- package/src/core/textures/SubTexture.ts +17 -38
- package/src/core/textures/Texture.ts +159 -20
- package/src/main-api/Renderer.ts +36 -14
- package/dist/src/core/animations/Animation.d.ts +0 -16
- package/dist/src/core/animations/Animation.js +0 -111
- package/dist/src/core/animations/Animation.js.map +0 -1
- package/dist/src/core/animations/CoreTransition.d.ts +0 -24
- package/dist/src/core/animations/CoreTransition.js +0 -63
- package/dist/src/core/animations/CoreTransition.js.map +0 -1
- package/dist/src/core/animations/Playback.d.ts +0 -62
- package/dist/src/core/animations/Playback.js +0 -155
- package/dist/src/core/animations/Playback.js.map +0 -1
- package/dist/src/core/animations/Transition.d.ts +0 -25
- package/dist/src/core/animations/Transition.js +0 -63
- package/dist/src/core/animations/Transition.js.map +0 -1
- package/dist/src/core/animations/utils.d.ts +0 -2
- package/dist/src/core/animations/utils.js +0 -137
- package/dist/src/core/animations/utils.js.map +0 -1
|
@@ -109,6 +109,12 @@ export interface ImageTextureProps {
|
|
|
109
109
|
* @default null
|
|
110
110
|
*/
|
|
111
111
|
sy?: number | null;
|
|
112
|
+
/**
|
|
113
|
+
* Maximum number of times to retry loading the image if it fails.
|
|
114
|
+
*
|
|
115
|
+
* @default 5
|
|
116
|
+
*/
|
|
117
|
+
maxRetryCount?: number;
|
|
112
118
|
}
|
|
113
119
|
|
|
114
120
|
/**
|
|
@@ -131,11 +137,15 @@ export class ImageTexture extends Texture {
|
|
|
131
137
|
public props: Required<ImageTextureProps>;
|
|
132
138
|
public override type: TextureType = TextureType.image;
|
|
133
139
|
|
|
134
|
-
constructor(
|
|
140
|
+
constructor(
|
|
141
|
+
txManager: CoreTextureManager,
|
|
142
|
+
props: Required<ImageTextureProps>,
|
|
143
|
+
) {
|
|
135
144
|
super(txManager);
|
|
136
145
|
|
|
137
146
|
this.platform = txManager.platform;
|
|
138
|
-
this.props =
|
|
147
|
+
this.props = props;
|
|
148
|
+
this.maxRetryCount = props.maxRetryCount as number;
|
|
139
149
|
}
|
|
140
150
|
|
|
141
151
|
hasAlphaChannel(mimeType: string) {
|
|
@@ -152,14 +162,19 @@ export class ImageTexture extends Texture {
|
|
|
152
162
|
return new Promise<{
|
|
153
163
|
data: HTMLImageElement | null;
|
|
154
164
|
premultiplyAlpha: boolean;
|
|
155
|
-
}>((resolve) => {
|
|
165
|
+
}>((resolve, reject) => {
|
|
156
166
|
img.onload = () => {
|
|
157
167
|
resolve({ data: img, premultiplyAlpha: hasAlpha });
|
|
158
168
|
};
|
|
159
169
|
|
|
160
|
-
img.onerror = () => {
|
|
161
|
-
|
|
162
|
-
|
|
170
|
+
img.onerror = (err) => {
|
|
171
|
+
const errorMessage =
|
|
172
|
+
err instanceof Error
|
|
173
|
+
? err.message
|
|
174
|
+
: err instanceof Event
|
|
175
|
+
? `Image loading failed for ${img.src}`
|
|
176
|
+
: 'Unknown image loading error';
|
|
177
|
+
reject(new Error(`Image loading failed: ${errorMessage}`));
|
|
163
178
|
};
|
|
164
179
|
|
|
165
180
|
if (src instanceof Blob) {
|
|
@@ -253,17 +268,12 @@ export class ImageTexture extends Texture {
|
|
|
253
268
|
}
|
|
254
269
|
|
|
255
270
|
override async getTextureSource(): Promise<TextureData> {
|
|
256
|
-
let resp;
|
|
271
|
+
let resp: TextureData;
|
|
257
272
|
try {
|
|
258
273
|
resp = await this.determineImageTypeAndLoadImage();
|
|
259
274
|
} catch (e) {
|
|
260
275
|
this.setState('failed', e as Error);
|
|
261
276
|
|
|
262
|
-
// log error only in development
|
|
263
|
-
if (isProductionEnvironment === false) {
|
|
264
|
-
console.error('ImageTexture:', e);
|
|
265
|
-
}
|
|
266
|
-
|
|
267
277
|
return {
|
|
268
278
|
data: null,
|
|
269
279
|
};
|
|
@@ -276,23 +286,6 @@ export class ImageTexture extends Texture {
|
|
|
276
286
|
};
|
|
277
287
|
}
|
|
278
288
|
|
|
279
|
-
let w, h;
|
|
280
|
-
// check if resp.data is typeof Uint8ClampedArray else
|
|
281
|
-
// use resp.data.width and resp.data.height
|
|
282
|
-
if (resp.data instanceof Uint8Array) {
|
|
283
|
-
w = this.props.w ?? 0;
|
|
284
|
-
h = this.props.h ?? 0;
|
|
285
|
-
} else {
|
|
286
|
-
w = resp.data?.width ?? (this.props.w || 0);
|
|
287
|
-
h = resp.data?.height ?? (this.props.h || 0);
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
// we're loaded!
|
|
291
|
-
this.setState('fetched', {
|
|
292
|
-
w,
|
|
293
|
-
h,
|
|
294
|
-
});
|
|
295
|
-
|
|
296
289
|
return {
|
|
297
290
|
data: resp.data,
|
|
298
291
|
premultiplyAlpha: this.props.premultiplyAlpha ?? true,
|
|
@@ -375,26 +368,25 @@ export class ImageTexture extends Texture {
|
|
|
375
368
|
* @returns The cache key as a string, or `false` if the key cannot be generated.
|
|
376
369
|
*/
|
|
377
370
|
static override makeCacheKey(props: ImageTextureProps): string | false {
|
|
378
|
-
const resolvedProps = ImageTexture.resolveDefaults(props);
|
|
379
371
|
// Only cache key-able textures; prioritise key
|
|
380
|
-
const key =
|
|
372
|
+
const key = props.key || props.src;
|
|
381
373
|
if (typeof key !== 'string') {
|
|
382
374
|
return false;
|
|
383
375
|
}
|
|
384
376
|
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
377
|
+
let cacheKey = `ImageTexture,${key},${props.premultiplyAlpha ?? 'true'},${
|
|
378
|
+
props.maxRetryCount
|
|
379
|
+
}`;
|
|
380
|
+
|
|
381
|
+
if (props.sh !== null && props.sw !== null) {
|
|
382
|
+
cacheKey += ',';
|
|
383
|
+
cacheKey += props.sx ?? '';
|
|
384
|
+
cacheKey += props.sy ?? '';
|
|
385
|
+
cacheKey += props.sw || '';
|
|
386
|
+
cacheKey += props.sh || '';
|
|
393
387
|
}
|
|
394
388
|
|
|
395
|
-
return
|
|
396
|
-
resolvedProps.premultiplyAlpha ?? 'true'
|
|
397
|
-
}${dimensionProps}`;
|
|
389
|
+
return cacheKey;
|
|
398
390
|
}
|
|
399
391
|
|
|
400
392
|
static override resolveDefaults(
|
|
@@ -411,6 +403,7 @@ export class ImageTexture extends Texture {
|
|
|
411
403
|
sy: props.sy ?? null,
|
|
412
404
|
sw: props.sw ?? null,
|
|
413
405
|
sh: props.sh ?? null,
|
|
406
|
+
maxRetryCount: props.maxRetryCount ?? 5,
|
|
414
407
|
};
|
|
415
408
|
}
|
|
416
409
|
|
|
@@ -58,9 +58,12 @@ export class NoiseTexture extends Texture {
|
|
|
58
58
|
|
|
59
59
|
public override type: TextureType = TextureType.noise;
|
|
60
60
|
|
|
61
|
-
constructor(
|
|
61
|
+
constructor(
|
|
62
|
+
txManager: CoreTextureManager,
|
|
63
|
+
props: Required<NoiseTextureProps>,
|
|
64
|
+
) {
|
|
62
65
|
super(txManager);
|
|
63
|
-
this.props =
|
|
66
|
+
this.props = props;
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
override async getTextureSource(): Promise<TextureData> {
|
|
@@ -75,8 +78,7 @@ export class NoiseTexture extends Texture {
|
|
|
75
78
|
pixelData8[i + 3] = 255;
|
|
76
79
|
}
|
|
77
80
|
|
|
78
|
-
|
|
79
|
-
|
|
81
|
+
// Noise Texture data ready - dimensions will be set during upload
|
|
80
82
|
return {
|
|
81
83
|
data: new ImageData(pixelData8, w, h),
|
|
82
84
|
};
|
|
@@ -42,9 +42,12 @@ export class RenderTexture extends Texture {
|
|
|
42
42
|
|
|
43
43
|
public override type: TextureType = TextureType.renderToTexture;
|
|
44
44
|
|
|
45
|
-
constructor(
|
|
45
|
+
constructor(
|
|
46
|
+
txManager: CoreTextureManager,
|
|
47
|
+
props: Required<RenderTextureProps>,
|
|
48
|
+
) {
|
|
46
49
|
super(txManager);
|
|
47
|
-
this.props =
|
|
50
|
+
this.props = props;
|
|
48
51
|
}
|
|
49
52
|
|
|
50
53
|
get w() {
|
|
@@ -64,8 +67,7 @@ export class RenderTexture extends Texture {
|
|
|
64
67
|
}
|
|
65
68
|
|
|
66
69
|
override async getTextureSource(): Promise<TextureData> {
|
|
67
|
-
|
|
68
|
-
|
|
70
|
+
// Render texture data ready - dimensions will be set during upload
|
|
69
71
|
return {
|
|
70
72
|
data: null,
|
|
71
73
|
premultiplyAlpha: null,
|
|
@@ -30,6 +30,8 @@ import {
|
|
|
30
30
|
type TextureState,
|
|
31
31
|
} from './Texture.js';
|
|
32
32
|
|
|
33
|
+
let subTextureId = 0;
|
|
34
|
+
|
|
33
35
|
/**
|
|
34
36
|
* Properties of the {@link SubTexture}
|
|
35
37
|
*/
|
|
@@ -83,10 +85,11 @@ export class SubTexture extends Texture {
|
|
|
83
85
|
parentTexture: Texture;
|
|
84
86
|
|
|
85
87
|
public override type: TextureType = TextureType.subTexture;
|
|
88
|
+
public subtextureId = `subtexture-${subTextureId++}`;
|
|
86
89
|
|
|
87
|
-
constructor(txManager: CoreTextureManager, props: SubTextureProps) {
|
|
90
|
+
constructor(txManager: CoreTextureManager, props: Required<SubTextureProps>) {
|
|
88
91
|
super(txManager);
|
|
89
|
-
this.props =
|
|
92
|
+
this.props = props;
|
|
90
93
|
|
|
91
94
|
assertTruthy(this.props.texture, 'SubTexture requires a parent texture');
|
|
92
95
|
assertTruthy(
|
|
@@ -97,8 +100,8 @@ export class SubTexture extends Texture {
|
|
|
97
100
|
// Resolve parent texture from cache or fallback to provided texture
|
|
98
101
|
this.parentTexture = txManager.resolveParentTexture(this.props.texture);
|
|
99
102
|
|
|
100
|
-
if (this.renderableOwners.
|
|
101
|
-
this.parentTexture.setRenderableOwner(this, true);
|
|
103
|
+
if (this.renderableOwners.length > 0) {
|
|
104
|
+
this.parentTexture.setRenderableOwner(this.subtextureId, true);
|
|
102
105
|
}
|
|
103
106
|
|
|
104
107
|
// If parent texture is already loaded / failed, trigger loaded event manually
|
|
@@ -107,23 +110,17 @@ export class SubTexture extends Texture {
|
|
|
107
110
|
// synchronous task after calling loadTexture()
|
|
108
111
|
queueMicrotask(() => {
|
|
109
112
|
const parentTx = this.parentTexture;
|
|
110
|
-
if (parentTx.state === 'loaded') {
|
|
111
|
-
this.onParentTxLoaded(parentTx, parentTx.dimensions
|
|
112
|
-
} else if (parentTx.state === 'fetching') {
|
|
113
|
-
this.onParentTxFetching();
|
|
114
|
-
} else if (parentTx.state === 'fetched') {
|
|
115
|
-
this.onParentTxFetched();
|
|
113
|
+
if (parentTx.state === 'loaded' && parentTx.dimensions !== null) {
|
|
114
|
+
this.onParentTxLoaded(parentTx, parentTx.dimensions);
|
|
116
115
|
} else if (parentTx.state === 'loading') {
|
|
117
116
|
this.onParentTxLoading();
|
|
118
|
-
} else if (parentTx.state === 'failed') {
|
|
119
|
-
this.onParentTxFailed(parentTx, parentTx.error
|
|
117
|
+
} else if (parentTx.state === 'failed' && parentTx.error !== null) {
|
|
118
|
+
this.onParentTxFailed(parentTx, parentTx.error);
|
|
120
119
|
} else if (parentTx.state === 'freed') {
|
|
121
120
|
this.onParentTxFreed();
|
|
122
121
|
}
|
|
123
122
|
|
|
124
|
-
parentTx.on('fetched', this.onParentTxFetched);
|
|
125
123
|
parentTx.on('loading', this.onParentTxLoading);
|
|
126
|
-
parentTx.on('fetching', this.onParentTxFetching);
|
|
127
124
|
parentTx.on('loaded', this.onParentTxLoaded);
|
|
128
125
|
parentTx.on('failed', this.onParentTxFailed);
|
|
129
126
|
parentTx.on('freed', this.onParentTxFreed);
|
|
@@ -133,51 +130,33 @@ export class SubTexture extends Texture {
|
|
|
133
130
|
private onParentTxLoaded: TextureLoadedEventHandler = () => {
|
|
134
131
|
// We ignore the parent's passed dimensions, and simply use the SubTexture's
|
|
135
132
|
// configured dimensions (because that's all that matters here)
|
|
136
|
-
this.
|
|
133
|
+
this.setState('loaded', {
|
|
137
134
|
w: this.props.w,
|
|
138
135
|
h: this.props.h,
|
|
139
136
|
});
|
|
140
137
|
};
|
|
141
138
|
|
|
142
139
|
private onParentTxFailed: TextureFailedEventHandler = (target, error) => {
|
|
143
|
-
this.
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
private onParentTxFetched = () => {
|
|
147
|
-
this.forwardParentTxState('fetched', {
|
|
148
|
-
w: this.props.w,
|
|
149
|
-
h: this.props.h,
|
|
150
|
-
});
|
|
151
|
-
};
|
|
152
|
-
|
|
153
|
-
private onParentTxFetching = () => {
|
|
154
|
-
this.forwardParentTxState('fetching');
|
|
140
|
+
this.retryCount = this.parentTexture.retryCount - 1;
|
|
141
|
+
this.setState('failed', error);
|
|
155
142
|
};
|
|
156
143
|
|
|
157
144
|
private onParentTxLoading = () => {
|
|
158
|
-
this.
|
|
145
|
+
this.setState('loading');
|
|
159
146
|
};
|
|
160
147
|
|
|
161
148
|
private onParentTxFreed = () => {
|
|
162
|
-
this.
|
|
149
|
+
this.setState('freed');
|
|
163
150
|
};
|
|
164
151
|
|
|
165
|
-
private forwardParentTxState(
|
|
166
|
-
state: TextureState,
|
|
167
|
-
errorOrDimensions?: Error | Dimensions,
|
|
168
|
-
) {
|
|
169
|
-
this.setState(state, errorOrDimensions);
|
|
170
|
-
}
|
|
171
|
-
|
|
172
152
|
override onChangeIsRenderable(isRenderable: boolean): void {
|
|
173
153
|
// Propagate the renderable owner change to the parent texture
|
|
174
|
-
this.parentTexture.setRenderableOwner(this, isRenderable);
|
|
154
|
+
this.parentTexture.setRenderableOwner(this.subtextureId, isRenderable);
|
|
175
155
|
}
|
|
176
156
|
|
|
177
157
|
override async getTextureSource(): Promise<TextureData> {
|
|
178
158
|
// Check if parent texture is loaded
|
|
179
159
|
return new Promise((resolve, reject) => {
|
|
180
|
-
this.setState('fetched');
|
|
181
160
|
resolve({
|
|
182
161
|
data: this.props,
|
|
183
162
|
});
|
|
@@ -54,12 +54,12 @@ export interface CompressedData {
|
|
|
54
54
|
/**
|
|
55
55
|
* All mipmap levels
|
|
56
56
|
*/
|
|
57
|
-
mipmaps
|
|
57
|
+
mipmaps: ArrayBuffer[];
|
|
58
58
|
|
|
59
59
|
/**
|
|
60
60
|
* Supported container types ('pvr' or 'ktx').
|
|
61
61
|
*/
|
|
62
|
-
type: 'pvr' | 'ktx';
|
|
62
|
+
type: 'pvr' | 'ktx' | 'astc';
|
|
63
63
|
|
|
64
64
|
/**
|
|
65
65
|
* The width of the compressed texture in pixels. Defaults to 0.
|
|
@@ -72,6 +72,15 @@ export interface CompressedData {
|
|
|
72
72
|
* The height of the compressed texture in pixels.
|
|
73
73
|
**/
|
|
74
74
|
h: number;
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* block info compressed texture format
|
|
78
|
+
*/
|
|
79
|
+
blockInfo: {
|
|
80
|
+
width: number;
|
|
81
|
+
height: number;
|
|
82
|
+
bytes: number;
|
|
83
|
+
};
|
|
75
84
|
}
|
|
76
85
|
|
|
77
86
|
/**
|
|
@@ -144,16 +153,10 @@ export abstract class Texture extends EventEmitter {
|
|
|
144
153
|
private _dimensions: Dimensions | null = null;
|
|
145
154
|
private _error: Error | null = null;
|
|
146
155
|
|
|
147
|
-
/**
|
|
148
|
-
* Texture states that are considered transitional and should be skipped during cleanup
|
|
149
|
-
*/
|
|
150
|
-
public static readonly TRANSITIONAL_TEXTURE_STATES: readonly TextureState[] =
|
|
151
|
-
['fetching', 'fetched', 'loading'];
|
|
152
|
-
|
|
153
156
|
// aggregate state
|
|
154
157
|
public state: TextureState = 'initial';
|
|
155
158
|
|
|
156
|
-
readonly renderableOwners =
|
|
159
|
+
readonly renderableOwners: any[] = [];
|
|
157
160
|
|
|
158
161
|
readonly renderable: boolean = false;
|
|
159
162
|
|
|
@@ -165,8 +168,37 @@ export abstract class Texture extends EventEmitter {
|
|
|
165
168
|
|
|
166
169
|
public textureData: TextureData | null = null;
|
|
167
170
|
|
|
171
|
+
public memUsed = 0;
|
|
172
|
+
|
|
173
|
+
/**
|
|
174
|
+
* Memory used by this texture in bytes
|
|
175
|
+
*
|
|
176
|
+
* @remarks
|
|
177
|
+
* This is tracked by the TextureMemoryManager and updated when the texture
|
|
178
|
+
* is loaded/freed. Set to 0 when texture is not loaded.
|
|
179
|
+
*/
|
|
180
|
+
public retryCount = 0;
|
|
181
|
+
public maxRetryCount: number;
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Timestamp when texture was created (for startup grace period)
|
|
185
|
+
*/
|
|
186
|
+
private createdAt: number = Date.now();
|
|
187
|
+
|
|
188
|
+
/**
|
|
189
|
+
* Flag to track if grace period has expired to avoid repeated Date.now() calls
|
|
190
|
+
*/
|
|
191
|
+
private gracePeriodExpired: boolean = false;
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Grace period in milliseconds to prevent premature cleanup during app startup
|
|
195
|
+
* This helps prevent race conditions when bounds calculation is delayed
|
|
196
|
+
*/
|
|
197
|
+
private static readonly STARTUP_GRACE_PERIOD = 2000; // 2 seconds
|
|
198
|
+
|
|
168
199
|
constructor(protected txManager: CoreTextureManager) {
|
|
169
200
|
super();
|
|
201
|
+
this.maxRetryCount = this.txManager.maxRetryCount;
|
|
170
202
|
}
|
|
171
203
|
|
|
172
204
|
get dimensions(): Dimensions | null {
|
|
@@ -177,6 +209,59 @@ export abstract class Texture extends EventEmitter {
|
|
|
177
209
|
return this._error;
|
|
178
210
|
}
|
|
179
211
|
|
|
212
|
+
/**
|
|
213
|
+
* Checks if the texture is within the startup grace period.
|
|
214
|
+
* During this period, textures are protected from cleanup to prevent
|
|
215
|
+
* race conditions during app initialization.
|
|
216
|
+
*/
|
|
217
|
+
isWithinStartupGracePeriod(): boolean {
|
|
218
|
+
// If grace period already expired, return false immediately
|
|
219
|
+
if (this.gracePeriodExpired === true) {
|
|
220
|
+
return false;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
// Check if grace period has expired now
|
|
224
|
+
const hasExpired =
|
|
225
|
+
Date.now() - this.createdAt >= Texture.STARTUP_GRACE_PERIOD;
|
|
226
|
+
|
|
227
|
+
if (hasExpired) {
|
|
228
|
+
// Cache the result to avoid future Date.now() calls
|
|
229
|
+
this.gracePeriodExpired = true;
|
|
230
|
+
return false;
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
return true;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
/**
|
|
237
|
+
* Checks if the texture can be safely cleaned up.
|
|
238
|
+
* Considers the renderable state, startup grace period, and renderable owners.
|
|
239
|
+
*/
|
|
240
|
+
canBeCleanedUp(): boolean {
|
|
241
|
+
// Never cleanup if explicitly prevented
|
|
242
|
+
if (this.preventCleanup) {
|
|
243
|
+
return false;
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
// Don't cleanup if still within startup grace period
|
|
247
|
+
if (this.isWithinStartupGracePeriod()) {
|
|
248
|
+
return false;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
// Don't cleanup if not renderable
|
|
252
|
+
if (this.renderable === true) {
|
|
253
|
+
return false;
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
// Don't cleanup if there are still renderable owners
|
|
257
|
+
if (this.renderableOwners.length > 0) {
|
|
258
|
+
return false;
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
// Safe to cleanup
|
|
262
|
+
return true;
|
|
263
|
+
}
|
|
264
|
+
|
|
180
265
|
/**
|
|
181
266
|
* Add/remove an owner to/from the Texture based on its renderability.
|
|
182
267
|
*
|
|
@@ -191,33 +276,43 @@ export abstract class Texture extends EventEmitter {
|
|
|
191
276
|
* @param owner
|
|
192
277
|
* @param renderable
|
|
193
278
|
*/
|
|
194
|
-
setRenderableOwner(owner:
|
|
195
|
-
const oldSize = this.renderableOwners.
|
|
279
|
+
setRenderableOwner(owner: string | number, renderable: boolean): void {
|
|
280
|
+
const oldSize = this.renderableOwners.length;
|
|
281
|
+
const hasOwnerIndex = this.renderableOwners.indexOf(owner);
|
|
196
282
|
|
|
197
283
|
if (renderable === true) {
|
|
198
|
-
if (
|
|
284
|
+
if (hasOwnerIndex === -1) {
|
|
199
285
|
// Add the owner to the set
|
|
200
|
-
this.renderableOwners.
|
|
286
|
+
this.renderableOwners.push(owner);
|
|
201
287
|
}
|
|
202
288
|
|
|
203
|
-
const newSize = this.renderableOwners.
|
|
204
|
-
if (
|
|
289
|
+
const newSize = this.renderableOwners.length;
|
|
290
|
+
if (oldSize !== newSize && newSize === 1) {
|
|
205
291
|
(this.renderable as boolean) = true;
|
|
206
292
|
this.onChangeIsRenderable?.(true);
|
|
207
293
|
this.load();
|
|
208
294
|
}
|
|
209
295
|
} else {
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
296
|
+
if (hasOwnerIndex !== -1) {
|
|
297
|
+
this.renderableOwners.splice(hasOwnerIndex, 1);
|
|
298
|
+
}
|
|
299
|
+
|
|
300
|
+
const newSize = this.renderableOwners.length;
|
|
301
|
+
if (oldSize !== newSize && newSize === 0) {
|
|
213
302
|
(this.renderable as boolean) = false;
|
|
214
303
|
this.onChangeIsRenderable?.(false);
|
|
215
|
-
|
|
304
|
+
|
|
305
|
+
// note, not doing a cleanup here, cleanup is managed by the Stage/TextureMemoryManager
|
|
306
|
+
// when it deems appropriate based on memory pressure
|
|
216
307
|
}
|
|
217
308
|
}
|
|
218
309
|
}
|
|
219
310
|
|
|
220
311
|
load(): void {
|
|
312
|
+
if (this.retryCount > this.maxRetryCount) {
|
|
313
|
+
// We've exceeded the max retry count, do not attempt to load again
|
|
314
|
+
return;
|
|
315
|
+
}
|
|
221
316
|
this.txManager.loadTexture(this);
|
|
222
317
|
}
|
|
223
318
|
|
|
@@ -256,6 +351,18 @@ export abstract class Texture extends EventEmitter {
|
|
|
256
351
|
this.ctxTexture?.free();
|
|
257
352
|
}
|
|
258
353
|
|
|
354
|
+
/**
|
|
355
|
+
* Release the texture data and core context texture for this Texture without changing state.
|
|
356
|
+
*
|
|
357
|
+
* @remarks
|
|
358
|
+
* The ctxTexture is created by the renderer and lives on the GPU.
|
|
359
|
+
*/
|
|
360
|
+
release(): void {
|
|
361
|
+
this.ctxTexture?.release();
|
|
362
|
+
this.ctxTexture = undefined;
|
|
363
|
+
this.freeTextureData();
|
|
364
|
+
}
|
|
365
|
+
|
|
259
366
|
/**
|
|
260
367
|
* Destroy the texture.
|
|
261
368
|
*
|
|
@@ -281,7 +388,7 @@ export abstract class Texture extends EventEmitter {
|
|
|
281
388
|
* e.g. ImageData that is downloaded from a URL.
|
|
282
389
|
*/
|
|
283
390
|
freeTextureData(): void {
|
|
284
|
-
this.
|
|
391
|
+
queueMicrotask(this.freeTextureDataTask);
|
|
285
392
|
}
|
|
286
393
|
|
|
287
394
|
public setState(
|
|
@@ -308,6 +415,18 @@ export abstract class Texture extends EventEmitter {
|
|
|
308
415
|
} else if (state === 'failed') {
|
|
309
416
|
this._error = errorOrDimensions as Error;
|
|
310
417
|
payload = this._error;
|
|
418
|
+
|
|
419
|
+
// increment the retry count for the texture
|
|
420
|
+
// this is used to compare against maxRetryCount, if set
|
|
421
|
+
// to determine if we should try loading again
|
|
422
|
+
this.retryCount += 1;
|
|
423
|
+
|
|
424
|
+
queueMicrotask(this.releaseTask);
|
|
425
|
+
} else if (state === 'loading') {
|
|
426
|
+
this._error = null;
|
|
427
|
+
this._dimensions = null;
|
|
428
|
+
} else {
|
|
429
|
+
this._error = null;
|
|
311
430
|
}
|
|
312
431
|
|
|
313
432
|
// emit the new state
|
|
@@ -333,6 +452,26 @@ export abstract class Texture extends EventEmitter {
|
|
|
333
452
|
return this.textureData;
|
|
334
453
|
}
|
|
335
454
|
|
|
455
|
+
/**
|
|
456
|
+
* Task for queueMicrotask to free texture data.
|
|
457
|
+
*
|
|
458
|
+
* @remarks
|
|
459
|
+
* This method is called in a microtask to free the texture data.
|
|
460
|
+
*/
|
|
461
|
+
private freeTextureDataTask = (): void => {
|
|
462
|
+
this.textureData = null;
|
|
463
|
+
};
|
|
464
|
+
|
|
465
|
+
/**
|
|
466
|
+
* Task for queueMicrotask to release the texture.
|
|
467
|
+
*
|
|
468
|
+
* @remarks
|
|
469
|
+
* This method is called in a microtask to release the texture.
|
|
470
|
+
*/
|
|
471
|
+
private releaseTask = (): void => {
|
|
472
|
+
this.release();
|
|
473
|
+
};
|
|
474
|
+
|
|
336
475
|
/**
|
|
337
476
|
* Get the texture source for this texture.
|
|
338
477
|
*
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -425,6 +425,20 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
425
425
|
* @defaultValue `null`
|
|
426
426
|
*/
|
|
427
427
|
platform: typeof Platform | null;
|
|
428
|
+
|
|
429
|
+
/**
|
|
430
|
+
* Number of times to retry loading a failed texture
|
|
431
|
+
*
|
|
432
|
+
* @remarks
|
|
433
|
+
* When a texture fails to load, Lightning will retry up to this many times
|
|
434
|
+
* before permanently giving up. Each retry will clear the texture ownership
|
|
435
|
+
* and then re-establish it to trigger a new load attempt.
|
|
436
|
+
*
|
|
437
|
+
* Set to null to disable retries. Set to 0 to always try once and never retry.
|
|
438
|
+
* This is typically only used on ImageTexture instances.
|
|
439
|
+
*
|
|
440
|
+
*/
|
|
441
|
+
maxRetryCount?: number;
|
|
428
442
|
};
|
|
429
443
|
|
|
430
444
|
/**
|
|
@@ -493,7 +507,7 @@ export class RendererMain extends EventEmitter {
|
|
|
493
507
|
*/
|
|
494
508
|
constructor(
|
|
495
509
|
settings: Partial<RendererMainSettings>,
|
|
496
|
-
target
|
|
510
|
+
target?: string | HTMLElement,
|
|
497
511
|
) {
|
|
498
512
|
super();
|
|
499
513
|
|
|
@@ -508,7 +522,7 @@ export class RendererMain extends EventEmitter {
|
|
|
508
522
|
boundsMargin: settings.boundsMargin || 0,
|
|
509
523
|
deviceLogicalPixelRatio: settings.deviceLogicalPixelRatio || 1,
|
|
510
524
|
devicePhysicalPixelRatio:
|
|
511
|
-
settings.devicePhysicalPixelRatio || window.devicePixelRatio,
|
|
525
|
+
settings.devicePhysicalPixelRatio || window.devicePixelRatio || 1,
|
|
512
526
|
clearColor: settings.clearColor ?? 0x00000000,
|
|
513
527
|
fpsUpdateInterval: settings.fpsUpdateInterval || 0,
|
|
514
528
|
targetFPS: settings.targetFPS || 0,
|
|
@@ -525,6 +539,7 @@ export class RendererMain extends EventEmitter {
|
|
|
525
539
|
canvas: settings.canvas,
|
|
526
540
|
createImageBitmapSupport: settings.createImageBitmapSupport || 'full',
|
|
527
541
|
platform: settings.platform || null,
|
|
542
|
+
maxRetryCount: settings.maxRetryCount ?? 5,
|
|
528
543
|
};
|
|
529
544
|
|
|
530
545
|
const {
|
|
@@ -582,24 +597,31 @@ export class RendererMain extends EventEmitter {
|
|
|
582
597
|
textureProcessingTimeLimit: settings.textureProcessingTimeLimit!,
|
|
583
598
|
createImageBitmapSupport: settings.createImageBitmapSupport!,
|
|
584
599
|
platform,
|
|
600
|
+
maxRetryCount: settings.maxRetryCount ?? 5,
|
|
585
601
|
});
|
|
586
602
|
|
|
587
603
|
// Extract the root node
|
|
588
604
|
this.root = this.stage.root as unknown as INode;
|
|
589
605
|
|
|
590
606
|
// Get the target element and attach the canvas to it
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
607
|
+
if (target) {
|
|
608
|
+
let targetEl: HTMLElement | null;
|
|
609
|
+
if (typeof target === 'string') {
|
|
610
|
+
targetEl = document.getElementById(target);
|
|
611
|
+
} else {
|
|
612
|
+
targetEl = target;
|
|
613
|
+
}
|
|
597
614
|
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
615
|
+
if (!targetEl) {
|
|
616
|
+
throw new Error('Could not find target element');
|
|
617
|
+
}
|
|
601
618
|
|
|
602
|
-
|
|
619
|
+
targetEl.appendChild(canvas);
|
|
620
|
+
} else if (settings.canvas !== canvas) {
|
|
621
|
+
throw new Error(
|
|
622
|
+
'New canvas element could not be appended to undefined target',
|
|
623
|
+
);
|
|
624
|
+
}
|
|
603
625
|
|
|
604
626
|
// Initialize inspector (if enabled)
|
|
605
627
|
if (inspector && isProductionEnvironment === false) {
|
|
@@ -832,8 +854,8 @@ export class RendererMain extends EventEmitter {
|
|
|
832
854
|
* **NOTE3**: This will not cleanup textures that are marked as `preventCleanup`.
|
|
833
855
|
* **NOTE4**: This has nothing to do with the garbage collection of JavaScript.
|
|
834
856
|
*/
|
|
835
|
-
cleanup(
|
|
836
|
-
this.stage.cleanup(
|
|
857
|
+
cleanup() {
|
|
858
|
+
this.stage.cleanup();
|
|
837
859
|
}
|
|
838
860
|
|
|
839
861
|
/**
|