@solidtv/renderer 1.3.2 → 1.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/src/core/CoreNode.d.ts +0 -2
- package/dist/src/core/CoreNode.js +4 -1
- package/dist/src/core/CoreNode.js.map +1 -1
- package/dist/src/core/CoreShaderManager.js +1 -1
- package/dist/src/core/CoreShaderManager.js.map +1 -1
- package/dist/src/core/Stage.js +11 -5
- 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 +2 -8
- package/dist/src/core/renderers/webgl/WebGlRenderer.js +21 -24
- 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.d.ts +1 -1
- package/dist/src/core/text-rendering/SdfTextRenderer.js +48 -17
- package/dist/src/core/text-rendering/SdfTextRenderer.js.map +1 -1
- package/dist/src/core/text-rendering/TextRenderer.d.ts +23 -13
- package/dist/src/core/text-rendering/TextRenderer.js +16 -1
- package/dist/src/core/text-rendering/TextRenderer.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 +6 -4
- package/src/core/CoreShaderManager.ts +1 -1
- package/src/core/Stage.ts +14 -5
- 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 +22 -38
- 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 +51 -19
- package/src/core/text-rendering/TextRenderer.ts +23 -13
- 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
|
@@ -6,13 +6,12 @@ import type {
|
|
|
6
6
|
Vec3,
|
|
7
7
|
Vec4,
|
|
8
8
|
} from '../renderers/webgl/internal/ShaderUtils.js';
|
|
9
|
-
import { isWebGl2 } from '../renderers/webgl/internal/WebGlUtils.js';
|
|
10
9
|
|
|
11
10
|
/**
|
|
12
11
|
* Optimized WebGL Context Wrapper
|
|
13
12
|
*
|
|
14
13
|
* @remarks
|
|
15
|
-
* This class contains the subset of the WebGLRenderingContext
|
|
14
|
+
* This class contains the subset of the WebGLRenderingContext
|
|
16
15
|
* API that is used by the renderer. Select high volume WebGL methods include
|
|
17
16
|
* caching optimizations to avoid making WebGL calls if the state is already set
|
|
18
17
|
* to the desired value.
|
|
@@ -94,56 +93,40 @@ export class WebGlContextWrapper {
|
|
|
94
93
|
public readonly INVALID_OPERATION: number;
|
|
95
94
|
//#endregion WebGL Enums
|
|
96
95
|
|
|
97
|
-
constructor(private gl: WebGLRenderingContext
|
|
98
|
-
//
|
|
99
|
-
//
|
|
100
|
-
//
|
|
101
|
-
//
|
|
102
|
-
//
|
|
103
|
-
//
|
|
104
|
-
this.activeTextureUnit =
|
|
105
|
-
(gl.getParameter(gl.ACTIVE_TEXTURE) as number) - gl.TEXTURE0;
|
|
96
|
+
constructor(private gl: WebGLRenderingContext) {
|
|
97
|
+
// A freshly created WebGL context is in a fully specified default state.
|
|
98
|
+
// Rather than reading that state back with getParameter/isEnabled — each a
|
|
99
|
+
// synchronous CPU<->GPU round-trip, and previously ~one per texture unit
|
|
100
|
+
// (16-32) just to enumerate the bindings — we seed the cache with those
|
|
101
|
+
// known defaults. The engine never restores a lost context in place (it
|
|
102
|
+
// reloads the app on `contextLost`), so there is no live state to recover.
|
|
103
|
+
this.activeTextureUnit = 0;
|
|
106
104
|
const maxTextureUnits = gl.getParameter(
|
|
107
105
|
gl.MAX_TEXTURE_IMAGE_UNITS,
|
|
108
106
|
) as number;
|
|
109
|
-
//
|
|
110
|
-
this.texture2dUnits = new Array<
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
this.
|
|
118
|
-
this.
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
this.
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
this.
|
|
130
|
-
|
|
131
|
-
this.
|
|
132
|
-
this.blendSrcRgb = gl.getParameter(gl.BLEND_SRC_RGB) as number;
|
|
133
|
-
this.blendDstRgb = gl.getParameter(gl.BLEND_DST_RGB) as number;
|
|
134
|
-
this.blendSrcAlpha = gl.getParameter(gl.BLEND_SRC_ALPHA) as number;
|
|
135
|
-
this.blendDstAlpha = gl.getParameter(gl.BLEND_DST_ALPHA) as number;
|
|
136
|
-
|
|
137
|
-
this.boundArrayBuffer = gl.getParameter(
|
|
138
|
-
gl.ARRAY_BUFFER_BINDING,
|
|
139
|
-
) as WebGLBuffer;
|
|
140
|
-
this.boundElementArrayBuffer = gl.getParameter(
|
|
141
|
-
gl.ELEMENT_ARRAY_BUFFER_BINDING,
|
|
142
|
-
) as WebGLBuffer;
|
|
143
|
-
|
|
144
|
-
this.curProgram = gl.getParameter(
|
|
145
|
-
gl.CURRENT_PROGRAM,
|
|
146
|
-
) as WebGLProgram | null;
|
|
107
|
+
// All texture units start unbound (TEXTURE_BINDING_2D === null).
|
|
108
|
+
this.texture2dUnits = new Array<WebGLTexture | null>(maxTextureUnits).fill(
|
|
109
|
+
null,
|
|
110
|
+
);
|
|
111
|
+
|
|
112
|
+
// SCISSOR_TEST disabled; SCISSOR_BOX defaults to the full drawing buffer.
|
|
113
|
+
this.scissorEnabled = false;
|
|
114
|
+
this.scissorX = 0;
|
|
115
|
+
this.scissorY = 0;
|
|
116
|
+
this.scissorWidth = gl.drawingBufferWidth;
|
|
117
|
+
this.scissorHeight = gl.drawingBufferHeight;
|
|
118
|
+
|
|
119
|
+
// BLEND disabled; blend func defaults to (ONE, ZERO) for both RGB and alpha.
|
|
120
|
+
this.blendEnabled = false;
|
|
121
|
+
this.blendSrcRgb = gl.ONE;
|
|
122
|
+
this.blendDstRgb = gl.ZERO;
|
|
123
|
+
this.blendSrcAlpha = gl.ONE;
|
|
124
|
+
this.blendDstAlpha = gl.ZERO;
|
|
125
|
+
|
|
126
|
+
// No buffers or program bound on a fresh context.
|
|
127
|
+
this.boundArrayBuffer = null;
|
|
128
|
+
this.boundElementArrayBuffer = null;
|
|
129
|
+
this.curProgram = null;
|
|
147
130
|
|
|
148
131
|
this.canvas = gl.canvas;
|
|
149
132
|
|
|
@@ -175,9 +158,6 @@ export class WebGlContextWrapper {
|
|
|
175
158
|
this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT;
|
|
176
159
|
this.ONE = gl.ONE;
|
|
177
160
|
this.ONE_MINUS_SRC_ALPHA = gl.ONE_MINUS_SRC_ALPHA;
|
|
178
|
-
this.MAX_VERTEX_TEXTURE_IMAGE_UNITS = gl.MAX_VERTEX_TEXTURE_IMAGE_UNITS;
|
|
179
|
-
this.TRIANGLES = gl.TRIANGLES;
|
|
180
|
-
this.UNSIGNED_SHORT = gl.UNSIGNED_SHORT;
|
|
181
161
|
this.VERTEX_SHADER = gl.VERTEX_SHADER;
|
|
182
162
|
this.FRAGMENT_SHADER = gl.FRAGMENT_SHADER;
|
|
183
163
|
this.STATIC_DRAW = gl.STATIC_DRAW;
|
|
@@ -188,15 +168,6 @@ export class WebGlContextWrapper {
|
|
|
188
168
|
this.INVALID_ENUM = gl.INVALID_ENUM;
|
|
189
169
|
this.INVALID_OPERATION = gl.INVALID_OPERATION;
|
|
190
170
|
}
|
|
191
|
-
/**
|
|
192
|
-
* Returns true if the WebGL context is WebGL2
|
|
193
|
-
*
|
|
194
|
-
* @returns
|
|
195
|
-
*/
|
|
196
|
-
isWebGl2() {
|
|
197
|
-
return isWebGl2(this.gl);
|
|
198
|
-
}
|
|
199
|
-
|
|
200
171
|
/**
|
|
201
172
|
* ```
|
|
202
173
|
* gl.activeTexture(textureUnit + gl.TEXTURE0);
|
|
@@ -1109,33 +1080,6 @@ export class WebGlContextWrapper {
|
|
|
1109
1080
|
return this.gl.getError();
|
|
1110
1081
|
}
|
|
1111
1082
|
|
|
1112
|
-
/**
|
|
1113
|
-
* ```
|
|
1114
|
-
* gl.createVertexArray();
|
|
1115
|
-
* ```
|
|
1116
|
-
*
|
|
1117
|
-
* @returns
|
|
1118
|
-
*/
|
|
1119
|
-
createVertexArray() {
|
|
1120
|
-
if (this.gl instanceof WebGL2RenderingContext) {
|
|
1121
|
-
return this.gl.createVertexArray();
|
|
1122
|
-
}
|
|
1123
|
-
return undefined;
|
|
1124
|
-
}
|
|
1125
|
-
|
|
1126
|
-
/**
|
|
1127
|
-
* ```
|
|
1128
|
-
* gl.bindVertexArray(vertexArray);
|
|
1129
|
-
* ```
|
|
1130
|
-
*
|
|
1131
|
-
* @param vertexArray
|
|
1132
|
-
*/
|
|
1133
|
-
bindVertexArray(vertexArray: WebGLVertexArrayObject | null) {
|
|
1134
|
-
if (this.gl instanceof WebGL2RenderingContext) {
|
|
1135
|
-
this.gl.bindVertexArray(vertexArray);
|
|
1136
|
-
}
|
|
1137
|
-
}
|
|
1138
|
-
|
|
1139
1083
|
/**
|
|
1140
1084
|
* ```
|
|
1141
1085
|
* gl.getAttribLocation(program, name);
|
|
@@ -1341,19 +1285,6 @@ export class WebGlContextWrapper {
|
|
|
1341
1285
|
}
|
|
1342
1286
|
}
|
|
1343
1287
|
|
|
1344
|
-
/**
|
|
1345
|
-
* ```
|
|
1346
|
-
* gl.deleteVertexArray(vertexArray);
|
|
1347
|
-
* ```
|
|
1348
|
-
*
|
|
1349
|
-
* @param vertexArray - The vertex array object to delete
|
|
1350
|
-
*/
|
|
1351
|
-
deleteVertexArray(vertexArray: WebGLVertexArrayObject) {
|
|
1352
|
-
if (this.isWebGl2()) {
|
|
1353
|
-
(this.gl as WebGL2RenderingContext).deleteVertexArray(vertexArray);
|
|
1354
|
-
}
|
|
1355
|
-
}
|
|
1356
|
-
|
|
1357
1288
|
/**
|
|
1358
1289
|
* Check for WebGL errors and return error information
|
|
1359
1290
|
* @param operation Description of the operation for error reporting
|
|
@@ -14,10 +14,6 @@ import { SdfRenderOp } from './SdfRenderOp.js';
|
|
|
14
14
|
import type { CoreContextTexture } from '../CoreContextTexture.js';
|
|
15
15
|
import {
|
|
16
16
|
createIndexBuffer,
|
|
17
|
-
type CoreWebGlParameters,
|
|
18
|
-
type CoreWebGlExtensions,
|
|
19
|
-
getWebGlParameters,
|
|
20
|
-
getWebGlExtensions,
|
|
21
17
|
type WebGlColor,
|
|
22
18
|
} from './internal/RendererUtils.js';
|
|
23
19
|
import { WebGlCtxTexture } from './WebGlCtxTexture.js';
|
|
@@ -51,17 +47,11 @@ const GL_OUT_OF_MEMORY = 0x0505;
|
|
|
51
47
|
*/
|
|
52
48
|
const MAX_DRAINED_GL_ERRORS = 8;
|
|
53
49
|
|
|
54
|
-
interface CoreWebGlSystem {
|
|
55
|
-
parameters: CoreWebGlParameters;
|
|
56
|
-
extensions: CoreWebGlExtensions;
|
|
57
|
-
}
|
|
58
|
-
|
|
59
50
|
export type WebGlRenderOp = CoreNode | SdfRenderOp;
|
|
60
51
|
|
|
61
52
|
export class WebGlRenderer extends CoreRenderer {
|
|
62
53
|
//// WebGL Native Context and Data
|
|
63
54
|
glw: WebGlContextWrapper;
|
|
64
|
-
system: CoreWebGlSystem;
|
|
65
55
|
|
|
66
56
|
//// Persistent data
|
|
67
57
|
quadBuffer: ArrayBuffer;
|
|
@@ -169,17 +159,20 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
169
159
|
constructor(options: WebGlRendererOptions) {
|
|
170
160
|
super(options);
|
|
171
161
|
|
|
162
|
+
// CPU-side vertex buffers, reused every frame. Their GL buffers and
|
|
163
|
+
// BufferCollections are wired up below, once the GL context exists.
|
|
172
164
|
this.quadBuffer = new ArrayBuffer(this.stage.options.quadBufferSize);
|
|
173
165
|
this.fQuadBuffer = new Float32Array(this.quadBuffer);
|
|
174
166
|
this.uiQuadBuffer = new Uint32Array(this.quadBuffer);
|
|
175
167
|
|
|
168
|
+
// Shared SDF vertex buffer: 512 KB for ~3600 glyphs.
|
|
169
|
+
this.sdfBuffer = new ArrayBuffer(512 * 1024);
|
|
170
|
+
this.fSdfBuffer = new Float32Array(this.sdfBuffer);
|
|
171
|
+
this.uiSdfBuffer = new Uint32Array(this.sdfBuffer);
|
|
172
|
+
|
|
176
173
|
this.mode = 'webgl';
|
|
177
174
|
|
|
178
|
-
const gl = createWebGLContext(
|
|
179
|
-
options.canvas,
|
|
180
|
-
options.forceWebGL2,
|
|
181
|
-
options.contextSpy,
|
|
182
|
-
);
|
|
175
|
+
const gl = createWebGLContext(options.canvas, options.contextSpy);
|
|
183
176
|
const glw = (this.glw = new WebGlContextWrapper(gl));
|
|
184
177
|
glw.viewport(0, 0, options.canvas.width, options.canvas.height);
|
|
185
178
|
|
|
@@ -192,11 +185,6 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
192
185
|
|
|
193
186
|
createIndexBuffer(glw, this.stage.bufferMemory);
|
|
194
187
|
|
|
195
|
-
this.system = {
|
|
196
|
-
parameters: getWebGlParameters(this.glw),
|
|
197
|
-
extensions: getWebGlExtensions(this.glw),
|
|
198
|
-
};
|
|
199
|
-
|
|
200
188
|
// Create the static node coords buffer
|
|
201
189
|
// 80 is the magic number used in createIndexBuffer
|
|
202
190
|
// @see RendererUtils.ts
|
|
@@ -261,13 +249,8 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
261
249
|
},
|
|
262
250
|
},
|
|
263
251
|
]);
|
|
264
|
-
// --- Shared SDF buffer ---------------------------------------------------
|
|
265
|
-
// Allocate 512 KB for SDF vertex data (~3600 glyphs).
|
|
266
|
-
const sdfBufSize = 512 * 1024;
|
|
267
|
-
this.sdfBuffer = new ArrayBuffer(sdfBufSize);
|
|
268
|
-
this.fSdfBuffer = new Float32Array(this.sdfBuffer);
|
|
269
|
-
this.uiSdfBuffer = new Uint32Array(this.sdfBuffer);
|
|
270
252
|
|
|
253
|
+
// --- Shared SDF buffer collection (CPU buffer allocated above) ----------
|
|
271
254
|
const sdfWebGlBuffer = glw.createBuffer();
|
|
272
255
|
const sdfStride = 6 * Float32Array.BYTES_PER_ELEMENT; // 24 bytes
|
|
273
256
|
this.sdfQuadBufferCollection = new BufferCollection([
|
|
@@ -661,7 +644,8 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
661
644
|
* SdfRenderOp, resulting in one draw call for many text nodes.
|
|
662
645
|
*/
|
|
663
646
|
addSdfQuads(
|
|
664
|
-
glyphs:
|
|
647
|
+
glyphs: Float32Array,
|
|
648
|
+
glyphCount: number,
|
|
665
649
|
fontScale: number,
|
|
666
650
|
transform: Float32Array,
|
|
667
651
|
color: number,
|
|
@@ -677,7 +661,6 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
677
661
|
| null,
|
|
678
662
|
sdfShader: WebGlShaderNode,
|
|
679
663
|
): void {
|
|
680
|
-
const glyphCount = glyphs.length;
|
|
681
664
|
if (glyphCount === 0) {
|
|
682
665
|
return;
|
|
683
666
|
}
|
|
@@ -713,20 +696,21 @@ export class WebGlRenderer extends CoreRenderer {
|
|
|
713
696
|
// Record start quad for this batch segment
|
|
714
697
|
const startQuad = this.sdfQuadCount;
|
|
715
698
|
|
|
699
|
+
// Read packed glyph fields directly from the Float32Array (stride = 8).
|
|
700
|
+
let go = 0;
|
|
716
701
|
for (let gi = 0; gi < glyphCount; gi++) {
|
|
717
|
-
const glyph = glyphs[gi]!;
|
|
718
|
-
|
|
719
702
|
// Glyph corners in design units
|
|
720
|
-
const gx1 =
|
|
721
|
-
const gy1 =
|
|
722
|
-
const gx2 = gx1 +
|
|
723
|
-
const gy2 = gy1 +
|
|
703
|
+
const gx1 = glyphs[go]!;
|
|
704
|
+
const gy1 = glyphs[go + 1]!;
|
|
705
|
+
const gx2 = gx1 + glyphs[go + 2]!;
|
|
706
|
+
const gy2 = gy1 + glyphs[go + 3]!;
|
|
724
707
|
|
|
725
708
|
// Atlas UVs
|
|
726
|
-
const u1 =
|
|
727
|
-
const v1 =
|
|
728
|
-
const u2 = u1 +
|
|
729
|
-
const v2 = v1 +
|
|
709
|
+
const u1 = glyphs[go + 4]!;
|
|
710
|
+
const v1 = glyphs[go + 5]!;
|
|
711
|
+
const u2 = u1 + glyphs[go + 6]!;
|
|
712
|
+
const v2 = v1 + glyphs[go + 7]!;
|
|
713
|
+
go += 8;
|
|
730
714
|
|
|
731
715
|
// Transform to world space
|
|
732
716
|
// Note: we use gx/y directly since m0,m1,m3,m4 are already pre-scaled
|
|
@@ -21,11 +21,11 @@ export type ShaderSource<T> =
|
|
|
21
21
|
export type WebGlShaderType<T extends object = Record<string, unknown>> =
|
|
22
22
|
CoreShaderType<T> & {
|
|
23
23
|
/**
|
|
24
|
-
* fragment shader source for WebGl
|
|
24
|
+
* fragment shader source for WebGl
|
|
25
25
|
*/
|
|
26
26
|
fragment: ShaderSource<T>;
|
|
27
27
|
/**
|
|
28
|
-
* vertex shader source for WebGl
|
|
28
|
+
* vertex shader source for WebGl
|
|
29
29
|
*/
|
|
30
30
|
vertex?: ShaderSource<T>;
|
|
31
31
|
/**
|
|
@@ -43,7 +43,6 @@ export type WebGlShaderType<T extends object = Record<string, unknown>> =
|
|
|
43
43
|
* extensions required for specific shader?
|
|
44
44
|
*/
|
|
45
45
|
webgl1Extensions?: string[];
|
|
46
|
-
webgl2Extensions?: string[];
|
|
47
46
|
supportsIndexedTextures?: boolean;
|
|
48
47
|
};
|
|
49
48
|
|
|
@@ -19,13 +19,6 @@ import { CoreNode } from '../../CoreNode.js';
|
|
|
19
19
|
|
|
20
20
|
export class WebGlShaderProgram implements CoreShaderProgram {
|
|
21
21
|
protected program: WebGLProgram | null;
|
|
22
|
-
/**
|
|
23
|
-
* Vertex Array Object
|
|
24
|
-
*
|
|
25
|
-
* @remarks
|
|
26
|
-
* Used by WebGL2 Only
|
|
27
|
-
*/
|
|
28
|
-
protected vao: WebGLVertexArrayObject | undefined;
|
|
29
22
|
protected renderer: WebGlRenderer;
|
|
30
23
|
protected glw: WebGlContextWrapper;
|
|
31
24
|
protected attributeLocations: string[];
|
|
@@ -45,22 +38,15 @@ export class WebGlShaderProgram implements CoreShaderProgram {
|
|
|
45
38
|
this.renderer = renderer;
|
|
46
39
|
const glw = (this.glw = renderer.glw);
|
|
47
40
|
|
|
48
|
-
// Check that extensions are supported
|
|
49
|
-
const webGl2 = glw.isWebGl2();
|
|
50
|
-
let requiredExtensions: string[] = [];
|
|
51
|
-
|
|
52
41
|
this.supportsIndexedTextures =
|
|
53
42
|
config.supportsIndexedTextures || this.supportsIndexedTextures;
|
|
54
|
-
requiredExtensions =
|
|
55
|
-
(webGl2 && config.webgl2Extensions) ||
|
|
56
|
-
(!webGl2 && config.webgl1Extensions) ||
|
|
57
|
-
[];
|
|
58
43
|
|
|
59
|
-
|
|
44
|
+
// Check that required extensions are supported
|
|
45
|
+
const requiredExtensions = config.webgl1Extensions || [];
|
|
60
46
|
requiredExtensions.forEach((extensionName) => {
|
|
61
47
|
if (!glw.getExtension(extensionName)) {
|
|
62
48
|
throw new Error(
|
|
63
|
-
`Shader "${this.constructor.name}" requires extension "${extensionName}" for WebGL
|
|
49
|
+
`Shader "${this.constructor.name}" requires extension "${extensionName}" for WebGL 1.0 but wasn't found`,
|
|
64
50
|
);
|
|
65
51
|
}
|
|
66
52
|
});
|
|
@@ -310,9 +296,6 @@ export class WebGlShaderProgram implements CoreShaderProgram {
|
|
|
310
296
|
return;
|
|
311
297
|
}
|
|
312
298
|
this.glw.useProgram(this.program, this.uniformLocations!);
|
|
313
|
-
if (this.glw.isWebGl2() && this.vao) {
|
|
314
|
-
this.glw.bindVertexArray(this.vao);
|
|
315
|
-
}
|
|
316
299
|
}
|
|
317
300
|
|
|
318
301
|
detach(): void {
|
|
@@ -1,92 +1,5 @@
|
|
|
1
1
|
import type { WebGlContextWrapper } from '../../../lib/WebGlContextWrapper.js';
|
|
2
2
|
|
|
3
|
-
export interface CoreWebGlParameters {
|
|
4
|
-
MAX_RENDERBUFFER_SIZE: number;
|
|
5
|
-
MAX_TEXTURE_SIZE: number;
|
|
6
|
-
MAX_VIEWPORT_DIMS: Int32Array;
|
|
7
|
-
MAX_VERTEX_TEXTURE_IMAGE_UNITS: number;
|
|
8
|
-
MAX_TEXTURE_IMAGE_UNITS: number;
|
|
9
|
-
MAX_COMBINED_TEXTURE_IMAGE_UNITS: number;
|
|
10
|
-
MAX_VERTEX_ATTRIBS: number;
|
|
11
|
-
MAX_VARYING_VECTORS: number;
|
|
12
|
-
MAX_VERTEX_UNIFORM_VECTORS: number;
|
|
13
|
-
MAX_FRAGMENT_UNIFORM_VECTORS: number;
|
|
14
|
-
}
|
|
15
|
-
|
|
16
|
-
/**
|
|
17
|
-
* Get device specific webgl parameters
|
|
18
|
-
* @param glw
|
|
19
|
-
*/
|
|
20
|
-
export function getWebGlParameters(
|
|
21
|
-
glw: WebGlContextWrapper,
|
|
22
|
-
): CoreWebGlParameters {
|
|
23
|
-
const params: CoreWebGlParameters = {
|
|
24
|
-
MAX_RENDERBUFFER_SIZE: 0,
|
|
25
|
-
MAX_TEXTURE_SIZE: 0,
|
|
26
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment, @typescript-eslint/no-explicit-any
|
|
27
|
-
MAX_VIEWPORT_DIMS: 0 as any, // Code below will replace this with an Int32Array
|
|
28
|
-
MAX_VERTEX_TEXTURE_IMAGE_UNITS: 0,
|
|
29
|
-
MAX_TEXTURE_IMAGE_UNITS: 0,
|
|
30
|
-
MAX_COMBINED_TEXTURE_IMAGE_UNITS: 0,
|
|
31
|
-
MAX_VERTEX_ATTRIBS: 0,
|
|
32
|
-
MAX_VARYING_VECTORS: 0,
|
|
33
|
-
MAX_VERTEX_UNIFORM_VECTORS: 0,
|
|
34
|
-
MAX_FRAGMENT_UNIFORM_VECTORS: 0,
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
// Map over all parameters and get them
|
|
38
|
-
const keys = Object.keys(params) as Array<keyof CoreWebGlParameters>;
|
|
39
|
-
keys.forEach((key) => {
|
|
40
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
41
|
-
params[key] = glw.getParameter(glw[key]);
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
return params;
|
|
45
|
-
}
|
|
46
|
-
|
|
47
|
-
export interface CoreWebGlExtensions {
|
|
48
|
-
ANGLE_instanced_arrays: ANGLE_instanced_arrays | null;
|
|
49
|
-
WEBGL_compressed_texture_s3tc: WEBGL_compressed_texture_s3tc | null;
|
|
50
|
-
WEBGL_compressed_texture_astc: WEBGL_compressed_texture_astc | null;
|
|
51
|
-
WEBGL_compressed_texture_etc: WEBGL_compressed_texture_etc | null;
|
|
52
|
-
WEBGL_compressed_texture_etc1: WEBGL_compressed_texture_etc1 | null;
|
|
53
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
54
|
-
WEBGL_compressed_texture_pvrtc: any | null;
|
|
55
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
-
WEBKIT_WEBGL_compressed_texture_pvrtc: any | null;
|
|
57
|
-
WEBGL_compressed_texture_s3tc_srgb: WEBGL_compressed_texture_s3tc_srgb | null;
|
|
58
|
-
OES_vertex_array_object: OES_vertex_array_object | null;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
/**
|
|
62
|
-
* Get device webgl extensions
|
|
63
|
-
* @param glw
|
|
64
|
-
*/
|
|
65
|
-
export function getWebGlExtensions(
|
|
66
|
-
glw: WebGlContextWrapper,
|
|
67
|
-
): CoreWebGlExtensions {
|
|
68
|
-
const extensions: CoreWebGlExtensions = {
|
|
69
|
-
ANGLE_instanced_arrays: null,
|
|
70
|
-
WEBGL_compressed_texture_s3tc: null,
|
|
71
|
-
WEBGL_compressed_texture_astc: null,
|
|
72
|
-
WEBGL_compressed_texture_etc: null,
|
|
73
|
-
WEBGL_compressed_texture_etc1: null,
|
|
74
|
-
WEBGL_compressed_texture_pvrtc: null,
|
|
75
|
-
WEBKIT_WEBGL_compressed_texture_pvrtc: null,
|
|
76
|
-
WEBGL_compressed_texture_s3tc_srgb: null,
|
|
77
|
-
OES_vertex_array_object: null,
|
|
78
|
-
};
|
|
79
|
-
|
|
80
|
-
// Map over all extensions and get them
|
|
81
|
-
const keys = Object.keys(extensions) as Array<keyof CoreWebGlExtensions>;
|
|
82
|
-
keys.forEach((key) => {
|
|
83
|
-
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
|
84
|
-
extensions[key] = glw.getExtension(key);
|
|
85
|
-
});
|
|
86
|
-
|
|
87
|
-
return extensions;
|
|
88
|
-
}
|
|
89
|
-
|
|
90
3
|
/**
|
|
91
4
|
* Allocate big memory chunk that we
|
|
92
5
|
* can re-use to draw quads
|
|
@@ -19,12 +19,6 @@ interface CanvasFont {
|
|
|
19
19
|
metrics?: FontMetrics;
|
|
20
20
|
}
|
|
21
21
|
|
|
22
|
-
/**
|
|
23
|
-
* Global font set regardless of if run in the main thread or a web worker
|
|
24
|
-
*/
|
|
25
|
-
// const globalFontSet: FontFaceSet = (resolvedGlobal.document?.fonts ||
|
|
26
|
-
// (resolvedGlobal as unknown as { fonts: FontFaceSet }).fonts) as FontFaceSet;
|
|
27
|
-
|
|
28
22
|
// Global state variables for fontHandler
|
|
29
23
|
const fontFamilies: Record<string, FontFace> = {};
|
|
30
24
|
const fontLoadPromises = new Map<string, Promise<void>>();
|
|
@@ -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
|
+
});
|
|
@@ -14,7 +14,7 @@ import { Sdf } from '../shaders/webgl/SdfShader.js';
|
|
|
14
14
|
import type { WebGlCtxTexture } from '../renderers/webgl/WebGlCtxTexture.js';
|
|
15
15
|
import type { WebGlShaderNode } from '../renderers/webgl/WebGlShaderNode.js';
|
|
16
16
|
import { isProductionEnvironment } from '../../utils.js';
|
|
17
|
-
import
|
|
17
|
+
import { SDF_GLYPH_STRIDE, type TextLayout } from './TextRenderer.js';
|
|
18
18
|
import { mapTextLayout } from './TextLayoutEngine.js';
|
|
19
19
|
import type { RectWithValid } from '../lib/utils.js';
|
|
20
20
|
import type { Dimensions } from '../../common/CommonTypes.js';
|
|
@@ -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
|
}
|
|
@@ -177,6 +197,7 @@ const renderQuads = (
|
|
|
177
197
|
const startIdx = webGlRenderer.sdfBufferIdx;
|
|
178
198
|
webGlRenderer.addSdfQuads(
|
|
179
199
|
layout.glyphs,
|
|
200
|
+
layout.glyphCount,
|
|
180
201
|
layout.fontScale,
|
|
181
202
|
renderProps.globalTransform,
|
|
182
203
|
renderProps.color,
|
|
@@ -189,7 +210,7 @@ const renderQuads = (
|
|
|
189
210
|
layout.height,
|
|
190
211
|
renderProps.parentHasRenderTexture,
|
|
191
212
|
renderProps.framebufferDimensions,
|
|
192
|
-
|
|
213
|
+
shader,
|
|
193
214
|
);
|
|
194
215
|
|
|
195
216
|
// Snapshot the written vertex data into the cache for future frames
|
|
@@ -201,7 +222,7 @@ const renderQuads = (
|
|
|
201
222
|
cache.vertices = new Float32Array(len);
|
|
202
223
|
}
|
|
203
224
|
cache.vertices.set(webGlRenderer.fSdfBuffer.subarray(startIdx, endIdx));
|
|
204
|
-
cache.glyphCount = layout.
|
|
225
|
+
cache.glyphCount = layout.glyphCount;
|
|
205
226
|
cache.color = renderProps.color;
|
|
206
227
|
cache.alpha = renderProps.worldAlpha;
|
|
207
228
|
cache.layoutRef = layout;
|
|
@@ -283,7 +304,19 @@ const generateTextLayout = (
|
|
|
283
304
|
|
|
284
305
|
const lineAmount = lines.length;
|
|
285
306
|
|
|
286
|
-
|
|
307
|
+
// Upper bound on glyph slots = total chars across all lines. Surrogate pairs
|
|
308
|
+
// and skipped codepoints (zero-width / missing) may leave the tail of this
|
|
309
|
+
// buffer unused; the real count is tracked in `glyphCount`.
|
|
310
|
+
let maxGlyphs = 0;
|
|
311
|
+
for (let i = 0; i < lineAmount; i++) {
|
|
312
|
+
maxGlyphs += (lines[i] as TextLineStruct)[0].length;
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
// Packed glyph buffer: SDF_GLYPH_STRIDE floats per glyph. One Float32Array
|
|
316
|
+
// replaces what used to be one object literal per glyph.
|
|
317
|
+
const glyphs = new Float32Array(maxGlyphs * SDF_GLYPH_STRIDE);
|
|
318
|
+
let glyphIdx = 0;
|
|
319
|
+
let glyphCount = 0;
|
|
287
320
|
let currentX = 0;
|
|
288
321
|
let baselineY = 0;
|
|
289
322
|
for (let i = 0; i < lineAmount; i++) {
|
|
@@ -341,18 +374,16 @@ const generateTextLayout = (
|
|
|
341
374
|
// BMFont line-box top; subtracting atlasBase re-anchors it relative to
|
|
342
375
|
// the alphabetic baseline so fonts with different BMFont 'base' values
|
|
343
376
|
// share the same on-screen baseline.
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
glyphs.push(glyphLayout);
|
|
377
|
+
glyphs[glyphIdx] = currentX + glyph.xoffset;
|
|
378
|
+
glyphs[glyphIdx + 1] = baselineY + glyph.yoffset - atlasBase;
|
|
379
|
+
glyphs[glyphIdx + 2] = glyph.width;
|
|
380
|
+
glyphs[glyphIdx + 3] = glyph.height;
|
|
381
|
+
glyphs[glyphIdx + 4] = glyph.x * invAtlasWidth;
|
|
382
|
+
glyphs[glyphIdx + 5] = glyph.y * invAtlasHeight;
|
|
383
|
+
glyphs[glyphIdx + 6] = glyph.width * invAtlasWidth;
|
|
384
|
+
glyphs[glyphIdx + 7] = glyph.height * invAtlasHeight;
|
|
385
|
+
glyphIdx += SDF_GLYPH_STRIDE;
|
|
386
|
+
glyphCount++;
|
|
356
387
|
|
|
357
388
|
// Advance position with letter spacing (in design units)
|
|
358
389
|
currentX += glyph.xadvance + letterSpacing;
|
|
@@ -363,6 +394,7 @@ const generateTextLayout = (
|
|
|
363
394
|
// Convert final dimensions to pixel space for the layout
|
|
364
395
|
return {
|
|
365
396
|
glyphs,
|
|
397
|
+
glyphCount,
|
|
366
398
|
distanceRange: fontScale * fontData.distanceField.distanceRange,
|
|
367
399
|
width: effectiveWidth * fontScale,
|
|
368
400
|
height: effectiveHeight,
|