@aelionsdk/material-compiler 0.1.0-beta.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/LICENSE +21 -0
- package/README.md +7 -0
- package/dist/compiler.d.ts +10 -0
- package/dist/compiler.d.ts.map +1 -0
- package/dist/compiler.js +349 -0
- package/dist/index.d.ts +6 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +5 -0
- package/dist/node-registry.d.ts +4 -0
- package/dist/node-registry.d.ts.map +1 -0
- package/dist/node-registry.js +164 -0
- package/dist/types.d.ts +174 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +1 -0
- package/dist/webgl2.d.ts +3 -0
- package/dist/webgl2.d.ts.map +1 -0
- package/dist/webgl2.js +366 -0
- package/dist/webgpu.d.ts +3 -0
- package/dist/webgpu.d.ts.map +1 -0
- package/dist/webgpu.js +206 -0
- package/package.json +43 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 FoyonaCZY and AelionSDK contributors
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
# @aelionsdk/material-compiler
|
|
2
|
+
|
|
3
|
+
Aelion Material Protocol graph compiler for WebGL2 and WebGPU
|
|
4
|
+
|
|
5
|
+
Install with `npm install @aelionsdk/material-compiler@next`.
|
|
6
|
+
|
|
7
|
+
Version 0.1.0-beta.1 is a prerelease and its API may change before the first stable release. This package is part of [AelionSDK](https://github.com/FoyonaCZY/AelionSDK); see the repository README for supported browsers, examples and deployment requirements.
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { Diagnostic } from '@aelionsdk/core';
|
|
2
|
+
import type { CompileMaterialOptions, CompiledMaterialGraph, MaterialGraph, MaterialImplementationDescriptor, MaterialExecutionPlan, MaterialInstanceValue, MaterialRuntimeSelectionOptions, RuntimeMaterialDefinition } from './types.js';
|
|
3
|
+
export declare function compileMaterialGraph(graph: MaterialGraph, options: CompileMaterialOptions): CompiledMaterialGraph;
|
|
4
|
+
export declare function buildMaterialExecutionPlan(graph: MaterialGraph, compiled: CompiledMaterialGraph): MaterialExecutionPlan;
|
|
5
|
+
export declare function validateMaterialInstance(definition: RuntimeMaterialDefinition, instance: MaterialInstanceValue): readonly Diagnostic[];
|
|
6
|
+
export declare function selectMaterialImplementation(definition: RuntimeMaterialDefinition, options: MaterialRuntimeSelectionOptions): {
|
|
7
|
+
readonly implementation?: MaterialImplementationDescriptor;
|
|
8
|
+
readonly diagnostics: readonly Diagnostic[];
|
|
9
|
+
};
|
|
10
|
+
//# sourceMappingURL=compiler.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compiler.d.ts","sourceRoot":"","sources":["../src/compiler.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,UAAU,EAAa,MAAM,iBAAiB,CAAC;AAG7D,OAAO,KAAK,EACV,sBAAsB,EACtB,qBAAqB,EAErB,aAAa,EAEb,gCAAgC,EAEhC,qBAAqB,EACrB,qBAAqB,EACrB,+BAA+B,EAC/B,yBAAyB,EAC1B,MAAM,YAAY,CAAC;AAmGpB,wBAAgB,oBAAoB,CAClC,KAAK,EAAE,aAAa,EACpB,OAAO,EAAE,sBAAsB,GAC9B,qBAAqB,CAiKvB;AAED,wBAAgB,0BAA0B,CACxC,KAAK,EAAE,aAAa,EACpB,QAAQ,EAAE,qBAAqB,GAC9B,qBAAqB,CA0CvB;AA2BD,wBAAgB,wBAAwB,CACtC,UAAU,EAAE,yBAAyB,EACrC,QAAQ,EAAE,qBAAqB,GAC9B,SAAS,UAAU,EAAE,CAmGvB;AAED,wBAAgB,4BAA4B,CAC1C,UAAU,EAAE,yBAAyB,EACrC,OAAO,EAAE,+BAA+B,GACvC;IACD,QAAQ,CAAC,cAAc,CAAC,EAAE,gCAAgC,CAAC;IAC3D,QAAQ,CAAC,WAAW,EAAE,SAAS,UAAU,EAAE,CAAC;CAC7C,CAgDA"}
|
package/dist/compiler.js
ADDED
|
@@ -0,0 +1,349 @@
|
|
|
1
|
+
import { VISUAL_NODE_SET_1 } from './node-registry.js';
|
|
2
|
+
const DEFAULT_BUDGET = {
|
|
3
|
+
maxNodes: 128,
|
|
4
|
+
maxDepth: 32,
|
|
5
|
+
maxPasses: 8,
|
|
6
|
+
maxTextureSamples: 256,
|
|
7
|
+
};
|
|
8
|
+
function issue(diagnostics, code, message, path) {
|
|
9
|
+
diagnostics.push({
|
|
10
|
+
code,
|
|
11
|
+
severity: 'error',
|
|
12
|
+
message,
|
|
13
|
+
path,
|
|
14
|
+
recoverable: false,
|
|
15
|
+
});
|
|
16
|
+
}
|
|
17
|
+
function literalType(value) {
|
|
18
|
+
return typeof value === 'number' ? 'float' : typeof value === 'string' ? 'enum' : undefined;
|
|
19
|
+
}
|
|
20
|
+
function bindingType(binding, options, outputTypes, diagnostics, path) {
|
|
21
|
+
if ('value' in binding) {
|
|
22
|
+
const type = literalType(binding.value);
|
|
23
|
+
if (type === undefined) {
|
|
24
|
+
issue(diagnostics, 'MATERIAL_GRAPH_LITERAL_TYPE_INVALID', 'Unsupported literal type', path);
|
|
25
|
+
}
|
|
26
|
+
return type;
|
|
27
|
+
}
|
|
28
|
+
if ('parameter' in binding) {
|
|
29
|
+
const type = options.parameters[binding.parameter];
|
|
30
|
+
if (type === undefined) {
|
|
31
|
+
issue(diagnostics, 'MATERIAL_GRAPH_PARAMETER_MISSING', `Unknown parameter ${binding.parameter}`, path);
|
|
32
|
+
}
|
|
33
|
+
return type;
|
|
34
|
+
}
|
|
35
|
+
if ('inputPort' in binding) {
|
|
36
|
+
const type = options.inputPorts[binding.inputPort];
|
|
37
|
+
if (type === undefined) {
|
|
38
|
+
issue(diagnostics, 'MATERIAL_GRAPH_PORT_MISSING', `Unknown input port ${binding.inputPort}`, path);
|
|
39
|
+
}
|
|
40
|
+
return type;
|
|
41
|
+
}
|
|
42
|
+
if ('system' in binding) {
|
|
43
|
+
const type = (options.systems ?? { transitionProgress: 'float' })[binding.system];
|
|
44
|
+
if (type === undefined) {
|
|
45
|
+
issue(diagnostics, 'MATERIAL_GRAPH_SYSTEM_MISSING', `Unknown system value ${binding.system}`, path);
|
|
46
|
+
}
|
|
47
|
+
return type;
|
|
48
|
+
}
|
|
49
|
+
if ('node' in binding) {
|
|
50
|
+
const type = outputTypes.get(binding.node)?.[binding.output];
|
|
51
|
+
if (type === undefined) {
|
|
52
|
+
issue(diagnostics, 'MATERIAL_GRAPH_OUTPUT_MISSING', `Unknown node output ${binding.node}.${binding.output}`, path);
|
|
53
|
+
}
|
|
54
|
+
return type;
|
|
55
|
+
}
|
|
56
|
+
issue(diagnostics, 'MATERIAL_GRAPH_RESOURCE_UNTYPED', 'Resource bindings require an explicit typed node', path);
|
|
57
|
+
return undefined;
|
|
58
|
+
}
|
|
59
|
+
export function compileMaterialGraph(graph, options) {
|
|
60
|
+
const diagnostics = [];
|
|
61
|
+
const budget = { ...DEFAULT_BUDGET, ...options.budget };
|
|
62
|
+
const nodeById = new Map(graph.nodes.map(node => [node.id, node]));
|
|
63
|
+
if (nodeById.size !== graph.nodes.length) {
|
|
64
|
+
issue(diagnostics, 'MATERIAL_GRAPH_DUPLICATE_NODE', 'Node IDs must be unique', ['nodes']);
|
|
65
|
+
}
|
|
66
|
+
if (graph.nodes.length > budget.maxNodes) {
|
|
67
|
+
issue(diagnostics, 'MATERIAL_BUDGET_EXCEEDED', `Graph has ${graph.nodes.length} nodes; limit is ${budget.maxNodes}`, ['nodes']);
|
|
68
|
+
}
|
|
69
|
+
const visiting = new Set();
|
|
70
|
+
const visited = new Set();
|
|
71
|
+
const order = [];
|
|
72
|
+
const depths = new Map();
|
|
73
|
+
const visit = (id) => {
|
|
74
|
+
if (visiting.has(id)) {
|
|
75
|
+
issue(diagnostics, 'MATERIAL_DEPENDENCY_CYCLE', `Graph cycle includes ${id}`, ['nodes']);
|
|
76
|
+
return 0;
|
|
77
|
+
}
|
|
78
|
+
if (visited.has(id))
|
|
79
|
+
return depths.get(id) ?? 0;
|
|
80
|
+
const node = nodeById.get(id);
|
|
81
|
+
if (node === undefined)
|
|
82
|
+
return 0;
|
|
83
|
+
visiting.add(id);
|
|
84
|
+
let depth = 1;
|
|
85
|
+
for (const binding of Object.values(node.inputs)) {
|
|
86
|
+
if ('node' in binding) {
|
|
87
|
+
if (!nodeById.has(binding.node)) {
|
|
88
|
+
issue(diagnostics, 'MATERIAL_GRAPH_NODE_MISSING', `Node ${id} references missing node ${binding.node}`, ['nodes', id, 'inputs']);
|
|
89
|
+
}
|
|
90
|
+
else {
|
|
91
|
+
depth = Math.max(depth, visit(binding.node) + 1);
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
visiting.delete(id);
|
|
96
|
+
visited.add(id);
|
|
97
|
+
depths.set(id, depth);
|
|
98
|
+
order.push(id);
|
|
99
|
+
return depth;
|
|
100
|
+
};
|
|
101
|
+
graph.nodes.forEach(node => visit(node.id));
|
|
102
|
+
const depth = Math.max(0, ...depths.values());
|
|
103
|
+
if (depth > budget.maxDepth) {
|
|
104
|
+
issue(diagnostics, 'MATERIAL_BUDGET_EXCEEDED', `Graph depth ${depth} exceeds limit ${budget.maxDepth}`, ['nodes']);
|
|
105
|
+
}
|
|
106
|
+
const outputTypes = new Map();
|
|
107
|
+
let estimatedPasses = 0;
|
|
108
|
+
let estimatedTextureSamples = 0;
|
|
109
|
+
for (const id of order) {
|
|
110
|
+
const node = nodeById.get(id);
|
|
111
|
+
if (node === undefined)
|
|
112
|
+
continue;
|
|
113
|
+
const definition = VISUAL_NODE_SET_1.get(`${node.type}@${node.typeVersion}`);
|
|
114
|
+
if (definition === undefined) {
|
|
115
|
+
issue(diagnostics, 'MATERIAL_NODE_UNSUPPORTED', `Unsupported node ${node.type}@${node.typeVersion}`, ['nodes', id, 'type']);
|
|
116
|
+
continue;
|
|
117
|
+
}
|
|
118
|
+
for (const [inputName, expectedType] of Object.entries(definition.inputs)) {
|
|
119
|
+
const binding = node.inputs[inputName];
|
|
120
|
+
if (binding === undefined) {
|
|
121
|
+
issue(diagnostics, 'MATERIAL_GRAPH_INPUT_MISSING', `Node ${id} is missing input ${inputName}`, ['nodes', id, 'inputs', inputName]);
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
const actualType = bindingType(binding, options, outputTypes, diagnostics, [
|
|
125
|
+
'nodes',
|
|
126
|
+
id,
|
|
127
|
+
'inputs',
|
|
128
|
+
inputName,
|
|
129
|
+
]);
|
|
130
|
+
if (actualType !== undefined && actualType !== expectedType) {
|
|
131
|
+
issue(diagnostics, 'MATERIAL_GRAPH_TYPE_MISMATCH', `Node ${id} input ${inputName} expects ${expectedType}, received ${actualType}`, ['nodes', id, 'inputs', inputName]);
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
for (const inputName of Object.keys(node.inputs)) {
|
|
135
|
+
if (!(inputName in definition.inputs)) {
|
|
136
|
+
issue(diagnostics, 'MATERIAL_GRAPH_INPUT_UNKNOWN', `Node ${id} has unknown input ${inputName}`, ['nodes', id, 'inputs', inputName]);
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
outputTypes.set(id, definition.outputs);
|
|
140
|
+
estimatedPasses += definition.estimatedPasses;
|
|
141
|
+
estimatedTextureSamples += definition.estimatedTextureSamples;
|
|
142
|
+
}
|
|
143
|
+
for (const [name, output] of Object.entries(graph.outputs)) {
|
|
144
|
+
const type = outputTypes.get(output.node)?.[output.output];
|
|
145
|
+
if (type !== 'visual-frame') {
|
|
146
|
+
issue(diagnostics, 'MATERIAL_GRAPH_OUTPUT_INVALID', `Graph output ${name} must resolve to visual-frame`, ['outputs', name]);
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
if (estimatedPasses > budget.maxPasses || estimatedTextureSamples > budget.maxTextureSamples) {
|
|
150
|
+
issue(diagnostics, 'MATERIAL_BUDGET_EXCEEDED', 'Graph static execution budget exceeds host limits', ['nodes']);
|
|
151
|
+
}
|
|
152
|
+
return {
|
|
153
|
+
nodeSet: graph.nodeSet,
|
|
154
|
+
order,
|
|
155
|
+
depth,
|
|
156
|
+
estimatedPasses,
|
|
157
|
+
estimatedTextureSamples,
|
|
158
|
+
executableNodes: order.flatMap(id => {
|
|
159
|
+
const node = nodeById.get(id);
|
|
160
|
+
return node === undefined
|
|
161
|
+
? []
|
|
162
|
+
: [
|
|
163
|
+
{
|
|
164
|
+
id: node.id,
|
|
165
|
+
type: node.type,
|
|
166
|
+
typeVersion: node.typeVersion,
|
|
167
|
+
inputs: node.inputs,
|
|
168
|
+
},
|
|
169
|
+
];
|
|
170
|
+
}),
|
|
171
|
+
diagnostics,
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
export function buildMaterialExecutionPlan(graph, compiled) {
|
|
175
|
+
const passes = [];
|
|
176
|
+
let pendingFusedNodes = [];
|
|
177
|
+
const flushFused = (ownerId, samples) => {
|
|
178
|
+
if (pendingFusedNodes.length === 0)
|
|
179
|
+
return;
|
|
180
|
+
passes.push({
|
|
181
|
+
id: `${ownerId}:draw`,
|
|
182
|
+
kind: 'draw',
|
|
183
|
+
nodes: pendingFusedNodes,
|
|
184
|
+
estimatedTextureSamples: Math.max(1, samples),
|
|
185
|
+
});
|
|
186
|
+
pendingFusedNodes = [];
|
|
187
|
+
};
|
|
188
|
+
for (const id of compiled.order) {
|
|
189
|
+
const node = graph.nodes.find(value => value.id === id);
|
|
190
|
+
const definition = node === undefined ? undefined : VISUAL_NODE_SET_1.get(`${node.type}@${node.typeVersion}`);
|
|
191
|
+
if (node?.type === 'blur.gaussian') {
|
|
192
|
+
flushFused(id, 1);
|
|
193
|
+
passes.push({
|
|
194
|
+
id: `${id}:horizontal`,
|
|
195
|
+
kind: 'blur-horizontal',
|
|
196
|
+
nodes: [id],
|
|
197
|
+
estimatedTextureSamples: 8,
|
|
198
|
+
}, {
|
|
199
|
+
id: `${id}:vertical`,
|
|
200
|
+
kind: 'blur-vertical',
|
|
201
|
+
nodes: [id],
|
|
202
|
+
estimatedTextureSamples: 8,
|
|
203
|
+
});
|
|
204
|
+
continue;
|
|
205
|
+
}
|
|
206
|
+
pendingFusedNodes.push(id);
|
|
207
|
+
if ((definition?.estimatedPasses ?? 0) > 0) {
|
|
208
|
+
flushFused(id, definition?.estimatedTextureSamples ?? 1);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
flushFused('output', 1);
|
|
212
|
+
return { passes, intermediateTextureCount: Math.max(0, passes.length - 1) };
|
|
213
|
+
}
|
|
214
|
+
function runtimeIssue(diagnostics, code, message, path) {
|
|
215
|
+
issue(diagnostics, code, message, path);
|
|
216
|
+
}
|
|
217
|
+
function staticValue(value) {
|
|
218
|
+
if (value !== null && typeof value === 'object' && !Array.isArray(value)) {
|
|
219
|
+
const animation = Reflect.get(value, 'animation');
|
|
220
|
+
if (animation !== null && typeof animation === 'object') {
|
|
221
|
+
const keyframes = Reflect.get(animation, 'keyframes');
|
|
222
|
+
if (Array.isArray(keyframes)) {
|
|
223
|
+
const first = keyframes[0];
|
|
224
|
+
return first !== null && typeof first === 'object'
|
|
225
|
+
? (Reflect.get(first, 'value') ?? null)
|
|
226
|
+
: null;
|
|
227
|
+
}
|
|
228
|
+
}
|
|
229
|
+
}
|
|
230
|
+
return value;
|
|
231
|
+
}
|
|
232
|
+
export function validateMaterialInstance(definition, instance) {
|
|
233
|
+
const diagnostics = [];
|
|
234
|
+
const parameters = new Map(definition.parameters.map(value => [value.id, value]));
|
|
235
|
+
for (const [id, source] of Object.entries(instance.parameters)) {
|
|
236
|
+
const parameter = parameters.get(id);
|
|
237
|
+
if (parameter === undefined) {
|
|
238
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Unknown parameter ${id}`, [
|
|
239
|
+
'parameters',
|
|
240
|
+
id,
|
|
241
|
+
]);
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
const value = staticValue(source);
|
|
245
|
+
const numeric = parameter.type === 'float' || parameter.type === 'integer';
|
|
246
|
+
if (numeric && (typeof value !== 'number' || !Number.isFinite(value))) {
|
|
247
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Parameter ${id} must be finite`, [
|
|
248
|
+
'parameters',
|
|
249
|
+
id,
|
|
250
|
+
]);
|
|
251
|
+
continue;
|
|
252
|
+
}
|
|
253
|
+
if (parameter.type === 'integer' && !Number.isSafeInteger(value)) {
|
|
254
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Parameter ${id} must be a safe integer`, ['parameters', id]);
|
|
255
|
+
}
|
|
256
|
+
if (parameter.type === 'boolean' && typeof value !== 'boolean') {
|
|
257
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Parameter ${id} must be boolean`, [
|
|
258
|
+
'parameters',
|
|
259
|
+
id,
|
|
260
|
+
]);
|
|
261
|
+
}
|
|
262
|
+
if ((parameter.type === 'enum' || parameter.type === 'string') && typeof value !== 'string') {
|
|
263
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Parameter ${id} must be a string`, [
|
|
264
|
+
'parameters',
|
|
265
|
+
id,
|
|
266
|
+
]);
|
|
267
|
+
}
|
|
268
|
+
if (parameter.type === 'enum' &&
|
|
269
|
+
(typeof value !== 'string' || !parameter.values?.includes(value))) {
|
|
270
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Parameter ${id} has an invalid enum value`, ['parameters', id]);
|
|
271
|
+
}
|
|
272
|
+
if (numeric && typeof value === 'number') {
|
|
273
|
+
if (parameter.range?.min !== undefined && value < parameter.range.min) {
|
|
274
|
+
runtimeIssue(diagnostics, 'MATERIAL_PARAMETER_OUT_OF_RANGE', `${id} is below its minimum`, [
|
|
275
|
+
'parameters',
|
|
276
|
+
id,
|
|
277
|
+
]);
|
|
278
|
+
}
|
|
279
|
+
if (parameter.range?.max !== undefined && value > parameter.range.max) {
|
|
280
|
+
runtimeIssue(diagnostics, 'MATERIAL_PARAMETER_OUT_OF_RANGE', `${id} exceeds its maximum`, [
|
|
281
|
+
'parameters',
|
|
282
|
+
id,
|
|
283
|
+
]);
|
|
284
|
+
}
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
const resources = instance.resourceBindings ?? {};
|
|
288
|
+
for (const slot of definition.resourceSlots) {
|
|
289
|
+
if (slot.required &&
|
|
290
|
+
resources[slot.id] === undefined &&
|
|
291
|
+
slot.fallbackResourceId === undefined) {
|
|
292
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Required resource ${slot.id} is missing`, ['resourceBindings', slot.id]);
|
|
293
|
+
}
|
|
294
|
+
}
|
|
295
|
+
const inputBindings = instance.inputBindings ?? {};
|
|
296
|
+
for (const port of definition.ports) {
|
|
297
|
+
if (port.direction === 'input' &&
|
|
298
|
+
port.binding === 'instance' &&
|
|
299
|
+
port.required &&
|
|
300
|
+
inputBindings[port.id] === undefined) {
|
|
301
|
+
runtimeIssue(diagnostics, 'MATERIAL_INSTANCE_INVALID', `Required input ${port.id} is missing`, ['inputBindings', port.id]);
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
return diagnostics;
|
|
305
|
+
}
|
|
306
|
+
export function selectMaterialImplementation(definition, options) {
|
|
307
|
+
const executableCode = definition.implementations.some(value => value.type !== 'graph');
|
|
308
|
+
if (executableCode && options.trust !== 'trusted-code') {
|
|
309
|
+
return {
|
|
310
|
+
diagnostics: [
|
|
311
|
+
{
|
|
312
|
+
code: 'MATERIAL_TRUST_REQUIRED',
|
|
313
|
+
severity: 'error',
|
|
314
|
+
message: 'Shader/WASM Material implementations require a trusted-code package',
|
|
315
|
+
recoverable: false,
|
|
316
|
+
},
|
|
317
|
+
],
|
|
318
|
+
};
|
|
319
|
+
}
|
|
320
|
+
if (options.trust === 'trusted-code' &&
|
|
321
|
+
executableCode &&
|
|
322
|
+
options.trustedCodeAuthorized !== true) {
|
|
323
|
+
return {
|
|
324
|
+
diagnostics: [
|
|
325
|
+
{
|
|
326
|
+
code: 'MATERIAL_TRUST_REQUIRED',
|
|
327
|
+
severity: 'error',
|
|
328
|
+
message: 'The host did not authorize this trusted-code Material package',
|
|
329
|
+
recoverable: false,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
const implementation = definition.implementations.find(value => (value.type === 'graph' && options.backend !== 'cpu') ||
|
|
335
|
+
(value.type === 'shader' && value.backend === options.backend) ||
|
|
336
|
+
(value.type === 'wasm' && options.backend === 'cpu'));
|
|
337
|
+
return implementation === undefined
|
|
338
|
+
? {
|
|
339
|
+
diagnostics: [
|
|
340
|
+
{
|
|
341
|
+
code: 'MATERIAL_BACKEND_UNAVAILABLE',
|
|
342
|
+
severity: 'error',
|
|
343
|
+
message: `No Material implementation supports ${options.backend}`,
|
|
344
|
+
recoverable: true,
|
|
345
|
+
},
|
|
346
|
+
],
|
|
347
|
+
}
|
|
348
|
+
: { implementation, diagnostics: [] };
|
|
349
|
+
}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,eAAe,CAAC;AAC9B,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,aAAa,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"node-registry.d.ts","sourceRoot":"","sources":["../src/node-registry.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAqKzD,eAAO,MAAM,iBAAiB,qCAE7B,CAAC;AAEF,eAAO,MAAM,6BAA6B,mCAAc,CAAC"}
|
|
@@ -0,0 +1,164 @@
|
|
|
1
|
+
const definitions = [
|
|
2
|
+
{
|
|
3
|
+
type: 'time.transition-curve',
|
|
4
|
+
version: '1.0.0',
|
|
5
|
+
inputs: { progress: 'float', curve: 'enum' },
|
|
6
|
+
outputs: { value: 'float' },
|
|
7
|
+
estimatedTextureSamples: 0,
|
|
8
|
+
estimatedPasses: 0,
|
|
9
|
+
},
|
|
10
|
+
{
|
|
11
|
+
type: 'composite.mix',
|
|
12
|
+
version: '1.0.0',
|
|
13
|
+
inputs: { a: 'visual-frame', b: 'visual-frame', amount: 'float' },
|
|
14
|
+
outputs: { frame: 'visual-frame' },
|
|
15
|
+
estimatedTextureSamples: 2,
|
|
16
|
+
estimatedPasses: 1,
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
type: 'color.temperature',
|
|
20
|
+
version: '1.0.0',
|
|
21
|
+
inputs: { source: 'visual-frame', amount: 'float' },
|
|
22
|
+
outputs: { frame: 'visual-frame' },
|
|
23
|
+
estimatedTextureSamples: 1,
|
|
24
|
+
estimatedPasses: 0,
|
|
25
|
+
},
|
|
26
|
+
{
|
|
27
|
+
type: 'color.lift-black',
|
|
28
|
+
version: '1.0.0',
|
|
29
|
+
inputs: { source: 'visual-frame', amount: 'float' },
|
|
30
|
+
outputs: { frame: 'visual-frame' },
|
|
31
|
+
estimatedTextureSamples: 1,
|
|
32
|
+
estimatedPasses: 0,
|
|
33
|
+
},
|
|
34
|
+
{
|
|
35
|
+
type: 'color.extract-highlights',
|
|
36
|
+
version: '1.0.0',
|
|
37
|
+
inputs: { source: 'visual-frame', threshold: 'float' },
|
|
38
|
+
outputs: { frame: 'visual-frame' },
|
|
39
|
+
estimatedTextureSamples: 1,
|
|
40
|
+
estimatedPasses: 1,
|
|
41
|
+
},
|
|
42
|
+
{
|
|
43
|
+
type: 'blur.gaussian',
|
|
44
|
+
version: '1.0.0',
|
|
45
|
+
inputs: { source: 'visual-frame', radiusPx: 'float' },
|
|
46
|
+
outputs: { frame: 'visual-frame' },
|
|
47
|
+
estimatedTextureSamples: 16,
|
|
48
|
+
estimatedPasses: 2,
|
|
49
|
+
},
|
|
50
|
+
{
|
|
51
|
+
type: 'color.scale-rgb',
|
|
52
|
+
version: '1.0.0',
|
|
53
|
+
inputs: { source: 'visual-frame', scale: 'float' },
|
|
54
|
+
outputs: { frame: 'visual-frame' },
|
|
55
|
+
estimatedTextureSamples: 1,
|
|
56
|
+
estimatedPasses: 0,
|
|
57
|
+
},
|
|
58
|
+
{
|
|
59
|
+
type: 'composite.screen',
|
|
60
|
+
version: '1.0.0',
|
|
61
|
+
inputs: { base: 'visual-frame', overlay: 'visual-frame' },
|
|
62
|
+
outputs: { frame: 'visual-frame' },
|
|
63
|
+
estimatedTextureSamples: 2,
|
|
64
|
+
estimatedPasses: 1,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
type: 'math.add',
|
|
68
|
+
version: '1.0.0',
|
|
69
|
+
inputs: { a: 'float', b: 'float' },
|
|
70
|
+
outputs: { value: 'float' },
|
|
71
|
+
estimatedTextureSamples: 0,
|
|
72
|
+
estimatedPasses: 0,
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
type: 'math.subtract',
|
|
76
|
+
version: '1.0.0',
|
|
77
|
+
inputs: { a: 'float', b: 'float' },
|
|
78
|
+
outputs: { value: 'float' },
|
|
79
|
+
estimatedTextureSamples: 0,
|
|
80
|
+
estimatedPasses: 0,
|
|
81
|
+
},
|
|
82
|
+
{
|
|
83
|
+
type: 'math.multiply',
|
|
84
|
+
version: '1.0.0',
|
|
85
|
+
inputs: { a: 'float', b: 'float' },
|
|
86
|
+
outputs: { value: 'float' },
|
|
87
|
+
estimatedTextureSamples: 0,
|
|
88
|
+
estimatedPasses: 0,
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
type: 'math.divide',
|
|
92
|
+
version: '1.0.0',
|
|
93
|
+
inputs: { a: 'float', b: 'float' },
|
|
94
|
+
outputs: { value: 'float' },
|
|
95
|
+
estimatedTextureSamples: 0,
|
|
96
|
+
estimatedPasses: 0,
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
type: 'math.clamp',
|
|
100
|
+
version: '1.0.0',
|
|
101
|
+
inputs: { value: 'float', min: 'float', max: 'float' },
|
|
102
|
+
outputs: { value: 'float' },
|
|
103
|
+
estimatedTextureSamples: 0,
|
|
104
|
+
estimatedPasses: 0,
|
|
105
|
+
},
|
|
106
|
+
{
|
|
107
|
+
type: 'math.smoothstep',
|
|
108
|
+
version: '1.0.0',
|
|
109
|
+
inputs: { edge0: 'float', edge1: 'float', x: 'float' },
|
|
110
|
+
outputs: { value: 'float' },
|
|
111
|
+
estimatedTextureSamples: 0,
|
|
112
|
+
estimatedPasses: 0,
|
|
113
|
+
},
|
|
114
|
+
{
|
|
115
|
+
type: 'color.exposure',
|
|
116
|
+
version: '1.0.0',
|
|
117
|
+
inputs: { source: 'visual-frame', stops: 'float' },
|
|
118
|
+
outputs: { frame: 'visual-frame' },
|
|
119
|
+
estimatedTextureSamples: 1,
|
|
120
|
+
estimatedPasses: 0,
|
|
121
|
+
},
|
|
122
|
+
{
|
|
123
|
+
type: 'color.contrast',
|
|
124
|
+
version: '1.0.0',
|
|
125
|
+
inputs: { source: 'visual-frame', amount: 'float' },
|
|
126
|
+
outputs: { frame: 'visual-frame' },
|
|
127
|
+
estimatedTextureSamples: 1,
|
|
128
|
+
estimatedPasses: 0,
|
|
129
|
+
},
|
|
130
|
+
{
|
|
131
|
+
type: 'color.saturation',
|
|
132
|
+
version: '1.0.0',
|
|
133
|
+
inputs: { source: 'visual-frame', amount: 'float' },
|
|
134
|
+
outputs: { frame: 'visual-frame' },
|
|
135
|
+
estimatedTextureSamples: 1,
|
|
136
|
+
estimatedPasses: 0,
|
|
137
|
+
},
|
|
138
|
+
{
|
|
139
|
+
type: 'color.invert',
|
|
140
|
+
version: '1.0.0',
|
|
141
|
+
inputs: { source: 'visual-frame' },
|
|
142
|
+
outputs: { frame: 'visual-frame' },
|
|
143
|
+
estimatedTextureSamples: 1,
|
|
144
|
+
estimatedPasses: 0,
|
|
145
|
+
},
|
|
146
|
+
{
|
|
147
|
+
type: 'composite.multiply',
|
|
148
|
+
version: '1.0.0',
|
|
149
|
+
inputs: { base: 'visual-frame', overlay: 'visual-frame' },
|
|
150
|
+
outputs: { frame: 'visual-frame' },
|
|
151
|
+
estimatedTextureSamples: 2,
|
|
152
|
+
estimatedPasses: 1,
|
|
153
|
+
},
|
|
154
|
+
{
|
|
155
|
+
type: 'composite.add',
|
|
156
|
+
version: '1.0.0',
|
|
157
|
+
inputs: { base: 'visual-frame', overlay: 'visual-frame' },
|
|
158
|
+
outputs: { frame: 'visual-frame' },
|
|
159
|
+
estimatedTextureSamples: 2,
|
|
160
|
+
estimatedPasses: 1,
|
|
161
|
+
},
|
|
162
|
+
];
|
|
163
|
+
export const VISUAL_NODE_SET_1 = new Map(definitions.map(definition => [`${definition.type}@${definition.version}`, definition]));
|
|
164
|
+
export const VISUAL_NODE_SET_1_DEFINITIONS = definitions;
|