@shaderfrog/core 2.0.0-beta.3 → 2.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.
@@ -37,15 +37,17 @@ var __generator = (this && this.__generator) || function (thisArg, body) {
37
37
  import { expect, it } from 'vitest';
38
38
  import { outputNode, sourceNode } from '../../graph/graph-node';
39
39
  import { makeEdge } from '../../graph/edge';
40
- import { compileSource } from '../../graph/graph';
41
- import { texture2DStrategy } from '../../strategy';
40
+ import { compileSource, nodeName } from '../../graph/graph';
41
+ import { namedAttributeStrategy, texture2DStrategy } from '../../strategy';
42
42
  import { isError } from '../../graph/context';
43
43
  import { threngine } from './threngine';
44
44
  import { makeId } from '../../util/id';
45
45
  import { fail } from '../../test-util';
46
+ import importers from './importers';
47
+ import { generate, parser } from '@shaderfrog/glsl-parser';
46
48
  var p = { x: 0, y: 0 };
47
49
  var makeSourceNode = function (id, source, stage, strategies) {
48
- if (strategies === void 0) { strategies = [texture2DStrategy()]; }
50
+ if (strategies === void 0) { strategies = [texture2DStrategy(), namedAttributeStrategy('position')]; }
49
51
  return sourceNode(id, "Shader ".concat(id), p, {
50
52
  version: 2,
51
53
  preprocess: false,
@@ -82,8 +84,54 @@ it('threngine compileSource() and manipulateAst()', function () { return __await
82
84
  // Threngine has parsers for vertex shaders, make sure that is set properly
83
85
  expect(result.vertexResult).toContain("vec4 main_Shader_".concat(vertInput.id, "() {\n vec4 frogOut = modelViewMatrix * vec4(position, 1.0);\n return frogOut;\n}"));
84
86
  // Check that it inlned. For fun.
85
- expect(result.vertexResult).toContain("gl_Position = main_Shader_".concat(vertInput.id, "();"));
87
+ expect(result.vertexResult).toContain("gl_Position = ".concat(nodeName(vertInput), "();"));
86
88
  return [2 /*return*/];
87
89
  }
88
90
  });
89
91
  }); });
