@lightningjs/renderer 2.14.3 → 2.15.0
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 +2 -2
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.d.ts +3 -2
- package/dist/src/core/CoreTextureManager.js +29 -10
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.d.ts +20 -0
- package/dist/src/core/Stage.js +31 -2
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/platform.js +29 -3
- package/dist/src/core/platform.js.map +1 -1
- package/dist/src/core/renderers/CoreContextTexture.d.ts +1 -1
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.d.ts +1 -1
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js +7 -6
- package/dist/src/core/renderers/canvas/CanvasCoreTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js +3 -0
- package/dist/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +5 -6
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +30 -18
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +46 -0
- package/dist/src/main-api/Renderer.js +39 -1
- 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.ts +2 -2
- package/src/core/CoreTextureManager.ts +27 -10
- package/src/core/Stage.ts +36 -2
- package/src/core/platform.ts +40 -3
- package/src/core/renderers/CoreContextTexture.ts +1 -1
- package/src/core/renderers/canvas/CanvasCoreTexture.ts +11 -11
- package/src/core/renderers/webgl/WebGlCoreCtxRenderTexture.ts +5 -0
- package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +52 -40
- package/src/main-api/Renderer.ts +57 -1
|
@@ -389,12 +389,18 @@ export class CoreTextureManager extends EventEmitter {
|
|
|
389
389
|
|
|
390
390
|
// For non-image textures, upload immediately
|
|
391
391
|
if (texture.type !== TextureType.image) {
|
|
392
|
-
this.uploadTexture(texture)
|
|
392
|
+
this.uploadTexture(texture).catch((err) => {
|
|
393
|
+
console.error('Failed to upload non-image texture:', err);
|
|
394
|
+
texture.setState('failed');
|
|
395
|
+
});
|
|
393
396
|
} else {
|
|
394
397
|
// For image textures, queue for throttled upload
|
|
395
398
|
// If it's a priority texture, upload it immediately
|
|
396
399
|
if (priority === true) {
|
|
397
|
-
this.uploadTexture(texture)
|
|
400
|
+
this.uploadTexture(texture).catch((err) => {
|
|
401
|
+
console.error('Failed to upload priority texture:', err);
|
|
402
|
+
texture.setState('failed');
|
|
403
|
+
});
|
|
398
404
|
} else {
|
|
399
405
|
this.enqueueUploadTexture(texture);
|
|
400
406
|
}
|
|
@@ -410,8 +416,9 @@ export class CoreTextureManager extends EventEmitter {
|
|
|
410
416
|
* Upload a texture to the GPU
|
|
411
417
|
*
|
|
412
418
|
* @param texture Texture to upload
|
|
419
|
+
* @returns Promise that resolves when the texture is fully loaded
|
|
413
420
|
*/
|
|
414
|
-
uploadTexture(texture: Texture): void {
|
|
421
|
+
async uploadTexture(texture: Texture): Promise<void> {
|
|
415
422
|
if (
|
|
416
423
|
this.stage.txMemManager.doNotExceedCriticalThreshold === true &&
|
|
417
424
|
this.stage.txMemManager.criticalCleanupRequested === true
|
|
@@ -427,7 +434,7 @@ export class CoreTextureManager extends EventEmitter {
|
|
|
427
434
|
return;
|
|
428
435
|
}
|
|
429
436
|
|
|
430
|
-
coreContext.load();
|
|
437
|
+
await coreContext.load();
|
|
431
438
|
}
|
|
432
439
|
|
|
433
440
|
/**
|
|
@@ -442,7 +449,7 @@ export class CoreTextureManager extends EventEmitter {
|
|
|
442
449
|
*
|
|
443
450
|
* @param maxProcessingTime - The maximum processing time in milliseconds
|
|
444
451
|
*/
|
|
445
|
-
processSome(maxProcessingTime: number): void {
|
|
452
|
+
async processSome(maxProcessingTime: number): Promise<void> {
|
|
446
453
|
if (this.initialized === false) {
|
|
447
454
|
return;
|
|
448
455
|
}
|
|
@@ -456,18 +463,28 @@ export class CoreTextureManager extends EventEmitter {
|
|
|
456
463
|
) {
|
|
457
464
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
458
465
|
const texture = this.priorityQueue.pop()!;
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
466
|
+
try {
|
|
467
|
+
await texture.getTextureData();
|
|
468
|
+
await this.uploadTexture(texture);
|
|
469
|
+
} catch (error) {
|
|
470
|
+
console.error('Failed to process priority texture:', error);
|
|
471
|
+
// Continue with next texture instead of stopping entire queue
|
|
472
|
+
}
|
|
462
473
|
}
|
|
463
474
|
|
|
464
|
-
// Process uploads
|
|
475
|
+
// Process uploads - await each upload to prevent GPU overload
|
|
465
476
|
while (
|
|
466
477
|
this.uploadTextureQueue.length > 0 &&
|
|
467
478
|
getTimeStamp() - startTime < maxProcessingTime
|
|
468
479
|
) {
|
|
469
480
|
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
470
|
-
this.
|
|
481
|
+
const texture = this.uploadTextureQueue.shift()!;
|
|
482
|
+
try {
|
|
483
|
+
await this.uploadTexture(texture);
|
|
484
|
+
} catch (error) {
|
|
485
|
+
console.error('Failed to upload texture:', error);
|
|
486
|
+
// Continue with next texture instead of stopping entire queue
|
|
487
|
+
}
|
|
471
488
|
}
|
|
472
489
|
}
|
|
473
490
|
|
package/src/core/Stage.ts
CHANGED
|
@@ -68,6 +68,7 @@ export interface StageOptions {
|
|
|
68
68
|
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
69
69
|
clearColor: number;
|
|
70
70
|
fpsUpdateInterval: number;
|
|
71
|
+
targetFPS: number;
|
|
71
72
|
enableContextSpy: boolean;
|
|
72
73
|
forceWebGL2: boolean;
|
|
73
74
|
numImageWorkers: number;
|
|
@@ -111,6 +112,16 @@ export class Stage {
|
|
|
111
112
|
public readonly strictBounds: boolean;
|
|
112
113
|
public readonly defaultTexture: Texture | null = null;
|
|
113
114
|
|
|
115
|
+
/**
|
|
116
|
+
* Target frame time in milliseconds (calculated from targetFPS)
|
|
117
|
+
*
|
|
118
|
+
* @remarks
|
|
119
|
+
* This is pre-calculated to avoid recalculating on every frame.
|
|
120
|
+
* - 0 means no throttling (use display refresh rate)
|
|
121
|
+
* - >0 means throttle to this frame time (1000 / targetFPS)
|
|
122
|
+
*/
|
|
123
|
+
public targetFrameTime: number = 0;
|
|
124
|
+
|
|
114
125
|
/**
|
|
115
126
|
* Renderer Event Bus for the Stage to emit events onto
|
|
116
127
|
*
|
|
@@ -156,6 +167,10 @@ export class Stage {
|
|
|
156
167
|
} = options;
|
|
157
168
|
|
|
158
169
|
this.eventBus = options.eventBus;
|
|
170
|
+
|
|
171
|
+
// Calculate target frame time from targetFPS option
|
|
172
|
+
this.targetFrameTime = options.targetFPS > 0 ? 1000 / options.targetFPS : 0;
|
|
173
|
+
|
|
159
174
|
this.txManager = new CoreTextureManager(this, {
|
|
160
175
|
numImageWorkers,
|
|
161
176
|
createImageBitmapSupport,
|
|
@@ -292,6 +307,20 @@ export class Stage {
|
|
|
292
307
|
this.renderRequested = true;
|
|
293
308
|
}
|
|
294
309
|
|
|
310
|
+
/**
|
|
311
|
+
* Update the target frame time based on the current targetFPS setting
|
|
312
|
+
*
|
|
313
|
+
* @remarks
|
|
314
|
+
* This should be called whenever the targetFPS option is changed
|
|
315
|
+
* to ensure targetFrameTime stays in sync.
|
|
316
|
+
* targetFPS of 0 means no throttling (targetFrameTime = 0)
|
|
317
|
+
* targetFPS > 0 means throttle to 1000/targetFPS milliseconds
|
|
318
|
+
*/
|
|
319
|
+
updateTargetFrameTime() {
|
|
320
|
+
this.targetFrameTime =
|
|
321
|
+
this.options.targetFPS > 0 ? 1000 / this.options.targetFPS : 0;
|
|
322
|
+
}
|
|
323
|
+
|
|
295
324
|
updateFrameTime() {
|
|
296
325
|
const newFrameTime = getTimeStamp();
|
|
297
326
|
this.lastFrameTime = this.currentFrameTime;
|
|
@@ -372,8 +401,13 @@ export class Stage {
|
|
|
372
401
|
this.root.update(this.deltaTime, this.root.clippingRect);
|
|
373
402
|
}
|
|
374
403
|
|
|
375
|
-
// Process some textures
|
|
376
|
-
|
|
404
|
+
// Process some textures asynchronously but don't block the frame
|
|
405
|
+
// Use a background task to prevent frame drops
|
|
406
|
+
this.txManager
|
|
407
|
+
.processSome(this.options.textureProcessingTimeLimit)
|
|
408
|
+
.catch((err) => {
|
|
409
|
+
console.error('Error processing textures:', err);
|
|
410
|
+
});
|
|
377
411
|
|
|
378
412
|
// Reset render operations and clear the canvas
|
|
379
413
|
renderer.reset();
|
package/src/core/platform.ts
CHANGED
|
@@ -24,14 +24,38 @@ import type { Stage } from './Stage.js';
|
|
|
24
24
|
*/
|
|
25
25
|
export const startLoop = (stage: Stage) => {
|
|
26
26
|
let isIdle = false;
|
|
27
|
-
|
|
27
|
+
let lastFrameTime = 0;
|
|
28
|
+
|
|
29
|
+
const runLoop = (currentTime: number = 0) => {
|
|
30
|
+
const targetFrameTime = stage.targetFrameTime;
|
|
31
|
+
|
|
32
|
+
// Check if we should throttle this frame
|
|
33
|
+
if (targetFrameTime > 0 && currentTime - lastFrameTime < targetFrameTime) {
|
|
34
|
+
// Too early for next frame, schedule with setTimeout for precise timing
|
|
35
|
+
const delay = targetFrameTime - (currentTime - lastFrameTime);
|
|
36
|
+
setTimeout(() => requestAnimationFrame(runLoop), delay);
|
|
37
|
+
return;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
lastFrameTime = currentTime;
|
|
41
|
+
|
|
28
42
|
stage.updateFrameTime();
|
|
29
43
|
stage.updateAnimations();
|
|
30
44
|
|
|
31
45
|
if (!stage.hasSceneUpdates()) {
|
|
32
46
|
// We still need to calculate the fps else it looks like the app is frozen
|
|
33
47
|
stage.calculateFps();
|
|
34
|
-
|
|
48
|
+
|
|
49
|
+
if (targetFrameTime > 0) {
|
|
50
|
+
// Use setTimeout for throttled idle frames
|
|
51
|
+
setTimeout(
|
|
52
|
+
() => requestAnimationFrame(runLoop),
|
|
53
|
+
Math.max(targetFrameTime, 16.666666666666668),
|
|
54
|
+
);
|
|
55
|
+
} else {
|
|
56
|
+
// Use standard idle timeout when not throttling
|
|
57
|
+
setTimeout(() => requestAnimationFrame(runLoop), 16.666666666666668);
|
|
58
|
+
}
|
|
35
59
|
|
|
36
60
|
if (!isIdle) {
|
|
37
61
|
stage.eventBus.emit('idle');
|
|
@@ -49,8 +73,21 @@ export const startLoop = (stage: Stage) => {
|
|
|
49
73
|
isIdle = false;
|
|
50
74
|
stage.drawFrame();
|
|
51
75
|
stage.flushFrameEvents();
|
|
52
|
-
|
|
76
|
+
|
|
77
|
+
// Schedule next frame
|
|
78
|
+
if (targetFrameTime > 0) {
|
|
79
|
+
// Use setTimeout + rAF combination for precise FPS control
|
|
80
|
+
const nextFrameDelay = Math.max(
|
|
81
|
+
0,
|
|
82
|
+
targetFrameTime - (performance.now() - currentTime),
|
|
83
|
+
);
|
|
84
|
+
setTimeout(() => requestAnimationFrame(runLoop), nextFrameDelay);
|
|
85
|
+
} else {
|
|
86
|
+
// Use standard rAF when not throttling
|
|
87
|
+
requestAnimationFrame(runLoop);
|
|
88
|
+
}
|
|
53
89
|
};
|
|
90
|
+
|
|
54
91
|
requestAnimationFrame(runLoop);
|
|
55
92
|
};
|
|
56
93
|
|
|
@@ -35,19 +35,19 @@ export class CanvasCoreTexture extends CoreContextTexture {
|
|
|
35
35
|
}
|
|
36
36
|
| undefined;
|
|
37
37
|
|
|
38
|
-
load(): void {
|
|
38
|
+
async load(): Promise<void> {
|
|
39
39
|
this.textureSource.setState('loading');
|
|
40
40
|
|
|
41
|
-
|
|
42
|
-
.
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
.
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
41
|
+
try {
|
|
42
|
+
const size = await this.onLoadRequest();
|
|
43
|
+
this.textureSource.setState('loaded', size);
|
|
44
|
+
this.textureSource.freeTextureData();
|
|
45
|
+
this.updateMemSize();
|
|
46
|
+
} catch (err) {
|
|
47
|
+
this.textureSource.setState('failed', err as Error);
|
|
48
|
+
this.textureSource.freeTextureData();
|
|
49
|
+
throw err;
|
|
50
|
+
}
|
|
51
51
|
}
|
|
52
52
|
|
|
53
53
|
free(): void {
|
|
@@ -41,6 +41,11 @@ export class WebGlCoreCtxRenderTexture extends WebGlCoreCtxTexture {
|
|
|
41
41
|
const { glw } = this;
|
|
42
42
|
const nativeTexture = (this._nativeCtxTexture =
|
|
43
43
|
this.createNativeCtxTexture());
|
|
44
|
+
|
|
45
|
+
if (!nativeTexture) {
|
|
46
|
+
throw new Error('Failed to create native texture for RenderTexture');
|
|
47
|
+
}
|
|
48
|
+
|
|
44
49
|
const { width, height } = this.textureSource;
|
|
45
50
|
|
|
46
51
|
// Create Framebuffer object
|
|
@@ -78,54 +78,65 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
78
78
|
* to force the texture to be pre-loaded prior to accessing the ctxTexture
|
|
79
79
|
* property.
|
|
80
80
|
*/
|
|
81
|
-
load() {
|
|
82
|
-
// If the texture is already loading or loaded,
|
|
81
|
+
async load(): Promise<void> {
|
|
82
|
+
// If the texture is already loading or loaded, return resolved promise
|
|
83
83
|
if (this.state === 'loading' || this.state === 'loaded') {
|
|
84
|
-
return;
|
|
84
|
+
return Promise.resolve();
|
|
85
85
|
}
|
|
86
86
|
|
|
87
87
|
this.state = 'loading';
|
|
88
88
|
this.textureSource.setState('loading');
|
|
89
|
+
|
|
90
|
+
// Await the native texture creation to ensure GPU buffer is fully allocated
|
|
89
91
|
this._nativeCtxTexture = this.createNativeCtxTexture();
|
|
90
92
|
|
|
91
93
|
if (this._nativeCtxTexture === null) {
|
|
92
94
|
this.state = 'failed';
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
new Error('Could not create WebGL Texture'),
|
|
96
|
-
);
|
|
95
|
+
const error = new Error('Could not create WebGL Texture');
|
|
96
|
+
this.textureSource.setState('failed', error);
|
|
97
97
|
console.error('Could not create WebGL Texture');
|
|
98
|
-
|
|
98
|
+
throw error;
|
|
99
99
|
}
|
|
100
100
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.state = 'failed';
|
|
125
|
-
this.textureSource.setState('failed', err);
|
|
101
|
+
try {
|
|
102
|
+
const { width, height } = await this.onLoadRequest();
|
|
103
|
+
|
|
104
|
+
// If the texture has been freed while loading, return early.
|
|
105
|
+
// Type assertion needed because state could change during async operations
|
|
106
|
+
if ((this.state as string) === 'freed') {
|
|
107
|
+
return;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
this.state = 'loaded';
|
|
111
|
+
this._w = width;
|
|
112
|
+
this._h = height;
|
|
113
|
+
// Update the texture source's width and height so that it can be used
|
|
114
|
+
// for rendering.
|
|
115
|
+
this.textureSource.setState('loaded', { width, height });
|
|
116
|
+
|
|
117
|
+
// cleanup source texture data next tick
|
|
118
|
+
// This is done using queueMicrotask to ensure it runs after the current
|
|
119
|
+
// event loop tick, allowing the texture to be fully loaded and bound
|
|
120
|
+
// to the GL context before freeing the source data.
|
|
121
|
+
// This is important to avoid issues with the texture data being
|
|
122
|
+
// freed while the texture is still being loaded or used.
|
|
123
|
+
queueMicrotask(() => {
|
|
126
124
|
this.textureSource.freeTextureData();
|
|
127
|
-
console.error(err);
|
|
128
125
|
});
|
|
126
|
+
} catch (err: unknown) {
|
|
127
|
+
// If the texture has been freed while loading, return early.
|
|
128
|
+
// Type assertion needed because state could change during async operations
|
|
129
|
+
if ((this.state as string) === 'freed') {
|
|
130
|
+
return;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
this.state = 'failed';
|
|
134
|
+
const error = err instanceof Error ? err : new Error(String(err));
|
|
135
|
+
this.textureSource.setState('failed', error);
|
|
136
|
+
this.textureSource.freeTextureData();
|
|
137
|
+
console.error(err);
|
|
138
|
+
throw error; // Re-throw to propagate the error
|
|
139
|
+
}
|
|
129
140
|
}
|
|
130
141
|
|
|
131
142
|
/**
|
|
@@ -268,17 +279,17 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
268
279
|
}
|
|
269
280
|
|
|
270
281
|
/**
|
|
271
|
-
* Create native context texture
|
|
282
|
+
* Create native context texture asynchronously
|
|
272
283
|
*
|
|
273
284
|
* @remarks
|
|
274
|
-
* When this method
|
|
285
|
+
* When this method resolves, the returned texture will be bound to the GL context state
|
|
286
|
+
* and fully ready for use. This ensures proper GPU resource allocation timing.
|
|
275
287
|
*
|
|
276
|
-
* @
|
|
277
|
-
* @param height
|
|
278
|
-
* @returns
|
|
288
|
+
* @returns Promise that resolves to the native WebGL texture or null on failure
|
|
279
289
|
*/
|
|
280
|
-
protected createNativeCtxTexture() {
|
|
290
|
+
protected createNativeCtxTexture(): WebGLTexture | null {
|
|
281
291
|
const { glw } = this;
|
|
292
|
+
|
|
282
293
|
const nativeTexture = glw.createTexture();
|
|
283
294
|
if (!nativeTexture) {
|
|
284
295
|
return null;
|
|
@@ -296,6 +307,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
296
307
|
// texture wrapping method
|
|
297
308
|
glw.texParameteri(glw.TEXTURE_WRAP_S, glw.CLAMP_TO_EDGE);
|
|
298
309
|
glw.texParameteri(glw.TEXTURE_WRAP_T, glw.CLAMP_TO_EDGE);
|
|
310
|
+
|
|
299
311
|
return nativeTexture;
|
|
300
312
|
}
|
|
301
313
|
}
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -157,6 +157,22 @@ export interface RendererMainSettings {
|
|
|
157
157
|
*/
|
|
158
158
|
fpsUpdateInterval?: number;
|
|
159
159
|
|
|
160
|
+
/**
|
|
161
|
+
* Target FPS for the global render loop
|
|
162
|
+
*
|
|
163
|
+
* @remarks
|
|
164
|
+
* Controls the maximum frame rate of the entire rendering system.
|
|
165
|
+
* When set to 0, no throttling is applied (use display refresh rate).
|
|
166
|
+
* When set to a positive number, the global requestAnimationFrame loop
|
|
167
|
+
* will be throttled to this target FPS, affecting all animations and rendering.
|
|
168
|
+
*
|
|
169
|
+
* This provides global performance control for the entire application,
|
|
170
|
+
* useful for managing performance on lower-end devices.
|
|
171
|
+
*
|
|
172
|
+
* @defaultValue `0` (no throttling, use display refresh rate)
|
|
173
|
+
*/
|
|
174
|
+
targetFPS?: number;
|
|
175
|
+
|
|
160
176
|
/**
|
|
161
177
|
* Include context call (i.e. WebGL) information in FPS updates
|
|
162
178
|
*
|
|
@@ -403,6 +419,7 @@ export class RendererMain extends EventEmitter {
|
|
|
403
419
|
settings.devicePhysicalPixelRatio || window.devicePixelRatio,
|
|
404
420
|
clearColor: settings.clearColor ?? 0x00000000,
|
|
405
421
|
fpsUpdateInterval: settings.fpsUpdateInterval || 0,
|
|
422
|
+
targetFPS: settings.targetFPS || 0,
|
|
406
423
|
numImageWorkers:
|
|
407
424
|
settings.numImageWorkers !== undefined ? settings.numImageWorkers : 2,
|
|
408
425
|
enableContextSpy: settings.enableContextSpy ?? false,
|
|
@@ -412,7 +429,7 @@ export class RendererMain extends EventEmitter {
|
|
|
412
429
|
quadBufferSize: settings.quadBufferSize ?? 4 * 1024 * 1024,
|
|
413
430
|
fontEngines: settings.fontEngines,
|
|
414
431
|
strictBounds: settings.strictBounds ?? true,
|
|
415
|
-
textureProcessingTimeLimit: settings.textureProcessingTimeLimit ||
|
|
432
|
+
textureProcessingTimeLimit: settings.textureProcessingTimeLimit || 42,
|
|
416
433
|
canvas: settings.canvas || document.createElement('canvas'),
|
|
417
434
|
createImageBitmapSupport: settings.createImageBitmapSupport || 'full',
|
|
418
435
|
};
|
|
@@ -449,6 +466,7 @@ export class RendererMain extends EventEmitter {
|
|
|
449
466
|
enableContextSpy: this.settings.enableContextSpy,
|
|
450
467
|
forceWebGL2: this.settings.forceWebGL2,
|
|
451
468
|
fpsUpdateInterval: this.settings.fpsUpdateInterval,
|
|
469
|
+
targetFPS: this.settings.targetFPS,
|
|
452
470
|
numImageWorkers: this.settings.numImageWorkers,
|
|
453
471
|
renderEngine: this.settings.renderEngine,
|
|
454
472
|
textureMemory: resolvedTxSettings,
|
|
@@ -748,4 +766,42 @@ export class RendererMain extends EventEmitter {
|
|
|
748
766
|
setClearColor(color: number) {
|
|
749
767
|
this.stage.setClearColor(color);
|
|
750
768
|
}
|
|
769
|
+
|
|
770
|
+
/**
|
|
771
|
+
* Gets the target FPS for the global render loop
|
|
772
|
+
*
|
|
773
|
+
* @returns The current target FPS (0 means no throttling)
|
|
774
|
+
*
|
|
775
|
+
* @remarks
|
|
776
|
+
* This controls the maximum frame rate of the entire rendering system.
|
|
777
|
+
* When 0, the system runs at display refresh rate.
|
|
778
|
+
*/
|
|
779
|
+
get targetFPS(): number {
|
|
780
|
+
return this.stage.options.targetFPS;
|
|
781
|
+
}
|
|
782
|
+
|
|
783
|
+
/**
|
|
784
|
+
* Sets the target FPS for the global render loop
|
|
785
|
+
*
|
|
786
|
+
* @param fps - The target FPS to set for the global render loop.
|
|
787
|
+
* Set to 0 or a negative value to disable throttling.
|
|
788
|
+
*
|
|
789
|
+
* @remarks
|
|
790
|
+
* This setting affects the entire rendering system immediately.
|
|
791
|
+
* All animations, rendering, and frame updates will be throttled
|
|
792
|
+
* to this target FPS. Provides global performance control.
|
|
793
|
+
*
|
|
794
|
+
* @example
|
|
795
|
+
* ```typescript
|
|
796
|
+
* // Set global target to 30fps for better performance
|
|
797
|
+
* renderer.targetFPS = 30;
|
|
798
|
+
*
|
|
799
|
+
* // Disable global throttling (use display refresh rate)
|
|
800
|
+
* renderer.targetFPS = 0;
|
|
801
|
+
* ```
|
|
802
|
+
*/
|
|
803
|
+
set targetFPS(fps: number) {
|
|
804
|
+
this.stage.options.targetFPS = fps > 0 ? fps : 0;
|
|
805
|
+
this.stage.updateTargetFrameTime();
|
|
806
|
+
}
|
|
751
807
|
}
|