UNPKG

xgpu

Version:

XGPU is an extendable library for WebGPU that provides a higher-level, easy-to-use interface for building rendering engines or processing numeric data. It handles automatic data binding, buffer alignment, variable declarations in shaders, and more. XGPU i

1,012 lines (998 loc) 40 kB
// Copyright (c) 2023 Thomas Le Coz. All rights reserved. // This code is governed by an MIT license that can be found in the LICENSE file. import { BuiltIns } from "./BuiltIns"; import { Bindgroup } from "./shader/Bindgroup"; import { PrimitiveFloatUniform, PrimitiveIntUniform, PrimitiveUintUniform, Vec4 } from "./PrimitiveType"; import { CubeMapTexture } from "./shader/resources/CubeMapTexture"; import { ImageTexture } from "./shader/resources/ImageTexture"; import { ImageTextureArray } from "./shader/resources/ImageTextureArray"; import { ImageTextureIO } from "./shader/resources/ImageTextureIO"; import { TextureSampler } from "./shader/resources/TextureSampler"; import { UniformBuffer } from "./shader/resources/UniformBuffer"; import { UniformGroup } from "./shader/resources/UniformGroup"; import { UniformGroupArray } from "./shader/resources/UniformGroupArray"; import { VertexAttribute } from "./shader/resources/VertexAttribute"; import { VertexBuffer } from "./shader/resources/VertexBuffer"; import { VertexBufferIO } from "./shader/resources/VertexBufferIO"; import { VideoTexture } from "./shader/resources/VideoTexture"; /* export type VertexShaderDescriptor = { main:string, constants?:string, inputs:{ [key:string]:VertexShaderInput }, outputs:{ [key:string]:VertexShaderOutput } } | string; export type FragmentShaderDescriptor = { main:string, constants?:string, inputs:{ [key:string]:FragmentShaderInput }, outputs:{ [key:string]:FragmentShaderOutputs } } | string; export type ShaderResource = (IShaderResource | VertexBufferIO | ImageTextureIO| PrimitiveType | VertexAttribute) export type DefaultBindgroup = { uniforms?:UniformBuffer buffer?:VertexBuffer, [key:string]:PipelineResource } export type BindgroupDescriptor = Bindgroup | { [key:string]:PipelineResource } export type BindgroupsDescriptor = { default?:BindgroupDescriptor, [key:string]:BindgroupDescriptor } export type RenderPipelineDescriptor = { vertexCount?:number, instanceCount?:number, firstVertexId?:number, firstInstanceId?:number, cullMode?: "front" | "back" | "none", topology?: "point-list" | "line-list" | "line-strip" | "triangle-list" | "triangle-strip", frontFace?: "ccw" | "cw", stripIndexFormat?: "uint16" | "uint32", antiAliasing?: boolean, useDepthTexture?: boolean, depthTextureSize?: number, depthTest?: boolean, clearColor?: { r: number, g: number, b: number, a: number }, blendMode?: BlendMode, bindgroups?: BindgroupsDescriptor, indexBuffer?: IndexBuffer, vertexShader:VertexShaderDescriptor, fragmentShader?:FragmentShaderDescriptor, //[key:string]:PipelineResource } */ ///export type HighLevelParserTarget = RenderPipeline | ComputePipeline | Bindgroup; export class HighLevelParser { constructor() { } parseShaderBuiltins(descriptor) { //------------- COMPUTE INPUTS ----------------- const addComputeInput = (name, val) => { if (typeof descriptor.computeShader === "string") { const main = descriptor.computeShader; descriptor.computeShader = { main }; } if (!descriptor.computeShader.inputs) descriptor.computeShader.inputs = {}; descriptor.computeShader.inputs[name] = val; }; const checkComputeInputBuiltIns = (name, o) => { for (let z in BuiltIns.computeInputs) { if (o === BuiltIns.computeInputs[z]) { addComputeInput(name, o); } } }; //-------------- COMPUTE OUTPUTS ------------ const addComputeOutput = (name, val) => { if (typeof descriptor.computeShader === "string") { const main = descriptor.computeShader; descriptor.computeShader = { main }; } if (!descriptor.computeShader.outputs) descriptor.computeShader.outputs = {}; descriptor.computeShader.outputs[name] = val; }; const checkComputeOutputBuiltIns = (name, o) => { for (let z in BuiltIns.computeOutputs) { if (o === BuiltIns.computeOutputs[z]) { addComputeOutput(name, o); } } }; //------------- VERTEX INPUTS ----------------- const addVertexInput = (name, val) => { if (typeof descriptor.vertexShader === "string") { const main = descriptor.vertexShader; descriptor.vertexShader = { main }; } if (!descriptor.vertexShader.inputs) descriptor.vertexShader.inputs = {}; descriptor.vertexShader.inputs[name] = val; }; const checkVertexInputBuiltIns = (name, o) => { for (let z in BuiltIns.vertexInputs) { if (o === BuiltIns.vertexInputs[z]) { addVertexInput(name, o); } } }; //-------------- VERTEX OUTPUTS ------------ const addVertexOutput = (name, val) => { if (typeof descriptor.vertexShader === "string") { const main = descriptor.vertexShader; descriptor.vertexShader = { main }; } if (!descriptor.vertexShader.outputs) descriptor.vertexShader.outputs = {}; descriptor.vertexShader.outputs[name] = val; }; const checkVertexOutputBuiltIns = (name, o) => { for (let z in BuiltIns.vertexOutputs) { if (o === BuiltIns.vertexOutputs[z]) { addVertexOutput(name, o); } } }; //------------- FRAGMENT INPUTS ----------------- const addFragmentInput = (name, val) => { if (typeof descriptor.fragmentShader === "string") { const main = descriptor.fragmentShader; descriptor.fragmentShader = { main }; } if (!descriptor.fragmentShader.inputs) descriptor.fragmentShader.inputs = {}; descriptor.fragmentShader.inputs[name] = val; }; const checkFragmentInputBuiltIns = (name, o) => { for (let z in BuiltIns.fragmentInputs) { if (o === BuiltIns.vertexInputs[z]) { addFragmentInput(name, o); } } }; //-------------- FRAGMENT OUTPUTS ------------ const addFragmentOutput = (name, val) => { if (typeof descriptor.fragmentShader === "string") { const main = descriptor.fragmentShader; descriptor.fragmentShader = { main }; } if (!descriptor.fragmentShader.outputs) descriptor.fragmentShader.outputs = {}; descriptor.fragmentShader.outputs[name] = val; }; const checkFragmentOutputBuiltIns = (name, o) => { for (let z in BuiltIns.fragmentOutputs) { if (o === BuiltIns.fragmentOutputs[z]) { addFragmentOutput(name, o); } } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) { checkVertexInputBuiltIns(z, o); checkVertexOutputBuiltIns(z, o); checkFragmentInputBuiltIns(z, o); checkFragmentOutputBuiltIns(z, o); checkComputeInputBuiltIns(z, o); checkComputeOutputBuiltIns(z, o); } } return descriptor; } parseVertexBufferIOs(descriptor) { if (this.targetIsBindgroup) return descriptor; const addVertexBufferIO = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.io) descriptor.bindgroups.io = {}; descriptor.bindgroups.io[name] = o; return o; }; const checkVertexBufferIO = (name, o) => { if (o instanceof VertexBufferIO) { addVertexBufferIO(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkVertexBufferIO(z, o); } return descriptor; } parseImageTextureIOs(descriptor) { if (this.targetIsBindgroup) return descriptor; const addTextureIO = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.io) descriptor.bindgroups.io = {}; descriptor.bindgroups.io[name] = o; return o; }; const checkTextureIO = (name, o) => { if (o instanceof ImageTextureIO) { addTextureIO(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkTextureIO(z, o); } return descriptor; } parseVertexBuffers(descriptor) { if (this.targetIsBindgroup) return descriptor; const addVertexBuffer = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; return o; }; const checkVertexBuffer = (name, o) => { if (o instanceof VertexBuffer) { addVertexBuffer(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkVertexBuffer(z, o); } return descriptor; } parseVertexAttributes(descriptor) { const addVertexAttribute = (name, o) => { let bindgroup = descriptor; if (!this.targetIsBindgroup) { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; bindgroup = descriptor.bindgroups.default; } if (!bindgroup.buffer) { const attributes = {}; attributes[name] = o; //console.log(attributes) bindgroup.buffer = new VertexBuffer(attributes); } else { //console.log("O = ", o) let offset = o.offset; if (o instanceof VertexAttribute) { offset = o.dataOffset; bindgroup.buffer.attributes[o.name] = o; } const attribute = bindgroup.buffer.createArray(name, o.type, offset); if (o.datas) attribute.datas = o.datas; } //console.log("addVertexAttribute ", name, "buffer = ", bindgroup) }; const checkVertexAttribute = (name, o) => { if (o.type && VertexAttribute.types[o.type]) { addVertexAttribute(name, o); } else if (o instanceof VertexAttribute) { addVertexAttribute(name, { type: o.format, offset: o.dataOffset, datas: o.datas }); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkVertexAttribute(z, o); } return descriptor; } parseUniformBuffers(descriptor) { if (this.targetIsBindgroup) return descriptor; const addUniformBuffer = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; return o; }; const checkUniformBuffer = (name, o) => { if (o instanceof UniformBuffer) { addUniformBuffer(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkUniformBuffer(z, o); } return descriptor; } parseUniform(descriptor) { const addUniform = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; let bindgroup = descriptor.bindgroups.default; let uniformBufferName = "uniforms"; if (this.targetIsBindgroup) { bindgroup = descriptor; uniformBufferName = descriptor.uniformBufferName ? descriptor.uniformBufferName : "bindgroupUniforms"; } /* if(o.name){ console.warn("parseUniform ",name) let old = o; let newObj = o.clone(); o.addEventListener("ON_CHANGE",()=>{ newObj.set(old); }) o = o.clone(); }*/ if (!bindgroup[uniformBufferName]) { const uniforms = {}; uniforms[name] = o; bindgroup[uniformBufferName] = new UniformBuffer(uniforms, { useLocalVariable: true }); } else { //console.warn(name); bindgroup[uniformBufferName].add(name, o); } //console.log("addUniform ", name, " vertexBuffer = ", descriptor.bindgroups.default.buffer) }; const checkUniform = (name, o) => { if (o instanceof PrimitiveFloatUniform || o instanceof PrimitiveIntUniform || o instanceof PrimitiveUintUniform || o instanceof UniformGroup || o instanceof UniformGroupArray) { addUniform(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkUniform(z, o); } return descriptor; } parseImageTextureArray(descriptor) { if (this.targetIsBindgroup) return descriptor; const addImageTextureArray = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; }; const checkImageTextureArray = (name, o) => { if (o instanceof ImageTextureArray) { addImageTextureArray(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkImageTextureArray(z, o); } return descriptor; } parseImageTexture(descriptor) { if (this.targetIsBindgroup) return descriptor; const addImageTexture = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; }; const checkImageTexture = (name, o) => { if (o instanceof ImageTexture) { addImageTexture(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkImageTexture(z, o); } return descriptor; } parseTextureSampler(descriptor) { if (this.targetIsBindgroup) return descriptor; const addTextureSampler = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; }; const checkTextureSampler = (name, o) => { if (o instanceof TextureSampler) { addTextureSampler(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkTextureSampler(z, o); } return descriptor; } parseVideoTexture(descriptor) { if (this.targetIsBindgroup) return descriptor; const addVideoTexture = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; }; const checkVideoTexture = (name, o) => { if (o instanceof VideoTexture) { addVideoTexture(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkVideoTexture(z, o); } return descriptor; } parseCubeMapTexture(descriptor) { if (this.targetIsBindgroup) return descriptor; const addCubeMapTexture = (name, o) => { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; descriptor.bindgroups.default[name] = o; }; const checkCubeMapTexture = (name, o) => { if (o instanceof CubeMapTexture) { addCubeMapTexture(name, o); } }; let o; for (let z in descriptor) { o = descriptor[z]; if (o) checkCubeMapTexture(z, o); } return descriptor; } parseDrawConfig(descriptor, drawConfig) { // vertexCount: number, instanceCount: number, firstVertexId: number, firstInstanceId: number if (descriptor.vertexCount) { if (isNaN(descriptor.vertexCount)) throw new Error("descriptor.vertexCount is a reserved keyword and must be a number"); drawConfig.vertexCount = descriptor.vertexCount; } if (descriptor.instanceCount) { if (isNaN(descriptor.instanceCount)) throw new Error("descriptor.instanceCount is a reserved keyword and must be a number"); drawConfig.instanceCount = descriptor.instanceCount; } if (descriptor.firstVertexId) { if (isNaN(descriptor.firstVertexId)) throw new Error("descriptor.firstVertexId is a reserved keyword and must be a number"); drawConfig.firstVertexId = descriptor.firstVertexId; } if (descriptor.firstInstanceId) { if (isNaN(descriptor.firstInstanceId)) throw new Error("descriptor.firstInstanceId is a reserved keyword and must be a number"); drawConfig.firstInstanceId = descriptor.firstInstanceId; } return descriptor; } parseBindgroup(descriptor) { for (let z in descriptor) { if (descriptor[z] instanceof Bindgroup) { if (!descriptor.bindgroups) descriptor.bindgroups = {}; descriptor.bindgroups[z] = descriptor[z]; delete descriptor[z]; } } return descriptor; } firstPass(descriptor, target, drawConfig) { descriptor = this.parseBindgroup(descriptor); descriptor = this.parseVertexBuffers(descriptor); descriptor = this.parseVertexAttributes(descriptor); descriptor = this.parseUniformBuffers(descriptor); descriptor = this.parseUniform(descriptor); descriptor = this.parseImageTexture(descriptor); descriptor = this.parseImageTextureArray(descriptor); descriptor = this.parseTextureSampler(descriptor); descriptor = this.parseVideoTexture(descriptor); descriptor = this.parseCubeMapTexture(descriptor); descriptor = this.parseVertexBufferIOs(descriptor); descriptor = this.parseImageTextureIOs(descriptor); if (target === "render" || target === "compute") { //console.log("target.type = ", target) descriptor = this.parseShaderBuiltins(descriptor); if (target === "render") { descriptor = this.parseDrawConfig(descriptor, drawConfig); } } return descriptor; } //-------- parseHighLevelObj(descriptor) { const isBuiltIn = (obj) => { for (let z in BuiltIns.vertexInputs) if (BuiltIns.vertexInputs[z] === obj) return true; for (let z in BuiltIns.vertexOutputs) if (BuiltIns.vertexOutputs[z] === obj) return true; for (let z in BuiltIns.fragmentInputs) if (BuiltIns.fragmentInputs[z] === obj) return true; for (let z in BuiltIns.fragmentOutputs) if (BuiltIns.fragmentOutputs[z] === obj) return true; for (let z in BuiltIns.computeInputs) if (BuiltIns.computeInputs[z] === obj) return true; return false; }; const searchComplexObject = (o) => { let name; let obj; let objs = []; for (let z in o) { //console.log("=> ", z, o[z]) obj = o[z]; if (!obj) continue; //console.log("OBJ = ", z, obj) name = obj.constructor.name; if (name === "Object") { if (z !== "bindgroups" && z !== "vertexShader" && z !== "fragmentShader" && z !== "computeShader") { if (!isBuiltIn(obj)) { objs.push({ name, resource: obj }); //console.log("######## ", z) } } } } return objs; }; const analyseObjects = (objs) => { const primitives = []; const vertexAttributes = []; const shaderResources = []; let o; let resource; let containerName; for (let i = 0; i < objs.length; i++) { containerName = objs[i].name; o = objs[i].resource; for (let name in o) { resource = o[name]; if (resource instanceof PrimitiveFloatUniform || resource instanceof PrimitiveIntUniform || resource instanceof PrimitiveUintUniform) { primitives.push({ containerName, name, resource }); } else if (resource instanceof VertexAttribute) { vertexAttributes.push({ containerName, name, resource }); } else { shaderResources.push({ containerName, name, resource }); } } } return { primitives, vertexAttributes, shaderResources }; }; //console.log("descriptor = ", descriptor) let objects = searchComplexObject(descriptor); if (objects.length) { if (!descriptor.bindgroups) descriptor.bindgroups = {}; if (!descriptor.bindgroups.default) descriptor.bindgroups.default = {}; analyseObjects(objects); } //descriptor.bindgroups.default.buffer = new VertexBuffer(attributes); return descriptor; } //--- findAndFixRepetitionInDataStructure(o) { let name; let obj; let exist = {}; let bindgroup; let resource; let uniformBuffers = []; let bool; for (let z in o.bindgroups) { bindgroup = o.bindgroups[z]; for (let a in bindgroup) { resource = bindgroup[a]; if (resource instanceof UniformBuffer) { bool = true; for (let i = resource.itemNames.length - 1; i >= 0; i--) { name = resource.itemNames[i]; obj = resource.items[name]; if (!exist[name]) { exist[name] = obj; } else { resource.remove(name); if (resource.itemNames.length === 0) { bool = false; } } } if (bool) uniformBuffers.push(resource); else { bindgroup[a] = undefined; } } resource = bindgroup[a]; } } return o; } targetIsBindgroup; parseBindgroupEntries(descriptor) { const uniformBufferName = descriptor.uniformBufferName ? descriptor.uniformBufferName : "bindgroupUniforms"; const addUniform = (name, resource) => { if (!descriptor[uniformBufferName]) { const uniforms = {}; uniforms[name] = resource; descriptor[uniformBufferName] = new UniformBuffer(uniforms, { useLocalVariable: true }); } else { descriptor[uniformBufferName].add(name, resource); } }; const vertexBufferName = descriptor.vertexBufferName ? descriptor.vertexBufferName : "bindgroupVertexBuffer"; const addVertexAttribute = (name, resource) => { if (!descriptor[vertexBufferName]) { const attributes = {}; attributes[name] = resource; descriptor[vertexBufferName] = new VertexBuffer(attributes); } else { const attribute = descriptor[vertexBufferName].createArray(name, resource.type, resource.dataOffset); if (resource.datas) attribute.datas = resource.datas; } }; let resource; for (let z in descriptor) { resource = descriptor[z]; if (!resource) continue; if (resource instanceof PrimitiveFloatUniform || resource instanceof PrimitiveIntUniform || resource instanceof PrimitiveUintUniform) { addUniform(z, resource); } else if (VertexAttribute.types[resource.type]) { addVertexAttribute(z, resource); } } return descriptor; } parseDebugValues = (descriptor) => { let o; let indexs = []; let objectById = []; let objectByName = {}; let nb = 0; for (let name in descriptor) { o = descriptor[name]; if (o && o.__debug == true) { if (typeof (o) === "function") { o = { name, id: nb, ...(o()) }; } else { o.id = nb; o.name = name; } /* if (name.includes("_")) { throw new Error(`BUILTINS ERROR :: ${name} A variable using a BuiltIns.vertexDebug cannot use an underscore in its name.`) }*/ descriptor[name] = undefined; indexs[nb] = new Vec4(o.vertexId, o.instanceId, 0, 0); objectByName[name] = o; objectById[nb] = o; nb++; } } return { nb, indexs, objectByName, objectById }; }; parseVertexShaderDebug = (descriptor) => { if (typeof (descriptor.vertexShader) === "string") { descriptor.vertexShader = { main: descriptor.vertexShader }; } const clearDebug = (shader) => { let lines = shader.split("\n"); let line; let result = ""; for (let i = 0; i < lines.length; i++) { line = lines[i]; if (line.includes("debug.")) continue; result += line + "\n"; } return result; }; const shader = descriptor.vertexShader.main; descriptor.vertexShader.main = clearDebug(shader); const debugByName = descriptor.__DEBUG__.objectByName; const removeEmptySpaceAtStart = (line) => { let abc = "abcdefghijklmnopqrstuvwxyz/"; abc += abc.toUpperCase(); let char; for (let i = 0; i < line.length; i++) { char = line[i]; if (abc.includes(char)) { return line.slice(i); } } return line; }; const extractDebugName = (line) => { let abc = "abcdefghijklmnopqrstuvwxyz0123456789_"; abc += abc.toUpperCase(); let char; let name = ""; for (let i = 0; i < line.length; i++) { char = line[i]; if (abc.includes(char)) { name += char; continue; } if (char === " ") continue; if (char != "=") { //console.log("char = ", char) throw new Error(`VERTEX SHADER ERROR on this line :"debug.${line} ". The keyword "debug" must only be used to store data. It can't be used in computations.`); } return name; } return name; }; const analyseAndRewriteDebug = (shader) => { let lines = shader.split("\n"); let line; let result = ";"; let names = []; let nbUsedByName = {}; let newName; let objById = []; let count = 0; for (let i = 0; i < lines.length; i++) { line = removeEmptySpaceAtStart(lines[i]); if (line.slice(0, 2) == "//") continue; if (line.includes("debug.")) { if (line.slice(0, "debug.".length) === "debug.") { if (line.split("=").length == 2) { const dName = extractDebugName(line.slice("debug.".length)); const o = debugByName[dName]; if (!debugByName[dName]) { throw new Error(`VERTEX SHADER ERROR on this line :" ${line} ". The value "debug.${dName}" is used in the vertexShader but not defined in RenderPipeline.initFromObject `); } if (names.includes(dName) === false) names.push(dName); if (isNaN(nbUsedByName[dName])) { //debugByName[dName] = undefined; //delete debugByName[dName]; nbUsedByName[dName] = 0; } else nbUsedByName[dName]++; newName = dName + "__" + nbUsedByName[dName]; o.newName = newName; //console.log("o = ", o) debugByName[newName] = objById[count++] = { ...o }; line = line.replace("debug." + dName, "debug." + newName); } else { throw new Error(`VERTEX SHADER ERROR on this line :" ${line} ".`); } } else { //console.log(line.slice(0, "debug.".length) + " VS " + "debug.") throw new Error(`VERTEX SHADER ERROR on this line :" ${line} ". The keyword "debug" must only be used to store data. It can't be used in computations.`); } } result += line + "\n"; } descriptor.__DEBUG__.objectById = objById; for (let i = 0; i < names.length; i++) { debugByName[names[i]] = undefined; delete debugByName[names[i]]; } return result; }; const clearComments = (shader) => { let lines = shader.split("\n"); for (let i = 0; i < lines.length; i++) lines[i] = lines[i].split("//")[0]; return lines.join("\n"); }; descriptor.vertexShader.debugVersion = analyseAndRewriteDebug(clearComments(shader)); const simplifyComplexData = () => { const objById = descriptor.__DEBUG__.objectById; const objByName = descriptor.__DEBUG__.objectByName; let o, name, newName, n; let result = []; for (let i = 0; i < objById.length; i++) { o = { ...objById[i] }; if (o.type == "mat4x4<f32>") { name = o.newName; newName = name + "_m4"; o.isMatrix = true; o.realType = o.type; o.type = "vec4<f32>"; objByName[name] = undefined; delete objByName[name]; n = newName + "0"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "1"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "2"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "3"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); } else if (o.type == "mat3x3<f32>") { name = o.newName; newName = name + "_m3"; o.isMatrix = true; o.realType = o.type; o.type = "vec3<f32>"; objByName[name] = undefined; delete objByName[name]; n = newName + "0"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "1"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "2"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + "3"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); } else if (o.isArray) { const arrayOfMatrix = o.type.includes("mat"); const len = o.len; name = o.newName; newName = name + "_ar"; o.isMatrix = false; o.realType = o.type; o.type = "vec4<f32>"; if (o.realType.includes("i32")) o.type = "vec4<i32>"; else if (o.realType.includes("u32")) o.type = "vec4<u32>"; objByName[name] = undefined; delete objByName[name]; if (arrayOfMatrix) { objByName[name] = undefined; delete objByName[name]; for (let j = 0; j < len; j++) { n = newName + j + "_m0"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + j + "_m1"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + j + "_m2"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); n = newName + j + "_m3"; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); } } else { for (let i = 0; i < len; i++) { n = newName + i; objByName[n] = { ...o, newName: n }; result.push(objByName[n]); } } } else { result.push(o); } } descriptor.__DEBUG__.objectById = result; }; simplifyComplexData(); return descriptor; }; parse(descriptor, target, drawConfig) { this.targetIsBindgroup = target === "bindgroup"; if (target === "bindgroup") { descriptor = this.parseBindgroupEntries(descriptor); } else { const debug = this.parseDebugValues(descriptor); descriptor = this.firstPass(descriptor, target, drawConfig); descriptor = this.parseHighLevelObj(descriptor); descriptor = this.findAndFixRepetitionInDataStructure(descriptor); if (debug.nb != 0) { descriptor.__DEBUG__ = debug; descriptor = this.parseVertexShaderDebug(descriptor); } //console.log(descriptor); } return descriptor; } static instance = null; static parse(descriptor, target, drawConfig) { if (!this.instance) this.instance = new HighLevelParser(); //console.log("highLevelParser = ", this.instance) return this.instance.parse(descriptor, target, drawConfig); } }