92
+ it('threngine compileSource() linking through vertex', function () { return __awaiter(void 0, void 0, void 0, function () {
93
+ var outV, outF, vert1, vert2, graph, engineContext, result;
94
+ return __generator(this, function (_a) {
95
+ switch (_a.label) {
96
+ case 0:
97
+ outV = outputNode(makeId(), 'Output v', p, 'vertex');
98
+ outF = outputNode(makeId(), 'Output f', p, 'fragment');
99
+ vert1 = makeSourceNode(makeId(), "void main() {\n gl_Position = modelViewMatrix * vec4(position, 1.0);\n}\n", 'vertex');
100
+ vert2 = makeSourceNode(makeId(), "void main() {\n gl_Position = modelViewMatrix * vec4(position, 1.0);\n}\n", 'vertex');
101
+ graph = {
102
+ nodes: [outV, outF, vert1, vert2],
103
+ edges: [
104
+ makeEdge(makeId(), vert1.id, outV.id, 'out', 'filler_gl_Position', 'vertex'),
105
+ makeEdge(makeId(), vert2.id, vert1.id, 'out', 'filler_position', 'vertex'),
106
+ ],
107
+ };
108
+ engineContext = {
109
+ engine: 'three',
110
+ nodes: {},
111
+ runtime: {},
112
+ debuggingNonsense: {},
113
+ };
114
+ return [4 /*yield*/, compileSource(graph, threngine, engineContext)];
115
+ case 1:
116
+ result = _a.sent();
117
+ if (isError(result)) {
118
+ fail(result);
119
+ }
120
+ // Because vert2 links through vert1, it should be a vec3, not a vec4
121
+ expect(result.vertexResult).toContain("vec3 ".concat(nodeName(vert2), "() {"));
122
+ expect(result.vertexResult).toContain("vec4 ".concat(nodeName(vert1), "() {"));
123
+ return [2 /*return*/];
124
+ }
125
+ });
126
+ }); });
127
+ it('threngine shadertoy import', function () { return __awaiter(void 0, void 0, void 0, function () {
128
+ var testImport, p;
129
+ return __generator(this, function (_a) {
130
+ testImport = "\nvoid mainImage( out vec4 fragColor, in vec2 fragCoord ){\n vec3 rd = normalize(vec3(2.*fragCoord - iResolution.xy, iResolution.y));\n fragColor = vec4(sqrt(clamp(col, 0., 1.)), 1.0 * iTime);\n}\n";
131
+ p = parser.parse(testImport);
132
+ importers.shadertoy.convertAst(p, 'fragment');
133
+ console.log(generate(p));
134
+ expect(generate(p)).toContain("\nprecision highp float;\nprecision highp int;\n\nuniform vec2 mouse;\nuniform float time;\nuniform vec2 renderResolution;\n\nvoid main() {\n vec3 rd = normalize(vec3(2.*gl_FragCoord.xy - renderResolution.xy, renderResolution.y));\n gl_FragColor = vec4(sqrt(clamp(col, 0., 1.)), 1.0 * time);\n}\n");
135
+ return [2 /*return*/];
136
+ });
137
+ }); });
@@ -18,13 +18,14 @@ export var applyAssignmentToStrategy = function (strategy, ast, graphNode, sibli
18
18
  [
19
19
  nodeInput(name, "filler_".concat(name), 'filler', undefined, // Data type for what plugs into this filler
20
20
  ['code', 'data'], false),
21
- function (fillerAst) {
21
+ function (filler) {
22
+ var filled = filler();
22
23
  if ('expression' in assignNode) {
23
24
  assignNode.expression.right =
24
- fillerAst;
25
+ filled;
25
26
  }
26
27
  else {
27
- assignNode.initializer = fillerAst;
28
+ assignNode.initializer = filled;
28
29
  }
29
30
  return ast;
30
31
  },
@@ -13,8 +13,8 @@ export var constApplyDeclarationOf = function (strategy, ast, graphNode, sibling
13
13
  [
14
14
  nodeInput(name, "filler_".concat(name), 'filler', undefined, // Data type for what plugs into this filler
15
15
  ['code', 'data'], false),
16
- function (fillerAst) {
17
- declaration.initializer = fillerAst;
16
+ function (filler) {
17
+ declaration.initializer = filler();
18
18
  return ast;
19
19
  },
20
20
  ],
@@ -12,10 +12,7 @@ export var applyHardCodeStrategy = function (strategy, ast, graphNode, siblingNo
12
12
  // so this awkward branch is to make types line up. But I don't think
13
13
  // this code path would ever get called
14
14
  function (filler) {
15
- if (!filler || Array.isArray(filler)) {
16
- throw new Error('Cannot use void filler');
17
- }
18
- return filler;
15
+ return filler();
19
16
  },
20
17
  ];
21
18
  return ci;
@@ -62,10 +62,9 @@ export var applyInjectStrategy = function (strategy, ast, graphNode, siblingNode
62
62
  [
63
63
  nodeInput(name, "filler_".concat(name), 'filler', undefined, // Data type for what plugs into this filler
64
64
  ['code', 'data'], false),
65
- function (fillerAst) {
66
- var toInsert = Array.isArray(fillerAst)
67
- ? fillerAst
68
- : [fillerAst];
65
+ function (filler) {
66
+ var result = filler();
67
+ var toInsert = Array.isArray(result) ? result : [result];
69
68
  // Loop backwards through the matches because when we inject code into
70
69
  // parent nodes, it modifies the statement list arrays
71
70
  for (
@@ -28,7 +28,7 @@ export var applyNamedAttributeStrategy = function (strategy, ast, graphNode, sib
28
28
  [
29
29
  nodeInput(attributeName, "filler_".concat(attributeName), 'filler', undefined, // Data type for what plugs into this filler
30
30
  ['code', 'data'], true),
31
- function (fillerAst) {
31
+ function (filler) {
32
32
  Object.entries(program.scopes[0].bindings).forEach(function (_a) {
33
33
  var _b = __read(_a, 2), name = _b[0], binding = _b[1];
34
34
  binding.references.forEach(function (ref) {
@@ -37,7 +37,7 @@ export var applyNamedAttributeStrategy = function (strategy, ast, graphNode, sib
37
37
  if (ref.type === 'identifier' &&
38
38
  ref !== binding.declaration &&
39
39
  ref.identifier === attributeName) {
40
- ref.identifier = generateFiller(fillerAst);
40
+ ref.identifier = generateFiller(filler());
41
41
  }
42
42
  });
43
43
  });
@@ -8,7 +8,32 @@ import { Texture2DStrategy } from './texture2D';
8
8
  import { NamedAttributeStrategy } from './namedAttribute';
9
9
  import { VariableStrategy } from './variable';
10
10
  import { InjectStrategy } from './inject';
11
- import { ComputedInput } from '../graph/parsers';
11
+ import { NodeInput } from '../graph';
12
+ /**
13
+ * Fillers are generated by *strategies*. Strategies are applied to the AST
14
+ * by applyStrategy() in strategy.ts, which runs strategy functions like
15
+ * applyTexture2DStrategy(), which returns a list of fillers found by each
16
+ * strategy.
17
+ */
18
+ export type Filler = (...args: string[]) => AstNode | AstNode[] | void;
19
+ export type InputFiller = (filler: Filler) => AstNode | Program;
20
+ export type FillerArguments = AstNode[];
21
+ export type FillerStmt = AstNode;
22
+ export type InputFillerGroup = {
23
+ filler: InputFiller;
24
+ fillerArgs?: FillerArguments;
25
+ fillerStmt?: AstNode;
26
+ };
27
+ export type InputFillers = Record<string, InputFillerGroup>;
28
+ /**
29
+ * A computed input is ultimately what the strategy application process returns
30
+ */
31
+ export type ComputedInput = [
32
+ NodeInput,
33
+ InputFiller,
34
+ FillerArguments?,
35
+ FillerStmt?
36
+ ];
12
37
  export declare enum StrategyType {
13
38
  VARIABLE = "Variable Names",
14
39
  ASSIGNMENT_TO = "Assignment To",
@@ -24,5 +49,5 @@ export interface BaseStrategy {
24
49
  config: Object;
25
50
  }
26
51
  export type Strategy = UniformStrategy | AssignemntToStrategy | Texture2DStrategy | NamedAttributeStrategy | VariableStrategy | HardCodeStrategy | InjectStrategy | DeclarationOfStrategy;
27
- export type ApplyStrategy<T> = (strategy: T, ast: AstNode | Program, node: SourceNode, sibling: SourceNode) => ComputedInput[];
28
- export declare const applyStrategy: (strategy: Strategy, ast: AstNode | Program, node: SourceNode, sibling: SourceNode) => ComputedInput[];
52
+ export type ApplyStrategy<T> = (strategy: T, ast: AstNode | Program, node: SourceNode, sibling?: SourceNode) => ComputedInput[];
53
+ export declare const applyStrategy: (strategy: Strategy, ast: AstNode | Program, node: SourceNode, sibling?: SourceNode) => ComputedInput[];
@@ -20,6 +20,8 @@ import { generate } from '@shaderfrog/glsl-parser';
20
20
  import { applyStrategy, StrategyType } from '.';
21
21
  import { makeExpression } from '../util/ast';
22
22
  import preprocess from '@shaderfrog/glsl-parser/preprocessor';
23
+ import { NodeType } from '../graph/graph-types';
24
+ import { mangleEntireProgram } from '../graph';
23
25
  it('named attribute strategy`', function () {
24
26
  var source = "\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = replaceThisAtrribute;\n}\n";
25
27
  var ast = parser.parse(source, { quiet: true });
@@ -30,11 +32,11 @@ it('named attribute strategy`', function () {
30
32
  },
31
33
  }, ast, { source: source }, {});
32
34
  expect(fillers.length).toBe(1);
33
- fillers[0][1]({
35
+ fillers[0][1](function () { return ({
34
36
  type: 'literal',
35
37
  literal: "myFiller()",
36
38
  whitespace: '',
37
- });
39
+ }); });
38
40
  var result = generate(ast);
39
41
  // Should replace the use of the filler, but not the declaration
40
42
  expect(result).toBe("\nin vec3 replaceThisAtrribute;\nvoid main() {\n vec2 y = myFiller();\n}\n");
@@ -51,11 +53,11 @@ it('inject strategy after', function () {
51
53
  },
52
54
  }, ast, { source: source }, {});
53
55
  expect(fillers.length).toBe(1);
54
- fillers[0][1]({
56
+ fillers[0][1](function () { return ({
55
57
  type: 'literal',
56
58
  literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
57
59
  whitespace: '',
58
- });
60
+ }); });
59
61
  var result = generate(ast);
60
62
  // Should fill references
61
63
  expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Middle comment\nre(x, y, z);\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\n// Final comment\n}");
@@ -72,11 +74,11 @@ it('inject strategy before', function () {
72
74
  },
73
75
  }, ast, { source: source }, {});
74
76
  expect(fillers.length).toBe(1);
75
- fillers[0][1]({
77
+ fillers[0][1](function () { return ({
76
78
  type: 'literal',
77
79
  literal: "someOtherCall(x, y, z);\nsomeOtherCall(x, y, z);",
78
80
  whitespace: '\n',
79
- });
81
+ }); });
80
82
  var result = generate(ast);
81
83
  // Should fill references
82
84
  expect(result).toBe("\nuniform float x;\n// Some comment\nvoid main() {\n/* some comment */\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Middle comment\nsomeOtherCall(x, y, z);\nsomeOtherCall(x, y, z);\nre(x, y, z);\n// Final comment\n}");
@@ -91,7 +93,7 @@ var constructor = function () { return ({
91
93
  id: '1',
92
94
  name: '1',
93
95
  engine: true,
94
- type: '',
96
+ type: NodeType.SOURCE,
95
97
  inputs: [],
96
98
  outputs: [],
97
99
  position: { x: 0, y: 0 },
@@ -122,25 +124,7 @@ var engine = {
122
124
  it('correctly fills with uniform strategy', function () {
123
125
  var _a, _b, _c;
124
126
  var ast = parser.parse("\nlayout(std140,column_major) uniform;\nuniform sampler2D image;\nuniform vec4 input, output, other;\nuniform vec4 zenput;\nuniform Light0 { vec4 y; } x;\nvec3 topLevel = vec3(0.0);\nvoid other(in vec3 param) {}\nvoid main() {\n vec4 computed = texture2D(image, uvPow * 1.0);\n vec4 x = input;\n vec4 y = output;\n vec4 z = zenput;\n}", { quiet: true });
125
- // TODO: Experimenting with strategy tests where we mangle in the test to
126
- // avoid having to mangle in the strategy, in service of maybe mangling the
127
- // AST as part of producing context. But as the test shows -
128
- // mangleEntireProgram does NOT modify binding names
129
- //
130
- // You started updating binding names in the parser but realized that
131
- // technically a mangler can produce different results for different nodes
132
- // during the rename, since the parser takes in the node to mangle.
133
- //
134
- // which raised the question about why pass in the node at all to the mangler?
135
- // looks like it's for "doNotDescope" hack to avoid renaming a specific
136
- // varaible.
137
- //
138
- // But maybe that could be done here instead? And mangleEntireProgram could be
139
- // aware of the output varaibles to ignore? Which means we need to track the
140
- // output varialbe names somewhere... do we alredy?
141
- var node = { name: 'fake', id: '1' };
142
- // mangleEntireProgram(engine, ast, node);
143
- var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, {}, {});
127
+ var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, {});
144
128
  // It should find uniforms with simple types, excluding sampler2D
145
129
  expect(fillers.map(function (_a) {
146
130
  var _b = __read(_a, 1), name = _b[0].displayName;
@@ -155,15 +139,21 @@ it('correctly fills with uniform strategy', function () {
155
139
  (_a = fillers.find(function (_a) {
156
140
  var _b = __read(_a, 1), name = _b[0].displayName;
157
141
  return name === 'input';
158
- })) === null || _a === void 0 ? void 0 : _a[1](makeExpression('a'));
142
+ })) === null || _a === void 0 ? void 0 : _a[1](function () {
143
+ return makeExpression('a');
144
+ });
159
145
  (_b = fillers.find(function (_a) {
160
146
  var _b = __read(_a, 1), name = _b[0].displayName;
161
147
  return name === 'output';
162
- })) === null || _b === void 0 ? void 0 : _b[1](makeExpression('b'));
148
+ })) === null || _b === void 0 ? void 0 : _b[1](function () {
149
+ return makeExpression('b');
150
+ });
163
151
  (_c = fillers.find(function (_a) {
164
152
  var _b = __read(_a, 1), name = _b[0].displayName;
165
153
  return name === 'zenput';
166
- })) === null || _c === void 0 ? void 0 : _c[1](makeExpression('c'));
154
+ })) === null || _c === void 0 ? void 0 : _c[1](function () {
155
+ return makeExpression('c');
156
+ });
167
157
  var result = generate(ast);
168
158
  // Should fill references
169
159
  expect(result).toContain('vec4 x = a;');
@@ -178,6 +168,44 @@ it('correctly fills with uniform strategy', function () {
178
168
  // Should remove uniform lines
179
169
  expect(result).not.toContain('uniform vec4 zenput');
180
170
  });
171
+ it('correctly fills with uniform strategy through mangling', function () {
172
+ var _a, _b, _c;
173
+ var ast = parser.parse("\nuniform sampler2D image;\nuniform vec4 input, output;\nvoid main() {\n vec4 computed = texture2D(image, uvPow * 1.0);\n vec4 x = input;\n vec4 y = output;\n}", { quiet: true });
174
+ var node = { id: '1', name: 'fake' };
175
+ var fillers = applyStrategy({ type: StrategyType.UNIFORM, config: {} }, ast, node);
176
+ mangleEntireProgram(engine, ast, node);
177
+ // It should find uniforms with simple types, excluding sampler2D
178
+ expect(fillers.map(function (_a) {
179
+ var _b = __read(_a, 1), name = _b[0].displayName;
180
+ return name;
181
+ })).toEqual([
182
+ 'image',
183
+ 'input',
184
+ 'output',
185
+ ]);
186
+ var a = (_a = fillers.find(function (_a) {
187
+ var _b = __read(_a, 1), name = _b[0].displayName;
188
+ return name === 'input';
189
+ })) === null || _a === void 0 ? void 0 : _a[1];
190
+ (_b = fillers.find(function (_a) {
191
+ var _b = __read(_a, 1), name = _b[0].displayName;
192
+ return name === 'input';
193
+ })) === null || _b === void 0 ? void 0 : _b[1](function () {
194
+ return makeExpression('a');
195
+ });
196
+ (_c = fillers.find(function (_a) {
197
+ var _b = __read(_a, 1), name = _b[0].displayName;
198
+ return name === 'output';
199
+ })) === null || _c === void 0 ? void 0 : _c[1](function () {
200
+ return makeExpression('b');
201
+ });
202
+ var result = generate(ast);
203
+ // Should fill references
204
+ expect(result).toContain('vec4 x = a;');
205
+ expect(result).toContain('vec4 y = b;');
206
+ // Should preserve things it shouldn't touch
207
+ expect(result).toContain("uniform sampler2D image_".concat(node.id, ";"));
208
+ });
181
209
  it('uses name without suffix for single call', function () {
182
210
  var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n}", { quiet: true });
183
211
  expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, ast, {}, {}).map(function (_a) {
@@ -185,12 +213,12 @@ it('uses name without suffix for single call', function () {
185
213
  return name;
186
214
  })).toEqual(['noiseImage']);
187
215
  });
188
- it('finds multiple texture2D inputs for one uniform', function () {
216
+ it('finds one texture2D input for one texture2D() call', function () {
189
217
  var ast = parser.parse("\nvoid main() {\n vec4 computed = texture2D(noiseImage, uvPow * 1.0);\n computed += texture2D(noiseImage, uvPow * 2.0);\n}", { quiet: true });
190
218
  expect(applyStrategy({ type: StrategyType.TEXTURE_2D, config: {} }, ast, {}, {}).map(function (_a) {
191
219
  var _b = __read(_a, 1), name = _b[0].displayName;
192
220
  return name;
193
- })).toEqual(['noiseImage_0', 'noiseImage_1']);
221
+ })).toEqual(['noiseImage']);
194
222
  });
195
223
  it('Make sure texture2D finds preprocessed texture() call', function () {
196
224
  // I thought this was a regression, but it wasn't a real bug, but tests seems
@@ -33,7 +33,7 @@ export var texture2DStrategy = function () { return ({
33
33
  }); };
34
34
  export var applyTexture2DStrategy = function (strategy, ast, graphNode, siblingNode) {
35
35
  var texture2Dcalls = [];
36
- var seen = {};
36
+ var references = {};
37
37
  var visitors = {
38
38
  function_call: {
39
39
  enter: function (path) {
@@ -47,36 +47,34 @@ export var applyTexture2DStrategy = function (strategy, ast, graphNode, siblingN
47
47
  throw new Error('This error is impossible. A function call always has a parent.');
48
48
  }
49
49
  var name = generate(path.node.args[0]);
50
- seen[name] = (seen[name] || 0) + 1;
51
- texture2Dcalls.push([
52
- name,
53
- path.parent,
54
- path.key,
55
- // Remove the first argument and comma
56
- path.node.args.slice(2),
57
- ]);
50
+ if (!(name in references)) {
51
+ references[name] = [];
52
+ texture2Dcalls.push(name);
53
+ }
54
+ references[name].push({
55
+ parent: path.parent,
56
+ key: path.key,
57
+ args: path.node.args.slice(2),
58
+ });
58
59
  }
59
60
  },
60
61
  },
61
62
  };
62
63
  visit(ast, visitors);
63
- var names = new Set(Object.entries(seen).reduce(function (arr, _a) {
64
- var _b = __read(_a, 2), name = _b[0], count = _b[1];
65
- return __spreadArray(__spreadArray([], __read(arr), false), __read((count > 1 ? [name] : [])), false);
66
- }, []));
67
- var inputs = texture2Dcalls.map(function (_a, index) {
68
- var _b = __read(_a, 4), name = _b[0], parent = _b[1], key = _b[2], texture2dArgs = _b[3];
69
- // Suffix input name if it's used more than once
70
- var iName = names.has(name) ? "".concat(name, "_").concat(index) : name;
64
+ var inputs = texture2Dcalls.map(function (name) {
71
65
  return [
72
- nodeInput(iName, "filler_".concat(iName), 'filler', 'vector4', // Data type for what plugs into this filler
66
+ nodeInput(name, "filler_".concat(name), 'filler', 'vector4', // Data type for what plugs into this filler
73
67
  ['code', 'data'], false),
74
- function (fillerAst) {
75
- // @ts-ignore
76
- parent[key] = fillerAst;
68
+ function (filler) {
69
+ references[name].forEach(function (_a) {
70
+ var parent = _a.parent, key = _a.key, args = _a.args;
71
+ // Backfilling into the filler! Similar to parsers.ts filler
72
+ var f = filler.apply(void 0, __spreadArray([], __read(args.map(generate)), false));
73
+ // @ts-ignore
74
+ parent[key] = f;
75
+ });
77
76
  return ast;
78
77
  },
79
- texture2dArgs,
80
78
  ];
81
79
  });
82
80
  return inputs;
@@ -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 __read = (this && this.__read) || function (o, n) {
2
13
  var m = typeof Symbol === "function" && o[Symbol.iterator];
3
14
  if (!m) return o;
@@ -17,6 +28,7 @@ var __read = (this && this.__read) || function (o, n) {
17
28
  import { nodeInput } from '../graph/base-node';
18
29
  import { StrategyType } from '.';
19
30
  import { generateFiller } from '../util/ast';
31
+ import { renameBinding } from '@shaderfrog/glsl-parser/parser/utils';
20
32
  export var uniformStrategy = function () { return ({
21
33
  type: StrategyType.UNIFORM,
22
34
  config: {},
@@ -127,64 +139,50 @@ var mapUniformType = function (type) {
127
139
  }
128
140
  // console.log(`Unknown uniform type, can't map to graph: ${type}`);
129
141
  };
130
- export var applyUniformStrategy = function (strategy, ast, graphNode, siblingNode) {
142
+ var isUniformDeclaration = function (node) {
143
+ var _a, _b, _c;
144
+ return node.type === 'declaration_statement' &&
145
+ node.declaration.type === 'declarator_list' &&
146
+ !!((_c = (_b = (_a = node.declaration) === null || _a === void 0 ? void 0 : _a.specified_type) === null || _b === void 0 ? void 0 : _b.qualifiers) === null || _c === void 0 ? void 0 : _c.find(function (n) { return n.token === 'uniform'; }));
147
+ };
148
+ // commented this out to allow for sampler2D uniforms to appear as inputs
149
+ // && uniformType !== 'sampler2D'
150
+ export var applyUniformStrategy = function (strategy, ast, graphNode) {
131
151
  var program = ast;
132
- return (program.program || []).flatMap(function (node) {
133
- var _a, _b, _c, _d, _e, _f, _g;
152
+ return (program.program || [])
153
+ .filter(isUniformDeclaration)
154
+ .flatMap(function (node) {
155
+ var _a, _b, _c;
156
+ var declaration = node.declaration;
134
157
  // The uniform declaration type, like vec4
135
- var uniformType = (_d = (_c = (_b = (_a = node.declaration) === null || _a === void 0 ? void 0 : _a.specified_type) === null || _b === void 0 ? void 0 : _b.specifier) === null || _c === void 0 ? void 0 : _c.specifier) === null || _d === void 0 ? void 0 : _d.token;
158
+ // TODO: File VSCode bug, this is highlighted like a function
159
+ var uniformType = (_c = (_b = (_a = declaration === null || declaration === void 0 ? void 0 : declaration.specified_type) === null || _a === void 0 ? void 0 : _a.specifier) === null || _b === void 0 ? void 0 : _b.specifier) === null || _c === void 0 ? void 0 : _c.token;
136
160
  var graphDataType = mapUniformType(uniformType);
137
- // If this is a uniform declaration line
138
- if (node.type === 'declaration_statement' &&
139
- node.declaration.type === 'declarator_list' &&
140
- ((_g = (_f = (_e = node.declaration) === null || _e === void 0 ? void 0 : _e.specified_type) === null || _f === void 0 ? void 0 : _f.qualifiers) === null || _g === void 0 ? void 0 : _g.find(function (n) { return n.token === 'uniform'; }))
141
- // commented this out to allow for sampler2D uniforms to appear as inputs
142
- // && uniformType !== 'sampler2D'
143
- ) {
144
- // Capture all the declared names, removing mangling suffix
145
- var declarations_1 = node.declaration.declarations;
146
- var names = declarations_1.map(function (d) { return d.identifier.identifier; });
147
- // Tricky code warning: The flow of preparing a node for the graph is:
148
- // 1. Produce/mangle the AST (with unmangled names)
149
- // 2. findInputs() (with unmangled names)
150
- // 3. The AST is *then* mangled in graph.ts
151
- // 4. Later, the inputs are filled in, and now, we have an input with
152
- // the name "x" but the ast now has the mangled name "x_1". So
153
- // here, we look for the *mangled* name in the strategy runner
154
- return names.map(function (name) { return [
155
- nodeInput(name, "uniform_".concat(name), 'uniform', graphDataType, ['code', 'data'], true),
156
- function (filler) {
157
- //const mangledName = mangleName(name, graphNode, siblingNode);
158
- var mangledName = name;
159
- // Remove the declaration line, or the declared uniform
160
- if (declarations_1.length === 1) {
161
- program.program.splice(program.program.indexOf(node), 1);
162
- }
163
- else {
164
- var decl = node.declaration;
165
- decl.declarations = decl.declarations.filter(function (d) { return d.identifier.identifier !== mangledName; });
166
- }
167
- // And rename all the references to said uniform
168
- program.scopes[0].bindings[name].references.forEach(function (ref) {
169
- if (ref.type === 'identifier' && ref.identifier === mangledName) {
170
- ref.identifier = generateFiller(filler);
171
- }
172
- else if (ref.type === 'parameter_declaration' &&
173
- 'identifier' in ref &&
174
- ref.identifier.identifier === mangledName) {
175
- ref.identifier.identifier = generateFiller(filler);
176
- }
177
- else if ('identifier' in ref) {
178
- ref.identifier = generateFiller(filler);
179
- }
180
- else {
181
- console.warn('Unknown uniform reference for', graphNode.name, 'ref');
182
- }
183
- });
184
- return ast;
185
- },
186
- ]; });
187
- }
188
- return [];
161
+ var declarations = declaration.declarations;
162
+ // Capture the uniform names, and then capture their references in the
163
+ // closure. This allows the scope binding to be renamed when the AST is
164
+ // mangled, but this strategy can still find the original named variables
165
+ // to work with
166
+ var names = declarations.map(function (d) { return d.identifier.identifier; });
167
+ var references = names.reduce(function (acc, name) {
168
+ var _a;
169
+ return (__assign(__assign({}, acc), (_a = {}, _a[name] = program.scopes[0].bindings[name], _a)));
170
+ }, {});
171
+ return names.map(function (name) { return [
172
+ nodeInput(name, "uniform_".concat(name), 'uniform', graphDataType, ['code', 'data'], true),
173
+ function (filler) {
174
+ // Remove the declaration line, or the declared uniform
175
+ if (declarations.length === 1) {
176
+ program.program.splice(program.program.indexOf(node), 1);
177
+ }
178
+ else {
179
+ var decl = node.declaration;
180
+ decl.declarations = decl.declarations.filter(function (d) { return d.identifier.identifier !== name; });
181
+ }
182
+ // Rename all the references to said uniform
183
+ renameBinding(references[name], generateFiller(filler()));
184
+ return ast;
185
+ },
186
+ ]; });
189
187
  });
190
188
  };
@@ -35,7 +35,7 @@ var __spreadArray = (this && this.__spreadArray) || function (to, from, pack) {
35
35
  return to.concat(ar || Array.prototype.slice.call(from));
36
36
  };
37
37
  import { nodeInput } from '../graph/base-node';
38
- import { StrategyType } from '.';
38
+ import { StrategyType, } from '.';
39
39
  import { generateFiller } from '../util/ast';
40
40
  export var variableStrategy = function () { return ({
41
41
  type: StrategyType.VARIABLE,
@@ -48,15 +48,15 @@ export var applyVariableStrategy = function (strategy, ast, graphNode, siblingNo
48
48
  var identifier, replacer;
49
49
  if (ref.type === 'declaration') {
50
50
  identifier = ref.identifier.identifier;
51
- replacer = function (fillerAst) {
52
- ref.identifier.identifier = generateFiller(fillerAst);
51
+ replacer = function (filler) {
52
+ ref.identifier.identifier = generateFiller(filler());
53
53
  return ast;
54
54
  };
55
55
  }
56
56
  else if (ref.type === 'identifier') {
57
57
  identifier = ref.identifier;
58
- replacer = function (fillerAst) {
59
- ref.identifier = generateFiller(fillerAst);
58
+ replacer = function (filler) {
59
+ ref.identifier = generateFiller(filler());
60
60
  return ast;
61
61
  };
62
62
  // } else if (ref.type === 'parameter_declaration') {
package/util/ast.d.ts CHANGED
@@ -1,8 +1,10 @@
1
+ /**
2
+ * Utility functions to work with ASTs
3
+ */
1
4
  import { AstNode, ExpressionStatementNode, FunctionNode, DeclarationStatementNode, DeclarationNode } from '@shaderfrog/glsl-parser/ast';
2
5
  import { Program } from '@shaderfrog/glsl-parser/ast';
3
6
  import { ShaderStage } from '../graph/graph-types';
4
7
  import { Scope } from '@shaderfrog/glsl-parser/parser/scope';
5
- import { Filler } from '../graph/parsers';
6
8
  export interface FrogProgram extends Program {
7
9
  outVar?: string;
8
10
  }
@@ -10,7 +12,7 @@ export declare const findVec4Constructor: (ast: AstNode) => AstNode | undefined;
10
12
  export declare const findAssignmentTo: (ast: AstNode | Program, assignTo: string, nth?: number) => DeclarationNode | ExpressionStatementNode | undefined;
11
13
  export declare const findDeclarationOf: (ast: AstNode | Program, declarationOf: string) => DeclarationNode | undefined;
12
14
  export declare const from2To3: (ast: Program, stage: ShaderStage) => void;
13
- export declare const makeStatement: (stmt: string) => readonly [DeclarationStatementNode | FunctionNode | import("@shaderfrog/glsl-parser/ast").PreprocessorNode, Scope];
15
+ export declare const makeStatement: (stmt: string, ws?: string) => readonly [DeclarationStatementNode | FunctionNode | import("@shaderfrog/glsl-parser/ast").PreprocessorNode, Scope];
14
16
  export declare const makeFnStatement: (fnStmt: string) => readonly [AstNode, Scope];
15
17
  /**
16
18
  * Add a new scope into an existing one. Meant to be used for adding net new
@@ -46,4 +48,6 @@ export declare const returnGlPositionVec3Right: (fnName: string, ast: Program) =
46
48
  * returns a vec4.
47
49
  */
48
50
  export declare const convert300MainToReturn: (ast: FrogProgram) => void;
49
- export declare const generateFiller: (filler: Filler) => string;
51
+ export declare const generateFiller: (ast: AstNode | AstNode[] | void) => string;
52
+ export declare const isDeclarationStatement: (node: Program["program"][0]) => node is DeclarationStatementNode;
53
+ export declare const backfillAst: (ast: Program, fromType: string, targetVariable: string, mainFn?: FunctionNode) => Program;