@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.
- package/README.md +2 -2
- package/engine.d.ts +1 -0
- package/graph/code-nodes.d.ts +8 -2
- package/graph/code-nodes.js +5 -1
- package/graph/context.d.ts +3 -2
- package/graph/context.js +25 -12
- package/graph/evaluate.js +7 -0
- package/graph/graph.d.ts +8 -2
- package/graph/graph.js +105 -125
- package/graph/graph.test.js +226 -50
- package/graph/parsers.d.ts +2 -11
- package/graph/parsers.js +24 -14
- package/graph/shader-sections.d.ts +1 -1
- package/graph/shader-sections.js +1 -1
- package/package.json +4 -4
- package/plugins/babylon/bablyengine.js +4 -1
- package/plugins/playcanvas/playengine.js +4 -1
- package/plugins/three/importers.d.ts +1 -0
- package/plugins/three/importers.js +49 -1
- package/plugins/three/threngine.js +5 -2
- package/plugins/three/threngine.test.js +52 -4
- package/strategy/assignemntTo.js +4 -3
- package/strategy/declarationOf.js +2 -2
- package/strategy/hardCode.js +1 -4
- package/strategy/inject.js +3 -4
- package/strategy/namedAttribute.js +2 -2
- package/strategy/strategy.d.ts +28 -3
- package/strategy/stratgies.test.js +59 -31
- package/strategy/texture2D.js +20 -22
- package/strategy/uniform.js +54 -56
- package/strategy/variable.js +5 -5
- package/util/ast.d.ts +7 -3
- package/util/ast.js +29 -8
- package/util/ast.test.d.ts +1 -0
- package/util/ast.test.js +14 -0
- package/util/whitespace.d.ts +2 -0
- package/util/whitespace.js +27 -1
|
@@ -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 =
|
|
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
|
+
}); });
|
package/strategy/assignemntTo.js
CHANGED
|
@@ -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 (
|
|
21
|
+
function (filler) {
|
|
22
|
+
var filled = filler();
|
|
22
23
|
if ('expression' in assignNode) {
|
|
23
24
|
assignNode.expression.right =
|
|
24
|
-
|
|
25
|
+
filled;
|
|
25
26
|
}
|
|
26
27
|
else {
|
|
27
|
-
assignNode.initializer =
|
|
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 (
|
|
17
|
-
declaration.initializer =
|
|
16
|
+
function (filler) {
|
|
17
|
+
declaration.initializer = filler();
|
|
18
18
|
return ast;
|
|
19
19
|
},
|
|
20
20
|
],
|
package/strategy/hardCode.js
CHANGED
|
@@ -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
|
-
|
|
16
|
-
throw new Error('Cannot use void filler');
|
|
17
|
-
}
|
|
18
|
-
return filler;
|
|
15
|
+
return filler();
|
|
19
16
|
},
|
|
20
17
|
];
|
|
21
18
|
return ci;
|
package/strategy/inject.js
CHANGED
|
@@ -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 (
|
|
66
|
-
var
|
|
67
|
-
|
|
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 (
|
|
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(
|
|
40
|
+
ref.identifier = generateFiller(filler());
|
|
41
41
|
}
|
|
42
42
|
});
|
|
43
43
|
});
|
package/strategy/strategy.d.ts
CHANGED
|
@@ -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 {
|
|
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
|
|
28
|
-
export declare const applyStrategy: (strategy: Strategy, ast: AstNode | Program, node: SourceNode, sibling
|
|
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
|
-
|
|
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](
|
|
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](
|
|
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](
|
|
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
|
|
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(['
|
|
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
|
package/strategy/texture2D.js
CHANGED
|
@@ -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
|
|
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
|
-
|
|
51
|
-
|
|
52
|
-
name
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
path.
|
|
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
|
|
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(
|
|
66
|
+
nodeInput(name, "filler_".concat(name), 'filler', 'vector4', // Data type for what plugs into this filler
|
|
73
67
|
['code', 'data'], false),
|
|
74
|
-
function (
|
|
75
|
-
|
|
76
|
-
|
|
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;
|
package/strategy/uniform.js
CHANGED
|
@@ -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
|
-
|
|
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 || [])
|
|
133
|
-
|
|
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
|
-
|
|
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
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
//
|
|
142
|
-
|
|
143
|
-
) {
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
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
|
};
|
package/strategy/variable.js
CHANGED
|
@@ -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 (
|
|
52
|
-
ref.identifier.identifier = generateFiller(
|
|
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 (
|
|
59
|
-
ref.identifier = generateFiller(
|
|
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: (
|
|
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;
|