@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.
Files changed (40) hide show
  1. package/dist/base_types.js +602 -0
  2. package/dist/base_types.js.map +1 -0
  3. package/dist/builder/ast.js +2135 -0
  4. package/dist/builder/ast.js.map +1 -0
  5. package/dist/builder/base.js +477 -0
  6. package/dist/builder/base.js.map +1 -0
  7. package/dist/builder/builtinfunc.js +4101 -0
  8. package/dist/builder/builtinfunc.js.map +1 -0
  9. package/dist/builder/constructors.js +173 -0
  10. package/dist/builder/constructors.js.map +1 -0
  11. package/dist/builder/errors.js +148 -0
  12. package/dist/builder/errors.js.map +1 -0
  13. package/dist/builder/programbuilder.js +2323 -0
  14. package/dist/builder/programbuilder.js.map +1 -0
  15. package/dist/builder/reflection.js +60 -0
  16. package/dist/builder/reflection.js.map +1 -0
  17. package/dist/builder/types.js +1563 -0
  18. package/dist/builder/types.js.map +1 -0
  19. package/dist/device.js +515 -0
  20. package/dist/device.js.map +1 -0
  21. package/dist/gpuobject.js +2047 -0
  22. package/dist/gpuobject.js.map +1 -0
  23. package/dist/helpers/drawtext.js +187 -0
  24. package/dist/helpers/drawtext.js.map +1 -0
  25. package/dist/helpers/font.js +189 -0
  26. package/dist/helpers/font.js.map +1 -0
  27. package/dist/helpers/glyphmanager.js +121 -0
  28. package/dist/helpers/glyphmanager.js.map +1 -0
  29. package/dist/helpers/textureatlas.js +170 -0
  30. package/dist/helpers/textureatlas.js.map +1 -0
  31. package/dist/index.d.ts +3873 -0
  32. package/dist/index.js +16 -0
  33. package/dist/index.js.map +1 -0
  34. package/dist/timer.js +38 -0
  35. package/dist/timer.js.map +1 -0
  36. package/dist/uniformdata.js +147 -0
  37. package/dist/uniformdata.js.map +1 -0
  38. package/dist/vertexdata.js +135 -0
  39. package/dist/vertexdata.js.map +1 -0
  40. 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