@shaderfrog/core 0.0.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.
@@ -0,0 +1,312 @@
1
+ import util from 'util';
2
+
3
+ import { parser } from '@shaderfrog/glsl-parser';
4
+ import { visit, AstNode } from '@shaderfrog/glsl-parser/ast';
5
+ import { generate } from '@shaderfrog/glsl-parser';
6
+
7
+ import {
8
+ applyStrategy,
9
+ strategyRunners,
10
+ StrategyType,
11
+ texture2DStrategy,
12
+ } from './core/strategy';
13
+ import * as graphModule from './core/graph';
14
+ import {
15
+ Graph,
16
+ evaluateNode,
17
+ ShaderStage,
18
+ compileGraph,
19
+ computeAllContexts,
20
+ } from './core/graph';
21
+ import { shaderSectionsToProgram } from './ast/shader-sections';
22
+ import { addNode, outputNode, sourceNode } from './core/nodes/engine-node';
23
+ import { makeExpression, returnGlPositionVec3Right } from './ast/manipulate';
24
+
25
+ import { mergeShaderSections, findShaderSections } from './ast/shader-sections';
26
+ import { Program } from '@shaderfrog/glsl-parser/ast';
27
+ import { numberNode } from './core/nodes/data-nodes';
28
+ import { makeEdge } from './core/nodes/edge';
29
+ import { SourceNode } from './core/nodes/code-nodes';
30
+ import { threngine } from './plugins/three/threngine';
31
+ import { Engine, EngineContext, PhysicalNodeConstructor } from './core/engine';
32
+
33
+ const inspect = (thing: any): void =>
34
+ console.log(util.inspect(thing, false, null, true));
35
+
36
+ const mergeBlocks = (ast1: Program, ast2: Program): string => {
37
+ const s1 = findShaderSections(ast1);
38
+ const s2 = findShaderSections(ast2);
39
+ const merged = mergeShaderSections(s1, s2);
40
+ return generate(
41
+ shaderSectionsToProgram(merged, {
42
+ includePrecisions: true,
43
+ includeVersion: true,
44
+ })
45
+ );
46
+ };
47
+
48
+ const dedupe = (code: string) =>
49
+ generate(
50
+ shaderSectionsToProgram(findShaderSections(parser.parse(code)), {
51
+ includePrecisions: true,
52
+ includeVersion: true,
53
+ })
54
+ );
55
+
56
+ let counter = 0;
57
+ const p = { x: 0, y: 0 };
58
+ const id = () => '' + counter++;
59
+
60
+ const constructor: PhysicalNodeConstructor = () => ({
61
+ config: {
62
+ version: 3,
63
+ preprocess: false,
64
+ strategies: [],
65
+ uniforms: [],
66
+ },
67
+ id: '1',
68
+ name: '1',
69
+ type: '',
70
+ inputs: [],
71
+ outputs: [],
72
+ position: { x: 0, y: 0 },
73
+ source: '',
74
+ stage: undefined,
75
+ groupId: null,
76
+ });
77
+
78
+ const engine: Engine = {
79
+ name: 'three',
80
+ evaluateNode: (node) => {
81
+ if (node.type === 'number') {
82
+ return parseFloat(node.value);
83
+ }
84
+ return node.value;
85
+ },
86
+ constructors: {
87
+ physical: constructor,
88
+ toon: constructor,
89
+ },
90
+ mergeOptions: {
91
+ includePrecisions: true,
92
+ includeVersion: true,
93
+ },
94
+ importers: {},
95
+ preserve: new Set<string>(),
96
+ parsers: {},
97
+ };
98
+
99
+ // it('graph compiler arbitrary helper test', () => {
100
+ // const graph: Graph = {
101
+ // nodes: [
102
+ // outputNode('0', 'Output v', p, 'vertex'),
103
+ // outputNode('1', 'Output f', p, 'fragment'),
104
+ // makeSourceNode(
105
+ // '2',
106
+ // `uniform sampler2D image1;
107
+ // uniform sampler2D image2;
108
+ // void main() {
109
+ // vec3 col = texture2D(image1, posTurn - 0.4 * time).rgb + 1.0;
110
+ // vec3 col = texture2D(image2, negTurn - 0.4 * time).rgb + 2.0;
111
+ // }
112
+ // `,
113
+ // 'fragment'
114
+ // ),
115
+ // makeSourceNode(
116
+ // '3',
117
+ // `void main() {
118
+ // return vec4(0.0);
119
+ // }
120
+ // `,
121
+ // 'fragment'
122
+ // ),
123
+ // makeSourceNode(
124
+ // '4',
125
+ // `void main() {
126
+ // return vec4(1.0);
127
+ // }
128
+ // `,
129
+ // 'fragment'
130
+ // ),
131
+ // ],
132
+ // edges: [
133
+ // makeEdge(id(), '2', '1', 'out', 'filler_frogFragOut', 'fragment'),
134
+ // makeEdge(id(), '3', '2', 'out', 'filler_image1', 'fragment'),
135
+ // makeEdge(id(), '4', '2', 'out', 'filler_image2', 'fragment'),
136
+ // ],
137
+ // };
138
+ // const engineContext: EngineContext = {
139
+ // engine: 'three',
140
+ // nodes: {},
141
+ // runtime: {},
142
+ // debuggingNonsense: {},
143
+ // };
144
+
145
+ // const result = compileGraph(engineContext, engine, graph);
146
+ // const built = generate(
147
+ // shaderSectionsToProgram(result.fragment, {
148
+ // includePrecisions: true,
149
+ // includeVersion: true,
150
+ // }).program
151
+ // );
152
+ // expect(built).toBe('hi');
153
+ // });
154
+
155
+ describe('evaluateNode()', () => {
156
+ it('evaluates binary nodes', () => {
157
+ const finalAdd = addNode(id(), p);
158
+ const add2 = addNode(id(), p);
159
+ const num1 = numberNode(id(), 'number', p, '3');
160
+ const num2 = numberNode(id(), 'number', p, '5');
161
+ const num3 = numberNode(id(), 'number', p, '7');
162
+ const graph: Graph = {
163
+ nodes: [num1, num2, num3, finalAdd, add2],
164
+ edges: [
165
+ makeEdge(id(), num1.id, finalAdd.id, 'out', 'a'),
166
+ makeEdge(id(), add2.id, finalAdd.id, 'out', 'b'),
167
+ makeEdge(id(), num2.id, add2.id, 'out', 'a'),
168
+ makeEdge(id(), num3.id, add2.id, 'out', 'b'),
169
+ ],
170
+ };
171
+ expect(evaluateNode(engine, graph, finalAdd)).toBe(15);
172
+ });
173
+ });
174
+
175
+ it('should merge uniforms with interface blocks', () => {
176
+ let astX = parser.parse(`uniform vec2 x;`);
177
+ let astY = parser.parse(`uniform vec2 y, z;
178
+ uniform vec3 a;`);
179
+ expect(mergeBlocks(astX, astY)).toEqual(`uniform vec2 x, y, z;
180
+ uniform vec3 a;
181
+ `);
182
+
183
+ const astL01 = parser.parse(`uniform Light0 { vec4 y; } x;`);
184
+ const astL02 = parser.parse(`uniform Light0 { vec4 y; } x;`);
185
+ expect(mergeBlocks(astL01, astL02)).toEqual(`uniform Light0 { vec4 y; } x;
186
+ `);
187
+
188
+ const astL001 = parser.parse(`uniform Light0 { vec4 y; } x;`);
189
+ const astL002 = parser.parse(`uniform Light0 x;`);
190
+ expect(mergeBlocks(astL001, astL002)).toEqual(`uniform Light0 { vec4 y; } x;
191
+ `);
192
+
193
+ const astLo01 = parser.parse(`uniform Light0 x;`);
194
+ const astLo02 = parser.parse(`uniform Light0 { vec4 y; } x;`);
195
+ expect(mergeBlocks(astLo01, astLo02)).toEqual(`uniform Light0 { vec4 y; } x;
196
+ `);
197
+
198
+ // This may be a bug, look at how the uniforms are merged. I at least want to
199
+ // note its current behavior in this test
200
+ const vec2Arr1 = parser.parse(`uniform vec2 y[5];`);
201
+ const vec2Arr2 = parser.parse(`uniform vec2 y[10];`);
202
+ expect(mergeBlocks(vec2Arr1, vec2Arr2)).toEqual(`uniform vec2 y[10];
203
+ `);
204
+
205
+ const block1 = parser.parse(`uniform Scene { mat4 view; };`);
206
+ const block2 = parser.parse(`uniform Scene { mat4 view; };`);
207
+ expect(mergeBlocks(block1, block2)).toEqual(`uniform Scene { mat4 view; };
208
+ `);
209
+
210
+ // Verify these lines are preserved (they go through dedupeUniforms)
211
+ expect(dedupe(`layout(std140,column_major) uniform;`)).toEqual(
212
+ `layout(std140,column_major) uniform;`
213
+ );
214
+ });
215
+
216
+ describe('strategies', () => {
217
+ let orig: any;
218
+ beforeEach(() => {
219
+ orig = graphModule.mangleName;
220
+ // Terrible hack. in the real world, strategies are applied after mangling
221
+ // @ts-ignore
222
+ graphModule.mangleName = (name: string) => name;
223
+ });
224
+ afterEach(() => {
225
+ // @ts-ignore
226
+ graphModule.mangleName = orig;
227
+ });
228
+
229
+ it('correctly fills with uniform strategy', () => {
230
+ const ast = parser.parse(`
231
+ layout(std140,column_major) uniform;
232
+ uniform sampler2D image;
233
+ uniform vec4 input, output, other;
234
+ uniform vec4 zenput;
235
+ uniform Light0 { vec4 y; } x;
236
+ void main() {
237
+ vec4 computed = texture2D(image, uvPow * 1.0);
238
+ vec4 x = input;
239
+ vec4 y = output;
240
+ vec4 z = zenput;
241
+ }`);
242
+ const fillers = applyStrategy(
243
+ { type: StrategyType.UNIFORM, config: {} },
244
+ {} as SourceNode,
245
+ ast
246
+ );
247
+
248
+ // It should find uniforms with simple types, excluding sampler2D
249
+ expect(fillers.map(([{ displayName: name }]) => name)).toEqual([
250
+ 'image',
251
+ 'input',
252
+ 'output',
253
+ 'other',
254
+ 'zenput',
255
+ ]);
256
+
257
+ fillers.find(([{ displayName: name }]) => name === 'input')?.[1](
258
+ makeExpression('a')
259
+ );
260
+ fillers.find(([{ displayName: name }]) => name === 'output')?.[1](
261
+ makeExpression('b')
262
+ );
263
+ fillers.find(([{ displayName: name }]) => name === 'zenput')?.[1](
264
+ makeExpression('c')
265
+ );
266
+ const result = generate(ast);
267
+
268
+ // Should fill references
269
+ expect(result).toContain('vec4 x = a;');
270
+ expect(result).toContain('vec4 y = b;');
271
+ expect(result).toContain('vec4 z = c;');
272
+
273
+ // Should preserve things it shouldn't touch
274
+ expect(result).toContain('layout(std140,column_major) uniform;');
275
+ expect(result).toContain('uniform sampler2D image;');
276
+ expect(result).toContain('uniform Light0 { vec4 y; } x;');
277
+
278
+ // Should remove uniforms from declarator list
279
+ expect(result).toContain('uniform vec4 other;');
280
+ // Should remove uniform lines
281
+ expect(result).not.toContain('uniform vec4 zenput');
282
+ });
283
+
284
+ it('uses name without suffix for single call', () => {
285
+ const ast = parser.parse(`
286
+ void main() {
287
+ vec4 computed = texture2D(noiseImage, uvPow * 1.0);
288
+ }`);
289
+ expect(
290
+ applyStrategy(
291
+ { type: StrategyType.TEXTURE_2D, config: {} },
292
+ {} as SourceNode,
293
+ ast
294
+ ).map(([{ displayName: name }]) => name)
295
+ ).toEqual(['noiseImage']);
296
+ });
297
+
298
+ it('finds multiple texture2D inputs for one uniform', () => {
299
+ const ast = parser.parse(`
300
+ void main() {
301
+ vec4 computed = texture2D(noiseImage, uvPow * 1.0);
302
+ computed += texture2D(noiseImage, uvPow * 2.0);
303
+ }`);
304
+ expect(
305
+ applyStrategy(
306
+ { type: StrategyType.TEXTURE_2D, config: {} },
307
+ {} as SourceNode,
308
+ ast
309
+ ).map(([{ displayName: name }]) => name)
310
+ ).toEqual(['noiseImage_0', 'noiseImage_1']);
311
+ });
312
+ });