@solidtv/renderer 1.2.10 → 1.3.1
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/CoreTextNode.js +3 -1
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.d.ts +10 -0
- package/dist/src/core/CoreTextureManager.js +38 -5
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.js +3 -1
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.d.ts +16 -0
- package/dist/src/core/TextureMemoryManager.js +26 -2
- package/dist/src/core/TextureMemoryManager.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.js +22 -6
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/textureSvg.js +4 -1
- package/dist/src/core/lib/textureSvg.js.map +1 -1
- package/dist/src/core/lib/validateImageBitmap.d.ts +18 -0
- package/dist/src/core/lib/validateImageBitmap.js +66 -0
- package/dist/src/core/lib/validateImageBitmap.js.map +1 -1
- package/dist/src/core/platforms/web/WebPlatform.js +8 -0
- package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
- package/dist/src/core/renderers/CoreRenderer.d.ts +11 -0
- package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/canvas/CanvasRenderer.d.ts +1 -0
- package/dist/src/core/renderers/canvas/CanvasRenderer.js +4 -0
- package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js +5 -1
- package/dist/src/core/renderers/webgl/WebGlCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +13 -0
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +33 -0
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js +5 -1
- package/dist/src/core/text-rendering/SdfFontHandler.js.map +1 -1
- package/dist/src/core/textures/ImageTexture.js +21 -7
- package/dist/src/core/textures/ImageTexture.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +110 -7
- package/dist/src/main-api/Renderer.js +14 -5
- 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/CoreTextNode.test.ts +145 -0
- package/src/core/CoreTextNode.ts +3 -1
- package/src/core/CoreTextureManager.ts +61 -8
- package/src/core/Stage.ts +3 -0
- package/src/core/TextureMemoryManager.test.ts +98 -0
- package/src/core/TextureMemoryManager.ts +28 -2
- package/src/core/lib/ImageWorker.ts +30 -6
- package/src/core/lib/textureSvg.ts +4 -1
- package/src/core/lib/validateImageBitmap.test.ts +119 -0
- package/src/core/lib/validateImageBitmap.ts +89 -0
- package/src/core/platforms/web/WebPlatform.outOfMemory.test.ts +78 -0
- package/src/core/platforms/web/WebPlatform.ts +8 -0
- package/src/core/renderers/CoreRenderer.ts +12 -0
- package/src/core/renderers/canvas/CanvasRenderer.ts +5 -0
- package/src/core/renderers/webgl/WebGlCtxTexture.ts +5 -1
- package/src/core/renderers/webgl/WebGlRenderer.ts +36 -0
- package/src/core/text-rendering/SdfFontHandler.ts +6 -1
- package/src/core/text-rendering/tests/SdfFontHandler.test.ts +112 -0
- package/src/core/textures/ImageTexture.ts +25 -7
- package/src/main-api/Renderer.ts +125 -11
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
|
2
|
+
import { loadFont } from '../SdfFontHandler.js';
|
|
3
|
+
import { EventEmitter } from '../../../common/EventEmitter.js';
|
|
4
|
+
import { TextureError, TextureErrorCode } from '../../TextureError.js';
|
|
5
|
+
import type { Stage } from '../../Stage.js';
|
|
6
|
+
|
|
7
|
+
// Minimal XHR stand-in: synchronously delivers valid SDF font JSON so loadFont
|
|
8
|
+
// proceeds to create the atlas texture and attach its event listeners.
|
|
9
|
+
class FakeXHR {
|
|
10
|
+
status = 200;
|
|
11
|
+
response: unknown = null;
|
|
12
|
+
responseType = '';
|
|
13
|
+
onload: (() => void) | null = null;
|
|
14
|
+
onerror: (() => void) | null = null;
|
|
15
|
+
open(): void {}
|
|
16
|
+
send(): void {
|
|
17
|
+
this.response = { chars: [{}] };
|
|
18
|
+
if (this.onload !== null) {
|
|
19
|
+
this.onload();
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// A texture is just an EventEmitter to loadFont; stub the few props it reads.
|
|
25
|
+
function makeFakeTexture() {
|
|
26
|
+
const tex = new EventEmitter() as unknown as EventEmitter & {
|
|
27
|
+
state: string;
|
|
28
|
+
preventCleanup: boolean;
|
|
29
|
+
setRenderableOwner: (owner: string, val: boolean) => void;
|
|
30
|
+
};
|
|
31
|
+
tex.state = 'loading'; // not 'loaded' -> goes through the listener path
|
|
32
|
+
tex.preventCleanup = false;
|
|
33
|
+
tex.setRenderableOwner = () => {};
|
|
34
|
+
return tex;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
// Drain microtasks + one macrotask so the async loader reaches listener setup.
|
|
38
|
+
const flush = (): Promise<void> => new Promise((r) => setTimeout(r, 0));
|
|
39
|
+
|
|
40
|
+
describe('SdfFontHandler loadFont — failed event argument', () => {
|
|
41
|
+
let errSpy: ReturnType<typeof vi.spyOn>;
|
|
42
|
+
let originalXHR: unknown;
|
|
43
|
+
|
|
44
|
+
beforeEach(() => {
|
|
45
|
+
errSpy = vi.spyOn(console, 'error').mockImplementation(() => {});
|
|
46
|
+
originalXHR = (globalThis as unknown as { XMLHttpRequest: unknown })
|
|
47
|
+
.XMLHttpRequest;
|
|
48
|
+
(globalThis as unknown as { XMLHttpRequest: unknown }).XMLHttpRequest =
|
|
49
|
+
FakeXHR;
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
afterEach(() => {
|
|
53
|
+
errSpy.mockRestore();
|
|
54
|
+
(globalThis as unknown as { XMLHttpRequest: unknown }).XMLHttpRequest =
|
|
55
|
+
originalXHR;
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('rejects with the TextureError (second emit arg), not the emitting texture', async () => {
|
|
59
|
+
const tex = makeFakeTexture();
|
|
60
|
+
const stage = {
|
|
61
|
+
txManager: { createTexture: () => tex },
|
|
62
|
+
} as unknown as Stage;
|
|
63
|
+
|
|
64
|
+
const promise = loadFont(stage, {
|
|
65
|
+
fontFamily: 'TestSdfFailFont',
|
|
66
|
+
atlasUrl: 'atlas.png',
|
|
67
|
+
atlasDataUrl: 'atlas.json',
|
|
68
|
+
} as Parameters<typeof loadFont>[1]);
|
|
69
|
+
|
|
70
|
+
await flush();
|
|
71
|
+
|
|
72
|
+
const error = new TextureError(
|
|
73
|
+
TextureErrorCode.TEXTURE_UPLOAD_FAILED,
|
|
74
|
+
'boom',
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
// Attach the rejection assertion before emitting so the handler is ready.
|
|
78
|
+
const assertion = expect(promise).rejects.toBe(error);
|
|
79
|
+
|
|
80
|
+
// EventEmitter calls listeners as (target, data) -> (tex, error).
|
|
81
|
+
tex.emit('failed', error);
|
|
82
|
+
|
|
83
|
+
await assertion;
|
|
84
|
+
});
|
|
85
|
+
|
|
86
|
+
it('logs the error, not the texture, on failure', async () => {
|
|
87
|
+
const tex = makeFakeTexture();
|
|
88
|
+
const stage = {
|
|
89
|
+
txManager: { createTexture: () => tex },
|
|
90
|
+
} as unknown as Stage;
|
|
91
|
+
|
|
92
|
+
const promise = loadFont(stage, {
|
|
93
|
+
fontFamily: 'TestSdfFailFontLog',
|
|
94
|
+
atlasUrl: 'atlas.png',
|
|
95
|
+
atlasDataUrl: 'atlas.json',
|
|
96
|
+
} as Parameters<typeof loadFont>[1]);
|
|
97
|
+
|
|
98
|
+
await flush();
|
|
99
|
+
|
|
100
|
+
const error = new TextureError(
|
|
101
|
+
TextureErrorCode.TEXTURE_UPLOAD_FAILED,
|
|
102
|
+
'boom',
|
|
103
|
+
);
|
|
104
|
+
const rejected = promise.catch(() => {});
|
|
105
|
+
tex.emit('failed', error);
|
|
106
|
+
await rejected;
|
|
107
|
+
|
|
108
|
+
const lastCall = errSpy.mock.calls[errSpy.mock.calls.length - 1]!;
|
|
109
|
+
expect(lastCall[1]).toBe(error);
|
|
110
|
+
expect(lastCall[1]).not.toBe(tex);
|
|
111
|
+
});
|
|
112
|
+
});
|
|
@@ -192,6 +192,18 @@ export class ImageTexture extends Texture {
|
|
|
192
192
|
const hasAlphaChannel = premultiplyAlpha ?? blob.type.includes('image/png');
|
|
193
193
|
const imageBitmapSupported = this.txManager.imageBitmapSupported;
|
|
194
194
|
|
|
195
|
+
// When the device does NOT honor the createImageBitmap premultiply option
|
|
196
|
+
// (e.g. older Safari), request a straight ('none') bitmap and let WebGL
|
|
197
|
+
// premultiply on upload instead. `premultiplyAlpha` in the returned
|
|
198
|
+
// TextureData means "WebGL should premultiply this source on upload".
|
|
199
|
+
const useGlPremultiply =
|
|
200
|
+
hasAlphaChannel === true &&
|
|
201
|
+
imageBitmapSupported.premultiplyHonored === false;
|
|
202
|
+
const bitmapMode: 'premultiply' | 'none' =
|
|
203
|
+
hasAlphaChannel === true && useGlPremultiply === false
|
|
204
|
+
? 'premultiply'
|
|
205
|
+
: 'none';
|
|
206
|
+
|
|
195
207
|
if (imageBitmapSupported.full === true && sw !== null && sh !== null) {
|
|
196
208
|
// createImageBitmap with crop
|
|
197
209
|
const bitmap = await this.platform.createImageBitmap(
|
|
@@ -201,28 +213,29 @@ export class ImageTexture extends Texture {
|
|
|
201
213
|
sw,
|
|
202
214
|
sh,
|
|
203
215
|
{
|
|
204
|
-
premultiplyAlpha:
|
|
216
|
+
premultiplyAlpha: bitmapMode,
|
|
205
217
|
colorSpaceConversion: 'none',
|
|
206
218
|
imageOrientation: 'none',
|
|
207
219
|
},
|
|
208
220
|
);
|
|
209
|
-
return { data: bitmap, premultiplyAlpha:
|
|
221
|
+
return { data: bitmap, premultiplyAlpha: useGlPremultiply };
|
|
210
222
|
} else if (imageBitmapSupported.basic === true) {
|
|
211
223
|
// basic createImageBitmap without options or crop
|
|
212
|
-
// this is supported for Chrome v50 to v52/54 that doesn't support options
|
|
224
|
+
// this is supported for Chrome v50 to v52/54 that doesn't support options.
|
|
225
|
+
// The browser default premultiplies, so WebGL must not premultiply again.
|
|
213
226
|
return {
|
|
214
227
|
data: await this.platform.createImageBitmap(blob),
|
|
215
|
-
premultiplyAlpha:
|
|
228
|
+
premultiplyAlpha: false,
|
|
216
229
|
};
|
|
217
230
|
}
|
|
218
231
|
|
|
219
232
|
// default createImageBitmap without crop but with options
|
|
220
233
|
const bitmap = await this.platform.createImageBitmap(blob, {
|
|
221
|
-
premultiplyAlpha:
|
|
234
|
+
premultiplyAlpha: bitmapMode,
|
|
222
235
|
colorSpaceConversion: 'none',
|
|
223
236
|
imageOrientation: 'none',
|
|
224
237
|
});
|
|
225
|
-
return { data: bitmap, premultiplyAlpha:
|
|
238
|
+
return { data: bitmap, premultiplyAlpha: useGlPremultiply };
|
|
226
239
|
}
|
|
227
240
|
|
|
228
241
|
async loadImage(src: string) {
|
|
@@ -274,9 +287,14 @@ export class ImageTexture extends Texture {
|
|
|
274
287
|
};
|
|
275
288
|
}
|
|
276
289
|
|
|
290
|
+
// The loader computes whether WebGL should premultiply this source on
|
|
291
|
+
// upload (it depends on the source type and on whether createImageBitmap
|
|
292
|
+
// honored the premultiply option). Preserve it; fall back to the prop only
|
|
293
|
+
// when the loader didn't decide.
|
|
277
294
|
return {
|
|
278
295
|
data: resp.data,
|
|
279
|
-
premultiplyAlpha:
|
|
296
|
+
premultiplyAlpha:
|
|
297
|
+
resp.premultiplyAlpha ?? this.props.premultiplyAlpha ?? true,
|
|
280
298
|
};
|
|
281
299
|
}
|
|
282
300
|
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -28,7 +28,7 @@ import { Platform } from '../core/platforms/Platform.js';
|
|
|
28
28
|
* @category Events
|
|
29
29
|
* @example
|
|
30
30
|
* ```typescript
|
|
31
|
-
* renderer.on('fpsUpdate', (data) => {
|
|
31
|
+
* renderer.on('fpsUpdate', (_target, data) => {
|
|
32
32
|
* console.log(`Current FPS: ${data.fps}`);
|
|
33
33
|
* if (data.contextSpyData) {
|
|
34
34
|
* console.log('WebGL calls:', data.contextSpyData);
|
|
@@ -49,7 +49,7 @@ export interface RendererMainFpsUpdateEvent {
|
|
|
49
49
|
* @category Events
|
|
50
50
|
* @example
|
|
51
51
|
* ```typescript
|
|
52
|
-
* renderer.on('frameTick', (data) => {
|
|
52
|
+
* renderer.on('frameTick', (_target, data) => {
|
|
53
53
|
* console.log(`Frame time: ${data.time}ms, delta: ${data.delta}ms`);
|
|
54
54
|
* });
|
|
55
55
|
* ```
|
|
@@ -67,7 +67,7 @@ export interface RendererMainFrameTickEvent {
|
|
|
67
67
|
* @category Events
|
|
68
68
|
* @example
|
|
69
69
|
* ```typescript
|
|
70
|
-
* renderer.on('renderUpdate', (data) => {
|
|
70
|
+
* renderer.on('renderUpdate', (_target, data) => {
|
|
71
71
|
* console.log(`Rendered quads: ${data.quads}, renderOps: ${data.renderOps}`);
|
|
72
72
|
* });
|
|
73
73
|
* ```
|
|
@@ -110,7 +110,7 @@ export interface RendererMainIdleEvent {
|
|
|
110
110
|
* @category Events
|
|
111
111
|
* @example
|
|
112
112
|
* ```typescript
|
|
113
|
-
* renderer.on('criticalCleanup', (data) => {
|
|
113
|
+
* renderer.on('criticalCleanup', (_target, data) => {
|
|
114
114
|
* console.log(`Memory cleanup triggered!`);
|
|
115
115
|
* console.log(`Memory used: ${data.memUsed} bytes`);
|
|
116
116
|
* console.log(`Critical threshold: ${data.criticalThreshold} bytes`);
|
|
@@ -130,7 +130,7 @@ export interface RendererMainCriticalCleanupEvent {
|
|
|
130
130
|
* @category Events
|
|
131
131
|
* @example
|
|
132
132
|
* ```typescript
|
|
133
|
-
* renderer.on('criticalCleanupFailed', (data) => {
|
|
133
|
+
* renderer.on('criticalCleanupFailed', (_target, data) => {
|
|
134
134
|
* console.warn(`Memory cleanup failed!`);
|
|
135
135
|
* console.log(`Memory still used: ${data.memUsed} bytes`);
|
|
136
136
|
* console.log(`Critical threshold: ${data.criticalThreshold} bytes`);
|
|
@@ -167,6 +167,90 @@ export interface RendererMainContextLostEvent {
|
|
|
167
167
|
readonly __eventHasNoPayload?: never;
|
|
168
168
|
}
|
|
169
169
|
|
|
170
|
+
/**
|
|
171
|
+
* GPU Out Of Memory Event Data
|
|
172
|
+
*
|
|
173
|
+
* @remarks
|
|
174
|
+
* Fired when the renderer detects a real `GL_OUT_OF_MEMORY` from the GPU (probed
|
|
175
|
+
* once per frame). This is the only certain signal that the texture memory
|
|
176
|
+
* estimate has overshot the device's real VRAM budget. At this point a texture
|
|
177
|
+
* upload has already failed and the driver may soon drop the WebGL context, so
|
|
178
|
+
* the supported recovery is for the application to reload with a lower
|
|
179
|
+
* `criticalThreshold`.
|
|
180
|
+
*
|
|
181
|
+
* `memUsed` is the estimated texture memory in use at the moment of the failure.
|
|
182
|
+
* Because the upload failed, the real GPU budget is at or below this value — so
|
|
183
|
+
* it is a good basis for the next `criticalThreshold`.
|
|
184
|
+
*
|
|
185
|
+
* The renderer deliberately does NOT persist, reload, or change the threshold
|
|
186
|
+
* itself — that is application policy. The recommended integration is to lower
|
|
187
|
+
* the threshold, persist it, and reload:
|
|
188
|
+
*
|
|
189
|
+
* @category Events
|
|
190
|
+
* @example
|
|
191
|
+
* ```typescript
|
|
192
|
+
* // --- on startup: read the calibrated threshold before creating the renderer
|
|
193
|
+
* //
|
|
194
|
+
* // Namespace the storage key per app. On TV devices that run from the
|
|
195
|
+
* // filesystem (file://) the origin is null/opaque, so a bare key can collide
|
|
196
|
+
* // across apps — including the path keeps each app's calibration separate.
|
|
197
|
+
* const STORAGE_KEY = `myapp:criticalThreshold:${location.pathname}`;
|
|
198
|
+
*
|
|
199
|
+
* // Never let calibration drive the threshold so low the UX breaks. Pick a
|
|
200
|
+
* // floor that matches your app (here, 70% of the default budget).
|
|
201
|
+
* const DEFAULT_CRITICAL = 200e6;
|
|
202
|
+
* const MIN_THRESHOLD = Math.round(DEFAULT_CRITICAL * 0.7);
|
|
203
|
+
*
|
|
204
|
+
* function readCriticalThreshold(): number {
|
|
205
|
+
* const raw =
|
|
206
|
+
* typeof localStorage !== 'undefined'
|
|
207
|
+
* ? localStorage.getItem(STORAGE_KEY)
|
|
208
|
+
* : null;
|
|
209
|
+
* const stored = raw !== null ? parseInt(raw, 10) : NaN;
|
|
210
|
+
* if (Number.isNaN(stored) === false && stored > 0) {
|
|
211
|
+
* return Math.max(stored, MIN_THRESHOLD);
|
|
212
|
+
* }
|
|
213
|
+
* return DEFAULT_CRITICAL;
|
|
214
|
+
* }
|
|
215
|
+
*
|
|
216
|
+
* const renderer = new RendererMain(
|
|
217
|
+
* { textureMemory: { criticalThreshold: readCriticalThreshold() } },
|
|
218
|
+
* 'app',
|
|
219
|
+
* );
|
|
220
|
+
*
|
|
221
|
+
* // --- at runtime: react to a real GPU out-of-memory
|
|
222
|
+
* let handlingOOM = false;
|
|
223
|
+
* renderer.on('outOfMemory', (_target, { memUsed, criticalThreshold }) => {
|
|
224
|
+
* if (handlingOOM === true) {
|
|
225
|
+
* return; // debounce — several uploads can fail in the same burst
|
|
226
|
+
* }
|
|
227
|
+
* handlingOOM = true;
|
|
228
|
+
*
|
|
229
|
+
* // The OOM proves the real budget is <= memUsed. Drop to 90% of the lower
|
|
230
|
+
* // of (estimate, current threshold), but never below the floor.
|
|
231
|
+
* const ceiling = Math.min(memUsed, criticalThreshold);
|
|
232
|
+
* const next = Math.max(Math.round(ceiling * 0.9), MIN_THRESHOLD);
|
|
233
|
+
*
|
|
234
|
+
* try {
|
|
235
|
+
* localStorage.setItem(STORAGE_KEY, String(next));
|
|
236
|
+
* } catch (e) {
|
|
237
|
+
* // storage may be blocked (e.g. file:// with storage disabled); the new
|
|
238
|
+
* // value just won't survive the reload.
|
|
239
|
+
* }
|
|
240
|
+
*
|
|
241
|
+
* // Reload so the renderer reinitializes with the lower budget. The engine
|
|
242
|
+
* // does not rebuild GPU resources in place, so reload is the clean recovery.
|
|
243
|
+
* location.reload();
|
|
244
|
+
* });
|
|
245
|
+
* ```
|
|
246
|
+
*/
|
|
247
|
+
export interface RendererMainOutOfMemoryEvent {
|
|
248
|
+
/** Estimated texture memory in use at the time of the failure (bytes) */
|
|
249
|
+
memUsed: number;
|
|
250
|
+
/** Critical threshold in effect at the time of the failure (bytes) */
|
|
251
|
+
criticalThreshold: number;
|
|
252
|
+
}
|
|
253
|
+
|
|
170
254
|
/**
|
|
171
255
|
* Settings for the Renderer that can be updated during runtime.
|
|
172
256
|
*/
|
|
@@ -458,6 +542,25 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
458
542
|
*/
|
|
459
543
|
createImageBitmapSupport: 'auto' | 'basic' | 'options' | 'full';
|
|
460
544
|
|
|
545
|
+
/**
|
|
546
|
+
* Override for whether `createImageBitmap(..., { premultiplyAlpha: 'premultiply' })`
|
|
547
|
+
* is actually honored by the target device.
|
|
548
|
+
*
|
|
549
|
+
* @remarks
|
|
550
|
+
* Some older browsers (notably older Safari/WebKit) accept the
|
|
551
|
+
* `premultiplyAlpha: 'premultiply'` option without throwing but silently
|
|
552
|
+
* ignore it, returning straight (non-premultiplied) alpha. This causes edge
|
|
553
|
+
* "ghosting" on images with transparency.
|
|
554
|
+
*
|
|
555
|
+
* Set to `'auto'` to detect via a cheap startup probe (one 1×1 texture
|
|
556
|
+
* upload + framebuffer readback). Set to a boolean to force the value. Leave
|
|
557
|
+
* unset to assume the option is honored — the default, which preserves
|
|
558
|
+
* existing behavior with no probe overhead.
|
|
559
|
+
*
|
|
560
|
+
* @defaultValue `true` (assume honored; no probe)
|
|
561
|
+
*/
|
|
562
|
+
premultiplyAlphaHonored?: boolean | 'auto';
|
|
563
|
+
|
|
461
564
|
/**
|
|
462
565
|
* Provide an alternative platform abstraction layer
|
|
463
566
|
*
|
|
@@ -513,11 +616,11 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
513
616
|
*
|
|
514
617
|
* Listen to events using the standard EventEmitter API:
|
|
515
618
|
* ```typescript
|
|
516
|
-
* renderer.on('fpsUpdate', (data: RendererMainFpsUpdateEvent) => {
|
|
619
|
+
* renderer.on('fpsUpdate', (_target, data: RendererMainFpsUpdateEvent) => {
|
|
517
620
|
* console.log(`FPS: ${data.fps}`);
|
|
518
621
|
* });
|
|
519
622
|
*
|
|
520
|
-
* renderer.on('idle', (
|
|
623
|
+
* renderer.on('idle', () => {
|
|
521
624
|
* // Renderer is idle - no scene changes
|
|
522
625
|
* });
|
|
523
626
|
* ```
|
|
@@ -529,6 +632,7 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
529
632
|
* @see {@link RendererMainCriticalCleanupEvent}
|
|
530
633
|
* @see {@link RendererMainCriticalCleanupFailedEvent}
|
|
531
634
|
* @see {@link RendererMainContextLostEvent}
|
|
635
|
+
* @see {@link RendererMainOutOfMemoryEvent}
|
|
532
636
|
*
|
|
533
637
|
* @fires RendererMain#fpsUpdate
|
|
534
638
|
* @fires RendererMain#frameTick
|
|
@@ -537,6 +641,7 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
537
641
|
* @fires RendererMain#criticalCleanup
|
|
538
642
|
* @fires RendererMain#criticalCleanupFailed
|
|
539
643
|
* @fires RendererMain#contextLost
|
|
644
|
+
* @fires RendererMain#outOfMemory
|
|
540
645
|
*/
|
|
541
646
|
export class RendererMain extends EventEmitter {
|
|
542
647
|
readonly root: INode;
|
|
@@ -586,6 +691,12 @@ export class RendererMain extends EventEmitter {
|
|
|
586
691
|
textureProcessingTimeLimit: settings.textureProcessingTimeLimit || 10,
|
|
587
692
|
canvas: settings.canvas,
|
|
588
693
|
createImageBitmapSupport: settings.createImageBitmapSupport || 'full',
|
|
694
|
+
// undefined -> true (assume honored, no probe); 'auto' -> probe;
|
|
695
|
+
// explicit boolean -> force the value.
|
|
696
|
+
premultiplyAlphaHonored:
|
|
697
|
+
settings.premultiplyAlphaHonored === undefined
|
|
698
|
+
? true
|
|
699
|
+
: settings.premultiplyAlphaHonored,
|
|
589
700
|
platform: settings.platform || null,
|
|
590
701
|
maxRetryCount: settings.maxRetryCount ?? 5,
|
|
591
702
|
};
|
|
@@ -646,6 +757,7 @@ export class RendererMain extends EventEmitter {
|
|
|
646
757
|
targetFPS: settings.targetFPS!,
|
|
647
758
|
textureProcessingTimeLimit: settings.textureProcessingTimeLimit!,
|
|
648
759
|
createImageBitmapSupport: settings.createImageBitmapSupport!,
|
|
760
|
+
premultiplyAlphaHonored: settings.premultiplyAlphaHonored,
|
|
649
761
|
platform,
|
|
650
762
|
maxRetryCount: settings.maxRetryCount ?? 5,
|
|
651
763
|
});
|
|
@@ -691,11 +803,13 @@ export class RendererMain extends EventEmitter {
|
|
|
691
803
|
const currentTxSettings =
|
|
692
804
|
(this.stage && this.stage.options.textureMemory) || {};
|
|
693
805
|
|
|
806
|
+
const criticalThreshold =
|
|
807
|
+
textureMemory?.criticalThreshold ??
|
|
808
|
+
currentTxSettings?.criticalThreshold ??
|
|
809
|
+
200e6;
|
|
810
|
+
|
|
694
811
|
return {
|
|
695
|
-
criticalThreshold
|
|
696
|
-
textureMemory?.criticalThreshold ??
|
|
697
|
-
currentTxSettings?.criticalThreshold ??
|
|
698
|
-
200e6,
|
|
812
|
+
criticalThreshold,
|
|
699
813
|
targetThresholdLevel:
|
|
700
814
|
textureMemory?.targetThresholdLevel ??
|
|
701
815
|
currentTxSettings?.targetThresholdLevel ??
|