@galacean/engine-shader-compiler 2.0.0-alpha.52 → 2.0.0-alpha.53
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 +40 -4
- package/bundler/cli.js +199 -104
- package/bundler/precompile.cjs.js +117 -47
- package/bundler/precompile.js +117 -47
- package/bundler/rollup.cjs.js +115 -45
- package/bundler/rollup.js +115 -45
- package/dist/browser.js +13421 -6555
- package/dist/browser.js.map +1 -1
- package/dist/browser.min.js +1 -1
- package/dist/browser.min.js.map +1 -1
- package/dist/main.js +1758 -8450
- package/dist/main.js.map +1 -1
- package/dist/module.js +1730 -8427
- package/dist/module.js.map +1 -1
- package/dist/precompile.main.js +2280 -0
- package/dist/precompile.main.js.map +1 -0
- package/dist/precompile.module.js +2276 -0
- package/dist/precompile.module.js.map +1 -0
- package/package.json +24 -13
- package/types/GLESBackend.d.ts +22 -0
- package/types/ShaderBackend.d.ts +15 -0
- package/types/ShaderCompilation.d.ts +40 -0
- package/types/ShaderCompiler.d.ts +42 -14
- package/types/ShaderInstructionEncoder.d.ts +46 -14
- package/types/ShaderPrecompiler.d.ts +31 -0
- package/types/bundler/precompile.d.ts +26 -3
- package/types/bundler/rollup.d.ts +23 -2
- package/types/codeGen/CodeGenVisitor.d.ts +11 -6
- package/types/codeGen/GLES100.d.ts +3 -5
- package/types/codeGen/GLES300.d.ts +14 -7
- package/types/codeGen/GLESVisitor.d.ts +55 -9
- package/types/codeGen/VisitorContext.d.ts +88 -29
- package/types/codeGen/types.d.ts +1 -1
- package/types/index.d.ts +4 -1
- package/types/precompile.d.ts +1 -0
- package/dist/browser.verbose.js +0 -9601
- package/dist/browser.verbose.js.map +0 -1
- package/dist/browser.verbose.min.js +0 -2
- package/dist/browser.verbose.min.js.map +0 -1
- package/dist/main.verbose.js +0 -9598
- package/dist/main.verbose.js.map +0 -1
- package/dist/module.verbose.js +0 -9591
- package/dist/module.verbose.js.map +0 -1
- package/types/GSError.d.ts +0 -16
- package/types/ParserUtils.d.ts +0 -43
- package/types/Preprocessor.d.ts +0 -30
- package/types/ShaderCompilerUtils.d.ts +0 -10
- package/types/common/BaseLexer.d.ts +0 -51
- package/types/common/BaseToken.d.ts +0 -35
- package/types/common/IBaseSymbol.d.ts +0 -5
- package/types/common/ShaderPosition.d.ts +0 -8
- package/types/common/ShaderRange.d.ts +0 -8
- package/types/common/SymbolTable.d.ts +0 -13
- package/types/common/SymbolTableStack.d.ts +0 -17
- package/types/common/enums/Keyword.d.ts +0 -124
- package/types/common/enums/ShaderStage.d.ts +0 -5
- package/types/common/index.d.ts +0 -5
- package/types/common/types.d.ts +0 -101
- package/types/lalr/CFG.d.ts +0 -5
- package/types/lalr/LALR1.d.ts +0 -27
- package/types/lalr/Production.d.ts +0 -9
- package/types/lalr/State.d.ts +0 -20
- package/types/lalr/StateItem.d.ts +0 -20
- package/types/lalr/Utils.d.ts +0 -22
- package/types/lalr/index.d.ts +0 -2
- package/types/lalr/types.d.ts +0 -14
- package/types/lexer/Lexer.d.ts +0 -143
- package/types/lexer/index.d.ts +0 -1
- package/types/parser/AST.d.ts +0 -502
- package/types/parser/Grammar.d.ts +0 -11
- package/types/parser/GrammarSymbol.d.ts +0 -128
- package/types/parser/SemanticAnalyzer.d.ts +0 -42
- package/types/parser/ShaderInfo.d.ts +0 -10
- package/types/parser/ShaderTargetParser.d.ts +0 -26
- package/types/parser/builtin/functions.d.ts +0 -34
- package/types/parser/builtin/index.d.ts +0 -2
- package/types/parser/builtin/variables.d.ts +0 -11
- package/types/parser/index.d.ts +0 -2
- package/types/parser/symbolTable/FnSymbol.d.ts +0 -6
- package/types/parser/symbolTable/StructSymbol.d.ts +0 -6
- package/types/parser/symbolTable/SymbolDataType.d.ts +0 -8
- package/types/parser/symbolTable/SymbolInfo.d.ts +0 -22
- package/types/parser/symbolTable/VarSymbol.d.ts +0 -8
- package/types/parser/symbolTable/index.d.ts +0 -6
- package/types/parser/types.d.ts +0 -24
- package/types/sourceParser/ShaderSourceFactory.d.ts +0 -8
- package/types/sourceParser/ShaderSourceParser.d.ts +0 -30
- package/types/sourceParser/ShaderSourceSymbol.d.ts +0 -10
- package/types/sourceParser/SourceLexer.d.ts +0 -19
- package/types/sourceParser/index.d.ts +0 -1
- package/verbose/package.json +0 -10
package/README.md
CHANGED
|
@@ -7,6 +7,7 @@ npm install @galacean/engine-shader-compiler
|
|
|
7
7
|
## Usage
|
|
8
8
|
|
|
9
9
|
```typescript
|
|
10
|
+
import { Shader, ShaderFactory, WebGLEngine } from "@galacean/engine";
|
|
10
11
|
import { ShaderCompiler } from "@galacean/engine-shader-compiler";
|
|
11
12
|
|
|
12
13
|
// Create shader compiler
|
|
@@ -20,20 +21,55 @@ const engine = await WebGLEngine.create({ canvas: "canvas", shaderCompiler });
|
|
|
20
21
|
// Create shader by galacean shader code directly
|
|
21
22
|
const shader = Shader.create(galaceanShaderCode);
|
|
22
23
|
|
|
24
|
+
// Register project chunks before creating shaders that include them
|
|
25
|
+
ShaderFactory.registerInclude("ShaderLibrary/UserCommon.glsl", userCommonSource);
|
|
26
|
+
const shaderWithIncludes = Shader.create(galaceanShaderCodeWithIncludes);
|
|
27
|
+
|
|
23
28
|
.......
|
|
24
29
|
|
|
25
30
|
// Run engine
|
|
26
31
|
engine.run()
|
|
27
32
|
```
|
|
28
33
|
|
|
29
|
-
|
|
34
|
+
Authoring diagnostics are provided separately by `@galacean/engine-shader-analyzer`; the runtime compiler does not include analyzer diagnostics.
|
|
35
|
+
|
|
36
|
+
`Shader.create()` never needs a filesystem path. Include paths are resolved against logical keys registered through `ShaderFactory.registerInclude()`:
|
|
37
|
+
|
|
38
|
+
| Registered key | Current logical shader | `#include` | Result |
|
|
39
|
+
| --- | --- | --- | --- |
|
|
40
|
+
| `User/Math.glsl` | no loader metadata | `"User/Math.glsl"` or `"/User/Math.glsl"` | `User/Math.glsl` |
|
|
41
|
+
| `User/Math.glsl` | no loader metadata | `"./User/Math.glsl"` | `User/Math.glsl` |
|
|
42
|
+
| `Assets/Shaders/Math.glsl` | `Assets/Shaders/PBR.shader` | `"./Math.glsl"` | `Assets/Shaders/Math.glsl` |
|
|
43
|
+
| `Assets/Shared/Math.glsl` | `Assets/Shaders/PBR.shader` | `"../Shared/Math.glsl"` | `Assets/Shared/Math.glsl` |
|
|
44
|
+
|
|
45
|
+
The asset loader supplies the current logical shader location internally. Moving a shader folder remains valid when the shader asset and its registered include keys move together. Disk paths do not define the logical root.
|
|
30
46
|
|
|
31
|
-
|
|
47
|
+
## Precompile
|
|
32
48
|
|
|
33
|
-
|
|
34
|
-
|
|
49
|
+
Import `ShaderPrecompiler` from `@galacean/engine-shader-compiler/precompile` to generate `.shaderc` data in memory. The CLI writes this data to files.
|
|
50
|
+
|
|
51
|
+
Compile a directory tree to `.shaderc` artifacts:
|
|
52
|
+
|
|
53
|
+
```sh
|
|
54
|
+
shader-compiler-precompile Assets/Shaders build/shaders --clean --emit-index
|
|
35
55
|
```
|
|
36
56
|
|
|
57
|
+
The input directory is the logical registry root. A shader at `PBR/Root.shader` resolves `./Common.glsl` to `PBR/Common.glsl`; `/Shared/Math.glsl` and `Shared/Math.glsl` both resolve to `Shared/Math.glsl` below the input root. Compilation failures exit non-zero and prevent Rollup builds using the plugin from succeeding.
|
|
58
|
+
|
|
59
|
+
Inherited helper calls use forward declarations while overriding function bodies keep their original macro context. GLES can move ordinary struct signature dependencies ahead of those declarations. If this requires moving a type with conditional fields, macro-dependent array bounds, or across macro/precision changes, code generation reports `UnsupportedForwardDeclaration` with source context and rejects the pass. This is a GLES backend limitation; authoring analysis can still accept the source, and future backends can provide their own declaration ordering.
|
|
60
|
+
|
|
61
|
+
GLES also moves replacement uniforms, struct declarations, and their enclosing conditions ahead of inherited references when their macro and precision context remains unchanged. Mutually exclusive functions that need different signatures at a shared forward-declaration site, and declarations that cross a macro or precision change, report `UnsupportedForwardDeclaration`.
|
|
62
|
+
|
|
63
|
+
When static inheritance coverage remains unknown, the instruction stream selects declarations using the actual macro state at their original source positions. The active declaration in the narrowest ShaderLab scope wins; macro directives still execute in source order. This applies to functions, uniforms, ordinary structs, and generated IO declarations. Forward declarations and moved types retain the ownership of their original declarations.
|
|
64
|
+
|
|
65
|
+
`ShaderCompiler.generate()` returns these variant-selectable instruction streams alongside unselected `vertex` and `fragment` text for inspection. The text views alone do not contain deferred ownership and must not be re-encoded as a substitute for the returned instructions. Engine `ShaderPass` consumes the instructions to produce final GLSL. `.shaderc` preserves them as JSON; use matching compiler and engine versions when shipping artifacts. Existing instruction opcodes retain their numeric values.
|
|
66
|
+
|
|
67
|
+
## Declaration dependencies
|
|
68
|
+
|
|
69
|
+
The neutral parser records exact struct dependencies at each type occurrence, including possible macro replacement types. Macro-dependent types retain conservative semantic typing; their dependency identities do not select a runtime macro variant. GLES consumes these facts while generating reachable declarations, including helper signatures and nested struct fields, without a second declaration-tree scan.
|
|
70
|
+
|
|
71
|
+
Global emission is deduplicated by declaration identity. Same-named structs, functions, and uniforms in different macro branches remain separate candidates, retain their original guards and positions, and can discover further dependencies during generation. Backend-owned return lowering preserves control flow by emitting a block when a single return becomes an output assignment followed by an exit.
|
|
72
|
+
|
|
37
73
|
## CFG Grammar conflict detection
|
|
38
74
|
|
|
39
75
|
The Galacean shader syntax is defined using Context-Free Grammar (CFG) and is documented within the `\*.y` file. When modifications to the shader syntax are required, it is recommended to make changes to the existing CFG syntax file, and employ [Bison](https://www.gnu.org/software/bison/manual/bison.html) to detect any potential grammar conflicts.
|
package/bundler/cli.js
CHANGED
|
@@ -1,20 +1,48 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import path from 'node:path';
|
|
5
|
-
import { createRequire } from 'node:module';
|
|
6
|
-
import { pathToFileURL } from 'node:url';
|
|
2
|
+
'use strict';
|
|
7
3
|
|
|
8
|
-
|
|
9
|
-
|
|
4
|
+
var node_util = require('node:util');
|
|
5
|
+
var fs = require('node:fs');
|
|
6
|
+
var path = require('node:path');
|
|
7
|
+
var node_module = require('node:module');
|
|
8
|
+
|
|
9
|
+
function _interopDefaultLegacy (e) { return e && typeof e === 'object' && 'default' in e ? e : { 'default': e }; }
|
|
10
|
+
|
|
11
|
+
function _interopNamespace(e) {
|
|
12
|
+
if (e && e.__esModule) return e;
|
|
13
|
+
var n = Object.create(null);
|
|
14
|
+
if (e) {
|
|
15
|
+
Object.keys(e).forEach(function (k) {
|
|
16
|
+
if (k !== 'default') {
|
|
17
|
+
var d = Object.getOwnPropertyDescriptor(e, k);
|
|
18
|
+
Object.defineProperty(n, k, d.get ? d : {
|
|
19
|
+
enumerable: true,
|
|
20
|
+
get: function () { return e[k]; }
|
|
21
|
+
});
|
|
22
|
+
}
|
|
23
|
+
});
|
|
24
|
+
}
|
|
25
|
+
n["default"] = e;
|
|
26
|
+
return Object.freeze(n);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
var fs__default = /*#__PURE__*/_interopDefaultLegacy(fs);
|
|
30
|
+
var path__default = /*#__PURE__*/_interopDefaultLegacy(path);
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* Converts a filesystem path to the slash-separated form used by shader asset keys.
|
|
34
|
+
* @param p - Filesystem path.
|
|
35
|
+
* @returns Slash-separated path.
|
|
36
|
+
*/ function normalizePath(p) {
|
|
37
|
+
return p.split(path__default["default"].sep).join("/");
|
|
10
38
|
}
|
|
11
39
|
/** Recursively find all files with a given extension. */ function findFiles(dir, ext) {
|
|
12
40
|
const results = [];
|
|
13
|
-
if (!
|
|
14
|
-
for (const entry of
|
|
41
|
+
if (!fs__default["default"].existsSync(dir)) return results;
|
|
42
|
+
for (const entry of fs__default["default"].readdirSync(dir, {
|
|
15
43
|
withFileTypes: true
|
|
16
44
|
})){
|
|
17
|
-
const fullPath =
|
|
45
|
+
const fullPath = path__default["default"].join(dir, entry.name);
|
|
18
46
|
if (entry.isDirectory()) {
|
|
19
47
|
results.push(...findFiles(fullPath, ext));
|
|
20
48
|
} else if (entry.name.endsWith(ext)) {
|
|
@@ -37,13 +65,17 @@ import { pathToFileURL } from 'node:url';
|
|
|
37
65
|
}
|
|
38
66
|
/** Remove empty parent directories upward from `startDir` until `stopDir` is hit. */ function pruneEmptyDirs(startDir, stopDir) {
|
|
39
67
|
let dir = startDir;
|
|
40
|
-
while(dir !== stopDir &&
|
|
41
|
-
|
|
42
|
-
dir =
|
|
68
|
+
while(dir !== stopDir && fs__default["default"].existsSync(dir) && fs__default["default"].readdirSync(dir).length === 0){
|
|
69
|
+
fs__default["default"].rmdirSync(dir);
|
|
70
|
+
dir = path__default["default"].dirname(dir);
|
|
43
71
|
}
|
|
44
72
|
}
|
|
45
|
-
|
|
46
|
-
|
|
73
|
+
/**
|
|
74
|
+
* Precompiles shaders once or starts incremental watch mode.
|
|
75
|
+
* @param options - Source, output, cleanup, watch, and artifact-generation options.
|
|
76
|
+
* @returns After a one-shot compile finishes or a watch has been installed.
|
|
77
|
+
* @throws Error when a one-shot compile has failures or the source tree is invalid.
|
|
78
|
+
*/ async function precompile(options) {
|
|
47
79
|
const { failed } = await runFull(options);
|
|
48
80
|
if (options.watch) {
|
|
49
81
|
await startWatcher(options);
|
|
@@ -51,17 +83,22 @@ async function precompile(options) {
|
|
|
51
83
|
}
|
|
52
84
|
console.log("[shader-compiler-bundler] Done.");
|
|
53
85
|
if (failed > 0) {
|
|
54
|
-
|
|
86
|
+
throw new Error(`${failed} shader(s) failed to precompile.`);
|
|
55
87
|
}
|
|
56
88
|
}
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
89
|
+
/**
|
|
90
|
+
* Precompiles the current shader source tree once.
|
|
91
|
+
* @param options - Source, output, cleanup, and artifact-generation options.
|
|
92
|
+
* @returns An object containing the number of shader sources that failed to compile.
|
|
93
|
+
* @throws Error when the input tree or an explicitly selected source is invalid.
|
|
94
|
+
*/ async function runFull(options) {
|
|
95
|
+
const inputDir = path__default["default"].resolve(options.input);
|
|
96
|
+
const outputDir = path__default["default"].resolve(options.output);
|
|
60
97
|
const platformTarget = options.platformTarget ?? 0;
|
|
61
|
-
if (!
|
|
98
|
+
if (!fs__default["default"].existsSync(inputDir)) {
|
|
62
99
|
throw new Error(`Input directory does not exist: ${inputDir}`);
|
|
63
100
|
}
|
|
64
|
-
|
|
101
|
+
fs__default["default"].mkdirSync(outputDir, {
|
|
65
102
|
recursive: true
|
|
66
103
|
});
|
|
67
104
|
// Emit source indexes before compile so collectIncludeMap can resolve the freshly-built sources.
|
|
@@ -69,22 +106,13 @@ async function runFull(options) {
|
|
|
69
106
|
emitSources(inputDir);
|
|
70
107
|
}
|
|
71
108
|
const shaderCompiler = await loadShaderCompiler();
|
|
72
|
-
shaderCompiler.
|
|
109
|
+
shaderCompiler.setIncludeMap(await collectIncludeMap(inputDir));
|
|
73
110
|
let failed = 0;
|
|
74
111
|
if (options.only) {
|
|
75
|
-
const target =
|
|
112
|
+
const target = path__default["default"].resolve(options.only);
|
|
76
113
|
if (!compileSingle(shaderCompiler, target, inputDir, outputDir, platformTarget)) failed++;
|
|
77
114
|
} else {
|
|
78
|
-
|
|
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.`);
|
|
115
|
+
failed = compileAll(shaderCompiler, inputDir, outputDir, platformTarget, options.clean);
|
|
88
116
|
}
|
|
89
117
|
if (options.emitIndex) {
|
|
90
118
|
emitIndex(outputDir);
|
|
@@ -93,26 +121,40 @@ async function runFull(options) {
|
|
|
93
121
|
failed
|
|
94
122
|
};
|
|
95
123
|
}
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
124
|
+
/**
|
|
125
|
+
* Watches one shader source tree and serializes incremental rebuilds.
|
|
126
|
+
* @param options - Source, output, cleanup, and artifact-generation options.
|
|
127
|
+
* @returns Once the filesystem watcher has been installed.
|
|
128
|
+
* @throws Error when the initial include registry cannot be loaded.
|
|
129
|
+
*/ async function startWatcher(options) {
|
|
130
|
+
const inputDir = path__default["default"].resolve(options.input);
|
|
131
|
+
const outputDir = path__default["default"].resolve(options.output);
|
|
99
132
|
const platformTarget = options.platformTarget ?? 0;
|
|
133
|
+
if (!fs__default["default"].existsSync(inputDir)) {
|
|
134
|
+
throw new Error(`Input directory does not exist: ${inputDir}`);
|
|
135
|
+
}
|
|
136
|
+
fs__default["default"].mkdirSync(outputDir, {
|
|
137
|
+
recursive: true
|
|
138
|
+
});
|
|
100
139
|
const shaderCompiler = await loadShaderCompiler();
|
|
101
|
-
shaderCompiler.
|
|
140
|
+
shaderCompiler.setIncludeMap(await collectIncludeMap(inputDir));
|
|
102
141
|
// .glsl change → full recompile (can't cheaply track include graphs).
|
|
103
142
|
// .shader change → single-file recompile.
|
|
104
143
|
const handleInputChange = async (filename)=>{
|
|
105
144
|
if (!filename) return;
|
|
106
145
|
const norm = normalizePath(filename);
|
|
107
|
-
const fullPath =
|
|
146
|
+
const fullPath = path__default["default"].join(inputDir, filename);
|
|
108
147
|
if (norm.endsWith(".glsl")) {
|
|
109
148
|
console.log(`[shader-compiler-bundler] Include changed (${norm}), full recompile...`);
|
|
110
|
-
|
|
111
|
-
|
|
149
|
+
if (options.emitSources) emitSources(inputDir);
|
|
150
|
+
shaderCompiler.setIncludeMap(await collectIncludeMap(inputDir));
|
|
151
|
+
const failed = compileAll(shaderCompiler, inputDir, outputDir, platformTarget, options.clean);
|
|
152
|
+
if (options.emitIndex) emitIndex(outputDir);
|
|
153
|
+
if (failed > 0) console.error(`[shader-compiler-bundler] ${failed} shader(s) failed to precompile.`);
|
|
112
154
|
return;
|
|
113
155
|
}
|
|
114
156
|
if (!norm.endsWith(".shader")) return;
|
|
115
|
-
if (!
|
|
157
|
+
if (!fs__default["default"].existsSync(fullPath)) {
|
|
116
158
|
removeBundleFor(fullPath, inputDir, outputDir);
|
|
117
159
|
} else {
|
|
118
160
|
compileSingle(shaderCompiler, fullPath, inputDir, outputDir, platformTarget);
|
|
@@ -121,69 +163,93 @@ async function startWatcher(options) {
|
|
|
121
163
|
if (options.emitIndex) emitIndex(outputDir);
|
|
122
164
|
};
|
|
123
165
|
console.log(`[shader-compiler-bundler] Watching ${inputDir} ...`);
|
|
124
|
-
|
|
166
|
+
let changeQueue = Promise.resolve();
|
|
167
|
+
fs__default["default"].watch(inputDir, {
|
|
125
168
|
recursive: true
|
|
126
169
|
}, (_event, filename)=>{
|
|
127
|
-
handleInputChange(filename).catch((e)=>console.error(e));
|
|
170
|
+
changeQueue = changeQueue.then(()=>handleInputChange(filename)).catch((e)=>console.error(e));
|
|
128
171
|
});
|
|
129
172
|
}
|
|
173
|
+
function compileAll(shaderCompiler, inputDir, outputDir, platformTarget, clean) {
|
|
174
|
+
const shaderFiles = findFiles(inputDir, ".shader");
|
|
175
|
+
console.log(`[shader-compiler-bundler] Precompiling ${shaderFiles.length} shader(s)...`);
|
|
176
|
+
let failed = 0;
|
|
177
|
+
for (const file of shaderFiles){
|
|
178
|
+
if (!compileSingle(shaderCompiler, file, inputDir, outputDir, platformTarget)) failed++;
|
|
179
|
+
}
|
|
180
|
+
if (clean) {
|
|
181
|
+
const removed = cleanOrphanedBundles(shaderFiles, inputDir, outputDir);
|
|
182
|
+
if (removed > 0) console.log(`[shader-compiler-bundler] Cleaned ${removed} orphaned .shaderc file(s).`);
|
|
183
|
+
}
|
|
184
|
+
if (failed > 0) console.warn(`[shader-compiler-bundler] ${failed} shader(s) failed to precompile.`);
|
|
185
|
+
return failed;
|
|
186
|
+
}
|
|
130
187
|
async function loadShaderCompiler() {
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const instance = new mod.ShaderCompiler();
|
|
136
|
-
if (typeof instance._precompile !== "function") {
|
|
137
|
-
throw new Error("ShaderCompiler._precompile is not available; rebuild @galacean/engine-shader-compiler first.");
|
|
188
|
+
const mod = await (process.env.GALACEAN_SHADER_COMPILER_BOOTSTRAP === "true" ? importBootstrapPrecompiler() : importReleasePrecompiler());
|
|
189
|
+
const instance = new mod.ShaderPrecompiler();
|
|
190
|
+
if (typeof instance.precompile !== "function") {
|
|
191
|
+
throw new Error("ShaderPrecompiler is unavailable; rebuild @galacean/engine-shader-compiler first.");
|
|
138
192
|
}
|
|
139
193
|
return instance;
|
|
140
194
|
}
|
|
195
|
+
async function importBootstrapPrecompiler() {
|
|
196
|
+
// @ts-expect-error generated repo-local runtime has no declaration file
|
|
197
|
+
return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../.bootstrap/main.js')); });
|
|
198
|
+
}
|
|
199
|
+
async function importReleasePrecompiler() {
|
|
200
|
+
// @ts-expect-error generated Node-only runtime has no declaration file
|
|
201
|
+
return Promise.resolve().then(function () { return /*#__PURE__*/_interopNamespace(require('../dist/precompile.main.js')); });
|
|
202
|
+
}
|
|
141
203
|
// Local .glsl come from inputDir; standard library comes from
|
|
142
204
|
// `@galacean/engine-shader/sources` with a sibling-dir fallback for engine self-build cold-start.
|
|
143
205
|
async function collectIncludeMap(inputDir) {
|
|
144
|
-
const map =
|
|
206
|
+
const map = Object.create(null);
|
|
145
207
|
for (const file of findFiles(inputDir, ".glsl")){
|
|
146
|
-
const relPath = normalizePath(
|
|
147
|
-
map[
|
|
208
|
+
const relPath = normalizePath(path__default["default"].relative(inputDir, file));
|
|
209
|
+
addIncludeSource(map, relPath, fs__default["default"].readFileSync(file, "utf-8"));
|
|
148
210
|
}
|
|
149
|
-
let usedRelease = false;
|
|
150
211
|
// Resolve from inputDir's node_modules so consumers without transitive linkage to shader-compiler can still find it.
|
|
212
|
+
const requireFromInput = node_module.createRequire(path__default["default"].join(inputDir, "package.json"));
|
|
213
|
+
let sourcesPath;
|
|
151
214
|
try {
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
215
|
+
sourcesPath = requireFromInput.resolve("@galacean/engine-shader/sources");
|
|
216
|
+
} catch (error) {
|
|
217
|
+
if (error.code !== "MODULE_NOT_FOUND") throw error;
|
|
218
|
+
}
|
|
219
|
+
if (sourcesPath) {
|
|
220
|
+
const { shaderLibrary } = requireFromInput(sourcesPath);
|
|
155
221
|
for (const { source, path: chunkPath } of shaderLibrary){
|
|
156
|
-
map
|
|
222
|
+
addIncludeSource(map, chunkPath, source);
|
|
157
223
|
}
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
if (!usedRelease) {
|
|
163
|
-
const siblingLibrary = path.join(path.dirname(inputDir), "ShaderLibrary");
|
|
164
|
-
if (fs.existsSync(siblingLibrary)) {
|
|
224
|
+
} else {
|
|
225
|
+
// Engine self-build cold-start has no generated sources entry yet.
|
|
226
|
+
const siblingLibrary = path__default["default"].join(path__default["default"].dirname(inputDir), "ShaderLibrary");
|
|
227
|
+
if (fs__default["default"].existsSync(siblingLibrary)) {
|
|
165
228
|
for (const file of findFiles(siblingLibrary, ".glsl")){
|
|
166
|
-
const rel = normalizePath(
|
|
167
|
-
map
|
|
229
|
+
const rel = normalizePath(path__default["default"].relative(siblingLibrary, file));
|
|
230
|
+
addIncludeSource(map, `ShaderLibrary/${rel}`, fs__default["default"].readFileSync(file, "utf-8"));
|
|
168
231
|
}
|
|
169
232
|
}
|
|
170
233
|
}
|
|
171
234
|
return map;
|
|
172
235
|
}
|
|
173
|
-
|
|
174
|
-
|
|
236
|
+
function addIncludeSource(map, key, source) {
|
|
237
|
+
if (Object.prototype.hasOwnProperty.call(map, key)) {
|
|
238
|
+
throw new Error(`Shader include "${key}" is registered more than once.`);
|
|
239
|
+
}
|
|
240
|
+
map[key] = source;
|
|
241
|
+
}
|
|
175
242
|
function compileSingle(shaderCompiler, shaderPath, inputDir, outputDir, platformTarget) {
|
|
176
|
-
const
|
|
177
|
-
const
|
|
243
|
+
const relativePath = shaderSourceRelativePath(shaderPath, inputDir);
|
|
244
|
+
const source = fs__default["default"].readFileSync(shaderPath, "utf-8");
|
|
178
245
|
const bundleRelative = relativePath.replace(/\.shader$/, ".shaderc");
|
|
179
|
-
const bundlePath =
|
|
180
|
-
|
|
181
|
-
fs.mkdirSync(path.dirname(bundlePath), {
|
|
246
|
+
const bundlePath = path__default["default"].join(outputDir, bundleRelative);
|
|
247
|
+
fs__default["default"].mkdirSync(path__default["default"].dirname(bundlePath), {
|
|
182
248
|
recursive: true
|
|
183
249
|
});
|
|
184
250
|
try {
|
|
185
|
-
const precompiled = shaderCompiler.
|
|
186
|
-
|
|
251
|
+
const precompiled = shaderCompiler.precompile(source, platformTarget, relativePath);
|
|
252
|
+
fs__default["default"].writeFileSync(bundlePath, JSON.stringify(precompiled));
|
|
187
253
|
console.log(` ${relativePath} -> ${bundleRelative}`);
|
|
188
254
|
return true;
|
|
189
255
|
} catch (e) {
|
|
@@ -192,17 +258,24 @@ function compileSingle(shaderCompiler, shaderPath, inputDir, outputDir, platform
|
|
|
192
258
|
return false;
|
|
193
259
|
}
|
|
194
260
|
}
|
|
261
|
+
function shaderSourceRelativePath(shaderPath, inputDir) {
|
|
262
|
+
const relativePath = path__default["default"].relative(inputDir, shaderPath);
|
|
263
|
+
if (relativePath === ".." || relativePath.startsWith(`..${path__default["default"].sep}`) || path__default["default"].isAbsolute(relativePath) || path__default["default"].extname(relativePath) !== ".shader") {
|
|
264
|
+
throw new Error(`Shader source must be a .shader file inside the input directory: ${shaderPath}`);
|
|
265
|
+
}
|
|
266
|
+
return normalizePath(relativePath);
|
|
267
|
+
}
|
|
195
268
|
function cleanOrphanedBundles(shaderFiles, inputDir, outputDir) {
|
|
196
|
-
const aliveSet = new Set(shaderFiles.map((f)=>normalizePath(
|
|
269
|
+
const aliveSet = new Set(shaderFiles.map((f)=>normalizePath(path__default["default"].relative(inputDir, f)).replace(/\.shader$/, "")));
|
|
197
270
|
const bundleFiles = findFiles(outputDir, ".shaderc");
|
|
198
271
|
let removed = 0;
|
|
199
272
|
for (const bundleFile of bundleFiles){
|
|
200
|
-
const rel = normalizePath(
|
|
273
|
+
const rel = normalizePath(path__default["default"].relative(outputDir, bundleFile)).replace(/\.shaderc$/, "");
|
|
201
274
|
if (!aliveSet.has(rel)) {
|
|
202
|
-
|
|
275
|
+
fs__default["default"].unlinkSync(bundleFile);
|
|
203
276
|
console.log(` Removed orphaned: ${rel}.shaderc`);
|
|
204
277
|
removed++;
|
|
205
|
-
pruneEmptyDirs(
|
|
278
|
+
pruneEmptyDirs(path__default["default"].dirname(bundleFile), outputDir);
|
|
206
279
|
}
|
|
207
280
|
}
|
|
208
281
|
return removed;
|
|
@@ -210,19 +283,23 @@ function cleanOrphanedBundles(shaderFiles, inputDir, outputDir) {
|
|
|
210
283
|
function emitIndex(outputDir) {
|
|
211
284
|
const bundleFiles = findFiles(outputDir, ".shaderc");
|
|
212
285
|
const entries = bundleFiles.map((file)=>{
|
|
213
|
-
const rel = normalizePath(
|
|
286
|
+
const rel = normalizePath(path__default["default"].relative(outputDir, file));
|
|
214
287
|
return {
|
|
215
288
|
varName: shadercPathToVarName(rel),
|
|
216
289
|
importPath: `./${rel}`
|
|
217
290
|
};
|
|
218
291
|
}).sort((a, b)=>a.varName.localeCompare(b.varName));
|
|
292
|
+
assertUniqueIdentifiers(entries.map(({ varName, importPath })=>({
|
|
293
|
+
identifier: varName,
|
|
294
|
+
path: importPath
|
|
295
|
+
})));
|
|
219
296
|
const imports = entries.map((e)=>`import ${e.varName}Data from "${e.importPath}";`).join("\n");
|
|
220
297
|
const exports = entries.map((e)=>`export const ${e.varName}: IPrecompiledShader = ${e.varName}Data;`).join("\n");
|
|
221
298
|
const content = `// Auto-generated by shader-compiler-precompile --emit-index — do not edit.\nimport type { IPrecompiledShader } from "@galacean/engine-design";\n${imports}\n\n${exports}\n`;
|
|
222
|
-
const indexPath =
|
|
223
|
-
const existing =
|
|
299
|
+
const indexPath = path__default["default"].join(outputDir, "index.ts");
|
|
300
|
+
const existing = fs__default["default"].existsSync(indexPath) ? fs__default["default"].readFileSync(indexPath, "utf-8") : "";
|
|
224
301
|
if (content !== existing) {
|
|
225
|
-
|
|
302
|
+
fs__default["default"].writeFileSync(indexPath, content);
|
|
226
303
|
console.log(" Updated index.ts");
|
|
227
304
|
}
|
|
228
305
|
}
|
|
@@ -231,17 +308,21 @@ function emitIndex(outputDir) {
|
|
|
231
308
|
// <sibling>/ShaderLibrary/index.ts — `.glsl` array (only if the directory exists)
|
|
232
309
|
function emitSources(inputDir) {
|
|
233
310
|
emitShaderSourceIndex(inputDir);
|
|
234
|
-
const libraryDir =
|
|
235
|
-
if (
|
|
311
|
+
const libraryDir = path__default["default"].join(path__default["default"].dirname(inputDir), "ShaderLibrary");
|
|
312
|
+
if (fs__default["default"].existsSync(libraryDir)) {
|
|
236
313
|
emitLibrarySourceIndex(libraryDir);
|
|
237
314
|
}
|
|
238
315
|
}
|
|
239
316
|
function emitShaderSourceIndex(rootDir) {
|
|
240
|
-
const srcDir =
|
|
317
|
+
const srcDir = path__default["default"].dirname(rootDir);
|
|
241
318
|
const files = findFiles(rootDir, ".shader").sort();
|
|
242
|
-
const importPaths = files.map((f)=>normalizePath(
|
|
243
|
-
const paths = files.map((f)=>normalizePath(
|
|
319
|
+
const importPaths = files.map((f)=>normalizePath(path__default["default"].relative(rootDir, f)));
|
|
320
|
+
const paths = files.map((f)=>normalizePath(path__default["default"].relative(srcDir, f)));
|
|
244
321
|
const ids = importPaths.map((p)=>pathToIdentifier(p, ".shader"));
|
|
322
|
+
assertUniqueIdentifiers(ids.map((identifier, index)=>({
|
|
323
|
+
identifier,
|
|
324
|
+
path: importPaths[index]
|
|
325
|
+
})));
|
|
245
326
|
const lines = [];
|
|
246
327
|
lines.push("// Auto-generated by shader-compiler-precompile --emit-sources — do not edit.");
|
|
247
328
|
lines.push("");
|
|
@@ -257,16 +338,20 @@ function emitShaderSourceIndex(rootDir) {
|
|
|
257
338
|
paths.forEach((p, i)=>lines.push(` { source: ${ids[i]}, path: ${JSON.stringify(p)} },`));
|
|
258
339
|
lines.push("];");
|
|
259
340
|
lines.push("");
|
|
260
|
-
const out =
|
|
261
|
-
|
|
262
|
-
console.log(` Emitted shaders index ${
|
|
341
|
+
const out = path__default["default"].join(rootDir, "index.ts");
|
|
342
|
+
fs__default["default"].writeFileSync(out, lines.join("\n"), "utf-8");
|
|
343
|
+
console.log(` Emitted shaders index ${path__default["default"].relative(process.cwd(), out)} (${files.length})`);
|
|
263
344
|
}
|
|
264
345
|
function emitLibrarySourceIndex(rootDir) {
|
|
265
|
-
const srcDir =
|
|
346
|
+
const srcDir = path__default["default"].dirname(rootDir);
|
|
266
347
|
const files = findFiles(rootDir, ".glsl").sort();
|
|
267
|
-
const importPaths = files.map((f)=>normalizePath(
|
|
268
|
-
const paths = files.map((f)=>normalizePath(
|
|
348
|
+
const importPaths = files.map((f)=>normalizePath(path__default["default"].relative(rootDir, f)));
|
|
349
|
+
const paths = files.map((f)=>normalizePath(path__default["default"].relative(srcDir, f)));
|
|
269
350
|
const ids = importPaths.map((p)=>pathToIdentifier(p, ".glsl"));
|
|
351
|
+
assertUniqueIdentifiers(ids.map((identifier, index)=>({
|
|
352
|
+
identifier,
|
|
353
|
+
path: importPaths[index]
|
|
354
|
+
})));
|
|
270
355
|
const lines = [];
|
|
271
356
|
lines.push("// Auto-generated by shader-compiler-precompile --emit-sources — do not edit.");
|
|
272
357
|
lines.push("");
|
|
@@ -278,17 +363,27 @@ function emitLibrarySourceIndex(rootDir) {
|
|
|
278
363
|
paths.forEach((p, i)=>lines.push(` { source: ${ids[i]}, path: ${JSON.stringify(p)} },`));
|
|
279
364
|
lines.push("];");
|
|
280
365
|
lines.push("");
|
|
281
|
-
const out =
|
|
282
|
-
|
|
283
|
-
console.log(` Emitted library index ${
|
|
366
|
+
const out = path__default["default"].join(rootDir, "index.ts");
|
|
367
|
+
fs__default["default"].writeFileSync(out, lines.join("\n"), "utf-8");
|
|
368
|
+
console.log(` Emitted library index ${path__default["default"].relative(process.cwd(), out)} (${files.length})`);
|
|
369
|
+
}
|
|
370
|
+
function assertUniqueIdentifiers(entries) {
|
|
371
|
+
const pathsByIdentifier = new Map();
|
|
372
|
+
for (const { identifier, path: sourcePath } of entries){
|
|
373
|
+
const existingPath = pathsByIdentifier.get(identifier);
|
|
374
|
+
if (existingPath !== undefined) {
|
|
375
|
+
throw new Error(`Shader asset paths "${existingPath}" and "${sourcePath}" both emit identifier "${identifier}".`);
|
|
376
|
+
}
|
|
377
|
+
pathsByIdentifier.set(identifier, sourcePath);
|
|
378
|
+
}
|
|
284
379
|
}
|
|
285
380
|
function removeBundleFor(shaderPath, inputDir, outputDir) {
|
|
286
|
-
const rel = normalizePath(
|
|
287
|
-
const bundlePath =
|
|
288
|
-
if (
|
|
289
|
-
|
|
381
|
+
const rel = normalizePath(path__default["default"].relative(inputDir, shaderPath)).replace(/\.shader$/, ".shaderc");
|
|
382
|
+
const bundlePath = path__default["default"].join(outputDir, rel);
|
|
383
|
+
if (fs__default["default"].existsSync(bundlePath)) {
|
|
384
|
+
fs__default["default"].unlinkSync(bundlePath);
|
|
290
385
|
console.log(` Removed: ${rel}`);
|
|
291
|
-
pruneEmptyDirs(
|
|
386
|
+
pruneEmptyDirs(path__default["default"].dirname(bundlePath), outputDir);
|
|
292
387
|
}
|
|
293
388
|
}
|
|
294
389
|
|
|
@@ -299,7 +394,7 @@ function removeBundleFor(shaderPath, inputDir, outputDir) {
|
|
|
299
394
|
* shader-compiler-precompile <input-dir> <output-dir>
|
|
300
395
|
* [--clean] [--watch] [--only <file>] [--emit-index] [--emit-sources]
|
|
301
396
|
*/ async function main() {
|
|
302
|
-
const { positionals, values } = parseArgs({
|
|
397
|
+
const { positionals, values } = node_util.parseArgs({
|
|
303
398
|
allowPositionals: true,
|
|
304
399
|
options: {
|
|
305
400
|
clean: {
|