@shaderfrog/core 2.0.0-beta.3 → 3.0.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.
Files changed (49) hide show
  1. package/README.md +2 -2
  2. package/engine.d.ts +39 -3
  3. package/engine.js +5 -0
  4. package/graph/code-nodes.d.ts +8 -2
  5. package/graph/code-nodes.js +5 -1
  6. package/graph/context.d.ts +8 -6
  7. package/graph/context.js +53 -50
  8. package/graph/data-nodes.d.ts +15 -6
  9. package/graph/data-nodes.js +1 -1
  10. package/graph/evaluate.js +7 -0
  11. package/graph/graph-types.d.ts +26 -6
  12. package/graph/graph-types.js +69 -0
  13. package/graph/graph.d.ts +35 -4
  14. package/graph/graph.js +187 -143
  15. package/graph/graph.test.js +244 -54
  16. package/graph/parsers.d.ts +4 -12
  17. package/graph/parsers.js +32 -18
  18. package/graph/shader-sections.d.ts +31 -14
  19. package/graph/shader-sections.js +93 -19
  20. package/package.json +4 -4
  21. package/plugins/babylon/bablyengine.js +9 -10
  22. package/plugins/playcanvas/playengine.js +9 -10
  23. package/plugins/three/importers.d.ts +1 -0
  24. package/plugins/three/importers.js +49 -1
  25. package/plugins/three/threngine.d.ts +6 -4
  26. package/plugins/three/threngine.js +59 -39
  27. package/plugins/three/threngine.test.js +51 -4
  28. package/strategy/assignmentTo.d.ts +10 -0
  29. package/strategy/assignmentTo.js +35 -0
  30. package/strategy/declarationOf.js +2 -2
  31. package/strategy/hardCode.js +1 -4
  32. package/strategy/index.d.ts +1 -1
  33. package/strategy/index.js +1 -1
  34. package/strategy/inject.js +3 -4
  35. package/strategy/namedAttribute.js +2 -2
  36. package/strategy/strategy.d.ts +30 -5
  37. package/strategy/strategy.js +1 -1
  38. package/strategy/stratgies.test.js +59 -31
  39. package/strategy/texture2D.js +20 -22
  40. package/strategy/uniform.js +54 -56
  41. package/strategy/variable.js +5 -5
  42. package/util/ast.d.ts +9 -4
  43. package/util/ast.js +37 -8
  44. package/util/ast.test.d.ts +1 -0
  45. package/util/ast.test.js +14 -0
  46. package/util/indexByid.d.ts +4 -0
  47. package/util/indexByid.js +18 -0
  48. package/util/whitespace.d.ts +2 -0
  49. package/util/whitespace.js +22 -3
@@ -1,3 +1,14 @@
1
+ var __assign = (this && this.__assign) || function () {
2
+ __assign = Object.assign || function(t) {
3
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
4
+ s = arguments[i];
5
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
6
+ t[p] = s[p];
7
+ }
8
+ return t;
9
+ };
10
+ return __assign.apply(this, arguments);
11
+ };
1
12
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
2
13
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
3
14
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -39,20 +50,22 @@ import util from 'util';
39
50
  import { parser } from '@shaderfrog/glsl-parser';
40
51
  import { generate } from '@shaderfrog/glsl-parser';
41
52
  import { addNode, outputNode, sourceNode } from './graph-node';
42
- import { shaderSectionsToProgram, mergeShaderSections, findShaderSections, } from './shader-sections';
53
+ import { shaderSectionsToProgram, mergeShaderSections, findShaderSections, extractSource, filterUniformNames, filterQualifiedStatements, } from './shader-sections';
43
54
  import { numberNode } from './data-nodes';
44
- import { makeEdge } from './edge';
55
+ import { linkFromVertToFrag, makeEdge } from './edge';
45
56
  import { evaluateNode } from './evaluate';
46
- import { compileSource } from './graph';
57
+ import { compileSource, nodeName } from './graph';
47
58
  import { texture2DStrategy } from '../strategy';
48
59
  import { isError } from './context';
49
60
  import { fail } from '../test-util';
61
+ import { SourceType } from './code-nodes';
62
+ import { makeId } from 'src/util/id';
50
63
  var inspect = function (thing) {
51
64
  return console.log(util.inspect(thing, false, null, true));
52
65
  };
