@thi.ng/shader-ast-glsl
Version:
Customizable GLSL codegen for @thi.ng/shader-ast
144 lines (143 loc) • 5.03 kB
JavaScript
import { isBoolean } from "@thi.ng/checks/is-boolean";
import { isNumber } from "@thi.ng/checks/is-number";
import { unsupported } from "@thi.ng/errors/unsupported";
import { F, V2, V4 } from "@thi.ng/shader-ast/api/types";
import { isMat, isVec } from "@thi.ng/shader-ast/ast/checks";
import { itemType } from "@thi.ng/shader-ast/ast/item";
import { sym } from "@thi.ng/shader-ast/ast/sym";
import { defTarget } from "@thi.ng/shader-ast/target";
import { GLSLVersion } from "./api.js";
const RE_SEMI = /[};]$/;
const targetGLSL = (opts) => {
const _opts = {
type: "fs",
version: GLSLVersion.GLES_300,
versionPragma: true,
prelude: "",
...opts
};
const isVS = _opts.type === "vs";
const ff = _opts.prec !== void 0 ? (x) => x === (x | 0) ? x + ".0" : x.toFixed(_opts.prec) : (x) => x === (x | 0) ? x + ".0" : String(x);
const $type = (t) => t;
const $list = (body, sep = ", ") => body.map(emit).join(sep);
const $fn = (t) => `${t.id}(${$list(t.args)})`;
const $decl = (sym2, arg = false) => {
const { id, type, opts: opts2, init } = sym2;
const res = [];
if (opts2.type) {
let type2;
if (_opts.version < GLSLVersion.GLES_300) {
if (isVS) {
type2 = {
in: "attribute",
out: "varying",
uni: "uniform"
}[opts2.type];
} else {
type2 = {
in: "varying",
out: null,
uni: "uniform"
}[opts2.type];
!type2 && unsupported(
"GLSL 100 doesn't support fragment shader output variables"
);
}
} else {
opts2.loc != null && res.push(`layout(location=${opts2.loc}) `);
opts2.smooth != null && res.push(opts2.smooth + " ");
type2 = opts2.type === "uni" ? "uniform" : opts2.type;
}
res.push(type2 + " ");
} else {
opts2.const && res.push("const ");
arg && opts2.q && res.push(opts2.q + " ");
}
opts2.prec && res.push(opts2.prec + " ");
res.push($type(itemType(type)), " ", id);
opts2.num && res.push(`[${opts2.num}]`);
init && res.push(" = ", emit(init));
return res.join("");
};
const emitIndex = (t) => `${emit(t.val)}[${emit(t.id)}]`;
const emit = defTarget({
arg: (t) => $decl(t, true),
array_init: (t) => _opts.version >= GLSLVersion.GLES_300 ? `${t.type}(${$list(t.init)})` : unsupported(
`array initializers not available in GLSL ${_opts.version}`
),
assign: (t) => emit(t.l) + " = " + emit(t.r),
ctrl: (t) => t.id,
call: $fn,
call_i: (t) => t.id === "texture" && _opts.version < GLSLVersion.GLES_300 ? `${t.id}${t.args[0].type.substring(7)}(${$list(
t.args
)})` : $fn(t),
decl: (t) => $decl(t.id),
fn: (t) => `${$type(t.type)} ${t.id}(${$list(t.args)}) ${emit(t.scope)}`,
for: (t) => `for(${t.init ? emit(t.init) : ""}; ${emit(t.test)}; ${t.iter ? emit(t.iter) : ""}) ${emit(t.scope)}`,
idx: emitIndex,
idxm: emitIndex,
if: (t) => {
const res = `if (${emit(t.test)}) ` + emit(t.t);
return t.f ? res + " else " + emit(t.f) : res;
},
lit: (t) => {
const v = t.val;
switch (t.type) {
case "bool":
return isBoolean(v) ? String(v) : `bool(${emit(v)})`;
case "float":
return isNumber(v) ? ff(v) : `float(${emit(v)})`;
case "int":
return isNumber(v) ? String(v) : `int(${emit(v)})`;
case "uint":
return isNumber(v) ? `${v}u` : `uint(${emit(v)})`;
default: {
if (isVec(t) || isMat(t)) {
return `${t.type}(${$list(v)})`;
}
return unsupported(`unknown type: ${t.type}`);
}
}
},
op1: (t) => t.post ? `(${emit(t.val)}${t.op})` : `(${t.op}${emit(t.val)})`,
op2: (t) => `(${emit(t.l)} ${t.op} ${emit(t.r)})`,
ret: (t) => "return" + (t.val ? " " + emit(t.val) : ""),
scope: (t) => {
let res = t.body.map(emit).reduce(
(acc, x) => (acc.push(RE_SEMI.test(x) ? x : x + ";"), acc),
[]
).join("\n");
res += t.body.length && !RE_SEMI.test(res) ? ";" : "";
if (!t.global) {
return `{
${res}
}`;
}
if (_opts.prelude) {
res = _opts.prelude + "\n" + res;
}
if (_opts.versionPragma) {
res = `#version ${_opts.version}
` + res;
}
return res;
},
swizzle: (t) => `${emit(t.val)}.${t.id}`,
sym: (t) => t.id,
ternary: (t) => `(${emit(t.test)} ? ${emit(t.t)} : ${emit(t.f)})`,
while: (t) => `while (${emit(t.test)}) ${emit(t.scope)}`
});
Object.assign(emit, {
gl_FragColor: sym(V4, "gl_FragColor"),
gl_FragCoord: sym(V4, "gl_FragCoord", { const: true }),
gl_FragData: sym("vec4[]", "gl_FragData", { num: 1 }),
gl_FrontFacing: sym("bool", "gl_FrontFacing", { const: true }),
gl_PointCoord: sym(V2, "gl_PointCoord", { const: true }),
gl_PointSize: sym(F, "gl_PointSize"),
gl_Position: sym(V4, "gl_Position")
});
return emit;
};
export {
targetGLSL
};