@flighthq/render-gl 0.1.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/glBackground.d.ts +3 -0
- package/dist/glBackground.d.ts.map +1 -0
- package/dist/glBackground.js +17 -0
- package/dist/glBackground.js.map +1 -0
- package/dist/glDraw.d.ts +11 -0
- package/dist/glDraw.d.ts.map +1 -0
- package/dist/glDraw.js +132 -0
- package/dist/glDraw.js.map +1 -0
- package/dist/glElement.d.ts +2 -0
- package/dist/glElement.d.ts.map +1 -0
- package/dist/glElement.js +9 -0
- package/dist/glElement.js.map +1 -0
- package/dist/glFullscreenPass.d.ts +11 -0
- package/dist/glFullscreenPass.d.ts.map +1 -0
- package/dist/glFullscreenPass.js +114 -0
- package/dist/glFullscreenPass.js.map +1 -0
- package/dist/glMaterialRegistry.d.ts +5 -0
- package/dist/glMaterialRegistry.d.ts.map +1 -0
- package/dist/glMaterialRegistry.js +25 -0
- package/dist/glMaterialRegistry.js.map +1 -0
- package/dist/glProgram.d.ts +4 -0
- package/dist/glProgram.d.ts.map +1 -0
- package/dist/glProgram.js +37 -0
- package/dist/glProgram.js.map +1 -0
- package/dist/glReadback.d.ts +3 -0
- package/dist/glReadback.d.ts.map +1 -0
- package/dist/glReadback.js +33 -0
- package/dist/glReadback.js.map +1 -0
- package/dist/glRenderState.d.ts +7 -0
- package/dist/glRenderState.d.ts.map +1 -0
- package/dist/glRenderState.js +153 -0
- package/dist/glRenderState.js.map +1 -0
- package/dist/glRenderTarget.d.ts +41 -0
- package/dist/glRenderTarget.d.ts.map +1 -0
- package/dist/glRenderTarget.js +284 -0
- package/dist/glRenderTarget.js.map +1 -0
- package/dist/glRenderTargetPool.d.ts +6 -0
- package/dist/glRenderTargetPool.d.ts.map +1 -0
- package/dist/glRenderTargetPool.js +33 -0
- package/dist/glRenderTargetPool.js.map +1 -0
- package/dist/glShader.d.ts +39 -0
- package/dist/glShader.d.ts.map +1 -0
- package/dist/glShader.js +116 -0
- package/dist/glShader.js.map +1 -0
- package/dist/glShaderBinding.d.ts +21 -0
- package/dist/glShaderBinding.d.ts.map +1 -0
- package/dist/glShaderBinding.js +60 -0
- package/dist/glShaderBinding.js.map +1 -0
- package/dist/glShaderRegistry.d.ts +5 -0
- package/dist/glShaderRegistry.d.ts.map +1 -0
- package/dist/glShaderRegistry.js +6 -0
- package/dist/glShaderRegistry.js.map +1 -0
- package/dist/glShaderTypes.d.ts +2 -0
- package/dist/glShaderTypes.d.ts.map +1 -0
- package/dist/glShaderTypes.js +2 -0
- package/dist/glShaderTypes.js.map +1 -0
- package/dist/glTestHelper.d.ts +14 -0
- package/dist/glTestHelper.d.ts.map +1 -0
- package/dist/glTestHelper.js +69 -0
- package/dist/glTestHelper.js.map +1 -0
- package/dist/index.d.ts +14 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +13 -0
- package/dist/index.js.map +1 -0
- package/dist/internal.d.ts +2 -0
- package/dist/internal.d.ts.map +1 -0
- package/dist/internal.js +2 -0
- package/dist/internal.js.map +1 -0
- package/package.json +40 -0
- package/src/glBackground.test.ts +43 -0
- package/src/glDraw.test.ts +360 -0
- package/src/glElement.test.ts +38 -0
- package/src/glFullscreenPass.test.ts +185 -0
- package/src/glMaterialRegistry.test.ts +48 -0
- package/src/glProgram.test.ts +59 -0
- package/src/glReadback.test.ts +82 -0
- package/src/glRenderState.test.ts +186 -0
- package/src/glRenderTarget.test.ts +317 -0
- package/src/glRenderTargetPool.test.ts +97 -0
- package/src/glShader.test.ts +331 -0
- package/src/glShaderBinding.test.ts +149 -0
- package/src/glShaderRegistry.test.ts +19 -0
|
@@ -0,0 +1,331 @@
|
|
|
1
|
+
import { EntityRuntimeKey } from '@flighthq/types';
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
compileDefaultGlProgram,
|
|
5
|
+
compileGlBitmapProgram,
|
|
6
|
+
createDefaultGlBitmapShader,
|
|
7
|
+
createGlBitmapShader,
|
|
8
|
+
setGlAttributes,
|
|
9
|
+
setGlBaseUniforms,
|
|
10
|
+
setGlMatrixFromTransform,
|
|
11
|
+
setGlMatrixFromValues,
|
|
12
|
+
} from './glShader';
|
|
13
|
+
import { makeGL, makeShaderLoc } from './glTestHelper';
|
|
14
|
+
|
|
15
|
+
describe('compileDefaultGlProgram', () => {
|
|
16
|
+
it('returns shader locations with all required fields', () => {
|
|
17
|
+
const gl = makeGL();
|
|
18
|
+
const loc = compileDefaultGlProgram(gl);
|
|
19
|
+
expect(loc.program).toBeDefined();
|
|
20
|
+
expect(typeof loc.locPosition).toBe('number');
|
|
21
|
+
expect(typeof loc.locTexCoord).toBe('number');
|
|
22
|
+
expect(loc.locMatrix).toBeDefined();
|
|
23
|
+
expect(loc.locAlpha).toBeDefined();
|
|
24
|
+
expect(loc.locTexture).toBeDefined();
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
it('compiles both vertex and fragment shaders', () => {
|
|
28
|
+
const gl = makeGL();
|
|
29
|
+
compileDefaultGlProgram(gl);
|
|
30
|
+
expect(gl.createShader).toHaveBeenCalledWith((gl as unknown as { VERTEX_SHADER: number }).VERTEX_SHADER);
|
|
31
|
+
expect(gl.createShader).toHaveBeenCalledWith((gl as unknown as { FRAGMENT_SHADER: number }).FRAGMENT_SHADER);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('deletes both shader objects after linking', () => {
|
|
35
|
+
const gl = makeGL();
|
|
36
|
+
compileDefaultGlProgram(gl);
|
|
37
|
+
expect(gl.deleteShader).toHaveBeenCalledTimes(2);
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
it('throws when vertex shader compilation fails', () => {
|
|
41
|
+
const gl = makeGL();
|
|
42
|
+
(gl.getShaderParameter as ReturnType<typeof vi.fn>).mockReturnValueOnce(false);
|
|
43
|
+
expect(() => compileDefaultGlProgram(gl)).toThrow('shader compile error');
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
it('throws when program linking fails', () => {
|
|
47
|
+
const gl = makeGL();
|
|
48
|
+
(gl.getProgramParameter as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
|
49
|
+
expect(() => compileDefaultGlProgram(gl)).toThrow('program link error');
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
describe('compileGlBitmapProgram', () => {
|
|
54
|
+
it('compiles a program from the default fragment shader when none is given', () => {
|
|
55
|
+
const gl = makeGL();
|
|
56
|
+
const loc = compileGlBitmapProgram(gl);
|
|
57
|
+
expect(loc.program).toBeDefined();
|
|
58
|
+
expect(loc.locAlpha).toBeDefined();
|
|
59
|
+
expect(loc.locTexture).toBeDefined();
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
it('uses a provided custom fragment shader source', () => {
|
|
63
|
+
const gl = makeGL();
|
|
64
|
+
const frag =
|
|
65
|
+
'#version 300 es\nprecision mediump float;\nout vec4 fragColor;\nvoid main() { fragColor = vec4(1.0); }';
|
|
66
|
+
compileGlBitmapProgram(gl, frag);
|
|
67
|
+
expect(gl.shaderSource).toHaveBeenCalledWith(expect.anything(), frag);
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
it('throws when program linking fails', () => {
|
|
71
|
+
const gl = makeGL();
|
|
72
|
+
(gl.getProgramParameter as ReturnType<typeof vi.fn>).mockReturnValue(false);
|
|
73
|
+
expect(() => compileGlBitmapProgram(gl, 'frag')).toThrow('program link error');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('createDefaultGlBitmapShader', () => {
|
|
78
|
+
it('returns a shader with the provided program', () => {
|
|
79
|
+
const loc = makeShaderLoc();
|
|
80
|
+
const m = new Float32Array(9);
|
|
81
|
+
const shader = createDefaultGlBitmapShader(loc, m);
|
|
82
|
+
expect(shader.program).toBe(loc.program);
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('returns a shader with a bind method', () => {
|
|
86
|
+
const loc = makeShaderLoc();
|
|
87
|
+
const m = new Float32Array(9);
|
|
88
|
+
const shader = createDefaultGlBitmapShader(loc, m);
|
|
89
|
+
expect(typeof shader.bind).toBe('function');
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
it('does not bind color transform uniforms for the default shader', () => {
|
|
93
|
+
const gl = makeGL();
|
|
94
|
+
const loc = makeShaderLoc();
|
|
95
|
+
loc.locColorMultiplier = undefined;
|
|
96
|
+
loc.locColorOffset = undefined;
|
|
97
|
+
loc.locHasColorTransform = undefined;
|
|
98
|
+
const m = new Float32Array(9);
|
|
99
|
+
const canvas = document.createElement('canvas');
|
|
100
|
+
canvas.width = 100;
|
|
101
|
+
canvas.height = 100;
|
|
102
|
+
const shader = createDefaultGlBitmapShader(loc, m);
|
|
103
|
+
const renderProxy = {
|
|
104
|
+
alpha: 0.75,
|
|
105
|
+
colorTransform: {
|
|
106
|
+
redMultiplier: 0.5,
|
|
107
|
+
greenMultiplier: 0.25,
|
|
108
|
+
blueMultiplier: 1.5,
|
|
109
|
+
alphaMultiplier: 0.8,
|
|
110
|
+
redOffset: 10,
|
|
111
|
+
greenOffset: 20,
|
|
112
|
+
blueOffset: 30,
|
|
113
|
+
alphaOffset: 40,
|
|
114
|
+
},
|
|
115
|
+
transform2D: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 },
|
|
116
|
+
useColorTransform: true,
|
|
117
|
+
};
|
|
118
|
+
|
|
119
|
+
shader.bind(gl, { canvas, [EntityRuntimeKey]: { renderTargetViewport: null } } as never, renderProxy as never);
|
|
120
|
+
|
|
121
|
+
expect(gl.uniform4f).not.toHaveBeenCalled();
|
|
122
|
+
});
|
|
123
|
+
});
|
|
124
|
+
|
|
125
|
+
describe('createGlBitmapShader', () => {
|
|
126
|
+
it('returns a shader compiled from the fragment source', () => {
|
|
127
|
+
const gl = makeGL();
|
|
128
|
+
const shader = createGlBitmapShader(gl, '#version 300 es\nvoid main() {}');
|
|
129
|
+
expect(shader.program).toBeDefined();
|
|
130
|
+
expect(shader.locations).toBeDefined();
|
|
131
|
+
expect(typeof shader.bind).toBe('function');
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
it('invokes the onBind hook with the locations and render node during bind', () => {
|
|
135
|
+
const gl = makeGL();
|
|
136
|
+
const onBind = vi.fn();
|
|
137
|
+
const shader = createGlBitmapShader(gl, '#version 300 es\nvoid main() {}', onBind);
|
|
138
|
+
const canvas = document.createElement('canvas');
|
|
139
|
+
canvas.width = 100;
|
|
140
|
+
canvas.height = 100;
|
|
141
|
+
const renderProxy = { alpha: 1, transform2D: { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 } };
|
|
142
|
+
|
|
143
|
+
shader.bind(
|
|
144
|
+
gl,
|
|
145
|
+
{ canvas, [EntityRuntimeKey]: { matrixArray: new Float32Array(9), renderTargetViewport: null } } as never,
|
|
146
|
+
renderProxy as never,
|
|
147
|
+
);
|
|
148
|
+
|
|
149
|
+
expect(onBind).toHaveBeenCalledWith(gl, shader.locations, renderProxy);
|
|
150
|
+
});
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
describe('setGlAttributes', () => {
|
|
154
|
+
it('enables the position vertex attrib array', () => {
|
|
155
|
+
const gl = makeGL();
|
|
156
|
+
const loc = makeShaderLoc();
|
|
157
|
+
setGlAttributes(gl, loc);
|
|
158
|
+
expect(gl.enableVertexAttribArray).toHaveBeenCalledWith(loc.locPosition);
|
|
159
|
+
});
|
|
160
|
+
|
|
161
|
+
it('enables the texCoord vertex attrib array', () => {
|
|
162
|
+
const gl = makeGL();
|
|
163
|
+
const loc = makeShaderLoc();
|
|
164
|
+
setGlAttributes(gl, loc);
|
|
165
|
+
expect(gl.enableVertexAttribArray).toHaveBeenCalledWith(loc.locTexCoord);
|
|
166
|
+
});
|
|
167
|
+
|
|
168
|
+
it('configures position attrib with stride 16 and offset 0', () => {
|
|
169
|
+
const gl = makeGL();
|
|
170
|
+
const loc = makeShaderLoc();
|
|
171
|
+
setGlAttributes(gl, loc);
|
|
172
|
+
expect(gl.vertexAttribPointer).toHaveBeenCalledWith(
|
|
173
|
+
loc.locPosition,
|
|
174
|
+
2,
|
|
175
|
+
(gl as unknown as { FLOAT: number }).FLOAT,
|
|
176
|
+
false,
|
|
177
|
+
16,
|
|
178
|
+
0,
|
|
179
|
+
);
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
it('configures texCoord attrib with stride 16 and offset 8', () => {
|
|
183
|
+
const gl = makeGL();
|
|
184
|
+
const loc = makeShaderLoc();
|
|
185
|
+
setGlAttributes(gl, loc);
|
|
186
|
+
expect(gl.vertexAttribPointer).toHaveBeenCalledWith(
|
|
187
|
+
loc.locTexCoord,
|
|
188
|
+
2,
|
|
189
|
+
(gl as unknown as { FLOAT: number }).FLOAT,
|
|
190
|
+
false,
|
|
191
|
+
16,
|
|
192
|
+
8,
|
|
193
|
+
);
|
|
194
|
+
});
|
|
195
|
+
});
|
|
196
|
+
|
|
197
|
+
describe('setGlBaseUniforms', () => {
|
|
198
|
+
it('sets alpha and texture uniforms', () => {
|
|
199
|
+
const gl = makeGL();
|
|
200
|
+
const loc = makeShaderLoc();
|
|
201
|
+
const renderProxy = {
|
|
202
|
+
alpha: 0.5,
|
|
203
|
+
};
|
|
204
|
+
|
|
205
|
+
setGlBaseUniforms(gl, loc, renderProxy as never);
|
|
206
|
+
|
|
207
|
+
expect(gl.uniform1f).toHaveBeenCalledWith(loc.locAlpha, 0.5);
|
|
208
|
+
expect(gl.uniform1i).toHaveBeenCalledWith(loc.locTexture, 0);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
it('does not bind feature-specific uniforms', () => {
|
|
212
|
+
const gl = makeGL();
|
|
213
|
+
const loc = makeShaderLoc();
|
|
214
|
+
const renderProxy = {
|
|
215
|
+
alpha: 1,
|
|
216
|
+
colorTransform: {
|
|
217
|
+
redMultiplier: 0.2,
|
|
218
|
+
greenMultiplier: 0.4,
|
|
219
|
+
blueMultiplier: 0.6,
|
|
220
|
+
alphaMultiplier: 0.8,
|
|
221
|
+
redOffset: 25.5,
|
|
222
|
+
greenOffset: 51,
|
|
223
|
+
blueOffset: 76.5,
|
|
224
|
+
alphaOffset: 102,
|
|
225
|
+
},
|
|
226
|
+
useColorTransform: true,
|
|
227
|
+
};
|
|
228
|
+
|
|
229
|
+
setGlBaseUniforms(gl, loc, renderProxy as never);
|
|
230
|
+
|
|
231
|
+
expect(gl.uniform4f).not.toHaveBeenCalled();
|
|
232
|
+
});
|
|
233
|
+
});
|
|
234
|
+
|
|
235
|
+
describe('setGlMatrixFromTransform', () => {
|
|
236
|
+
it('computes matrix scaled by canvas dimensions for identity transform', () => {
|
|
237
|
+
const gl = makeGL();
|
|
238
|
+
const loc = makeShaderLoc();
|
|
239
|
+
const m = new Float32Array(9);
|
|
240
|
+
const canvas = document.createElement('canvas');
|
|
241
|
+
canvas.width = 200;
|
|
242
|
+
canvas.height = 100;
|
|
243
|
+
|
|
244
|
+
setGlMatrixFromTransform(gl, loc, m, { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 }, canvas);
|
|
245
|
+
|
|
246
|
+
expect(m[0]).toBeCloseTo(0.01); // a * 2/width
|
|
247
|
+
expect(m[1]).toBeCloseTo(0); // -b * 2/height
|
|
248
|
+
expect(m[2]).toBe(0);
|
|
249
|
+
expect(m[3]).toBeCloseTo(0); // c * 2/width
|
|
250
|
+
expect(m[4]).toBeCloseTo(-0.02); // -d * 2/height
|
|
251
|
+
expect(m[5]).toBe(0);
|
|
252
|
+
expect(m[6]).toBeCloseTo(-1); // tx * 2/width - 1
|
|
253
|
+
expect(m[7]).toBeCloseTo(1); // -ty * 2/height + 1
|
|
254
|
+
expect(m[8]).toBe(1);
|
|
255
|
+
});
|
|
256
|
+
|
|
257
|
+
it('bakes translation into m[6] and m[7]', () => {
|
|
258
|
+
const gl = makeGL();
|
|
259
|
+
const loc = makeShaderLoc();
|
|
260
|
+
const m = new Float32Array(9);
|
|
261
|
+
const canvas = document.createElement('canvas');
|
|
262
|
+
canvas.width = 200;
|
|
263
|
+
canvas.height = 100;
|
|
264
|
+
|
|
265
|
+
setGlMatrixFromTransform(gl, loc, m, { a: 1, b: 0, c: 0, d: 1, tx: 10, ty: 20 }, canvas);
|
|
266
|
+
|
|
267
|
+
expect(m[6]).toBeCloseTo(-0.9); // 10 * 2/200 - 1
|
|
268
|
+
expect(m[7]).toBeCloseTo(0.6); // -20 * 2/100 + 1
|
|
269
|
+
});
|
|
270
|
+
|
|
271
|
+
it('negates b and d components to flip y-axis for clip space', () => {
|
|
272
|
+
const gl = makeGL();
|
|
273
|
+
const loc = makeShaderLoc();
|
|
274
|
+
const m = new Float32Array(9);
|
|
275
|
+
const canvas = document.createElement('canvas');
|
|
276
|
+
canvas.width = 100;
|
|
277
|
+
canvas.height = 100;
|
|
278
|
+
|
|
279
|
+
setGlMatrixFromTransform(gl, loc, m, { a: 0, b: 2, c: 3, d: 0, tx: 0, ty: 0 }, canvas);
|
|
280
|
+
|
|
281
|
+
expect(m[1]).toBeCloseTo(-0.04); // -b * 2/100
|
|
282
|
+
expect(m[3]).toBeCloseTo(0.06); // c * 2/100
|
|
283
|
+
expect(m[4]).toBeCloseTo(0); // -d * 2/100
|
|
284
|
+
});
|
|
285
|
+
|
|
286
|
+
it('calls uniformMatrix3fv with the filled matrix', () => {
|
|
287
|
+
const gl = makeGL();
|
|
288
|
+
const loc = makeShaderLoc();
|
|
289
|
+
const m = new Float32Array(9);
|
|
290
|
+
const canvas = document.createElement('canvas');
|
|
291
|
+
canvas.width = 100;
|
|
292
|
+
canvas.height = 100;
|
|
293
|
+
|
|
294
|
+
setGlMatrixFromTransform(gl, loc, m, { a: 1, b: 0, c: 0, d: 1, tx: 0, ty: 0 }, canvas);
|
|
295
|
+
|
|
296
|
+
expect(gl.uniformMatrix3fv).toHaveBeenCalledWith(loc.locMatrix, false, m);
|
|
297
|
+
});
|
|
298
|
+
});
|
|
299
|
+
|
|
300
|
+
describe('setGlMatrixFromValues', () => {
|
|
301
|
+
it('produces the same result as setGlMatrixFromTransform for equivalent inputs', () => {
|
|
302
|
+
const gl = makeGL();
|
|
303
|
+
const loc = makeShaderLoc();
|
|
304
|
+
const m1 = new Float32Array(9);
|
|
305
|
+
const m2 = new Float32Array(9);
|
|
306
|
+
const canvas = document.createElement('canvas');
|
|
307
|
+
canvas.width = 200;
|
|
308
|
+
canvas.height = 100;
|
|
309
|
+
const t = { a: 2, b: 0.5, c: -0.5, d: 3, tx: 10, ty: 20 };
|
|
310
|
+
|
|
311
|
+
setGlMatrixFromTransform(gl, loc, m1, t, canvas);
|
|
312
|
+
setGlMatrixFromValues(gl, loc, m2, t.a, t.b, t.c, t.d, t.tx, t.ty, canvas);
|
|
313
|
+
|
|
314
|
+
for (let i = 0; i < 9; i++) {
|
|
315
|
+
expect(m2[i]).toBeCloseTo(m1[i]);
|
|
316
|
+
}
|
|
317
|
+
});
|
|
318
|
+
|
|
319
|
+
it('calls uniformMatrix3fv with the filled matrix', () => {
|
|
320
|
+
const gl = makeGL();
|
|
321
|
+
const loc = makeShaderLoc();
|
|
322
|
+
const m = new Float32Array(9);
|
|
323
|
+
const canvas = document.createElement('canvas');
|
|
324
|
+
canvas.width = 100;
|
|
325
|
+
canvas.height = 100;
|
|
326
|
+
|
|
327
|
+
setGlMatrixFromValues(gl, loc, m, 1, 0, 0, 1, 0, 0, canvas);
|
|
328
|
+
|
|
329
|
+
expect(gl.uniformMatrix3fv).toHaveBeenCalledWith(loc.locMatrix, false, m);
|
|
330
|
+
});
|
|
331
|
+
});
|
|
@@ -0,0 +1,149 @@
|
|
|
1
|
+
import { getOrCreateRenderProxy2D } from '@flighthq/render';
|
|
2
|
+
import type { DisplayObject, Kind } from '@flighthq/types';
|
|
3
|
+
|
|
4
|
+
import { getGlRenderStateRuntime } from './glRenderState';
|
|
5
|
+
import {
|
|
6
|
+
getGlMaterialShader,
|
|
7
|
+
getGlShader,
|
|
8
|
+
registerGlMaterialShader,
|
|
9
|
+
resolveGlShader,
|
|
10
|
+
setGlShader,
|
|
11
|
+
} from './glShaderBinding';
|
|
12
|
+
import { createGlState } from './glTestHelper';
|
|
13
|
+
|
|
14
|
+
function makeShader() {
|
|
15
|
+
return { locations: {} as never, program: {} as never, bind: vi.fn() };
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
describe('getGlMaterialShader', () => {
|
|
19
|
+
it('returns null when no material shader is registered for the kind', () => {
|
|
20
|
+
const { state } = createGlState();
|
|
21
|
+
expect(getGlMaterialShader(state, 'Tint' as Kind)).toBeNull();
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
it('returns the shader registered for the kind', () => {
|
|
25
|
+
const { state } = createGlState();
|
|
26
|
+
const shader = makeShader();
|
|
27
|
+
registerGlMaterialShader(state, 'Tint' as Kind, shader);
|
|
28
|
+
expect(getGlMaterialShader(state, 'Tint' as Kind)).toBe(shader);
|
|
29
|
+
});
|
|
30
|
+
|
|
31
|
+
it('returns null for a kind other than the one registered', () => {
|
|
32
|
+
const { state } = createGlState();
|
|
33
|
+
registerGlMaterialShader(state, 'Tint' as Kind, makeShader());
|
|
34
|
+
expect(getGlMaterialShader(state, 'ColorMatrix' as Kind)).toBeNull();
|
|
35
|
+
});
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
describe('getGlShader', () => {
|
|
39
|
+
it('returns the shader bound to a render node', () => {
|
|
40
|
+
const { state } = createGlState();
|
|
41
|
+
const node = {} as DisplayObject;
|
|
42
|
+
const shader = makeShader();
|
|
43
|
+
setGlShader(state, node, shader);
|
|
44
|
+
const renderProxy = getOrCreateRenderProxy2D(state, node);
|
|
45
|
+
expect(getGlShader(renderProxy)).toBe(shader);
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
it('returns undefined for a render node with no binding', () => {
|
|
49
|
+
const { state } = createGlState();
|
|
50
|
+
const renderProxy = getOrCreateRenderProxy2D(state, {} as DisplayObject);
|
|
51
|
+
expect(getGlShader(renderProxy)).toBeUndefined();
|
|
52
|
+
});
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
describe('registerGlMaterialShader', () => {
|
|
56
|
+
it('makes the shader retrievable by its material kind', () => {
|
|
57
|
+
const { state } = createGlState();
|
|
58
|
+
const shader = makeShader();
|
|
59
|
+
registerGlMaterialShader(state, 'Tint' as Kind, shader);
|
|
60
|
+
expect(getGlMaterialShader(state, 'Tint' as Kind)).toBe(shader);
|
|
61
|
+
});
|
|
62
|
+
|
|
63
|
+
it('overwrites a previous registration for the same kind', () => {
|
|
64
|
+
const { state } = createGlState();
|
|
65
|
+
const first = makeShader();
|
|
66
|
+
const second = makeShader();
|
|
67
|
+
registerGlMaterialShader(state, 'Tint' as Kind, first);
|
|
68
|
+
registerGlMaterialShader(state, 'Tint' as Kind, second);
|
|
69
|
+
expect(getGlMaterialShader(state, 'Tint' as Kind)).toBe(second);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
it('makes resolveGlShader return the material shader for a matching node', () => {
|
|
73
|
+
const { state } = createGlState();
|
|
74
|
+
const shader = makeShader();
|
|
75
|
+
registerGlMaterialShader(state, 'Tint' as Kind, shader);
|
|
76
|
+
const renderProxy = getOrCreateRenderProxy2D(state, {} as DisplayObject);
|
|
77
|
+
renderProxy.material = { kind: 'Tint' as Kind } as never;
|
|
78
|
+
expect(resolveGlShader(state, renderProxy)).toBe(shader);
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('keeps registrations isolated per render state', () => {
|
|
82
|
+
const { state: a } = createGlState();
|
|
83
|
+
const { state: b } = createGlState();
|
|
84
|
+
registerGlMaterialShader(a, 'Tint' as Kind, makeShader());
|
|
85
|
+
expect(getGlMaterialShader(b, 'Tint' as Kind)).toBeNull();
|
|
86
|
+
});
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
describe('resolveGlShader', () => {
|
|
90
|
+
it('returns the default bitmap shader when no binding is set', () => {
|
|
91
|
+
const { state } = createGlState();
|
|
92
|
+
const renderProxy = getOrCreateRenderProxy2D(state, {} as DisplayObject);
|
|
93
|
+
expect(resolveGlShader(state, renderProxy)).toBe(getGlRenderStateRuntime(state).defaultBitmapShader);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
it('returns the bound shader when shader support is enabled', () => {
|
|
97
|
+
const { state } = createGlState();
|
|
98
|
+
const node = {} as DisplayObject;
|
|
99
|
+
const shader = makeShader();
|
|
100
|
+
setGlShader(state, node, shader);
|
|
101
|
+
const renderProxy = getOrCreateRenderProxy2D(state, node);
|
|
102
|
+
expect(resolveGlShader(state, renderProxy)).toBe(shader);
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
it('falls back to the default shader when no binding resolver is installed', () => {
|
|
106
|
+
const { state } = createGlState();
|
|
107
|
+
const renderProxy = getOrCreateRenderProxy2D(state, {} as DisplayObject);
|
|
108
|
+
// No binding was made, so the resolver is unset — the per-node lookup is skipped.
|
|
109
|
+
expect(resolveGlShader(state, renderProxy)).toBe(getGlRenderStateRuntime(state).defaultBitmapShader);
|
|
110
|
+
});
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
describe('setGlShader', () => {
|
|
114
|
+
it('stores a shader keyed by the per-state render node', () => {
|
|
115
|
+
const { state } = createGlState();
|
|
116
|
+
const node = {} as DisplayObject;
|
|
117
|
+
const shader = makeShader();
|
|
118
|
+
setGlShader(state, node, shader);
|
|
119
|
+
const renderProxy = getOrCreateRenderProxy2D(state, node);
|
|
120
|
+
expect(getGlShader(renderProxy)).toBe(shader);
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
it('installs the per-node shader binding resolver', () => {
|
|
124
|
+
const { state } = createGlState();
|
|
125
|
+
const runtime = getGlRenderStateRuntime(state);
|
|
126
|
+
expect(runtime.webglShaderBindingResolver).toBeUndefined();
|
|
127
|
+
setGlShader(state, {} as DisplayObject, makeShader());
|
|
128
|
+
expect(runtime.webglShaderBindingResolver).toBe(getGlShader);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
it('clears the binding when passed null', () => {
|
|
132
|
+
const { state } = createGlState();
|
|
133
|
+
const node = {} as DisplayObject;
|
|
134
|
+
setGlShader(state, node, makeShader());
|
|
135
|
+
setGlShader(state, node, null);
|
|
136
|
+
const renderProxy = getOrCreateRenderProxy2D(state, node);
|
|
137
|
+
expect(getGlShader(renderProxy)).toBeUndefined();
|
|
138
|
+
});
|
|
139
|
+
|
|
140
|
+
it('does not leak a binding across render states', () => {
|
|
141
|
+
const { state: a } = createGlState();
|
|
142
|
+
const { state: b } = createGlState();
|
|
143
|
+
const node = {} as DisplayObject;
|
|
144
|
+
const shader = makeShader();
|
|
145
|
+
setGlShader(a, node, shader);
|
|
146
|
+
expect(getGlShader(getOrCreateRenderProxy2D(b, node))).toBeUndefined();
|
|
147
|
+
expect(getGlShader(getOrCreateRenderProxy2D(a, node))).toBe(shader);
|
|
148
|
+
});
|
|
149
|
+
});
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { getGlRenderStateRuntime } from './glRenderState';
|
|
2
|
+
import { registerGlBitmapShader } from './glShaderRegistry';
|
|
3
|
+
import { createGlState, makeShaderLoc } from './glTestHelper';
|
|
4
|
+
|
|
5
|
+
describe('registerGlBitmapShader', () => {
|
|
6
|
+
it('uses the provided shader as the state default shader', () => {
|
|
7
|
+
const { state } = createGlState();
|
|
8
|
+
const loc = makeShaderLoc();
|
|
9
|
+
const shader = {
|
|
10
|
+
bind: vi.fn(),
|
|
11
|
+
locations: loc,
|
|
12
|
+
program: loc.program,
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
registerGlBitmapShader(state, shader);
|
|
16
|
+
|
|
17
|
+
expect(getGlRenderStateRuntime(state).defaultBitmapShader).toBe(shader);
|
|
18
|
+
});
|
|
19
|
+
});
|