@lightningjs/renderer 3.0.2 → 3.0.4
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 +56 -196
- package/dist/src/core/CoreNode.d.ts +2 -1
- package/dist/src/core/CoreNode.js +31 -7
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreTextNode.d.ts +26 -6
- package/dist/src/core/CoreTextNode.js +163 -60
- package/dist/src/core/CoreTextNode.js.map +1 -1
- package/dist/src/core/CoreTextureManager.d.ts +8 -0
- package/dist/src/core/CoreTextureManager.js +13 -1
- package/dist/src/core/CoreTextureManager.js.map +1 -1
- package/dist/src/core/Stage.d.ts +8 -0
- package/dist/src/core/Stage.js +23 -0
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/TextureMemoryManager.d.ts +8 -13
- package/dist/src/core/TextureMemoryManager.js +22 -27
- package/dist/src/core/TextureMemoryManager.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.d.ts +2 -2
- package/dist/src/core/lib/ImageWorker.js +31 -12
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/WebGlContextWrapper.d.ts +105 -56
- package/dist/src/core/lib/WebGlContextWrapper.js +164 -158
- package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
- package/dist/src/core/lib/fps.d.ts +15 -0
- package/dist/src/core/lib/fps.js +62 -0
- package/dist/src/core/lib/fps.js.map +1 -0
- package/dist/src/core/lib/textureCompression.js +19 -10
- package/dist/src/core/lib/textureCompression.js.map +1 -1
- package/dist/src/core/lib/validateImageBitmap.d.ts +2 -1
- package/dist/src/core/lib/validateImageBitmap.js +4 -4
- package/dist/src/core/lib/validateImageBitmap.js.map +1 -1
- package/dist/src/core/platform.js +2 -2
- package/dist/src/core/platform.js.map +1 -1
- package/dist/src/core/platforms/Platform.d.ts +4 -0
- package/dist/src/core/platforms/Platform.js.map +1 -1
- package/dist/src/core/platforms/web/WebPlatform.d.ts +2 -0
- package/dist/src/core/platforms/web/WebPlatform.js +13 -0
- package/dist/src/core/platforms/web/WebPlatform.js.map +1 -1
- package/dist/src/core/renderers/CoreRenderer.d.ts +6 -0
- package/dist/src/core/renderers/CoreRenderer.js +8 -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 +5 -0
- package/dist/src/core/renderers/canvas/CanvasRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderOp.d.ts +45 -0
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js +127 -0
- package/dist/src/core/renderers/webgl/WebGlRenderOp.js.map +1 -0
- package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +4 -2
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +30 -22
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +2 -3
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasFont.d.ts +14 -0
- package/dist/src/core/text-rendering/CanvasFont.js +120 -0
- package/dist/src/core/text-rendering/CanvasFont.js.map +1 -0
- package/dist/src/core/text-rendering/CanvasFontHandler.d.ts +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasTextRenderer.d.ts +3 -5
- package/dist/src/core/text-rendering/CanvasTextRenderer.js +16 -22
- package/dist/src/core/text-rendering/CanvasTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/CoreFont.d.ts +33 -0
- package/dist/src/core/text-rendering/CoreFont.js +48 -0
- package/dist/src/core/text-rendering/CoreFont.js.map +1 -0
- package/dist/src/core/text-rendering/FontManager.d.ts +11 -0
- package/dist/src/core/text-rendering/FontManager.js +41 -0
- package/dist/src/core/text-rendering/FontManager.js.map +1 -0
- package/dist/src/core/text-rendering/SdfFont.d.ts +29 -0
- package/dist/src/core/text-rendering/SdfFont.js +142 -0
- package/dist/src/core/text-rendering/SdfFont.js.map +1 -0
- package/dist/src/core/text-rendering/SdfTextRenderer.d.ts +4 -6
- package/dist/src/core/text-rendering/SdfTextRenderer.js +87 -168
- package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/TextGenerator.d.ts +10 -0
- package/dist/src/core/text-rendering/TextGenerator.js +36 -0
- package/dist/src/core/text-rendering/TextGenerator.js.map +1 -0
- package/dist/src/core/text-rendering/TextLayoutEngine.js +43 -12
- package/dist/src/core/text-rendering/TextLayoutEngine.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +41 -27
- package/dist/src/core/text-rendering/Utils.d.ts +2 -0
- package/dist/src/core/text-rendering/Utils.js +3 -0
- package/dist/src/core/text-rendering/Utils.js.map +1 -1
- package/dist/src/main-api/Inspector.d.ts +1 -1
- package/dist/src/main-api/Inspector.js +25 -20
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +14 -0
- package/dist/src/main-api/Renderer.js +29 -3
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +2 -1
- package/src/core/CoreNode.test.ts +1 -1
- package/src/core/CoreNode.ts +37 -8
- package/src/core/CoreTextNode.test.ts +350 -0
- package/src/core/CoreTextNode.ts +201 -74
- package/src/core/CoreTextureManager.ts +14 -2
- package/src/core/Stage.ts +29 -0
- package/src/core/TextureMemoryManager.test.ts +134 -0
- package/src/core/TextureMemoryManager.ts +23 -30
- package/src/core/platforms/Platform.ts +5 -0
- package/src/core/platforms/web/WebPlatform.ts +13 -0
- package/src/core/renderers/CoreRenderer.ts +10 -0
- package/src/core/renderers/canvas/CanvasRenderer.ts +6 -0
- package/src/core/renderers/webgl/WebGlRenderer.rtt.test.ts +551 -0
- package/src/core/renderers/webgl/WebGlRenderer.ts +40 -31
- package/src/core/renderers/webgl/WebGlShaderProgram.test.ts +274 -0
- package/src/core/renderers/webgl/WebGlShaderProgram.ts +7 -7
- package/src/core/text-rendering/CanvasFontHandler.ts +2 -2
- package/src/core/text-rendering/CanvasTextRenderer.ts +24 -45
- package/src/core/text-rendering/SdfTextRenderer.ts +106 -215
- package/src/core/text-rendering/TextLayoutEngine.ts +61 -28
- package/src/core/text-rendering/TextRenderer.ts +42 -33
- package/src/core/text-rendering/Utils.ts +5 -1
- package/src/core/text-rendering/tests/TextLayoutEngine.test.ts +20 -0
- package/src/main-api/Inspector.ts +25 -25
- package/src/main-api/Renderer.test.ts +153 -0
- package/src/main-api/Renderer.ts +33 -3
- package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.d.ts +0 -1
- package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.js +0 -2
- package/dist/src/core/renderers/webgl/WebGlCoreShader.destroy.js.map +0 -1
- package/src/core/renderers/webgl/SdfRenderOp.ts +0 -106
|
@@ -18,7 +18,6 @@
|
|
|
18
18
|
*/
|
|
19
19
|
|
|
20
20
|
import { CoreRenderer, type BufferInfo } from '../CoreRenderer.js';
|
|
21
|
-
import type { SdfRenderOp } from './SdfRenderOp.js';
|
|
22
21
|
import type { CoreContextTexture } from '../CoreContextTexture.js';
|
|
23
22
|
import {
|
|
24
23
|
createIndexBuffer,
|
|
@@ -47,15 +46,15 @@ import type { WebGlShaderType } from './WebGlShaderNode.js';
|
|
|
47
46
|
import { WebGlShaderNode } from './WebGlShaderNode.js';
|
|
48
47
|
import type { Dimensions } from '../../../common/CommonTypes.js';
|
|
49
48
|
import type { GlContextWrapper } from '../../platforms/GlContextWrapper.js';
|
|
50
|
-
import type { Platform } from '../../platforms/Platform.js';
|
|
51
49
|
import type { Stage } from '../../Stage.js';
|
|
50
|
+
import type { CoreTextNode } from '../../CoreTextNode.js';
|
|
52
51
|
|
|
53
52
|
interface CoreWebGlSystem {
|
|
54
53
|
parameters: CoreWebGlParameters;
|
|
55
54
|
extensions: CoreWebGlExtensions;
|
|
56
55
|
}
|
|
57
56
|
|
|
58
|
-
export type WebGlRenderOp = CoreNode |
|
|
57
|
+
export type WebGlRenderOp = CoreNode | CoreTextNode;
|
|
59
58
|
|
|
60
59
|
export class WebGlRenderer extends CoreRenderer {
|
|
61
60
|
//// WebGL Native Context and Data
|
|
@@ -474,28 +473,27 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
474
473
|
private insertRTTNodeInOrder(node: CoreNode) {
|
|
475
474
|
let insertIndex = this.rttNodes.length; // Default to the end of the array
|
|
476
475
|
|
|
476
|
+
// Build a one-shot index map so all lookups below are O(1) instead of O(n).
|
|
477
|
+
const rttNodes = this.rttNodes;
|
|
478
|
+
const indexMap = new Map<number, number>();
|
|
479
|
+
for (let i = 0; i < rttNodes.length; i++) {
|
|
480
|
+
indexMap.set(rttNodes[i]!.id, i);
|
|
481
|
+
}
|
|
482
|
+
|
|
477
483
|
// 1. Traverse upwards to ensure the node is placed before its RTT parent (if any).
|
|
478
484
|
let currentNode: CoreNode = node;
|
|
479
|
-
while (currentNode) {
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
}
|
|
483
|
-
|
|
484
|
-
const parentIndex = this.rttNodes.indexOf(currentNode.parent);
|
|
485
|
-
if (parentIndex !== -1) {
|
|
486
|
-
// Found an RTT parent in the list; set insertIndex to place node before the parent
|
|
485
|
+
while (currentNode.parent !== null) {
|
|
486
|
+
const parentIndex = indexMap.get(currentNode.parent.id);
|
|
487
|
+
if (parentIndex !== undefined) {
|
|
487
488
|
insertIndex = parentIndex;
|
|
488
489
|
break;
|
|
489
490
|
}
|
|
490
|
-
|
|
491
491
|
currentNode = currentNode.parent;
|
|
492
492
|
}
|
|
493
493
|
|
|
494
494
|
// 2. Traverse downwards to ensure the node is placed after any RTT children.
|
|
495
|
-
|
|
496
|
-
const maxChildIndex = this.findMaxChildRTTIndex(node);
|
|
495
|
+
const maxChildIndex = this.findMaxChildRTTIndex(node, indexMap);
|
|
497
496
|
if (maxChildIndex !== -1) {
|
|
498
|
-
// Adjust insertIndex to be after the last child RTT node
|
|
499
497
|
insertIndex = Math.max(insertIndex, maxChildIndex + 1);
|
|
500
498
|
}
|
|
501
499
|
|
|
@@ -503,25 +501,25 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
503
501
|
this.rttNodes.splice(insertIndex, 0, node);
|
|
504
502
|
}
|
|
505
503
|
|
|
506
|
-
//
|
|
507
|
-
private findMaxChildRTTIndex(
|
|
504
|
+
// Iterative DFS to find the highest rttNodes index among all RTT descendants of node.
|
|
505
|
+
private findMaxChildRTTIndex(
|
|
506
|
+
node: CoreNode,
|
|
507
|
+
indexMap: Map<number, number>,
|
|
508
|
+
): number {
|
|
508
509
|
let maxIndex = -1;
|
|
509
|
-
|
|
510
|
-
const
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
510
|
+
// Explicit stack avoids recursive arrow function allocation and call-stack growth.
|
|
511
|
+
const stack: CoreNode[] = [node];
|
|
512
|
+
while (stack.length !== 0) {
|
|
513
|
+
const current = stack.pop()!;
|
|
514
|
+
const idx = indexMap.get(current.id);
|
|
515
|
+
if (idx !== undefined && idx > maxIndex) {
|
|
516
|
+
maxIndex = idx;
|
|
514
517
|
}
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
traverseChildren(child);
|
|
518
|
+
const children = current.children;
|
|
519
|
+
for (let i = 0; i < children.length; i++) {
|
|
520
|
+
stack.push(children[i]!);
|
|
519
521
|
}
|
|
520
|
-
}
|
|
521
|
-
|
|
522
|
-
// Start traversal directly with the provided node
|
|
523
|
-
traverseChildren(node);
|
|
524
|
-
|
|
522
|
+
}
|
|
525
523
|
return maxIndex;
|
|
526
524
|
}
|
|
527
525
|
|
|
@@ -724,4 +722,15 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
724
722
|
normalized: normalizedColor,
|
|
725
723
|
};
|
|
726
724
|
}
|
|
725
|
+
|
|
726
|
+
override destroy(): void {
|
|
727
|
+
const loseCtx = this.glw.getExtension(
|
|
728
|
+
'WEBGL_lose_context',
|
|
729
|
+
) as WEBGL_lose_context | null;
|
|
730
|
+
loseCtx?.loseContext();
|
|
731
|
+
}
|
|
732
|
+
|
|
733
|
+
override deleteBuffer(buffer: WebGLBuffer): void {
|
|
734
|
+
this.glw.deleteBuffer(buffer);
|
|
735
|
+
}
|
|
727
736
|
}
|
|
@@ -0,0 +1,274 @@
|
|
|
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 2026 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
|
+
|
|
20
|
+
import { describe, expect, it, vi } from 'vitest';
|
|
21
|
+
import { mock } from 'vitest-mock-extended';
|
|
22
|
+
import { CoreTextNode, type CoreTextNodeProps } from '../../CoreTextNode.js';
|
|
23
|
+
import type { Stage } from '../../Stage.js';
|
|
24
|
+
import type { TextRenderer } from '../../text-rendering/TextRenderer.js';
|
|
25
|
+
import { createBound } from '../../lib/utils.js';
|
|
26
|
+
import type { CoreRenderer } from '../CoreRenderer.js';
|
|
27
|
+
import { WebGlRenderer } from './WebGlRenderer.js';
|
|
28
|
+
import { WebGlShaderProgram } from './WebGlShaderProgram.js';
|
|
29
|
+
|
|
30
|
+
const makeStage = (): Stage =>
|
|
31
|
+
mock<Stage>({
|
|
32
|
+
strictBound: createBound(0, 0, 1920, 1080),
|
|
33
|
+
preloadBound: createBound(0, 0, 1920, 1080),
|
|
34
|
+
defaultTexture: {
|
|
35
|
+
state: 'loaded',
|
|
36
|
+
},
|
|
37
|
+
pixelRatio: 2,
|
|
38
|
+
renderer: mock<CoreRenderer>() as CoreRenderer,
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
const makeTextProps = (): CoreTextNodeProps => ({
|
|
42
|
+
alpha: 1,
|
|
43
|
+
autosize: false,
|
|
44
|
+
boundsMargin: null,
|
|
45
|
+
clipping: false,
|
|
46
|
+
color: 0xffffffff,
|
|
47
|
+
colorBl: 0xffffffff,
|
|
48
|
+
colorBottom: 0xffffffff,
|
|
49
|
+
colorBr: 0xffffffff,
|
|
50
|
+
colorLeft: 0xffffffff,
|
|
51
|
+
colorRight: 0xffffffff,
|
|
52
|
+
colorTl: 0xffffffff,
|
|
53
|
+
colorTop: 0xffffffff,
|
|
54
|
+
colorTr: 0xffffffff,
|
|
55
|
+
h: 20,
|
|
56
|
+
mount: 0,
|
|
57
|
+
mountX: 0,
|
|
58
|
+
mountY: 0,
|
|
59
|
+
parent: null,
|
|
60
|
+
pivot: 0,
|
|
61
|
+
pivotX: 0,
|
|
62
|
+
pivotY: 0,
|
|
63
|
+
rotation: 0,
|
|
64
|
+
rtt: false,
|
|
65
|
+
scale: 1,
|
|
66
|
+
scaleX: 1,
|
|
67
|
+
scaleY: 1,
|
|
68
|
+
shader: null,
|
|
69
|
+
src: '',
|
|
70
|
+
texture: null,
|
|
71
|
+
textureOptions: {},
|
|
72
|
+
w: 100,
|
|
73
|
+
x: 0,
|
|
74
|
+
y: 0,
|
|
75
|
+
zIndex: 0,
|
|
76
|
+
text: 'Test',
|
|
77
|
+
textAlign: 'left',
|
|
78
|
+
contain: 'none',
|
|
79
|
+
fontFamily: 'Arial',
|
|
80
|
+
fontStyle: 'normal',
|
|
81
|
+
fontSize: 16,
|
|
82
|
+
letterSpacing: 0,
|
|
83
|
+
lineHeight: 1,
|
|
84
|
+
maxHeight: 0,
|
|
85
|
+
maxLines: 0,
|
|
86
|
+
maxWidth: 0,
|
|
87
|
+
offsetY: 0,
|
|
88
|
+
overflowSuffix: '...',
|
|
89
|
+
verticalAlign: 'top',
|
|
90
|
+
wordBreak: 'break-word',
|
|
91
|
+
textRendererOverride: null,
|
|
92
|
+
forceLoad: false,
|
|
93
|
+
});
|
|
94
|
+
|
|
95
|
+
const makeSdfTextRenderer = (): TextRenderer =>
|
|
96
|
+
({
|
|
97
|
+
clearCache: vi.fn(),
|
|
98
|
+
type: 'sdf',
|
|
99
|
+
font: {
|
|
100
|
+
isFontLoaded: vi.fn().mockReturnValue(true),
|
|
101
|
+
loadFont: vi.fn(),
|
|
102
|
+
waitingForFont: vi.fn(),
|
|
103
|
+
stopWaitingForFont: vi.fn(),
|
|
104
|
+
},
|
|
105
|
+
init: vi.fn(),
|
|
106
|
+
renderText: vi.fn().mockReturnValue({
|
|
107
|
+
width: 100,
|
|
108
|
+
height: 20,
|
|
109
|
+
layout: { glyphs: [], width: 100, height: 20 },
|
|
110
|
+
}),
|
|
111
|
+
addQuads: vi.fn().mockReturnValue(new Float32Array(0)),
|
|
112
|
+
renderQuads: vi.fn(),
|
|
113
|
+
} as unknown as TextRenderer);
|
|
114
|
+
|
|
115
|
+
const makeSdfTextNode = () => {
|
|
116
|
+
const node = new CoreTextNode(
|
|
117
|
+
makeStage(),
|
|
118
|
+
makeTextProps(),
|
|
119
|
+
makeSdfTextRenderer(),
|
|
120
|
+
);
|
|
121
|
+
node.parentHasRenderTexture = true;
|
|
122
|
+
node.framebufferDimensions = { w: 320, h: 180 };
|
|
123
|
+
node.rttParent = { framebufferDimensions: { w: 640, h: 360 } } as any;
|
|
124
|
+
return node;
|
|
125
|
+
};
|
|
126
|
+
|
|
127
|
+
describe('WebGlShaderProgram.bindRenderOp', () => {
|
|
128
|
+
function createProgram() {
|
|
129
|
+
const program = Object.create(
|
|
130
|
+
WebGlShaderProgram.prototype,
|
|
131
|
+
) as WebGlShaderProgram;
|
|
132
|
+
const bindTextures = vi.fn();
|
|
133
|
+
const bindBufferCollection = vi.fn();
|
|
134
|
+
const uniform1f = vi.fn();
|
|
135
|
+
const uniform2f = vi.fn();
|
|
136
|
+
const glw = {
|
|
137
|
+
canvas: { width: 1920, height: 1080 },
|
|
138
|
+
uniform1f,
|
|
139
|
+
uniform2f,
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
(program as any).bindTextures = bindTextures;
|
|
143
|
+
(program as any).bindBufferCollection = bindBufferCollection;
|
|
144
|
+
(program as any).glw = glw;
|
|
145
|
+
(program as any).useTimeValue = false;
|
|
146
|
+
(program as any).useSystemAlpha = false;
|
|
147
|
+
(program as any).useSystemDimensions = false;
|
|
148
|
+
|
|
149
|
+
return {
|
|
150
|
+
program,
|
|
151
|
+
bindTextures,
|
|
152
|
+
bindBufferCollection,
|
|
153
|
+
uniform1f,
|
|
154
|
+
uniform2f,
|
|
155
|
+
};
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
it('binds SDF shader props while using the main framebuffer resolution', () => {
|
|
159
|
+
const {
|
|
160
|
+
program,
|
|
161
|
+
bindTextures,
|
|
162
|
+
bindBufferCollection,
|
|
163
|
+
uniform1f,
|
|
164
|
+
uniform2f,
|
|
165
|
+
} = createProgram();
|
|
166
|
+
const onSdfBind = vi.fn();
|
|
167
|
+
const renderOp = {
|
|
168
|
+
isCoreNode: false,
|
|
169
|
+
isSdfRenderOp: true,
|
|
170
|
+
shader: { shaderType: { onSdfBind } },
|
|
171
|
+
sdfShaderProps: { size: 16, distanceRange: 4 },
|
|
172
|
+
renderOpTextures: [],
|
|
173
|
+
quadBufferCollection: {},
|
|
174
|
+
parentHasRenderTexture: false,
|
|
175
|
+
framebufferDimensions: null,
|
|
176
|
+
rtt: false,
|
|
177
|
+
stage: { pixelRatio: 1.5 },
|
|
178
|
+
time: 0,
|
|
179
|
+
worldAlpha: 1,
|
|
180
|
+
w: 100,
|
|
181
|
+
h: 20,
|
|
182
|
+
};
|
|
183
|
+
|
|
184
|
+
program.bindRenderOp(renderOp as any);
|
|
185
|
+
|
|
186
|
+
expect(bindTextures).toHaveBeenCalledWith(renderOp.renderOpTextures);
|
|
187
|
+
expect(bindBufferCollection).toHaveBeenCalledWith(
|
|
188
|
+
renderOp.quadBufferCollection,
|
|
189
|
+
);
|
|
190
|
+
expect(uniform1f).toHaveBeenCalledWith('u_pixelRatio', 1.5);
|
|
191
|
+
expect(uniform2f).toHaveBeenCalledWith('u_resolution', 1920, 1080);
|
|
192
|
+
expect(onSdfBind).toHaveBeenCalledWith(renderOp.sdfShaderProps);
|
|
193
|
+
});
|
|
194
|
+
|
|
195
|
+
it('keeps SDF binding active when rendering into a parent framebuffer', () => {
|
|
196
|
+
const { program, uniform1f, uniform2f } = createProgram();
|
|
197
|
+
const onSdfBind = vi.fn();
|
|
198
|
+
const renderOp = {
|
|
199
|
+
isCoreNode: false,
|
|
200
|
+
isSdfRenderOp: true,
|
|
201
|
+
shader: { shaderType: { onSdfBind } },
|
|
202
|
+
sdfShaderProps: { size: 18, distanceRange: 6 },
|
|
203
|
+
renderOpTextures: [],
|
|
204
|
+
quadBufferCollection: {},
|
|
205
|
+
parentHasRenderTexture: true,
|
|
206
|
+
framebufferDimensions: { w: 320, h: 180 },
|
|
207
|
+
rtt: false,
|
|
208
|
+
stage: { pixelRatio: 2 },
|
|
209
|
+
time: 0,
|
|
210
|
+
worldAlpha: 1,
|
|
211
|
+
w: 100,
|
|
212
|
+
h: 20,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
program.bindRenderOp(renderOp as any);
|
|
216
|
+
|
|
217
|
+
expect(uniform1f).toHaveBeenCalledWith('u_pixelRatio', 1);
|
|
218
|
+
expect(uniform2f).toHaveBeenCalledWith('u_resolution', 320, 180);
|
|
219
|
+
expect(onSdfBind).toHaveBeenCalledWith(renderOp.sdfShaderProps);
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
it('uses parent RTT dimensions for SDF text render ops', () => {
|
|
223
|
+
const { program, uniform1f, uniform2f } = createProgram();
|
|
224
|
+
const onSdfBind = vi.fn();
|
|
225
|
+
const sdfShaderProps = {
|
|
226
|
+
color: 0xffffffff,
|
|
227
|
+
distanceRange: 1,
|
|
228
|
+
size: 16,
|
|
229
|
+
transform: new Float32Array([1, 0, 0, 0, 1, 0, 0, 0, 1]),
|
|
230
|
+
};
|
|
231
|
+
const renderOp = {
|
|
232
|
+
framebufferDimensions: { w: 320, h: 180 },
|
|
233
|
+
isCoreNode: true,
|
|
234
|
+
isSdfRenderOp: true,
|
|
235
|
+
parentFramebufferDimensions: { w: 640, h: 360 },
|
|
236
|
+
parentHasRenderTexture: true,
|
|
237
|
+
quadBufferCollection: {},
|
|
238
|
+
renderOpTextures: [],
|
|
239
|
+
rtt: false,
|
|
240
|
+
sdfShaderProps,
|
|
241
|
+
shader: {
|
|
242
|
+
shaderType: {
|
|
243
|
+
onSdfBind,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
stage: { pixelRatio: 2 },
|
|
247
|
+
time: 0,
|
|
248
|
+
worldAlpha: 1,
|
|
249
|
+
w: 100,
|
|
250
|
+
h: 20,
|
|
251
|
+
} as any;
|
|
252
|
+
|
|
253
|
+
program.bindRenderOp(renderOp);
|
|
254
|
+
|
|
255
|
+
expect(uniform1f).toHaveBeenCalledWith('u_pixelRatio', 1.0);
|
|
256
|
+
expect(uniform2f).toHaveBeenCalledWith('u_resolution', 640, 360);
|
|
257
|
+
expect(onSdfBind).toHaveBeenCalledWith(sdfShaderProps);
|
|
258
|
+
});
|
|
259
|
+
});
|
|
260
|
+
|
|
261
|
+
describe('WebGlRenderer.canReuseRenderOp', () => {
|
|
262
|
+
it('reuses SDF text render ops with matching parent RTT dimensions', () => {
|
|
263
|
+
const renderer = Object.create(WebGlRenderer.prototype) as WebGlRenderer;
|
|
264
|
+
const node = makeSdfTextNode();
|
|
265
|
+
|
|
266
|
+
node.props.shader = {
|
|
267
|
+
shaderKey: 'default',
|
|
268
|
+
} as any;
|
|
269
|
+
|
|
270
|
+
renderer.curRenderOp = node;
|
|
271
|
+
|
|
272
|
+
expect(renderer.reuseRenderOp(node)).toBe(true);
|
|
273
|
+
});
|
|
274
|
+
});
|
|
@@ -33,6 +33,8 @@ import {
|
|
|
33
33
|
type UniformSet4Params,
|
|
34
34
|
} from './internal/ShaderUtils.js';
|
|
35
35
|
import { CoreNode } from '../../CoreNode.js';
|
|
36
|
+
import type { CoreTextNode } from '../../CoreTextNode.js';
|
|
37
|
+
import type { SdfShaderProps } from '../../shaders/webgl/SdfShader.js';
|
|
36
38
|
|
|
37
39
|
export class WebGlShaderProgram implements CoreShaderProgram {
|
|
38
40
|
protected program: WebGLProgram | null;
|
|
@@ -200,13 +202,11 @@ export class WebGlShaderProgram implements CoreShaderProgram {
|
|
|
200
202
|
}
|
|
201
203
|
|
|
202
204
|
bindRenderOp(renderOp: WebGlRenderOp) {
|
|
203
|
-
const isCoreNode = renderOp.isCoreNode;
|
|
204
|
-
|
|
205
205
|
this.bindTextures(renderOp.renderOpTextures);
|
|
206
206
|
this.bindBufferCollection(renderOp.quadBufferCollection);
|
|
207
207
|
|
|
208
208
|
const parentHasRenderTexture = renderOp.parentHasRenderTexture;
|
|
209
|
-
const framebufferDimensions = isCoreNode
|
|
209
|
+
const framebufferDimensions = renderOp.isCoreNode
|
|
210
210
|
? renderOp.parentFramebufferDimensions
|
|
211
211
|
: renderOp.framebufferDimensions;
|
|
212
212
|
|
|
@@ -248,11 +248,11 @@ export class WebGlShaderProgram implements CoreShaderProgram {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
/**temporary fix to make sdf texts work */
|
|
251
|
-
if (
|
|
252
|
-
const opShader = renderOp.shader
|
|
253
|
-
(opShader.shaderType as WebGlShaderType).onSdfBind?.call(
|
|
251
|
+
if (renderOp.isSdfRenderOp === true) {
|
|
252
|
+
const opShader = renderOp.shader!; // SdfRenderOp has .shader
|
|
253
|
+
(opShader.shaderType as WebGlShaderType<SdfShaderProps>).onSdfBind?.call(
|
|
254
254
|
this.glw,
|
|
255
|
-
renderOp.sdfShaderProps,
|
|
255
|
+
(renderOp as CoreTextNode).sdfShaderProps,
|
|
256
256
|
);
|
|
257
257
|
return;
|
|
258
258
|
}
|
|
@@ -136,7 +136,7 @@ export const getFontFamilies = (): FontFamilyMap => {
|
|
|
136
136
|
*/
|
|
137
137
|
export const init = (
|
|
138
138
|
c: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
139
|
-
mc
|
|
139
|
+
mc?: CanvasRenderingContext2D | OffscreenCanvasRenderingContext2D,
|
|
140
140
|
): void => {
|
|
141
141
|
if (initialized === true) {
|
|
142
142
|
return;
|
|
@@ -149,7 +149,7 @@ export const init = (
|
|
|
149
149
|
}
|
|
150
150
|
|
|
151
151
|
context = c;
|
|
152
|
-
measureContext = mc;
|
|
152
|
+
measureContext = mc || c;
|
|
153
153
|
|
|
154
154
|
// Register the default 'sans-serif' font face
|
|
155
155
|
const defaultMetrics: FontMetrics = {
|
|
@@ -19,15 +19,21 @@
|
|
|
19
19
|
|
|
20
20
|
import { assertTruthy } from '../../utils.js';
|
|
21
21
|
import type { Stage } from '../Stage.js';
|
|
22
|
-
import type {
|
|
22
|
+
import type {
|
|
23
|
+
CanvasRenderInfo,
|
|
24
|
+
FontHandler,
|
|
25
|
+
TextLineStruct,
|
|
26
|
+
TextRenderInfo,
|
|
27
|
+
} from './TextRenderer.js';
|
|
23
28
|
import * as CanvasFontHandler from './CanvasFontHandler.js';
|
|
24
29
|
import type { CoreTextNodeProps } from '../CoreTextNode.js';
|
|
25
|
-
import { hasZeroWidthSpace } from './Utils.js';
|
|
30
|
+
import { getLayoutCacheKey, hasZeroWidthSpace } from './Utils.js';
|
|
26
31
|
import { mapTextLayout } from './TextLayoutEngine.js';
|
|
27
32
|
|
|
28
33
|
const MAX_TEXTURE_DIMENSION = 4096;
|
|
29
34
|
|
|
30
35
|
const type = 'canvas' as const;
|
|
36
|
+
const font: FontHandler = CanvasFontHandler;
|
|
31
37
|
|
|
32
38
|
let canvas: HTMLCanvasElement | OffscreenCanvas | null = null;
|
|
33
39
|
let context:
|
|
@@ -43,16 +49,7 @@ let measureContext:
|
|
|
43
49
|
| null = null;
|
|
44
50
|
|
|
45
51
|
// Cache for text layout calculations
|
|
46
|
-
const
|
|
47
|
-
string,
|
|
48
|
-
{
|
|
49
|
-
lines: string[];
|
|
50
|
-
lineWidths: number[];
|
|
51
|
-
maxLineWidth: number;
|
|
52
|
-
remainingText: string;
|
|
53
|
-
moreTextLines: boolean;
|
|
54
|
-
}
|
|
55
|
-
>();
|
|
52
|
+
const renderInfoCache = new Map<string, CanvasRenderInfo>();
|
|
56
53
|
|
|
57
54
|
// Initialize the Text Renderer
|
|
58
55
|
const init = (stage: Stage): void => {
|
|
@@ -96,6 +93,12 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
|
|
|
96
93
|
assertTruthy(canvas, 'Canvas is not initialized');
|
|
97
94
|
assertTruthy(context, 'Canvas context is not available');
|
|
98
95
|
assertTruthy(measureContext, 'Canvas measureContext is not available');
|
|
96
|
+
const cacheKey = getLayoutCacheKey(props);
|
|
97
|
+
|
|
98
|
+
let layout = renderInfoCache.get(cacheKey);
|
|
99
|
+
if (layout !== undefined) {
|
|
100
|
+
return layout;
|
|
101
|
+
}
|
|
99
102
|
// Extract already normalized properties
|
|
100
103
|
const {
|
|
101
104
|
text,
|
|
@@ -189,46 +192,23 @@ const renderText = (props: CoreTextNodeProps): TextRenderInfo => {
|
|
|
189
192
|
if (canvas.width > 0 && canvas.height > 0) {
|
|
190
193
|
imageData = context.getImageData(0, 0, canvasW, canvasH);
|
|
191
194
|
}
|
|
192
|
-
|
|
195
|
+
const renderInfo = {
|
|
196
|
+
type,
|
|
193
197
|
imageData,
|
|
194
198
|
width: effectiveWidth,
|
|
195
199
|
height: effectiveHeight,
|
|
196
200
|
remainingLines,
|
|
197
201
|
hasRemainingText,
|
|
198
|
-
};
|
|
202
|
+
} as CanvasRenderInfo;
|
|
203
|
+
renderInfoCache.set(cacheKey, renderInfo);
|
|
204
|
+
return renderInfo;
|
|
199
205
|
};
|
|
200
206
|
|
|
201
|
-
/**
|
|
202
|
-
* Generate a cache key for text layout calculations
|
|
203
|
-
*/
|
|
204
|
-
function generateLayoutCacheKey(
|
|
205
|
-
text: string,
|
|
206
|
-
fontFamily: string,
|
|
207
|
-
fontSize: number,
|
|
208
|
-
fontStyle: string,
|
|
209
|
-
wordWrap: boolean,
|
|
210
|
-
wordWrapWidth: number,
|
|
211
|
-
letterSpacing: number,
|
|
212
|
-
maxLines: number,
|
|
213
|
-
overflowSuffix: string,
|
|
214
|
-
): string {
|
|
215
|
-
return `${text}-${fontFamily}-${fontSize}-${fontStyle}-${wordWrap}-${wordWrapWidth}-${letterSpacing}-${maxLines}-${overflowSuffix}`;
|
|
216
|
-
}
|
|
217
|
-
|
|
218
207
|
/**
|
|
219
208
|
* Clear layout cache for memory management
|
|
220
209
|
*/
|
|
221
|
-
const
|
|
222
|
-
|
|
223
|
-
};
|
|
224
|
-
|
|
225
|
-
/**
|
|
226
|
-
* Add quads for rendering (Canvas doesn't use quads)
|
|
227
|
-
*/
|
|
228
|
-
const addQuads = (): Float32Array | null => {
|
|
229
|
-
// Canvas renderer doesn't use quad-based rendering
|
|
230
|
-
// Return null for interface compatibility
|
|
231
|
-
return null;
|
|
210
|
+
const clearCache = (): void => {
|
|
211
|
+
renderInfoCache.clear();
|
|
232
212
|
};
|
|
233
213
|
|
|
234
214
|
/**
|
|
@@ -244,12 +224,11 @@ const renderQuads = (): void => {
|
|
|
244
224
|
*/
|
|
245
225
|
const CanvasTextRenderer = {
|
|
246
226
|
type,
|
|
247
|
-
font
|
|
227
|
+
font,
|
|
248
228
|
renderText,
|
|
249
|
-
addQuads,
|
|
250
229
|
renderQuads,
|
|
251
230
|
init,
|
|
252
|
-
|
|
231
|
+
clearCache,
|
|
253
232
|
};
|
|
254
233
|
|
|
255
234
|
export default CanvasTextRenderer;
|