UNPKG

@babylonjs/core

Version:

Getting started? Play directly with the Babylon.js API using our [playground](https://playground.babylonjs.com/). It also contains a lot of samples to learn how to use it.

533 lines (532 loc) 29.7 kB
import { WebGPUShaderProcessingContext } from "./webgpuShaderProcessingContext.js"; import { Logger } from "../../Misc/logger.js"; import { WebGPUShaderProcessor } from "./webgpuShaderProcessor.js"; import { RemoveComments, InjectStartingAndEndingCode } from "../../Misc/codeStringParsingTools.js"; import "../../ShadersWGSL/ShadersInclude/bakedVertexAnimationDeclaration.js"; import "../../ShadersWGSL/ShadersInclude/bakedVertexAnimation.js"; import "../../ShadersWGSL/ShadersInclude/instancesDeclaration.js"; import "../../ShadersWGSL/ShadersInclude/instancesVertex.js"; import "../../ShadersWGSL/ShadersInclude/helperFunctions.js"; import "../../ShadersWGSL/ShadersInclude/fresnelFunction.js"; import "../../ShadersWGSL/ShadersInclude/meshUboDeclaration.js"; import "../../ShadersWGSL/ShadersInclude/sceneUboDeclaration.js"; import "../../ShadersWGSL/ShadersInclude/decalFragment.js"; const builtInName_frag_depth = "fragmentOutputs.fragDepth"; const leftOverVarName = "uniforms"; const internalsVarName = "internals"; const gpuTextureViewDimensionByWebGPUTextureFunction = { texture_1d: "1d" /* WebGPUConstants.TextureViewDimension.E1d */, texture_2d: "2d" /* WebGPUConstants.TextureViewDimension.E2d */, texture_2d_array: "2d-array" /* WebGPUConstants.TextureViewDimension.E2dArray */, texture_3d: "3d" /* WebGPUConstants.TextureViewDimension.E3d */, texture_cube: "cube" /* WebGPUConstants.TextureViewDimension.Cube */, texture_cube_array: "cube-array" /* WebGPUConstants.TextureViewDimension.CubeArray */, texture_multisampled_2d: "2d" /* WebGPUConstants.TextureViewDimension.E2d */, texture_depth_2d: "2d" /* WebGPUConstants.TextureViewDimension.E2d */, texture_depth_2d_array: "2d-array" /* WebGPUConstants.TextureViewDimension.E2dArray */, texture_depth_cube: "cube" /* WebGPUConstants.TextureViewDimension.Cube */, texture_depth_cube_array: "cube-array" /* WebGPUConstants.TextureViewDimension.CubeArray */, texture_depth_multisampled_2d: "2d" /* WebGPUConstants.TextureViewDimension.E2d */, texture_storage_1d: "1d" /* WebGPUConstants.TextureViewDimension.E1d */, texture_storage_2d: "2d" /* WebGPUConstants.TextureViewDimension.E2d */, texture_storage_2d_array: "2d-array" /* WebGPUConstants.TextureViewDimension.E2dArray */, texture_storage_3d: "3d" /* WebGPUConstants.TextureViewDimension.E3d */, texture_external: null, }; /** @internal */ export class WebGPUShaderProcessorWGSL extends WebGPUShaderProcessor { constructor() { super(...arguments); this.shaderLanguage = 1 /* ShaderLanguage.WGSL */; this.uniformRegexp = /uniform\s+(\w+)\s*:\s*(.+)\s*;/; this.textureRegexp = /var\s+(\w+)\s*:\s*((array<\s*)?(texture_\w+)\s*(<\s*(.+)\s*>)?\s*(,\s*\w+\s*>\s*)?);/; this.noPrecision = true; this.pureMode = false; } _getArraySize(name, uniformType, preProcessors) { let length = 0; const endArray = uniformType.lastIndexOf(">"); if (uniformType.indexOf("array") >= 0 && endArray > 0) { let startArray = endArray; while (startArray > 0 && uniformType.charAt(startArray) !== " " && uniformType.charAt(startArray) !== ",") { startArray--; } const lengthInString = uniformType.substring(startArray + 1, endArray); length = +lengthInString; if (isNaN(length)) { length = +preProcessors[lengthInString.trim()]; } while (startArray > 0 && (uniformType.charAt(startArray) === " " || uniformType.charAt(startArray) === ",")) { startArray--; } uniformType = uniformType.substring(uniformType.indexOf("<") + 1, startArray + 1); } return [name, uniformType, length]; } initializeShaders(processingContext) { this._webgpuProcessingContext = processingContext; this._attributesInputWGSL = []; this._attributesWGSL = []; this._attributesConversionCodeWGSL = []; this._hasNonFloatAttribute = false; this._varyingsWGSL = []; this._varyingNamesWGSL = []; this._stridedUniformArrays = []; } preProcessShaderCode(code) { // Same check as in webgpuShaderProcessorsGLSL to avoid same ubDelcaration to be injected twice. const ubDeclaration = this.pureMode ? "" : `struct ${WebGPUShaderProcessor.InternalsUBOName} {\n yFactor_: f32,\n textureOutputHeight_: f32,\n};\nvar<uniform> ${internalsVarName} : ${WebGPUShaderProcessor.InternalsUBOName};\n`; const alreadyInjected = code.indexOf(ubDeclaration) !== -1; return alreadyInjected ? code : ubDeclaration + RemoveComments(code); } varyingCheck(varying) { const regex = /(flat|linear|perspective)?\s*(center|centroid|sample)?\s*\bvarying\b/; return regex.test(varying); } varyingProcessor(varying, isFragment, preProcessors) { const varyingRegex = /\s*(flat|linear|perspective)?\s*(center|centroid|sample)?\s*varying\s+(?:(?:highp)?|(?:lowp)?)\s*(\S+)\s*:\s*(.+)\s*;/gm; const match = varyingRegex.exec(varying); if (match !== null) { const interpolationType = match[1] ?? "perspective"; const interpolationSampling = match[2] ?? "center"; const varyingType = match[4]; const name = match[3]; const interpolation = interpolationType === "flat" ? `@interpolate(${interpolationType})` : `@interpolate(${interpolationType}, ${interpolationSampling})`; let location; if (isFragment) { location = this._webgpuProcessingContext.availableVaryings[name]; if (location === undefined) { Logger.Warn(`Invalid fragment shader: The varying named "${name}" is not declared in the vertex shader! This declaration will be ignored.`); } } else { location = this._webgpuProcessingContext.getVaryingNextLocation(varyingType, this._getArraySize(name, varyingType, preProcessors)[2]); this._webgpuProcessingContext.availableVaryings[name] = location; this._varyingsWGSL.push(` @location(${location}) ${interpolation} ${name} : ${varyingType},`); this._varyingNamesWGSL.push(name); } varying = ""; } return varying; } attributeProcessor(attribute, preProcessors) { const attribRegex = /\s*attribute\s+(\S+)\s*:\s*(.+)\s*;/gm; const match = attribRegex.exec(attribute); if (match !== null) { const attributeType = match[2]; const name = match[1]; const location = this._webgpuProcessingContext.getAttributeNextLocation(attributeType, this._getArraySize(name, attributeType, preProcessors)[2]); this._webgpuProcessingContext.availableAttributes[name] = location; this._webgpuProcessingContext.orderedAttributes[location] = name; const numComponents = this._webgpuProcessingContext.vertexBufferKindToNumberOfComponents[name]; if (numComponents !== undefined) { // Special case for an int/ivecX vertex buffer that is used as a float/vecX attribute in the shader. const newType = numComponents < 0 ? (numComponents === -1 ? "i32" : "vec" + -numComponents + "<i32>") : numComponents === 1 ? "u32" : "vec" + numComponents + "<u32>"; const newName = `_int_${name}_`; this._attributesInputWGSL.push(`@location(${location}) ${newName} : ${newType},`); this._attributesWGSL.push(`${name} : ${attributeType},`); this._attributesConversionCodeWGSL.push(`vertexInputs.${name} = ${attributeType}(vertexInputs_.${newName});`); this._hasNonFloatAttribute = true; } else { this._attributesInputWGSL.push(`@location(${location}) ${name} : ${attributeType},`); this._attributesWGSL.push(`${name} : ${attributeType},`); this._attributesConversionCodeWGSL.push(`vertexInputs.${name} = vertexInputs_.${name};`); } attribute = ""; } return attribute; } uniformProcessor(uniform, isFragment, preProcessors) { const match = this.uniformRegexp.exec(uniform); if (match !== null) { const uniformType = match[2]; const name = match[1]; this._addUniformToLeftOverUBO(name, uniformType, preProcessors); uniform = ""; } return uniform; } textureProcessor(texture, isFragment, preProcessors) { const match = this.textureRegexp.exec(texture); if (match !== null) { const name = match[1]; // name of the variable const type = match[2]; // texture_2d<f32> or array<texture_2d_array<f32>, 5> for eg const isArrayOfTexture = !!match[3]; const textureFunc = match[4]; // texture_2d, texture_depth_2d, etc const isStorageTexture = textureFunc.indexOf("storage") > 0; const componentType = match[6]; // f32 or i32 or u32 or undefined const storageTextureFormat = isStorageTexture ? componentType.substring(0, componentType.indexOf(",")).trim() : null; let arraySize = isArrayOfTexture ? this._getArraySize(name, type, preProcessors)[2] : 0; let textureInfo = this._webgpuProcessingContext.availableTextures[name]; if (!textureInfo) { textureInfo = { isTextureArray: arraySize > 0, isStorageTexture, textures: [], sampleType: "float" /* WebGPUConstants.TextureSampleType.Float */, }; arraySize = arraySize || 1; for (let i = 0; i < arraySize; ++i) { textureInfo.textures.push(this._webgpuProcessingContext.getNextFreeUBOBinding()); } } else { arraySize = textureInfo.textures.length; } this._webgpuProcessingContext.availableTextures[name] = textureInfo; const isDepthTexture = textureFunc.indexOf("depth") > 0; const textureDimension = gpuTextureViewDimensionByWebGPUTextureFunction[textureFunc]; const sampleType = isDepthTexture ? "depth" /* WebGPUConstants.TextureSampleType.Depth */ : componentType === "u32" ? "uint" /* WebGPUConstants.TextureSampleType.Uint */ : componentType === "i32" ? "sint" /* WebGPUConstants.TextureSampleType.Sint */ : "float" /* WebGPUConstants.TextureSampleType.Float */; textureInfo.sampleType = sampleType; if (textureDimension === undefined) { // eslint-disable-next-line no-throw-literal throw `Can't get the texture dimension corresponding to the texture function "${textureFunc}"!`; } for (let i = 0; i < arraySize; ++i) { const { groupIndex, bindingIndex } = textureInfo.textures[i]; if (i === 0) { texture = `@group(${groupIndex}) @binding(${bindingIndex}) ${texture}`; } this._addTextureBindingDescription(name, textureInfo, i, textureDimension, storageTextureFormat, !isFragment); } } return texture; } _convertDefinesToConst(preProcessors) { let code = ""; for (const key in preProcessors) { const value = preProcessors[key]; if (key.startsWith("__")) { continue; } if (!isNaN(parseInt(value)) || !isNaN(parseFloat(value))) { code += `const ${key} = ${value};\n`; } else if (key && value === "") { code += `const ${key} = true;\n`; } } return code; } postProcessor(code, _defines, _isFragment, _processingContext, _parameters, preProcessors, preProcessorsFromCode) { // Collect the preprocessor names (coming from a "#define NAME VALUE" declaration) directly defined in the shader code (preProcessorsFromCode) and not defined at the material level (preProcessors). // This is because we will have to perform a replace on the code to replace the defines with their values. // // We don't have to do it for preprocessor names defined at the material level because replacing them by "const NAME = VALUE;" will take care of it (see _convertDefinesToConst()) and is faster than doing a search/replace for each of them. // // The reason why doing "const NAME = VALUE;" doesn't work for preprocessor names defined in the code is that VALUE can be any string and not only numbers or booleans. // So, if we have this code: // // #define vDiffuseUV vMainUV // textureSample(..., fragmentInputs.vDiffuseUV) /// // only a search/replace will work, 'const vDiffuseUV = "vMainUV";' will not work // // Note that the search/replace text processing will also apply to the "#define NAME VALUE" definition itself, so it will become "#define VALUE VALUE" // It's not a problem, though, because all #define XXX will be commented out in the final code. const defineList = []; for (const key in preProcessorsFromCode) { const value = preProcessorsFromCode[key]; // Excludes the defines that are booleans (note that there aren't "false" booleans: we simply don't add them in the preProcessorsFromCode object). // That's because we need (at least some of) them to stay untouched, like #define DISABLE_UNIFORMTY_ANALYSIS or #define CUSTOM_VERTEX_BEGIN (else, they would be replaced with "#define true" after the search/replace processing) if (value !== "true") { defineList.push(key); } } // We must sort the define names by length to avoid replacing a define with a longer name (ex: #define A 1 and #define AB 2, if we replace A with 1, we will have #define 1B 2) // So, we start by longest names and we finish with the shortest ones. defineList.sort((a, b) => (a.length - b.length > 0 ? -1 : a.length === b.length ? 0 : 1)); for (const name of defineList) { // Let's retrieve the value of the define from the code // Note that we can't use the value from preProcessorsFromCode[name] because this value could have been changed from a previous replace // For example: // #define IOR 1.333 // #define ETA 1.0/IOR // // After IOR replacement is performed, we will have: // #define 1.333 1.333 // #define ETA 1.0/1.333 // // but preProcessorsFromCode["ETA"] is still "1.0/IOR" and not "1.0/1.333", so we must retrieve the value for ETA from the current code const i0 = code.indexOf("#define " + name); let i1 = code.indexOf("\n", i0); if (i1 === -1) { i1 = code.length; } const value = code.substring(i0 + 8 + name.length + 1, i1); code = code.replace(new RegExp(name, "g"), value); } code = this._convertDefinesToConst(preProcessors) + code; return code; } finalizeShaders(vertexCode, fragmentCode) { const enabledExtensions = []; const fragCoordCode = fragmentCode.indexOf("fragmentInputs.position") >= 0 && !this.pureMode ? ` if (internals.yFactor_ == 1.) { fragmentInputs.position.y = internals.textureOutputHeight_ - fragmentInputs.position.y; } ` : ""; // Add the group/binding info to the sampler declaration (var xxx: sampler|sampler_comparison) vertexCode = this._processSamplers(vertexCode, true); fragmentCode = this._processSamplers(fragmentCode, false); // Add the group/binding info to the uniform/storage buffer declarations (var<uniform> XXX:YYY or var<storage(,read_write|read)> XXX:YYY) vertexCode = this._processCustomBuffers(vertexCode, true); fragmentCode = this._processCustomBuffers(fragmentCode, false); // Builds the leftover UBOs. const leftOverUBO = this._buildLeftOverUBO(); vertexCode = leftOverUBO + vertexCode; fragmentCode = leftOverUBO + fragmentCode; // Vertex code vertexCode = vertexCode.replace(/#define /g, "//#define "); vertexCode = this._processStridedUniformArrays(vertexCode); let vertexInputs = "struct VertexInputs {\n @builtin(vertex_index) vertexIndex : u32,\n @builtin(instance_index) instanceIndex : u32,\n"; if (this._attributesInputWGSL.length > 0) { vertexInputs += this._attributesInputWGSL.join("\n"); } vertexInputs += "\n};\nvar<private> vertexInputs" + (this._hasNonFloatAttribute ? "_" : "") + " : VertexInputs;\n"; if (this._hasNonFloatAttribute) { vertexInputs += "struct VertexInputs_ {\n vertexIndex : u32, instanceIndex : u32,\n"; vertexInputs += this._attributesWGSL.join("\n"); vertexInputs += "\n};\nvar<private> vertexInputs : VertexInputs_;\n"; } let vertexOutputs = "struct FragmentInputs {\n @builtin(position) position : vec4<f32>,\n"; if (this._varyingsWGSL.length > 0) { vertexOutputs += this._varyingsWGSL.join("\n"); } vertexOutputs += "\n};\nvar<private> vertexOutputs : FragmentInputs;\n"; vertexCode = vertexInputs + vertexOutputs + vertexCode; let vertexMainStartingCode = `\n vertexInputs${this._hasNonFloatAttribute ? "_" : ""} = input;\n`; if (this._hasNonFloatAttribute) { vertexMainStartingCode += "vertexInputs.vertexIndex = vertexInputs_.vertexIndex;\nvertexInputs.instanceIndex = vertexInputs_.instanceIndex;\n"; vertexMainStartingCode += this._attributesConversionCodeWGSL.join("\n"); vertexMainStartingCode += "\n"; } const vertexMainEndingCode = this.pureMode ? ` return vertexOutputs;` : ` vertexOutputs.position.y = vertexOutputs.position.y * internals.yFactor_;\n return vertexOutputs;`; let needDiagnosticOff = vertexCode.indexOf(`#define DISABLE_UNIFORMITY_ANALYSIS`) !== -1; vertexCode = (needDiagnosticOff ? "diagnostic(off, derivative_uniformity);\n" : "") + "diagnostic(off, chromium.unreachable_code);\n" + InjectStartingAndEndingCode(vertexCode, "fn main", vertexMainStartingCode, vertexMainEndingCode); // fragment code fragmentCode = fragmentCode.replace(/#define /g, "//#define "); fragmentCode = this._processStridedUniformArrays(fragmentCode); if (!this.pureMode) { fragmentCode = fragmentCode.replace(/dpdy/g, "(-internals.yFactor_)*dpdy"); // will also handle dpdyCoarse and dpdyFine } let fragmentInputs = "struct FragmentInputs {\n @builtin(position) position : vec4<f32>,\n @builtin(front_facing) frontFacing : bool,\n"; if (this._varyingsWGSL.length > 0) { fragmentInputs += this._varyingsWGSL.join("\n"); } fragmentInputs += "\n};\nvar<private> fragmentInputs : FragmentInputs;\n"; let fragmentOutputs = "struct FragmentOutputs {\n"; // Adding fragData output locations const regexRoot = "fragmentOutputs\\.fragData"; let match = fragmentCode.match(new RegExp(regexRoot + "0", "g")); let indexLocation = 0; if (match) { fragmentOutputs += ` @location(${indexLocation}) fragData0 : vec4<f32>,\n`; indexLocation++; for (let index = 1; index < 8; index++) { match = fragmentCode.match(new RegExp(regexRoot + index, "g")); if (match) { fragmentOutputs += ` @location(${indexLocation}) fragData${indexLocation} : vec4<f32>,\n`; indexLocation++; } } if (fragmentCode.indexOf("MRT_AND_COLOR") !== -1) { fragmentOutputs += ` @location(${indexLocation}) color : vec4<f32>,\n`; indexLocation++; } } // Adding fragData output locations const regex = /oitDepthSampler/; match = fragmentCode.match(regex); if (match) { fragmentOutputs += ` @location(${indexLocation++}) depth : vec2<f32>,\n`; fragmentOutputs += ` @location(${indexLocation++}) frontColor : vec4<f32>,\n`; fragmentOutputs += ` @location(${indexLocation++}) backColor : vec4<f32>,\n`; } if (indexLocation === 0) { const useDualSourceBlending = fragmentCode.indexOf("DUAL_SOURCE_BLENDING") !== -1; if (useDualSourceBlending) { enabledExtensions.push("dual_source_blending"); fragmentOutputs += " @location(0) @blend_src(0) color : vec4<f32>,\n"; fragmentOutputs += " @location(0) @blend_src(1) color2 : vec4<f32>,\n"; } else { fragmentOutputs += " @location(0) color : vec4<f32>,\n"; } indexLocation++; } // FragDepth let hasFragDepth = false; let idx = 0; while (!hasFragDepth) { idx = fragmentCode.indexOf(builtInName_frag_depth, idx); if (idx < 0) { break; } const saveIndex = idx; hasFragDepth = true; while (idx > 1 && fragmentCode.charAt(idx) !== "\n") { if (fragmentCode.charAt(idx) === "/" && fragmentCode.charAt(idx - 1) === "/") { hasFragDepth = false; break; } idx--; } idx = saveIndex + builtInName_frag_depth.length; } if (hasFragDepth) { fragmentOutputs += " @builtin(frag_depth) fragDepth: f32,\n"; } fragmentOutputs += "};\nvar<private> fragmentOutputs : FragmentOutputs;\n"; fragmentCode = fragmentInputs + fragmentOutputs + fragmentCode; const fragmentStartingCode = " fragmentInputs = input;\n " + fragCoordCode; const fragmentEndingCode = " return fragmentOutputs;"; needDiagnosticOff = fragmentCode.indexOf(`#define DISABLE_UNIFORMITY_ANALYSIS`) !== -1; if (enabledExtensions.length > 0) { fragmentCode = "enable " + enabledExtensions.join(";\nenable ") + ";\n" + fragmentCode; } fragmentCode = (needDiagnosticOff ? "diagnostic(off, derivative_uniformity);\n" : "") + "diagnostic(off, chromium.unreachable_code);\n" + InjectStartingAndEndingCode(fragmentCode, "fn main", fragmentStartingCode, fragmentEndingCode); this._collectBindingNames(); this._preCreateBindGroupEntries(); this._webgpuProcessingContext.vertexBufferKindToNumberOfComponents = {}; return { vertexCode, fragmentCode }; } _generateLeftOverUBOCode(name, uniformBufferDescription) { let stridedArrays = ""; let ubo = `struct ${name} {\n`; for (const leftOverUniform of this._webgpuProcessingContext.leftOverUniforms) { const type = leftOverUniform.type.replace(/^(.*?)(<.*>)?$/, "$1"); const size = WebGPUShaderProcessor.UniformSizes[type]; if (leftOverUniform.length > 0) { if (size <= 2) { const stridedArrayType = `${name}_${this._stridedUniformArrays.length}_strided_arr`; stridedArrays += `struct ${stridedArrayType} { @size(16) el: ${type}, }`; this._stridedUniformArrays.push(leftOverUniform.name); ubo += ` @align(16) ${leftOverUniform.name} : array<${stridedArrayType}, ${leftOverUniform.length}>,\n`; } else { ubo += ` ${leftOverUniform.name} : array<${leftOverUniform.type}, ${leftOverUniform.length}>,\n`; } } else { ubo += ` ${leftOverUniform.name} : ${leftOverUniform.type},\n`; } } ubo += "};\n"; ubo = `${stridedArrays}\n${ubo}`; ubo += `@group(${uniformBufferDescription.binding.groupIndex}) @binding(${uniformBufferDescription.binding.bindingIndex}) var<uniform> ${leftOverVarName} : ${name};\n`; return ubo; } _processSamplers(code, isVertex) { const samplerRegexp = /var\s+(\w+Sampler)\s*:\s*(sampler|sampler_comparison)\s*;/gm; // eslint-disable-next-line no-constant-condition while (true) { const match = samplerRegexp.exec(code); if (match === null) { break; } const name = match[1]; // name of the variable const samplerType = match[2]; // sampler or sampler_comparison const suffixLessLength = name.length - `Sampler`.length; const textureName = name.lastIndexOf(`Sampler`) === suffixLessLength ? name.substring(0, suffixLessLength) : null; const samplerBindingType = samplerType === "sampler_comparison" ? "comparison" /* WebGPUConstants.SamplerBindingType.Comparison */ : "filtering" /* WebGPUConstants.SamplerBindingType.Filtering */; if (textureName) { const textureInfo = this._webgpuProcessingContext.availableTextures[textureName]; if (textureInfo) { textureInfo.autoBindSampler = true; } } let samplerInfo = this._webgpuProcessingContext.availableSamplers[name]; if (!samplerInfo) { samplerInfo = { binding: this._webgpuProcessingContext.getNextFreeUBOBinding(), type: samplerBindingType, }; this._webgpuProcessingContext.availableSamplers[name] = samplerInfo; } this._addSamplerBindingDescription(name, samplerInfo, isVertex); const part1 = code.substring(0, match.index); const insertPart = `@group(${samplerInfo.binding.groupIndex}) @binding(${samplerInfo.binding.bindingIndex}) `; const part2 = code.substring(match.index); code = part1 + insertPart + part2; samplerRegexp.lastIndex += insertPart.length; } return code; } _processCustomBuffers(code, isVertex) { const instantiateBufferRegexp = /var<\s*(uniform|storage)\s*(,\s*(read|read_write)\s*)?>\s+(\S+)\s*:\s*(\S+)\s*;/gm; // eslint-disable-next-line no-constant-condition while (true) { const match = instantiateBufferRegexp.exec(code); if (match === null) { break; } const type = match[1]; const decoration = match[3]; let name = match[4]; const structName = match[5]; let bufferInfo = this._webgpuProcessingContext.availableBuffers[name]; if (!bufferInfo) { const knownUBO = type === "uniform" ? WebGPUShaderProcessingContext.KnownUBOs[structName] : null; let binding; if (knownUBO) { name = structName; binding = knownUBO.binding; if (binding.groupIndex === -1) { binding = this._webgpuProcessingContext.availableBuffers[name]?.binding; if (!binding) { binding = this._webgpuProcessingContext.getNextFreeUBOBinding(); } } } else { binding = this._webgpuProcessingContext.getNextFreeUBOBinding(); } bufferInfo = { binding }; this._webgpuProcessingContext.availableBuffers[name] = bufferInfo; } this._addBufferBindingDescription(name, this._webgpuProcessingContext.availableBuffers[name], decoration === "read_write" ? "storage" /* WebGPUConstants.BufferBindingType.Storage */ : type === "storage" ? "read-only-storage" /* WebGPUConstants.BufferBindingType.ReadOnlyStorage */ : "uniform" /* WebGPUConstants.BufferBindingType.Uniform */, isVertex); const groupIndex = bufferInfo.binding.groupIndex; const bindingIndex = bufferInfo.binding.bindingIndex; const part1 = code.substring(0, match.index); const insertPart = `@group(${groupIndex}) @binding(${bindingIndex}) `; const part2 = code.substring(match.index); code = part1 + insertPart + part2; instantiateBufferRegexp.lastIndex += insertPart.length; } return code; } _processStridedUniformArrays(code) { for (const uniformArrayName of this._stridedUniformArrays) { code = code.replace(new RegExp(`${uniformArrayName}\\s*\\[(.*?)\\]`, "g"), `${uniformArrayName}[$1].el`); } return code; } } //# sourceMappingURL=webgpuShaderProcessorsWGSL.js.map