@luma.gl/shaders 10.0.0-alpha.8 → 10.0.0-alpha.9
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/dist/dist.dev.js +189 -268
- package/dist/dist.min.js +119 -119
- package/dist/glsl.cjs.map +2 -2
- package/dist/index.cjs.map +3 -3
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib/shader-assembler-base.cjs +3 -4
- package/dist/lib/shader-assembler-base.d.ts +4 -5
- package/dist/lib/shader-assembler-base.d.ts.map +1 -1
- package/dist/lib/shader-assembler-base.js +3 -4
- package/dist/lib/shader-assembler-base.js.map +1 -1
- package/dist/lib/shader-assembly/assemble-glsl-shaders.cjs +5 -16
- package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts +4 -4
- package/dist/lib/shader-assembly/assemble-glsl-shaders.d.ts.map +1 -1
- package/dist/lib/shader-assembly/assemble-glsl-shaders.js +13 -18
- package/dist/lib/shader-assembly/assemble-glsl-shaders.js.map +1 -1
- package/dist/lib/shader-assembly/assemble-wgsl-shaders.cjs +5 -24
- package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts +5 -6
- package/dist/lib/shader-assembly/assemble-wgsl-shaders.d.ts.map +1 -1
- package/dist/lib/shader-assembly/assemble-wgsl-shaders.js +13 -26
- package/dist/lib/shader-assembly/assemble-wgsl-shaders.js.map +1 -1
- package/dist/lib/shader-assembly/shader-assembly-shared.d.ts +2 -2
- package/dist/lib/shader-assembly/shader-assembly-shared.d.ts.map +1 -1
- package/dist/lib/shader-assembly/shader-assembly-shared.js.map +1 -1
- package/dist/lib/shader-assembly/shader-assembly-types.d.ts +7 -7
- package/dist/lib/shader-assembly/shader-assembly-types.d.ts.map +1 -1
- package/dist/lib/shader-assembly/shader-hooks.cjs +17 -36
- package/dist/lib/shader-assembly/shader-hooks.d.ts +34 -32
- package/dist/lib/shader-assembly/shader-hooks.d.ts.map +1 -1
- package/dist/lib/shader-assembly/shader-hooks.js +20 -44
- package/dist/lib/shader-assembly/shader-hooks.js.map +1 -1
- package/dist/lib/shader-assembly/shader-injections.cjs +26 -2
- package/dist/lib/shader-assembly/shader-injections.d.ts +20 -7
- package/dist/lib/shader-assembly/shader-injections.d.ts.map +1 -1
- package/dist/lib/shader-assembly/shader-injections.js +24 -0
- package/dist/lib/shader-assembly/shader-injections.js.map +1 -1
- package/dist/lib/shader-module/shader-module.cjs +3 -9
- package/dist/lib/shader-module/shader-module.d.ts +4 -8
- package/dist/lib/shader-module/shader-module.d.ts.map +1 -1
- package/dist/lib/shader-module/shader-module.js +3 -6
- package/dist/lib/shader-module/shader-module.js.map +1 -1
- package/dist/lib/shader-plugin.cjs +13 -38
- package/dist/lib/shader-plugin.d.ts +5 -30
- package/dist/lib/shader-plugin.d.ts.map +1 -1
- package/dist/lib/shader-plugin.js +17 -40
- package/dist/lib/shader-plugin.js.map +1 -1
- package/dist/wgsl.cjs.map +2 -2
- package/package.json +2 -2
- package/src/index.ts +5 -6
- package/src/lib/shader-assembler-base.ts +5 -6
- package/src/lib/shader-assembly/assemble-glsl-shaders.ts +25 -25
- package/src/lib/shader-assembly/assemble-wgsl-shaders.ts +27 -42
- package/src/lib/shader-assembly/shader-assembly-shared.ts +5 -5
- package/src/lib/shader-assembly/shader-assembly-types.ts +7 -7
- package/src/lib/shader-assembly/shader-hooks.ts +55 -82
- package/src/lib/shader-assembly/shader-injections.ts +52 -8
- package/src/lib/shader-module/shader-module.ts +7 -14
- package/src/lib/shader-plugin.ts +31 -90
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
import {type PlatformInfo} from './platform-info';
|
|
6
6
|
import {type ShaderModule} from '../shader-module/shader-module';
|
|
7
7
|
import {type ShaderHook} from './shader-hooks';
|
|
8
|
-
import {type ShaderInjection} from './shader-injections';
|
|
8
|
+
import {type ShaderInjection, type ResolvedShaderInjection} from './shader-injections';
|
|
9
9
|
import {type AttributeShaderType} from '../../types';
|
|
10
10
|
import {type ResolvedShaderPluginInterStageVariable} from '../shader-plugin';
|
|
11
11
|
|
|
@@ -45,13 +45,13 @@ export type AssembleShaderOptions = {
|
|
|
45
45
|
/** GLSL only: Overrides to be injected. In WGSL these are supplied during Pipeline creation time */
|
|
46
46
|
constants?: Record<string, number>;
|
|
47
47
|
/** Per-call hooks, appended after registered assembler hooks; matching names override for this call. */
|
|
48
|
-
hookFunctions?: readonly
|
|
48
|
+
hookFunctions?: readonly ShaderHook[];
|
|
49
49
|
/** Hook declarations contributed by resolved plugins. */
|
|
50
50
|
pluginHooks?: readonly ShaderHook[];
|
|
51
51
|
/** Code injections */
|
|
52
|
-
inject?: Record<string, string |
|
|
52
|
+
inject?: readonly ShaderInjection[] | Record<string, string | ResolvedShaderInjection>;
|
|
53
53
|
/** Ordered code injections contributed by ShaderPlugin descriptors. */
|
|
54
|
-
pluginInjections?: Record<string,
|
|
54
|
+
pluginInjections?: Record<string, ResolvedShaderInjection[]>;
|
|
55
55
|
/** Vertex inputs contributed by ShaderPlugin descriptors. */
|
|
56
56
|
pluginVertexInputs?: Record<string, AttributeShaderType>;
|
|
57
57
|
/** Inter-stage variables contributed by ShaderPlugin descriptors. */
|
|
@@ -81,13 +81,13 @@ export type AssembleStageOptions = {
|
|
|
81
81
|
/** GLSL only: Overrides to be injected. In WGSL these are supplied during Pipeline creation time */
|
|
82
82
|
constants?: Record<string, number>;
|
|
83
83
|
/** Per-call hooks, appended after registered assembler hooks; matching names override for this call. */
|
|
84
|
-
hookFunctions?: readonly
|
|
84
|
+
hookFunctions?: readonly ShaderHook[];
|
|
85
85
|
/** Hook declarations contributed by resolved plugins. */
|
|
86
86
|
pluginHooks?: readonly ShaderHook[];
|
|
87
87
|
/** Code injections */
|
|
88
|
-
inject?: Record<string, string |
|
|
88
|
+
inject?: readonly ShaderInjection[] | Record<string, string | ResolvedShaderInjection>;
|
|
89
89
|
/** Ordered code injections contributed by ShaderPlugin descriptors. */
|
|
90
|
-
pluginInjections?: Record<string,
|
|
90
|
+
pluginInjections?: Record<string, ResolvedShaderInjection[]>;
|
|
91
91
|
/** Vertex inputs contributed by ShaderPlugin descriptors. */
|
|
92
92
|
pluginVertexInputs?: Record<string, AttributeShaderType>;
|
|
93
93
|
/** Inter-stage variables contributed by ShaderPlugin descriptors. */
|
|
@@ -2,51 +2,57 @@
|
|
|
2
2
|
// SPDX-License-Identifier: MIT
|
|
3
3
|
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
|
-
import type {
|
|
5
|
+
import type {ResolvedShaderInjection} from './shader-injections';
|
|
6
6
|
|
|
7
|
-
/**
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
*
|
|
15
|
-
* If a shader module injects code it will be executed upon the hook
|
|
16
|
-
* function call.
|
|
17
|
-
*/
|
|
18
|
-
export type ShaderHook = {
|
|
19
|
-
/** `vs:` or `fs:` followed by the name and arguments of the function, e.g. `vs:MYHOOK_func(inout vec4 value)`. Hook name without arguments
|
|
20
|
-
will also be used as the name of the shader hook */
|
|
21
|
-
hook: string;
|
|
22
|
-
/** Code always included at the beginning of a hook function */
|
|
7
|
+
/** Language-specific source for a callable shader hook. */
|
|
8
|
+
export type ShaderHookImplementation = {
|
|
9
|
+
/** Function parameters in the selected shader language, without parentheses. */
|
|
10
|
+
parameters: string;
|
|
11
|
+
/** Return type in this language. Omit for a hook that returns no value. */
|
|
12
|
+
returnType?: string;
|
|
13
|
+
/** Source included before injected code. */
|
|
23
14
|
header?: string;
|
|
24
|
-
/**
|
|
15
|
+
/** Source included after injected code. */
|
|
25
16
|
footer?: string;
|
|
26
|
-
/** To Be Documented */
|
|
27
|
-
signature?: string;
|
|
28
17
|
};
|
|
29
18
|
|
|
30
|
-
/**
|
|
31
|
-
export type
|
|
19
|
+
/** One logical extension point with independent GLSL and WGSL syntax. */
|
|
20
|
+
export type ShaderHook = {
|
|
21
|
+
/** Function name shared by both shader languages. */
|
|
22
|
+
name: string;
|
|
23
|
+
/** Shader stage that owns the hook. */
|
|
24
|
+
stage: 'vertex' | 'fragment';
|
|
25
|
+
/** GLSL implementation. Omit to skip this hook during GLSL assembly. */
|
|
26
|
+
glsl?: ShaderHookImplementation;
|
|
27
|
+
/** WGSL implementation. Omit to skip this hook during WGSL assembly. */
|
|
28
|
+
wgsl?: ShaderHookImplementation;
|
|
29
|
+
};
|
|
32
30
|
|
|
33
|
-
/**
|
|
31
|
+
/** Language-selected hook ready for source generation. */
|
|
32
|
+
type ResolvedShaderHook = ShaderHookImplementation & {signature: string};
|
|
33
|
+
|
|
34
|
+
/** Normalized shader hooks per shader */
|
|
34
35
|
export type ShaderHookRegistry = {
|
|
35
|
-
vertex
|
|
36
|
-
|
|
36
|
+
/** Normalized shader hooks for vertex shader */
|
|
37
|
+
vertex: Record<string, ResolvedShaderHook>;
|
|
38
|
+
/** Normalized shader hooks for fragment shader */
|
|
39
|
+
fragment: Record<string, ResolvedShaderHook>;
|
|
37
40
|
};
|
|
38
41
|
|
|
39
42
|
/** Generate hook source code */
|
|
40
43
|
export function getShaderHooks(
|
|
41
|
-
hookFunctions: Record<string,
|
|
42
|
-
hookInjections: Record<string,
|
|
44
|
+
hookFunctions: Record<string, ResolvedShaderHook>,
|
|
45
|
+
hookInjections: Record<string, ResolvedShaderInjection[]>,
|
|
43
46
|
shaderLanguage: 'glsl' | 'wgsl' = 'glsl'
|
|
44
47
|
): string {
|
|
45
48
|
let result = '';
|
|
46
49
|
for (const hookName in hookFunctions) {
|
|
47
50
|
const hookFunction = hookFunctions[hookName];
|
|
48
|
-
const
|
|
49
|
-
|
|
51
|
+
const returnType = hookFunction.returnType || 'void';
|
|
52
|
+
const functionPrefix = shaderLanguage === 'wgsl' ? 'fn' : returnType;
|
|
53
|
+
const returnSuffix =
|
|
54
|
+
shaderLanguage === 'wgsl' && returnType !== 'void' ? ` -> ${returnType}` : '';
|
|
55
|
+
result += `${functionPrefix} ${hookFunction.signature}${returnSuffix} {\n`;
|
|
50
56
|
if (hookFunction.header) {
|
|
51
57
|
result += ` ${hookFunction.header}`;
|
|
52
58
|
}
|
|
@@ -66,37 +72,33 @@ export function getShaderHooks(
|
|
|
66
72
|
return result;
|
|
67
73
|
}
|
|
68
74
|
|
|
69
|
-
/**
|
|
70
|
-
* Parse string based hook functions and split them per shader stage.
|
|
71
|
-
* A later declaration of the same stage-prefixed hook name replaces an earlier one.
|
|
72
|
-
*/
|
|
75
|
+
/** Select language syntax and merge compatible plugin declarations without mutating descriptors. */
|
|
73
76
|
export function normalizeShaderHooks(
|
|
74
|
-
hookFunctions: readonly
|
|
77
|
+
hookFunctions: readonly ShaderHook[],
|
|
75
78
|
pluginHooks: readonly ShaderHook[] = [],
|
|
76
|
-
language
|
|
79
|
+
language: 'glsl' | 'wgsl' = 'glsl'
|
|
77
80
|
): ShaderHookRegistry {
|
|
78
81
|
const result: ShaderHookRegistry = {vertex: {}, fragment: {}};
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
? qualified[2]
|
|
89
|
-
: {...hookFunction, hook: qualified[2]}
|
|
90
|
-
: hookFunction
|
|
91
|
-
);
|
|
82
|
+
for (const hook of hookFunctions) {
|
|
83
|
+
const implementation = hook[language];
|
|
84
|
+
if (implementation) {
|
|
85
|
+
const prefix = hook.stage === 'vertex' ? 'vs' : 'fs';
|
|
86
|
+
result[hook.stage][`${prefix}:${hook.name}`] = {
|
|
87
|
+
...implementation,
|
|
88
|
+
signature: `${hook.name}(${implementation.parameters})`
|
|
89
|
+
};
|
|
90
|
+
}
|
|
92
91
|
}
|
|
93
|
-
|
|
94
92
|
for (const hook of pluginHooks) {
|
|
95
|
-
const
|
|
93
|
+
const selected = normalizeShaderHooks([hook], [], language);
|
|
96
94
|
for (const stage of ['vertex', 'fragment'] as const) {
|
|
97
|
-
for (const [name, declaration] of Object.entries(
|
|
95
|
+
for (const [name, declaration] of Object.entries(selected[stage])) {
|
|
98
96
|
const existing = result[stage][name];
|
|
99
|
-
if (
|
|
97
|
+
if (
|
|
98
|
+
existing &&
|
|
99
|
+
(existing.signature !== declaration.signature ||
|
|
100
|
+
(existing.returnType || 'void') !== (declaration.returnType || 'void'))
|
|
101
|
+
) {
|
|
100
102
|
throw new Error(`Conflicting ShaderPlugin hook ${name}`);
|
|
101
103
|
}
|
|
102
104
|
result[stage][name] = existing || declaration;
|
|
@@ -118,7 +120,7 @@ export function isShaderHookRegistered(registry: ShaderHookRegistry, hookName: s
|
|
|
118
120
|
*/
|
|
119
121
|
export function validateShaderHookInjections(
|
|
120
122
|
registry: ShaderHookRegistry,
|
|
121
|
-
hookInjections: Record<string,
|
|
123
|
+
hookInjections: Record<string, ResolvedShaderInjection[]>,
|
|
122
124
|
stage?: 'vertex' | 'fragment'
|
|
123
125
|
): void {
|
|
124
126
|
const stagePrefix = stage === 'vertex' ? 'vs:' : stage === 'fragment' ? 'fs:' : null;
|
|
@@ -139,32 +141,3 @@ export function validateShaderHookInjections(
|
|
|
139
141
|
}
|
|
140
142
|
}
|
|
141
143
|
}
|
|
142
|
-
|
|
143
|
-
function addShaderHookToRegistry(
|
|
144
|
-
registry: ShaderHookRegistry,
|
|
145
|
-
hookFunction: ShaderHook | string
|
|
146
|
-
): void {
|
|
147
|
-
const options: ShaderHookOptions = typeof hookFunction === 'string' ? {} : hookFunction;
|
|
148
|
-
const hook = (typeof hookFunction === 'string' ? hookFunction : hookFunction.hook).trim();
|
|
149
|
-
const stageSeparatorIndex = hook.indexOf(':');
|
|
150
|
-
const shaderStage = hook.slice(0, stageSeparatorIndex);
|
|
151
|
-
const signature = hook.slice(stageSeparatorIndex + 1);
|
|
152
|
-
const name = hook.split('(')[0].trim();
|
|
153
|
-
const normalizedHook: Required<ShaderHook> = {
|
|
154
|
-
hook: name,
|
|
155
|
-
signature,
|
|
156
|
-
header: options.header || '',
|
|
157
|
-
footer: options.footer || ''
|
|
158
|
-
};
|
|
159
|
-
|
|
160
|
-
switch (shaderStage) {
|
|
161
|
-
case 'vs':
|
|
162
|
-
registry.vertex[name] = normalizedHook;
|
|
163
|
-
break;
|
|
164
|
-
case 'fs':
|
|
165
|
-
registry.fragment[name] = normalizedHook;
|
|
166
|
-
break;
|
|
167
|
-
default:
|
|
168
|
-
throw new Error(shaderStage);
|
|
169
|
-
}
|
|
170
|
-
}
|
|
@@ -17,29 +17,71 @@ const fragments: string[] = [];
|
|
|
17
17
|
|
|
18
18
|
export const DECLARATION_INJECT_MARKER = '__LUMA_INJECT_DECLARATIONS__';
|
|
19
19
|
|
|
20
|
-
/**
|
|
21
|
-
*
|
|
22
|
-
*/
|
|
20
|
+
/** Language-specific source inserted at a named shader anchor or hook. */
|
|
23
21
|
export type ShaderInjection = {
|
|
22
|
+
/** Shader stage receiving the injection. */
|
|
23
|
+
stage: 'vertex' | 'fragment';
|
|
24
|
+
/** Unqualified hook name or structural anchor such as #main-start. */
|
|
25
|
+
target: string;
|
|
26
|
+
/** GLSL source. Omit to skip this injection for GLSL assembly. */
|
|
27
|
+
glsl?: string;
|
|
28
|
+
/** WGSL source. Omit to skip this injection for WGSL assembly. */
|
|
29
|
+
wgsl?: string;
|
|
30
|
+
/** Relative order at the target; defaults to zero. */
|
|
31
|
+
order?: number;
|
|
32
|
+
};
|
|
33
|
+
|
|
34
|
+
/** Language-selected source and ordering used internally during shader assembly. */
|
|
35
|
+
export type ResolvedShaderInjection = {
|
|
24
36
|
/** Plugin responsible for this injection, when supplied by a ShaderPlugin. */
|
|
25
37
|
pluginName?: string;
|
|
26
38
|
injection: string;
|
|
27
39
|
order: number;
|
|
28
40
|
};
|
|
29
41
|
|
|
42
|
+
/** Select language-specific injections while retaining repeated targets and declaration order. */
|
|
43
|
+
export function resolveShaderInjections(
|
|
44
|
+
declarations: readonly ShaderInjection[],
|
|
45
|
+
language: 'glsl' | 'wgsl',
|
|
46
|
+
pluginName?: string
|
|
47
|
+
): Record<string, ResolvedShaderInjection[]> {
|
|
48
|
+
const resolved: Record<string, ResolvedShaderInjection[]> = {};
|
|
49
|
+
for (const declaration of declarations) {
|
|
50
|
+
const source = declaration[language];
|
|
51
|
+
if (source === undefined) continue;
|
|
52
|
+
if (
|
|
53
|
+
(declaration.stage !== 'vertex' && declaration.stage !== 'fragment') ||
|
|
54
|
+
declaration.target.includes(':')
|
|
55
|
+
) {
|
|
56
|
+
throw new Error('Shader injection targets require an explicit stage and unqualified target');
|
|
57
|
+
}
|
|
58
|
+
const target = `${declaration.stage === 'vertex' ? 'vs' : 'fs'}:${declaration.target}`;
|
|
59
|
+
if (!/^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][A-Za-z0-9_]*)$/.test(target)) {
|
|
60
|
+
throw new Error(`Shader injection target "${target}" must be a named shader anchor or hook`);
|
|
61
|
+
}
|
|
62
|
+
resolved[target] ||= [];
|
|
63
|
+
resolved[target].push({
|
|
64
|
+
injection: source,
|
|
65
|
+
order: declaration.order ?? 0,
|
|
66
|
+
...(pluginName ? {pluginName} : {})
|
|
67
|
+
});
|
|
68
|
+
}
|
|
69
|
+
return resolved;
|
|
70
|
+
}
|
|
71
|
+
|
|
30
72
|
/**
|
|
31
73
|
* ShaderInjections, parsed and split per shader
|
|
32
74
|
*/
|
|
33
75
|
export type ShaderInjections = {
|
|
34
|
-
vertex: Record<string,
|
|
35
|
-
fragment: Record<string,
|
|
76
|
+
vertex: Record<string, ResolvedShaderInjection>;
|
|
77
|
+
fragment: Record<string, ResolvedShaderInjection>;
|
|
36
78
|
};
|
|
37
79
|
|
|
38
80
|
/**
|
|
39
81
|
*
|
|
40
82
|
*/
|
|
41
83
|
export function normalizeInjections(
|
|
42
|
-
injections: Record<string, string |
|
|
84
|
+
injections: Record<string, string | ResolvedShaderInjection>
|
|
43
85
|
): ShaderInjections {
|
|
44
86
|
const result: ShaderInjections = {vertex: {}, fragment: {}};
|
|
45
87
|
|
|
@@ -84,7 +126,7 @@ function getHookStage(hook: string): 'vertex' | 'fragment' {
|
|
|
84
126
|
export function injectShader(
|
|
85
127
|
source: string,
|
|
86
128
|
stage: 'vertex' | 'fragment',
|
|
87
|
-
inject: Record<string,
|
|
129
|
+
inject: Record<string, ResolvedShaderInjection[]>,
|
|
88
130
|
injectStandardStubs = false,
|
|
89
131
|
language: 'glsl' | 'wgsl' = 'glsl',
|
|
90
132
|
entryPoints: {vertex?: string; fragment?: string} = {}
|
|
@@ -93,7 +135,9 @@ export function injectShader(
|
|
|
93
135
|
|
|
94
136
|
for (const key in inject) {
|
|
95
137
|
const fragmentData = inject[key];
|
|
96
|
-
fragmentData.sort(
|
|
138
|
+
fragmentData.sort(
|
|
139
|
+
(a: ResolvedShaderInjection, b: ResolvedShaderInjection): number => a.order - b.order
|
|
140
|
+
);
|
|
97
141
|
fragments.length = fragmentData.length;
|
|
98
142
|
for (let i = 0, len = fragmentData.length; i < len; ++i) {
|
|
99
143
|
fragments[i] = fragmentData[i].injection;
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
getValidatedProperties
|
|
11
11
|
} from '../filters/prop-types';
|
|
12
12
|
import type {ShaderModuleUniformValue, UniformTypes, UniformValue} from '../utils/uniform-types';
|
|
13
|
-
import {
|
|
13
|
+
import type {ResolvedShaderInjection} from '../shader-assembly/shader-injections';
|
|
14
14
|
|
|
15
15
|
import type {ShaderBinding as Binding} from '../../types';
|
|
16
16
|
|
|
@@ -83,8 +83,6 @@ export type ShaderModule<
|
|
|
83
83
|
|
|
84
84
|
/** Boolean or numeric preprocessor defines contributed by this shader module. */
|
|
85
85
|
defines?: Record<string, boolean | number>;
|
|
86
|
-
/** Injections */
|
|
87
|
-
inject?: Record<string, string | {injection: string; order: number}>;
|
|
88
86
|
dependencies?: ShaderModule<any, any>[];
|
|
89
87
|
/** Information on deprecated properties */
|
|
90
88
|
deprecations?: ShaderModuleDeprecation[];
|
|
@@ -93,10 +91,10 @@ export type ShaderModule<
|
|
|
93
91
|
instance?: {
|
|
94
92
|
propValidators?: Record<string, PropValidator>;
|
|
95
93
|
parsedDeprecations: ShaderModuleDeprecation[];
|
|
96
|
-
|
|
94
|
+
/** Empty compatibility value for consumers using older shadertools assembly. */
|
|
97
95
|
normalizedInjections: {
|
|
98
|
-
vertex: Record<string,
|
|
99
|
-
fragment: Record<string,
|
|
96
|
+
vertex: Record<string, ResolvedShaderInjection>;
|
|
97
|
+
fragment: Record<string, ResolvedShaderInjection>;
|
|
100
98
|
};
|
|
101
99
|
};
|
|
102
100
|
};
|
|
@@ -123,16 +121,11 @@ export function initializeShaderModule(module: ShaderModule): void {
|
|
|
123
121
|
|
|
124
122
|
initializeShaderModules(module.dependencies || []);
|
|
125
123
|
|
|
126
|
-
const {
|
|
127
|
-
propTypes = {},
|
|
128
|
-
deprecations = [],
|
|
129
|
-
// defines = {},
|
|
130
|
-
inject = {}
|
|
131
|
-
} = module;
|
|
124
|
+
const {propTypes = {}, deprecations = []} = module;
|
|
132
125
|
|
|
133
126
|
const instance: Required<ShaderModule>['instance'] = {
|
|
134
|
-
|
|
135
|
-
|
|
127
|
+
parsedDeprecations: parseDeprecationDefinitions(deprecations),
|
|
128
|
+
normalizedInjections: {vertex: {}, fragment: {}}
|
|
136
129
|
};
|
|
137
130
|
|
|
138
131
|
if (propTypes) {
|
package/src/lib/shader-plugin.ts
CHANGED
|
@@ -3,47 +3,14 @@
|
|
|
3
3
|
// SPDX-FileCopyrightText: Copyright (c) vis.gl contributors
|
|
4
4
|
|
|
5
5
|
import type {ShaderHook} from './shader-assembly/shader-hooks';
|
|
6
|
-
import
|
|
6
|
+
import {
|
|
7
|
+
resolveShaderInjections,
|
|
8
|
+
type ShaderInjection,
|
|
9
|
+
type ResolvedShaderInjection
|
|
10
|
+
} from './shader-assembly/shader-injections';
|
|
7
11
|
import type {ShaderModule} from './shader-module/shader-module';
|
|
8
12
|
import {shaderTypeDecoder, type AttributeShaderType} from '../types';
|
|
9
13
|
|
|
10
|
-
/** Named shader anchor or hook target accepted by ShaderPlugin injections. */
|
|
11
|
-
export type ShaderPluginInjectionTarget =
|
|
12
|
-
| 'vs:#decl'
|
|
13
|
-
| 'vs:#main-start'
|
|
14
|
-
| 'vs:#main-end'
|
|
15
|
-
| 'fs:#decl'
|
|
16
|
-
| 'fs:#main-start'
|
|
17
|
-
| 'fs:#main-end'
|
|
18
|
-
| `vs:${string}`
|
|
19
|
-
| `fs:${string}`;
|
|
20
|
-
|
|
21
|
-
/** Explicit stage and unqualified target for plugin object declarations. */
|
|
22
|
-
export type ShaderPluginTarget = {
|
|
23
|
-
/** Shader stage containing this extension point. */
|
|
24
|
-
stage: 'vertex' | 'fragment';
|
|
25
|
-
/** Unqualified hook name or structural anchor such as #main-start. */
|
|
26
|
-
target: string;
|
|
27
|
-
};
|
|
28
|
-
|
|
29
|
-
/** Hook function declared by a plugin, with parameter signatures per language. */
|
|
30
|
-
export type ShaderPluginHook = ShaderPluginTarget & {
|
|
31
|
-
/** GLSL parameter list, including parentheses. */
|
|
32
|
-
glsl?: string;
|
|
33
|
-
/** WGSL parameter list, including parentheses. */
|
|
34
|
-
wgsl?: string;
|
|
35
|
-
};
|
|
36
|
-
|
|
37
|
-
/** Named shader source injection contributed by a ShaderPlugin. */
|
|
38
|
-
export type ShaderPluginInjection = ShaderPluginTarget & {
|
|
39
|
-
/** GLSL source for this target. Omit to skip this injection when assembling GLSL. */
|
|
40
|
-
glsl?: string;
|
|
41
|
-
/** WGSL source for this target. Omit to skip this injection when assembling WGSL. */
|
|
42
|
-
wgsl?: string;
|
|
43
|
-
/** Relative injection order within the target. */
|
|
44
|
-
order?: number;
|
|
45
|
-
};
|
|
46
|
-
|
|
47
14
|
/** Portable interpolation modes for ShaderPlugin inter-stage variables. */
|
|
48
15
|
export type ShaderPluginInterStageVariableInterpolation = 'smooth' | 'flat';
|
|
49
16
|
|
|
@@ -70,9 +37,9 @@ export type ShaderPlugin = {
|
|
|
70
37
|
/** Boolean or numeric preprocessor defines added to shader assembly. */
|
|
71
38
|
defines?: Record<string, boolean | number>;
|
|
72
39
|
/** Named injections as explicit-stage objects or optionally language-qualified string keys. */
|
|
73
|
-
injections?:
|
|
74
|
-
/** Callable extension points
|
|
75
|
-
hooks?:
|
|
40
|
+
injections?: ShaderInjection[] | Record<string, string>;
|
|
41
|
+
/** Callable extension points with a shared name and language-specific source. */
|
|
42
|
+
hooks?: ShaderHook[];
|
|
76
43
|
/** Shader-facing vertex inputs added by this plugin. Buffer ownership remains with the caller. */
|
|
77
44
|
vertexInputs?: Record<string, AttributeShaderType>;
|
|
78
45
|
/** Values transported from the selected vertex entry point to the fragment entry point. */
|
|
@@ -88,16 +55,13 @@ export type ResolvedShaderPlugins = {
|
|
|
88
55
|
/** Resolved boolean or numeric preprocessor defines. */
|
|
89
56
|
defines: Record<string, boolean | number>;
|
|
90
57
|
/** Resolved named code injections grouped by target. */
|
|
91
|
-
injections: Record<string,
|
|
58
|
+
injections: Record<string, ResolvedShaderInjection[]>;
|
|
92
59
|
/** Resolved shader-facing vertex inputs in plugin declaration order. */
|
|
93
60
|
vertexInputs: Record<string, AttributeShaderType>;
|
|
94
61
|
/** Resolved inter-stage variables in plugin declaration order. */
|
|
95
62
|
interStageVariables: Record<string, ResolvedShaderPluginInterStageVariable>;
|
|
96
63
|
};
|
|
97
64
|
|
|
98
|
-
const SHADER_PLUGIN_INJECTION_TARGET_REGEX =
|
|
99
|
-
/^(vs|fs):(?:#(?:decl|main-start|main-end)|[A-Za-z_][\w-]*)$/;
|
|
100
|
-
|
|
101
65
|
/** Resolve plugin interfaces and select injection source for one shader language. */
|
|
102
66
|
export function resolveShaderPlugins(
|
|
103
67
|
plugins: readonly ShaderPlugin[] = [],
|
|
@@ -198,29 +162,31 @@ function appendShaderPlugin(
|
|
|
198
162
|
}
|
|
199
163
|
|
|
200
164
|
for (const hook of plugin.hooks || []) {
|
|
201
|
-
const
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
typeof hook === 'string'
|
|
206
|
-
? declarationSource!.slice(declarationSource!.indexOf('('))
|
|
207
|
-
: hook[shaderLanguage];
|
|
208
|
-
if (signature === undefined) continue;
|
|
209
|
-
const target =
|
|
210
|
-
typeof hook === 'string' ? declarationSource!.split('(')[0] : getShaderPluginTarget(hook);
|
|
211
|
-
if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target) || !/^\([^]*\)$/.test(signature.trim())) {
|
|
165
|
+
const implementation = hook[shaderLanguage];
|
|
166
|
+
if (!implementation) continue;
|
|
167
|
+
const target = `${hook.stage === 'vertex' ? 'vs' : 'fs'}:${hook.name}`;
|
|
168
|
+
if (!/^(vs|fs):[A-Za-z_][A-Za-z0-9_]*$/.test(target)) {
|
|
212
169
|
throw new Error(`Invalid ShaderPlugin hook ${target}`);
|
|
213
170
|
}
|
|
214
|
-
const
|
|
215
|
-
|
|
216
|
-
|
|
171
|
+
const existing = resolved.hooks.find(
|
|
172
|
+
value => value.stage === hook.stage && value.name === hook.name
|
|
173
|
+
);
|
|
174
|
+
const previous = existing?.[shaderLanguage];
|
|
175
|
+
if (
|
|
176
|
+
previous &&
|
|
177
|
+
(previous.parameters !== implementation.parameters ||
|
|
178
|
+
(previous.returnType || 'void') !== (implementation.returnType || 'void') ||
|
|
179
|
+
previous.header !== implementation.header ||
|
|
180
|
+
previous.footer !== implementation.footer)
|
|
181
|
+
) {
|
|
217
182
|
throw new Error(`Conflicting ShaderPlugin hook ${target}`);
|
|
218
183
|
}
|
|
219
|
-
if (!existing)
|
|
184
|
+
if (!existing)
|
|
185
|
+
resolved.hooks.push({name: hook.name, stage: hook.stage, [shaderLanguage]: implementation});
|
|
220
186
|
}
|
|
221
187
|
const pluginInjections = Array.isArray(plugin.injections)
|
|
222
188
|
? plugin.injections
|
|
223
|
-
: Object.entries(plugin.injections || {}).flatMap(([key, source]):
|
|
189
|
+
: Object.entries(plugin.injections || {}).flatMap(([key, source]): ShaderInjection[] => {
|
|
224
190
|
const target = selectShaderLanguage(key, shaderLanguage);
|
|
225
191
|
if (target === undefined) return [];
|
|
226
192
|
const [stage, ...name] = target.split(':');
|
|
@@ -234,20 +200,10 @@ function appendShaderPlugin(
|
|
|
234
200
|
}
|
|
235
201
|
];
|
|
236
202
|
});
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
assertNamedShaderPluginInjectionTarget(target);
|
|
242
|
-
if (!resolved.injections[target]) {
|
|
243
|
-
resolved.injections[target] = [];
|
|
244
|
-
}
|
|
245
|
-
const injections = resolved.injections[target];
|
|
246
|
-
injections.push({
|
|
247
|
-
pluginName: plugin.name,
|
|
248
|
-
injection: source,
|
|
249
|
-
order: injection.order ?? 0
|
|
250
|
-
});
|
|
203
|
+
const selectedInjections = resolveShaderInjections(pluginInjections, shaderLanguage, plugin.name);
|
|
204
|
+
for (const [target, injections] of Object.entries(selectedInjections)) {
|
|
205
|
+
resolved.injections[target] ||= [];
|
|
206
|
+
resolved.injections[target].push(...injections);
|
|
251
207
|
}
|
|
252
208
|
}
|
|
253
209
|
|
|
@@ -270,21 +226,6 @@ function normalizeShaderPluginInterStageVariable(
|
|
|
270
226
|
return {type: varying.type, interpolation};
|
|
271
227
|
}
|
|
272
228
|
|
|
273
|
-
function assertNamedShaderPluginInjectionTarget(target: ShaderPluginInjectionTarget): void {
|
|
274
|
-
if (!SHADER_PLUGIN_INJECTION_TARGET_REGEX.test(target)) {
|
|
275
|
-
throw new Error(
|
|
276
|
-
`ShaderPlugin injection target "${target}" must be a named shader anchor or hook`
|
|
277
|
-
);
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
|
|
281
|
-
function getShaderPluginTarget(value: ShaderPluginTarget): ShaderPluginInjectionTarget {
|
|
282
|
-
if ((value.stage !== 'vertex' && value.stage !== 'fragment') || value.target.includes(':')) {
|
|
283
|
-
throw new Error('ShaderPlugin object targets require an explicit stage and unqualified target');
|
|
284
|
-
}
|
|
285
|
-
return `${value.stage === 'vertex' ? 'vs' : 'fs'}:${value.target}` as ShaderPluginInjectionTarget;
|
|
286
|
-
}
|
|
287
|
-
|
|
288
229
|
function selectShaderLanguage(value: string, language: 'glsl' | 'wgsl'): string | undefined {
|
|
289
230
|
const match = /^(glsl|wgsl):([\s\S]+)$/.exec(value);
|
|
290
231
|
return match ? (match[1] === language ? match[2] : undefined) : value;
|