@galacean/engine-shader-compiler 2.0.0-alpha.31

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 (84) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +43 -0
  3. package/dist/browser.js +8720 -0
  4. package/dist/browser.js.map +1 -0
  5. package/dist/browser.min.js +2 -0
  6. package/dist/browser.min.js.map +1 -0
  7. package/dist/browser.verbose.js +9385 -0
  8. package/dist/browser.verbose.js.map +1 -0
  9. package/dist/browser.verbose.min.js +2 -0
  10. package/dist/browser.verbose.min.js.map +1 -0
  11. package/dist/bundler/cli.js +339 -0
  12. package/dist/bundler/rollup.cjs.js +366 -0
  13. package/dist/bundler/rollup.js +339 -0
  14. package/dist/main.js +8717 -0
  15. package/dist/main.js.map +1 -0
  16. package/dist/main.verbose.js +9382 -0
  17. package/dist/main.verbose.js.map +1 -0
  18. package/dist/module.js +8710 -0
  19. package/dist/module.js.map +1 -0
  20. package/dist/module.verbose.js +9375 -0
  21. package/dist/module.verbose.js.map +1 -0
  22. package/package.json +63 -0
  23. package/types/GSError.d.ts +16 -0
  24. package/types/ParserUtils.d.ts +26 -0
  25. package/types/Preprocessor.d.ts +33 -0
  26. package/types/ShaderCompiler.d.ts +21 -0
  27. package/types/ShaderCompilerUtils.d.ts +10 -0
  28. package/types/ShaderInstructionEncoder.d.ts +1 -0
  29. package/types/TempArray.d.ts +5 -0
  30. package/types/bundler/cli.d.ts +1 -0
  31. package/types/bundler/precompile.d.ts +25 -0
  32. package/types/bundler/rollup.d.ts +41 -0
  33. package/types/codeGen/CodeGenVisitor.d.ts +1 -0
  34. package/types/codeGen/GLES100.d.ts +12 -0
  35. package/types/codeGen/GLES300.d.ts +20 -0
  36. package/types/codeGen/GLESVisitor.d.ts +1 -0
  37. package/types/codeGen/VisitorContext.d.ts +2 -0
  38. package/types/codeGen/index.d.ts +3 -0
  39. package/types/codeGen/types.d.ts +12 -0
  40. package/types/common/BaseLexer.d.ts +2 -0
  41. package/types/common/BaseToken.d.ts +35 -0
  42. package/types/common/IBaseSymbol.d.ts +5 -0
  43. package/types/common/ShaderPosition.d.ts +8 -0
  44. package/types/common/ShaderRange.d.ts +8 -0
  45. package/types/common/SymbolTable.d.ts +9 -0
  46. package/types/common/SymbolTableStack.d.ts +13 -0
  47. package/types/common/enums/Keyword.d.ts +124 -0
  48. package/types/common/enums/ShaderStage.d.ts +5 -0
  49. package/types/common/index.d.ts +5 -0
  50. package/types/common/types.d.ts +101 -0
  51. package/types/index.d.ts +3 -0
  52. package/types/lalr/CFG.d.ts +5 -0
  53. package/types/lalr/LALR1.d.ts +26 -0
  54. package/types/lalr/Production.d.ts +9 -0
  55. package/types/lalr/State.d.ts +20 -0
  56. package/types/lalr/StateItem.d.ts +20 -0
  57. package/types/lalr/Utils.d.ts +22 -0
  58. package/types/lalr/index.d.ts +2 -0
  59. package/types/lalr/types.d.ts +14 -0
  60. package/types/lexer/Lexer.d.ts +111 -0
  61. package/types/lexer/index.d.ts +1 -0
  62. package/types/parser/AST.d.ts +482 -0
  63. package/types/parser/Grammar.d.ts +11 -0
  64. package/types/parser/GrammarSymbol.d.ts +128 -0
  65. package/types/parser/SemanticAnalyzer.d.ts +2 -0
  66. package/types/parser/ShaderInfo.d.ts +10 -0
  67. package/types/parser/ShaderTargetParser.d.ts +24 -0
  68. package/types/parser/builtin/functions.d.ts +34 -0
  69. package/types/parser/builtin/index.d.ts +2 -0
  70. package/types/parser/builtin/variables.d.ts +11 -0
  71. package/types/parser/index.d.ts +2 -0
  72. package/types/parser/symbolTable/FnSymbol.d.ts +6 -0
  73. package/types/parser/symbolTable/StructSymbol.d.ts +6 -0
  74. package/types/parser/symbolTable/SymbolDataType.d.ts +8 -0
  75. package/types/parser/symbolTable/SymbolInfo.d.ts +22 -0
  76. package/types/parser/symbolTable/VarSymbol.d.ts +8 -0
  77. package/types/parser/symbolTable/index.d.ts +6 -0
  78. package/types/parser/types.d.ts +24 -0
  79. package/types/sourceParser/ShaderSourceFactory.d.ts +8 -0
  80. package/types/sourceParser/ShaderSourceParser.d.ts +1 -0
  81. package/types/sourceParser/ShaderSourceSymbol.d.ts +10 -0
  82. package/types/sourceParser/SourceLexer.d.ts +19 -0
  83. package/types/sourceParser/index.d.ts +1 -0
  84. package/verbose/package.json +11 -0
