@thi.ng/shader-ast-optimize 0.3.31 → 0.3.33
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/CHANGELOG.md +1 -1
- package/contant-folding.js +130 -183
- package/package.json +11 -9
package/CHANGELOG.md
CHANGED
package/contant-folding.js
CHANGED
|
@@ -4,211 +4,158 @@ import { deg, rad } from "@thi.ng/math/angle";
|
|
|
4
4
|
import { clamp } from "@thi.ng/math/interval";
|
|
5
5
|
import { mix } from "@thi.ng/math/mix";
|
|
6
6
|
import { fract, mod } from "@thi.ng/math/prec";
|
|
7
|
-
import {
|
|
7
|
+
import {
|
|
8
|
+
isFloat,
|
|
9
|
+
isInt,
|
|
10
|
+
isLitNumericConst,
|
|
11
|
+
isLitVecConst,
|
|
12
|
+
isUint
|
|
13
|
+
} from "@thi.ng/shader-ast/ast/checks";
|
|
8
14
|
import { FLOAT0, float, int, lit, uint } from "@thi.ng/shader-ast/ast/lit";
|
|
9
15
|
import { allChildren, walk } from "@thi.ng/shader-ast/ast/scope";
|
|
10
16
|
import { LOGGER } from "@thi.ng/shader-ast/logger";
|
|
11
|
-
/**
|
|
12
|
-
* Replaces contents of `node` with those of `next`. All other existing props in
|
|
13
|
-
* `node` will be removed.
|
|
14
|
-
*
|
|
15
|
-
* @param node -
|
|
16
|
-
* @param next -
|
|
17
|
-
*
|
|
18
|
-
* @internal
|
|
19
|
-
*/
|
|
20
17
|
const replaceNode = (node, next) => {
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
18
|
+
if (LOGGER.level <= LogLevel.DEBUG) {
|
|
19
|
+
LOGGER.debug(`replacing AST node:`);
|
|
20
|
+
LOGGER.debug(" old: " + JSON.stringify(node));
|
|
21
|
+
LOGGER.debug(" new: " + JSON.stringify(next));
|
|
22
|
+
}
|
|
23
|
+
for (let k in node) {
|
|
24
|
+
!next.hasOwnProperty(k) && delete node[k];
|
|
25
|
+
}
|
|
26
|
+
Object.assign(node, next);
|
|
27
|
+
return true;
|
|
31
28
|
};
|
|
32
29
|
const replaceNumericNode = (node, res) => {
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
30
|
+
node.type === "int" && (res |= 0);
|
|
31
|
+
node.type === "uint" && (res >>>= 0);
|
|
32
|
+
return replaceNode(node, lit(node.type, res));
|
|
36
33
|
};
|
|
37
|
-
|
|
38
|
-
const maybeFoldMath = (op, l, r) => op === "+"
|
|
39
|
-
? l + r
|
|
40
|
-
: op === "-"
|
|
41
|
-
? l - r
|
|
42
|
-
: op === "*"
|
|
43
|
-
? l * r
|
|
44
|
-
: op === "/"
|
|
45
|
-
? l / r
|
|
46
|
-
: undefined;
|
|
47
|
-
/** @internal */
|
|
34
|
+
const maybeFoldMath = (op, l, r) => op === "+" ? l + r : op === "-" ? l - r : op === "*" ? l * r : op === "/" ? l / r : void 0;
|
|
48
35
|
const COMPS = { x: 0, y: 1, z: 2, w: 3 };
|
|
49
36
|
const BUILTINS = {
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
37
|
+
abs: ([a]) => Math.abs(a),
|
|
38
|
+
acos: ([a]) => Math.acos(a),
|
|
39
|
+
asin: ([a]) => Math.asin(a),
|
|
40
|
+
ceil: ([a]) => Math.ceil(a),
|
|
41
|
+
clamp: ([a, b, c]) => clamp(a, b, c),
|
|
42
|
+
cos: ([a]) => Math.cos(a),
|
|
43
|
+
degrees: ([a]) => deg(a),
|
|
44
|
+
exp: ([a]) => Math.exp(a),
|
|
45
|
+
exp2: ([a]) => Math.pow(2, a),
|
|
46
|
+
floor: ([a]) => Math.floor(a),
|
|
47
|
+
fract: ([a]) => fract(a),
|
|
48
|
+
inversesqrt: ([a]) => 1 / Math.sqrt(a),
|
|
49
|
+
log: ([a]) => Math.log(a),
|
|
50
|
+
log2: ([a]) => Math.log2(a),
|
|
51
|
+
max: ([a, b]) => Math.max(a, b),
|
|
52
|
+
min: ([a, b]) => Math.min(a, b),
|
|
53
|
+
mix: ([a, b, c]) => mix(a, b, c),
|
|
54
|
+
mod: ([a, b]) => mod(a, b),
|
|
55
|
+
pow: ([a, b]) => Math.pow(a, b),
|
|
56
|
+
radians: ([a]) => rad(a),
|
|
57
|
+
sign: ([a]) => Math.sign(a),
|
|
58
|
+
sin: ([a]) => Math.sin(a),
|
|
59
|
+
tan: ([a]) => Math.tan(a),
|
|
60
|
+
sqrt: ([a]) => Math.sqrt(a)
|
|
74
61
|
};
|
|
75
|
-
|
|
76
|
-
|
|
62
|
+
const foldNode = defmulti(
|
|
63
|
+
(t) => t.tag,
|
|
64
|
+
{},
|
|
65
|
+
{
|
|
77
66
|
[DEFAULT]: () => false,
|
|
78
67
|
op1: (node) => {
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
68
|
+
const $node = node;
|
|
69
|
+
if ($node.op == "-" && isLitNumericConst($node.val)) {
|
|
70
|
+
$node.val.val *= -1;
|
|
71
|
+
return replaceNode(node, $node.val);
|
|
72
|
+
}
|
|
84
73
|
},
|
|
85
74
|
op2: (node) => {
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
if (isNumR && r.val === 0)
|
|
113
|
-
return replaceNode(node, l);
|
|
114
|
-
}
|
|
75
|
+
const $node = node;
|
|
76
|
+
const op = $node.op;
|
|
77
|
+
const l = $node.l;
|
|
78
|
+
const r = $node.r;
|
|
79
|
+
const isNumL = isLitNumericConst(l);
|
|
80
|
+
const isNumR = isLitNumericConst(r);
|
|
81
|
+
if (isNumL && isNumR) {
|
|
82
|
+
let res = maybeFoldMath(op, l.val, r.val);
|
|
83
|
+
if (res !== void 0)
|
|
84
|
+
return replaceNumericNode(node, res);
|
|
85
|
+
} else if (op === "*") {
|
|
86
|
+
if (isNumL && l.val === 0 || isNumR && r.val === 0)
|
|
87
|
+
return replaceNode(node, FLOAT0);
|
|
88
|
+
if (isNumL && l.val === 1)
|
|
89
|
+
return replaceNode(node, r);
|
|
90
|
+
if (isNumR && r.val === 1)
|
|
91
|
+
return replaceNode(node, l);
|
|
92
|
+
} else if (op === "/") {
|
|
93
|
+
if (isNumR && r.val === 1)
|
|
94
|
+
return replaceNode(node, l);
|
|
95
|
+
} else if (op === "+" || op === "-") {
|
|
96
|
+
if (isNumL && l.val === 0)
|
|
97
|
+
return replaceNode(node, r);
|
|
98
|
+
if (isNumR && r.val === 0)
|
|
99
|
+
return replaceNode(node, l);
|
|
100
|
+
}
|
|
115
101
|
},
|
|
116
102
|
call_i: (node) => {
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
103
|
+
const $node = node;
|
|
104
|
+
if ($node.args.every((x) => isLitNumericConst(x))) {
|
|
105
|
+
const op = BUILTINS[$node.id];
|
|
106
|
+
if (op !== void 0) {
|
|
107
|
+
return replaceNumericNode(
|
|
108
|
+
node,
|
|
109
|
+
op($node.args.map((x) => x.val))
|
|
110
|
+
);
|
|
123
111
|
}
|
|
112
|
+
}
|
|
124
113
|
},
|
|
125
114
|
lit: (node) => {
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
}
|
|
115
|
+
const $node = node;
|
|
116
|
+
if (isLitNumericConst($node.val)) {
|
|
117
|
+
if (isFloat($node.val)) {
|
|
118
|
+
return replaceNode(node, float($node.val.val));
|
|
119
|
+
}
|
|
120
|
+
if (isInt($node.val)) {
|
|
121
|
+
return replaceNode(node, int($node.val.val));
|
|
122
|
+
}
|
|
123
|
+
if (isUint($node.val)) {
|
|
124
|
+
return replaceNode(node, uint($node.val.val));
|
|
137
125
|
}
|
|
126
|
+
}
|
|
138
127
|
},
|
|
139
128
|
swizzle: (node) => {
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
129
|
+
const $node = node;
|
|
130
|
+
const val = $node.val;
|
|
131
|
+
if (isLitVecConst(val)) {
|
|
132
|
+
if (isFloat(node)) {
|
|
133
|
+
return replaceNode(
|
|
134
|
+
node,
|
|
135
|
+
float(val.val[COMPS[$node.id]])
|
|
136
|
+
);
|
|
146
137
|
}
|
|
147
|
-
|
|
148
|
-
});
|
|
149
|
-
/**
|
|
150
|
-
* Traverses given AST (potentially several times) and applies constant folding
|
|
151
|
-
* optimizations where possible. Returns possibly updated tree (mutates
|
|
152
|
-
* original).
|
|
153
|
-
*
|
|
154
|
-
* @remarks
|
|
155
|
-
* Currently, only the following operations are supported / considered:
|
|
156
|
-
*
|
|
157
|
-
* - scalar math operators
|
|
158
|
-
* - scalar math built-in functions
|
|
159
|
-
* - single component vector swizzling
|
|
160
|
-
* - literal hoisting
|
|
161
|
-
*
|
|
162
|
-
* @example
|
|
163
|
-
* ```ts
|
|
164
|
-
* const foo = defn("float", "foo", ["float"], (x) => [
|
|
165
|
-
* ret(mul(x, add(neg(float(10)), float(42))))]
|
|
166
|
-
* )
|
|
167
|
-
*
|
|
168
|
-
* const bar = vec2(100, 200);
|
|
169
|
-
*
|
|
170
|
-
* const prog = scope([
|
|
171
|
-
* foo,
|
|
172
|
-
* foo(add(float(1), float(2))),
|
|
173
|
-
* foo(add($x(bar), $y(bar)))
|
|
174
|
-
* ], true);
|
|
175
|
-
*
|
|
176
|
-
* // serialized (GLSL)
|
|
177
|
-
* glsl(prog);
|
|
178
|
-
*
|
|
179
|
-
* // float foo(in float _sym0) {
|
|
180
|
-
* // return (_sym0 * (-10.0 + 42.0));
|
|
181
|
-
* // };
|
|
182
|
-
* // foo((1.0 + 2.0));
|
|
183
|
-
* // foo((vec2(100.0, 200.0).x + vec2(100.0, 200.0).y));
|
|
184
|
-
*
|
|
185
|
-
* // with constant folding
|
|
186
|
-
* glsl(constantFolding(prog))
|
|
187
|
-
*
|
|
188
|
-
* // float foo(in float _sym0) {
|
|
189
|
-
* // return (_sym0 * 32.0);
|
|
190
|
-
* // };
|
|
191
|
-
* // foo(3.0);
|
|
192
|
-
* // foo(300.0);
|
|
193
|
-
*
|
|
194
|
-
* const expr = mul(float(4), $x(vec2(2)))
|
|
195
|
-
*
|
|
196
|
-
* glsl(expr)
|
|
197
|
-
* // (4.0 * vec2(2.0).x)
|
|
198
|
-
*
|
|
199
|
-
* glsl(constantFolding(expr))
|
|
200
|
-
* // 8.0
|
|
201
|
-
* ```
|
|
202
|
-
*
|
|
203
|
-
* @param tree -
|
|
204
|
-
*/
|
|
205
|
-
export const constantFolding = (tree) => {
|
|
206
|
-
let exec = true;
|
|
207
|
-
while (exec) {
|
|
208
|
-
exec = false;
|
|
209
|
-
walk((_, node) => {
|
|
210
|
-
exec = foldNode(node) || exec;
|
|
211
|
-
}, allChildren, null, tree, false);
|
|
138
|
+
}
|
|
212
139
|
}
|
|
213
|
-
|
|
140
|
+
}
|
|
141
|
+
);
|
|
142
|
+
const constantFolding = (tree) => {
|
|
143
|
+
let exec = true;
|
|
144
|
+
while (exec) {
|
|
145
|
+
exec = false;
|
|
146
|
+
walk(
|
|
147
|
+
(_, node) => {
|
|
148
|
+
exec = foldNode(node) || exec;
|
|
149
|
+
},
|
|
150
|
+
allChildren,
|
|
151
|
+
null,
|
|
152
|
+
tree,
|
|
153
|
+
false
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
return tree;
|
|
157
|
+
};
|
|
158
|
+
export {
|
|
159
|
+
constantFolding,
|
|
160
|
+
foldNode
|
|
214
161
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@thi.ng/shader-ast-optimize",
|
|
3
|
-
"version": "0.3.
|
|
3
|
+
"version": "0.3.33",
|
|
4
4
|
"description": "Shader AST code optimization passes/strategies",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"module": "./index.js",
|
|
@@ -24,7 +24,9 @@
|
|
|
24
24
|
"author": "Karsten Schmidt (https://thi.ng)",
|
|
25
25
|
"license": "Apache-2.0",
|
|
26
26
|
"scripts": {
|
|
27
|
-
"build": "yarn
|
|
27
|
+
"build": "yarn build:esbuild && yarn build:decl",
|
|
28
|
+
"build:decl": "tsc --declaration --emitDeclarationOnly",
|
|
29
|
+
"build:esbuild": "esbuild --format=esm --platform=neutral --target=es2022 --tsconfig=tsconfig.json --outdir=. src/**/*.ts",
|
|
28
30
|
"clean": "rimraf --glob '*.js' '*.d.ts' '*.map' doc",
|
|
29
31
|
"doc": "typedoc --excludePrivate --excludeInternal --out doc src/index.ts",
|
|
30
32
|
"doc:ae": "mkdir -p .ae/doc .ae/temp && api-extractor run --local --verbose",
|
|
@@ -33,15 +35,15 @@
|
|
|
33
35
|
"test": "bun test"
|
|
34
36
|
},
|
|
35
37
|
"dependencies": {
|
|
36
|
-
"@thi.ng/api": "^8.9.
|
|
37
|
-
"@thi.ng/defmulti": "^3.0.
|
|
38
|
-
"@thi.ng/logger": "^2.0.
|
|
39
|
-
"@thi.ng/math": "^5.7.
|
|
40
|
-
"@thi.ng/shader-ast": "^0.12.
|
|
38
|
+
"@thi.ng/api": "^8.9.12",
|
|
39
|
+
"@thi.ng/defmulti": "^3.0.10",
|
|
40
|
+
"@thi.ng/logger": "^2.0.2",
|
|
41
|
+
"@thi.ng/math": "^5.7.7",
|
|
42
|
+
"@thi.ng/shader-ast": "^0.12.85"
|
|
41
43
|
},
|
|
42
44
|
"devDependencies": {
|
|
43
45
|
"@microsoft/api-extractor": "^7.38.3",
|
|
44
|
-
"
|
|
46
|
+
"esbuild": "^0.19.8",
|
|
45
47
|
"rimraf": "^5.0.5",
|
|
46
48
|
"tools": "^0.0.1",
|
|
47
49
|
"typedoc": "^0.25.4",
|
|
@@ -76,5 +78,5 @@
|
|
|
76
78
|
"parent": "@thi.ng/shader-ast",
|
|
77
79
|
"year": 2019
|
|
78
80
|
},
|
|
79
|
-
"gitHead": "
|
|
81
|
+
"gitHead": "5e7bafedfc3d53bc131469a28de31dd8e5b4a3ff\n"
|
|
80
82
|
}
|