53
66
  var mergeBlocks = function (ast1, ast2) {
54
- var s1 = findShaderSections(ast1);
55
- var s2 = findShaderSections(ast2);
67
+ var s1 = findShaderSections('', ast1);
68
+ var s2 = findShaderSections('', ast2);
56
69
  var merged = mergeShaderSections(s1, s2);
57
70
  return generate(shaderSectionsToProgram(merged, {
58
71
  includePrecisions: true,
@@ -60,14 +73,12 @@ var mergeBlocks = function (ast1, ast2) {
60
73
  }));
61
74
  };
62
75
  var dedupe = function (code) {
63
- return generate(shaderSectionsToProgram(findShaderSections(parser.parse(code)), {
76
+ return generate(shaderSectionsToProgram(findShaderSections('', parser.parse(code)), {
64
77
  includePrecisions: true,
65
78
  includeVersion: true,
66
79
  }));
67
80
  };
68
- var counter = 0;
69
81
  var p = { x: 0, y: 0 };
70
- var id = function () { return '' + counter++; };
71
82
  var constructor = function () { return ({
72
83
  config: {
73
84
  version: 3,
@@ -103,7 +114,7 @@ var engine = {
103
114
  includeVersion: true,
104
115
  },
105
116
  importers: {},
106
- preserve: new Set(),
117
+ preserve: new Set('vUv'),
107
118
  parsers: {},
108
119
  };
109
120
  var makeSourceNode = function (id, source, stage, strategies) {
@@ -115,45 +126,22 @@ var makeSourceNode = function (id, source, stage, strategies) {
115
126
  uniforms: [],
116
127
  }, source, stage);
117
128
  };
118
- /**
119
- * What exactly am I doing here?
120
- *
121
- * I opened shaderfrog to start looking at the backfilling case and inlining
122
- * function calls at the top of functions
123
- *
124
- * WHie doing that I found jest not to work well anyore and switched to vitest,
125
- * which is fine, but with esm by default I can't stub the mangleName() function
126
- * call, which means mangling happens as-is in the tests.
127
- *
128
- * Without changing the mangling strategy, the strategies.test.ts file fails
129
- * because the uniform strategy looks for a mangled variable name, but the
130
- * program itself isn't mangled.
131
- *
132
- * One way to fix this is to make fillers not have to care about mangling names,
133
- * which would be simpler on the surface.
134
- *
135
- * Then everything in the tests broke and you found out the reason why was
136
- * trying to use scopes to rename things, and most of the ast manipulation steps
137
- * don't modify scopes, so you made some of them modify scopes, and now things
138
- * are fucked
139
- */
140
- it('compileSource() fragment produces inlined output', function () { return __awaiter(void 0, void 0, void 0, function () {
129
+ it('compileSource() fragment produces inlined output without', function () { return __awaiter(void 0, void 0, void 0, function () {
141
130
  var outV, outF, imageReplacemMe, input1, input2, graph, engineContext, result, imgOut;
142
131
  return __generator(this, function (_a) {
143
132
  switch (_a.label) {
144
133
  case 0:
145
- outV = outputNode(id(), 'Output v', p, 'vertex');
146
- outF = outputNode(id(), 'Output f', p, 'fragment');
147
- imageReplacemMe = makeSourceNode(id(), "uniform sampler2D image1;\nuniform sampler2D image2;\nvoid main() {\n vec3 col1 = texture2D(image1, posTurn - 0.4 * time).rgb + 1.0;\n vec3 col2 = texture2D(image2, negTurn - 0.4 * time).rgb + 2.0;\n gl_FragColor = vec4(col1 + col2, 1.0);\n}\n", 'fragment');
148
- input1 = makeSourceNode(id(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(0.0);\n}\n", 'fragment');
149
- input2 = makeSourceNode(id(), "float a = 2.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
134
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
135
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
136
+ imageReplacemMe = makeSourceNode(makeId(), "uniform sampler2D image1;\nuniform sampler2D image2;\nvoid main() {\n vec3 col1 = texture2D(image1, posTurn - 0.4 * time).rgb + 1.0;\n vec3 col2 = texture2D(image2, negTurn - 0.4 * time).rgb + 2.0;\n gl_FragColor = vec4(col1 + col2, 1.0);\n}\n", 'fragment');
137
+ input1 = makeSourceNode(makeId(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(0.0);\n}\n", 'fragment');
138
+ input2 = makeSourceNode(makeId(), "float a = 2.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
150
139
  graph = {
151
140
  nodes: [outV, outF, imageReplacemMe, input1, input2],
152
141
  edges: [
153
- makeEdge(id(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
154
- makeEdge(id(), input1.id, imageReplacemMe.id, 'out', 'filler_image1', 'fragment'),
155
- makeEdge(id(), input2.id, imageReplacemMe.id, 'out', 'filler_image2', 'fragment'),
156
- makeEdge(id(), input2.id, imageReplacemMe.id, 'out', 'filler_image2', 'fragment'),
142
+ makeEdge(makeId(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
143
+ makeEdge(makeId(), input1.id, imageReplacemMe.id, 'out', 'filler_image1', 'fragment'),
144
+ makeEdge(makeId(), input2.id, imageReplacemMe.id, 'out', 'filler_image2', 'fragment'),
157
145
  ],
158
146
  };
159
147
  engineContext = {
@@ -178,18 +166,206 @@ it('compileSource() fragment produces inlined output', function () { return __aw
178
166
  }
179
167
  });
180
168
  }); });
169
+ it('compileSource() vertex produces inlined output', function () { return __awaiter(void 0, void 0, void 0, function () {
170
+ var outV, outF, vert, graph, engineContext, result, iMainName;
171
+ return __generator(this, function (_a) {
172
+ switch (_a.label) {
173
+ case 0:
174
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
175
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
176
+ vert = makeSourceNode(makeId(), "uniform vec4 modelViewMatrix;\nattribute vec3 position;\nfloat a = 2.0;\nvoid main() {\n gl_Position = modelViewMatrix * vec4(position, 1.0);\n}\n", 'vertex');
177
+ graph = {
178
+ nodes: [outV, outF, vert],
179
+ edges: [
180
+ makeEdge(makeId(), vert.id, outV.id, 'out', 'filler_gl_Position', 'vertex'),
181
+ ],
182
+ };
183
+ engineContext = {
184
+ engine: 'three',
185
+ nodes: {},
186
+ runtime: {},
187
+ debuggingNonsense: {},
188
+ };
189
+ return [4 /*yield*/, compileSource(graph, engine, engineContext)];
190
+ case 1:
191
+ result = _a.sent();
192
+ if (isError(result)) {
193
+ fail(result);
194
+ }
195
+ iMainName = nodeName(vert);
196
+ expect(result.vertexResult).toContain("\nvoid main() {\n gl_Position = ".concat(iMainName, "();\n}"));
197
+ return [2 /*return*/];
198
+ }
199
+ });
200
+ }); });
201
+ it('compileSource() fragment backfilling one level', function () { return __awaiter(void 0, void 0, void 0, function () {
202
+ var outV, outF, imageReplacemMe, input1, graph, engineContext, preserver, result, inputMain, imageMain;
203
+ return __generator(this, function (_a) {
204
+ switch (_a.label) {
205
+ case 0:
206
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
207
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
208
+ imageReplacemMe = makeSourceNode(makeId(), "attribute vec2 vUv;\nuniform sampler2D image1;\nuniform sampler2D image2;\nvoid main() {\n vec3 col1 = texture2D(image1, vUv - 0.4 * time).rgb + 1.0;\n vec3 other1 = texture2D(image1, vUv + 1.0).rgb;\n vec3 col2 = texture2D(image2, negTurn - 0.4 * time).rgb + 2.0;\n gl_FragColor = vec4(col1 + col2, 1.0);\n}\n", 'fragment');
209
+ imageReplacemMe.backfillers = {
210
+ filler_image1: [
211
+ {
212
+ argType: 'vec2',
213
+ targetVariable: 'vUv',
214
+ },
215
+ ],
216
+ };
217
+ input1 = makeSourceNode(makeId(), "attribute vec2 vUv;\nvoid main() {\n gl_FragColor = vec4(vUv, 1.0, 1.0);\n}\n", 'fragment');
218
+ graph = {
219
+ nodes: [outV, outF, imageReplacemMe, input1],
220
+ edges: [
221
+ makeEdge(makeId(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
222
+ makeEdge(makeId(), input1.id, imageReplacemMe.id, 'out', 'filler_image1', 'fragment'),
223
+ ],
224
+ };
225
+ engineContext = {
226
+ engine: 'three',
227
+ nodes: {},
228
+ runtime: {},
229
+ debuggingNonsense: {},
230
+ };
231
+ preserver = __assign(__assign({}, engine), { preserve: new Set(['vUv']) });
232
+ return [4 /*yield*/, compileSource(graph, preserver, engineContext)];
233
+ case 1:
234
+ result = _a.sent();
235
+ if (isError(result)) {
236
+ fail(result);
237
+ }
238
+ inputMain = nodeName(input1);
239
+ imageMain = nodeName(imageReplacemMe);
240
+ // Should preserve global variable despite backfilling
241
+ expect(result.fragmentResult).toContain("in vec2 vUv;");
242
+ // Backfilled variable should be in the main fn parameters
243
+ // I don't think is inlined? I think this is expected by convertToMain
244
+ expect(result.fragmentResult).toContain("\nvec4 ".concat(inputMain, "(vec2 vUv) {\n frogOut_").concat(input1.id, " = vec4(vUv, 1.0, 1.0);\n return frogOut_").concat(input1.id, ";\n}"));
245
+ // The image function should pass its parameters to the child
246
+ expect(result.fragmentResult).toContain("\nvec4 ".concat(imageMain, "() {\n vec3 col1 = ").concat(inputMain, "(vUv - 0.4 * time).rgb + 1.0;\n vec3 other1 = ").concat(inputMain, "(vUv + 1.0).rgb;\n vec3 col2 = texture(image2"));
247
+ return [2 /*return*/];
248
+ }
249
+ });
250
+ }); });
251
+ it('compileSource() orphaned vertex nodes are called properly in output', function () { return __awaiter(void 0, void 0, void 0, function () {
252
+ var outV, outF, vert, frag, graph, engineContext, result, iMainName;
253
+ return __generator(this, function (_a) {
254
+ switch (_a.label) {
255
+ case 0:
256
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
257
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
258
+ vert = makeSourceNode(makeId(), "uniform vec4 modelViewMatrix;\nattribute vec3 position;\nfloat a = 2.0;\nvoid main() {\n gl_Position = modelViewMatrix * vec4(position, 1.0);\n}\n", 'vertex');
259
+ frag = makeSourceNode(makeId(), "attribute vec2 vUv;\nvoid main() {\n gl_FragColor = vec4(vUv, 0.0, 1.0);\n}\n", 'fragment');
260
+ graph = {
261
+ nodes: [outV, outF, frag, vert],
262
+ edges: [
263
+ makeEdge(makeId(), frag.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
264
+ linkFromVertToFrag(makeId(), vert.id, frag.id),
265
+ ],
266
+ };
267
+ engineContext = {
268
+ engine: 'three',
269
+ nodes: {},
270
+ runtime: {},
271
+ debuggingNonsense: {},
272
+ };
273
+ return [4 /*yield*/, compileSource(graph, engine, engineContext)];
274
+ case 1:
275
+ result = _a.sent();
276
+ if (isError(result)) {
277
+ fail(result);
278
+ }
279
+ iMainName = nodeName(vert);
280
+ expect(result.vertexResult).toContain("\nvoid main() {\n ".concat(iMainName, "();\n gl_Position = vec4(1.0);\n}"));
281
+ return [2 /*return*/];
282
+ }
283
+ });
284
+ }); });
285
+ it('compileSource() inlining a fragment expression', function () { return __awaiter(void 0, void 0, void 0, function () {
286
+ var outV, outF, imageReplacemMe, input, graph, engineContext, result;
287
+ return __generator(this, function (_a) {
288
+ switch (_a.label) {
289
+ case 0:
290
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
291
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
292
+ imageReplacemMe = makeSourceNode(makeId(), "uniform sampler2D image1;\nvoid main() {\n vec3 col1 = texture2D(image1, posTurn - 0.4 * time).rgb + 1.0;\n gl_FragColor = vec4(col1, 1.0);\n}\n", 'fragment');
293
+ input = makeSourceNode(makeId(), "vec4(1.0)", 'fragment');
294
+ input.sourceType = SourceType.EXPRESSION;
295
+ graph = {
296
+ nodes: [outV, outF, imageReplacemMe, input],
297
+ edges: [
298
+ makeEdge(makeId(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
299
+ makeEdge(makeId(), input.id, imageReplacemMe.id, 'out', 'filler_image1', 'fragment'),
300
+ ],
301
+ };
302
+ engineContext = {
303
+ engine: 'three',
304
+ nodes: {},
305
+ runtime: {},
306
+ debuggingNonsense: {},
307
+ };
308
+ return [4 /*yield*/, compileSource(graph, engine, engineContext)];
309
+ case 1:
310
+ result = _a.sent();
311
+ if (isError(result)) {
312
+ fail(result);
313
+ }
314
+ // Verify it inlined the expression and did not memoize the source into a
315
+ // varaible
316
+ expect(result.fragmentResult).toContain("vec4 ".concat(nodeName(imageReplacemMe), "() {\n vec3 col1 = vec4(1.0).rgb + 1.0;"));
317
+ return [2 /*return*/];
318
+ }
319
+ });
320
+ }); });
321
+ it('compileSource() binary properly inlines dependencies', function () { return __awaiter(void 0, void 0, void 0, function () {
322
+ var outV, outF, color, expr, add, graph, engineContext, result;
323
+ return __generator(this, function (_a) {
324
+ switch (_a.label) {
325
+ case 0:
326
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
327
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
328
+ color = makeSourceNode(makeId(), "uniform sampler2D image;\nvoid main() {\n vec3 col = texture2D(image, vec2(0.0)).rgb;\n gl_FragColor = vec4(col, 1.0);\n}\n", 'fragment');
329
+ expr = makeSourceNode(makeId(), "vec4(1.0)", 'fragment');
330
+ expr.sourceType = SourceType.EXPRESSION;
331
+ add = addNode(makeId(), p);
332
+ graph = {
333
+ nodes: [color, expr, add, outV, outF],
334
+ edges: [
335
+ makeEdge(makeId(), color.id, add.id, 'out', 'a'),
336
+ makeEdge(makeId(), expr.id, add.id, 'out', 'b'),
337
+ makeEdge(makeId(), add.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
338
+ ],
339
+ };
340
+ engineContext = {
341
+ engine: 'three',
342
+ nodes: {},
343
+ runtime: {},
344
+ debuggingNonsense: {},
345
+ };
346
+ return [4 /*yield*/, compileSource(graph, engine, engineContext)];
347
+ case 1:
348
+ result = _a.sent();
349
+ if (isError(result)) {
350
+ fail(result);
351
+ }
352
+ expect(result.fragmentResult).toContain("void main() {\n frogFragOut = (".concat(nodeName(color), "()+ vec4(1.0));\n}"));
353
+ return [2 /*return*/];
354
+ }
355
+ });
356
+ }); });
181
357
  it('compileSource() base case', function () { return __awaiter(void 0, void 0, void 0, function () {
182
358
  var outV, outF, imageReplacemMe, graph, engineContext, result, imgOut;
183
359
  return __generator(this, function (_a) {
184
360
  switch (_a.label) {
185
361
  case 0:
186
- outV = outputNode(id(), 'Output v', p, 'vertex');
187
- outF = outputNode(id(), 'Output f', p, 'fragment');
188
- imageReplacemMe = makeSourceNode(id(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
362
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
363
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
364
+ imageReplacemMe = makeSourceNode(makeId(), "float a = 1.0;\nvoid main() {\n gl_FragColor = vec4(1.0);\n}\n", 'fragment');
189
365
  graph = {
190
366
  nodes: [outV, outF, imageReplacemMe],
191
367
  edges: [
192
- makeEdge(id(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
368
+ makeEdge(makeId(), imageReplacemMe.id, outF.id, 'out', 'filler_frogFragOut', 'fragment'),
193
369
  ],
194
370
  };
195
371
  engineContext = {
@@ -214,18 +390,18 @@ it('compileSource() base case', function () { return __awaiter(void 0, void 0, v
214
390
  }); });
215
391
  describe('evaluateNode()', function () {
216
392
  it('evaluates binary nodes', function () {
217
- var finalAdd = addNode(id(), p);
218
- var add2 = addNode(id(), p);
219
- var num1 = numberNode(id(), 'number', p, '3');
220
- var num2 = numberNode(id(), 'number', p, '5');
221
- var num3 = numberNode(id(), 'number', p, '7');
393
+ var finalAdd = addNode(makeId(), p);
394
+ var add2 = addNode(makeId(), p);
395
+ var num1 = numberNode(makeId(), 'number', p, '3');
396
+ var num2 = numberNode(makeId(), 'number', p, '5');
397
+ var num3 = numberNode(makeId(), 'number', p, '7');
222
398
  var graph = {
223
399
  nodes: [num1, num2, num3, finalAdd, add2],
224
400
  edges: [
225
- makeEdge(id(), num1.id, finalAdd.id, 'out', 'a'),
226
- makeEdge(id(), add2.id, finalAdd.id, 'out', 'b'),
227
- makeEdge(id(), num2.id, add2.id, 'out', 'a'),
228
- makeEdge(id(), num3.id, add2.id, 'out', 'b'),
401
+ makeEdge(makeId(), num1.id, finalAdd.id, 'out', 'a'),
402
+ makeEdge(makeId(), add2.id, finalAdd.id, 'out', 'b'),
403
+ makeEdge(makeId(), num2.id, add2.id, 'out', 'a'),
404
+ makeEdge(makeId(), num3.id, add2.id, 'out', 'b'),
229
405
  ],
230
406
  };
231
407
  expect(evaluateNode(engine, graph, finalAdd)).toBe(15);
@@ -259,3 +435,17 @@ it('should merge uniforms with interface blocks', function () {
259
435
  // Verify these lines are preserved (they go through dedupeUniforms)
260
436
  expect(dedupe("layout(std140,column_major) uniform;")).toEqual("layout(std140,column_major) uniform;");
261
437
  });
438
+ it('filterUniformNames', function () {
439
+ var stmts = parser
440
+ .parse("uniform vec4 x,y;\nuniform vec2 x, y[5];\nuniform Light0 { vec4 y; } x;\nuniform Light0 { vec4 x; } y;\n")
441
+ .program.filter(function (s) { return s.type === 'declaration_statement'; });
442
+ var filtered = filterUniformNames(stmts.map(function (x) { return ({ nodeId: '', source: x }); }), function (name) { return name !== 'x'; });
443
+ expect(generate(extractSource(filtered))).toEqual("uniform vec4 y;\nuniform vec2 y[5];\nuniform Light0 { vec4 x; } y;\n");
444
+ });
445
+ it('filterQualifiedStatements', function () {
446
+ var stmts = parser
447
+ .parse("in vec2 x, y;\nout vec2 x;\n")
448
+ .program.filter(function (s) { return s.type === 'declaration_statement'; });
449
+ var filtered = filterQualifiedStatements(stmts.map(function (x) { return ({ nodeId: '', source: x }); }), function (name) { return name !== 'x'; });
450
+ expect(generate(extractSource(filtered))).toEqual("in vec2 y;\n");
451
+ });
@@ -1,22 +1,14 @@
1
1
  import { AstNode, Program } from '@shaderfrog/glsl-parser/ast';
2
2
  import { Engine, EngineContext } from '../engine';
3
+ import { ComputedInput, Filler } from '../strategy';
3
4
  import { Edge } from './edge';
4
5
  import { SourceNode } from './code-nodes';
5
- import { NodeInput } from './base-node';
6
6
  import { Graph } from './graph-types';
7
7
  import { Evaluate } from './evaluate';
8
+ import { NodeContext } from './context';
8
9
  export declare const alphabet = "abcdefghijklmnopqrstuvwxyz";
9
- export type Filler = AstNode | AstNode[] | void;
10
- export type InputFiller = (filler: Filler) => AstNode | Program;
11
- export type InputFillerGroup = {
12
- filler: InputFiller;
13
- backfillArgs?: AstNode[];
14
- };
15
- export type InputFillers = Record<string, InputFillerGroup>;
16
- type FillerArguments = AstNode[];
17
- export type ComputedInput = [NodeInput, InputFiller, FillerArguments?];
18
10
  export type ProduceAst = (engineContext: EngineContext, engine: Engine, graph: Graph, node: SourceNode, inputEdges: Edge[]) => AstNode | Program;
19
- export type OnBeforeCompile = (graph: Graph, engineContext: EngineContext, node: SourceNode, sibling?: SourceNode) => Promise<void>;
11
+ export type OnBeforeCompile = (graph: Graph, engineContext: EngineContext, node: SourceNode, sibling?: SourceNode) => Promise<Partial<NodeContext> | void>;
20
12
  export type ManipulateAst = (engineContext: EngineContext, engine: Engine, graph: Graph, ast: AstNode | Program, inputEdges: Edge[], node: SourceNode, sibling: SourceNode) => AstNode | Program;
21
13
  export type NodeParser = {
22
14
  onBeforeCompile?: OnBeforeCompile;
@@ -24,7 +16,7 @@ export type NodeParser = {
24
16
  findInputs?: FindInputs;
25
17
  produceFiller?: ProduceNodeFiller;
26
18
  };
27
- export type FindInputs = (engineContext: EngineContext, ast: Program | AstNode, inputEdges: Edge[], node: SourceNode, sibling: SourceNode) => ComputedInput[];
19
+ export type FindInputs = (engineContext: EngineContext, ast: Program | AstNode, inputEdges: Edge[], node: SourceNode, sibling?: SourceNode) => ComputedInput[];
28
20
  export type ProduceNodeFiller = (node: SourceNode, ast: Program | AstNode) => Filler;
29
21
  type CoreNodeParser = {
30
22
  produceAst: ProduceAst;
package/graph/parsers.js CHANGED
@@ -27,13 +27,14 @@ var _a;
27
27
  import { parser } from '@shaderfrog/glsl-parser';
28
28
  import { visit, } from '@shaderfrog/glsl-parser/ast';
29
29
  import preprocess from '@shaderfrog/glsl-parser/preprocessor';
30
- import { convert300MainToReturn, from2To3, makeExpression, makeExpressionWithScopes, makeFnBodyStatementWithScopes, makeFnStatement, } from '../util/ast';
30
+ import { convert300MainToReturn, findMainOrThrow, from2To3, makeExpression, makeExpressionWithScopes, makeFnBodyStatementWithScopes, } from '../util/ast';
31
31
  import { applyStrategy } from '../strategy';
32
32
  import { SourceType } from './code-nodes';
33
33
  import { nodeInput } from './base-node';
34
34
  import { MAGIC_OUTPUT_STMTS, NodeType } from './graph-types';
35
35
  import { nodeName } from './graph';
36
36
  import { generateFiller } from '../util/ast';
37
+ import { unshiftFnStmtWithIndent } from '../util/whitespace';
37
38
  /*
38
39
  * Core graph parsers, which is the plumbing/interface the graph and context
39
40
  * calls into, to parse, find inputs, etc, and define this per-node type.
@@ -50,7 +51,11 @@ export var alphabet = 'abcdefghijklmnopqrstuvwxyz';
50
51
  export var coreParsers = (_a = {},
51
52
  _a[NodeType.SOURCE] = {
52
53
  produceAst: function (engineContext, engine, graph, node, inputEdges) {
54
+ var _a;
53
55
  var ast;
56
+ // Load the source either from the computed source at runtime, or the
57
+ // node's source code itself
58
+ var source = ((_a = engineContext.nodes[node.id]) === null || _a === void 0 ? void 0 : _a.computedSource) || node.source;
54
59
  // @ts-ignore
55
60
  if (node.expressionOnly) {
56
61
  node.sourceType = SourceType.EXPRESSION;
@@ -58,7 +63,7 @@ export var coreParsers = (_a = {},
58
63
  delete node.expressionOnly;
59
64
  }
60
65
  if (node.sourceType === SourceType.FN_BODY_FRAGMENT) {
61
- var _a = makeFnBodyStatementWithScopes(node.source), statements = _a.statements, scope = _a.scope;
66
+ var _b = makeFnBodyStatementWithScopes(source), statements = _b.statements, scope = _b.scope;
62
67
  ast = {
63
68
  type: 'program',
64
69
  scopes: [scope],
@@ -67,7 +72,7 @@ export var coreParsers = (_a = {},
67
72
  };
68
73
  }
69
74
  else if (node.sourceType === SourceType.EXPRESSION) {
70
- var _b = makeExpressionWithScopes(node.source), expression = _b.expression, scope = _b.scope;
75
+ var _c = makeExpressionWithScopes(source), expression = _c.expression, scope = _c.scope;
71
76
  ast = {
72
77
  type: 'program',
73
78
  scopes: [scope],
@@ -77,8 +82,8 @@ export var coreParsers = (_a = {},
77
82
  }
78
83
  else {
79
84
  var preprocessed = node.config.preprocess === false
80
- ? node.source
81
- : preprocess(node.source, {
85
+ ? source
86
+ : preprocess(source, {
82
87
  preserve: {
83
88
  version: function () { return true; },
84
89
  },
@@ -109,11 +114,20 @@ export var coreParsers = (_a = {},
109
114
  });
110
115
  },
111
116
  produceFiller: function (node, ast) {
112
- return node.sourceType === SourceType.EXPRESSION
113
- ? ast.program[0]
114
- : node.sourceType === SourceType.FN_BODY_FRAGMENT
115
- ? ast.program
116
- : makeExpression("".concat(nodeName(node), "()"));
117
+ return function () {
118
+ var args = [];
119
+ for (var _i = 0; _i < arguments.length; _i++) {
120
+ args[_i] = arguments[_i];
121
+ }
122
+ var fillerNode = node.sourceType === SourceType.EXPRESSION
123
+ ? ast.program[0]
124
+ : node.sourceType === SourceType.FN_BODY_FRAGMENT
125
+ ? ast.program
126
+ : // Backfilling into the call of this program's filler.
127
+ // Similar to texutre2D.ts filler
128
+ makeExpression("".concat(nodeName(node), "(").concat(args.join(', '), ")"));
129
+ return fillerNode;
130
+ };
117
131
  },
118
132
  },
119
133
  // TODO: Output node assumes strategies are still passed in on node creation,
@@ -128,16 +142,16 @@ export var coreParsers = (_a = {},
128
142
  })), false), [
129
143
  [
130
144
  nodeInput(MAGIC_OUTPUT_STMTS, "filler_".concat(MAGIC_OUTPUT_STMTS), 'filler', 'rgba', ['code'], false),
131
- function (fillerAst) {
132
- var fn = ast.program.find(function (stmt) { return stmt.type === 'function'; });
133
- fn === null || fn === void 0 ? void 0 : fn.body.statements.unshift(makeFnStatement(generateFiller(fillerAst))[0]);
145
+ function (filler) {
146
+ var main = findMainOrThrow(ast);
147
+ main.body.statements = unshiftFnStmtWithIndent(main, generateFiller(filler()));
134
148
  return ast;
135
149
  },
136
150
  ],
137
151
  ], false);
138
152
  },
139
153
  produceFiller: function (node, ast) {
140
- return makeExpression('impossible_call()');
154
+ return function () { return makeExpression('impossible_call()'); };
141
155
  },
142
156
  },
143
157
  _a[NodeType.BINARY] = {
@@ -158,7 +172,7 @@ export var coreParsers = (_a = {},
158
172
  var letter = alphabet.charAt(index);
159
173
  return [
160
174
  nodeInput(letter, letter, 'filler', undefined, ['data', 'code'], false),
161
- function (fillerAst) {
175
+ function (filler) {
162
176
  var foundPath;
163
177
  var visitors = {
164
178
  identifier: {
@@ -175,18 +189,18 @@ export var coreParsers = (_a = {},
175
189
  }
176
190
  if (foundPath.parent && foundPath.key) {
177
191
  // @ts-ignore
178
- foundPath.parent[foundPath.key] = fillerAst;
192
+ foundPath.parent[foundPath.key] = filler();
179
193
  return ast;
180
194
  }
181
195
  else {
182
- return fillerAst;
196
+ return filler();
183
197
  }
184
198
  },
185
199
  ];
186
200
  });
187
201
  },
188
202
  produceFiller: function (node, ast) {
189
- return ast;
203
+ return function () { return ast; };
190
204
  },
191
205
  evaluate: function (node, inputEdges, inputNodes, evaluateNode) {
192
206
  var operator = node.operator;
@@ -1,28 +1,45 @@
1
1
  /**
2
2
  * Categorizing / deduping parts of shaders to help merge them together
3
3
  */
4
- import { AstNode, DeclarationStatementNode, PreprocessorNode } from '@shaderfrog/glsl-parser/ast';
4
+ import { DeclarationStatementNode, PreprocessorNode, ProgramStatement } from '@shaderfrog/glsl-parser/ast';
5
5
  import { Program } from '@shaderfrog/glsl-parser/ast';
6
+ export type LineAndSource<T = any> = {
7
+ nodeId: string;
8
+ source: T;
9
+ };
10
+ export declare function extractSource<T>(lineAndSource: LineAndSource<T>): T;
11
+ export declare function extractSource<T>(lineAndSource: LineAndSource<T>[]): T[];
6
12
  export interface ShaderSections {
7
- precision: DeclarationStatementNode[];
8
- version: AstNode[];
9
- preprocessor: PreprocessorNode[];
10
- structs: AstNode[];
11
- inStatements: DeclarationStatementNode[];
12
- outStatements: DeclarationStatementNode[];
13
- uniforms: DeclarationStatementNode[];
14
- program: AstNode[];
13
+ precision: LineAndSource<DeclarationStatementNode>[];
14
+ version: LineAndSource<PreprocessorNode>[];
15
+ preprocessor: LineAndSource<PreprocessorNode>[];
16
+ structs: LineAndSource<DeclarationStatementNode>[];
17
+ inStatements: LineAndSource<DeclarationStatementNode>[];
18
+ outStatements: LineAndSource<DeclarationStatementNode>[];
19
+ uniforms: LineAndSource<DeclarationStatementNode>[];
20
+ program: LineAndSource<ProgramStatement>[];
15
21
  }
16
- export declare const emptyShaderSections: () => ShaderSections;
22
+ export declare const filterSections: (filter: (s: LineAndSource) => boolean, sections: ShaderSections) => ShaderSections;
23
+ export declare const mapSections: (map: (s: LineAndSource) => LineAndSource, sections: ShaderSections) => ShaderSections;
24
+ export declare const shaderSectionsCons: () => ShaderSections;
17
25
  declare enum Precision {
18
26
  highp = 2,
19
27
  mediump = 1,
20
28
  lowp = 0
21
29
  }
22
30
  export declare const higherPrecision: (p1: Precision, p2: Precision) => Precision;
23
- export declare const dedupeVersions: (nodes: AstNode[]) => AstNode;
31
+ export declare const dedupeVersions: (nodes: PreprocessorNode[]) => PreprocessorNode;
24
32
  export declare const highestPrecisions: (nodes: DeclarationStatementNode[]) => DeclarationStatementNode[];
25
- export declare const dedupeQualifiedStatements: (statements: DeclarationStatementNode[], qualifier: string) => any;
33
+ export declare const extractDeclarationNameAndType: (stmt: DeclarationStatementNode) => {
34
+ type: string;
35
+ names: string[];
36
+ };
37
+ export declare const filterQualifiedStatements: (statements: LineAndSource<DeclarationStatementNode>[], filter: (name: string) => boolean) => LineAndSource<DeclarationStatementNode>[];
38
+ export declare const dedupeQualifiedStatements: (statements: DeclarationStatementNode[], qualifier: string) => ProgramStatement[];
39
+ /**
40
+ * Remove uniform declarations by the variable names they declare
41
+ */
42
+ export declare const filterUniformNames: (declarations: LineAndSource<DeclarationStatementNode>[], filter: (name: string) => boolean) => LineAndSource<DeclarationStatementNode>[];
26
43
  /**
27
44
  * Merge uniforms together into lists of identifiers under the same type.
28
45
  * There's special case handling for mixing of uniforms with "interface blocks"
@@ -32,7 +49,7 @@ export declare const dedupeQualifiedStatements: (statements: DeclarationStatemen
32
49
  * This function consumes uniforms as found by findShaderSections, so the
33
50
  * definitions must line up
34
51
  */
35
- export declare const dedupeUniforms: (statements: DeclarationStatementNode[]) => any;
52
+ export declare const dedupeUniforms: (statements: DeclarationStatementNode[]) => DeclarationStatementNode[];
36
53
  export declare const mergeShaderSections: (s1: ShaderSections, s2: ShaderSections) => ShaderSections;
37
54
  export type MergeOptions = {
38
55
  includePrecisions: boolean;
@@ -43,5 +60,5 @@ export declare const shaderSectionsToProgram: (sections: ShaderSections, mergeOp
43
60
  * Group an AST into logical sections. The output of this funciton is consumed
44
61
  * by the dedupe methods, namely dedupeUniforms, so the data shapes are coupled
45
62
  */
46
- export declare const findShaderSections: (ast: Program) => ShaderSections;
63
+ export declare const findShaderSections: (nodeId: string, ast: Program) => ShaderSections;
47
64
  export {};