@solidtv/renderer 1.3.2 → 1.4.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/CoreNode.d.ts +0 -2
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/Stage.js +1 -2
- package/dist/src/core/Stage.js.map +1 -1
- package/dist/src/core/lib/ImageWorker.d.ts +16 -1
- package/dist/src/core/lib/ImageWorker.js +52 -17
- package/dist/src/core/lib/ImageWorker.js.map +1 -1
- package/dist/src/core/lib/WebGlContextWrapper.d.ts +2 -32
- package/dist/src/core/lib/WebGlContextWrapper.js +26 -81
- package/dist/src/core/lib/WebGlContextWrapper.js.map +1 -1
- package/dist/src/core/renderers/CoreRenderer.d.ts +0 -1
- package/dist/src/core/renderers/CoreRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlRenderer.d.ts +1 -7
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +9 -13
- package/dist/src/core/renderers/webgl/WebGlRenderer.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderNode.d.ts +2 -3
- package/dist/src/core/renderers/webgl/WebGlShaderNode.js.map +1 -1
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.d.ts +0 -7
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js +3 -19
- package/dist/src/core/renderers/webgl/WebGlShaderProgram.js.map +1 -1
- package/dist/src/core/renderers/webgl/internal/RendererUtils.d.ts +0 -33
- package/dist/src/core/renderers/webgl/internal/RendererUtils.js +0 -50
- package/dist/src/core/renderers/webgl/internal/RendererUtils.js.map +1 -1
- package/dist/src/core/renderers/webgl/internal/ShaderUtils.d.ts +0 -1
- package/dist/src/core/renderers/webgl/internal/ShaderUtils.js.map +1 -1
- package/dist/src/core/text-rendering/CanvasFontHandler.js +0 -5
- package/dist/src/core/text-rendering/CanvasFontHandler.js.map +1 -1
- package/dist/src/core/text-rendering/SdfTextRenderer.js +23 -4
- package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
- package/dist/src/main-api/Inspector.d.ts +2 -1
- package/dist/src/main-api/Inspector.js +43 -15
- package/dist/src/main-api/Inspector.js.map +1 -1
- package/dist/src/main-api/Renderer.d.ts +0 -10
- package/dist/src/main-api/Renderer.js +1 -3
- package/dist/src/main-api/Renderer.js.map +1 -1
- package/dist/src/utils.d.ts +1 -1
- package/dist/src/utils.js +2 -2
- package/dist/src/utils.js.map +1 -1
- package/dist/tsconfig.dist.tsbuildinfo +1 -1
- package/package.json +1 -1
- package/src/core/CoreNode.ts +0 -3
- package/src/core/Stage.ts +0 -2
- package/src/core/lib/ImageWorker.test.ts +106 -0
- package/src/core/lib/ImageWorker.ts +60 -22
- package/src/core/lib/WebGlContextWrapper.ts +32 -101
- package/src/core/renderers/CoreRenderer.ts +0 -1
- package/src/core/renderers/webgl/WebGlRenderer.ts +9 -26
- package/src/core/renderers/webgl/WebGlShaderNode.ts +2 -3
- package/src/core/renderers/webgl/WebGlShaderProgram.ts +3 -20
- package/src/core/renderers/webgl/internal/RendererUtils.ts +0 -87
- package/src/core/renderers/webgl/internal/ShaderUtils.ts +0 -1
- package/src/core/text-rendering/CanvasFontHandler.ts +0 -6
- package/src/core/text-rendering/SdfTextRenderer.test.ts +17 -0
- package/src/core/text-rendering/SdfTextRenderer.ts +24 -4
- package/src/main-api/Inspector.ts +46 -16
- package/src/main-api/Renderer.ts +1 -14
- package/src/utils.ts +1 -2
- package/src/core/renderers/webgl/internal/WebGlUtils.ts +0 -16
|
@@ -131,3 +131,20 @@ describe('SdfTextRenderer layout cache', () => {
|
|
|
131
131
|
expect(SdfFontHandler.getFontData).toHaveBeenCalledTimes(0);
|
|
132
132
|
});
|
|
133
133
|
});
|
|
134
|
+
|
|
135
|
+
describe('SdfTextRenderer lazy shader compile', () => {
|
|
136
|
+
it('registers the SDF shader at init but defers compilation', () => {
|
|
137
|
+
const shManager = {
|
|
138
|
+
registerShaderType: vi.fn(),
|
|
139
|
+
createShader: vi.fn(() => ({})),
|
|
140
|
+
};
|
|
141
|
+
const fakeStage = { options: { textLayoutCacheSize: 10 }, shManager };
|
|
142
|
+
|
|
143
|
+
SdfTextRenderer.init(fakeStage as never);
|
|
144
|
+
|
|
145
|
+
// Registration is cheap and stays at boot; the expensive compile + link is
|
|
146
|
+
// deferred until the first SDF glyph actually renders (see getSdfShader).
|
|
147
|
+
expect(shManager.registerShaderType).toHaveBeenCalledTimes(1);
|
|
148
|
+
expect(shManager.createShader).not.toHaveBeenCalled();
|
|
149
|
+
});
|
|
150
|
+
});
|
|
@@ -38,9 +38,27 @@ const init = (stage: Stage): void => {
|
|
|
38
38
|
maxLayoutCacheSize = configuredCacheSize;
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
// Register SDF shader
|
|
41
|
+
// Register the SDF shader, but defer the (expensive) compile + link until the
|
|
42
|
+
// first SDF glyph actually renders — see getSdfShader / renderQuads. SDF fonts
|
|
43
|
+
// load asynchronously, so the first frame almost never needs the shader, and
|
|
44
|
+
// compiling it during Stage construction sits on the critical path to first
|
|
45
|
+
// paint for no benefit.
|
|
42
46
|
stage.shManager.registerShaderType('Sdf', Sdf);
|
|
43
|
-
sdfShader =
|
|
47
|
+
sdfShader = null;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
/**
|
|
51
|
+
* Lazily compile (and memoize) the SDF shader on first use.
|
|
52
|
+
*
|
|
53
|
+
* @remarks
|
|
54
|
+
* The shader program is compiled and linked the first time an SDF glyph is
|
|
55
|
+
* actually drawn, not at boot. Subsequent calls return the cached node.
|
|
56
|
+
*/
|
|
57
|
+
const getSdfShader = (stage: Stage): WebGlShaderNode => {
|
|
58
|
+
if (sdfShader === null) {
|
|
59
|
+
sdfShader = stage.shManager.createShader('Sdf') as WebGlShaderNode;
|
|
60
|
+
}
|
|
61
|
+
return sdfShader;
|
|
44
62
|
};
|
|
45
63
|
|
|
46
64
|
const font: FontHandler = SdfFontHandler;
|
|
@@ -141,6 +159,8 @@ const renderQuads = (
|
|
|
141
159
|
const webGlRenderer = renderer as WebGlRenderer;
|
|
142
160
|
const cache = renderProps.sdfCache;
|
|
143
161
|
const ctxTexture = atlasTexture.ctxTexture as WebGlCtxTexture;
|
|
162
|
+
// Compiles on the first real SDF draw; cheap memoized lookup thereafter.
|
|
163
|
+
const shader = getSdfShader(webGlRenderer.stage);
|
|
144
164
|
|
|
145
165
|
// --- Cache-hit fast path ------------------------------------------------
|
|
146
166
|
if (cache !== undefined && cache.vertices !== null) {
|
|
@@ -167,7 +187,7 @@ const renderQuads = (
|
|
|
167
187
|
layout.height,
|
|
168
188
|
renderProps.parentHasRenderTexture,
|
|
169
189
|
renderProps.framebufferDimensions,
|
|
170
|
-
|
|
190
|
+
shader,
|
|
171
191
|
);
|
|
172
192
|
return null;
|
|
173
193
|
}
|
|
@@ -189,7 +209,7 @@ const renderQuads = (
|
|
|
189
209
|
layout.height,
|
|
190
210
|
renderProps.parentHasRenderTexture,
|
|
191
211
|
renderProps.framebufferDimensions,
|
|
192
|
-
|
|
212
|
+
shader,
|
|
193
213
|
);
|
|
194
214
|
|
|
195
215
|
// Snapshot the written vertex data into the cache for future frames
|
|
@@ -230,6 +230,11 @@ const knownProperties = new Set<string>([
|
|
|
230
230
|
'parent',
|
|
231
231
|
'data',
|
|
232
232
|
'text',
|
|
233
|
+
'texture',
|
|
234
|
+
'rtt',
|
|
235
|
+
'mount',
|
|
236
|
+
'mountX',
|
|
237
|
+
'mountY',
|
|
233
238
|
'componentName',
|
|
234
239
|
'componentLocation',
|
|
235
240
|
]);
|
|
@@ -293,6 +298,10 @@ export class Inspector {
|
|
|
293
298
|
private scaleX = 1;
|
|
294
299
|
private scaleY = 1;
|
|
295
300
|
private textureMetrics = new Map<Texture, TextureMetrics>();
|
|
301
|
+
// Stable reference so the same function can be passed to both
|
|
302
|
+
// add/removeEventListener — `.bind()` returns a new fn each call, so binding
|
|
303
|
+
// inline in destroy() would never actually remove the listener.
|
|
304
|
+
private boundSetRootPosition = this.setRootPosition.bind(this);
|
|
296
305
|
|
|
297
306
|
// Performance monitoring for frequent setter calls
|
|
298
307
|
private static setterCallCount = new Map<
|
|
@@ -393,7 +402,7 @@ export class Inspector {
|
|
|
393
402
|
this.resizeObserver.observe(canvas);
|
|
394
403
|
|
|
395
404
|
//listen for changes on window
|
|
396
|
-
window.addEventListener('resize', this.
|
|
405
|
+
window.addEventListener('resize', this.boundSetRootPosition);
|
|
397
406
|
|
|
398
407
|
// Start animation stats timer if enabled
|
|
399
408
|
this.startAnimationStatsTimer();
|
|
@@ -1033,7 +1042,7 @@ export class Inspector {
|
|
|
1033
1042
|
});
|
|
1034
1043
|
coreNodeListeners.clear();
|
|
1035
1044
|
|
|
1036
|
-
this.destroyNode(node
|
|
1045
|
+
this.destroyNode(node);
|
|
1037
1046
|
originalDestroy.call(node, isChild);
|
|
1038
1047
|
},
|
|
1039
1048
|
configurable: true,
|
|
@@ -1151,7 +1160,7 @@ export class Inspector {
|
|
|
1151
1160
|
this.resizeObserver.disconnect();
|
|
1152
1161
|
|
|
1153
1162
|
// Remove resize listener
|
|
1154
|
-
window.removeEventListener('resize', this.
|
|
1163
|
+
window.removeEventListener('resize', this.boundSetRootPosition);
|
|
1155
1164
|
if (this.root && this.root.parentNode) {
|
|
1156
1165
|
this.root.remove();
|
|
1157
1166
|
}
|
|
@@ -1160,8 +1169,11 @@ export class Inspector {
|
|
|
1160
1169
|
Inspector.clearAnimationStats();
|
|
1161
1170
|
}
|
|
1162
1171
|
|
|
1163
|
-
destroyNode(
|
|
1164
|
-
|
|
1172
|
+
destroyNode(node: CoreNode) {
|
|
1173
|
+
// Use the mirror div stored on the node rather than a document-global
|
|
1174
|
+
// getElementById, which would collide across multiple renderer/inspector
|
|
1175
|
+
// instances sharing one document.
|
|
1176
|
+
const div = (node as CoreNode & { div?: HTMLElement }).div;
|
|
1165
1177
|
div?.remove();
|
|
1166
1178
|
}
|
|
1167
1179
|
|
|
@@ -1180,23 +1192,41 @@ export class Inspector {
|
|
|
1180
1192
|
* Special case for parent property
|
|
1181
1193
|
*/
|
|
1182
1194
|
if (property === 'parent') {
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
//
|
|
1186
|
-
//
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1195
|
+
// value is the parent CoreNode. Its mirror div is stored on the node
|
|
1196
|
+
// itself (see createNode/createTextNode), so use that reference instead
|
|
1197
|
+
// of a document-global getElementById — the latter collides across
|
|
1198
|
+
// multiple renderer/inspector instances sharing one document.
|
|
1199
|
+
//
|
|
1200
|
+
// The stage root (the only node with parent === null) is mirrored by
|
|
1201
|
+
// `this.root`, the container that is actually attached to the document.
|
|
1202
|
+
// createNodes() also makes a plain div for the root node, but that div is
|
|
1203
|
+
// never attached, so the root's direct children must be parented to
|
|
1204
|
+
// `this.root` instead — otherwise the whole mirror tree ends up detached
|
|
1205
|
+
// from the document. This replaces the old `parentId === 1` heuristic.
|
|
1206
|
+
const parentNode = value as CoreNode & { div?: HTMLElement };
|
|
1207
|
+
const parentDiv =
|
|
1208
|
+
parentNode.parent === null ? this.root : parentNode.div ?? this.root;
|
|
1209
|
+
parentDiv.appendChild(div);
|
|
1210
|
+
return;
|
|
1211
|
+
}
|
|
1191
1212
|
|
|
1192
|
-
|
|
1193
|
-
|
|
1213
|
+
// special case for mount changes — mount has no CSS equivalent, but it
|
|
1214
|
+
// shifts the node's left/top, so recompute position from the latest props.
|
|
1215
|
+
if (
|
|
1216
|
+
property === 'mount' ||
|
|
1217
|
+
property === 'mountX' ||
|
|
1218
|
+
property === 'mountY'
|
|
1219
|
+
) {
|
|
1220
|
+
div.style.left = `${props.x - props.w * (props.mountX ?? 0)}px`;
|
|
1221
|
+
div.style.top = `${props.y - props.h * (props.mountY ?? 0)}px`;
|
|
1194
1222
|
return;
|
|
1195
1223
|
}
|
|
1196
1224
|
|
|
1197
1225
|
// special case for text
|
|
1198
1226
|
if (property === 'text') {
|
|
1199
|
-
|
|
1227
|
+
// textContent (not innerHTML): text may contain <, &, etc. which would
|
|
1228
|
+
// corrupt the mirror tree or act as an injection vector via innerHTML.
|
|
1229
|
+
div.textContent = String(value);
|
|
1200
1230
|
|
|
1201
1231
|
// Keep DOM text invisible without breaking visibility checks
|
|
1202
1232
|
// Use very low opacity (0.001) instead of 0 so Playwright still detects it
|
package/src/main-api/Renderer.ts
CHANGED
|
@@ -514,17 +514,6 @@ export type RendererMainSettings = RendererRuntimeSettings & {
|
|
|
514
514
|
*/
|
|
515
515
|
textBaselineMode: TextBaselineMode;
|
|
516
516
|
|
|
517
|
-
/**
|
|
518
|
-
* Force WebGL2
|
|
519
|
-
*
|
|
520
|
-
* @remarks
|
|
521
|
-
* Force the renderer to use WebGL2. This can be used to force the renderer to
|
|
522
|
-
* use WebGL2 even if the browser supports WebGL1.
|
|
523
|
-
*
|
|
524
|
-
* @defaultValue `false`
|
|
525
|
-
*/
|
|
526
|
-
forceWebGL2: boolean;
|
|
527
|
-
|
|
528
517
|
/**
|
|
529
518
|
* Canvas object to use for rendering
|
|
530
519
|
*
|
|
@@ -699,7 +688,6 @@ export class RendererMain extends EventEmitter {
|
|
|
699
688
|
numImageWorkers:
|
|
700
689
|
settings.numImageWorkers !== undefined ? settings.numImageWorkers : 2,
|
|
701
690
|
enableContextSpy: settings.enableContextSpy ?? false,
|
|
702
|
-
forceWebGL2: settings.forceWebGL2 ?? false,
|
|
703
691
|
inspector: settings.inspector ?? false,
|
|
704
692
|
inspectorOptions: settings.inspectorOptions ?? {},
|
|
705
693
|
renderEngine: settings.renderEngine,
|
|
@@ -761,7 +749,6 @@ export class RendererMain extends EventEmitter {
|
|
|
761
749
|
deviceLogicalPixelRatio,
|
|
762
750
|
devicePhysicalPixelRatio,
|
|
763
751
|
enableContextSpy: settings.enableContextSpy!,
|
|
764
|
-
forceWebGL2: settings.forceWebGL2!,
|
|
765
752
|
fpsUpdateInterval: settings.fpsUpdateInterval!,
|
|
766
753
|
enableClear: settings.enableClear!,
|
|
767
754
|
numImageWorkers: settings.numImageWorkers!,
|
|
@@ -941,7 +928,7 @@ export class RendererMain extends EventEmitter {
|
|
|
941
928
|
*/
|
|
942
929
|
destroyNode(node: INode) {
|
|
943
930
|
if (ENABLE_INSPECTOR && this.inspector) {
|
|
944
|
-
this.inspector.destroyNode(node
|
|
931
|
+
this.inspector.destroyNode(node as unknown as CoreNode);
|
|
945
932
|
}
|
|
946
933
|
|
|
947
934
|
return node.destroy();
|
package/src/utils.ts
CHANGED
|
@@ -2,7 +2,6 @@ import type { ContextSpy } from './core/lib/ContextSpy.js';
|
|
|
2
2
|
|
|
3
3
|
export function createWebGLContext(
|
|
4
4
|
canvas: HTMLCanvasElement | OffscreenCanvas,
|
|
5
|
-
forceWebGL2 = false,
|
|
6
5
|
contextSpy: ContextSpy | null,
|
|
7
6
|
): WebGLRenderingContext {
|
|
8
7
|
const config: WebGLContextAttributes = {
|
|
@@ -20,7 +19,7 @@ export function createWebGLContext(
|
|
|
20
19
|
const gl =
|
|
21
20
|
// TODO: Remove this assertion once this issue is fixed in TypeScript
|
|
22
21
|
// https://github.com/microsoft/TypeScript/issues/53614
|
|
23
|
-
(canvas.getContext(
|
|
22
|
+
(canvas.getContext('webgl', config) ||
|
|
24
23
|
canvas.getContext(
|
|
25
24
|
'experimental-webgl' as 'webgl',
|
|
26
25
|
config,
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* Generic WebGL Utility Functions
|
|
3
|
-
*
|
|
4
|
-
* @remarks
|
|
5
|
-
* Nothing here should be coupled to Renderer logic / types.
|
|
6
|
-
*
|
|
7
|
-
* @param gl
|
|
8
|
-
* @returns
|
|
9
|
-
*/
|
|
10
|
-
export function isWebGl2(
|
|
11
|
-
gl: WebGLRenderingContext,
|
|
12
|
-
): gl is WebGL2RenderingContext {
|
|
13
|
-
return (
|
|
14
|
-
self.WebGL2RenderingContext && gl instanceof self.WebGL2RenderingContext
|
|
15
|
-
);
|
|
16
|
-
}
|