@@ -0,0 +1,339 @@
1
+ #!/usr/bin/env node
2
+ import { parseArgs } from 'node:util';
3
+ import fs from 'node:fs';
4
+ import path from 'node:path';
5
+ import { createRequire } from 'node:module';
6
+ import { pathToFileURL } from 'node:url';
7
+
8
+ /** Normalize to forward slashes (Windows uses `\`; downstream regex/keys assume `/`). */ function normalizePath(p) {
9
+ return p.split(path.sep).join("/");
10
+ }
11
+ /** Recursively find all files with a given extension. */ function findFiles(dir, ext) {
12
+ const results = [];
13
+ if (!fs.existsSync(dir)) return results;
14
+ for (const entry of fs.readdirSync(dir, {
15
+ withFileTypes: true
16
+ })){
17
+ const fullPath = path.join(dir, entry.name);
18
+ if (entry.isDirectory()) {
19
+ results.push(...findFiles(fullPath, ext));
20
+ } else if (entry.name.endsWith(ext)) {
21
+ results.push(normalizePath(fullPath));
22
+ }
23
+ }
24
+ return results;
25
+ }
26
+ /** `PBR.shaderc` → `PBRSource`, `2D/Sprite.shaderc` → `SpriteSource`. */ function shadercPathToVarName(relPath) {
27
+ const normalized = normalizePath(relPath).replace(/\.shaderc$/, "");
28
+ const base = normalized.split("/").pop() ?? normalized;
29
+ const cleaned = base.replace(/[^A-Za-z0-9]/g, "");
30
+ if (cleaned.length === 0) return "Source";
31
+ return `${cleaned[0].toUpperCase()}${cleaned.slice(1)}Source`;
32
+ }
33
+ /** `Common/Common.glsl` → `Common_Common` — TS identifier for source-index imports. */ function pathToIdentifier(rel, ext) {
34
+ let id = rel.replace(new RegExp(`\\${ext}$`), "").replace(/[^A-Za-z0-9_]/g, "_");
35
+ if (/^[0-9]/.test(id)) id = "_" + id;
36
+ return id;
37
+ }
38
+ /** Remove empty parent directories upward from `startDir` until `stopDir` is hit. */ function pruneEmptyDirs(startDir, stopDir) {
39
+ let dir = startDir;
40
+ while(dir !== stopDir && fs.existsSync(dir) && fs.readdirSync(dir).length === 0){
41
+ fs.rmdirSync(dir);
42
+ dir = path.dirname(dir);
43
+ }
44
+ }
45
+ // One-shot mode exits non-zero on failure so CI breaks; watch mode logs and keeps running.
46
+ async function precompile(options) {
47
+ const { failed } = await runFull(options);
48
+ if (options.watch) {
49
+ await startWatcher(options);
50
+ return;
51
+ }
52
+ console.log("[shader-compiler-bundler] Done.");
53
+ if (failed > 0) {
54
+ process.exit(1);
55
+ }
56
+ }
57
+ async function runFull(options) {
58
+ const inputDir = path.resolve(options.input);
59
+ const outputDir = path.resolve(options.output);
60
+ const platformTarget = options.platformTarget ?? 0;
61
+ if (!fs.existsSync(inputDir)) {
62
+ throw new Error(`Input directory does not exist: ${inputDir}`);
63
+ }
64
+ fs.mkdirSync(outputDir, {
65
+ recursive: true
66
+ });
67
+ // Emit source indexes before compile so collectIncludeMap can resolve the freshly-built sources.
68
+ if (options.emitSources) {
69
+ emitSources(inputDir);
70
+ }
71
+ const shaderCompiler = await loadShaderCompiler();
72
+ shaderCompiler._setIncludeMap(await collectIncludeMap(inputDir));
73
+ let failed = 0;
74
+ if (options.only) {
75
+ const target = path.resolve(options.only);
76
+ if (!compileSingle(shaderCompiler, target, inputDir, outputDir, platformTarget)) failed++;
77
+ } else {
78
+ const shaderFiles = findFiles(inputDir, ".shader");
79
+ console.log(`[shader-compiler-bundler] Precompiling ${shaderFiles.length} shader(s)...`);
80
+ for (const file of shaderFiles){
81
+ if (!compileSingle(shaderCompiler, file, inputDir, outputDir, platformTarget)) failed++;
82
+ }
83
+ if (options.clean) {
84
+ const removed = cleanOrphanedBundles(shaderFiles, inputDir, outputDir);
85
+ if (removed > 0) console.log(`[shader-compiler-bundler] Cleaned ${removed} orphaned .shaderc file(s).`);
86
+ }
87
+ if (failed > 0) console.warn(`[shader-compiler-bundler] ${failed} shader(s) failed to precompile.`);
88
+ }
89
+ if (options.emitIndex) {
90
+ emitIndex(outputDir);
91
+ }
92
+ return {
93
+ failed
94
+ };
95
+ }
96
+ async function startWatcher(options) {
97
+ const inputDir = path.resolve(options.input);
98
+ const outputDir = path.resolve(options.output);
99
+ const platformTarget = options.platformTarget ?? 0;
100
+ const shaderCompiler = await loadShaderCompiler();
101
+ shaderCompiler._setIncludeMap(await collectIncludeMap(inputDir));
102
+ // .glsl change → full recompile (can't cheaply track include graphs).
103
+ // .shader change → single-file recompile.
104
+ const handleInputChange = async (filename)=>{
105
+ if (!filename) return;
106
+ const norm = normalizePath(filename);
107
+ const fullPath = path.join(inputDir, filename);
108
+ if (norm.endsWith(".glsl")) {
109
+ console.log(`[shader-compiler-bundler] Include changed (${norm}), full recompile...`);
110
+ shaderCompiler._setIncludeMap(await collectIncludeMap(inputDir));
111
+ runFull(options).catch((e)=>console.error(e));
112
+ return;
113
+ }
114
+ if (!norm.endsWith(".shader")) return;
115
+ if (!fs.existsSync(fullPath)) {
116
+ removeBundleFor(fullPath, inputDir, outputDir);
117
+ } else {
118
+ compileSingle(shaderCompiler, fullPath, inputDir, outputDir, platformTarget);
119
+ }
120
+ if (options.emitSources) emitSources(inputDir);
121
+ if (options.emitIndex) emitIndex(outputDir);
122
+ };
123
+ console.log(`[shader-compiler-bundler] Watching ${inputDir} ...`);
124
+ fs.watch(inputDir, {
125
+ recursive: true
126
+ }, (_event, filename)=>{
127
+ handleInputChange(filename).catch((e)=>console.error(e));
128
+ });
129
+ }
130
+ async function loadShaderCompiler() {
131
+ // @ts-ignore — `../main.js` is the compiled runtime entry; no .ts source.
132
+ const mod = await import('../main.js');
133
+ const instance = new mod.ShaderCompiler();
134
+ if (typeof instance._precompile !== "function") {
135
+ throw new Error("ShaderCompiler._precompile is not available; rebuild @galacean/engine-shader-compiler first.");
136
+ }
137
+ return instance;
138
+ }
139
+ // Local .glsl come from inputDir; standard library comes from
140
+ // `@galacean/engine-shader/sources` with a sibling-dir fallback for engine self-build cold-start.
141
+ async function collectIncludeMap(inputDir) {
142
+ const map = {};
143
+ for (const file of findFiles(inputDir, ".glsl")){
144
+ const relPath = normalizePath(path.relative(inputDir, file));
145
+ map[relPath] = fs.readFileSync(file, "utf-8");
146
+ }
147
+ let usedRelease = false;
148
+ // Resolve from inputDir's node_modules so consumers without transitive linkage to shader-compiler can still find it.
149
+ try {
150
+ const requireFromInput = createRequire(path.join(inputDir, "package.json"));
151
+ const sourcesPath = requireFromInput.resolve("@galacean/engine-shader/sources");
152
+ const { shaderLibrary } = await import(pathToFileURL(sourcesPath).href);
153
+ for (const { source, path: chunkPath } of shaderLibrary){
154
+ map[chunkPath] = source;
155
+ }
156
+ usedRelease = true;
157
+ } catch {
158
+ // engine self-build cold-start: dist/sources.* not yet built; use sibling source.
159
+ }
160
+ if (!usedRelease) {
161
+ const siblingLibrary = path.join(path.dirname(inputDir), "ShaderLibrary");
162
+ if (fs.existsSync(siblingLibrary)) {
163
+ for (const file of findFiles(siblingLibrary, ".glsl")){
164
+ const rel = normalizePath(path.relative(siblingLibrary, file));
165
+ map[`ShaderLibrary/${rel}`] = fs.readFileSync(file, "utf-8");
166
+ }
167
+ }
168
+ }
169
+ return map;
170
+ }
171
+ // Mirrors `ShaderPass._shaderRootPath` in engine; inlined to keep bundler engine-free.
172
+ const SHADER_ROOT_PATH = "shaders://root/";
173
+ function compileSingle(shaderCompiler, shaderPath, inputDir, outputDir, platformTarget) {
174
+ const source = fs.readFileSync(shaderPath, "utf-8");
175
+ const relativePath = normalizePath(path.relative(inputDir, shaderPath));
176
+ const bundleRelative = relativePath.replace(/\.shader$/, ".shaderc");
177
+ const bundlePath = path.join(outputDir, bundleRelative);
178
+ const basePathForIncludeKey = new URL(relativePath, SHADER_ROOT_PATH).href;
179
+ fs.mkdirSync(path.dirname(bundlePath), {
180
+ recursive: true
181
+ });
182
+ try {
183
+ const precompiled = shaderCompiler._precompile(source, platformTarget, basePathForIncludeKey);
184
+ fs.writeFileSync(bundlePath, JSON.stringify(precompiled));
185
+ console.log(` ${relativePath} -> ${bundleRelative}`);
186
+ return true;
187
+ } catch (e) {
188
+ console.error(` FAILED: ${relativePath}`);
189
+ console.error(e);
190
+ return false;
191
+ }
192
+ }
193
+ function cleanOrphanedBundles(shaderFiles, inputDir, outputDir) {
194
+ const aliveSet = new Set(shaderFiles.map((f)=>normalizePath(path.relative(inputDir, f)).replace(/\.shader$/, "")));
195
+ const bundleFiles = findFiles(outputDir, ".shaderc");
196
+ let removed = 0;
197
+ for (const bundleFile of bundleFiles){
198
+ const rel = normalizePath(path.relative(outputDir, bundleFile)).replace(/\.shaderc$/, "");
199
+ if (!aliveSet.has(rel)) {
200
+ fs.unlinkSync(bundleFile);
201
+ console.log(` Removed orphaned: ${rel}.shaderc`);
202
+ removed++;
203
+ pruneEmptyDirs(path.dirname(bundleFile), outputDir);
204
+ }
205
+ }
206
+ return removed;
207
+ }
208
+ function emitIndex(outputDir) {
209
+ const bundleFiles = findFiles(outputDir, ".shaderc");
210
+ const entries = bundleFiles.map((file)=>{
211
+ const rel = normalizePath(path.relative(outputDir, file));
212
+ return {
213
+ varName: shadercPathToVarName(rel),
214
+ importPath: `./${rel}`
215
+ };
216
+ }).sort((a, b)=>a.varName.localeCompare(b.varName));
217
+ const imports = entries.map((e)=>`import ${e.varName} from "${e.importPath}";`).join("\n");
218
+ const exportBlock = `export {\n${entries.map((e)=>` ${e.varName}`).join(",\n")}\n};`;
219
+ // prettier-ignore the export block: consumer's prettier config is unknown.
220
+ const content = `// Auto-generated by shader-compiler-precompile --emit-index — do not edit.\n${imports}\n\n// prettier-ignore\n${exportBlock}\n`;
221
+ const indexPath = path.join(outputDir, "index.ts");
222
+ const existing = fs.existsSync(indexPath) ? fs.readFileSync(indexPath, "utf-8") : "";
223
+ if (content !== existing) {
224
+ fs.writeFileSync(indexPath, content);
225
+ console.log(" Updated index.ts");
226
+ }
227
+ }
228
+ // Generate raw-source indexes:
229
+ // <inputDir>/index.ts — `.shader` array (defines IShaderSource interface)
230
+ // <sibling>/ShaderLibrary/index.ts — `.glsl` array (only if the directory exists)
231
+ function emitSources(inputDir) {
232
+ emitShaderSourceIndex(inputDir);
233
+ const libraryDir = path.join(path.dirname(inputDir), "ShaderLibrary");
234
+ if (fs.existsSync(libraryDir)) {
235
+ emitLibrarySourceIndex(libraryDir);
236
+ }
237
+ }
238
+ function emitShaderSourceIndex(rootDir) {
239
+ const srcDir = path.dirname(rootDir);
240
+ const files = findFiles(rootDir, ".shader").sort();
241
+ const importPaths = files.map((f)=>normalizePath(path.relative(rootDir, f)));
242
+ const paths = files.map((f)=>normalizePath(path.relative(srcDir, f)));
243
+ const ids = importPaths.map((p)=>pathToIdentifier(p, ".shader"));
244
+ const lines = [];
245
+ lines.push("// Auto-generated by shader-compiler-precompile --emit-sources — do not edit.");
246
+ lines.push("");
247
+ importPaths.forEach((rel, i)=>lines.push(`import ${ids[i]} from "./${rel}";`));
248
+ lines.push("");
249
+ lines.push("export interface IShaderSource {");
250
+ lines.push(" path: string;");
251
+ lines.push(" source: string;");
252
+ lines.push("}");
253
+ lines.push("");
254
+ lines.push("// prettier-ignore");
255
+ lines.push("export const shaders: IShaderSource[] = [");
256
+ paths.forEach((p, i)=>lines.push(` { source: ${ids[i]}, path: ${JSON.stringify(p)} },`));
257
+ lines.push("];");
258
+ lines.push("");
259
+ const out = path.join(rootDir, "index.ts");
260
+ fs.writeFileSync(out, lines.join("\n"), "utf-8");
261
+ console.log(` Emitted shaders index ${path.relative(process.cwd(), out)} (${files.length})`);
262
+ }
263
+ function emitLibrarySourceIndex(rootDir) {
264
+ const srcDir = path.dirname(rootDir);
265
+ const files = findFiles(rootDir, ".glsl").sort();
266
+ const importPaths = files.map((f)=>normalizePath(path.relative(rootDir, f)));
267
+ const paths = files.map((f)=>normalizePath(path.relative(srcDir, f)));
268
+ const ids = importPaths.map((p)=>pathToIdentifier(p, ".glsl"));
269
+ const lines = [];
270
+ lines.push("// Auto-generated by shader-compiler-precompile --emit-sources — do not edit.");
271
+ lines.push("");
272
+ lines.push(`import type { IShaderSource } from "../Shaders";`);
273
+ importPaths.forEach((rel, i)=>lines.push(`import ${ids[i]} from "./${rel}";`));
274
+ lines.push("");
275
+ lines.push("// prettier-ignore");
276
+ lines.push("export const shaderLibrary: IShaderSource[] = [");
277
+ paths.forEach((p, i)=>lines.push(` { source: ${ids[i]}, path: ${JSON.stringify(p)} },`));
278
+ lines.push("];");
279
+ lines.push("");
280
+ const out = path.join(rootDir, "index.ts");
281
+ fs.writeFileSync(out, lines.join("\n"), "utf-8");
282
+ console.log(` Emitted library index ${path.relative(process.cwd(), out)} (${files.length})`);
283
+ }
284
+ function removeBundleFor(shaderPath, inputDir, outputDir) {
285
+ const rel = normalizePath(path.relative(inputDir, shaderPath)).replace(/\.shader$/, ".shaderc");
286
+ const bundlePath = path.join(outputDir, rel);
287
+ if (fs.existsSync(bundlePath)) {
288
+ fs.unlinkSync(bundlePath);
289
+ console.log(` Removed: ${rel}`);
290
+ pruneEmptyDirs(path.dirname(bundlePath), outputDir);
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Entry point for the `shader-compiler-precompile` bin.
296
+ *
297
+ * Usage:
298
+ * shader-compiler-precompile <input-dir> <output-dir>
299
+ * [--clean] [--watch] [--only <file>] [--emit-index] [--emit-sources]
300
+ */ async function main() {
301
+ const { positionals, values } = parseArgs({
302
+ allowPositionals: true,
303
+ options: {
304
+ clean: {
305
+ type: "boolean"
306
+ },
307
+ watch: {
308
+ type: "boolean"
309
+ },
310
+ only: {
311
+ type: "string"
312
+ },
313
+ "emit-index": {
314
+ type: "boolean"
315
+ },
316
+ "emit-sources": {
317
+ type: "boolean"
318
+ }
319
+ }
320
+ });
321
+ const [input, output] = positionals;
322
+ if (!input || !output) {
323
+ console.error("Usage: shader-compiler-precompile <input-dir> <output-dir> [--clean] [--watch] [--only <file>] [--emit-index] [--emit-sources]");
324
+ process.exit(1);
325
+ }
326
+ await precompile({
327
+ input,
328
+ output,
329
+ clean: values.clean,
330
+ watch: values.watch,
331
+ only: values.only,
332
+ emitIndex: values["emit-index"],
333
+ emitSources: values["emit-sources"]
334
+ });
335
+ }
336
+ main().catch((err)=>{
337
+ console.error(err);
338
+ process.exit(1);
339
+ });