diff --git a/examples/jsm/nodes/accessors/CameraNode.js b/examples/jsm/nodes/accessors/CameraNode.js index 674e2666d6e0a2..c2070ec2add61c 100644 --- a/examples/jsm/nodes/accessors/CameraNode.js +++ b/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 ) ); diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 0a0945b2731547..db45d448fb2cf0 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/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`; } diff --git a/examples/jsm/nodes/core/NodeUniform.js b/examples/jsm/nodes/core/NodeUniform.js index cfb37e23f5a77e..4e7a7470ce015c 100644 --- a/examples/jsm/nodes/core/NodeUniform.js +++ b/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; } diff --git a/examples/jsm/renderers/common/Backend.js b/examples/jsm/renderers/common/Backend.js index 5bf86e627c9209..10360f2d89f352 100644 --- a/examples/jsm/renderers/common/Backend.js +++ b/examples/jsm/renderers/common/Backend.js @@ -42,7 +42,7 @@ class Backend { createBindings( renderObject ) { } - updateBindings( renderObject ) { } + _setupBindingsIndexes( renderObject ) { } // pipeline diff --git a/examples/jsm/renderers/common/BindGroup.js b/examples/jsm/renderers/common/BindGroup.js new file mode 100644 index 00000000000000..a56729a7bd69b1 --- /dev/null +++ b/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; diff --git a/examples/jsm/renderers/common/Bindings.js b/examples/jsm/renderers/common/Bindings.js index c6fbe4ed51156e..1fc80538879ec7 100644 --- a/examples/jsm/renderers/common/Bindings.js +++ b/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 ); } diff --git a/examples/jsm/renderers/common/nodes/NodeBuilderState.js b/examples/jsm/renderers/common/nodes/NodeBuilderState.js index 9ff5a09bfd8f0e..98e1bd631d4628 100644 --- a/examples/jsm/renderers/common/nodes/NodeBuilderState.js +++ b/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; } diff --git a/examples/jsm/renderers/common/nodes/NodeSampledTexture.js b/examples/jsm/renderers/common/nodes/NodeSampledTexture.js index 01000f4990bea5..47112e700540da 100644 --- a/examples/jsm/renderers/common/nodes/NodeSampledTexture.js +++ b/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; diff --git a/examples/jsm/renderers/common/nodes/NodeSampler.js b/examples/jsm/renderers/common/nodes/NodeSampler.js index 3658296f862780..7c4dbf3202388f 100644 --- a/examples/jsm/renderers/common/nodes/NodeSampler.js +++ b/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; } diff --git a/examples/jsm/renderers/common/nodes/NodeStorageBuffer.js b/examples/jsm/renderers/common/nodes/NodeStorageBuffer.js index 27fc647df5ae8f..67a436f729d0e0 100644 --- a/examples/jsm/renderers/common/nodes/NodeStorageBuffer.js +++ b/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; } diff --git a/examples/jsm/renderers/common/nodes/NodeUniformBuffer.js b/examples/jsm/renderers/common/nodes/NodeUniformBuffer.js index 3babfd1b7a33c3..a323bb1c71fb90 100644 --- a/examples/jsm/renderers/common/nodes/NodeUniformBuffer.js +++ b/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; } diff --git a/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js b/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js index e44a6f93a9c6a8..0e5e3bb758433c 100644 --- a/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js +++ b/examples/jsm/renderers/common/nodes/NodeUniformsGroup.js @@ -15,12 +15,6 @@ class NodeUniformsGroup extends UniformsGroup { } - get shared() { - - return this.groupNode.shared; - - } - getNodes() { const nodes = []; diff --git a/examples/jsm/renderers/common/nodes/Nodes.js b/examples/jsm/renderers/common/nodes/Nodes.js index d126d0ff01430a..4b92572535f4aa 100644 --- a/examples/jsm/renderers/common/nodes/Nodes.js +++ b/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 ); diff --git a/examples/jsm/renderers/webgl/WebGLBackend.js b/examples/jsm/renderers/webgl/WebGLBackend.js index f8924508975953..607eb237320a66 100644 --- a/examples/jsm/renderers/webgl/WebGLBackend.js +++ b/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 ); + + } } diff --git a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js index 769737f06df760..5c1eae95bf769a 100644 --- a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js +++ b/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 ); } diff --git a/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index f28e1bc41e0426..32f9bfc66f98b0 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/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 ); } diff --git a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js index 73910e176e37c7..3f03527e6a710a 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/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 ) ); } diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js index 6359f78b593dcc..13230bc645991a 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js +++ b/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 } ); diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js index d78112e3675f09..5e662e45017212 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/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 } ) } );