@zephyr3d/device 0.1.0
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/base_types.js +602 -0
- package/dist/base_types.js.map +1 -0
- package/dist/builder/ast.js +2135 -0
- package/dist/builder/ast.js.map +1 -0
- package/dist/builder/base.js +477 -0
- package/dist/builder/base.js.map +1 -0
- package/dist/builder/builtinfunc.js +4101 -0
- package/dist/builder/builtinfunc.js.map +1 -0
- package/dist/builder/constructors.js +173 -0
- package/dist/builder/constructors.js.map +1 -0
- package/dist/builder/errors.js +148 -0
- package/dist/builder/errors.js.map +1 -0
- package/dist/builder/programbuilder.js +2323 -0
- package/dist/builder/programbuilder.js.map +1 -0
- package/dist/builder/reflection.js +60 -0
- package/dist/builder/reflection.js.map +1 -0
- package/dist/builder/types.js +1563 -0
- package/dist/builder/types.js.map +1 -0
- package/dist/device.js +515 -0
- package/dist/device.js.map +1 -0
- package/dist/gpuobject.js +2047 -0
- package/dist/gpuobject.js.map +1 -0
- package/dist/helpers/drawtext.js +187 -0
- package/dist/helpers/drawtext.js.map +1 -0
- package/dist/helpers/font.js +189 -0
- package/dist/helpers/font.js.map +1 -0
- package/dist/helpers/glyphmanager.js +121 -0
- package/dist/helpers/glyphmanager.js.map +1 -0
- package/dist/helpers/textureatlas.js +170 -0
- package/dist/helpers/textureatlas.js.map +1 -0
- package/dist/index.d.ts +3873 -0
- package/dist/index.js +16 -0
- package/dist/index.js.map +1 -0
- package/dist/timer.js +38 -0
- package/dist/timer.js.map +1 -0
- package/dist/uniformdata.js +147 -0
- package/dist/uniformdata.js.map +1 -0
- package/dist/vertexdata.js +135 -0
- package/dist/vertexdata.js.map +1 -0
- package/package.json +69 -0
|
@@ -0,0 +1,2323 @@
|
|
|
1
|
+
import { ShaderType } from '../base_types.js';
|
|
2
|
+
import { MAX_BINDING_GROUPS, getVertexAttribByName } from '../gpuobject.js';
|
|
3
|
+
import { PBReflection } from './reflection.js';
|
|
4
|
+
import { setCurrentProgramBuilder, PBShaderExp, getCurrentProgramBuilder, makeConstructor, Proxiable } from './base.js';
|
|
5
|
+
import { ASTAddressOf, ASTReferenceOf, ASTStructDefine, ASTShaderExpConstructor, getBuiltinInputStructName, getBuiltinOutputStructName, getBuiltinInputStructInstanceName, getBuiltinOutputStructInstanceName, builtinVariables, ASTDeclareVar, ASTScalar, ASTDiscard, DeclareType, ASTPrimitive, ASTAssignment, ASTLValueScalar, genSamplerName, ASTCallFunction, ASTTouch, ASTLValueDeclare, ASTGlobalScope, ASTFunctionParameter, ASTFunction, ASTScope, ASTReturn, ASTNakedScope, ASTIf, ASTSelect, ASTBreak, ASTContinue, ASTRange, ASTDoWhile, ASTWhile } from './ast.js';
|
|
6
|
+
import { PBDeviceNotSupport, PBReferenceValueRequired, PBPointerValueRequired, PBParamValueError, PBInternalError, PBParamLengthError, PBParamTypeError, PBTypeCastError, PBValueOutOfRange, PBError, PBNonScopedFunctionCall, PBASTError } from './errors.js';
|
|
7
|
+
import { setBuiltinFuncs } from './builtinfunc.js';
|
|
8
|
+
import { setConstructors } from './constructors.js';
|
|
9
|
+
import { PBStructTypeInfo, typeFrexpResult, typeFrexpResultVec2, typeFrexpResultVec3, typeFrexpResultVec4, PBPrimitiveType, typeBool, typeI32, typeU32, typeF32, PBArrayTypeInfo, typeTex2DArray, typeTex2D, typeTexCube, PBSamplerAccessMode, PBPointerTypeInfo, typeVoid, PBAddressSpace, PBFunctionTypeInfo } from './types.js';
|
|
10
|
+
|
|
11
|
+
const COMPUTE_UNIFORM_NAME = 'ch_compute_uniform_block';
|
|
12
|
+
const COMPUTE_STORAGE_NAME = 'ch_compute_storage_block';
|
|
13
|
+
const VERTEX_UNIFORM_NAME = 'ch_vertex_uniform_block';
|
|
14
|
+
const FRAGMENT_UNIFORM_NAME = 'ch_fragment_uniform_block';
|
|
15
|
+
const SHARED_UNIFORM_NAME = 'ch_shared_uniform_block';
|
|
16
|
+
const VERTEX_STORAGE_NAME = 'ch_vertex_storage_block';
|
|
17
|
+
const FRAGMENT_STORAGE_NAME = 'ch_fragment_storage_block';
|
|
18
|
+
const SHARED_STORAGE_NAME = 'ch_shared_storage_block';
|
|
19
|
+
const input_prefix = 'uu_in_';
|
|
20
|
+
const output_prefix_vs = 'uu_vsout_';
|
|
21
|
+
const output_prefix_fs = 'uu_fsout_';
|
|
22
|
+
/**
|
|
23
|
+
* The program builder class
|
|
24
|
+
* @public
|
|
25
|
+
*/ class ProgramBuilder {
|
|
26
|
+
/** @internal */ _device;
|
|
27
|
+
/** @internal */ _workgroupSize;
|
|
28
|
+
/** @internal */ _scopeStack = [];
|
|
29
|
+
/** @internal */ _shaderType = ShaderType.Vertex | ShaderType.Fragment | ShaderType.Compute;
|
|
30
|
+
/** @internal */ _structInfo;
|
|
31
|
+
/** @internal */ _uniforms;
|
|
32
|
+
/** @internal */ _globalScope;
|
|
33
|
+
/** @internal */ _builtinScope;
|
|
34
|
+
/** @internal */ _inputScope;
|
|
35
|
+
/** @internal */ _outputScope;
|
|
36
|
+
/** @internal */ _inputs;
|
|
37
|
+
/** @internal */ _outputs;
|
|
38
|
+
/** @internal */ _vertexAttributes;
|
|
39
|
+
/** @internal */ _depthRangeCorrection;
|
|
40
|
+
/** @internal */ _emulateDepthClamp;
|
|
41
|
+
/** @internal */ _lastError;
|
|
42
|
+
/** @internal */ _reflection;
|
|
43
|
+
/** @internal */ _autoStructureTypeIndex;
|
|
44
|
+
/** @internal */ _nameMap;
|
|
45
|
+
/**
|
|
46
|
+
* Creates a program builder for given device
|
|
47
|
+
* @param device - The device
|
|
48
|
+
*/ constructor(device){
|
|
49
|
+
this._device = device;
|
|
50
|
+
this._workgroupSize = null;
|
|
51
|
+
this._structInfo = {};
|
|
52
|
+
this._uniforms = [];
|
|
53
|
+
this._scopeStack = [];
|
|
54
|
+
this._globalScope = null;
|
|
55
|
+
this._builtinScope = null;
|
|
56
|
+
this._inputScope = null;
|
|
57
|
+
this._outputScope = null;
|
|
58
|
+
this._inputs = [];
|
|
59
|
+
this._outputs = [];
|
|
60
|
+
this._vertexAttributes = [];
|
|
61
|
+
this._depthRangeCorrection = device.type === 'webgpu';
|
|
62
|
+
this._emulateDepthClamp = false;
|
|
63
|
+
this._lastError = null;
|
|
64
|
+
this._reflection = new PBReflection(this);
|
|
65
|
+
this._autoStructureTypeIndex = 0;
|
|
66
|
+
this._nameMap = [];
|
|
67
|
+
}
|
|
68
|
+
/** Get last error */ get lastError() {
|
|
69
|
+
return this._lastError;
|
|
70
|
+
}
|
|
71
|
+
/** @internal */ get shaderType() {
|
|
72
|
+
return this._shaderType;
|
|
73
|
+
}
|
|
74
|
+
/** Current shader kind */ get shaderKind() {
|
|
75
|
+
return this._shaderType === ShaderType.Vertex ? 'vertex' : this._shaderType === ShaderType.Fragment ? 'fragment' : this._shaderType === ShaderType.Compute ? 'compute' : null;
|
|
76
|
+
}
|
|
77
|
+
/** Gets the global scope */ getGlobalScope() {
|
|
78
|
+
return this._globalScope;
|
|
79
|
+
}
|
|
80
|
+
/** @internal */ get builtinScope() {
|
|
81
|
+
return this._builtinScope;
|
|
82
|
+
}
|
|
83
|
+
/** @internal */ get inputScope() {
|
|
84
|
+
return this._inputScope;
|
|
85
|
+
}
|
|
86
|
+
/** @internal */ get outputScope() {
|
|
87
|
+
return this._outputScope;
|
|
88
|
+
}
|
|
89
|
+
/** @internal */ get depthRangeCorrection() {
|
|
90
|
+
return this._depthRangeCorrection;
|
|
91
|
+
}
|
|
92
|
+
get emulateDepthClamp() {
|
|
93
|
+
return this._emulateDepthClamp;
|
|
94
|
+
}
|
|
95
|
+
set emulateDepthClamp(val) {
|
|
96
|
+
this._emulateDepthClamp = val;
|
|
97
|
+
}
|
|
98
|
+
/** Get the shader code reflection interface */ getReflection() {
|
|
99
|
+
return this._reflection;
|
|
100
|
+
}
|
|
101
|
+
/** Get the device */ getDevice() {
|
|
102
|
+
return this._device;
|
|
103
|
+
}
|
|
104
|
+
/** @internal */ reset() {
|
|
105
|
+
this._workgroupSize = null;
|
|
106
|
+
this._structInfo = {};
|
|
107
|
+
this._uniforms = [];
|
|
108
|
+
this._scopeStack = [];
|
|
109
|
+
this._globalScope = null;
|
|
110
|
+
this._builtinScope = null;
|
|
111
|
+
this._inputScope = null;
|
|
112
|
+
this._outputScope = null;
|
|
113
|
+
this._inputs = [];
|
|
114
|
+
this._outputs = [];
|
|
115
|
+
this._vertexAttributes = [];
|
|
116
|
+
this._depthRangeCorrection = this._device.type === 'webgpu';
|
|
117
|
+
this._reflection = new PBReflection(this);
|
|
118
|
+
this._autoStructureTypeIndex = 0;
|
|
119
|
+
this._nameMap = [];
|
|
120
|
+
}
|
|
121
|
+
/**
|
|
122
|
+
* Query the global variable by the name
|
|
123
|
+
* @param name - Name of the variable
|
|
124
|
+
* @returns The variable or null if not exists
|
|
125
|
+
*/ queryGlobal(name) {
|
|
126
|
+
return this.getReflection().tag(name);
|
|
127
|
+
}
|
|
128
|
+
/** @internal */ pushScope(scope) {
|
|
129
|
+
this._scopeStack.unshift(scope);
|
|
130
|
+
}
|
|
131
|
+
/** @internal */ popScope() {
|
|
132
|
+
return this._scopeStack.shift();
|
|
133
|
+
}
|
|
134
|
+
/** Gets the current scope */ getCurrentScope() {
|
|
135
|
+
return this._scopeStack[0];
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Generates shader codes for a render program
|
|
139
|
+
* @param options - The build options
|
|
140
|
+
* @returns a tuple made by vertex shader source, fragment shader source, bind group layouts and vertex attributes used, or null if build faild
|
|
141
|
+
*/ buildRender(options) {
|
|
142
|
+
setCurrentProgramBuilder(this);
|
|
143
|
+
this._lastError = null;
|
|
144
|
+
this.defineInternalStructs();
|
|
145
|
+
const ret = this.buildRenderSource(options);
|
|
146
|
+
setCurrentProgramBuilder(null);
|
|
147
|
+
this.reset();
|
|
148
|
+
return ret;
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* Generates shader code for a compute program
|
|
152
|
+
* @param options - The build programs
|
|
153
|
+
* @returns a tuple made by compute shader source and bind group layouts, or null if build failed
|
|
154
|
+
*/ buildCompute(options) {
|
|
155
|
+
setCurrentProgramBuilder(this);
|
|
156
|
+
this._lastError = null;
|
|
157
|
+
this._workgroupSize = options.workgroupSize;
|
|
158
|
+
this.defineInternalStructs();
|
|
159
|
+
const ret = this.buildComputeSource(options);
|
|
160
|
+
setCurrentProgramBuilder(null);
|
|
161
|
+
this.reset();
|
|
162
|
+
return ret;
|
|
163
|
+
}
|
|
164
|
+
/**
|
|
165
|
+
* Creates a shader program for render
|
|
166
|
+
* @param options - The build options
|
|
167
|
+
* @returns The created program or null if build failed
|
|
168
|
+
*/ buildRenderProgram(options) {
|
|
169
|
+
const ret = this.buildRender(options);
|
|
170
|
+
return ret ? this._device.createGPUProgram({
|
|
171
|
+
type: 'render',
|
|
172
|
+
label: options.label,
|
|
173
|
+
params: {
|
|
174
|
+
vs: ret[0],
|
|
175
|
+
fs: ret[1],
|
|
176
|
+
bindGroupLayouts: ret[2],
|
|
177
|
+
vertexAttributes: ret[3]
|
|
178
|
+
}
|
|
179
|
+
}) : null;
|
|
180
|
+
}
|
|
181
|
+
/**
|
|
182
|
+
* Creates a shader program for compute
|
|
183
|
+
* @param options - The build options
|
|
184
|
+
* @returns The created program or null if build failed
|
|
185
|
+
*/ buildComputeProgram(options) {
|
|
186
|
+
const ret = this.buildCompute(options);
|
|
187
|
+
return ret ? this._device.createGPUProgram({
|
|
188
|
+
type: 'compute',
|
|
189
|
+
params: {
|
|
190
|
+
source: ret[0],
|
|
191
|
+
bindGroupLayouts: ret[1]
|
|
192
|
+
}
|
|
193
|
+
}) : null;
|
|
194
|
+
}
|
|
195
|
+
/**
|
|
196
|
+
* Creates a function
|
|
197
|
+
* @param name - Name of the function
|
|
198
|
+
* @param params - Parameters of the function
|
|
199
|
+
* @param body - The generator function
|
|
200
|
+
*/ func(name, params, body) {
|
|
201
|
+
this.getGlobalScope().$createFunctionIfNotExists(name, params, body);
|
|
202
|
+
}
|
|
203
|
+
/**
|
|
204
|
+
* Create the main entry function of the shader
|
|
205
|
+
* @param body - The shader generator function
|
|
206
|
+
*/ main(body) {
|
|
207
|
+
this.getGlobalScope().$mainFunc(body);
|
|
208
|
+
}
|
|
209
|
+
/**
|
|
210
|
+
* Create an 'AddressOf' expression for WGSL
|
|
211
|
+
* @param ref - The reference variable
|
|
212
|
+
* @returns the 'AddressOf' expression
|
|
213
|
+
*/ addressOf(ref) {
|
|
214
|
+
if (this._device.type !== 'webgpu') {
|
|
215
|
+
throw new PBDeviceNotSupport('pointer shader type');
|
|
216
|
+
}
|
|
217
|
+
if (!ref.$ast.isReference()) {
|
|
218
|
+
throw new PBReferenceValueRequired(ref);
|
|
219
|
+
}
|
|
220
|
+
const exp = new PBShaderExp('', ref.$ast.getType());
|
|
221
|
+
exp.$ast = new ASTAddressOf(ref.$ast);
|
|
222
|
+
return exp;
|
|
223
|
+
}
|
|
224
|
+
/**
|
|
225
|
+
* Creates a 'referenceOf' expression for WGSL
|
|
226
|
+
* @param ptr - The pointer variable
|
|
227
|
+
* @returns the 'referenceOf' expression
|
|
228
|
+
*/ referenceOf(ptr) {
|
|
229
|
+
if (this._device.type !== 'webgpu') {
|
|
230
|
+
throw new PBDeviceNotSupport('pointer shader type');
|
|
231
|
+
}
|
|
232
|
+
if (!ptr.$ast.getType().isPointerType()) {
|
|
233
|
+
throw new PBPointerValueRequired(ptr);
|
|
234
|
+
}
|
|
235
|
+
const ast = new ASTReferenceOf(ptr.$ast);
|
|
236
|
+
const exp = new PBShaderExp('', ast.getType());
|
|
237
|
+
exp.$ast = ast;
|
|
238
|
+
return exp;
|
|
239
|
+
}
|
|
240
|
+
/**
|
|
241
|
+
* Creates a structure type variable
|
|
242
|
+
* @param structName - Name of the structure type
|
|
243
|
+
* @param instanceName - Name of the variable
|
|
244
|
+
* @returns the created variable
|
|
245
|
+
*/ struct(structName, instanceName) {
|
|
246
|
+
let ctor = null;
|
|
247
|
+
for (const st of [
|
|
248
|
+
ShaderType.Vertex,
|
|
249
|
+
ShaderType.Fragment,
|
|
250
|
+
ShaderType.Compute
|
|
251
|
+
]){
|
|
252
|
+
if (st & this._shaderType) {
|
|
253
|
+
const structInfo = this._structInfo[st];
|
|
254
|
+
ctor = structInfo?.structs[structName];
|
|
255
|
+
if (ctor) {
|
|
256
|
+
break;
|
|
257
|
+
}
|
|
258
|
+
}
|
|
259
|
+
}
|
|
260
|
+
if (!ctor) {
|
|
261
|
+
throw new PBParamValueError('struct', 'structName', `Struct type ${structName} not exists`);
|
|
262
|
+
}
|
|
263
|
+
return ctor.call(this, instanceName);
|
|
264
|
+
}
|
|
265
|
+
/** @internal */ isIdenticalStruct(a, b, checkName) {
|
|
266
|
+
if (checkName && a.structName && b.structName && a.structName !== b.structName) {
|
|
267
|
+
return false;
|
|
268
|
+
}
|
|
269
|
+
if (a.structMembers.length !== b.structMembers.length) {
|
|
270
|
+
return false;
|
|
271
|
+
}
|
|
272
|
+
for(let index = 0; index < a.structMembers.length; index++){
|
|
273
|
+
const val = a.structMembers[index];
|
|
274
|
+
const other = b.structMembers[index];
|
|
275
|
+
if (val.name !== other.name) {
|
|
276
|
+
return false;
|
|
277
|
+
}
|
|
278
|
+
if (val.type.isStructType()) {
|
|
279
|
+
if (!other.type.isStructType()) {
|
|
280
|
+
return false;
|
|
281
|
+
}
|
|
282
|
+
if (!this.isIdenticalStruct(val.type, other.type, true)) {
|
|
283
|
+
return false;
|
|
284
|
+
}
|
|
285
|
+
} else if (!val.type.isCompatibleType(other.type)) {
|
|
286
|
+
return false;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
return true;
|
|
290
|
+
}
|
|
291
|
+
/** @internal */ generateStructureName() {
|
|
292
|
+
return `uu_GeneratedStruct${this._autoStructureTypeIndex++}`;
|
|
293
|
+
}
|
|
294
|
+
/** @internal */ getVertexAttributes() {
|
|
295
|
+
return this._vertexAttributes;
|
|
296
|
+
}
|
|
297
|
+
/** @internal */ defineHiddenStruct(type) {
|
|
298
|
+
for (const shaderType of [
|
|
299
|
+
ShaderType.Vertex,
|
|
300
|
+
ShaderType.Fragment,
|
|
301
|
+
ShaderType.Compute
|
|
302
|
+
]){
|
|
303
|
+
let structInfo = this._structInfo[shaderType];
|
|
304
|
+
if (!structInfo) {
|
|
305
|
+
structInfo = {
|
|
306
|
+
structs: {},
|
|
307
|
+
types: []
|
|
308
|
+
};
|
|
309
|
+
this._structInfo[shaderType] = structInfo;
|
|
310
|
+
}
|
|
311
|
+
if (structInfo.structs[type.structName]) {
|
|
312
|
+
throw new PBParamValueError('defineStruct', 'structName', `cannot re-define struct '${type.structName}'`);
|
|
313
|
+
}
|
|
314
|
+
structInfo.types.push(new ASTStructDefine(type, true));
|
|
315
|
+
}
|
|
316
|
+
}
|
|
317
|
+
// /**
|
|
318
|
+
// * Defines an uniform buffer
|
|
319
|
+
// * @param name - Name of the uniform buffer
|
|
320
|
+
// * @param args - Members of the buffer structure
|
|
321
|
+
// * @returns The structure type constructor
|
|
322
|
+
// */
|
|
323
|
+
// defineUniformBuffer(name: string, ...args: PBShaderExp[]): ShaderTypeFunc {
|
|
324
|
+
// return this.defineStructOrUniformBuffer(name, 'std140', ...args);
|
|
325
|
+
// }
|
|
326
|
+
// /**
|
|
327
|
+
// * Defines a structure type
|
|
328
|
+
// * @param structName - Name of the type
|
|
329
|
+
// * @param layout - The structure layout
|
|
330
|
+
// * @param args - Members of the structure
|
|
331
|
+
// * @returns The structure type constructor
|
|
332
|
+
// */
|
|
333
|
+
// defineStruct(structName: string, ...args: PBShaderExp[]): ShaderTypeFunc {
|
|
334
|
+
// return this.defineStructOrUniformBuffer(structName, 'default', ...args);
|
|
335
|
+
// }
|
|
336
|
+
/**
|
|
337
|
+
* Defines a structure type
|
|
338
|
+
* @param members - Members of the structure
|
|
339
|
+
* @param structName - Name of the type
|
|
340
|
+
* @returns The structure type constructor
|
|
341
|
+
*/ defineStruct(members, structName) {
|
|
342
|
+
const layout = 'default';
|
|
343
|
+
const structType = new PBStructTypeInfo(structName ?? '', layout, members.map((arg)=>{
|
|
344
|
+
if (!arg.$typeinfo.isPrimitiveType() && !arg.$typeinfo.isArrayType() && !arg.$typeinfo.isStructType() && !arg.$typeinfo.isAtomicI32() && !arg.$typeinfo.isAtomicU32()) {
|
|
345
|
+
throw new Error(`invalid struct member type: '${arg.$str}'`);
|
|
346
|
+
}
|
|
347
|
+
return {
|
|
348
|
+
name: arg.$str,
|
|
349
|
+
type: arg.$typeinfo
|
|
350
|
+
};
|
|
351
|
+
}));
|
|
352
|
+
for (const shaderType of [
|
|
353
|
+
ShaderType.Vertex,
|
|
354
|
+
ShaderType.Fragment,
|
|
355
|
+
ShaderType.Compute
|
|
356
|
+
]){
|
|
357
|
+
let structDef = null;
|
|
358
|
+
let ctor = null;
|
|
359
|
+
const structInfo = this._structInfo[shaderType];
|
|
360
|
+
if (structInfo) {
|
|
361
|
+
if (getCurrentProgramBuilder().shaderType === shaderType && structInfo.structs[structType.structName]) {
|
|
362
|
+
throw new PBParamValueError('defineStruct', 'structName', `cannot re-define struct '${structType.structName}'`);
|
|
363
|
+
}
|
|
364
|
+
for (const type of structInfo.types){
|
|
365
|
+
if (!type.builtin && this.isIdenticalStruct(type.getType(), structType, false)) {
|
|
366
|
+
structDef = type;
|
|
367
|
+
ctor = structInfo.structs[type.getType().structName];
|
|
368
|
+
break;
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
if (structDef) {
|
|
373
|
+
if (structDef.type.layout !== layout) {
|
|
374
|
+
throw new Error(`Can not redefine struct ${structDef.type.structName} with different layout`);
|
|
375
|
+
}
|
|
376
|
+
if (shaderType !== getCurrentProgramBuilder().shaderType) {
|
|
377
|
+
if (!this._structInfo[getCurrentProgramBuilder().shaderType]) {
|
|
378
|
+
this._structInfo[getCurrentProgramBuilder().shaderType] = {
|
|
379
|
+
structs: {},
|
|
380
|
+
types: []
|
|
381
|
+
};
|
|
382
|
+
}
|
|
383
|
+
if (this._structInfo[getCurrentProgramBuilder().shaderType].types.indexOf(structDef) < 0) {
|
|
384
|
+
this._structInfo[getCurrentProgramBuilder().shaderType].types.push(structDef);
|
|
385
|
+
this._structInfo[getCurrentProgramBuilder().shaderType].structs[structDef.getType().structName] = ctor;
|
|
386
|
+
}
|
|
387
|
+
}
|
|
388
|
+
return ctor;
|
|
389
|
+
}
|
|
390
|
+
}
|
|
391
|
+
return this.internalDefineStruct(structName ?? this.generateStructureName(), layout, this._shaderType, false, ...members);
|
|
392
|
+
}
|
|
393
|
+
/**
|
|
394
|
+
* Defines a structure type
|
|
395
|
+
* @param structType - The structure type info
|
|
396
|
+
* @returns The structure type constructor
|
|
397
|
+
*/ defineStructByType(structType) {
|
|
398
|
+
const typeCopy = structType.extends(structType.structName || this.generateStructureName(), []);
|
|
399
|
+
for (const shaderType of [
|
|
400
|
+
ShaderType.Vertex,
|
|
401
|
+
ShaderType.Fragment,
|
|
402
|
+
ShaderType.Compute
|
|
403
|
+
]){
|
|
404
|
+
let structDef = null;
|
|
405
|
+
let ctor = null;
|
|
406
|
+
const structInfo = this._structInfo[shaderType];
|
|
407
|
+
if (structInfo) {
|
|
408
|
+
if (getCurrentProgramBuilder().shaderType === shaderType && structInfo.structs[typeCopy.structName]) {
|
|
409
|
+
throw new PBParamValueError('defineStruct', 'structName', `cannot re-define struct '${typeCopy.structName}'`);
|
|
410
|
+
}
|
|
411
|
+
for (const type of structInfo.types){
|
|
412
|
+
if (!type.builtin && this.isIdenticalStruct(type.getType(), typeCopy, false)) {
|
|
413
|
+
structDef = type;
|
|
414
|
+
ctor = structInfo.structs[type.getType().structName];
|
|
415
|
+
break;
|
|
416
|
+
}
|
|
417
|
+
}
|
|
418
|
+
}
|
|
419
|
+
if (structDef) {
|
|
420
|
+
if (structDef.type.layout !== typeCopy.layout) {
|
|
421
|
+
throw new Error(`Can not redefine struct ${structDef.type.structName} with different layout`);
|
|
422
|
+
}
|
|
423
|
+
if (shaderType !== getCurrentProgramBuilder().shaderType) {
|
|
424
|
+
if (!this._structInfo[getCurrentProgramBuilder().shaderType]) {
|
|
425
|
+
this._structInfo[getCurrentProgramBuilder().shaderType] = {
|
|
426
|
+
structs: {},
|
|
427
|
+
types: []
|
|
428
|
+
};
|
|
429
|
+
}
|
|
430
|
+
this._structInfo[getCurrentProgramBuilder().shaderType].types.push(structDef);
|
|
431
|
+
this._structInfo[getCurrentProgramBuilder().shaderType].structs[structDef.getType().structName] = ctor;
|
|
432
|
+
}
|
|
433
|
+
return ctor;
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
return this.internalDefineStructByType(this._shaderType, false, typeCopy);
|
|
437
|
+
}
|
|
438
|
+
/** @internal */ internalDefineStruct(structName, layout, shaderTypeMask, builtin, ...args) {
|
|
439
|
+
const structType = new PBStructTypeInfo(structName, layout, args.map((arg)=>{
|
|
440
|
+
if (!arg.$typeinfo.isPrimitiveType() && !arg.$typeinfo.isArrayType() && !arg.$typeinfo.isStructType() && !arg.$typeinfo.isAtomicI32() && !arg.$typeinfo.isAtomicU32()) {
|
|
441
|
+
throw new Error(`invalid struct member type: '${arg.$str}'`);
|
|
442
|
+
}
|
|
443
|
+
return {
|
|
444
|
+
name: arg.$str,
|
|
445
|
+
type: arg.$typeinfo
|
|
446
|
+
};
|
|
447
|
+
}));
|
|
448
|
+
return this.internalDefineStructByType(shaderTypeMask, builtin, structType);
|
|
449
|
+
}
|
|
450
|
+
/** @internal */ internalDefineStructByType(shaderTypeMask, builtin, structType) {
|
|
451
|
+
const struct = makeConstructor(function structConstructor(...blockArgs) {
|
|
452
|
+
let e;
|
|
453
|
+
if (blockArgs.length === 1 && typeof blockArgs[0] === 'string') {
|
|
454
|
+
e = new PBShaderExp(blockArgs[0], structType);
|
|
455
|
+
} else {
|
|
456
|
+
e = new PBShaderExp('', structType);
|
|
457
|
+
e.$ast = new ASTShaderExpConstructor(e.$typeinfo, blockArgs.map((arg)=>arg instanceof PBShaderExp ? arg.$ast : arg));
|
|
458
|
+
}
|
|
459
|
+
return e;
|
|
460
|
+
}, structType);
|
|
461
|
+
for (const shaderType of [
|
|
462
|
+
ShaderType.Vertex,
|
|
463
|
+
ShaderType.Fragment,
|
|
464
|
+
ShaderType.Compute
|
|
465
|
+
]){
|
|
466
|
+
if (shaderTypeMask & shaderType) {
|
|
467
|
+
let structInfo = this._structInfo[shaderType];
|
|
468
|
+
if (!structInfo) {
|
|
469
|
+
structInfo = {
|
|
470
|
+
structs: {},
|
|
471
|
+
types: []
|
|
472
|
+
};
|
|
473
|
+
this._structInfo[shaderType] = structInfo;
|
|
474
|
+
}
|
|
475
|
+
if (structInfo.structs[structType.structName]) {
|
|
476
|
+
throw new PBParamValueError('defineStruct', 'structName', `cannot re-define struct '${structType.structName}'`);
|
|
477
|
+
}
|
|
478
|
+
structInfo.types.push(new ASTStructDefine(structType, builtin));
|
|
479
|
+
structInfo.structs[structType.structName] = struct;
|
|
480
|
+
}
|
|
481
|
+
}
|
|
482
|
+
// this.changeStructLayout(structType, layout);
|
|
483
|
+
return struct;
|
|
484
|
+
}
|
|
485
|
+
/** @internal */ getFunction(name) {
|
|
486
|
+
return this._globalScope ? this._globalScope.$getFunctions(name) : null;
|
|
487
|
+
}
|
|
488
|
+
/** @internal */ get structInfo() {
|
|
489
|
+
return this._structInfo[this._shaderType];
|
|
490
|
+
}
|
|
491
|
+
/** @internal */ getBlockName(instanceName) {
|
|
492
|
+
return `ch_block_name_${instanceName}`;
|
|
493
|
+
}
|
|
494
|
+
/** @internal */ defineBuiltinStruct(shaderType, inOrOut) {
|
|
495
|
+
const structName = inOrOut === 'in' ? getBuiltinInputStructName(shaderType) : getBuiltinOutputStructName(shaderType);
|
|
496
|
+
const instanceName = inOrOut === 'in' ? getBuiltinInputStructInstanceName(shaderType) : getBuiltinOutputStructInstanceName(shaderType);
|
|
497
|
+
const stage = shaderType === ShaderType.Vertex ? 'vertex' : shaderType === ShaderType.Fragment ? 'fragment' : 'compute';
|
|
498
|
+
const builtinVars = builtinVariables['webgpu'];
|
|
499
|
+
const args = [];
|
|
500
|
+
const prefix = [];
|
|
501
|
+
for(const k in builtinVars){
|
|
502
|
+
if (builtinVars[k].stage === stage && builtinVars[k].inOrOut === inOrOut) {
|
|
503
|
+
args.push({
|
|
504
|
+
name: builtinVars[k].name,
|
|
505
|
+
type: builtinVars[k].type
|
|
506
|
+
});
|
|
507
|
+
prefix.push(`@builtin(${builtinVars[k].semantic}) `);
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
const inoutList = inOrOut === 'in' ? this._inputs : this._outputs;
|
|
511
|
+
for (const k of inoutList){
|
|
512
|
+
// for debug only
|
|
513
|
+
if (!(k[1] instanceof ASTDeclareVar)) {
|
|
514
|
+
throw new PBInternalError('defineBuiltinStruct() failed: input/output is not declare var ast node');
|
|
515
|
+
}
|
|
516
|
+
const type = k[1].value.getType();
|
|
517
|
+
if (!type.isPrimitiveType() && !type.isArrayType() && !type.isStructType()) {
|
|
518
|
+
throw new Error(`invalid in/out variable type: '${k[1].value.name}'`);
|
|
519
|
+
}
|
|
520
|
+
args.push({
|
|
521
|
+
name: k[1].value.name,
|
|
522
|
+
type: type
|
|
523
|
+
});
|
|
524
|
+
prefix.push(`@location(${k[1].value.value.$location}) ${type.isPrimitiveType() && type.isInteger() ? '@interpolate(flat) ' : ''}`);
|
|
525
|
+
}
|
|
526
|
+
if (args.length > 0) {
|
|
527
|
+
const st = this.findStructType(structName, shaderType);
|
|
528
|
+
if (st) {
|
|
529
|
+
st.getType().reset(structName, 'default', args);
|
|
530
|
+
st.prefix = prefix;
|
|
531
|
+
return null;
|
|
532
|
+
} else {
|
|
533
|
+
const structType = this.internalDefineStructByType(this._shaderType, false, new PBStructTypeInfo(structName, 'default', args));
|
|
534
|
+
this.findStructType(structName, shaderType).prefix = prefix;
|
|
535
|
+
const structInstance = this.struct(structName, instanceName);
|
|
536
|
+
const structInstanceIN = inOrOut === 'in' ? this.struct(structName, 'uu_AppInput') : structInstance;
|
|
537
|
+
return [
|
|
538
|
+
structType,
|
|
539
|
+
structInstance,
|
|
540
|
+
structName,
|
|
541
|
+
structInstanceIN
|
|
542
|
+
];
|
|
543
|
+
}
|
|
544
|
+
} else {
|
|
545
|
+
return null;
|
|
546
|
+
}
|
|
547
|
+
}
|
|
548
|
+
/** @internal */ defineInternalStructs() {
|
|
549
|
+
this.defineHiddenStruct(typeFrexpResult);
|
|
550
|
+
this.defineHiddenStruct(typeFrexpResultVec2);
|
|
551
|
+
this.defineHiddenStruct(typeFrexpResultVec3);
|
|
552
|
+
this.defineHiddenStruct(typeFrexpResultVec4);
|
|
553
|
+
}
|
|
554
|
+
/** @internal */ array(...args) {
|
|
555
|
+
if (args.length === 0) {
|
|
556
|
+
throw new PBParamLengthError('array');
|
|
557
|
+
}
|
|
558
|
+
args = args.map((arg)=>this.normalizeExpValue(arg));
|
|
559
|
+
let typeok = true;
|
|
560
|
+
let type = null;
|
|
561
|
+
let isBool = true;
|
|
562
|
+
let isFloat = true;
|
|
563
|
+
let isInt = true;
|
|
564
|
+
let isUint = true;
|
|
565
|
+
let isComposite = false;
|
|
566
|
+
for (const arg of args){
|
|
567
|
+
if (arg instanceof PBShaderExp) {
|
|
568
|
+
const argType = arg.$ast.getType();
|
|
569
|
+
if (!argType.isConstructible()) {
|
|
570
|
+
typeok = false;
|
|
571
|
+
break;
|
|
572
|
+
}
|
|
573
|
+
if (!type) {
|
|
574
|
+
type = argType;
|
|
575
|
+
} else if (!argType.isCompatibleType(type)) {
|
|
576
|
+
typeok = false;
|
|
577
|
+
}
|
|
578
|
+
}
|
|
579
|
+
}
|
|
580
|
+
if (typeok) {
|
|
581
|
+
if (type && type.isPrimitiveType() && type.isScalarType()) {
|
|
582
|
+
isBool = type.primitiveType === PBPrimitiveType.BOOL;
|
|
583
|
+
isFloat = type.primitiveType === PBPrimitiveType.F32;
|
|
584
|
+
isUint = type.primitiveType === PBPrimitiveType.U32;
|
|
585
|
+
isInt = type.primitiveType === PBPrimitiveType.I32;
|
|
586
|
+
} else if (type) {
|
|
587
|
+
isBool = false;
|
|
588
|
+
isFloat = false;
|
|
589
|
+
isUint = false;
|
|
590
|
+
isInt = false;
|
|
591
|
+
isComposite = true;
|
|
592
|
+
}
|
|
593
|
+
for (const arg of args){
|
|
594
|
+
if (!(arg instanceof PBShaderExp) && isComposite) {
|
|
595
|
+
typeok = false;
|
|
596
|
+
break;
|
|
597
|
+
}
|
|
598
|
+
if (typeof arg === 'number') {
|
|
599
|
+
isBool = false;
|
|
600
|
+
if ((arg | 0) === arg) {
|
|
601
|
+
if (arg < 0) {
|
|
602
|
+
isUint = false;
|
|
603
|
+
isInt = isInt && arg >= 0x80000000 >> 0;
|
|
604
|
+
} else {
|
|
605
|
+
isUint = isUint && arg <= 0xffffffff;
|
|
606
|
+
isInt = isInt && arg <= 0x7fffffff;
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
} else if (typeof arg === 'boolean') {
|
|
610
|
+
isFloat = false;
|
|
611
|
+
isInt = false;
|
|
612
|
+
isUint = false;
|
|
613
|
+
}
|
|
614
|
+
}
|
|
615
|
+
}
|
|
616
|
+
if (typeok && !isComposite) {
|
|
617
|
+
if (isBool) {
|
|
618
|
+
type = typeBool;
|
|
619
|
+
} else if (isInt) {
|
|
620
|
+
type = typeI32;
|
|
621
|
+
} else if (isUint) {
|
|
622
|
+
type = typeU32;
|
|
623
|
+
} else if (isFloat) {
|
|
624
|
+
type = typeF32;
|
|
625
|
+
}
|
|
626
|
+
typeok = !!type;
|
|
627
|
+
}
|
|
628
|
+
if (!typeok) {
|
|
629
|
+
throw new PBParamTypeError('array');
|
|
630
|
+
}
|
|
631
|
+
if (!type.isPrimitiveType() && !type.isArrayType() && !type.isStructType()) {
|
|
632
|
+
throw new PBParamTypeError('array');
|
|
633
|
+
}
|
|
634
|
+
const arrayType = new PBArrayTypeInfo(type, args.length);
|
|
635
|
+
const exp = new PBShaderExp('', arrayType);
|
|
636
|
+
exp.$ast = new ASTShaderExpConstructor(arrayType, args.map((arg)=>{
|
|
637
|
+
if (arg instanceof PBShaderExp) {
|
|
638
|
+
return arg.$ast;
|
|
639
|
+
}
|
|
640
|
+
if (!type.isPrimitiveType() || !type.isScalarType()) {
|
|
641
|
+
throw new PBTypeCastError(arg, typeof arg, type);
|
|
642
|
+
}
|
|
643
|
+
return new ASTScalar(arg, type);
|
|
644
|
+
}));
|
|
645
|
+
return exp;
|
|
646
|
+
}
|
|
647
|
+
/**
|
|
648
|
+
* Creates a 'discard' statement
|
|
649
|
+
*/ discard() {
|
|
650
|
+
this.getCurrentScope().$ast.statements.push(new ASTDiscard());
|
|
651
|
+
}
|
|
652
|
+
/** @internal */ tagShaderExp(getter, tagValue) {
|
|
653
|
+
if (typeof tagValue === 'string') {
|
|
654
|
+
this._reflection.tag(tagValue, getter);
|
|
655
|
+
} else if (Array.isArray(tagValue)) {
|
|
656
|
+
tagValue.forEach((tag)=>this.tagShaderExp(getter, tag));
|
|
657
|
+
} else {
|
|
658
|
+
for (const k of Object.keys(tagValue)){
|
|
659
|
+
this.tagShaderExp((scope)=>{
|
|
660
|
+
const value = getter(scope);
|
|
661
|
+
return value[k];
|
|
662
|
+
}, tagValue[k]);
|
|
663
|
+
}
|
|
664
|
+
}
|
|
665
|
+
}
|
|
666
|
+
/** @internal */ in(location, name, variable) {
|
|
667
|
+
if (this._inputs[location]) {
|
|
668
|
+
throw new Error(`input location ${location} already declared`);
|
|
669
|
+
}
|
|
670
|
+
variable.$location = location;
|
|
671
|
+
variable.$declareType = DeclareType.DECLARE_TYPE_IN;
|
|
672
|
+
this._inputs[location] = [
|
|
673
|
+
name,
|
|
674
|
+
new ASTDeclareVar(new ASTPrimitive(variable))
|
|
675
|
+
];
|
|
676
|
+
Object.defineProperty(this._inputScope, name, {
|
|
677
|
+
get: function() {
|
|
678
|
+
return variable;
|
|
679
|
+
},
|
|
680
|
+
set: function() {
|
|
681
|
+
throw new Error(`cannot assign to readonly variable: ${name}`);
|
|
682
|
+
}
|
|
683
|
+
});
|
|
684
|
+
variable.$tags.forEach((val)=>this.tagShaderExp(()=>variable, val));
|
|
685
|
+
}
|
|
686
|
+
/** @internal */ out(location, name, variable) {
|
|
687
|
+
if (this._outputs[location]) {
|
|
688
|
+
throw new Error(`output location ${location} has already been used`);
|
|
689
|
+
}
|
|
690
|
+
variable.$location = location;
|
|
691
|
+
variable.$declareType = DeclareType.DECLARE_TYPE_OUT;
|
|
692
|
+
this._outputs[location] = [
|
|
693
|
+
name,
|
|
694
|
+
new ASTDeclareVar(new ASTPrimitive(variable))
|
|
695
|
+
];
|
|
696
|
+
Object.defineProperty(this._outputScope, name, {
|
|
697
|
+
get: function() {
|
|
698
|
+
return variable;
|
|
699
|
+
},
|
|
700
|
+
set: function(v) {
|
|
701
|
+
getCurrentProgramBuilder().getCurrentScope().$ast.statements.push(new ASTAssignment(new ASTLValueScalar(variable.$ast), v instanceof PBShaderExp ? v.$ast : v));
|
|
702
|
+
}
|
|
703
|
+
});
|
|
704
|
+
}
|
|
705
|
+
/** @internal */ getDefaultSampler(t, comparison) {
|
|
706
|
+
const u = this._uniforms.findIndex((val)=>val.texture?.exp === t);
|
|
707
|
+
if (u < 0) {
|
|
708
|
+
return;
|
|
709
|
+
//throw new Error('invalid texture uniform object');
|
|
710
|
+
}
|
|
711
|
+
const samplerType = comparison ? 'comparison' : 'sample';
|
|
712
|
+
if (this._uniforms[u].texture.autoBindSampler && this._uniforms[u].texture.autoBindSampler !== samplerType) {
|
|
713
|
+
throw new Error('multiple sampler not supported');
|
|
714
|
+
}
|
|
715
|
+
this._uniforms[u].texture.autoBindSampler = samplerType;
|
|
716
|
+
if (this._device.type === 'webgpu') {
|
|
717
|
+
const samplerName = genSamplerName(t.$str, comparison);
|
|
718
|
+
if (!this.getGlobalScope()[samplerName]) {
|
|
719
|
+
throw new Error(`failed to find sampler name ${samplerName}`);
|
|
720
|
+
}
|
|
721
|
+
return this.getGlobalScope()[samplerName];
|
|
722
|
+
} else {
|
|
723
|
+
return null;
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
/** @internal */ normalizeExpValue(value) {
|
|
727
|
+
if (Array.isArray(value)) {
|
|
728
|
+
const converted = value.map((val)=>Array.isArray(val) ? this.normalizeExpValue(val) : val);
|
|
729
|
+
return this.array(...converted);
|
|
730
|
+
} else {
|
|
731
|
+
return value;
|
|
732
|
+
}
|
|
733
|
+
}
|
|
734
|
+
/** @internal */ guessExpValueType(value) {
|
|
735
|
+
const val = this.normalizeExpValue(value);
|
|
736
|
+
if (typeof val === 'boolean') {
|
|
737
|
+
return typeBool;
|
|
738
|
+
} else if (typeof val === 'number') {
|
|
739
|
+
if (!Number.isInteger(val)) {
|
|
740
|
+
return typeF32;
|
|
741
|
+
} else if (val >= 0x80000000 >> 1 && val <= 0x7fffffff) {
|
|
742
|
+
return typeI32;
|
|
743
|
+
} else if (val >= 0 && val <= 0xffffffff) {
|
|
744
|
+
return typeU32;
|
|
745
|
+
} else {
|
|
746
|
+
throw new PBValueOutOfRange(val);
|
|
747
|
+
}
|
|
748
|
+
} else if (val instanceof PBShaderExp) {
|
|
749
|
+
return val.$ast?.getType() || val.$typeinfo;
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
/** @internal */ findStructType(name, shaderType) {
|
|
753
|
+
for (const st of [
|
|
754
|
+
ShaderType.Vertex,
|
|
755
|
+
ShaderType.Fragment,
|
|
756
|
+
ShaderType.Compute
|
|
757
|
+
]){
|
|
758
|
+
if (st & shaderType) {
|
|
759
|
+
const structInfo = this._structInfo[st];
|
|
760
|
+
if (structInfo) {
|
|
761
|
+
for (const t of structInfo.types){
|
|
762
|
+
if (t.type.structName === name) {
|
|
763
|
+
return t;
|
|
764
|
+
}
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
}
|
|
768
|
+
}
|
|
769
|
+
return null;
|
|
770
|
+
}
|
|
771
|
+
/** @internal */ findStructConstructor(name, shaderType) {
|
|
772
|
+
for (const st of [
|
|
773
|
+
ShaderType.Vertex,
|
|
774
|
+
ShaderType.Fragment,
|
|
775
|
+
ShaderType.Compute
|
|
776
|
+
]){
|
|
777
|
+
if (st & shaderType) {
|
|
778
|
+
const structInfo = this._structInfo[st];
|
|
779
|
+
if (structInfo && structInfo.structs?.[name]) {
|
|
780
|
+
return structInfo.structs[name];
|
|
781
|
+
}
|
|
782
|
+
}
|
|
783
|
+
}
|
|
784
|
+
return null;
|
|
785
|
+
}
|
|
786
|
+
/** @internal */ buildComputeSource(options) {
|
|
787
|
+
try {
|
|
788
|
+
this._lastError = null;
|
|
789
|
+
this._shaderType = ShaderType.Compute;
|
|
790
|
+
this._scopeStack = [];
|
|
791
|
+
this._globalScope = new PBGlobalScope();
|
|
792
|
+
this._builtinScope = new PBBuiltinScope();
|
|
793
|
+
this._inputs = [];
|
|
794
|
+
this._outputs = [];
|
|
795
|
+
this._inputScope = new PBInputScope();
|
|
796
|
+
this._outputScope = new PBOutputScope();
|
|
797
|
+
this._reflection.clear();
|
|
798
|
+
this.generate(options.compute);
|
|
799
|
+
// this.removeUnusedSamplerBindings(this._globalScope);
|
|
800
|
+
this.mergeUniformsCompute(this._globalScope);
|
|
801
|
+
this.updateUniformBindings([
|
|
802
|
+
this._globalScope
|
|
803
|
+
], [
|
|
804
|
+
ShaderType.Compute
|
|
805
|
+
]);
|
|
806
|
+
return [
|
|
807
|
+
this.generateComputeSource(this._globalScope, this._builtinScope),
|
|
808
|
+
this.createBindGroupLayouts(options.label)
|
|
809
|
+
];
|
|
810
|
+
} catch (err) {
|
|
811
|
+
if (err instanceof PBError) {
|
|
812
|
+
this._lastError = err.getMessage(this._device.type);
|
|
813
|
+
console.error(this._lastError);
|
|
814
|
+
return null;
|
|
815
|
+
} else if (err instanceof Error) {
|
|
816
|
+
this._lastError = err.toString();
|
|
817
|
+
console.error(this._lastError);
|
|
818
|
+
return null;
|
|
819
|
+
} else {
|
|
820
|
+
this._lastError = Object.prototype.toString.call(err);
|
|
821
|
+
console.log(`Error: ${this._lastError}`);
|
|
822
|
+
return null;
|
|
823
|
+
}
|
|
824
|
+
}
|
|
825
|
+
}
|
|
826
|
+
/** @internal */ buildRenderSource(options) {
|
|
827
|
+
try {
|
|
828
|
+
this._lastError = null;
|
|
829
|
+
this._shaderType = ShaderType.Vertex;
|
|
830
|
+
this._scopeStack = [];
|
|
831
|
+
this._globalScope = new PBGlobalScope();
|
|
832
|
+
this._builtinScope = new PBBuiltinScope();
|
|
833
|
+
this._inputs = [];
|
|
834
|
+
this._outputs = [];
|
|
835
|
+
this._inputScope = new PBInputScope();
|
|
836
|
+
this._outputScope = new PBOutputScope();
|
|
837
|
+
this._reflection.clear();
|
|
838
|
+
this.generate(options.vertex);
|
|
839
|
+
const vertexScope = this._globalScope;
|
|
840
|
+
const vertexBuiltinScope = this._builtinScope;
|
|
841
|
+
const vertexInputs = this._inputs;
|
|
842
|
+
const vertexOutputs = this._outputs;
|
|
843
|
+
if (this._device.type === 'webgpu') {
|
|
844
|
+
// this.removeUnusedSamplerBindings(vertexScope);
|
|
845
|
+
}
|
|
846
|
+
this._shaderType = ShaderType.Fragment;
|
|
847
|
+
this._scopeStack = [];
|
|
848
|
+
this._globalScope = new PBGlobalScope();
|
|
849
|
+
this._builtinScope = new PBBuiltinScope();
|
|
850
|
+
this._inputs = [];
|
|
851
|
+
this._outputs = [];
|
|
852
|
+
this._inputScope = new PBInputScope();
|
|
853
|
+
this._outputScope = new PBOutputScope();
|
|
854
|
+
this._reflection.clear();
|
|
855
|
+
vertexOutputs.forEach((val, index)=>{
|
|
856
|
+
this.in(index, val[0], new PBShaderExp(val[1].value.name, val[1].value.getType()).tag(...val[1].value.value.$tags));
|
|
857
|
+
});
|
|
858
|
+
this.generate(options.fragment);
|
|
859
|
+
const fragScope = this._globalScope;
|
|
860
|
+
const fragBuiltinScope = this._builtinScope;
|
|
861
|
+
const fragInputs = this._inputs;
|
|
862
|
+
const fragOutputs = this._outputs;
|
|
863
|
+
if (this._device.type === 'webgpu') {
|
|
864
|
+
// this.removeUnusedSamplerBindings(fragScope);
|
|
865
|
+
}
|
|
866
|
+
this.mergeUniforms(vertexScope, fragScope);
|
|
867
|
+
this.updateUniformBindings([
|
|
868
|
+
vertexScope,
|
|
869
|
+
fragScope
|
|
870
|
+
], [
|
|
871
|
+
ShaderType.Vertex,
|
|
872
|
+
ShaderType.Fragment
|
|
873
|
+
]);
|
|
874
|
+
return [
|
|
875
|
+
this.generateRenderSource(ShaderType.Vertex, vertexScope, vertexBuiltinScope, vertexInputs.map((val)=>val[1]), vertexOutputs.map((val)=>val[1])),
|
|
876
|
+
this.generateRenderSource(ShaderType.Fragment, fragScope, fragBuiltinScope, fragInputs.map((val)=>val[1]), fragOutputs.map((val)=>val[1])),
|
|
877
|
+
this.createBindGroupLayouts(options.label),
|
|
878
|
+
this._vertexAttributes
|
|
879
|
+
];
|
|
880
|
+
} catch (err) {
|
|
881
|
+
if (err instanceof PBError) {
|
|
882
|
+
this._lastError = err.getMessage(this._device.type);
|
|
883
|
+
console.error(this._lastError);
|
|
884
|
+
return null;
|
|
885
|
+
} else if (err instanceof Error) {
|
|
886
|
+
this._lastError = err.toString();
|
|
887
|
+
console.error(this._lastError);
|
|
888
|
+
return null;
|
|
889
|
+
} else {
|
|
890
|
+
this._lastError = Object.prototype.toString.call(err);
|
|
891
|
+
console.log(`Error: ${this._lastError}`);
|
|
892
|
+
return null;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
/** @internal */ generate(body) {
|
|
897
|
+
this.pushScope(this._globalScope);
|
|
898
|
+
if (this._emulateDepthClamp && this._shaderType === ShaderType.Vertex) {
|
|
899
|
+
this._globalScope.$outputs.clamppedDepth = this.float().tag('CLAMPPED_DEPTH');
|
|
900
|
+
}
|
|
901
|
+
body && body.call(this._globalScope, this);
|
|
902
|
+
this.popScope();
|
|
903
|
+
}
|
|
904
|
+
/** @internal */ generateRenderSource(shaderType, scope, builtinScope, inputs, outputs) {
|
|
905
|
+
const context = {
|
|
906
|
+
type: shaderType,
|
|
907
|
+
mrt: shaderType === ShaderType.Fragment && outputs.length > 1,
|
|
908
|
+
defines: [],
|
|
909
|
+
extensions: new Set(),
|
|
910
|
+
builtins: [
|
|
911
|
+
...builtinScope.$_usedBuiltins
|
|
912
|
+
],
|
|
913
|
+
types: this._structInfo[shaderType]?.types || [],
|
|
914
|
+
typeReplacement: new Map(),
|
|
915
|
+
inputs: inputs,
|
|
916
|
+
outputs: outputs,
|
|
917
|
+
global: scope,
|
|
918
|
+
vertexAttributes: this._vertexAttributes,
|
|
919
|
+
workgroupSize: null
|
|
920
|
+
};
|
|
921
|
+
switch(this._device.type){
|
|
922
|
+
case 'webgl':
|
|
923
|
+
for (const u of this._uniforms){
|
|
924
|
+
if (u.texture) {
|
|
925
|
+
const type = u.texture.exp.$ast.getType();
|
|
926
|
+
if (type.isTextureType() && type.isDepthTexture()) {
|
|
927
|
+
if (u.texture.autoBindSampler === 'comparison') {
|
|
928
|
+
throw new PBDeviceNotSupport('depth texture comparison');
|
|
929
|
+
}
|
|
930
|
+
if (u.texture.autoBindSampler === 'sample') {
|
|
931
|
+
if (type.is2DTexture()) {
|
|
932
|
+
context.typeReplacement.set(u.texture.exp, typeTex2D);
|
|
933
|
+
} else if (type.isCubeTexture()) {
|
|
934
|
+
context.typeReplacement.set(u.texture.exp, typeTexCube);
|
|
935
|
+
}
|
|
936
|
+
}
|
|
937
|
+
}
|
|
938
|
+
}
|
|
939
|
+
}
|
|
940
|
+
return scope.$ast.toWebGL('', context);
|
|
941
|
+
case 'webgl2':
|
|
942
|
+
for (const u of this._uniforms){
|
|
943
|
+
if (u.texture) {
|
|
944
|
+
const type = u.texture.exp.$ast.getType();
|
|
945
|
+
if (type.isTextureType() && type.isDepthTexture() && u.texture.autoBindSampler === 'sample') {
|
|
946
|
+
if (type.is2DTexture()) {
|
|
947
|
+
context.typeReplacement.set(u.texture.exp, type.isArrayTexture() ? typeTex2DArray : typeTex2D);
|
|
948
|
+
} else if (type.isCubeTexture()) {
|
|
949
|
+
context.typeReplacement.set(u.texture.exp, typeTexCube);
|
|
950
|
+
}
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
}
|
|
954
|
+
return scope.$ast.toWebGL2('', context);
|
|
955
|
+
case 'webgpu':
|
|
956
|
+
return scope.$ast.toWGSL('', context);
|
|
957
|
+
default:
|
|
958
|
+
return null;
|
|
959
|
+
}
|
|
960
|
+
}
|
|
961
|
+
/** @internal */ generateComputeSource(scope, builtinScope) {
|
|
962
|
+
const context = {
|
|
963
|
+
type: ShaderType.Compute,
|
|
964
|
+
mrt: false,
|
|
965
|
+
defines: [],
|
|
966
|
+
extensions: new Set(),
|
|
967
|
+
builtins: [
|
|
968
|
+
...builtinScope.$_usedBuiltins
|
|
969
|
+
],
|
|
970
|
+
types: this._structInfo[ShaderType.Compute]?.types || [],
|
|
971
|
+
typeReplacement: null,
|
|
972
|
+
inputs: [],
|
|
973
|
+
outputs: [],
|
|
974
|
+
global: scope,
|
|
975
|
+
vertexAttributes: [],
|
|
976
|
+
workgroupSize: this._workgroupSize
|
|
977
|
+
};
|
|
978
|
+
return scope.$ast.toWGSL('', context);
|
|
979
|
+
}
|
|
980
|
+
/** @internal */ mergeUniformsCompute(globalScope) {
|
|
981
|
+
const uniformList = [];
|
|
982
|
+
for(let i = 0; i < this._uniforms.length; i++){
|
|
983
|
+
const u = this._uniforms[i];
|
|
984
|
+
if (u.block && (u.block.exp.$declareType === DeclareType.DECLARE_TYPE_UNIFORM || u.block.exp.$declareType === DeclareType.DECLARE_TYPE_STORAGE)) {
|
|
985
|
+
if (u.block.exp.$typeinfo.isStructType() && u.block.exp.$isBuffer) {
|
|
986
|
+
continue;
|
|
987
|
+
}
|
|
988
|
+
if (!uniformList[u.group]) {
|
|
989
|
+
uniformList[u.group] = [];
|
|
990
|
+
}
|
|
991
|
+
const exp = new PBShaderExp(u.block.exp.$str, u.block.exp.$ast.getType());
|
|
992
|
+
exp.$declareType = u.block.exp.$declareType;
|
|
993
|
+
exp.$isBuffer = u.block.exp.$isBuffer;
|
|
994
|
+
uniformList[u.group].push({
|
|
995
|
+
member: exp,
|
|
996
|
+
uniform: i
|
|
997
|
+
});
|
|
998
|
+
}
|
|
999
|
+
}
|
|
1000
|
+
for(const k in uniformList){
|
|
1001
|
+
if (uniformList[k].length > 0) {
|
|
1002
|
+
const types = [
|
|
1003
|
+
'std140',
|
|
1004
|
+
'std430'
|
|
1005
|
+
];
|
|
1006
|
+
const nameList = [
|
|
1007
|
+
COMPUTE_UNIFORM_NAME,
|
|
1008
|
+
COMPUTE_STORAGE_NAME
|
|
1009
|
+
];
|
|
1010
|
+
const ulist = [
|
|
1011
|
+
uniformList[k].filter((val)=>val.member.$declareType === DeclareType.DECLARE_TYPE_UNIFORM),
|
|
1012
|
+
uniformList[k].filter((val)=>val.member.$declareType === DeclareType.DECLARE_TYPE_STORAGE)
|
|
1013
|
+
];
|
|
1014
|
+
for(let i = 0; i < 2; i++){
|
|
1015
|
+
if (ulist[i].length === 0) {
|
|
1016
|
+
continue;
|
|
1017
|
+
}
|
|
1018
|
+
const nonBufferList = ulist[i].filter((val)=>!val.member.$isBuffer);
|
|
1019
|
+
const bufferList = ulist[i].filter((val)=>val.member.$isBuffer);
|
|
1020
|
+
const allLists = [
|
|
1021
|
+
nonBufferList,
|
|
1022
|
+
...bufferList.map((val)=>[
|
|
1023
|
+
val
|
|
1024
|
+
])
|
|
1025
|
+
];
|
|
1026
|
+
for(let p = 0; p < allLists.length; p++){
|
|
1027
|
+
if (allLists[p].length === 0) {
|
|
1028
|
+
continue;
|
|
1029
|
+
}
|
|
1030
|
+
const uname = `${nameList[i]}_${k}_${p}`;
|
|
1031
|
+
const structName = this.generateStructureName();
|
|
1032
|
+
const t = getCurrentProgramBuilder().internalDefineStruct(structName, types[i], ShaderType.Compute, false, ...allLists[p].map((val)=>val.member));
|
|
1033
|
+
const exp = t();
|
|
1034
|
+
if (i === 0) {
|
|
1035
|
+
exp.uniformBuffer(Number(k));
|
|
1036
|
+
} else {
|
|
1037
|
+
exp.storageBuffer(Number(k));
|
|
1038
|
+
}
|
|
1039
|
+
globalScope[uname] = exp;
|
|
1040
|
+
const index = this._uniforms.findIndex((val)=>val.block?.name === uname);
|
|
1041
|
+
this._uniforms[index].mask = ShaderType.Compute;
|
|
1042
|
+
let nameMap = this._nameMap[Number(k)];
|
|
1043
|
+
if (!nameMap) {
|
|
1044
|
+
nameMap = {};
|
|
1045
|
+
this._nameMap[Number(k)] = nameMap;
|
|
1046
|
+
}
|
|
1047
|
+
let writable = false;
|
|
1048
|
+
for(let n = allLists[p].length - 1; n >= 0; n--){
|
|
1049
|
+
const u = allLists[p][n];
|
|
1050
|
+
const exp = this._uniforms[u.uniform].block.exp;
|
|
1051
|
+
nameMap[exp.$str] = uname;
|
|
1052
|
+
exp.$str = `${uname}.${exp.$str}`;
|
|
1053
|
+
writable ||= exp.$ast.isWritable();
|
|
1054
|
+
}
|
|
1055
|
+
if (writable) {
|
|
1056
|
+
globalScope[uname].$ast.markWritable();
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
}
|
|
1060
|
+
}
|
|
1061
|
+
}
|
|
1062
|
+
this._uniforms = this._uniforms.filter((val)=>{
|
|
1063
|
+
return !val.block || val.block.exp.$typeinfo.isStructType() && val.block.exp.$isBuffer;
|
|
1064
|
+
//return !val.block || val.block.exp.$isBuffer;
|
|
1065
|
+
/*
|
|
1066
|
+
if (!val.block || (val.block.exp.$declareType !== AST.DeclareType.DECLARE_TYPE_UNIFORM && val.block.exp.$declareType !== AST.DeclareType.DECLARE_TYPE_STORAGE)) {
|
|
1067
|
+
return true;
|
|
1068
|
+
}
|
|
1069
|
+
const type = val.block.exp.$ast.getType();
|
|
1070
|
+
return (
|
|
1071
|
+
type.isTextureType() ||
|
|
1072
|
+
type.isSamplerType() ||
|
|
1073
|
+
(type.isStructType() && (type.detail.layout === 'std140' || type.detail.layout === 'std430'))
|
|
1074
|
+
);
|
|
1075
|
+
*/ });
|
|
1076
|
+
}
|
|
1077
|
+
/** @internal */ mergeUniforms(globalScopeVertex, globalScopeFragmet) {
|
|
1078
|
+
const vertexUniformList = [];
|
|
1079
|
+
const fragUniformList = [];
|
|
1080
|
+
const sharedUniformList = [];
|
|
1081
|
+
//const vertexUniformList: { members: PBShaderExp[]; uniforms: number[] }[] = [];
|
|
1082
|
+
//const fragUniformList: { members: PBShaderExp[]; uniforms: number[] }[] = [];
|
|
1083
|
+
//const sharedUniformList: { members: PBShaderExp[]; uniforms: number[] }[] = [];
|
|
1084
|
+
for(let i = 0; i < this._uniforms.length; i++){
|
|
1085
|
+
const u = this._uniforms[i];
|
|
1086
|
+
if (u.block && (u.block.exp.$declareType === DeclareType.DECLARE_TYPE_UNIFORM || u.block.exp.$declareType === DeclareType.DECLARE_TYPE_STORAGE)) {
|
|
1087
|
+
if (u.block.exp.$typeinfo.isStructType() && u.block.exp.$isBuffer) {
|
|
1088
|
+
continue;
|
|
1089
|
+
}
|
|
1090
|
+
const v = !!(u.mask & ShaderType.Vertex);
|
|
1091
|
+
const f = !!(u.mask & ShaderType.Fragment);
|
|
1092
|
+
if (v && f) {
|
|
1093
|
+
if (!sharedUniformList[u.group]) {
|
|
1094
|
+
sharedUniformList[u.group] = []; //{ members: [], uniforms: [] };
|
|
1095
|
+
}
|
|
1096
|
+
const exp = new PBShaderExp(u.block.exp.$str, u.block.exp.$ast.getType());
|
|
1097
|
+
exp.$declareType = u.block.exp.$declareType;
|
|
1098
|
+
exp.$isBuffer = u.block.exp.$isBuffer;
|
|
1099
|
+
sharedUniformList[u.group].push({
|
|
1100
|
+
member: exp,
|
|
1101
|
+
uniform: i
|
|
1102
|
+
});
|
|
1103
|
+
//sharedUniformList[u.group].uniforms.push(i);
|
|
1104
|
+
} else if (v) {
|
|
1105
|
+
if (!vertexUniformList[u.group]) {
|
|
1106
|
+
vertexUniformList[u.group] = []; //{ members: [], uniforms: [] };
|
|
1107
|
+
}
|
|
1108
|
+
const exp = new PBShaderExp(u.block.exp.$str, u.block.exp.$ast.getType());
|
|
1109
|
+
exp.$declareType = u.block.exp.$declareType;
|
|
1110
|
+
exp.$isBuffer = u.block.exp.$isBuffer;
|
|
1111
|
+
vertexUniformList[u.group].push({
|
|
1112
|
+
member: exp,
|
|
1113
|
+
uniform: i
|
|
1114
|
+
});
|
|
1115
|
+
//vertexUniformList[u.group].uniforms.push(i);
|
|
1116
|
+
} else if (f) {
|
|
1117
|
+
if (!fragUniformList[u.group]) {
|
|
1118
|
+
fragUniformList[u.group] = []; //{ members: [], uniforms: [] };
|
|
1119
|
+
}
|
|
1120
|
+
const exp = new PBShaderExp(u.block.exp.$str, u.block.exp.$ast.getType());
|
|
1121
|
+
exp.$declareType = u.block.exp.$declareType;
|
|
1122
|
+
exp.$isBuffer = u.block.exp.$isBuffer;
|
|
1123
|
+
fragUniformList[u.group].push({
|
|
1124
|
+
member: exp,
|
|
1125
|
+
uniform: i
|
|
1126
|
+
}); //members.push(exp);
|
|
1127
|
+
//fragUniformList[u.group].uniforms.push(i);
|
|
1128
|
+
}
|
|
1129
|
+
}
|
|
1130
|
+
}
|
|
1131
|
+
const uniformLists = [
|
|
1132
|
+
vertexUniformList,
|
|
1133
|
+
fragUniformList,
|
|
1134
|
+
sharedUniformList
|
|
1135
|
+
];
|
|
1136
|
+
const nameListUniform = [
|
|
1137
|
+
VERTEX_UNIFORM_NAME,
|
|
1138
|
+
FRAGMENT_UNIFORM_NAME,
|
|
1139
|
+
SHARED_UNIFORM_NAME
|
|
1140
|
+
];
|
|
1141
|
+
const nameListStorage = [
|
|
1142
|
+
VERTEX_STORAGE_NAME,
|
|
1143
|
+
FRAGMENT_STORAGE_NAME,
|
|
1144
|
+
SHARED_STORAGE_NAME
|
|
1145
|
+
];
|
|
1146
|
+
const maskList = [
|
|
1147
|
+
ShaderType.Vertex,
|
|
1148
|
+
ShaderType.Fragment,
|
|
1149
|
+
ShaderType.Vertex | ShaderType.Fragment
|
|
1150
|
+
];
|
|
1151
|
+
for(let i = 0; i < 3; i++){
|
|
1152
|
+
for(const k in uniformLists[i]){
|
|
1153
|
+
if (uniformLists[i][k]?.length > 0) {
|
|
1154
|
+
const ulist = [
|
|
1155
|
+
uniformLists[i][k].filter((val)=>val.member.$declareType === DeclareType.DECLARE_TYPE_UNIFORM),
|
|
1156
|
+
uniformLists[i][k].filter((val)=>val.member.$declareType === DeclareType.DECLARE_TYPE_STORAGE)
|
|
1157
|
+
];
|
|
1158
|
+
const nameList = [
|
|
1159
|
+
nameListUniform,
|
|
1160
|
+
nameListStorage
|
|
1161
|
+
];
|
|
1162
|
+
const layoutList = [
|
|
1163
|
+
'std140',
|
|
1164
|
+
'std430'
|
|
1165
|
+
];
|
|
1166
|
+
for(let j = 0; j < 2; j++){
|
|
1167
|
+
if (ulist[j].length === 0) {
|
|
1168
|
+
continue;
|
|
1169
|
+
}
|
|
1170
|
+
const nonBufferList = ulist[j].filter((val)=>!val.member.$isBuffer);
|
|
1171
|
+
const bufferList = ulist[j].filter((val)=>val.member.$isBuffer);
|
|
1172
|
+
const allLists = [
|
|
1173
|
+
nonBufferList,
|
|
1174
|
+
...bufferList.map((val)=>[
|
|
1175
|
+
val
|
|
1176
|
+
])
|
|
1177
|
+
];
|
|
1178
|
+
for(let p = 0; p < allLists.length; p++){
|
|
1179
|
+
if (allLists[p].length === 0) {
|
|
1180
|
+
continue;
|
|
1181
|
+
}
|
|
1182
|
+
const uname = `${nameList[j][i]}_${k}_${p}`;
|
|
1183
|
+
const structName = this.generateStructureName();
|
|
1184
|
+
const t = getCurrentProgramBuilder().internalDefineStruct(structName, layoutList[j], maskList[i], false, ...allLists[p].map((val)=>val.member));
|
|
1185
|
+
if (maskList[i] & ShaderType.Vertex) {
|
|
1186
|
+
const exp = t();
|
|
1187
|
+
if (j === 0) {
|
|
1188
|
+
exp.uniformBuffer(Number(k));
|
|
1189
|
+
} else {
|
|
1190
|
+
exp.storageBuffer(Number(k));
|
|
1191
|
+
}
|
|
1192
|
+
globalScopeVertex[uname] = exp;
|
|
1193
|
+
}
|
|
1194
|
+
if (maskList[i] & ShaderType.Fragment) {
|
|
1195
|
+
const exp = t();
|
|
1196
|
+
if (j === 0) {
|
|
1197
|
+
exp.uniformBuffer(Number(k));
|
|
1198
|
+
} else {
|
|
1199
|
+
exp.storageBuffer(Number(k));
|
|
1200
|
+
}
|
|
1201
|
+
globalScopeFragmet[uname] = exp;
|
|
1202
|
+
}
|
|
1203
|
+
const index = this._uniforms.findIndex((val)=>val.block?.name === uname);
|
|
1204
|
+
this._uniforms[index].mask = maskList[i];
|
|
1205
|
+
let nameMap = this._nameMap[Number(k)];
|
|
1206
|
+
if (!nameMap) {
|
|
1207
|
+
nameMap = {};
|
|
1208
|
+
this._nameMap[Number(k)] = nameMap;
|
|
1209
|
+
}
|
|
1210
|
+
let writable = false;
|
|
1211
|
+
for(let n = allLists[p].length - 1; n >= 0; n--){
|
|
1212
|
+
const u = allLists[p][n];
|
|
1213
|
+
const exp = this._uniforms[u.uniform].block.exp;
|
|
1214
|
+
nameMap[exp.$str] = uname;
|
|
1215
|
+
exp.$str = `${uname}.${exp.$str}`;
|
|
1216
|
+
writable ||= exp.$ast.isWritable();
|
|
1217
|
+
}
|
|
1218
|
+
if (writable) {
|
|
1219
|
+
if (maskList[i] & ShaderType.Vertex) {
|
|
1220
|
+
globalScopeVertex[uname].$ast.markWritable();
|
|
1221
|
+
} else {
|
|
1222
|
+
globalScopeFragmet[uname].$ast.markWritable();
|
|
1223
|
+
}
|
|
1224
|
+
}
|
|
1225
|
+
}
|
|
1226
|
+
}
|
|
1227
|
+
}
|
|
1228
|
+
}
|
|
1229
|
+
}
|
|
1230
|
+
this._uniforms = this._uniforms.filter((val)=>{
|
|
1231
|
+
return !val.block || val.block.exp.$typeinfo.isStructType() && val.block.exp.$isBuffer;
|
|
1232
|
+
/*
|
|
1233
|
+
if (!val.block) {
|
|
1234
|
+
return true;
|
|
1235
|
+
}
|
|
1236
|
+
const type = val.block.exp.$ast.getType();
|
|
1237
|
+
return (
|
|
1238
|
+
type.isTextureType() ||
|
|
1239
|
+
type.isSamplerType() ||
|
|
1240
|
+
(type.isStructType() && (type.detail.layout === 'std140' || type.detail.layout === 'std430'))
|
|
1241
|
+
);
|
|
1242
|
+
*/ });
|
|
1243
|
+
}
|
|
1244
|
+
/** @internal */ updateUniformBindings(scopes, shaderTypes) {
|
|
1245
|
+
this._uniforms = this._uniforms.filter((val)=>!!val.mask);
|
|
1246
|
+
const bindings = Array.from({
|
|
1247
|
+
length: MAX_BINDING_GROUPS
|
|
1248
|
+
}).fill(0);
|
|
1249
|
+
for (const u of this._uniforms){
|
|
1250
|
+
u.binding = bindings[u.group]++;
|
|
1251
|
+
}
|
|
1252
|
+
for(let i = 0; i < scopes.length; i++){
|
|
1253
|
+
const scope = scopes[i];
|
|
1254
|
+
const type = shaderTypes[i];
|
|
1255
|
+
for (const u of this._uniforms){
|
|
1256
|
+
if (u.mask & type) {
|
|
1257
|
+
const uniforms = scope.$ast.uniforms;
|
|
1258
|
+
const name = u.block ? u.block.name : u.texture ? u.texture.exp.$str : u.sampler.$str;
|
|
1259
|
+
const index = uniforms.findIndex((val)=>val.value.name === name);
|
|
1260
|
+
if (index < 0) {
|
|
1261
|
+
throw new Error(`updateUniformBindings() failed: unable to find uniform ${name}`);
|
|
1262
|
+
}
|
|
1263
|
+
uniforms[index].binding = u.binding;
|
|
1264
|
+
}
|
|
1265
|
+
}
|
|
1266
|
+
}
|
|
1267
|
+
}
|
|
1268
|
+
/** @internal */ createBindGroupLayouts(label) {
|
|
1269
|
+
const layouts = [];
|
|
1270
|
+
for (const uniformInfo of this._uniforms){
|
|
1271
|
+
let layout = layouts[uniformInfo.group];
|
|
1272
|
+
if (!layout) {
|
|
1273
|
+
layout = {
|
|
1274
|
+
label: `${label || 'unknown'}[${uniformInfo.group}]`,
|
|
1275
|
+
entries: []
|
|
1276
|
+
};
|
|
1277
|
+
if (this._nameMap[uniformInfo.group]) {
|
|
1278
|
+
layout.nameMap = this._nameMap[uniformInfo.group];
|
|
1279
|
+
}
|
|
1280
|
+
layouts[uniformInfo.group] = layout;
|
|
1281
|
+
}
|
|
1282
|
+
const entry = {
|
|
1283
|
+
binding: uniformInfo.binding,
|
|
1284
|
+
visibility: uniformInfo.mask,
|
|
1285
|
+
type: null,
|
|
1286
|
+
name: ''
|
|
1287
|
+
};
|
|
1288
|
+
if (uniformInfo.block) {
|
|
1289
|
+
entry.type = uniformInfo.block.exp.$typeinfo.clone(this.getBlockName(uniformInfo.block.name));
|
|
1290
|
+
const isStorage = uniformInfo.block.exp.$declareType === DeclareType.DECLARE_TYPE_STORAGE;
|
|
1291
|
+
entry.buffer = {
|
|
1292
|
+
type: isStorage ? uniformInfo.block.exp.$ast.isWritable() ? 'storage' : 'read-only-storage' : 'uniform',
|
|
1293
|
+
hasDynamicOffset: uniformInfo.block.dynamicOffset,
|
|
1294
|
+
uniformLayout: entry.type.toBufferLayout(0, entry.type.layout)
|
|
1295
|
+
};
|
|
1296
|
+
entry.name = uniformInfo.block.name;
|
|
1297
|
+
} else if (uniformInfo.texture) {
|
|
1298
|
+
entry.type = uniformInfo.texture.exp.$typeinfo;
|
|
1299
|
+
if (!entry.type.isTextureType()) {
|
|
1300
|
+
throw new Error('internal error');
|
|
1301
|
+
}
|
|
1302
|
+
if (entry.type.isStorageTexture()) {
|
|
1303
|
+
entry.storageTexture = {
|
|
1304
|
+
access: 'write-only',
|
|
1305
|
+
viewDimension: entry.type.is1DTexture() ? '1d' : '2d',
|
|
1306
|
+
format: entry.type.storageTexelFormat
|
|
1307
|
+
};
|
|
1308
|
+
} else if (entry.type.isExternalTexture()) {
|
|
1309
|
+
entry.externalTexture = {
|
|
1310
|
+
autoBindSampler: uniformInfo.texture.autoBindSampler ? genSamplerName(uniformInfo.texture.exp.$str, false) : null
|
|
1311
|
+
};
|
|
1312
|
+
} else {
|
|
1313
|
+
const sampleType = this._device.type === 'webgpu' ? uniformInfo.texture.exp.$sampleType : uniformInfo.texture.autoBindSampler && entry.type.isDepthTexture() ? 'float' : uniformInfo.texture.exp.$sampleType;
|
|
1314
|
+
let viewDimension;
|
|
1315
|
+
if (entry.type.isArrayTexture()) {
|
|
1316
|
+
viewDimension = entry.type.isCubeTexture() ? 'cube-array' : '2d-array';
|
|
1317
|
+
} else if (entry.type.is3DTexture()) {
|
|
1318
|
+
viewDimension = '3d';
|
|
1319
|
+
} else if (entry.type.isCubeTexture()) {
|
|
1320
|
+
viewDimension = 'cube';
|
|
1321
|
+
} else if (entry.type.is1DTexture()) {
|
|
1322
|
+
viewDimension = '1d';
|
|
1323
|
+
} else {
|
|
1324
|
+
viewDimension = '2d';
|
|
1325
|
+
}
|
|
1326
|
+
entry.texture = {
|
|
1327
|
+
sampleType: sampleType,
|
|
1328
|
+
viewDimension: viewDimension,
|
|
1329
|
+
multisampled: false,
|
|
1330
|
+
autoBindSampler: null,
|
|
1331
|
+
autoBindSamplerComparison: null
|
|
1332
|
+
};
|
|
1333
|
+
if (this._device.type === 'webgpu' || uniformInfo.texture.autoBindSampler === 'sample') {
|
|
1334
|
+
entry.texture.autoBindSampler = genSamplerName(uniformInfo.texture.exp.$str, false);
|
|
1335
|
+
}
|
|
1336
|
+
if (this._device.type === 'webgpu' && entry.type.isDepthTexture() || uniformInfo.texture.autoBindSampler === 'comparison') {
|
|
1337
|
+
entry.texture.autoBindSamplerComparison = genSamplerName(uniformInfo.texture.exp.$str, true);
|
|
1338
|
+
}
|
|
1339
|
+
}
|
|
1340
|
+
entry.name = uniformInfo.texture.exp.$str;
|
|
1341
|
+
} else if (uniformInfo.sampler) {
|
|
1342
|
+
entry.type = uniformInfo.sampler.$typeinfo;
|
|
1343
|
+
if (!entry.type.isSamplerType()) {
|
|
1344
|
+
throw new Error('internal error');
|
|
1345
|
+
}
|
|
1346
|
+
entry.sampler = {
|
|
1347
|
+
type: entry.type.accessMode === PBSamplerAccessMode.SAMPLE ? uniformInfo.sampler.$sampleType === 'float' ? 'filtering' : 'non-filtering' : 'comparison'
|
|
1348
|
+
};
|
|
1349
|
+
entry.name = uniformInfo.sampler.$str;
|
|
1350
|
+
} else {
|
|
1351
|
+
throw new PBInternalError('invalid uniform entry type');
|
|
1352
|
+
}
|
|
1353
|
+
layout.entries.push(entry);
|
|
1354
|
+
}
|
|
1355
|
+
for(let i = 0; i < layouts.length; i++){
|
|
1356
|
+
if (!layouts[i]) {
|
|
1357
|
+
layouts[i] = {
|
|
1358
|
+
label: `${label || 'unknown'}[${i}]`,
|
|
1359
|
+
entries: []
|
|
1360
|
+
};
|
|
1361
|
+
}
|
|
1362
|
+
}
|
|
1363
|
+
return layouts;
|
|
1364
|
+
}
|
|
1365
|
+
/** @internal */ _getFunctionOverload(funcName, args) {
|
|
1366
|
+
const thisArgs = args.filter((val)=>{
|
|
1367
|
+
if (val instanceof PBShaderExp) {
|
|
1368
|
+
const type = val.$ast.getType();
|
|
1369
|
+
if (type.isStructType() && this._structInfo[this._shaderType]?.types.findIndex((t)=>t.type.structName === type.structName) < 0) {
|
|
1370
|
+
return false;
|
|
1371
|
+
}
|
|
1372
|
+
}
|
|
1373
|
+
return true;
|
|
1374
|
+
});
|
|
1375
|
+
const fn = this.getGlobalScope().$getFunctions(funcName);
|
|
1376
|
+
return fn ? this._matchFunctionOverloading(fn, thisArgs) : null;
|
|
1377
|
+
}
|
|
1378
|
+
/** @internal */ _matchFunctionOverloading(overloadings, args) {
|
|
1379
|
+
for (const overload of overloadings){
|
|
1380
|
+
if (args.length !== overload.funcType.argTypes.length) {
|
|
1381
|
+
continue;
|
|
1382
|
+
}
|
|
1383
|
+
const result = [];
|
|
1384
|
+
let matches = true;
|
|
1385
|
+
for(let i = 0; i < args.length; i++){
|
|
1386
|
+
const argInfo = overload.funcType.argTypes[i];
|
|
1387
|
+
const argType = argInfo.byRef && argInfo.type instanceof PBPointerTypeInfo ? argInfo.type.pointerType : argInfo.type;
|
|
1388
|
+
const arg = args[i];
|
|
1389
|
+
if (typeof arg === 'boolean') {
|
|
1390
|
+
if (!argType.isPrimitiveType() || argType.primitiveType !== PBPrimitiveType.BOOL) {
|
|
1391
|
+
matches = false;
|
|
1392
|
+
break;
|
|
1393
|
+
}
|
|
1394
|
+
result.push(new ASTScalar(arg, typeBool));
|
|
1395
|
+
} else if (typeof arg === 'number') {
|
|
1396
|
+
if (!argType.isPrimitiveType() || !argType.isScalarType() || argType.scalarType === PBPrimitiveType.BOOL) {
|
|
1397
|
+
matches = false;
|
|
1398
|
+
break;
|
|
1399
|
+
}
|
|
1400
|
+
if (argType.scalarType === PBPrimitiveType.I32) {
|
|
1401
|
+
if (!Number.isInteger(arg) || arg < 0x80000000 >> 0 || arg > 0x7fffffff) {
|
|
1402
|
+
matches = false;
|
|
1403
|
+
break;
|
|
1404
|
+
}
|
|
1405
|
+
result.push(new ASTScalar(arg, typeI32));
|
|
1406
|
+
} else if (argType.scalarType === PBPrimitiveType.U32) {
|
|
1407
|
+
if (!Number.isInteger(arg) || arg < 0 || arg > 0xffffffff) {
|
|
1408
|
+
matches = false;
|
|
1409
|
+
break;
|
|
1410
|
+
}
|
|
1411
|
+
result.push(new ASTScalar(arg, typeU32));
|
|
1412
|
+
} else {
|
|
1413
|
+
result.push(new ASTScalar(arg, argType));
|
|
1414
|
+
}
|
|
1415
|
+
} else {
|
|
1416
|
+
if (!argType.isCompatibleType(arg.$ast.getType())) {
|
|
1417
|
+
matches = false;
|
|
1418
|
+
break;
|
|
1419
|
+
}
|
|
1420
|
+
result.push(arg.$ast);
|
|
1421
|
+
}
|
|
1422
|
+
}
|
|
1423
|
+
if (matches) {
|
|
1424
|
+
return [
|
|
1425
|
+
overload,
|
|
1426
|
+
result
|
|
1427
|
+
];
|
|
1428
|
+
}
|
|
1429
|
+
}
|
|
1430
|
+
return null;
|
|
1431
|
+
}
|
|
1432
|
+
/** @internal */ $callFunction(funcName, args, func) {
|
|
1433
|
+
if (this.getCurrentScope() === this.getGlobalScope()) {
|
|
1434
|
+
throw new PBNonScopedFunctionCall(funcName);
|
|
1435
|
+
}
|
|
1436
|
+
const exp = new PBShaderExp('', func.returnType);
|
|
1437
|
+
exp.$ast = new ASTCallFunction(funcName, args, func, getCurrentProgramBuilder().getDevice().type);
|
|
1438
|
+
this.getCurrentScope().$ast.statements.push(exp.$ast);
|
|
1439
|
+
return exp;
|
|
1440
|
+
}
|
|
1441
|
+
/** @internal */ $callFunctionNoCheck(funcName, args, retType) {
|
|
1442
|
+
if (this.getCurrentScope() === this.getGlobalScope()) {
|
|
1443
|
+
throw new PBNonScopedFunctionCall(funcName);
|
|
1444
|
+
}
|
|
1445
|
+
const exp = new PBShaderExp('', retType);
|
|
1446
|
+
exp.$ast = new ASTCallFunction(funcName, args, null, getCurrentProgramBuilder().getDevice().type, retType);
|
|
1447
|
+
this.getCurrentScope().$ast.statements.push(exp.$ast);
|
|
1448
|
+
return exp;
|
|
1449
|
+
}
|
|
1450
|
+
}
|
|
1451
|
+
/**
|
|
1452
|
+
* Base class for scope of the shader program
|
|
1453
|
+
* @public
|
|
1454
|
+
*/ class PBScope extends Proxiable {
|
|
1455
|
+
/** @internal */ $_variables;
|
|
1456
|
+
/** @internal */ $_parentScope;
|
|
1457
|
+
/** @internal */ $_AST;
|
|
1458
|
+
/** @internal */ $_localScope;
|
|
1459
|
+
/** @internal */ constructor(astScope, parent){
|
|
1460
|
+
super();
|
|
1461
|
+
this.$_parentScope = parent || null;
|
|
1462
|
+
this.$_variables = {};
|
|
1463
|
+
this.$_AST = astScope;
|
|
1464
|
+
this.$_localScope = null;
|
|
1465
|
+
}
|
|
1466
|
+
/** Get the program builder */ get $builder() {
|
|
1467
|
+
return getCurrentProgramBuilder();
|
|
1468
|
+
}
|
|
1469
|
+
/** Returns the scope of the builtin variables */ get $builtins() {
|
|
1470
|
+
return getCurrentProgramBuilder().builtinScope;
|
|
1471
|
+
}
|
|
1472
|
+
/** Returns the scope of the input variables */ get $inputs() {
|
|
1473
|
+
return getCurrentProgramBuilder().inputScope;
|
|
1474
|
+
}
|
|
1475
|
+
/** Returns the scope of the output variables */ get $outputs() {
|
|
1476
|
+
return getCurrentProgramBuilder().outputScope;
|
|
1477
|
+
}
|
|
1478
|
+
/** @internal */ get $parent() {
|
|
1479
|
+
return this.$_parentScope;
|
|
1480
|
+
}
|
|
1481
|
+
/** @internal */ get $ast() {
|
|
1482
|
+
return this.$_AST;
|
|
1483
|
+
}
|
|
1484
|
+
/** @internal */ set $ast(ast) {
|
|
1485
|
+
this.$_AST = ast;
|
|
1486
|
+
}
|
|
1487
|
+
/**
|
|
1488
|
+
* Get the input vertex attribute by specified semantic
|
|
1489
|
+
*
|
|
1490
|
+
* @remarks
|
|
1491
|
+
* Can only be called only in vertex shader
|
|
1492
|
+
*
|
|
1493
|
+
* @param semantic - The vertex semantic
|
|
1494
|
+
* @returns The input vertex attribute or null if not exists
|
|
1495
|
+
*/ $getVertexAttrib(semantic) {
|
|
1496
|
+
return getCurrentProgramBuilder().getReflection().attribute(semantic);
|
|
1497
|
+
}
|
|
1498
|
+
/** Get the current local scope */ get $l() {
|
|
1499
|
+
return this.$_getLocalScope();
|
|
1500
|
+
}
|
|
1501
|
+
/** Get the global scope */ get $g() {
|
|
1502
|
+
return this.$_getGlobalScope();
|
|
1503
|
+
}
|
|
1504
|
+
/** @internal */ $local(variable, init) {
|
|
1505
|
+
const initNonArray = getCurrentProgramBuilder().normalizeExpValue(init);
|
|
1506
|
+
variable.$global = this instanceof PBGlobalScope;
|
|
1507
|
+
this.$_declare(variable, initNonArray);
|
|
1508
|
+
}
|
|
1509
|
+
/** @internal */ $touch(exp) {
|
|
1510
|
+
this.$ast.statements.push(new ASTTouch(exp.$ast));
|
|
1511
|
+
}
|
|
1512
|
+
/**
|
|
1513
|
+
* Query the global variable by the name
|
|
1514
|
+
* @param name - Name of the variable
|
|
1515
|
+
* @returns The variable or null if not exists
|
|
1516
|
+
*/ $query(name) {
|
|
1517
|
+
return this.$builder.getReflection().tag(name);
|
|
1518
|
+
}
|
|
1519
|
+
/** @internal */ $_declareInternal(variable, init) {
|
|
1520
|
+
const key = variable.$str;
|
|
1521
|
+
if (this.$_variables[key]) {
|
|
1522
|
+
throw new Error(`cannot re-declare variable '${key}'`);
|
|
1523
|
+
}
|
|
1524
|
+
if (!(variable.$ast instanceof ASTPrimitive)) {
|
|
1525
|
+
throw new Error(`invalid variable declaration: '${variable.$ast.toString(getCurrentProgramBuilder().getDevice().type)}'`);
|
|
1526
|
+
}
|
|
1527
|
+
const varType = variable.$typeinfo;
|
|
1528
|
+
if (varType.isPointerType()) {
|
|
1529
|
+
if (!init) {
|
|
1530
|
+
throw new Error(`cannot declare pointer type variable without initialization: '${variable.$str}'`);
|
|
1531
|
+
}
|
|
1532
|
+
if (!(init instanceof PBShaderExp)) {
|
|
1533
|
+
throw new Error(`invalid initialization for pointer type declaration: '${variable.$str}`);
|
|
1534
|
+
}
|
|
1535
|
+
const initType = init.$ast.getType();
|
|
1536
|
+
if (!initType.isPointerType() || !varType.pointerType.isCompatibleType(initType.pointerType)) {
|
|
1537
|
+
throw new Error(`incompatible pointer type assignment: '${variable.$str}'`);
|
|
1538
|
+
}
|
|
1539
|
+
variable.$typeinfo = initType;
|
|
1540
|
+
}
|
|
1541
|
+
this.$_registerVar(variable, key);
|
|
1542
|
+
if (init === undefined || init === null) {
|
|
1543
|
+
return new ASTDeclareVar(variable.$ast);
|
|
1544
|
+
} else {
|
|
1545
|
+
if (init instanceof PBShaderExp && init.$ast instanceof ASTShaderExpConstructor && init.$ast.args.length === 0) {
|
|
1546
|
+
if (!init.$ast.getType().isCompatibleType(variable.$ast.getType())) {
|
|
1547
|
+
throw new PBTypeCastError(init, init.$ast.getType(), variable.$ast.getType());
|
|
1548
|
+
}
|
|
1549
|
+
return new ASTDeclareVar(variable.$ast);
|
|
1550
|
+
} else {
|
|
1551
|
+
return new ASTAssignment(new ASTLValueDeclare(variable.$ast), init instanceof PBShaderExp ? init.$ast : init);
|
|
1552
|
+
}
|
|
1553
|
+
}
|
|
1554
|
+
}
|
|
1555
|
+
/** @internal */ $_findOrSetUniform(variable) {
|
|
1556
|
+
const name = variable.$str;
|
|
1557
|
+
const uniformInfo = {
|
|
1558
|
+
group: variable.$group,
|
|
1559
|
+
binding: 0,
|
|
1560
|
+
mask: 0
|
|
1561
|
+
};
|
|
1562
|
+
if (variable.$typeinfo.isTextureType()) {
|
|
1563
|
+
uniformInfo.texture = {
|
|
1564
|
+
autoBindSampler: null,
|
|
1565
|
+
exp: variable
|
|
1566
|
+
};
|
|
1567
|
+
} else if (variable.$typeinfo.isSamplerType()) {
|
|
1568
|
+
uniformInfo.sampler = variable;
|
|
1569
|
+
} else {
|
|
1570
|
+
uniformInfo.block = {
|
|
1571
|
+
name: name,
|
|
1572
|
+
dynamicOffset: false,
|
|
1573
|
+
exp: variable
|
|
1574
|
+
};
|
|
1575
|
+
// throw new Error(`unsupported uniform type: ${name}`);
|
|
1576
|
+
}
|
|
1577
|
+
let found = false;
|
|
1578
|
+
for (const u of getCurrentProgramBuilder()._uniforms){
|
|
1579
|
+
if (u.group !== uniformInfo.group) {
|
|
1580
|
+
continue;
|
|
1581
|
+
}
|
|
1582
|
+
if (uniformInfo.block && u.block && u.block.name === uniformInfo.block.name && u.block.exp.$typeinfo.isCompatibleType(uniformInfo.block.exp.$typeinfo)) {
|
|
1583
|
+
u.mask |= getCurrentProgramBuilder().shaderType;
|
|
1584
|
+
variable = u.block.exp;
|
|
1585
|
+
// u.block.exp = variable;
|
|
1586
|
+
found = true;
|
|
1587
|
+
break;
|
|
1588
|
+
}
|
|
1589
|
+
if (uniformInfo.texture && u.texture && uniformInfo.texture.exp.$str === u.texture.exp.$str && uniformInfo.texture.exp.$typeinfo.isCompatibleType(u.texture.exp.$typeinfo)) {
|
|
1590
|
+
u.mask |= getCurrentProgramBuilder().shaderType;
|
|
1591
|
+
variable = u.texture.exp;
|
|
1592
|
+
// u.texture.exp = variable;
|
|
1593
|
+
found = true;
|
|
1594
|
+
break;
|
|
1595
|
+
}
|
|
1596
|
+
if (uniformInfo.sampler && u.sampler && uniformInfo.sampler.$str === u.sampler.$str && uniformInfo.sampler.$typeinfo.isCompatibleType(u.sampler.$typeinfo)) {
|
|
1597
|
+
u.mask |= getCurrentProgramBuilder().shaderType;
|
|
1598
|
+
variable = u.sampler;
|
|
1599
|
+
// u.sampler = variable;
|
|
1600
|
+
found = true;
|
|
1601
|
+
break;
|
|
1602
|
+
}
|
|
1603
|
+
}
|
|
1604
|
+
if (!found) {
|
|
1605
|
+
uniformInfo.mask = getCurrentProgramBuilder().shaderType;
|
|
1606
|
+
getCurrentProgramBuilder()._uniforms.push(uniformInfo);
|
|
1607
|
+
}
|
|
1608
|
+
if (uniformInfo.texture && !uniformInfo.texture.exp.$typeinfo.isStorageTexture() && getCurrentProgramBuilder().getDevice().type === 'webgpu') {
|
|
1609
|
+
// webgpu requires explicit sampler bindings
|
|
1610
|
+
const isDepth = variable.$typeinfo.isTextureType() && variable.$typeinfo.isDepthTexture();
|
|
1611
|
+
const samplerName = genSamplerName(variable.$str, false);
|
|
1612
|
+
const samplerExp = getCurrentProgramBuilder().sampler(samplerName).uniform(uniformInfo.group).sampleType(variable.$sampleType);
|
|
1613
|
+
samplerExp.$sampleType = variable.$sampleType;
|
|
1614
|
+
this.$local(samplerExp);
|
|
1615
|
+
if (isDepth) {
|
|
1616
|
+
const samplerNameComp = genSamplerName(variable.$str, true);
|
|
1617
|
+
const samplerExpComp = getCurrentProgramBuilder().samplerComparison(samplerNameComp).uniform(uniformInfo.group).sampleType(variable.$sampleType);
|
|
1618
|
+
this.$local(samplerExpComp);
|
|
1619
|
+
}
|
|
1620
|
+
}
|
|
1621
|
+
return variable;
|
|
1622
|
+
}
|
|
1623
|
+
/** @internal */ $_declare(variable, init) {
|
|
1624
|
+
if (this.$_variables[variable.$str]) {
|
|
1625
|
+
throw new PBASTError(variable.$ast, 'cannot re-declare variable');
|
|
1626
|
+
}
|
|
1627
|
+
if (variable.$declareType === DeclareType.DECLARE_TYPE_UNIFORM || variable.$declareType === DeclareType.DECLARE_TYPE_STORAGE) {
|
|
1628
|
+
const name = variable.$ast.name;
|
|
1629
|
+
if (!(this instanceof PBGlobalScope)) {
|
|
1630
|
+
throw new Error(`uniform or storage variables can only be declared within global scope: ${name}`);
|
|
1631
|
+
}
|
|
1632
|
+
if (variable.$declareType === DeclareType.DECLARE_TYPE_UNIFORM && !variable.$typeinfo.isTextureType() && !variable.$typeinfo.isSamplerType() && (!variable.$typeinfo.isConstructible() || !variable.$typeinfo.isHostSharable())) {
|
|
1633
|
+
throw new PBASTError(variable.$ast, `type '${variable.$typeinfo.toTypeName(getCurrentProgramBuilder().getDevice().type)}' cannot be declared in uniform address space`);
|
|
1634
|
+
}
|
|
1635
|
+
if (variable.$declareType === DeclareType.DECLARE_TYPE_STORAGE) {
|
|
1636
|
+
if (getCurrentProgramBuilder().getDevice().type !== 'webgpu') {
|
|
1637
|
+
throw new PBDeviceNotSupport('storage buffer binding');
|
|
1638
|
+
} else if (!variable.$typeinfo.isHostSharable()) {
|
|
1639
|
+
throw new PBASTError(variable.$ast, `type '${variable.$typeinfo.toTypeName(getCurrentProgramBuilder().getDevice().type)}' cannot be declared in storage address space`);
|
|
1640
|
+
}
|
|
1641
|
+
}
|
|
1642
|
+
/*
|
|
1643
|
+
if (
|
|
1644
|
+
variable.$declareType === AST.DeclareType.DECLARE_TYPE_STORAGE &&
|
|
1645
|
+
(variable.$typeinfo.isPrimitiveType() || variable.$typeinfo.isArrayType() || variable.$typeinfo.isAtomicI32() || variable.$typeinfo.isAtomicU32())
|
|
1646
|
+
) {
|
|
1647
|
+
originalType = variable.$typeinfo as PBPrimitiveTypeInfo | PBArrayTypeInfo;
|
|
1648
|
+
const wrappedStruct = getCurrentProgramBuilder().defineStruct(null, new PBShaderExp('value', originalType));
|
|
1649
|
+
variable.$typeinfo = wrappedStruct().$typeinfo;
|
|
1650
|
+
}
|
|
1651
|
+
*/ variable = this.$_findOrSetUniform(variable);
|
|
1652
|
+
const ast = this.$_declareInternal(variable);
|
|
1653
|
+
ast.group = variable.$group;
|
|
1654
|
+
ast.binding = 0;
|
|
1655
|
+
ast.blockName = getCurrentProgramBuilder().getBlockName(name);
|
|
1656
|
+
const type = variable.$typeinfo;
|
|
1657
|
+
if (type.isStructType() && variable.$isBuffer || type.isTextureType() || type.isSamplerType() || type.isStructType() && (type.detail.layout === 'std140' || type.detail.layout === 'std430')) {
|
|
1658
|
+
this.$ast.uniforms.push(ast);
|
|
1659
|
+
}
|
|
1660
|
+
variable.$tags.forEach((val)=>{
|
|
1661
|
+
getCurrentProgramBuilder().tagShaderExp(()=>variable, val);
|
|
1662
|
+
});
|
|
1663
|
+
} else {
|
|
1664
|
+
const ast = this.$_declareInternal(variable, init);
|
|
1665
|
+
this.$ast.statements.push(ast);
|
|
1666
|
+
}
|
|
1667
|
+
}
|
|
1668
|
+
/** @internal */ $_registerVar(variable, name) {
|
|
1669
|
+
const key = name || variable.$str;
|
|
1670
|
+
const options = {
|
|
1671
|
+
configurable: true,
|
|
1672
|
+
get: function() {
|
|
1673
|
+
return variable;
|
|
1674
|
+
},
|
|
1675
|
+
set: function(val) {
|
|
1676
|
+
getCurrentProgramBuilder().getCurrentScope().$ast.statements.push(new ASTAssignment(new ASTLValueScalar(variable.$ast), val instanceof PBShaderExp ? val.$ast : val));
|
|
1677
|
+
}
|
|
1678
|
+
};
|
|
1679
|
+
Object.defineProperty(this, key, options);
|
|
1680
|
+
this.$_variables[key] = variable;
|
|
1681
|
+
}
|
|
1682
|
+
/** @internal */ $localGet(prop) {
|
|
1683
|
+
if (typeof prop === 'string' && (prop[0] === '$' || prop in this)) {
|
|
1684
|
+
return this[prop];
|
|
1685
|
+
}
|
|
1686
|
+
return undefined;
|
|
1687
|
+
}
|
|
1688
|
+
/** @internal */ $localSet(prop, value) {
|
|
1689
|
+
if (prop[0] === '$' || prop in this) {
|
|
1690
|
+
this[prop] = value;
|
|
1691
|
+
return true;
|
|
1692
|
+
}
|
|
1693
|
+
return false;
|
|
1694
|
+
}
|
|
1695
|
+
/** @internal */ $get(prop) {
|
|
1696
|
+
const ret = this.$localGet(prop);
|
|
1697
|
+
return ret === undefined && this.$_parentScope ? this.$_parentScope.$thisProxy.$get(prop) : ret;
|
|
1698
|
+
}
|
|
1699
|
+
/** @internal */ $set(prop, value) {
|
|
1700
|
+
if (prop[0] === '$') {
|
|
1701
|
+
this[prop] = value;
|
|
1702
|
+
return true;
|
|
1703
|
+
} else {
|
|
1704
|
+
let scope = this;
|
|
1705
|
+
while(scope && !(prop in scope)){
|
|
1706
|
+
scope = scope.$_parentScope;
|
|
1707
|
+
}
|
|
1708
|
+
if (scope) {
|
|
1709
|
+
scope[prop] = value;
|
|
1710
|
+
return true;
|
|
1711
|
+
} else {
|
|
1712
|
+
if (this.$l) {
|
|
1713
|
+
this.$l[prop] = value;
|
|
1714
|
+
return true;
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
}
|
|
1718
|
+
return false;
|
|
1719
|
+
}
|
|
1720
|
+
/** @internal */ $_getLocalScope() {
|
|
1721
|
+
if (!this.$_localScope) {
|
|
1722
|
+
this.$_localScope = new PBLocalScope(this);
|
|
1723
|
+
}
|
|
1724
|
+
return this.$_localScope;
|
|
1725
|
+
}
|
|
1726
|
+
/** @internal */ $_getGlobalScope() {
|
|
1727
|
+
return this.$builder.getGlobalScope();
|
|
1728
|
+
}
|
|
1729
|
+
}
|
|
1730
|
+
/**
|
|
1731
|
+
* The local scope of a shader
|
|
1732
|
+
* @public
|
|
1733
|
+
*/ class PBLocalScope extends PBScope {
|
|
1734
|
+
/** @internal */ $_scope;
|
|
1735
|
+
constructor(scope){
|
|
1736
|
+
super(null, null);
|
|
1737
|
+
this.$_scope = scope;
|
|
1738
|
+
}
|
|
1739
|
+
/** @internal */ $get(prop) {
|
|
1740
|
+
return prop[0] === '$' ? this[prop] : this.$_scope.$localGet(prop);
|
|
1741
|
+
}
|
|
1742
|
+
/** @internal */ $set(prop, value) {
|
|
1743
|
+
if (prop[0] === '$') {
|
|
1744
|
+
this[prop] = value;
|
|
1745
|
+
return true;
|
|
1746
|
+
}
|
|
1747
|
+
const val = this.$_scope.$localGet(prop);
|
|
1748
|
+
if (val === undefined) {
|
|
1749
|
+
const type = getCurrentProgramBuilder().guessExpValueType(value);
|
|
1750
|
+
if (type.isCompatibleType(typeVoid)) {
|
|
1751
|
+
throw new Error(`Cannot assign void type to '${prop}'`);
|
|
1752
|
+
}
|
|
1753
|
+
const exp = new PBShaderExp(prop, type);
|
|
1754
|
+
if (value instanceof PBShaderExp && !this.$_scope.$parent) {
|
|
1755
|
+
exp.$declareType = value.$declareType;
|
|
1756
|
+
exp.$isBuffer = value.$isBuffer;
|
|
1757
|
+
exp.$group = value.$group;
|
|
1758
|
+
exp.$attrib = value.$attrib;
|
|
1759
|
+
exp.$sampleType = value.$sampleType;
|
|
1760
|
+
exp.$precision = value.$precision;
|
|
1761
|
+
exp.tag(...value.$tags);
|
|
1762
|
+
}
|
|
1763
|
+
this.$_scope.$local(exp, value);
|
|
1764
|
+
return true;
|
|
1765
|
+
} else {
|
|
1766
|
+
return this.$_scope.$localSet(prop, value);
|
|
1767
|
+
}
|
|
1768
|
+
}
|
|
1769
|
+
/** @internal */ $_getLocalScope() {
|
|
1770
|
+
return this;
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
/**
|
|
1774
|
+
* The builtin scope of a shader
|
|
1775
|
+
* @public
|
|
1776
|
+
*/ class PBBuiltinScope extends PBScope {
|
|
1777
|
+
/** @internal */ $_usedBuiltins;
|
|
1778
|
+
/** @internal */ $_builtinVars;
|
|
1779
|
+
constructor(){
|
|
1780
|
+
super(null);
|
|
1781
|
+
this.$_usedBuiltins = new Set();
|
|
1782
|
+
const isWebGPU = getCurrentProgramBuilder().getDevice().type === 'webgpu';
|
|
1783
|
+
if (!isWebGPU) {
|
|
1784
|
+
this.$_builtinVars = {};
|
|
1785
|
+
const v = builtinVariables[getCurrentProgramBuilder().getDevice().type];
|
|
1786
|
+
for(const k in v){
|
|
1787
|
+
const info = v[k];
|
|
1788
|
+
this.$_builtinVars[k] = new PBShaderExp(info.name, info.type);
|
|
1789
|
+
}
|
|
1790
|
+
}
|
|
1791
|
+
const v = builtinVariables[getCurrentProgramBuilder().getDevice().type];
|
|
1792
|
+
const that = this;
|
|
1793
|
+
for (const k of Object.keys(v)){
|
|
1794
|
+
Object.defineProperty(this, k, {
|
|
1795
|
+
get: function() {
|
|
1796
|
+
return that.$getBuiltinVar(k);
|
|
1797
|
+
},
|
|
1798
|
+
set: function(v) {
|
|
1799
|
+
if (typeof v !== 'number' && !(v instanceof PBShaderExp)) {
|
|
1800
|
+
throw new Error(`Invalid output value assignment`);
|
|
1801
|
+
}
|
|
1802
|
+
const exp = that.$getBuiltinVar(k);
|
|
1803
|
+
getCurrentProgramBuilder().getCurrentScope().$ast.statements.push(new ASTAssignment(new ASTLValueScalar(exp.$ast), v instanceof PBShaderExp ? v.$ast : v));
|
|
1804
|
+
}
|
|
1805
|
+
});
|
|
1806
|
+
}
|
|
1807
|
+
}
|
|
1808
|
+
/** @internal */ $_getLocalScope() {
|
|
1809
|
+
return null;
|
|
1810
|
+
}
|
|
1811
|
+
/** @internal */ $getBuiltinVar(name) {
|
|
1812
|
+
const pb = getCurrentProgramBuilder();
|
|
1813
|
+
this.$_usedBuiltins.add(name);
|
|
1814
|
+
const isWebGPU = pb.getDevice().type === 'webgpu';
|
|
1815
|
+
if (isWebGPU) {
|
|
1816
|
+
const v = builtinVariables[pb.getDevice().type];
|
|
1817
|
+
const info = v[name];
|
|
1818
|
+
const inout = info.inOrOut;
|
|
1819
|
+
const structName = inout === 'in' ? getBuiltinInputStructInstanceName(pb.shaderType) : getBuiltinOutputStructInstanceName(pb.shaderType);
|
|
1820
|
+
const scope = pb.getCurrentScope();
|
|
1821
|
+
if (!scope[structName] || !scope[structName][info.name]) {
|
|
1822
|
+
throw new Error(`invalid use of builtin variable ${name}`);
|
|
1823
|
+
}
|
|
1824
|
+
return scope[structName][info.name];
|
|
1825
|
+
} else {
|
|
1826
|
+
if (pb.getDevice().type === 'webgl2' && (name === 'vertexIndex' || name === 'instanceIndex')) {
|
|
1827
|
+
return pb.uint(this.$_builtinVars[name]);
|
|
1828
|
+
} else {
|
|
1829
|
+
return this.$_builtinVars[name];
|
|
1830
|
+
}
|
|
1831
|
+
}
|
|
1832
|
+
}
|
|
1833
|
+
}
|
|
1834
|
+
/**
|
|
1835
|
+
* The input scope of a shader
|
|
1836
|
+
* @public
|
|
1837
|
+
*/ class PBInputScope extends PBScope {
|
|
1838
|
+
/** @internal */ constructor(){
|
|
1839
|
+
super(null);
|
|
1840
|
+
}
|
|
1841
|
+
/** @internal */ $_getLocalScope() {
|
|
1842
|
+
return null;
|
|
1843
|
+
}
|
|
1844
|
+
/** @internal */ $set(prop, value) {
|
|
1845
|
+
if (prop[0] === '$') {
|
|
1846
|
+
this[prop] = value;
|
|
1847
|
+
} else if (prop in this) {
|
|
1848
|
+
throw new Error(`Can not assign to shader input variable: "${prop}"`);
|
|
1849
|
+
} else {
|
|
1850
|
+
const st = getCurrentProgramBuilder().shaderType;
|
|
1851
|
+
if (st !== ShaderType.Vertex) {
|
|
1852
|
+
throw new Error(`shader input variables can only be declared in vertex shader: "${prop}"`);
|
|
1853
|
+
}
|
|
1854
|
+
const attrib = getVertexAttribByName(value.$attrib);
|
|
1855
|
+
if (attrib === undefined) {
|
|
1856
|
+
throw new Error(`can not declare shader input variable: invalid vertex attribute: "${prop}"`);
|
|
1857
|
+
}
|
|
1858
|
+
if (getCurrentProgramBuilder()._vertexAttributes.indexOf(attrib) >= 0) {
|
|
1859
|
+
throw new Error(`can not declare shader input variable: attribute already declared: "${prop}"`);
|
|
1860
|
+
}
|
|
1861
|
+
if (!(value instanceof PBShaderExp) || !(value.$ast instanceof ASTShaderExpConstructor)) {
|
|
1862
|
+
throw new Error(`invalid shader input variable declaration: "${prop}"`);
|
|
1863
|
+
}
|
|
1864
|
+
const type = value.$ast.getType();
|
|
1865
|
+
if (!type.isPrimitiveType() || type.isMatrixType() || type.primitiveType === PBPrimitiveType.BOOL) {
|
|
1866
|
+
throw new Error(`type cannot be used as pipeline input/output: ${prop}`);
|
|
1867
|
+
}
|
|
1868
|
+
const location = getCurrentProgramBuilder()._inputs.length;
|
|
1869
|
+
const exp = new PBShaderExp(`${input_prefix}${prop}`, type).tag(...value.$tags);
|
|
1870
|
+
getCurrentProgramBuilder().in(location, prop, exp);
|
|
1871
|
+
getCurrentProgramBuilder()._vertexAttributes.push(attrib);
|
|
1872
|
+
getCurrentProgramBuilder().getReflection().setAttrib(value.$attrib, exp);
|
|
1873
|
+
// modify input struct for webgpu
|
|
1874
|
+
if (getCurrentProgramBuilder().getDevice().type === 'webgpu') {
|
|
1875
|
+
if (getCurrentProgramBuilder().findStructType(getBuiltinInputStructName(st), st)) {
|
|
1876
|
+
getCurrentProgramBuilder().defineBuiltinStruct(st, 'in');
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
}
|
|
1880
|
+
return true;
|
|
1881
|
+
}
|
|
1882
|
+
}
|
|
1883
|
+
/**
|
|
1884
|
+
* The output scope of a shader
|
|
1885
|
+
* @public
|
|
1886
|
+
*/ class PBOutputScope extends PBScope {
|
|
1887
|
+
constructor(){
|
|
1888
|
+
super(null);
|
|
1889
|
+
}
|
|
1890
|
+
/** @internal */ $_getLocalScope() {
|
|
1891
|
+
return null;
|
|
1892
|
+
}
|
|
1893
|
+
/** @internal */ $set(prop, value) {
|
|
1894
|
+
if (prop[0] === '$' /* || prop in this*/ ) {
|
|
1895
|
+
this[prop] = value;
|
|
1896
|
+
} else {
|
|
1897
|
+
const pb = getCurrentProgramBuilder();
|
|
1898
|
+
if (!(prop in this)) {
|
|
1899
|
+
if (pb.getCurrentScope() === pb.getGlobalScope() && (!(value instanceof PBShaderExp) || !(value.$ast instanceof ASTShaderExpConstructor))) {
|
|
1900
|
+
throw new Error(`invalid shader output variable declaration: ${prop}`);
|
|
1901
|
+
}
|
|
1902
|
+
const type = value.$ast.getType();
|
|
1903
|
+
if (!type.isPrimitiveType() || type.isMatrixType() || type.primitiveType === PBPrimitiveType.BOOL) {
|
|
1904
|
+
throw new Error(`type cannot be used as pipeline input/output: ${prop}`);
|
|
1905
|
+
}
|
|
1906
|
+
const location = pb._outputs.length;
|
|
1907
|
+
pb.out(location, prop, new PBShaderExp(`${pb.shaderKind === 'vertex' ? output_prefix_vs : output_prefix_fs}${prop}`, type).tag(...value.$tags));
|
|
1908
|
+
// modify output struct for webgpu
|
|
1909
|
+
if (getCurrentProgramBuilder().getDevice().type === 'webgpu') {
|
|
1910
|
+
const st = getCurrentProgramBuilder().shaderType;
|
|
1911
|
+
if (getCurrentProgramBuilder().findStructType(getBuiltinInputStructName(st), st)) {
|
|
1912
|
+
getCurrentProgramBuilder().defineBuiltinStruct(st, 'out');
|
|
1913
|
+
}
|
|
1914
|
+
}
|
|
1915
|
+
}
|
|
1916
|
+
if (getCurrentProgramBuilder().getCurrentScope() !== getCurrentProgramBuilder().getGlobalScope()) {
|
|
1917
|
+
const ast = value.$ast;
|
|
1918
|
+
if (!(ast instanceof ASTShaderExpConstructor) || ast.args.length > 0) {
|
|
1919
|
+
this[prop] = value;
|
|
1920
|
+
}
|
|
1921
|
+
}
|
|
1922
|
+
}
|
|
1923
|
+
return true;
|
|
1924
|
+
}
|
|
1925
|
+
}
|
|
1926
|
+
/**
|
|
1927
|
+
* The global scope of a shader
|
|
1928
|
+
* @public
|
|
1929
|
+
*/ class PBGlobalScope extends PBScope {
|
|
1930
|
+
/** @internal */ constructor(){
|
|
1931
|
+
super(new ASTGlobalScope());
|
|
1932
|
+
}
|
|
1933
|
+
/** @internal */ $mainFunc(body) {
|
|
1934
|
+
const pb = getCurrentProgramBuilder();
|
|
1935
|
+
if (pb.getDevice().type === 'webgpu') {
|
|
1936
|
+
const inputStruct = pb.defineBuiltinStruct(pb.shaderType, 'in');
|
|
1937
|
+
this.$local(inputStruct[1]);
|
|
1938
|
+
const isCompute = pb.shaderType === ShaderType.Compute;
|
|
1939
|
+
const outputStruct = isCompute ? null : pb.defineBuiltinStruct(pb.shaderType, 'out');
|
|
1940
|
+
if (!isCompute) {
|
|
1941
|
+
this.$local(outputStruct[1]);
|
|
1942
|
+
}
|
|
1943
|
+
this.$internalCreateFunction('chMainStub', [], false, body);
|
|
1944
|
+
this.$internalCreateFunction('main', inputStruct ? [
|
|
1945
|
+
inputStruct[3]
|
|
1946
|
+
] : [], true, function() {
|
|
1947
|
+
if (inputStruct) {
|
|
1948
|
+
this[inputStruct[1].$str] = this[inputStruct[3].$str];
|
|
1949
|
+
}
|
|
1950
|
+
if (pb.shaderType === ShaderType.Fragment && pb.emulateDepthClamp) {
|
|
1951
|
+
this.$builtins.fragDepth = pb.clamp(this.$inputs.clamppedDepth, 0, 1);
|
|
1952
|
+
}
|
|
1953
|
+
this.chMainStub();
|
|
1954
|
+
if (pb.shaderType === ShaderType.Vertex) {
|
|
1955
|
+
if (pb.depthRangeCorrection) {
|
|
1956
|
+
this.$builtins.position.z = pb.mul(pb.add(this.$builtins.position.z, this.$builtins.position.w), 0.5);
|
|
1957
|
+
}
|
|
1958
|
+
if (pb.emulateDepthClamp) {
|
|
1959
|
+
//z = gl_Position.z / gl_Position.w;
|
|
1960
|
+
//z = (gl_DepthRange.diff * z + gl_DepthRange.near + gl_DepthRange.far) * 0.5;
|
|
1961
|
+
this.$outputs.clamppedDepth = pb.div(this.$builtins.position.z, this.$builtins.position.w);
|
|
1962
|
+
this.$builtins.position.z = 0;
|
|
1963
|
+
}
|
|
1964
|
+
}
|
|
1965
|
+
if (!isCompute) {
|
|
1966
|
+
this.$return(outputStruct[1]);
|
|
1967
|
+
}
|
|
1968
|
+
});
|
|
1969
|
+
} else {
|
|
1970
|
+
this.$internalCreateFunction('main', [], true, function() {
|
|
1971
|
+
if (pb.shaderType === ShaderType.Fragment && pb.emulateDepthClamp) {
|
|
1972
|
+
this.$builtins.fragDepth = pb.clamp(this.$inputs.clamppedDepth, 0, 1);
|
|
1973
|
+
}
|
|
1974
|
+
body?.call(this);
|
|
1975
|
+
if (pb.shaderType === ShaderType.Vertex && pb.emulateDepthClamp) {
|
|
1976
|
+
this.$outputs.clamppedDepth = pb.div(pb.add(pb.div(this.$builtins.position.z, this.$builtins.position.w), 1), 2);
|
|
1977
|
+
this.$builtins.position.z = 0;
|
|
1978
|
+
}
|
|
1979
|
+
});
|
|
1980
|
+
}
|
|
1981
|
+
}
|
|
1982
|
+
/** @internal */ $createFunctionIfNotExists(name, params, body) {
|
|
1983
|
+
{
|
|
1984
|
+
this.$internalCreateFunction(name, params, false, body);
|
|
1985
|
+
}
|
|
1986
|
+
}
|
|
1987
|
+
/** @internal */ $getFunctions(name) {
|
|
1988
|
+
return this.$ast.findFunctions(name);
|
|
1989
|
+
}
|
|
1990
|
+
/** @internal */ $getCurrentFunctionScope() {
|
|
1991
|
+
let scope = getCurrentProgramBuilder().getCurrentScope();
|
|
1992
|
+
while(scope && !(scope instanceof PBFunctionScope)){
|
|
1993
|
+
scope = scope.$parent;
|
|
1994
|
+
}
|
|
1995
|
+
return scope;
|
|
1996
|
+
}
|
|
1997
|
+
/** @internal */ $internalCreateFunction(name, params, isMain, body) {
|
|
1998
|
+
const pb = getCurrentProgramBuilder();
|
|
1999
|
+
params.forEach((param)=>{
|
|
2000
|
+
if (!(param.$ast instanceof ASTPrimitive)) {
|
|
2001
|
+
throw new Error(`${name}(): invalid function definition`);
|
|
2002
|
+
}
|
|
2003
|
+
let ast = param.$ast;
|
|
2004
|
+
if (param.$inout) {
|
|
2005
|
+
if (getCurrentProgramBuilder().getDevice().type === 'webgpu') {
|
|
2006
|
+
param.$typeinfo = new PBPointerTypeInfo(param.$typeinfo, PBAddressSpace.UNKNOWN);
|
|
2007
|
+
}
|
|
2008
|
+
ast = new ASTReferenceOf(param.$ast);
|
|
2009
|
+
}
|
|
2010
|
+
param.$ast = new ASTFunctionParameter(ast);
|
|
2011
|
+
});
|
|
2012
|
+
const overloads = this.$getFunctions(name);
|
|
2013
|
+
const currentFunctionScope = this.$getCurrentFunctionScope();
|
|
2014
|
+
const astFunc = new ASTFunction(name, params.map((val)=>val.$ast), isMain, null, false);
|
|
2015
|
+
if (currentFunctionScope) {
|
|
2016
|
+
const curIndex = this.$ast.statements.indexOf(currentFunctionScope.$ast);
|
|
2017
|
+
if (curIndex < 0) {
|
|
2018
|
+
throw new Error('Internal error');
|
|
2019
|
+
}
|
|
2020
|
+
this.$ast.statements.splice(curIndex, 0, astFunc);
|
|
2021
|
+
} else {
|
|
2022
|
+
this.$ast.statements.push(astFunc);
|
|
2023
|
+
}
|
|
2024
|
+
new PBFunctionScope(this, params, astFunc, body);
|
|
2025
|
+
if (!astFunc.returnType) {
|
|
2026
|
+
astFunc.returnType = typeVoid;
|
|
2027
|
+
}
|
|
2028
|
+
astFunc.funcType = new PBFunctionTypeInfo(astFunc.name, astFunc.returnType, params.map((param)=>{
|
|
2029
|
+
const ast = param.$ast;
|
|
2030
|
+
return ast.paramAST instanceof ASTReferenceOf ? {
|
|
2031
|
+
type: ast.paramAST.value.getType(),
|
|
2032
|
+
byRef: ast.paramAST instanceof ASTReferenceOf
|
|
2033
|
+
} : {
|
|
2034
|
+
type: ast.paramAST.getType(),
|
|
2035
|
+
byRef: false
|
|
2036
|
+
};
|
|
2037
|
+
}));
|
|
2038
|
+
for (const overload of overloads){
|
|
2039
|
+
if (overload.funcType.argHash === astFunc.funcType.argHash) {
|
|
2040
|
+
if (overload.returnType.isCompatibleType(astFunc.returnType)) {
|
|
2041
|
+
// Function signature already exists
|
|
2042
|
+
// console.warn(`Function '${name}' already exists`);
|
|
2043
|
+
this.$ast.statements.splice(this.$ast.statements.indexOf(astFunc), 1);
|
|
2044
|
+
return;
|
|
2045
|
+
} else {
|
|
2046
|
+
throw new Error(`Invalid function overloading: ${name}`);
|
|
2047
|
+
}
|
|
2048
|
+
}
|
|
2049
|
+
}
|
|
2050
|
+
if (overloads.length === 0) {
|
|
2051
|
+
Object.defineProperty(this, name, {
|
|
2052
|
+
get: function() {
|
|
2053
|
+
const func = this.$getFunctions(name);
|
|
2054
|
+
if (func.length === 0) {
|
|
2055
|
+
throw new Error(`function ${name} not found`);
|
|
2056
|
+
}
|
|
2057
|
+
return (...args)=>{
|
|
2058
|
+
const argsNonArray = args.map((val)=>pb.normalizeExpValue(val));
|
|
2059
|
+
const funcType = pb._getFunctionOverload(name, argsNonArray);
|
|
2060
|
+
if (!funcType) {
|
|
2061
|
+
throw new Error(`ERROR: no matching overloads for function ${name}`);
|
|
2062
|
+
}
|
|
2063
|
+
return getCurrentProgramBuilder().$callFunction(name, funcType[1], funcType[0]);
|
|
2064
|
+
};
|
|
2065
|
+
}
|
|
2066
|
+
});
|
|
2067
|
+
}
|
|
2068
|
+
}
|
|
2069
|
+
}
|
|
2070
|
+
/**
|
|
2071
|
+
* Scope that is inside a function
|
|
2072
|
+
* @public
|
|
2073
|
+
*/ class PBInsideFunctionScope extends PBScope {
|
|
2074
|
+
/** @internal */ constructor(parent){
|
|
2075
|
+
super(new ASTScope(), parent);
|
|
2076
|
+
}
|
|
2077
|
+
/**
|
|
2078
|
+
* Creates a 'return' statement
|
|
2079
|
+
* @param retval - The return value
|
|
2080
|
+
*/ $return(retval) {
|
|
2081
|
+
const functionScope = this.findOwnerFunction();
|
|
2082
|
+
const astFunc = functionScope.$ast;
|
|
2083
|
+
let returnType = null;
|
|
2084
|
+
const retValNonArray = getCurrentProgramBuilder().normalizeExpValue(retval);
|
|
2085
|
+
if (retValNonArray !== undefined && retValNonArray !== null) {
|
|
2086
|
+
if (typeof retValNonArray === 'number') {
|
|
2087
|
+
if (astFunc.returnType) {
|
|
2088
|
+
if (astFunc.returnType.isPrimitiveType() && astFunc.returnType.isScalarType() && !astFunc.returnType.isCompatibleType(typeBool)) {
|
|
2089
|
+
returnType = astFunc.returnType;
|
|
2090
|
+
}
|
|
2091
|
+
}
|
|
2092
|
+
if (!returnType) {
|
|
2093
|
+
if (Number.isInteger(retValNonArray)) {
|
|
2094
|
+
if (retValNonArray < 0) {
|
|
2095
|
+
if (retValNonArray < 0x80000000 >> 0) {
|
|
2096
|
+
throw new Error(`function ${astFunc.name}: invalid return value: ${retValNonArray}`);
|
|
2097
|
+
}
|
|
2098
|
+
returnType = typeI32;
|
|
2099
|
+
} else {
|
|
2100
|
+
if (retValNonArray > 0xffffffff) {
|
|
2101
|
+
throw new Error(`function ${astFunc.name}: invalid return value: ${retValNonArray}`);
|
|
2102
|
+
}
|
|
2103
|
+
returnType = retValNonArray <= 0x7fffffff ? typeI32 : typeU32;
|
|
2104
|
+
}
|
|
2105
|
+
} else {
|
|
2106
|
+
returnType = typeF32;
|
|
2107
|
+
}
|
|
2108
|
+
}
|
|
2109
|
+
} else if (typeof retValNonArray === 'boolean') {
|
|
2110
|
+
returnType = typeBool;
|
|
2111
|
+
} else {
|
|
2112
|
+
returnType = retValNonArray.$ast.getType();
|
|
2113
|
+
}
|
|
2114
|
+
} else {
|
|
2115
|
+
returnType = typeVoid;
|
|
2116
|
+
}
|
|
2117
|
+
if (returnType.isPointerType()) {
|
|
2118
|
+
throw new Error('function can not return pointer type');
|
|
2119
|
+
}
|
|
2120
|
+
if (!astFunc.returnType) {
|
|
2121
|
+
astFunc.returnType = returnType;
|
|
2122
|
+
} else if (!astFunc.returnType.isCompatibleType(returnType)) {
|
|
2123
|
+
throw new Error(`function ${astFunc.name}: return type must be ${astFunc.returnType?.toTypeName(getCurrentProgramBuilder().getDevice().type) || 'void'}`);
|
|
2124
|
+
}
|
|
2125
|
+
let returnValue = null;
|
|
2126
|
+
if (retValNonArray !== undefined && retValNonArray !== null) {
|
|
2127
|
+
if (retValNonArray instanceof PBShaderExp) {
|
|
2128
|
+
returnValue = retValNonArray.$ast;
|
|
2129
|
+
} else {
|
|
2130
|
+
if (!returnType.isPrimitiveType() || !returnType.isScalarType()) {
|
|
2131
|
+
throw new PBTypeCastError(retValNonArray, typeof retValNonArray, returnType);
|
|
2132
|
+
}
|
|
2133
|
+
returnValue = new ASTScalar(retValNonArray, returnType);
|
|
2134
|
+
}
|
|
2135
|
+
}
|
|
2136
|
+
this.$ast.statements.push(new ASTReturn(returnValue));
|
|
2137
|
+
}
|
|
2138
|
+
/**
|
|
2139
|
+
* Creates a new scope
|
|
2140
|
+
* @param body - Generator function for the scope
|
|
2141
|
+
* @returns The created scope
|
|
2142
|
+
*/ $scope(body) {
|
|
2143
|
+
const astScope = new ASTNakedScope();
|
|
2144
|
+
this.$ast.statements.push(astScope);
|
|
2145
|
+
return new PBNakedScope(this, astScope, body);
|
|
2146
|
+
}
|
|
2147
|
+
/**
|
|
2148
|
+
* Creates an 'if' statement
|
|
2149
|
+
* @param condition - Condition expression for the if statement
|
|
2150
|
+
* @param body - Generator function for the scope inside the if statement
|
|
2151
|
+
* @returns The scope inside the if statement
|
|
2152
|
+
*/ $if(condition, body) {
|
|
2153
|
+
const astIf = new ASTIf('if', condition instanceof PBShaderExp ? condition.$ast : new ASTScalar(condition, typeof condition === 'number' ? typeF32 : typeBool));
|
|
2154
|
+
this.$ast.statements.push(astIf);
|
|
2155
|
+
return new PBIfScope(this, astIf, body);
|
|
2156
|
+
}
|
|
2157
|
+
/**
|
|
2158
|
+
* Creates a select statement: condition ? first : second
|
|
2159
|
+
* @param condition - Condition expression
|
|
2160
|
+
* @param first - The first value
|
|
2161
|
+
* @param second - The second value
|
|
2162
|
+
* @returns The first value if condition evaluates to true, otherwise returns the second value
|
|
2163
|
+
*/ $choice(condition, first, second) {
|
|
2164
|
+
const ast = new ASTSelect(condition instanceof PBShaderExp ? condition.$ast : condition, first instanceof PBShaderExp ? first.$ast : first, second instanceof PBShaderExp ? second.$ast : second);
|
|
2165
|
+
const exp = new PBShaderExp('', ast.getType());
|
|
2166
|
+
exp.$ast = ast;
|
|
2167
|
+
return exp;
|
|
2168
|
+
}
|
|
2169
|
+
/** Creates a 'break' statement */ $break() {
|
|
2170
|
+
this.$ast.statements.push(new ASTBreak());
|
|
2171
|
+
}
|
|
2172
|
+
/** Creates a 'continue' statement */ $continue() {
|
|
2173
|
+
this.$ast.statements.push(new ASTContinue());
|
|
2174
|
+
}
|
|
2175
|
+
/**
|
|
2176
|
+
* Creates a 'for' statement
|
|
2177
|
+
* @param counter - The repeat counter variable declaration
|
|
2178
|
+
* @param init - initial value of the repeat counter variable
|
|
2179
|
+
* @param end - end value of the counter exclusive
|
|
2180
|
+
* @param body - Generator function for the scope that inside the for statement
|
|
2181
|
+
*/ $for(counter, init, end, body) {
|
|
2182
|
+
const initializerType = counter.$ast.getType();
|
|
2183
|
+
if (!initializerType.isPrimitiveType() || !initializerType.isScalarType()) {
|
|
2184
|
+
throw new PBASTError(counter.$ast, 'invalid for range initializer type');
|
|
2185
|
+
}
|
|
2186
|
+
const initval = init instanceof PBShaderExp ? init.$ast : new ASTScalar(init, initializerType);
|
|
2187
|
+
const astFor = new ASTRange(counter.$ast, initval, end instanceof PBShaderExp ? end.$ast : new ASTScalar(end, initializerType), true);
|
|
2188
|
+
this.$ast.statements.push(astFor);
|
|
2189
|
+
new PBForScope(this, counter, end, astFor, body);
|
|
2190
|
+
}
|
|
2191
|
+
/**
|
|
2192
|
+
* Creates a 'do..while' statement
|
|
2193
|
+
* @param body - Generator function for the scope that inside the do..while statment
|
|
2194
|
+
* @returns The scope that inside the do..while statement
|
|
2195
|
+
*/ $do(body) {
|
|
2196
|
+
const astDoWhile = new ASTDoWhile(null);
|
|
2197
|
+
this.$ast.statements.push(astDoWhile);
|
|
2198
|
+
return new PBDoWhileScope(this, astDoWhile, body);
|
|
2199
|
+
}
|
|
2200
|
+
/**
|
|
2201
|
+
* Creates a 'while' statement
|
|
2202
|
+
* @param condition - Condition expression for the while statement
|
|
2203
|
+
* @param body - Generator function for the scope that inside the while statement
|
|
2204
|
+
*/ $while(condition, body) {
|
|
2205
|
+
const astWhile = new ASTWhile(condition instanceof PBShaderExp ? condition.$ast : new ASTScalar(condition, typeof condition === 'number' ? typeF32 : typeBool));
|
|
2206
|
+
this.$ast.statements.push(astWhile);
|
|
2207
|
+
new PBWhileScope(this, astWhile, body);
|
|
2208
|
+
}
|
|
2209
|
+
/** @internal */ findOwnerFunction() {
|
|
2210
|
+
for(let scope = this; scope; scope = scope.$parent){
|
|
2211
|
+
if (scope instanceof PBFunctionScope) {
|
|
2212
|
+
return scope;
|
|
2213
|
+
}
|
|
2214
|
+
}
|
|
2215
|
+
return null;
|
|
2216
|
+
}
|
|
2217
|
+
}
|
|
2218
|
+
/**
|
|
2219
|
+
* Scope that insides a function
|
|
2220
|
+
* @public
|
|
2221
|
+
*/ class PBFunctionScope extends PBInsideFunctionScope {
|
|
2222
|
+
/** @internal */ $typeinfo;
|
|
2223
|
+
/** @internal */ constructor(parent, params, ast, body){
|
|
2224
|
+
super(parent);
|
|
2225
|
+
this.$ast = ast;
|
|
2226
|
+
for (const param of params){
|
|
2227
|
+
if (this.$_variables[param.$str]) {
|
|
2228
|
+
throw new Error('Duplicate function parameter name is not allowed');
|
|
2229
|
+
}
|
|
2230
|
+
this.$_registerVar(param);
|
|
2231
|
+
}
|
|
2232
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2233
|
+
body && body.call(this);
|
|
2234
|
+
getCurrentProgramBuilder().popScope();
|
|
2235
|
+
}
|
|
2236
|
+
}
|
|
2237
|
+
/**
|
|
2238
|
+
* Scope that insides a while statement
|
|
2239
|
+
* @public
|
|
2240
|
+
*/ class PBWhileScope extends PBInsideFunctionScope {
|
|
2241
|
+
/** @internal */ constructor(parent, ast, body){
|
|
2242
|
+
super(parent);
|
|
2243
|
+
this.$ast = ast;
|
|
2244
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2245
|
+
body && body.call(this);
|
|
2246
|
+
getCurrentProgramBuilder().popScope();
|
|
2247
|
+
}
|
|
2248
|
+
}
|
|
2249
|
+
/**
|
|
2250
|
+
* Scope that insides a do..while statement
|
|
2251
|
+
* @public
|
|
2252
|
+
*/ class PBDoWhileScope extends PBInsideFunctionScope {
|
|
2253
|
+
/** @internal */ constructor(parent, ast, body){
|
|
2254
|
+
super(parent);
|
|
2255
|
+
this.$ast = ast;
|
|
2256
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2257
|
+
body && body.call(this);
|
|
2258
|
+
getCurrentProgramBuilder().popScope();
|
|
2259
|
+
}
|
|
2260
|
+
$while(condition) {
|
|
2261
|
+
this.$ast.condition = condition instanceof PBShaderExp ? condition.$ast : new ASTScalar(condition, typeof condition === 'number' ? typeF32 : typeBool);
|
|
2262
|
+
}
|
|
2263
|
+
}
|
|
2264
|
+
/**
|
|
2265
|
+
* Scope that insides a for statement
|
|
2266
|
+
* @public
|
|
2267
|
+
*/ class PBForScope extends PBInsideFunctionScope {
|
|
2268
|
+
/** @internal */ constructor(parent, counter, count, ast, body){
|
|
2269
|
+
super(parent);
|
|
2270
|
+
this.$ast = ast;
|
|
2271
|
+
this.$_registerVar(counter);
|
|
2272
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2273
|
+
body && body.call(this);
|
|
2274
|
+
getCurrentProgramBuilder().popScope();
|
|
2275
|
+
}
|
|
2276
|
+
}
|
|
2277
|
+
/**
|
|
2278
|
+
* A naked scope
|
|
2279
|
+
* @public
|
|
2280
|
+
*/ class PBNakedScope extends PBInsideFunctionScope {
|
|
2281
|
+
/** @internal */ constructor(parent, ast, body){
|
|
2282
|
+
super(parent);
|
|
2283
|
+
this.$ast = ast;
|
|
2284
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2285
|
+
body && body.call(this);
|
|
2286
|
+
getCurrentProgramBuilder().popScope();
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
/**
|
|
2290
|
+
* Scope that insides an if statement
|
|
2291
|
+
* @public
|
|
2292
|
+
*/ class PBIfScope extends PBInsideFunctionScope {
|
|
2293
|
+
/** @internal */ constructor(parent, ast, body){
|
|
2294
|
+
super(parent);
|
|
2295
|
+
this.$ast = ast;
|
|
2296
|
+
getCurrentProgramBuilder().pushScope(this);
|
|
2297
|
+
body && body.call(this);
|
|
2298
|
+
getCurrentProgramBuilder().popScope();
|
|
2299
|
+
}
|
|
2300
|
+
/**
|
|
2301
|
+
* Creates an 'else if' branch
|
|
2302
|
+
* @param condition - Condition expression for the else if branch
|
|
2303
|
+
* @param body - Generator function for the scope that insides the else if statement
|
|
2304
|
+
* @returns The scope that insides the else if statement
|
|
2305
|
+
*/ $elseif(condition, body) {
|
|
2306
|
+
const astElseIf = new ASTIf('else if', condition instanceof PBShaderExp ? condition.$ast : new ASTScalar(condition, typeof condition === 'number' ? typeF32 : typeBool));
|
|
2307
|
+
this.$ast.nextElse = astElseIf;
|
|
2308
|
+
return new PBIfScope(this.$_parentScope, astElseIf, body);
|
|
2309
|
+
}
|
|
2310
|
+
/**
|
|
2311
|
+
* Creates an 'else' branch
|
|
2312
|
+
* @param body - Generator function for the scope that insides the else statement
|
|
2313
|
+
*/ $else(body) {
|
|
2314
|
+
const astElse = new ASTIf('else', null);
|
|
2315
|
+
this.$ast.nextElse = astElse;
|
|
2316
|
+
new PBIfScope(this.$_parentScope, astElse, body);
|
|
2317
|
+
}
|
|
2318
|
+
}
|
|
2319
|
+
setBuiltinFuncs(ProgramBuilder);
|
|
2320
|
+
setConstructors(ProgramBuilder);
|
|
2321
|
+
|
|
2322
|
+
export { PBBuiltinScope, PBDoWhileScope, PBForScope, PBFunctionScope, PBGlobalScope, PBIfScope, PBInputScope, PBInsideFunctionScope, PBLocalScope, PBNakedScope, PBOutputScope, PBScope, PBWhileScope, ProgramBuilder };
|
|
2323
|
+
//# sourceMappingURL=programbuilder.js.map
|