Browse Source

WebGPURenderer: Introduce Bind Groups (#28705)

* introduce bind group

* WebGLBackend and revision

* cleanup
sunag 1 năm trước cách đây
mục cha
commit
4b9c621ff3

+ 16 - 9
examples/jsm/nodes/accessors/CameraNode.js

@@ -1,12 +1,19 @@
 import { uniform } from '../core/UniformNode.js';
+import { sharedUniformGroup } from '../core/UniformGroupNode.js';
 import { Vector3 } from 'three';
 
-export const cameraNear = /*#__PURE__*/ uniform( 'float' ).onRenderUpdate( ( { camera } ) => camera.near );
-export const cameraFar = /*#__PURE__*/ uniform( 'float' ).onRenderUpdate( ( { camera } ) => camera.far );
-export const cameraLogDepth = /*#__PURE__*/ uniform( 'float' ).onRenderUpdate( ( { camera } ) => 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
-export const cameraProjectionMatrix = /*#__PURE__*/ uniform( 'mat4' ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
-export const cameraProjectionMatrixInverse = /*#__PURE__*/ uniform( 'mat4' ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
-export const cameraViewMatrix = /*#__PURE__*/ uniform( 'mat4' ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
-export const cameraWorldMatrix = /*#__PURE__*/ uniform( 'mat4' ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
-export const cameraNormalMatrix = /*#__PURE__*/ uniform( 'mat3' ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
-export const cameraPosition = /*#__PURE__*/ uniform( new Vector3() ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );
+const cameraGroup = /*#__PURE__*/ sharedUniformGroup( 'camera' ).onRenderUpdate( () => {
+
+	cameraGroup.needsUpdate = true;
+
+} );
+
+export const cameraNear = /*#__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.near );
+export const cameraFar = /*#__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.far );
+export const cameraLogDepth = /*#__PURE__*/ uniform( 'float' ).label( 'cameraLogDepth' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ) );
+export const cameraProjectionMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrix );
+export const cameraProjectionMatrixInverse = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.projectionMatrixInverse );
+export const cameraViewMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorldInverse );
+export const cameraWorldMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.matrixWorld );
+export const cameraNormalMatrix = /*#__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera } ) => camera.normalMatrix );
+export const cameraPosition = /*#__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( cameraGroup ).onRenderUpdate( ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ) );

+ 96 - 17
examples/jsm/nodes/core/NodeBuilder.js

@@ -15,6 +15,8 @@ import {
 	ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform
 } from '../../renderers/common/nodes/NodeUniform.js';
 
+import BindGroup from '../../renderers/common/BindGroup.js';
+
 import {
 	REVISION, RenderTarget, Color, Vector2, Vector3, Vector4, IntType, UnsignedIntType, Float16BufferAttribute,
 	LinearFilter, LinearMipmapNearestFilter, NearestMipmapLinearFilter, LinearMipmapLinearFilter
@@ -28,7 +30,7 @@ import ChainMap from '../../renderers/common/ChainMap.js';
 
 import PMREMGenerator from '../../renderers/common/extras/PMREMGenerator.js';
 
-const uniformsGroupCache = new ChainMap();
+const bindGroupsCache = new ChainMap();
 
 const typeFromLength = new Map( [
 	[ 2, 'vec2' ],
@@ -87,9 +89,9 @@ class NodeBuilder {
 		this.flowCode = { vertex: '', fragment: '', compute: '' };
 		this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
 		this.structs = { vertex: [], fragment: [], compute: [], index: 0 };
-		this.bindings = { vertex: [], fragment: [], compute: [] };
-		this.bindingsOffset = { vertex: 0, fragment: 0, compute: 0 };
-		this.bindingsArray = null;
+		this.bindings = { vertex: {}, fragment: {}, compute: {} };
+		this.bindingsIndexes = {};
+		this.bindGroups = null;
 		this.attributes = [];
 		this.bufferAttributes = [];
 		this.varyings = [];
@@ -101,6 +103,8 @@ class NodeBuilder {
 		this.stacks = [];
 		this.tab = '\t';
 
+		this.instanceBindGroups = true;
+
 		this.currentFunctionNode = null;
 
 		this.context = {
@@ -144,54 +148,129 @@ class NodeBuilder {
 
 	}
 
-	_getSharedBindings( bindings ) {
+	_getBindGroup( groupName, bindings ) {
+
+		// cache individual uniforms group
+
+		const bindingsArray = [];
 
-		const shared = [];
+		let sharedGroup = true;
 
 		for ( const binding of bindings ) {
 
-			if ( binding.shared === true ) {
+			if ( binding.groupNode.shared === true ) {
 
 				// nodes is the chainmap key
 				const nodes = binding.getNodes();
 
-				let sharedBinding = uniformsGroupCache.get( nodes );
+				let sharedBinding = bindGroupsCache.get( nodes );
 
 				if ( sharedBinding === undefined ) {
 
-					uniformsGroupCache.set( nodes, binding );
+					bindGroupsCache.set( nodes, binding );
 
 					sharedBinding = binding;
 
 				}
 
-				shared.push( sharedBinding );
+				bindingsArray.push( sharedBinding );
 
 			} else {
 
-				shared.push( binding );
+				bindingsArray.push( binding );
+
+				sharedGroup = false;
 
 			}
 
 		}
 
-		return shared;
+		//
+
+		let bindGroup;
+
+		if ( sharedGroup ) {
+
+			bindGroup = bindGroupsCache.get( bindingsArray );
+
+			if ( bindGroup === undefined ) {
+
+				bindGroup = new BindGroup( groupName, bindingsArray );
+				bindGroupsCache.set( bindingsArray, bindGroup );
+
+			}
+
+		} else {
+
+			bindGroup = new BindGroup( groupName, bindingsArray );
+
+		}
+
+		return bindGroup;
+
+	}
+
+	getBindGroupArray( groupName, shaderStage ) {
+
+		const bindings = this.bindings[ shaderStage ];
+
+		let bindGroup = bindings[ groupName ];
+
+		if ( bindGroup === undefined ) {
+
+			if ( this.bindingsIndexes[ groupName ] === undefined ) {
+
+				this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length };
+
+			}
+
+			bindings[ groupName ] = bindGroup = [];
+
+		}
+
+		return bindGroup;
 
 	}
 
 	getBindings() {
 
-		let bindingsArray = this.bindingsArray;
+		let bindingsGroups = this.bindGroups;
 
-		if ( bindingsArray === null ) {
+		if ( bindingsGroups === null ) {
 
+			const groups = {};
 			const bindings = this.bindings;
 
-			this.bindingsArray = bindingsArray = this._getSharedBindings( ( this.material !== null ) ? [ ...bindings.vertex, ...bindings.fragment ] : bindings.compute );
+			for ( const shaderStage of shaderStages ) {
+
+				for ( const groupName in bindings[ shaderStage ] ) {
+
+					const uniforms = bindings[ shaderStage ][ groupName ];
+
+					const groupUniforms = groups[ groupName ] || ( groups[ groupName ] = [] );
+					groupUniforms.push( ...uniforms );
+
+				}
+
+			}
+
+			bindingsGroups = [];
+
+			for ( const groupName in groups ) {
+
+				const group = groups[ groupName ];
+
+				const bindingsGroup = this._getBindGroup( groupName, group );
+
+				bindingsGroups.push( bindingsGroup );
+
+			}
+
+			this.bindGroups = bindingsGroups;
 
 		}
 
-		return bindingsArray;
+		return bindingsGroups;
 
 	}
 
@@ -1280,7 +1359,7 @@ class NodeBuilder {
 
 	getSignature() {
 
-		return `// Three.js r${ REVISION } - NodeMaterial System\n`;
+		return `// Three.js r${ REVISION } - Node System\n`;
 
 	}
 

+ 1 - 2
examples/jsm/nodes/core/NodeUniform.js

@@ -1,13 +1,12 @@
 class NodeUniform {
 
-	constructor( name, type, node, needsUpdate = undefined ) {
+	constructor( name, type, node ) {
 
 		this.isNodeUniform = true;
 
 		this.name = name;
 		this.type = type;
 		this.node = node.getSelf();
-		this.needsUpdate = needsUpdate;
 
 	}
 

+ 1 - 1
examples/jsm/renderers/common/Backend.js

@@ -42,7 +42,7 @@ class Backend {
 
 	createBindings( renderObject ) { }
 
-	updateBindings( renderObject ) { }
+	_setupBindingsIndexes( renderObject ) { }
 
 	// pipeline
 

+ 16 - 0
examples/jsm/renderers/common/BindGroup.js

@@ -0,0 +1,16 @@
+let _id = 0;
+
+class BindGroup {
+
+	constructor( name = '', bindings = [] ) {
+
+		this.name = name;
+		this.bindings = bindings;
+
+		this.id = _id ++;
+
+	}
+
+}
+
+export default BindGroup;

+ 38 - 23
examples/jsm/renderers/common/Bindings.js

@@ -22,61 +22,77 @@ class Bindings extends DataMap {
 
 		const bindings = renderObject.getBindings();
 
-		const data = this.get( renderObject );
+		for ( const bindGroup of bindings ) {
 
-		if ( data.bindings !== bindings ) {
+			const groupData = this.get( bindGroup );
 
-			// each object defines an array of bindings (ubos, textures, samplers etc.)
+			if ( groupData.bindGroup === undefined ) {
 
-			data.bindings = bindings;
+				// each object defines an array of bindings (ubos, textures, samplers etc.)
 
-			this._init( bindings );
+				this._init( bindGroup );
 
-			this.backend.createBindings( bindings );
+				this.backend.createBindings( bindGroup, bindings );
+
+				groupData.bindGroup = bindGroup;
+
+			}
 
 		}
 
-		return data.bindings;
+		return bindings;
 
 	}
 
 	getForCompute( computeNode ) {
 
-		const data = this.get( computeNode );
+		const bindings = this.nodes.getForCompute( computeNode ).bindings;
 
-		if ( data.bindings === undefined ) {
+		for ( const bindGroup of bindings ) {
 
-			const nodeBuilderState = this.nodes.getForCompute( computeNode );
+			const groupData = this.get( bindGroup );
 
-			const bindings = nodeBuilderState.bindings;
+			if ( groupData.bindGroup === undefined ) {
 
-			data.bindings = bindings;
+				this._init( bindGroup );
 
-			this._init( bindings );
+				this.backend.createBindings( bindGroup, bindings );
 
-			this.backend.createBindings( bindings );
+				groupData.bindGroup = bindGroup;
+
+			}
 
 		}
 
-		return data.bindings;
+		return bindings;
 
 	}
 
 	updateForCompute( computeNode ) {
 
-		this._update( computeNode, this.getForCompute( computeNode ) );
+		this._updateBindings( computeNode, this.getForCompute( computeNode ) );
 
 	}
 
 	updateForRender( renderObject ) {
 
-		this._update( renderObject, this.getForRender( renderObject ) );
+		this._updateBindings( renderObject, this.getForRender( renderObject ) );
 
 	}
 
-	_init( bindings ) {
+	_updateBindings( object, bindings ) {
+
+		for ( const bindGroup of bindings ) {
 
-		for ( const binding of bindings ) {
+			this._update( object, bindGroup, bindings );
+
+		}
+
+	}
+
+	_init( bindGroup ) {
+
+		for ( const binding of bindGroup.bindings ) {
 
 			if ( binding.isSampledTexture ) {
 
@@ -94,7 +110,7 @@ class Bindings extends DataMap {
 
 	}
 
-	_update( object, bindings ) {
+	_update( object, bindGroup, bindings ) {
 
 		const { backend } = this;
 
@@ -102,7 +118,7 @@ class Bindings extends DataMap {
 
 		// iterate over all bindings and check if buffer updates or a new binding group is required
 
-		for ( const binding of bindings ) {
+		for ( const binding of bindGroup.bindings ) {
 
 			if ( binding.isNodeUniformsGroup ) {
 
@@ -152,7 +168,6 @@ class Bindings extends DataMap {
 
 				}
 
-
 				if ( texture.isStorageTexture === true ) {
 
 					const textureData = this.get( texture );
@@ -179,7 +194,7 @@ class Bindings extends DataMap {
 
 			const pipeline = this.pipelines.getForRender( object );
 
-			this.backend.updateBindings( bindings, pipeline );
+			this.backend.updateBindings( bindGroup, bindings, pipeline );
 
 		}
 

+ 22 - 9
examples/jsm/renderers/common/nodes/NodeBuilderState.js

@@ -1,6 +1,8 @@
+import BindGroup from '../BindGroup.js';
+
 class NodeBuilderState {
 
-	constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, transforms = [] ) {
+	constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, instanceBindGroups = true, transforms = [] ) {
 
 		this.vertexShader = vertexShader;
 		this.fragmentShader = fragmentShader;
@@ -14,29 +16,40 @@ class NodeBuilderState {
 		this.updateBeforeNodes = updateBeforeNodes;
 		this.updateAfterNodes = updateAfterNodes;
 
+		this.instanceBindGroups = instanceBindGroups;
+
 		this.usedTimes = 0;
 
 	}
 
 	createBindings() {
 
-		const bindingsArray = [];
+		const bindings = [];
 
-		for ( const instanceBinding of this.bindings ) {
+		for ( const instanceGroup of this.bindings ) {
 
-			let binding = instanceBinding;
+			const shared = this.instanceBindGroups && instanceGroup.bindings[ 0 ].groupNode.shared;
 
-			if ( instanceBinding.shared !== true ) {
+			if ( shared !== true ) {
 
-				binding = instanceBinding.clone();
+				const bindingsGroup = new BindGroup( instanceGroup.name );
+				bindings.push( bindingsGroup );
 
-			}
+				for ( const instanceBinding of instanceGroup.bindings ) {
+
+					bindingsGroup.bindings.push( instanceBinding.clone() );
 
-			bindingsArray.push( binding );
+				}
+
+			} else {
+
+				bindings.push( instanceGroup );
+
+			}
 
 		}
 
-		return bindingsArray;
+		return bindings;
 
 	}
 

+ 6 - 5
examples/jsm/renderers/common/nodes/NodeSampledTexture.js

@@ -2,11 +2,12 @@ import { SampledTexture } from '../SampledTexture.js';
 
 class NodeSampledTexture extends SampledTexture {
 
-	constructor( name, textureNode, access = null ) {
+	constructor( name, textureNode, groupNode, access = null ) {
 
 		super( name, textureNode ? textureNode.value : null );
 
 		this.textureNode = textureNode;
+		this.groupNode = groupNode;
 
 		this.access = access;
 
@@ -38,9 +39,9 @@ class NodeSampledTexture extends SampledTexture {
 
 class NodeSampledCubeTexture extends NodeSampledTexture {
 
-	constructor( name, textureNode, access ) {
+	constructor( name, textureNode, groupNode, access ) {
 
-		super( name, textureNode, access );
+		super( name, textureNode, groupNode, access );
 
 		this.isSampledCubeTexture = true;
 
@@ -50,9 +51,9 @@ class NodeSampledCubeTexture extends NodeSampledTexture {
 
 class NodeSampledTexture3D extends NodeSampledTexture {
 
-	constructor( name, textureNode, access ) {
+	constructor( name, textureNode, groupNode, access ) {
 
-		super( name, textureNode, access );
+		super( name, textureNode, groupNode, access );
 
 		this.isSampledTexture3D = true;
 

+ 2 - 1
examples/jsm/renderers/common/nodes/NodeSampler.js

@@ -2,11 +2,12 @@ import Sampler from '../Sampler.js';
 
 class NodeSampler extends Sampler {
 
-	constructor( name, textureNode ) {
+	constructor( name, textureNode, groupNode ) {
 
 		super( name, textureNode ? textureNode.value : null );
 
 		this.textureNode = textureNode;
+		this.groupNode = groupNode;
 
 	}
 

+ 2 - 1
examples/jsm/renderers/common/nodes/NodeStorageBuffer.js

@@ -4,11 +4,12 @@ let _id = 0;
 
 class NodeStorageBuffer extends StorageBuffer {
 
-	constructor( nodeUniform ) {
+	constructor( nodeUniform, groupNode ) {
 
 		super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
 
 		this.nodeUniform = nodeUniform;
+		this.groupNode = groupNode;
 
 	}
 

+ 2 - 1
examples/jsm/renderers/common/nodes/NodeUniformBuffer.js

@@ -4,11 +4,12 @@ let _id = 0;
 
 class NodeUniformBuffer extends UniformBuffer {
 
-	constructor( nodeUniform ) {
+	constructor( nodeUniform, groupNode ) {
 
 		super( 'UniformBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
 
 		this.nodeUniform = nodeUniform;
+		this.groupNode = groupNode;
 
 	}
 

+ 0 - 6
examples/jsm/renderers/common/nodes/NodeUniformsGroup.js

@@ -15,12 +15,6 @@ class NodeUniformsGroup extends UniformsGroup {
 
 	}
 
-	get shared() {
-
-		return this.groupNode.shared;
-
-	}
-
 	getNodes() {
 
 		const nodes = [];

+ 1 - 0
examples/jsm/renderers/common/nodes/Nodes.js

@@ -183,6 +183,7 @@ class Nodes extends DataMap {
 			nodeBuilder.updateNodes,
 			nodeBuilder.updateBeforeNodes,
 			nodeBuilder.updateAfterNodes,
+			nodeBuilder.instanceBindGroups,
 			nodeBuilder.transforms
 		);
 

+ 55 - 41
examples/jsm/renderers/webgl/WebGLBackend.js

@@ -567,7 +567,7 @@ class WebGLBackend extends Backend {
 
 	}
 
-	draw( renderObject, info ) {
+	draw( renderObject/*, info*/ ) {
 
 		const { object, pipeline, material, context } = renderObject;
 		const { programGPU } = this.get( pipeline );
@@ -975,7 +975,9 @@ class WebGLBackend extends Backend {
 
 		// Bindings
 
-		this._setupBindings( renderObject.getBindings(), programGPU );
+		const bindings = renderObject.getBindings();
+
+		this._setupBindings( bindings, programGPU );
 
 		//
 
@@ -1025,7 +1027,7 @@ class WebGLBackend extends Backend {
 		gl.transformFeedbackVaryings(
 			programGPU,
 			transformVaryingNames,
-			gl.SEPARATE_ATTRIBS,
+			gl.SEPARATE_ATTRIBS
 		);
 
 		gl.linkProgram( programGPU );
@@ -1041,7 +1043,7 @@ class WebGLBackend extends Backend {
 
 		// Bindings
 
-		this.createBindings( bindings );
+		this.createBindings( null, bindings );
 
 		this._setupBindings( bindings, programGPU );
 
@@ -1081,44 +1083,48 @@ class WebGLBackend extends Backend {
 
 	}
 
-	createBindings( bindings ) {
+	createBindings( bindGroup, bindings ) {
 
-		this.updateBindings( bindings );
+		this.updateBindings( bindGroup, bindings );
 
 	}
 
-	updateBindings( bindings ) {
+	updateBindings( bindGroup, bindings ) {
 
 		const { gl } = this;
 
 		let groupIndex = 0;
 		let textureIndex = 0;
 
-		for ( const binding of bindings ) {
+		for ( const bindGroup of bindings ) {
 
-			if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
+			for ( const binding of bindGroup.bindings ) {
 
-				const bufferGPU = gl.createBuffer();
-				const data = binding.buffer;
+				if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
 
-				gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
-				gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
-				gl.bindBufferBase( gl.UNIFORM_BUFFER, groupIndex, bufferGPU );
+					const bufferGPU = gl.createBuffer();
+					const data = binding.buffer;
 
-				this.set( binding, {
-					index: groupIndex ++,
-					bufferGPU
-				} );
+					gl.bindBuffer( gl.UNIFORM_BUFFER, bufferGPU );
+					gl.bufferData( gl.UNIFORM_BUFFER, data, gl.DYNAMIC_DRAW );
+					gl.bindBufferBase( gl.UNIFORM_BUFFER, groupIndex, bufferGPU );
 
-			} else if ( binding.isSampledTexture ) {
+					this.set( binding, {
+						index: groupIndex ++,
+						bufferGPU
+					} );
 
-				const { textureGPU, glTextureType } = this.get( binding.texture );
+				} else if ( binding.isSampledTexture ) {
 
-				this.set( binding, {
-					index: textureIndex ++,
-					textureGPU,
-					glTextureType
-				} );
+					const { textureGPU, glTextureType } = this.get( binding.texture );
+
+					this.set( binding, {
+						index: textureIndex ++,
+						textureGPU,
+						glTextureType
+					} );
+
+				}
 
 			}
 
@@ -1526,20 +1532,24 @@ class WebGLBackend extends Backend {
 
 		const gl = this.gl;
 
-		for ( const binding of bindings ) {
+		for ( const bindGroup of bindings ) {
 
-			const bindingData = this.get( binding );
-			const index = bindingData.index;
+			for ( const binding of bindGroup.bindings ) {
+
+				const bindingData = this.get( binding );
+				const index = bindingData.index;
 
-			if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
+				if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
 
-				const location = gl.getUniformBlockIndex( programGPU, binding.name );
-				gl.uniformBlockBinding( programGPU, location, index );
+					const location = gl.getUniformBlockIndex( programGPU, binding.name );
+					gl.uniformBlockBinding( programGPU, location, index );
 
-			} else if ( binding.isSampledTexture ) {
+				} else if ( binding.isSampledTexture ) {
 
-				const location = gl.getUniformLocation( programGPU, binding.name );
-				gl.uniform1i( location, index );
+					const location = gl.getUniformLocation( programGPU, binding.name );
+					gl.uniform1i( location, index );
+
+				}
 
 			}
 
@@ -1551,18 +1561,22 @@ class WebGLBackend extends Backend {
 
 		const { gl, state } = this;
 
-		for ( const binding of bindings ) {
+		for ( const bindGroup of bindings ) {
 
-			const bindingData = this.get( binding );
-			const index = bindingData.index;
+			for ( const binding of bindGroup.bindings ) {
 
-			if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
+				const bindingData = this.get( binding );
+				const index = bindingData.index;
 
-				gl.bindBufferBase( gl.UNIFORM_BUFFER, index, bindingData.bufferGPU );
+				if ( binding.isUniformsGroup || binding.isUniformBuffer ) {
 
-			} else if ( binding.isSampledTexture ) {
+					gl.bindBufferBase( gl.UNIFORM_BUFFER, index, bindingData.bufferGPU );
 
-				state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index );
+				} else if ( binding.isSampledTexture ) {
+
+					state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index );
+
+				}
 
 			}
 

+ 29 - 22
examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js

@@ -54,6 +54,8 @@ class GLSLNodeBuilder extends NodeBuilder {
 		this.uniformGroups = {};
 		this.transforms = [];
 
+		this.instanceBindGroups = false;
+
 	}
 
 	getMethod( method ) {
@@ -62,12 +64,6 @@ class GLSLNodeBuilder extends NodeBuilder {
 
 	}
 
-	getPropertyName( node, shaderStage ) {
-
-		return super.getPropertyName( node, shaderStage );
-
-	}
-
 	getOutputStructName() {
 
 		return '';
@@ -173,6 +169,18 @@ ${ flowData.code }
 
 	}
 
+	getPropertyName( node, shaderStage = this.shaderStage ) {
+
+		if ( node.isNodeUniform && node.node.isTextureNode !== true && node.node.isBufferNode !== true ) {
+
+			return shaderStage.charAt( 0 ) + '_' + node.name;
+
+		}
+
+		return super.getPropertyName( node, shaderStage );
+
+	}
+
 	generatePBO( storageArrayElementNode ) {
 
 		const { node, indexNode } = storageArrayElementNode;
@@ -344,7 +352,6 @@ ${ flowData.code }
 			let snippet = null;
 			let group = false;
 
-
 			if ( uniform.type === 'texture' ) {
 
 				const texture = uniform.node.value;
@@ -398,7 +405,7 @@ ${ flowData.code }
 
 				const vectorType = this.getVectorType( uniform.type );
 
-				snippet = `${vectorType} ${uniform.name};`;
+				snippet = `${ vectorType } ${ this.getPropertyName( uniform, shaderStage ) };`;
 
 				group = true;
 
@@ -835,40 +842,40 @@ void main() {
 
 		if ( uniformGPU === undefined ) {
 
-			if ( type === 'texture' ) {
+			const group = node.groupNode;
+			const groupName = group.name;
 
-				uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node );
+			const bindings = this.getBindGroupArray( groupName, shaderStage );
 
-				this.bindings[ shaderStage ].push( uniformGPU );
+			if ( type === 'texture' ) {
 
-			} else if ( type === 'cubeTexture' ) {
+				uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node, group );
+				bindings.push( uniformGPU );
 
-				uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node );
+			} else if ( type === 'cubeTexture' ) {
 
-				this.bindings[ shaderStage ].push( uniformGPU );
+				uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group );
+				bindings.push( uniformGPU );
 
 			} else if ( type === 'texture3D' ) {
 
-				uniformGPU = new NodeSampledTexture3D( uniformNode.name, uniformNode.node );
-				this.bindings[ shaderStage ].push( uniformGPU );
+				uniformGPU = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, group );
+				bindings.push( uniformGPU );
 
 			} else if ( type === 'buffer' ) {
 
 				node.name = `NodeBuffer_${ node.id }`;
 				uniformNode.name = `buffer${ node.id }`;
 
-				const buffer = new NodeUniformBuffer( node );
+				const buffer = new NodeUniformBuffer( node, group );
 				buffer.name = node.name;
 
-				this.bindings[ shaderStage ].push( buffer );
+				bindings.push( buffer );
 
 				uniformGPU = buffer;
 
 			} else {
 
-				const group = node.groupNode;
-				const groupName = group.name;
-
 				const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} );
 
 				let uniformsGroup = uniformsStage[ groupName ];
@@ -880,7 +887,7 @@ void main() {
 
 					uniformsStage[ groupName ] = uniformsGroup;
 
-					this.bindings[ shaderStage ].push( uniformsGroup );
+					bindings.push( uniformsGroup );
 
 				}
 

+ 23 - 11
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -766,10 +766,16 @@ class WebGPUBackend extends Backend {
 		const pipelineGPU = this.get( pipeline ).pipeline;
 		passEncoderGPU.setPipeline( pipelineGPU );
 
-		// bind group
+		// bind groups
 
-		const bindGroupGPU = this.get( bindings ).group;
-		passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+		for ( let i = 0, l = bindings.length; i < l; i ++ ) {
+
+			const bindGroup = bindings[ i ];
+			const bindingsData = this.get( bindGroup );
+
+			passEncoderGPU.setBindGroup( i, bindingsData.group );
+
+		}
 
 		passEncoderGPU.dispatchWorkgroups( computeNode.dispatchCount );
 
@@ -793,7 +799,7 @@ class WebGPUBackend extends Backend {
 
 		const { object, geometry, context, pipeline } = renderObject;
 
-		const bindingsData = this.get( renderObject.getBindings() );
+		const bindings = renderObject.getBindings();
 		const contextData = this.get( context );
 		const pipelineGPU = this.get( pipeline ).pipeline;
 		const currentSets = contextData.currentSets;
@@ -823,10 +829,16 @@ class WebGPUBackend extends Backend {
 
 		}
 
-		// bind group
+		// bind groups
 
-		const bindGroupGPU = bindingsData.group;
-		passEncoderGPU.setBindGroup( 0, bindGroupGPU );
+		for ( let i = 0, l = bindings.length; i < l; i ++ ) {
+
+			const bindGroup = bindings[ i ];
+			const bindingsData = this.get( bindGroup );
+
+			passEncoderGPU.setBindGroup( i, bindingsData.group );
+
+		}
 
 		// attributes
 
@@ -1198,15 +1210,15 @@ class WebGPUBackend extends Backend {
 
 	// bindings
 
-	createBindings( bindings ) {
+	createBindings( bindGroup ) {
 
-		this.bindingUtils.createBindings( bindings );
+		this.bindingUtils.createBindings( bindGroup );
 
 	}
 
-	updateBindings( bindings ) {
+	updateBindings( bindGroup ) {
 
-		this.bindingUtils.createBindings( bindings );
+		this.bindingUtils.createBindings( bindGroup );
 
 	}
 

+ 21 - 25
examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -449,7 +449,10 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 			let uniformGPU;
 
-			const bindings = this.bindings[ shaderStage ];
+			const group = node.groupNode;
+			const groupName = group.name;
+
+			const bindings = this.getBindGroupArray( groupName, shaderStage );
 
 			if ( type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'texture3D' ) {
 
@@ -457,15 +460,15 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 				if ( type === 'texture' || type === 'storageTexture' ) {
 
-					texture = new NodeSampledTexture( uniformNode.name, uniformNode.node, node.access ? node.access : null );
+					texture = new NodeSampledTexture( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
 
 				} else if ( type === 'cubeTexture' ) {
 
-					texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, node.access ? node.access : null );
+					texture = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
 
 				} else if ( type === 'texture3D' ) {
 
-					texture = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, node.access ? node.access : null );
+					texture = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, group, node.access ? node.access : null );
 
 				}
 
@@ -474,7 +477,7 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 				if ( shaderStage === 'fragment' && this.isUnfilterable( node.value ) === false && texture.store === false ) {
 
-					const sampler = new NodeSampler( `${uniformNode.name}_sampler`, uniformNode.node );
+					const sampler = new NodeSampler( `${uniformNode.name}_sampler`, uniformNode.node, group );
 					sampler.setVisibility( gpuShaderStageLib[ shaderStage ] );
 
 					bindings.push( sampler, texture );
@@ -492,7 +495,7 @@ class WGSLNodeBuilder extends NodeBuilder {
 			} else if ( type === 'buffer' || type === 'storageBuffer' ) {
 
 				const bufferClass = type === 'storageBuffer' ? NodeStorageBuffer : NodeUniformBuffer;
-				const buffer = new bufferClass( node );
+				const buffer = new bufferClass( node, group );
 				buffer.setVisibility( gpuShaderStageLib[ shaderStage ] );
 
 				bindings.push( buffer );
@@ -501,9 +504,6 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 			} else {
 
-				const group = node.groupNode;
-				const groupName = group.name;
-
 				const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} );
 
 				let uniformsGroup = uniformsStage[ groupName ];
@@ -527,12 +527,6 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 			nodeData.uniformGPU = uniformGPU;
 
-			if ( shaderStage === 'vertex' ) {
-
-				this.bindingsOffset[ 'fragment' ] = bindings.length;
-
-			}
-
 		}
 
 		return uniformNode;
@@ -816,10 +810,11 @@ ${ flowData.code }
 		const structSnippets = [];
 		const uniformGroups = {};
 
-		let index = this.bindingsOffset[ shaderStage ];
-
 		for ( const uniform of uniforms ) {
 
+			const groundName = uniform.groupNode.name;
+			const uniformIndexes = this.bindingsIndexes[ groundName ];
+
 			if ( uniform.type === 'texture' || uniform.type === 'cubeTexture' || uniform.type === 'storageTexture' || uniform.type === 'texture3D' ) {
 
 				const texture = uniform.node.value;
@@ -828,11 +823,11 @@ ${ flowData.code }
 
 					if ( texture.isDepthTexture === true && texture.compareFunction !== null ) {
 
-						bindingSnippets.push( `@binding( ${index ++} ) @group( 0 ) var ${uniform.name}_sampler : sampler_comparison;` );
+						bindingSnippets.push( `@binding( ${ uniformIndexes.binding ++ } ) @group( ${ uniformIndexes.group } ) var ${ uniform.name }_sampler : sampler_comparison;` );
 
 					} else {
 
-						bindingSnippets.push( `@binding( ${index ++} ) @group( 0 ) var ${uniform.name}_sampler : sampler;` );
+						bindingSnippets.push( `@binding( ${ uniformIndexes.binding ++ } ) @group( ${ uniformIndexes.group } ) var ${ uniform.name }_sampler : sampler;` );
 
 					}
 
@@ -865,7 +860,7 @@ ${ flowData.code }
 					const format = getFormat( texture );
 					const access = this.getStorageAccess( uniform.node );
 
-					textureType = `texture_storage_2d<${ format }, ${access}>`;
+					textureType = `texture_storage_2d<${ format }, ${ access }>`;
 
 				} else {
 
@@ -875,7 +870,7 @@ ${ flowData.code }
 
 				}
 
-				bindingSnippets.push( `@binding( ${index ++} ) @group( 0 ) var ${uniform.name} : ${textureType};` );
+				bindingSnippets.push( `@binding( ${ uniformIndexes.binding ++ } ) @group( ${ uniformIndexes.group } ) var ${ uniform.name } : ${ textureType };` );
 
 			} else if ( uniform.type === 'buffer' || uniform.type === 'storageBuffer' ) {
 
@@ -884,10 +879,10 @@ ${ flowData.code }
 				const bufferCount = bufferNode.bufferCount;
 
 				const bufferCountSnippet = bufferCount > 0 ? ', ' + bufferCount : '';
-				const bufferSnippet = `\t${uniform.name} : array< ${bufferType}${bufferCountSnippet} >\n`;
+				const bufferSnippet = `\t${ uniform.name } : array< ${ bufferType }${ bufferCountSnippet } >\n`;
 				const bufferAccessMode = bufferNode.isStorageBufferNode ? 'storage,read_write' : 'uniform';
 
-				bufferSnippets.push( this._getWGSLStructBinding( 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, index ++ ) );
+				bufferSnippets.push( this._getWGSLStructBinding( 'NodeBuffer_' + bufferNode.id, bufferSnippet, bufferAccessMode, uniformIndexes.binding ++, uniformIndexes.group ) );
 
 			} else {
 
@@ -895,7 +890,8 @@ ${ flowData.code }
 				const groupName = uniform.groupNode.name;
 
 				const group = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = {
-					index: index ++,
+					index: uniformIndexes.binding ++,
+					id: uniformIndexes.group,
 					snippets: []
 				} );
 
@@ -909,7 +905,7 @@ ${ flowData.code }
 
 			const group = uniformGroups[ name ];
 
-			structSnippets.push( this._getWGSLStructBinding( name, group.snippets.join( ',\n' ), 'uniform', group.index ) );
+			structSnippets.push( this._getWGSLStructBinding( name, group.snippets.join( ',\n' ), 'uniform', group.index, group.id ) );
 
 		}
 

+ 9 - 9
examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js

@@ -11,7 +11,7 @@ class WebGPUBindingUtils {
 
 	}
 
-	createBindingsLayout( bindings ) {
+	createBindingsLayout( bindGroup ) {
 
 		const backend = this.backend;
 		const device = backend.device;
@@ -20,7 +20,7 @@ class WebGPUBindingUtils {
 
 		let index = 0;
 
-		for ( const binding of bindings ) {
+		for ( const binding of bindGroup.bindings ) {
 
 			const bindingGPU = {
 				binding: index ++,
@@ -126,19 +126,18 @@ class WebGPUBindingUtils {
 
 	}
 
-	createBindings( bindings ) {
+	createBindings( bindGroup ) {
 
 		const backend = this.backend;
-		const bindingsData = backend.get( bindings );
+		const bindingsData = backend.get( bindGroup );
 
 		// setup (static) binding layout and (dynamic) binding group
 
-		const bindLayoutGPU = this.createBindingsLayout( bindings );
-		const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU );
+		const bindLayoutGPU = this.createBindingsLayout( bindGroup );
+		const bindGroupGPU = this.createBindGroup( bindGroup, bindLayoutGPU );
 
 		bindingsData.layout = bindLayoutGPU;
 		bindingsData.group = bindGroupGPU;
-		bindingsData.bindings = bindings;
 
 	}
 
@@ -154,7 +153,7 @@ class WebGPUBindingUtils {
 
 	}
 
-	createBindGroup( bindings, layoutGPU ) {
+	createBindGroup( bindGroup, layoutGPU ) {
 
 		const backend = this.backend;
 		const device = backend.device;
@@ -162,7 +161,7 @@ class WebGPUBindingUtils {
 		let bindingPoint = 0;
 		const entriesGPU = [];
 
-		for ( const binding of bindings ) {
+		for ( const binding of bindGroup.bindings ) {
 
 			if ( binding.isUniformBuffer ) {
 
@@ -256,6 +255,7 @@ class WebGPUBindingUtils {
 		}
 
 		return device.createBindGroup( {
+			label: 'bindGroup_' + bindGroup.name,
 			layout: layoutGPU,
 			entries: entriesGPU
 		} );

+ 26 - 4
examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -54,7 +54,18 @@ class WebGPUPipelineUtils {
 		const utils = backend.utils;
 
 		const pipelineData = backend.get( pipeline );
-		const bindingsData = backend.get( renderObject.getBindings() );
+
+		// bind group layouts
+
+		const bindGroupLayouts = [];
+
+		for ( const bindGroup of renderObject.getBindings() ) {
+
+			const bindingsData = backend.get( bindGroup );
+
+			bindGroupLayouts.push( bindingsData.layout );
+
+		}
 
 		// vertex buffers
 
@@ -145,7 +156,7 @@ class WebGPUPipelineUtils {
 				alphaToCoverageEnabled: material.alphaToCoverage
 			},
 			layout: device.createPipelineLayout( {
-				bindGroupLayouts: [ bindingsData.layout ]
+				bindGroupLayouts
 			} )
 		};
 
@@ -209,12 +220,23 @@ class WebGPUPipelineUtils {
 		const computeProgram = backend.get( pipeline.computeProgram ).module;
 
 		const pipelineGPU = backend.get( pipeline );
-		const bindingsData = backend.get( bindings );
+
+		// bind group layouts
+
+		const bindGroupLayouts = [];
+
+		for ( const bindingsGroup of bindings ) {
+
+			const bindingsData = backend.get( bindingsGroup );
+
+			bindGroupLayouts.push( bindingsData.layout );
+
+		}
 
 		pipelineGPU.pipeline = device.createComputePipeline( {
 			compute: computeProgram,
 			layout: device.createPipelineLayout( {
-				bindGroupLayouts: [ bindingsData.layout ]
+				bindGroupLayouts
 			} )
 		} );