@lightningjs/renderer 0.7.6 → 0.8.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/README.md +4 -0
- package/dist/src/common/CommonTypes.d.ts +6 -0
- package/dist/src/core/CoreNode.d.ts +63 -6
- package/dist/src/core/CoreNode.js +97 -20
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +5 -0
- package/dist/src/core/CoreTextNode.js +15 -10
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.js +2 -0
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.d.ts +4 -0
- package/dist/src/core/Stage.js +8 -1
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.d.ts +12 -0
- package/dist/src/core/TextureMemoryManager.js +42 -0
- package/dist/src/core/TextureMemoryManager.js.map +1 -0
- package/dist/src/core/platform.js +8 -0
- package/dist/src/core/platform.js.map +1 -1
- package/dist/src/core/renderers/CoreContextTexture.d.ts +5 -1
- package/dist/src/core/renderers/CoreContextTexture.js +3 -1
- package/dist/src/core/renderers/CoreContextTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.d.ts +2 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js +2 -2
- package/dist/src/core/renderers/webgl/WebGlCoreCtxSubTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.d.ts +3 -1
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js +22 -5
- package/dist/src/core/renderers/webgl/WebGlCoreCtxTexture.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.d.ts +3 -0
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js +4 -2
- package/dist/src/core/renderers/webgl/WebGlCoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js +17 -30
- package/dist/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.js.map +1 -1
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.d.ts +1 -0
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js +24 -30
- package/dist/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.d.ts +2 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js +18 -0
- package/dist/src/core/text-rendering/renderers/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.d.ts +8 -0
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js +26 -4
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js +1 -3
- package/dist/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.js.map +1 -1
- package/dist/src/core/text-rendering/renderers/TextRenderer.d.ts +19 -0
- package/dist/src/core/text-rendering/renderers/TextRenderer.js +26 -0
- package/dist/src/core/text-rendering/renderers/TextRenderer.js.map +1 -1
- package/dist/src/core/textures/Texture.d.ts +26 -1
- package/dist/src/core/textures/Texture.js +30 -1
- package/dist/src/core/textures/Texture.js.map +1 -1
- package/dist/src/main-api/ICoreDriver.d.ts +1 -0
- package/dist/src/main-api/Inspector.js +2 -1
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/RendererMain.d.ts +10 -1
- package/dist/src/main-api/RendererMain.js +6 -1
- package/dist/src/main-api/RendererMain.js.map +1 -1
- package/dist/src/render-drivers/main/MainCoreDriver.d.ts +1 -0
- package/dist/src/render-drivers/main/MainCoreDriver.js +7 -0
- package/dist/src/render-drivers/main/MainCoreDriver.js.map +1 -1
- package/dist/src/render-drivers/main/MainOnlyNode.d.ts +1 -0
- package/dist/src/render-drivers/main/MainOnlyNode.js +10 -6
- package/dist/src/render-drivers/main/MainOnlyNode.js.map +1 -1
- package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js +1 -0
- package/dist/src/render-drivers/threadx/ThreadXCoreDriver.js.map +1 -1
- package/dist/src/render-drivers/threadx/ThreadXRendererMessage.d.ts +1 -0
- package/dist/src/render-drivers/threadx/ThreadXRendererMessage.js.map +1 -1
- package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js +3 -0
- package/dist/src/render-drivers/threadx/worker/ThreadXRendererNode.js.map +1 -1
- package/dist/src/render-drivers/threadx/worker/renderer.js +1 -0
- package/dist/src/render-drivers/threadx/worker/renderer.js.map +1 -1
- package/dist/src/utils.d.ts +6 -0
- package/dist/src/utils.js +9 -1
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/common/CommonTypes.ts +7 -0
- package/src/core/CoreNode.ts +105 -20
- package/src/core/CoreTextNode.ts +44 -43
- package/src/core/CoreTextureManager.ts +2 -0
- package/src/core/Stage.ts +10 -0
- package/src/core/TextureMemoryManager.ts +66 -0
- package/src/core/platform.ts +8 -0
- package/src/core/renderers/CoreContextTexture.ts +6 -1
- package/src/core/renderers/webgl/WebGlCoreCtxSubTexture.ts +7 -2
- package/src/core/renderers/webgl/WebGlCoreCtxTexture.ts +34 -6
- package/src/core/renderers/webgl/WebGlCoreRenderer.ts +10 -2
- package/src/core/renderers/webgl/shaders/effects/LinearGradientEffect.ts +16 -32
- package/src/core/renderers/webgl/shaders/effects/RadialGradientEffect.ts +26 -32
- package/src/core/text-rendering/renderers/CanvasTextRenderer.ts +23 -0
- package/src/core/text-rendering/renderers/SdfTextRenderer/SdfTextRenderer.ts +32 -4
- package/src/core/text-rendering/renderers/SdfTextRenderer/internal/layoutText.ts +1 -3
- package/src/core/text-rendering/renderers/TextRenderer.ts +32 -0
- package/src/core/textures/Texture.ts +39 -2
- package/src/main-api/ICoreDriver.ts +2 -0
- package/src/main-api/Inspector.ts +2 -1
- package/src/main-api/RendererMain.ts +19 -2
- package/src/render-drivers/main/MainCoreDriver.ts +9 -0
- package/src/render-drivers/main/MainOnlyNode.ts +12 -6
- package/src/render-drivers/threadx/ThreadXCoreDriver.ts +1 -0
- package/src/render-drivers/threadx/ThreadXRendererMessage.ts +1 -0
- package/src/render-drivers/threadx/worker/ThreadXRendererNode.ts +7 -0
- package/src/render-drivers/threadx/worker/renderer.ts +1 -0
- package/src/utils.ts +10 -1
package/src/core/CoreTextNode.ts
CHANGED
|
@@ -55,35 +55,32 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
|
|
|
55
55
|
super(stage, props);
|
|
56
56
|
this._textRendererOverride = props.textRendererOverride;
|
|
57
57
|
const { resolvedTextRenderer, textRendererState } =
|
|
58
|
-
this.resolveTextRendererAndState(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
},
|
|
85
|
-
undefined,
|
|
86
|
-
);
|
|
58
|
+
this.resolveTextRendererAndState({
|
|
59
|
+
x: this.absX,
|
|
60
|
+
y: this.absY,
|
|
61
|
+
width: props.width,
|
|
62
|
+
height: props.height,
|
|
63
|
+
textAlign: props.textAlign,
|
|
64
|
+
color: props.color,
|
|
65
|
+
zIndex: props.zIndex,
|
|
66
|
+
contain: props.contain,
|
|
67
|
+
scrollable: props.scrollable,
|
|
68
|
+
scrollY: props.scrollY,
|
|
69
|
+
offsetY: props.offsetY,
|
|
70
|
+
letterSpacing: props.letterSpacing,
|
|
71
|
+
debug: props.debug,
|
|
72
|
+
fontFamily: props.fontFamily,
|
|
73
|
+
fontSize: props.fontSize,
|
|
74
|
+
fontStretch: props.fontStretch,
|
|
75
|
+
fontStyle: props.fontStyle,
|
|
76
|
+
fontWeight: props.fontWeight,
|
|
77
|
+
text: props.text,
|
|
78
|
+
lineHeight: props.lineHeight,
|
|
79
|
+
maxLines: props.maxLines,
|
|
80
|
+
textBaseline: props.textBaseline,
|
|
81
|
+
verticalAlign: props.verticalAlign,
|
|
82
|
+
overflowSuffix: props.overflowSuffix,
|
|
83
|
+
});
|
|
87
84
|
this.textRenderer = resolvedTextRenderer;
|
|
88
85
|
this.trState = textRendererState;
|
|
89
86
|
}
|
|
@@ -179,8 +176,10 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
|
|
|
179
176
|
set textRendererOverride(value: CoreTextNodeProps['textRendererOverride']) {
|
|
180
177
|
this._textRendererOverride = value;
|
|
181
178
|
|
|
179
|
+
this.textRenderer.destroyState(this.trState);
|
|
180
|
+
|
|
182
181
|
const { resolvedTextRenderer, textRendererState } =
|
|
183
|
-
this.resolveTextRendererAndState(this.trState.props
|
|
182
|
+
this.resolveTextRendererAndState(this.trState.props);
|
|
184
183
|
this.textRenderer = resolvedTextRenderer;
|
|
185
184
|
this.trState = textRendererState;
|
|
186
185
|
}
|
|
@@ -348,6 +347,11 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
|
|
|
348
347
|
return super.checkRenderProps();
|
|
349
348
|
}
|
|
350
349
|
|
|
350
|
+
override onChangeIsRenderable(isRenderable: boolean) {
|
|
351
|
+
super.onChangeIsRenderable(isRenderable);
|
|
352
|
+
this.textRenderer.setIsRenderable(this.trState, isRenderable);
|
|
353
|
+
}
|
|
354
|
+
|
|
351
355
|
override renderQuads(renderer: CoreRenderer) {
|
|
352
356
|
assertTruthy(this.globalTransform);
|
|
353
357
|
this.textRenderer.renderQuads(
|
|
@@ -358,15 +362,21 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
|
|
|
358
362
|
);
|
|
359
363
|
}
|
|
360
364
|
|
|
365
|
+
/**
|
|
366
|
+
* Destroy the node and cleanup all resources
|
|
367
|
+
*/
|
|
368
|
+
override destroy(): void {
|
|
369
|
+
super.destroy();
|
|
370
|
+
|
|
371
|
+
this.textRenderer.destroyState(this.trState);
|
|
372
|
+
}
|
|
373
|
+
|
|
361
374
|
/**
|
|
362
375
|
* Resolve a text renderer and a new state based on the current text renderer props provided
|
|
363
376
|
* @param props
|
|
364
377
|
* @returns
|
|
365
378
|
*/
|
|
366
|
-
private resolveTextRendererAndState(
|
|
367
|
-
props: TrProps,
|
|
368
|
-
prevState?: TextRendererState,
|
|
369
|
-
): {
|
|
379
|
+
private resolveTextRendererAndState(props: TrProps): {
|
|
370
380
|
resolvedTextRenderer: TextRenderer;
|
|
371
381
|
textRendererState: TextRendererState;
|
|
372
382
|
} {
|
|
@@ -377,15 +387,6 @@ export class CoreTextNode extends CoreNode implements ICoreTextNode {
|
|
|
377
387
|
|
|
378
388
|
const textRendererState = resolvedTextRenderer.createState(props);
|
|
379
389
|
|
|
380
|
-
const stateEvents = ['loading', 'loaded', 'failed'];
|
|
381
|
-
|
|
382
|
-
if (prevState) {
|
|
383
|
-
// Remove the old event listeners from previous state obj there was one
|
|
384
|
-
stateEvents.forEach((eventName) => {
|
|
385
|
-
prevState.emitter.off(eventName);
|
|
386
|
-
});
|
|
387
|
-
}
|
|
388
|
-
|
|
389
390
|
textRendererState.emitter.on('loaded', this.onTextLoaded);
|
|
390
391
|
textRendererState.emitter.on('failed', this.onTextFailed);
|
|
391
392
|
|
package/src/core/Stage.ts
CHANGED
|
@@ -37,11 +37,13 @@ import type {
|
|
|
37
37
|
FpsUpdatePayload,
|
|
38
38
|
FrameTickPayload,
|
|
39
39
|
} from '../common/CommonTypes.js';
|
|
40
|
+
import { TextureMemoryManager } from './TextureMemoryManager.js';
|
|
40
41
|
|
|
41
42
|
export interface StageOptions {
|
|
42
43
|
rootId: number;
|
|
43
44
|
appWidth: number;
|
|
44
45
|
appHeight: number;
|
|
46
|
+
txMemByteThreshold: number;
|
|
45
47
|
boundsMargin: number | [number, number, number, number];
|
|
46
48
|
deviceLogicalPixelRatio: number;
|
|
47
49
|
devicePhysicalPixelRatio: number;
|
|
@@ -73,6 +75,7 @@ export class Stage extends EventEmitter {
|
|
|
73
75
|
/// Module Instances
|
|
74
76
|
public readonly animationManager: AnimationManager;
|
|
75
77
|
public readonly txManager: CoreTextureManager;
|
|
78
|
+
public readonly txMemManager: TextureMemoryManager;
|
|
76
79
|
public readonly fontManager: TrFontManager;
|
|
77
80
|
public readonly textRenderers: Partial<TextRendererMap>;
|
|
78
81
|
public readonly shManager: CoreShaderManager;
|
|
@@ -106,9 +109,11 @@ export class Stage extends EventEmitter {
|
|
|
106
109
|
boundsMargin,
|
|
107
110
|
enableContextSpy,
|
|
108
111
|
numImageWorkers,
|
|
112
|
+
txMemByteThreshold,
|
|
109
113
|
} = options;
|
|
110
114
|
|
|
111
115
|
this.txManager = new CoreTextureManager(numImageWorkers);
|
|
116
|
+
this.txMemManager = new TextureMemoryManager(txMemByteThreshold);
|
|
112
117
|
this.shManager = new CoreShaderManager();
|
|
113
118
|
this.animationManager = new AnimationManager();
|
|
114
119
|
this.contextSpy = enableContextSpy ? new ContextSpy() : null;
|
|
@@ -138,6 +143,7 @@ export class Stage extends EventEmitter {
|
|
|
138
143
|
clearColor: clearColor ?? 0xff000000,
|
|
139
144
|
bufferMemory,
|
|
140
145
|
txManager: this.txManager,
|
|
146
|
+
txMemManager: this.txMemManager,
|
|
141
147
|
shManager: this.shManager,
|
|
142
148
|
contextSpy: this.contextSpy,
|
|
143
149
|
});
|
|
@@ -243,11 +249,15 @@ export class Stage extends EventEmitter {
|
|
|
243
249
|
|
|
244
250
|
renderer?.render();
|
|
245
251
|
|
|
252
|
+
this.calculateFps();
|
|
253
|
+
|
|
246
254
|
// Reset renderRequested flag if it was set
|
|
247
255
|
if (renderRequested) {
|
|
248
256
|
this.renderRequested = false;
|
|
249
257
|
}
|
|
258
|
+
}
|
|
250
259
|
|
|
260
|
+
calculateFps() {
|
|
251
261
|
// If there's an FPS update interval, emit the FPS update event
|
|
252
262
|
// when the specified interval has elapsed.
|
|
253
263
|
const { fpsUpdateInterval } = this.options;
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* If not stated otherwise in this file or this component's LICENSE file the
|
|
3
|
+
* following copyright and licenses apply:
|
|
4
|
+
*
|
|
5
|
+
* Copyright 2024 Comcast Cable Communications Management, LLC.
|
|
6
|
+
*
|
|
7
|
+
* Licensed under the Apache License, Version 2.0 (the License);
|
|
8
|
+
* you may not use this file except in compliance with the License.
|
|
9
|
+
* You may obtain a copy of the License at
|
|
10
|
+
*
|
|
11
|
+
* http://www.apache.org/licenses/LICENSE-2.0
|
|
12
|
+
*
|
|
13
|
+
* Unless required by applicable law or agreed to in writing, software
|
|
14
|
+
* distributed under the License is distributed on an "AS IS" BASIS,
|
|
15
|
+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
16
|
+
* See the License for the specific language governing permissions and
|
|
17
|
+
* limitations under the License.
|
|
18
|
+
*/
|
|
19
|
+
import type { CoreContextTexture } from './renderers/CoreContextTexture.js';
|
|
20
|
+
|
|
21
|
+
export class TextureMemoryManager {
|
|
22
|
+
private memUsed = 0;
|
|
23
|
+
private textures: Map<CoreContextTexture, number> = new Map();
|
|
24
|
+
private threshold: number;
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* @param byteThreshold Number of texture bytes to trigger garbage collection
|
|
28
|
+
*/
|
|
29
|
+
constructor(byteThreshold: number) {
|
|
30
|
+
this.threshold = byteThreshold;
|
|
31
|
+
|
|
32
|
+
// If the threshold is 0, we disable the memory manager by replacing the
|
|
33
|
+
// setTextureMemUse method with a no-op function.
|
|
34
|
+
if (byteThreshold === 0) {
|
|
35
|
+
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
|
36
|
+
this.setTextureMemUse = () => {};
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
setTextureMemUse(ctxTexture: CoreContextTexture, byteSize: number) {
|
|
41
|
+
if (this.textures.has(ctxTexture)) {
|
|
42
|
+
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
43
|
+
this.memUsed -= this.textures.get(ctxTexture)!;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
if (byteSize === 0) {
|
|
47
|
+
this.textures.delete(ctxTexture);
|
|
48
|
+
return;
|
|
49
|
+
} else {
|
|
50
|
+
this.memUsed += byteSize;
|
|
51
|
+
this.textures.set(ctxTexture, byteSize);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
if (this.memUsed > this.threshold) {
|
|
55
|
+
this.gc();
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
gc() {
|
|
60
|
+
this.textures.forEach((byteSize, ctxTexture) => {
|
|
61
|
+
if (!ctxTexture.renderable) {
|
|
62
|
+
ctxTexture.free();
|
|
63
|
+
}
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
}
|
package/src/core/platform.ts
CHANGED
|
@@ -23,14 +23,22 @@ import type { Stage } from './Stage.js';
|
|
|
23
23
|
* Platform render loop initiator
|
|
24
24
|
*/
|
|
25
25
|
export const startLoop = (stage: Stage) => {
|
|
26
|
+
let isIdle = false;
|
|
26
27
|
const runLoop = () => {
|
|
27
28
|
stage.updateAnimations();
|
|
28
29
|
|
|
29
30
|
if (!stage.hasSceneUpdates()) {
|
|
31
|
+
// We still need to calculate the fps else it looks like the app is frozen
|
|
32
|
+
stage.calculateFps();
|
|
30
33
|
setTimeout(runLoop, 16.666666666666668);
|
|
34
|
+
if (!isIdle) {
|
|
35
|
+
stage.emit('idle');
|
|
36
|
+
isIdle = true;
|
|
37
|
+
}
|
|
31
38
|
return;
|
|
32
39
|
}
|
|
33
40
|
|
|
41
|
+
isIdle = false;
|
|
34
42
|
stage.drawFrame();
|
|
35
43
|
requestAnimationFrame(runLoop);
|
|
36
44
|
};
|
|
@@ -17,14 +17,19 @@
|
|
|
17
17
|
* limitations under the License.
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
|
+
import type { TextureMemoryManager } from '../TextureMemoryManager.js';
|
|
20
21
|
import type { Texture } from '../textures/Texture.js';
|
|
21
22
|
|
|
22
23
|
export abstract class CoreContextTexture {
|
|
24
|
+
readonly memManager: TextureMemoryManager;
|
|
23
25
|
readonly textureSource: Texture;
|
|
24
26
|
|
|
25
|
-
constructor(textureSource: Texture) {
|
|
27
|
+
constructor(memManager: TextureMemoryManager, textureSource: Texture) {
|
|
28
|
+
this.memManager = memManager;
|
|
26
29
|
this.textureSource = textureSource;
|
|
27
30
|
}
|
|
28
31
|
|
|
29
32
|
abstract load(): void;
|
|
33
|
+
abstract free(): void;
|
|
34
|
+
abstract get renderable(): boolean;
|
|
30
35
|
}
|
|
@@ -18,13 +18,18 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import type { Dimensions } from '../../../common/CommonTypes.js';
|
|
21
|
+
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
|
|
21
22
|
import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
|
|
22
23
|
import type { SubTexture } from '../../textures/SubTexture.js';
|
|
23
24
|
import { WebGlCoreCtxTexture } from './WebGlCoreCtxTexture.js';
|
|
24
25
|
|
|
25
26
|
export class WebGlCoreCtxSubTexture extends WebGlCoreCtxTexture {
|
|
26
|
-
constructor(
|
|
27
|
-
|
|
27
|
+
constructor(
|
|
28
|
+
glw: WebGlContextWrapper,
|
|
29
|
+
memManager: TextureMemoryManager,
|
|
30
|
+
textureSource: SubTexture,
|
|
31
|
+
) {
|
|
32
|
+
super(glw, memManager, textureSource);
|
|
28
33
|
}
|
|
29
34
|
|
|
30
35
|
override async onLoadRequest(): Promise<Dimensions> {
|
|
@@ -19,6 +19,7 @@
|
|
|
19
19
|
|
|
20
20
|
import type { Dimensions } from '../../../common/CommonTypes.js';
|
|
21
21
|
import { assertTruthy } from '../../../utils.js';
|
|
22
|
+
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
|
|
22
23
|
import type { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
|
|
23
24
|
import type { Texture } from '../../textures/Texture.js';
|
|
24
25
|
import { isPowerOfTwo } from '../../utils.js';
|
|
@@ -44,8 +45,12 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
44
45
|
private _w = 0;
|
|
45
46
|
private _h = 0;
|
|
46
47
|
|
|
47
|
-
constructor(
|
|
48
|
-
|
|
48
|
+
constructor(
|
|
49
|
+
protected glw: WebGlContextWrapper,
|
|
50
|
+
memManager: TextureMemoryManager,
|
|
51
|
+
textureSource: Texture,
|
|
52
|
+
) {
|
|
53
|
+
super(memManager, textureSource);
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
get ctxTexture(): WebGLTexture {
|
|
@@ -56,6 +61,10 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
56
61
|
return this._nativeCtxTexture;
|
|
57
62
|
}
|
|
58
63
|
|
|
64
|
+
get renderable(): boolean {
|
|
65
|
+
return this.textureSource.renderable;
|
|
66
|
+
}
|
|
67
|
+
|
|
59
68
|
get w() {
|
|
60
69
|
return this._w;
|
|
61
70
|
}
|
|
@@ -80,8 +89,12 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
80
89
|
}
|
|
81
90
|
this._state = 'loading';
|
|
82
91
|
this.textureSource.setState('loading');
|
|
92
|
+
this._nativeCtxTexture = this.createNativeCtxTexture();
|
|
83
93
|
this.onLoadRequest()
|
|
84
94
|
.then(({ width, height }) => {
|
|
95
|
+
if (this._state === 'freed') {
|
|
96
|
+
return;
|
|
97
|
+
}
|
|
85
98
|
this._state = 'loaded';
|
|
86
99
|
this._w = width;
|
|
87
100
|
this._h = height;
|
|
@@ -100,8 +113,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
100
113
|
* Called when the texture data needs to be loaded and uploaded to a texture
|
|
101
114
|
*/
|
|
102
115
|
async onLoadRequest(): Promise<Dimensions> {
|
|
103
|
-
|
|
104
|
-
const { glw } = this;
|
|
116
|
+
const { glw, memManager } = this;
|
|
105
117
|
|
|
106
118
|
// On initial load request, create a 1x1 transparent texture to use until
|
|
107
119
|
// the texture data is finally loaded.
|
|
@@ -126,10 +138,17 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
126
138
|
glw.UNSIGNED_BYTE,
|
|
127
139
|
TRANSPARENT_TEXTURE_DATA,
|
|
128
140
|
);
|
|
141
|
+
memManager.setTextureMemUse(this, TRANSPARENT_TEXTURE_DATA.byteLength);
|
|
129
142
|
|
|
130
143
|
const textureData = await this.textureSource?.getTextureData();
|
|
144
|
+
// If the texture has been freed while loading, return early.
|
|
145
|
+
if (!this._nativeCtxTexture) {
|
|
146
|
+
assertTruthy(this._state === 'freed');
|
|
147
|
+
return { width: 0, height: 0 };
|
|
148
|
+
}
|
|
131
149
|
let width = 0;
|
|
132
150
|
let height = 0;
|
|
151
|
+
|
|
133
152
|
assertTruthy(this._nativeCtxTexture);
|
|
134
153
|
glw.activeTexture(0);
|
|
135
154
|
// If textureData is null, the texture is empty (0, 0) and we don't need to
|
|
@@ -150,6 +169,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
150
169
|
);
|
|
151
170
|
|
|
152
171
|
glw.texImage2D(0, glw.RGBA, glw.RGBA, glw.UNSIGNED_BYTE, data);
|
|
172
|
+
memManager.setTextureMemUse(this, width * height * 4);
|
|
153
173
|
|
|
154
174
|
// generate mipmaps for power-of-2 textures or in WebGL2RenderingContext
|
|
155
175
|
if (glw.isWebGl2() || (isPowerOfTwo(width) && isPowerOfTwo(height))) {
|
|
@@ -160,6 +180,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
160
180
|
height = 0;
|
|
161
181
|
// Reset to a 1x1 transparent texture
|
|
162
182
|
glw.bindTexture(this._nativeCtxTexture);
|
|
183
|
+
|
|
163
184
|
glw.texImage2D(
|
|
164
185
|
0,
|
|
165
186
|
glw.RGBA,
|
|
@@ -170,6 +191,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
170
191
|
glw.UNSIGNED_BYTE,
|
|
171
192
|
TRANSPARENT_TEXTURE_DATA,
|
|
172
193
|
);
|
|
194
|
+
memManager.setTextureMemUse(this, TRANSPARENT_TEXTURE_DATA.byteLength);
|
|
173
195
|
} else if ('mipmaps' in textureData.data && textureData.data.mipmaps) {
|
|
174
196
|
const {
|
|
175
197
|
mipmaps,
|
|
@@ -184,12 +206,14 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
184
206
|
: (mipmaps[0] as unknown as ArrayBufferView);
|
|
185
207
|
|
|
186
208
|
glw.bindTexture(this._nativeCtxTexture);
|
|
187
|
-
glw.compressedTexImage2D(0, glInternalFormat, width, height, 0, view);
|
|
188
209
|
|
|
210
|
+
glw.compressedTexImage2D(0, glInternalFormat, width, height, 0, view);
|
|
189
211
|
glw.texParameteri(glw.TEXTURE_WRAP_S, glw.CLAMP_TO_EDGE);
|
|
190
212
|
glw.texParameteri(glw.TEXTURE_WRAP_T, glw.CLAMP_TO_EDGE);
|
|
191
213
|
glw.texParameteri(glw.TEXTURE_MAG_FILTER, glw.LINEAR);
|
|
192
214
|
glw.texParameteri(glw.TEXTURE_MIN_FILTER, glw.LINEAR);
|
|
215
|
+
|
|
216
|
+
memManager.setTextureMemUse(this, view.byteLength);
|
|
193
217
|
} else {
|
|
194
218
|
console.error(
|
|
195
219
|
`WebGlCoreCtxTexture.onLoadRequest: Unexpected textureData returned`,
|
|
@@ -202,6 +226,7 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
202
226
|
height,
|
|
203
227
|
};
|
|
204
228
|
}
|
|
229
|
+
|
|
205
230
|
/**
|
|
206
231
|
* Free the WebGLTexture from the GPU
|
|
207
232
|
*
|
|
@@ -212,13 +237,16 @@ export class WebGlCoreCtxTexture extends CoreContextTexture {
|
|
|
212
237
|
return;
|
|
213
238
|
}
|
|
214
239
|
this._state = 'freed';
|
|
240
|
+
this.textureSource.setState('freed');
|
|
215
241
|
this._w = 0;
|
|
216
242
|
this._h = 0;
|
|
217
243
|
if (!this._nativeCtxTexture) {
|
|
218
244
|
return;
|
|
219
245
|
}
|
|
220
|
-
const { glw } = this;
|
|
246
|
+
const { glw, memManager } = this;
|
|
247
|
+
|
|
221
248
|
glw.deleteTexture(this._nativeCtxTexture);
|
|
249
|
+
memManager.setTextureMemUse(this, 0);
|
|
222
250
|
this._nativeCtxTexture = null;
|
|
223
251
|
}
|
|
224
252
|
|
|
@@ -57,6 +57,7 @@ import { WebGlCoreShader } from './WebGlCoreShader.js';
|
|
|
57
57
|
import { RoundedRectangle } from './shaders/RoundedRectangle.js';
|
|
58
58
|
import { ContextSpy } from '../../lib/ContextSpy.js';
|
|
59
59
|
import { WebGlContextWrapper } from '../../lib/WebGlContextWrapper.js';
|
|
60
|
+
import type { TextureMemoryManager } from '../../TextureMemoryManager.js';
|
|
60
61
|
|
|
61
62
|
const WORDS_PER_QUAD = 24;
|
|
62
63
|
const BYTES_PER_QUAD = WORDS_PER_QUAD * 4;
|
|
@@ -66,6 +67,7 @@ export interface WebGlCoreRendererOptions {
|
|
|
66
67
|
canvas: HTMLCanvasElement | OffscreenCanvas;
|
|
67
68
|
pixelRatio: number;
|
|
68
69
|
txManager: CoreTextureManager;
|
|
70
|
+
txMemManager: TextureMemoryManager;
|
|
69
71
|
shManager: CoreShaderManager;
|
|
70
72
|
clearColor: number;
|
|
71
73
|
bufferMemory: number;
|
|
@@ -84,6 +86,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
84
86
|
|
|
85
87
|
//// Core Managers
|
|
86
88
|
txManager: CoreTextureManager;
|
|
89
|
+
txMemManager: TextureMemoryManager;
|
|
87
90
|
shManager: CoreShaderManager;
|
|
88
91
|
|
|
89
92
|
//// Options
|
|
@@ -114,6 +117,7 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
114
117
|
const { canvas, clearColor, bufferMemory } = options;
|
|
115
118
|
this.options = options;
|
|
116
119
|
this.txManager = options.txManager;
|
|
120
|
+
this.txMemManager = options.txMemManager;
|
|
117
121
|
this.shManager = options.shManager;
|
|
118
122
|
this.defaultTexture = new ColorTexture(this.txManager);
|
|
119
123
|
// When the default texture is loaded, request a render in case the
|
|
@@ -198,9 +202,13 @@ export class WebGlCoreRenderer extends CoreRenderer {
|
|
|
198
202
|
|
|
199
203
|
override createCtxTexture(textureSource: Texture): CoreContextTexture {
|
|
200
204
|
if (textureSource instanceof SubTexture) {
|
|
201
|
-
return new WebGlCoreCtxSubTexture(
|
|
205
|
+
return new WebGlCoreCtxSubTexture(
|
|
206
|
+
this.glw,
|
|
207
|
+
this.txMemManager,
|
|
208
|
+
textureSource,
|
|
209
|
+
);
|
|
202
210
|
}
|
|
203
|
-
return new WebGlCoreCtxTexture(this.glw, textureSource);
|
|
211
|
+
return new WebGlCoreCtxTexture(this.glw, this.txMemManager, textureSource);
|
|
204
212
|
}
|
|
205
213
|
|
|
206
214
|
/**
|
|
@@ -61,13 +61,22 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
61
61
|
): Required<LinearGradientEffectProps> {
|
|
62
62
|
const colors = props.colors ?? [0xff000000, 0xffffffff];
|
|
63
63
|
|
|
64
|
-
let stops = props.stops;
|
|
65
|
-
if (
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
64
|
+
let stops = props.stops || [];
|
|
65
|
+
if (stops.length === 0 || stops.length !== colors.length) {
|
|
66
|
+
const colorsL = colors.length;
|
|
67
|
+
let i = 0;
|
|
68
|
+
const tmp = stops;
|
|
69
|
+
for (; i < colorsL; i++) {
|
|
70
|
+
if (stops[i]) {
|
|
71
|
+
tmp[i] = stops[i]!;
|
|
72
|
+
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
73
|
+
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
74
|
+
}
|
|
75
|
+
} else {
|
|
76
|
+
tmp[i] = i * (1 / (colors.length - 1));
|
|
77
|
+
}
|
|
70
78
|
}
|
|
79
|
+
stops = tmp;
|
|
71
80
|
}
|
|
72
81
|
return {
|
|
73
82
|
colors,
|
|
@@ -94,28 +103,6 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
94
103
|
},
|
|
95
104
|
stops: {
|
|
96
105
|
value: [],
|
|
97
|
-
validator: (
|
|
98
|
-
value: number[],
|
|
99
|
-
props: LinearGradientEffectProps,
|
|
100
|
-
): number[] => {
|
|
101
|
-
const colors = props.colors ?? [];
|
|
102
|
-
let stops = value;
|
|
103
|
-
const tmp: number[] = value;
|
|
104
|
-
if (stops.length === 0 || (stops && stops.length !== colors.length)) {
|
|
105
|
-
for (let i = 0; i < colors.length; i++) {
|
|
106
|
-
if (stops[i]) {
|
|
107
|
-
tmp[i] = stops[i]!;
|
|
108
|
-
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
109
|
-
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
110
|
-
}
|
|
111
|
-
} else {
|
|
112
|
-
tmp[i] = i * (1 / (colors.length - 1));
|
|
113
|
-
}
|
|
114
|
-
}
|
|
115
|
-
stops = tmp;
|
|
116
|
-
}
|
|
117
|
-
return tmp;
|
|
118
|
-
},
|
|
119
106
|
size: (props: LinearGradientEffectProps) => props.colors!.length,
|
|
120
107
|
method: 'uniform1fv',
|
|
121
108
|
type: 'float',
|
|
@@ -166,10 +153,7 @@ export class LinearGradientEffect extends ShaderEffect {
|
|
|
166
153
|
|
|
167
154
|
float stopCalc = (dist - stops[0]) / (stops[1] - stops[0]);
|
|
168
155
|
vec4 colorOut = $fromLinear(mix($toLinear(colors[0]), $toLinear(colors[1]), stopCalc));
|
|
169
|
-
|
|
170
|
-
stopCalc = (dist - stops[i]) / (stops[i + 1] - stops[i]);
|
|
171
|
-
colorOut = mix(colorOut, colors[i + 1], clamp(stopCalc, 0.0, 1.0));
|
|
172
|
-
}
|
|
156
|
+
${this.ColorLoop(colors)}
|
|
173
157
|
return mix(maskColor, colorOut, clamp(colorOut.a, 0.0, 1.0));
|
|
174
158
|
`;
|
|
175
159
|
};
|
|
@@ -66,13 +66,22 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
66
66
|
): Required<RadialGradientEffectProps> {
|
|
67
67
|
const colors = props.colors ?? [0xff000000, 0xffffffff];
|
|
68
68
|
|
|
69
|
-
let stops = props.stops;
|
|
70
|
-
if (
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
69
|
+
let stops = props.stops || [];
|
|
70
|
+
if (stops.length === 0 || stops.length !== colors.length) {
|
|
71
|
+
const colorsL = colors.length;
|
|
72
|
+
let i = 0;
|
|
73
|
+
const tmp = stops;
|
|
74
|
+
for (; i < colorsL; i++) {
|
|
75
|
+
if (stops[i]) {
|
|
76
|
+
tmp[i] = stops[i]!;
|
|
77
|
+
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
78
|
+
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
79
|
+
}
|
|
80
|
+
} else {
|
|
81
|
+
tmp[i] = i * (1 / (colors.length - 1));
|
|
82
|
+
}
|
|
75
83
|
}
|
|
84
|
+
stops = tmp;
|
|
76
85
|
}
|
|
77
86
|
return {
|
|
78
87
|
colors,
|
|
@@ -111,34 +120,22 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
111
120
|
},
|
|
112
121
|
stops: {
|
|
113
122
|
value: [],
|
|
114
|
-
validator: (
|
|
115
|
-
value: number[],
|
|
116
|
-
props: RadialGradientEffectProps,
|
|
117
|
-
): number[] => {
|
|
118
|
-
const colors = props.colors ?? [];
|
|
119
|
-
let stops = value;
|
|
120
|
-
const tmp: number[] = value;
|
|
121
|
-
if (stops.length === 0 || (stops && stops.length !== colors.length)) {
|
|
122
|
-
for (let i = 0; i < colors.length; i++) {
|
|
123
|
-
if (stops[i]) {
|
|
124
|
-
tmp[i] = stops[i]!;
|
|
125
|
-
if (stops[i - 1] === undefined && tmp[i - 2] !== undefined) {
|
|
126
|
-
tmp[i - 1] = tmp[i - 2]! + (stops[i]! - tmp[i - 2]!) / 2;
|
|
127
|
-
}
|
|
128
|
-
} else {
|
|
129
|
-
tmp[i] = i * (1 / (colors.length - 1));
|
|
130
|
-
}
|
|
131
|
-
}
|
|
132
|
-
stops = tmp;
|
|
133
|
-
}
|
|
134
|
-
return tmp;
|
|
135
|
-
},
|
|
136
123
|
size: (props: RadialGradientEffectProps) => props.colors!.length,
|
|
137
124
|
method: 'uniform1fv',
|
|
138
125
|
type: 'float',
|
|
139
126
|
},
|
|
140
127
|
};
|
|
141
128
|
|
|
129
|
+
static ColorLoop = (amount: number): string => {
|
|
130
|
+
let loop = '';
|
|
131
|
+
for (let i = 2; i < amount; i++) {
|
|
132
|
+
loop += `colorOut = mix(colorOut, colors[${i}], clamp((dist - stops[${
|
|
133
|
+
i - 1
|
|
134
|
+
}]) / (stops[${i}] - stops[${i - 1}]), 0.0, 1.0));`;
|
|
135
|
+
}
|
|
136
|
+
return loop;
|
|
137
|
+
};
|
|
138
|
+
|
|
142
139
|
static override onColorize = (props: RadialGradientEffectProps) => {
|
|
143
140
|
const colors = props.colors!.length || 1;
|
|
144
141
|
return `
|
|
@@ -149,10 +146,7 @@ export class RadialGradientEffect extends ShaderEffect {
|
|
|
149
146
|
|
|
150
147
|
float stopCalc = (dist - stops[0]) / (stops[1] - stops[0]);
|
|
151
148
|
vec4 colorOut = mix(colors[0], colors[1], stopCalc);
|
|
152
|
-
|
|
153
|
-
stopCalc = (dist - stops[i]) / (stops[i + 1] - stops[i]);
|
|
154
|
-
colorOut = mix(colorOut, colors[i + 1], clamp(stopCalc, 0.0, 1.0));
|
|
155
|
-
}
|
|
149
|
+
${this.ColorLoop(colors)}
|
|
156
150
|
return mix(maskColor, colorOut, clamp(colorOut.a, 0.0, 1.0));
|
|
157
151
|
`;
|
|
158
152
|
};
|