Browse Source

Auto Upgrade Samplers

Sebastien Vandenberghe 6 years ago
parent
commit
16cc9478a2

+ 5 - 5
src/Engines/Processors/iShaderProcessor.ts

@@ -5,10 +5,10 @@ import { ShaderProcessingContext } from "./shaderProcessingOptions";
 export interface IShaderProcessor {
     attributeProcessor?: (attribute: string, processingContext: Nullable<ShaderProcessingContext>) => string;
     varyingProcessor?: (varying: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
-    uniformProcessor?: (uniform: string, isFragment: boolean) => string;
+    uniformProcessor?: (uniform: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
     uniformBufferProcessor?: (uniformBuffer: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
-    endOfUniformBufferProcessor?: (closingBracketLine: string, isFragment: boolean) => string;
-    lineProcessor?: (line: string, isFragment: boolean) => string;
-    preProcessor?: (code: string, defines: string[], isFragment: boolean) => string;
-    postProcessor?: (code: string, defines: string[], isFragment: boolean) => string;
+    endOfUniformBufferProcessor?: (closingBracketLine: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
+    lineProcessor?: (line: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
+    preProcessor?: (code: string, defines: string[], isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
+    postProcessor?: (code: string, defines: string[], isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) => string;
 }

+ 3 - 3
src/Engines/Processors/shaderCodeNode.ts

@@ -27,7 +27,7 @@ export class ShaderCodeNode {
 
                     if (regex.test(this.line)) { // uniform
                         if (processor.uniformProcessor) {
-                            value = processor.uniformProcessor(this.line, options.isFragment);
+                            value = processor.uniformProcessor(this.line, options.isFragment, options.processingContext);
                         }
                     } else { // Uniform buffer
                         if (processor.uniformBufferProcessor) {
@@ -41,12 +41,12 @@ export class ShaderCodeNode {
                 if (options.lookForClosingBracketForUniformBuffer && this.line.indexOf("}") !== -1) {
                     options.lookForClosingBracketForUniformBuffer = false;
                     if (processor.endOfUniformBufferProcessor) {
-                        value = processor.endOfUniformBufferProcessor(this.line, options.isFragment);
+                        value = processor.endOfUniformBufferProcessor(this.line, options.isFragment, options.processingContext);
                     }
                 }
 
                 if (processor.lineProcessor) {
-                    value = processor.lineProcessor(value, options.isFragment);
+                    value = processor.lineProcessor(value, options.isFragment, options.processingContext);
                 }
             }
 

+ 2 - 2
src/Engines/Processors/shaderProcessor.ts

@@ -246,14 +246,14 @@ export class ShaderProcessor {
 
         // General pre processing
         if (options.processor.preProcessor) {
-            preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, options.isFragment);
+            preparedSourceCode = options.processor.preProcessor(preparedSourceCode, defines, options.isFragment, options.processingContext);
         }
 
         preparedSourceCode = this._EvaluatePreProcessors(preparedSourceCode, preprocessors, options);
 
         // Post processing
         if (options.processor.postProcessor) {
-            preparedSourceCode = options.processor.postProcessor(preparedSourceCode, defines, options.isFragment);
+            preparedSourceCode = options.processor.postProcessor(preparedSourceCode, defines, options.isFragment, options.processingContext);
         }
 
         return preparedSourceCode;

+ 12 - 3
src/Engines/WebGPU/webgpuPipelineContext.ts

@@ -7,6 +7,8 @@ import { WebGPUShaderProcessingContext } from './webgpuShaderProcessingContext';
 
 /** @hidden */
 export interface IWebGPUPipelineContextSamplerCache {
+    setIndex: number;
+
     textureBinding: number;
 
     samplerBinding: number;
@@ -30,7 +32,11 @@ export class WebGPUPipelineContext implements IPipelineContext {
 
     public availableAttributes: { [key: string]: number };
     public availableUBOs: { [key: string]: { setIndex: number, bindingIndex: number} };
-    public availableSamplers: { [key: string]: number };
+    public availableSamplers: { [key: string]: { setIndex: number, bindingIndex: number} };
+
+    public orderedAttributes: string[];
+    public orderedUBOsAndSamplers: { name: string, isSampler: boolean }[][];
+
     public sources: {
         vertex: string
         fragment: string,
@@ -65,6 +71,9 @@ export class WebGPUPipelineContext implements IPipelineContext {
     constructor(shaderProcessingContext: WebGPUShaderProcessingContext) {
         this.availableAttributes = shaderProcessingContext.availableAttributes;
         this.availableUBOs = shaderProcessingContext.availableUBOs;
+        this.availableSamplers = shaderProcessingContext.availableSamplers;
+        this.orderedAttributes = shaderProcessingContext.orderedAttributes;
+        this.orderedUBOsAndSamplers = shaderProcessingContext.orderedUBOsAndSamplers;
     }
 
     public _handlesSpectorRebuildCallback(onCompiled: (program: any) => void): void {
@@ -74,7 +83,7 @@ export class WebGPUPipelineContext implements IPipelineContext {
     public _fillEffectInformation(effect: Effect, uniformBuffersNames: { [key: string]: number }, uniformsNames: string[], uniforms: { [key: string]: Nullable<WebGLUniformLocation> }, samplerList: string[], samplers: { [key: string]: number }, attributesNames: string[], attributes: number[]) {
         const engine = this.engine;
 
-        // TODO WEBGPU. Cleanup SEB on this entire function.
+        // TODO WEBGPU. Cleanup SEB on this entire function. Should not need anything in here or almost.
         let effectAvailableUniforms = engine.getUniforms(this, uniformsNames);
         effectAvailableUniforms.forEach((uniform, index) => {
             uniforms[uniformsNames[index]] = uniform;
@@ -97,7 +106,7 @@ export class WebGPUPipelineContext implements IPipelineContext {
                 index--;
             }
             else {
-                samplers[name] = sampler;
+                samplers[name] = index;
             }
         }
 

+ 45 - 2
src/Engines/WebGPU/webgpuShaderProcessingContext.ts

@@ -1,5 +1,8 @@
 import { ShaderProcessingContext } from "../processors/shaderProcessingOptions";
 
+const _maxSets = 4;
+const _maxBindingsPerSet = 16;
+
 /**
  * @hidden
  */
@@ -7,17 +10,57 @@ export class WebGPUShaderProcessingContext implements ShaderProcessingContext {
     public attributeNextLocation: number;
     public varyingNextLocation: number;
     public uboNextBindingIndex: number;
+    public freeSetIndex: number;
+    public freeBindingIndex: number;
 
-    public availableAttributes: { [key: string]: number };
     public availableVaryings: { [key: string]: number };
+    public availableAttributes: { [key: string]: number };
     public availableUBOs: { [key: string]: { setIndex: number, bindingIndex: number} };
+    public availableSamplers: { [key: string]: { setIndex: number, bindingIndex: number} };
+
+    public orderedAttributes: string[];
+    public orderedUBOsAndSamplers: { name: string, isSampler: boolean }[][];
 
     constructor() {
         this.attributeNextLocation = 0;
         this.varyingNextLocation = 0;
+        this.freeSetIndex = 2;
+        this.freeBindingIndex = 0;
 
-        this.availableAttributes = { };
         this.availableVaryings = { };
+        this.availableAttributes = { };
         this.availableUBOs = { };
+        this.availableSamplers = { };
+
+        this.orderedAttributes = [];
+        this.orderedUBOsAndSamplers = [];
+    }
+
+    public getNextFreeUBOBinding() {
+        return this._getNextFreeBinding(1);
+    }
+
+    public getNextFreeTextureBinding() {
+        return this._getNextFreeBinding(2);
+    }
+
+    private _getNextFreeBinding(bindingCount: number) {
+        if (this.freeBindingIndex > _maxBindingsPerSet - bindingCount) {
+            this.freeSetIndex++;
+            this.freeBindingIndex = 0;
+        }
+
+        if (this.freeSetIndex === _maxSets) {
+            throw "Too many textures or UBOs have been declared and it is not supprted in WebGPU.";
+        }
+
+        const returnValue = {
+            setIndex: this.freeSetIndex,
+            bindingIndex: this.freeBindingIndex
+        };
+
+        this.freeBindingIndex += bindingCount;
+
+        return returnValue;
     }
 }

+ 84 - 25
src/Engines/WebGPU/webgpuShaderProcessors.ts

@@ -3,63 +3,112 @@ import { IShaderProcessor } from '../Processors/iShaderProcessor';
 import { ShaderProcessingContext } from "../processors/shaderProcessingOptions";
 import { WebGPUShaderProcessingContext } from './webgpuShaderProcessingContext';
 
-/** @hidden */
 const _knownUBOs: { [key: string]: { setIndex: number, bindingIndex: number} } = {
     "Scene": { setIndex: 0, bindingIndex: 0 },
     "Material": { setIndex: 1, bindingIndex: 0 },
-    "Mesh": { setIndex: 2, bindingIndex: 0 },
+    "Mesh": { setIndex: 1, bindingIndex: 1 },
+};
+
+const _knownSamplers: { [key: string]: { setIndex: number, bindingIndex: number} } = {
+    "environmentBrdfSampler": { setIndex: 0, bindingIndex: 1 },
+    // "reflectionSampler": { setIndex: 0, bindingIndex: 3 },
+};
+
+const _samplerFunctionByWebGLSamplerType: { [key: string]: string } = {
+    "textureCube": "samplerCube",
+    "texture2D": "sampler2D",
+    "sampler2D": "sampler2D",
+    "samplerCube": "samplerCube"
+};
+
+const _textureTypeByWebGLSamplerType: { [key: string]: string } = {
+    "textureCube": "textureCube",
+    "texture2D": "texture2D",
+    "sampler2D": "texture2D",
+    "samplerCube": "textureCube"
 };
 
 /** @hidden */
 export class WebGPUShaderProcessor implements IShaderProcessor {
-    // lineProcessor?: (line: string, isFragment: boolean) => string;
-    // preProcessor?: (code: string, defines: string[], isFragment: boolean) => string;
-    // postProcessor?: (code: string, defines: string[], isFragment: boolean) => string;
+
+    public varyingProcessor(varying: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) {
+        const webgpuProcessingContext = processingContext! as WebGPUShaderProcessingContext;
+
+        const varyingRegex = new RegExp(/\s*varying\s+(\S+)\s+(\S+)\s*;/gm);
+        const match = varyingRegex.exec(varying);
+        if (match != null) {
+            const name = match[2];
+            let location: number;
+            if (isFragment) {
+                location = webgpuProcessingContext.availableVaryings[name];
+            }
+            else {
+                location = webgpuProcessingContext.varyingNextLocation++;
+                webgpuProcessingContext.availableVaryings[name] = location;
+            }
+
+            varying = varying.replace(match[0], `layout(location = ${location}) ${isFragment ? "in" : "out"} ${match[1]} ${name};`);
+        }
+        return varying;
+    }
 
     public attributeProcessor(attribute: string, processingContext: Nullable<ShaderProcessingContext>) {
         const webgpuProcessingContext = processingContext! as WebGPUShaderProcessingContext;
 
         const attribRegex = new RegExp(/\s*attribute\s+(\S+)\s+(\S+)\s*;/gm);
-
         const match = attribRegex.exec(attribute);
         if (match != null) {
             const name = match[2];
             const location = webgpuProcessingContext.attributeNextLocation++;
 
             webgpuProcessingContext.availableAttributes[name] = location;
+            webgpuProcessingContext.orderedAttributes[location] = name;
+
             attribute = attribute.replace(match[0], `layout(location = ${location}) in ${match[1]} ${name};`);
         }
         return attribute;
     }
 
-    public varyingProcessor(varying: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>) {
+    public uniformProcessor(uniform: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>): string {
         const webgpuProcessingContext = processingContext! as WebGPUShaderProcessingContext;
 
-        const varyingRegex = new RegExp(/\s*varying\s+(\S+)\s+(\S+)\s*;/gm);
+        const uniformRegex = new RegExp(/\s*uniform\s+(\S+)\s+(\S+)\s*;/gm);
 
-        const match = varyingRegex.exec(varying);
+        const match = uniformRegex.exec(uniform);
         if (match != null) {
+            const uniformType = match[1];
             const name = match[2];
-            let location: number;
 
-            if (isFragment) {
-                location = webgpuProcessingContext.availableVaryings[name];
+            if (uniformType.indexOf("texture") === 0 || uniformType.indexOf("sampler") === 0) {
+                let samplerInfo = _knownSamplers[name];
+                if (!samplerInfo) {
+                    samplerInfo = webgpuProcessingContext.getNextFreeTextureBinding();
+                }
+
+                const setIndex = samplerInfo.setIndex;
+                const textureBindingIndex = samplerInfo.bindingIndex;
+                const samplerBindingIndex = samplerInfo.bindingIndex + 1;
+                const samplerFunction = _samplerFunctionByWebGLSamplerType[uniformType];
+                const textureType = _textureTypeByWebGLSamplerType[uniformType];
+
+                // Manage textures and samplers.
+                uniform = `layout(set = ${setIndex}, binding = ${textureBindingIndex}) uniform ${textureType} ${name}Texture;
+                    layout(set = ${setIndex}, binding = ${samplerBindingIndex}) uniform sampler ${name}Sampler;
+                    #define ${name} ${samplerFunction}(${name}Texture, ${name}Sampler)`;
+
+                webgpuProcessingContext.availableSamplers[name] = samplerInfo;
+                if (!webgpuProcessingContext.orderedUBOsAndSamplers[setIndex]) {
+                    webgpuProcessingContext.orderedUBOsAndSamplers[setIndex] = [];
+                }
+                webgpuProcessingContext.orderedUBOsAndSamplers[setIndex][textureBindingIndex] = { isSampler: true, name };
             }
             else {
-                location = webgpuProcessingContext.varyingNextLocation++;
-                webgpuProcessingContext.availableVaryings[name] = location;
+                // TODO WEBGPU. Manage none UBOs uniforms.
             }
-
-            varying = varying.replace(match[0], `layout(location = ${location}) ${isFragment ? "in" : "out"} ${match[1]} ${name};`);
         }
-        return varying;
+        return uniform;
     }
 
-    // public uniformProcessor(uniform: string, isFragment: boolean): string {
-    //     console.log("uniform ", uniform);
-    //     return uniform;
-    // }
-
     public uniformBufferProcessor(uniformBuffer: string, isFragment: boolean, processingContext: Nullable<ShaderProcessingContext>): string {
         const webgpuProcessingContext = processingContext! as WebGPUShaderProcessingContext;
         const uboRegex = new RegExp(/uniform\s+(\w+)/gm);
@@ -75,21 +124,30 @@ export class WebGPUShaderProcessor implements IShaderProcessor {
                 bindingIndex = knownUBO.bindingIndex;
             }
             else {
-                setIndex = 3;
                 if (isFragment) {
                     const availableUBO = webgpuProcessingContext.availableUBOs[name];
                     if (availableUBO) {
+                        setIndex = availableUBO.setIndex;
                         bindingIndex = availableUBO.bindingIndex;
                     }
                     else {
-                        bindingIndex = webgpuProcessingContext.uboNextBindingIndex++;
+                        const nextBinding = webgpuProcessingContext.getNextFreeUBOBinding();
+                        setIndex = nextBinding.setIndex;
+                        bindingIndex = nextBinding.bindingIndex;
                     }
                 }
                 else {
-                    bindingIndex = webgpuProcessingContext.uboNextBindingIndex++;
+                    const nextBinding = webgpuProcessingContext.getNextFreeUBOBinding();
+                    setIndex = nextBinding.setIndex;
+                    bindingIndex = nextBinding.bindingIndex;
                 }
             }
+
             webgpuProcessingContext.availableUBOs[name] = { setIndex, bindingIndex };
+            if (!webgpuProcessingContext.orderedUBOsAndSamplers[setIndex]) {
+                webgpuProcessingContext.orderedUBOsAndSamplers[setIndex] = [];
+            }
+            webgpuProcessingContext.orderedUBOsAndSamplers[setIndex][bindingIndex] = { isSampler: false, name };
 
             uniformBuffer = uniformBuffer.replace("uniform", `layout(set = ${setIndex}, binding = ${bindingIndex}) uniform`);
         }
@@ -127,6 +185,7 @@ export class WebGPUShaderProcessor implements IShaderProcessor {
 
         // Flip Y.
         // TODO WEBGPU. Triple check this part and wait on Google News for this issue.
+        // https://github.com/gpuweb/gpuweb/issues/379
         if (!isFragment) {
             const lastClosingCurly = code.lastIndexOf("}");
             code = code.substring(0, lastClosingCurly);

+ 99 - 163
src/Engines/webgpuEngine.ts

@@ -118,7 +118,9 @@ export class WebGPUEngine extends Engine {
         stages: GPURenderPipelineStageDescriptor,
         availableAttributes: { [key: string]: number },
         availableUBOs: { [key: string]: { setIndex: number, bindingIndex: number} },
-        availableSamplers: { [key: string]: number },
+        availableSamplers: { [key: string]: { setIndex: number, bindingIndex: number} },
+        orderedAttributes: string[],
+        orderedUBOsAndSamplers: { name: string, isSampler: boolean }[][],
         sources: {
             vertex: string
             fragment: string,
@@ -807,6 +809,8 @@ export class WebGPUEngine extends Engine {
             webGpuContext.availableAttributes = shader.availableAttributes;
             webGpuContext.availableUBOs = shader.availableUBOs;
             webGpuContext.availableSamplers = shader.availableSamplers;
+            webGpuContext.orderedAttributes = shader.orderedAttributes;
+            webGpuContext.orderedUBOsAndSamplers = shader.orderedUBOsAndSamplers;
             webGpuContext.sources = shader.sources;
         }
         else {
@@ -817,13 +821,13 @@ export class WebGPUEngine extends Engine {
                 webGpuContext.stages = this._compilePipelineStageDescriptor(vertexSourceCode, fragmentSourceCode, defines);
             }
 
-            this._findSamplers(webGpuContext, fragmentSourceCode);
-
             this._compiledShaders[key] = {
                 stages: webGpuContext.stages,
                 availableAttributes: webGpuContext.availableAttributes,
                 availableUBOs: webGpuContext.availableUBOs,
                 availableSamplers: webGpuContext.availableSamplers,
+                orderedAttributes: webGpuContext.orderedAttributes,
+                orderedUBOsAndSamplers: webGpuContext.orderedUBOsAndSamplers,
                 sources: {
                     fragment: fragmentSourceCode,
                     vertex: vertexSourceCode
@@ -832,25 +836,6 @@ export class WebGPUEngine extends Engine {
         }
     }
 
-    private _findSamplers(pipelineContext: WebGPUPipelineContext, fragmentSourceCode: string): void {
-        // TODO WEBGPU.
-        // Hard coded for WebGPU until an introspection lib is available.
-
-        const samplersRegex = /layout\(set\s*=\s*(\d+)\s*,\s*binding\s*=\s*(\d+).*\)\s*uniform\s*(texture\S*)\s*(\S*)\s*;/gm;
-        const results: { [key: string]: number } = { };
-        let matches: RegExpExecArray | null;
-        while (matches = samplersRegex.exec(fragmentSourceCode)) {
-            // const set = matches[1];
-            const binding = matches[2];
-            // const type = matches[3];
-            const name = matches[4].replace("Texture", "");
-
-            results[name] = +binding;
-        }
-
-        pipelineContext.availableSamplers = results;
-    }
-
     public getAttributes(pipelineContext: IPipelineContext, attributesNames: string[]): number[] {
         const results = new Array(attributesNames.length);
         const gpuPipelineContext = (pipelineContext as WebGPUPipelineContext);
@@ -1184,7 +1169,7 @@ export class WebGPUEngine extends Engine {
         throw "Unimplemented updateDynamicTexture on WebGPU so far";
     }
 
-    public setTexture(channel: number, uniform: Nullable<WebGLUniformLocation>, texture: Nullable<BaseTexture>, name: string): void {
+    public setTexture(channel: number, _: Nullable<WebGLUniformLocation>, texture: Nullable<BaseTexture>, name: string): void {
         if (this._currentEffect) {
             const pipeline = this._currentEffect._pipelineContext as WebGPUPipelineContext;
             if (!texture) {
@@ -1194,9 +1179,12 @@ export class WebGPUEngine extends Engine {
                 pipeline.samplers[name]!.texture = texture!.getInternalTexture()!;
             }
             else {
+                // TODO WEBGPU. GC + 121 mapping samplers <-> availableSamplers
+                const availableSampler = pipeline.availableSamplers[name];
                 pipeline.samplers[name] = {
-                    textureBinding: channel,
-                    samplerBinding: channel + 1,
+                    setIndex: availableSampler.setIndex,
+                    textureBinding: availableSampler.bindingIndex,
+                    samplerBinding: availableSampler.bindingIndex + 1,
                     texture: texture!.getInternalTexture()!
                 };
             }
@@ -1728,84 +1716,53 @@ export class WebGPUEngine extends Engine {
 
     private _getPipelineLayout(): GPUPipelineLayout {
         const bindGroupLayouts: GPUBindGroupLayout[] = [];
-        let bindings: GPUBindGroupLayoutBinding[] = [];
+        const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
 
-        // Group 0: Scene Lights Image Processing Environment
-        bindings = [];
+        for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
+            const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
+            if (setDefinition === undefined) {
+                continue;
+            }
 
-        // Group 1: Camera
-        if (this._currentEffect!._uniformBuffersNames["Scene"] > -1) {
-            const groupLayoutBinding: GPUBindGroupLayoutBinding = {
-                binding: 0,
-                visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
-                type: WebGPUConstants.GPUBindingType_uniformBuffer,
-            };
-            bindings.push(groupLayoutBinding);
-        }
-        if (bindings.length > 0) {
-            const uniformsBindGroupLayout = this._device.createBindGroupLayout({
-                bindings,
-            });
-            bindGroupLayouts[0] = uniformsBindGroupLayout;
-        }
-        bindings = [];
+            const bindings: GPUBindGroupLayoutBinding[] = [];
+            for (let j = 0; j < setDefinition.length; j++) {
+                const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
+                if (bindingDefinition === undefined) {
+                    continue;
+                }
 
-        // Group 3: Materials
-        if (this._currentEffect!._uniformBuffersNames["Material"] > -1) {
-            const groupLayoutBinding: GPUBindGroupLayoutBinding = {
-                binding: 0,
-                visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
-                type: WebGPUConstants.GPUBindingType_uniformBuffer,
-            };
-            bindings.push(groupLayoutBinding);
-        }
-        if (bindings.length > 0) {
-            const uniformsBindGroupLayout = this._device.createBindGroupLayout({
-                bindings,
-            });
-            bindGroupLayouts[1] = uniformsBindGroupLayout;
-        }
-        bindings = [];
+                // TODO WEBGPU. Authorize shared samplers and Vertex Textures.
+                if (bindingDefinition.isSampler) {
+                    bindings.push({
+                        binding: j,
+                        visibility: WebGPUConstants.GPUShaderStageBit_FRAGMENT,
+                        type: WebGPUConstants.GPUBindingType_sampledTexture,
+                    }, {
+                        // TODO WEBGPU. No Magic + 1.
+                        binding: j + 1,
+                        visibility: WebGPUConstants.GPUShaderStageBit_FRAGMENT,
+                        type: WebGPUConstants.GPUBindingType_sampler
+                    });
+                }
+                else {
+                    bindings.push({
+                        binding: j,
+                        visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
+                        type: WebGPUConstants.GPUBindingType_uniformBuffer,
+                    });
+                }
+            }
 
-        // Group 4: Mesh
-        if (this._currentEffect!._uniformBuffersNames["Mesh"]) {
-            const groupLayoutBinding: GPUBindGroupLayoutBinding = {
-                binding: 0,
-                visibility: WebGPUConstants.GPUShaderStageBit_VERTEX | WebGPUConstants.GPUShaderStageBit_FRAGMENT,
-                type: WebGPUConstants.GPUBindingType_uniformBuffer,
-            };
-            bindings.push(groupLayoutBinding);
-        }
-
-        // TODO WEBGPU. Should be on group 2 at the end so as we only have one mesh :-)
-        const samplers = this._currentEffect!._samplerList;
-        const context = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
-        for (let samplerName of samplers) {
-            const bindingInfo = context.samplers[samplerName];
-            if (bindingInfo) {
-                bindings.push({
-                    binding: bindingInfo.textureBinding,
-                    visibility: WebGPUConstants.GPUShaderStageBit_FRAGMENT,
-                    type: "sampled-texture"
-                }, {
-                    binding: bindingInfo.samplerBinding,
-                    visibility: WebGPUConstants.GPUShaderStageBit_FRAGMENT,
-                    type: "sampler"
+            if (bindings.length > 0) {
+                const uniformsBindGroupLayout = this._device.createBindGroupLayout({
+                    bindings,
                 });
+                bindGroupLayouts[i] = uniformsBindGroupLayout;
             }
         }
 
-        if (bindings.length > 0) {
-            const uniformsBindGroupLayout = this._device.createBindGroupLayout({
-                bindings,
-            });
-            bindGroupLayouts[2] = uniformsBindGroupLayout;
-        }
-
-        (this._currentEffect!._pipelineContext as WebGPUPipelineContext).bindGroupLayouts = bindGroupLayouts;
-        return this._device.createPipelineLayout({
-            bindGroupLayouts
-        });
+        webgpuPipelineContext.bindGroupLayouts = bindGroupLayouts;
+        return this._device.createPipelineLayout({ bindGroupLayouts });
     }
 
     private _getRenderPipeline(fillMode: number): GPURenderPipeline {
@@ -1891,86 +1848,65 @@ export class WebGPUEngine extends Engine {
 
     // TODO WEBGPU. find a better solution than hardcoded groups.
     private _getBindGroupsToRender(): GPUBindGroup[] {
-        const gpuContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
-        let bindGroups = gpuContext.bindGroups;
+        const webgpuPipelineContext = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
+        let bindGroups = webgpuPipelineContext.bindGroups;
         if (bindGroups) {
             return bindGroups;
         }
 
         bindGroups = [];
-        gpuContext.bindGroups = bindGroups;
-
-        const bindGroupLayouts = (this._currentEffect!._pipelineContext as WebGPUPipelineContext).bindGroupLayouts;
-        if (this._currentEffect!._uniformBuffersNames["Scene"] > -1) {
-            const dataBuffer = this._uniformsBuffers["Scene"];
-            const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
-            const uniformBindGroup = this._device.createBindGroup({
-                layout: bindGroupLayouts[0],
-                bindings: [{
-                    binding: 0,
-                    resource: {
-                        buffer: webgpuBuffer,
-                        offset: 0,
-                        size: dataBuffer.capacity,
-                    },
-                }],
-            });
+        webgpuPipelineContext.bindGroups = bindGroups;
 
-            bindGroups.push(uniformBindGroup);
-        }
-
-        if (this._currentEffect!._uniformBuffersNames["Material"] > -1) {
-            const dataBuffer = this._uniformsBuffers["Material"];
-            const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
-            const uniformBindGroup = this._device.createBindGroup({
-                layout: bindGroupLayouts[0],
-                bindings: [{
-                    binding: 0,
-                    resource: {
-                        buffer: webgpuBuffer,
-                        offset: 0,
-                        size: dataBuffer.capacity,
-                    },
-                }],
-            });
+        const bindGroupLayouts = webgpuPipelineContext.bindGroupLayouts;
 
-            bindGroups.push(uniformBindGroup);
-        }
+        for (let i = 0; i < webgpuPipelineContext.orderedUBOsAndSamplers.length; i++) {
+            const setDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i];
+            if (setDefinition === undefined) {
+                continue;
+            }
 
-        if (this._currentEffect!._uniformBuffersNames["Mesh"]) {
-            const dataBuffer = this._uniformsBuffers["Mesh"];
-            const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
-            const bindings: GPUBindGroupBinding[] = [{
-                binding: 0,
-                resource: {
-                    buffer: webgpuBuffer,
-                    offset: 0,
-                    size: dataBuffer.capacity,
-                },
-            }];
-
-            // TODO WEBGPU. Should be on group 2 at the end so as we only have one mesh :-)
-            const samplers = this._currentEffect!._samplerList;
-            const context = this._currentEffect!._pipelineContext as WebGPUPipelineContext;
-            for (let samplerName of samplers) {
-                const bindingInfo = context.samplers[samplerName];
-                if (bindingInfo) {
-                    bindings.push({
-                        binding: bindingInfo.textureBinding,
-                        resource: bindingInfo.texture._webGPUTextureView!,
-                    }, {
-                        binding: bindingInfo.samplerBinding,
-                        resource: bindingInfo.texture._webGPUSampler!,
-                    });
+            const bindings: GPUBindGroupBinding[] = [];
+            for (let j = 0; j < setDefinition.length; j++) {
+                const bindingDefinition = webgpuPipelineContext.orderedUBOsAndSamplers[i][j];
+                if (bindingDefinition === undefined) {
+                    continue;
+                }
+
+                // TODO WEBGPU. Authorize shared samplers and Vertex Textures.
+                if (bindingDefinition.isSampler) {
+                    const bindingInfo = webgpuPipelineContext.samplers[bindingDefinition.name];
+                    if (bindingInfo) {
+                        bindings.push({
+                            binding: bindingInfo.textureBinding,
+                            resource: bindingInfo.texture._webGPUTextureView!,
+                        }, {
+                            binding: bindingInfo.samplerBinding,
+                            resource: bindingInfo.texture._webGPUSampler!,
+                        });
+                    }
+                }
+                else {
+                    const dataBuffer = this._uniformsBuffers[bindingDefinition.name];
+                    if (dataBuffer) {
+                        const webgpuBuffer = dataBuffer.underlyingResource as GPUBuffer;
+                        bindings.push({
+                            binding: j,
+                            resource: {
+                                buffer: webgpuBuffer,
+                                offset: 0,
+                                size: dataBuffer.capacity,
+                            },
+                        });
+                    }
                 }
             }
-            const uniformBindGroup = this._device.createBindGroup({
-                layout: bindGroupLayouts[2],
-                bindings: bindings,
-            });
 
-            bindGroups.push(uniformBindGroup);
+            bindGroups[i] = this._device.createBindGroup({
+                layout: bindGroupLayouts[i],
+                bindings,
+            });
         }
+
         return bindGroups;
     }
 

+ 7 - 49
src/Shaders/ShadersInclude/pbrFragmentSamplersDeclaration.fx

@@ -1,11 +1,5 @@
 #ifdef ENVIRONMENTBRDF
-    #ifdef WEBGPU
-        layout(set = 2, binding = 1) uniform texture2D environmentBrdfSamplerTexture;
-        layout(set = 2, binding = 2) uniform sampler environmentBrdfSamplerSampler;
-        #define environmentBrdfSampler sampler2D(environmentBrdfSamplerTexture, environmentBrdfSamplerSampler)
-    #else
-        uniform sampler2D environmentBrdfSampler;
-    #endif
+    uniform sampler2D environmentBrdfSampler;
 #endif
 
 // Reflection
@@ -13,13 +7,7 @@
     #ifdef REFLECTIONMAP_3D
         #define sampleReflection(s, c) textureCube(s, c)
 
-        #ifdef WEBGPU
-            layout(set = 2, binding = 3) uniform textureCube reflectionSamplerTexture;
-            layout(set = 2, binding = 4) uniform sampler reflectionSamplerSampler;
-            #define reflectionSampler samplerCube(reflectionSamplerTexture, reflectionSamplerSampler)
-        #else
-            uniform samplerCube reflectionSampler;
-        #endif
+        uniform samplerCube reflectionSampler;
 
         #ifdef LODBASEDMICROSFURACE
             #define sampleReflectionLod(s, c, l) textureCubeLodEXT(s, c, l)
@@ -66,13 +54,7 @@
         varying vec2 vAlbedoUV;
     #endif
 
-    #ifdef WEBGPU
-        layout(set = 2, binding = 5) uniform texture2D albedoSamplerTexture;
-        layout(set = 2, binding = 6) uniform sampler albedoSamplerSampler;
-        #define albedoSampler sampler2D(albedoSamplerTexture, albedoSamplerSampler)
-    #else
-        uniform sampler2D albedoSampler;
-    #endif
+    uniform sampler2D albedoSampler;
 #endif
 
 #ifdef REFLECTIVITY
@@ -84,13 +66,7 @@
         varying vec2 vReflectivityUV;
     #endif
 
-    #ifdef WEBGPU
-        layout(set = 2, binding = 7) uniform texture2D reflectivitySamplerTexture;
-        layout(set = 2, binding = 8) uniform sampler reflectivitySamplerSampler;
-        #define reflectivitySampler sampler2D(reflectivitySamplerTexture, reflectivitySamplerSampler)
-    #else
-        uniform sampler2D reflectivitySampler;
-    #endif
+    uniform sampler2D reflectivitySampler;
 #endif
 
 #ifdef AMBIENT
@@ -102,13 +78,7 @@
         varying vec2 vAmbientUV;
     #endif
 
-    #ifdef WEBGPU
-        layout(set = 2, binding = 9) uniform texture2D ambientSamplerTexture;
-        layout(set = 2, binding = 10) uniform sampler ambientSamplerSampler;
-        #define ambientSampler sampler2D(ambientSamplerTexture, ambientSamplerSampler)
-    #else
-        uniform sampler2D ambientSampler;
-    #endif
+    uniform sampler2D ambientSampler;
 #endif
 
 #ifdef EMISSIVE
@@ -120,13 +90,7 @@
         varying vec2 vEmissiveUV;
     #endif
 
-    #ifdef WEBGPU
-        layout(set = 2, binding = 11) uniform texture2D emissiveSamplerTexture;
-        layout(set = 2, binding = 12) uniform sampler emissiveSamplerSampler;
-        #define emissiveSampler sampler2D(emissiveSamplerTexture, emissiveSamplerSampler)
-    #else
-        uniform sampler2D emissiveSampler;
-    #endif
+    uniform sampler2D emissiveSampler;
 #endif
 
 #ifdef BUMP
@@ -138,13 +102,7 @@
         varying vec2 vBumpUV;
     #endif
 
-    #ifdef WEBGPU
-        layout(set = 2, binding = 13) uniform texture2D bumpSamplerTexture;
-        layout(set = 2, binding = 14) uniform sampler bumpSamplerSampler;
-        #define bumpSampler sampler2D(bumpSamplerTexture, bumpSamplerSampler)
-    #else
-        uniform sampler2D bumpSampler;
-    #endif
+    uniform sampler2D bumpSampler;
 #endif
 
 #ifdef OPACITY