From d2cd50c20a07bcdeac9cfce19d7f9b777a486c40 Mon Sep 17 00:00:00 2001 From: sunag Date: Wed, 19 Jun 2024 17:35:25 -0300 Subject: [PATCH 1/3] introduce bind group --- examples/jsm/nodes/accessors/CameraNode.js | 25 ++++-- examples/jsm/nodes/core/NodeBuilder.js | 89 +++++++++++++++---- examples/jsm/renderers/common/BindGroup.js | 16 ++++ examples/jsm/renderers/common/Bindings.js | 58 +++++++----- .../common/nodes/NodeBuilderState.js | 27 ++++-- .../common/nodes/NodeSampledTexture.js | 11 +-- .../jsm/renderers/common/nodes/NodeSampler.js | 3 +- .../common/nodes/NodeStorageBuffer.js | 3 +- .../common/nodes/NodeUniformBuffer.js | 3 +- .../common/nodes/NodeUniformsGroup.js | 6 -- .../jsm/renderers/webgpu/WebGPUBackend.js | 26 ++++-- .../renderers/webgpu/nodes/WGSLNodeBuilder.js | 54 ++++++----- .../webgpu/utils/WebGPUBindingUtils.js | 9 +- .../webgpu/utils/WebGPUPipelineUtils.js | 30 ++++++- 14 files changed, 252 insertions(+), 108 deletions(-) create mode 100644 examples/jsm/renderers/common/BindGroup.js 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..38bbb90ff889e2 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 = []; @@ -144,54 +146,107 @@ 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; } 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 +1335,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/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..f875bb4ac542b9 100644 --- a/examples/jsm/renderers/common/Bindings.js +++ b/examples/jsm/renderers/common/Bindings.js @@ -20,63 +20,75 @@ class Bindings extends DataMap { getForRender( renderObject ) { - const bindings = renderObject.getBindings(); + const bindGroups = renderObject.getBindings(); - const data = this.get( renderObject ); + for ( const bindGroup of bindGroups ) { - if ( data.bindings !== bindings ) { + const groupData = this.get( bindGroup ); - // each object defines an array of bindings (ubos, textures, samplers etc.) + if ( groupData.bindings === 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 ); + + } } - return data.bindings; + return bindGroups; } getForCompute( computeNode ) { - const data = this.get( computeNode ); + const bindGroups = this.nodes.getForCompute( computeNode ).bindings; - if ( data.bindings === undefined ) { + for ( const bindGroup of bindGroups ) { - const nodeBuilderState = this.nodes.getForCompute( computeNode ); + const groupData = this.get( bindGroup ); - const bindings = nodeBuilderState.bindings; + if ( groupData.bindings === undefined ) { - data.bindings = bindings; + this._init( bindGroup ); - this._init( bindings ); + this.backend.createBindings( bindGroup ); - this.backend.createBindings( bindings ); + } } - return data.bindings; + return bindGroups; } updateForCompute( computeNode ) { - this._update( computeNode, this.getForCompute( computeNode ) ); + this._updateGroups( computeNode, this.getForCompute( computeNode ) ); } updateForRender( renderObject ) { - this._update( renderObject, this.getForRender( renderObject ) ); + this._updateGroups( renderObject, this.getForRender( renderObject ) ); + + } + + _updateGroups( object, bindGroups ) { + + for ( const bindGroup of bindGroups ) { + + this._update( object, bindGroup ); + + } } - _init( bindings ) { + _init( bindGroup ) { - for ( const binding of bindings ) { + for ( const binding of bindGroup.bindings ) { if ( binding.isSampledTexture ) { @@ -94,7 +106,7 @@ class Bindings extends DataMap { } - _update( object, bindings ) { + _update( object, bindGroup ) { const { backend } = this; @@ -102,7 +114,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 ) { @@ -179,7 +191,7 @@ class Bindings extends DataMap { const pipeline = this.pipelines.getForRender( object ); - this.backend.updateBindings( bindings, pipeline ); + this.backend.updateBindings( bindGroup, pipeline ); } diff --git a/examples/jsm/renderers/common/nodes/NodeBuilderState.js b/examples/jsm/renderers/common/nodes/NodeBuilderState.js index 9ff5a09bfd8f0e..813559346c5450 100644 --- a/examples/jsm/renderers/common/nodes/NodeBuilderState.js +++ b/examples/jsm/renderers/common/nodes/NodeBuilderState.js @@ -1,3 +1,5 @@ +import BindGroup from '../BindGroup.js'; + class NodeBuilderState { constructor( vertexShader, fragmentShader, computeShader, nodeAttributes, bindings, updateNodes, updateBeforeNodes, updateAfterNodes, transforms = [] ) { @@ -20,23 +22,32 @@ class NodeBuilderState { createBindings() { - const bindingsArray = []; + const bindings = []; - for ( const instanceBinding of this.bindings ) { + for ( const instanceGroup of this.bindings ) { - let binding = instanceBinding; + const shared = 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/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index f28e1bc41e0426..392a9c495a5e28 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 diff --git a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js index 73910e176e37c7..838ed6d1825ef8 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -449,7 +449,21 @@ class WGSLNodeBuilder extends NodeBuilder { let uniformGPU; - const bindings = this.bindings[ shaderStage ]; + const groupName = node.groupNode.name; + + let bindings = this.bindings[ shaderStage ][ groupName ]; + + if ( bindings === undefined ) { + + if ( this.bindingsIndexes[ groupName ] === undefined ) { + + this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length }; + + } + + bindings = this.bindings[ shaderStage ][ groupName ] = []; + + } if ( type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'texture3D' ) { @@ -457,15 +471,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, uniformNode.groupNode, 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, uniformNode.groupNode, 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, uniformNode.groupNode, node.access ? node.access : null ); } @@ -474,7 +488,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, node.groupNode ); sampler.setVisibility( gpuShaderStageLib[ shaderStage ] ); bindings.push( sampler, texture ); @@ -492,7 +506,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, node.groupNode ); buffer.setVisibility( gpuShaderStageLib[ shaderStage ] ); bindings.push( buffer ); @@ -527,12 +541,6 @@ class WGSLNodeBuilder extends NodeBuilder { nodeData.uniformGPU = uniformGPU; - if ( shaderStage === 'vertex' ) { - - this.bindingsOffset[ 'fragment' ] = bindings.length; - - } - } return uniformNode; @@ -816,10 +824,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 +837,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 +874,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 +884,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 +893,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 +904,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 +919,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..d81a5676869ce8 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 ++, @@ -154,7 +154,7 @@ class WebGPUBindingUtils { } - createBindGroup( bindings, layoutGPU ) { + createBindGroup( bindGroup, layoutGPU ) { const backend = this.backend; const device = backend.device; @@ -162,7 +162,7 @@ class WebGPUBindingUtils { let bindingPoint = 0; const entriesGPU = []; - for ( const binding of bindings ) { + for ( const binding of bindGroup.bindings ) { if ( binding.isUniformBuffer ) { @@ -256,6 +256,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 } ) } ); From e81a3a2604c553559fabefeef09ad320a3360332 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 20 Jun 2024 19:38:55 -0300 Subject: [PATCH 2/3] WebGLBackend and revision --- examples/jsm/nodes/core/NodeBuilder.js | 22 + examples/jsm/nodes/core/NodeUniform.js | 3 +- examples/jsm/renderers/common/Backend.js | 2 +- examples/jsm/renderers/common/Bindings.js | 39 +- .../common/nodes/NodeBuilderState.js | 6 +- examples/jsm/renderers/common/nodes/Nodes.js | 1 + examples/jsm/renderers/webgl/WebGLBackend.js | 96 +- .../webgl/nodes/GLSLNodeBuilder copy.js | 899 ++++++++++++++++++ .../renderers/webgl/nodes/GLSLNodeBuilder.js | 51 +- .../jsm/renderers/webgpu/WebGPUBackend.js | 8 +- .../renderers/webgpu/nodes/WGSLNodeBuilder.js | 30 +- .../webgpu/utils/WebGPUBindingUtils.js | 9 +- 12 files changed, 1049 insertions(+), 117 deletions(-) create mode 100644 examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 38bbb90ff889e2..6c2672fe63ae43 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -103,6 +103,8 @@ class NodeBuilder { this.stacks = []; this.tab = '\t'; + this.instanceBindGroups = true; + this.currentFunctionNode = null; this.context = { @@ -208,6 +210,26 @@ class NodeBuilder { } + getBindGroupArray( groupName, shaderStage ) { + + let bindings = this.bindings[ shaderStage ][ groupName ]; + + if ( bindings === undefined ) { + + if ( this.bindingsIndexes[ groupName ] === undefined ) { + + this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length }; + + } + + bindings = this.bindings[ shaderStage ][ groupName ] = []; + + } + + return bindings; + + } + getBindings() { let bindingsGroups = this.bindGroups; 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/Bindings.js b/examples/jsm/renderers/common/Bindings.js index f875bb4ac542b9..1fc80538879ec7 100644 --- a/examples/jsm/renderers/common/Bindings.js +++ b/examples/jsm/renderers/common/Bindings.js @@ -20,67 +20,71 @@ class Bindings extends DataMap { getForRender( renderObject ) { - const bindGroups = renderObject.getBindings(); + const bindings = renderObject.getBindings(); - for ( const bindGroup of bindGroups ) { + for ( const bindGroup of bindings ) { const groupData = this.get( bindGroup ); - if ( groupData.bindings === undefined ) { + if ( groupData.bindGroup === undefined ) { // each object defines an array of bindings (ubos, textures, samplers etc.) this._init( bindGroup ); - this.backend.createBindings( bindGroup ); + this.backend.createBindings( bindGroup, bindings ); + + groupData.bindGroup = bindGroup; } } - return bindGroups; + return bindings; } getForCompute( computeNode ) { - const bindGroups = this.nodes.getForCompute( computeNode ).bindings; + const bindings = this.nodes.getForCompute( computeNode ).bindings; - for ( const bindGroup of bindGroups ) { + for ( const bindGroup of bindings ) { const groupData = this.get( bindGroup ); - if ( groupData.bindings === undefined ) { + if ( groupData.bindGroup === undefined ) { this._init( bindGroup ); - this.backend.createBindings( bindGroup ); + this.backend.createBindings( bindGroup, bindings ); + + groupData.bindGroup = bindGroup; } } - return bindGroups; + return bindings; } updateForCompute( computeNode ) { - this._updateGroups( computeNode, this.getForCompute( computeNode ) ); + this._updateBindings( computeNode, this.getForCompute( computeNode ) ); } updateForRender( renderObject ) { - this._updateGroups( renderObject, this.getForRender( renderObject ) ); + this._updateBindings( renderObject, this.getForRender( renderObject ) ); } - _updateGroups( object, bindGroups ) { + _updateBindings( object, bindings ) { - for ( const bindGroup of bindGroups ) { + for ( const bindGroup of bindings ) { - this._update( object, bindGroup ); + this._update( object, bindGroup, bindings ); } @@ -106,7 +110,7 @@ class Bindings extends DataMap { } - _update( object, bindGroup ) { + _update( object, bindGroup, bindings ) { const { backend } = this; @@ -164,7 +168,6 @@ class Bindings extends DataMap { } - if ( texture.isStorageTexture === true ) { const textureData = this.get( texture ); @@ -191,7 +194,7 @@ class Bindings extends DataMap { const pipeline = this.pipelines.getForRender( object ); - this.backend.updateBindings( bindGroup, 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 813559346c5450..98e1bd631d4628 100644 --- a/examples/jsm/renderers/common/nodes/NodeBuilderState.js +++ b/examples/jsm/renderers/common/nodes/NodeBuilderState.js @@ -2,7 +2,7 @@ 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; @@ -16,6 +16,8 @@ class NodeBuilderState { this.updateBeforeNodes = updateBeforeNodes; this.updateAfterNodes = updateAfterNodes; + this.instanceBindGroups = instanceBindGroups; + this.usedTimes = 0; } @@ -26,7 +28,7 @@ class NodeBuilderState { for ( const instanceGroup of this.bindings ) { - const shared = instanceGroup.bindings[ 0 ].groupNode.shared; + const shared = this.instanceBindGroups && instanceGroup.bindings[ 0 ].groupNode.shared; if ( shared !== true ) { 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 copy.js b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js new file mode 100644 index 00000000000000..5117b0a83bdd93 --- /dev/null +++ b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js @@ -0,0 +1,899 @@ +import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js'; + +import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js'; +import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js'; + +import { NodeSampledTexture, NodeSampledCubeTexture, NodeSampledTexture3D } from '../../common/nodes/NodeSampledTexture.js'; + +import { ByteType, ShortType, RGBAIntegerFormat, RGBIntegerFormat, RedIntegerFormat, RGIntegerFormat, UnsignedByteType, UnsignedIntType, UnsignedShortType, RedFormat, RGFormat, IntType, DataTexture, RGBFormat, RGBAFormat, FloatType } from 'three'; + +const glslMethods = { + [ MathNode.ATAN2 ]: 'atan', + textureDimensions: 'textureSize', + equals: 'equal' +}; + +const precisionLib = { + low: 'lowp', + medium: 'mediump', + high: 'highp' +}; + +const supports = { + swizzleAssign: true, + storageBuffer: false +}; + +const defaultPrecisions = ` +precision highp float; +precision highp int; +precision highp sampler2D; +precision highp sampler3D; +precision highp samplerCube; +precision highp sampler2DArray; + +precision highp usampler2D; +precision highp usampler3D; +precision highp usamplerCube; +precision highp usampler2DArray; + +precision highp isampler2D; +precision highp isampler3D; +precision highp isamplerCube; +precision highp isampler2DArray; + +precision lowp sampler2DShadow; +`; + +class GLSLNodeBuilder extends NodeBuilder { + + constructor( object, renderer, scene = null ) { + + super( object, renderer, new GLSLNodeParser(), scene ); + + this.uniformGroups = {}; + this.transforms = []; + + this.instanceBindGroups = false; + + } + + getMethod( method ) { + + return glslMethods[ method ] || method; + + } + + getOutputStructName() { + + return ''; + + } + + buildFunctionCode( shaderNode ) { + + const layout = shaderNode.layout; + const flowData = this.flowShaderNode( shaderNode ); + + const parameters = []; + + for ( const input of layout.inputs ) { + + parameters.push( this.getType( input.type ) + ' ' + input.name ); + + } + + // + + const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) { + + ${ flowData.vars } + +${ flowData.code } + return ${ flowData.result }; + +}`; + + // + + return code; + + } + + setupPBO( storageBufferNode ) { + + const attribute = storageBufferNode.value; + + if ( attribute.pbo === undefined ) { + + const originalArray = attribute.array; + const numElements = attribute.count * attribute.itemSize; + + const { itemSize } = attribute; + + const isInteger = attribute.array.constructor.name.toLowerCase().includes( 'int' ); + + let format = isInteger ? RedIntegerFormat : RedFormat; + + + if ( itemSize === 2 ) { + + format = isInteger ? RGIntegerFormat : RGFormat; + + } else if ( itemSize === 3 ) { + + format = isInteger ? RGBIntegerFormat : RGBFormat; + + } else if ( itemSize === 4 ) { + + format = isInteger ? RGBAIntegerFormat : RGBAFormat; + + } + + const typeMap = { + Float32Array: FloatType, + Uint8Array: UnsignedByteType, + Uint16Array: UnsignedShortType, + Uint32Array: UnsignedIntType, + Int8Array: ByteType, + Int16Array: ShortType, + Int32Array: IntType, + Uint8ClampedArray: UnsignedByteType, + }; + + const width = Math.pow( 2, Math.ceil( Math.log2( Math.sqrt( numElements / itemSize ) ) ) ); + let height = Math.ceil( ( numElements / itemSize ) / width ); + if ( width * height * itemSize < numElements ) height ++; // Ensure enough space + + const newSize = width * height * itemSize; + + const newArray = new originalArray.constructor( newSize ); + + newArray.set( originalArray, 0 ); + + attribute.array = newArray; + + const pboTexture = new DataTexture( attribute.array, width, height, format, typeMap[ attribute.array.constructor.name ] || FloatType ); + pboTexture.needsUpdate = true; + pboTexture.isPBOTexture = true; + + const pbo = new UniformNode( pboTexture ); + pbo.setPrecision( 'high' ); + + attribute.pboNode = pbo; + attribute.pbo = pbo.value; + + this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); + + } + + } + + generatePBO( storageArrayElementNode ) { + + const { node, indexNode } = storageArrayElementNode; + const attribute = node.value; + + if ( this.renderer.backend.has( attribute ) ) { + + const attributeData = this.renderer.backend.get( attribute ); + attributeData.pbo = attribute.pbo; + + } + + + const nodeUniform = this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); + const textureName = this.getPropertyName( nodeUniform ); + + indexNode.increaseUsage( this ); // force cache generate to be used as index in x,y + const indexSnippet = indexNode.build( this, 'uint' ); + + const elementNodeData = this.getDataFromNode( storageArrayElementNode ); + + let propertyName = elementNodeData.propertyName; + + if ( propertyName === undefined ) { + + // property element + + const nodeVar = this.getVarFromNode( storageArrayElementNode ); + + propertyName = this.getPropertyName( nodeVar ); + + // property size + + const bufferNodeData = this.getDataFromNode( node ); + + let propertySizeName = bufferNodeData.propertySizeName; + + if ( propertySizeName === undefined ) { + + propertySizeName = propertyName + 'Size'; + + this.getVarFromNode( node, propertySizeName, 'uint' ); + + this.addLineFlowCode( `${ propertySizeName } = uint( textureSize( ${ textureName }, 0 ).x )` ); + + bufferNodeData.propertySizeName = propertySizeName; + + } + + // + + const { itemSize } = attribute; + + const channel = '.' + vectorComponents.join( '' ).slice( 0, itemSize ); + const uvSnippet = `ivec2(${indexSnippet} % ${ propertySizeName }, ${indexSnippet} / ${ propertySizeName })`; + + const snippet = this.generateTextureLoad( null, textureName, uvSnippet, null, '0' ); + + // + + const typePrefix = attribute.array.constructor.name.toLowerCase().charAt( 0 ); + + let prefix = 'vec4'; + if ( typePrefix === 'u' ) { + + prefix = 'uvec4'; + + } else if ( typePrefix === 'i' ) { + + prefix = 'ivec4'; + + } + + this.addLineFlowCode( `${ propertyName } = ${prefix}(${ snippet })${channel}` ); + + elementNodeData.propertyName = propertyName; + + } + + return propertyName; + + } + + generateTextureLoad( texture, textureProperty, uvIndexSnippet, depthSnippet, levelSnippet = '0' ) { + + if ( depthSnippet ) { + + return `texelFetch( ${ textureProperty }, ivec3( ${ uvIndexSnippet }, ${ depthSnippet } ), ${ levelSnippet } )`; + + } else { + + return `texelFetch( ${ textureProperty }, ${ uvIndexSnippet }, ${ levelSnippet } )`; + + } + + } + + generateTexture( texture, textureProperty, uvSnippet, depthSnippet ) { + + if ( texture.isDepthTexture ) { + + return `texture( ${ textureProperty }, ${ uvSnippet } ).x`; + + } else { + + if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`; + + return `texture( ${ textureProperty }, ${ uvSnippet } )`; + + } + + } + + generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet ) { + + return `textureLod( ${ textureProperty }, ${ uvSnippet }, ${ levelSnippet } )`; + + } + + generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet ) { + + return `textureGrad( ${ textureProperty }, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`; + + } + + generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) { + + if ( shaderStage === 'fragment' ) { + + return `texture( ${ textureProperty }, vec3( ${ uvSnippet }, ${ compareSnippet } ) )`; + + } else { + + console.error( `WebGPURenderer: THREE.DepthTexture.compareFunction() does not support ${ shaderStage } shader.` ); + + } + + } + + getVars( shaderStage ) { + + const snippets = []; + + const vars = this.vars[ shaderStage ]; + + if ( vars !== undefined ) { + + for ( const variable of vars ) { + + snippets.push( `${ this.getVar( variable.type, variable.name ) };` ); + + } + + } + + return snippets.join( '\n\t' ); + + } + + getUniforms( shaderStage ) { + + const uniforms = this.uniforms[ shaderStage ]; + + const bindingSnippets = []; + const uniformGroups = {}; + + for ( const uniform of uniforms ) { + + let snippet = null; + let group = false; + + + if ( uniform.type === 'texture' ) { + + const texture = uniform.node.value; + + let typePrefix = ''; + + if ( texture.isPBOTexture === true ) { + + const prefix = texture.source.data.data.constructor.name.toLowerCase().charAt( 0 ); + + if ( prefix === 'u' || prefix === 'i' ) { + + typePrefix = prefix; + + } + + } + + if ( texture.compareFunction ) { + + snippet = `sampler2DShadow ${ uniform.name };`; + + } else if ( texture.isDataArrayTexture === true ) { + + snippet = `${typePrefix}sampler2DArray ${ uniform.name };`; + + } else { + + snippet = `${typePrefix}sampler2D ${ uniform.name };`; + + } + + } else if ( uniform.type === 'cubeTexture' ) { + + snippet = `samplerCube ${ uniform.name };`; + + } else if ( uniform.type === 'texture3D' ) { + + snippet = `sampler3D ${ uniform.name };`; + + } else if ( uniform.type === 'buffer' ) { + + const bufferNode = uniform.node; + const bufferType = this.getType( bufferNode.bufferType ); + const bufferCount = bufferNode.bufferCount; + + const bufferCountSnippet = bufferCount > 0 ? bufferCount : ''; + snippet = `${bufferNode.name} {\n\t${ bufferType } ${ uniform.name }[${ bufferCountSnippet }];\n};\n`; + + } else { + + const vectorType = this.getVectorType( uniform.type ); + + snippet = `${vectorType} ${uniform.name};`; + + group = true; + + } + + const precision = uniform.node.precision; + + if ( precision !== null ) { + + snippet = precisionLib[ precision ] + ' ' + snippet; + + } + + if ( group ) { + + snippet = '\t' + snippet; + + const groupName = uniform.groupNode.name; + const groupSnippets = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = [] ); + + groupSnippets.push( snippet ); + + } else { + + snippet = 'uniform ' + snippet; + + bindingSnippets.push( snippet ); + + } + + } + + let output = ''; + + for ( const name in uniformGroups ) { + + const groupSnippets = uniformGroups[ name ]; + + output += this._getGLSLUniformStruct( shaderStage + '_' + name, groupSnippets.join( '\n' ) ) + '\n'; + + } + + output += bindingSnippets.join( '\n' ); + + return output; + + } + + getTypeFromAttribute( attribute ) { + + let nodeType = super.getTypeFromAttribute( attribute ); + + if ( /^[iu]/.test( nodeType ) && attribute.gpuType !== IntType ) { + + let dataAttribute = attribute; + + if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data; + + const array = dataAttribute.array; + + if ( ( array instanceof Uint32Array || array instanceof Int32Array || array instanceof Uint16Array || array instanceof Int16Array ) === false ) { + + nodeType = nodeType.slice( 1 ); + + } + + } + + return nodeType; + + } + + getAttributes( shaderStage ) { + + let snippet = ''; + + if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { + + const attributes = this.getAttributesArray(); + + let location = 0; + + for ( const attribute of attributes ) { + + snippet += `layout( location = ${ location ++ } ) in ${ attribute.type } ${ attribute.name };\n`; + + } + + } + + return snippet; + + } + + getStructMembers( struct ) { + + const snippets = []; + const members = struct.getMemberTypes(); + + for ( let i = 0; i < members.length; i ++ ) { + + const member = members[ i ]; + snippets.push( `layout( location = ${i} ) out ${ member} m${i};` ); + + } + + return snippets.join( '\n' ); + + } + + getStructs( shaderStage ) { + + const snippets = []; + const structs = this.structs[ shaderStage ]; + + if ( structs.length === 0 ) { + + return 'layout( location = 0 ) out vec4 fragColor;\n'; + + } + + for ( let index = 0, length = structs.length; index < length; index ++ ) { + + const struct = structs[ index ]; + + let snippet = '\n'; + snippet += this.getStructMembers( struct ); + snippet += '\n'; + + snippets.push( snippet ); + + } + + return snippets.join( '\n\n' ); + + } + + getVaryings( shaderStage ) { + + let snippet = ''; + + const varyings = this.varyings; + + if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { + + for ( const varying of varyings ) { + + if ( shaderStage === 'compute' ) varying.needsInterpolation = true; + const type = varying.type; + const flat = type.includes( 'int' ) || type.includes( 'uv' ) || type.includes( 'iv' ) ? 'flat ' : ''; + + snippet += `${flat}${varying.needsInterpolation ? 'out' : '/*out*/'} ${type} ${varying.name};\n`; + + } + + } else if ( shaderStage === 'fragment' ) { + + for ( const varying of varyings ) { + + if ( varying.needsInterpolation ) { + + const type = varying.type; + const flat = type.includes( 'int' ) || type.includes( 'uv' ) || type.includes( 'iv' ) ? 'flat ' : ''; + + snippet += `${flat}in ${type} ${varying.name};\n`; + + } + + } + + } + + return snippet; + + } + + getVertexIndex() { + + return 'uint( gl_VertexID )'; + + } + + getInstanceIndex() { + + return 'uint( gl_InstanceID )'; + + } + + getFrontFacing() { + + return 'gl_FrontFacing'; + + } + + getFragCoord() { + + return 'gl_FragCoord'; + + } + + getFragDepth() { + + return 'gl_FragDepth'; + + } + + isAvailable( name ) { + + let result = supports[ name ]; + + if ( result === undefined ) { + + if ( name === 'float32Filterable' ) { + + const extentions = this.renderer.backend.extensions; + + if ( extentions.has( 'OES_texture_float_linear' ) ) { + + extentions.get( 'OES_texture_float_linear' ); + result = true; + + } else { + + result = false; + + } + + } + + supports[ name ] = result; + + } + + return result; + + } + + isFlipY() { + + return true; + + } + + registerTransform( varyingName, attributeNode ) { + + this.transforms.push( { varyingName, attributeNode } ); + + } + + getTransforms( /* shaderStage */ ) { + + const transforms = this.transforms; + + let snippet = ''; + + for ( let i = 0; i < transforms.length; i ++ ) { + + const transform = transforms[ i ]; + + const attributeName = this.getPropertyName( transform.attributeNode ); + + snippet += `${ transform.varyingName } = ${ attributeName };\n\t`; + + } + + return snippet; + + } + + _getGLSLUniformStruct( name, vars ) { + + return ` +layout( std140 ) uniform ${name} { +${vars} +};`; + + } + + _getGLSLVertexCode( shaderData ) { + + return `#version 300 es + +${ this.getSignature() } + +// precision +${ defaultPrecisions } + +// uniforms +${shaderData.uniforms} + +// varyings +${shaderData.varyings} + +// attributes +${shaderData.attributes} + +// codes +${shaderData.codes} + +void main() { + + // vars + ${shaderData.vars} + + // transforms + ${shaderData.transforms} + + // flow + ${shaderData.flow} + + gl_PointSize = 1.0; + +} +`; + + } + + _getGLSLFragmentCode( shaderData ) { + + return `#version 300 es + +${ this.getSignature() } + +// precision +${ defaultPrecisions } + +// uniforms +${shaderData.uniforms} + +// varyings +${shaderData.varyings} + +// codes +${shaderData.codes} + +${shaderData.structs} + +void main() { + + // vars + ${shaderData.vars} + + // flow + ${shaderData.flow} + +} +`; + + } + + buildCode() { + + const shadersData = this.material !== null ? { fragment: {}, vertex: {} } : { compute: {} }; + + for ( const shaderStage in shadersData ) { + + let flow = '// code\n\n'; + flow += this.flowCode[ shaderStage ]; + + const flowNodes = this.flowNodes[ shaderStage ]; + const mainNode = flowNodes[ flowNodes.length - 1 ]; + + for ( const node of flowNodes ) { + + const flowSlotData = this.getFlowData( node/*, shaderStage*/ ); + const slotName = node.name; + + if ( slotName ) { + + if ( flow.length > 0 ) flow += '\n'; + + flow += `\t// flow -> ${ slotName }\n\t`; + + } + + flow += `${ flowSlotData.code }\n\t`; + + if ( node === mainNode && shaderStage !== 'compute' ) { + + flow += '// result\n\t'; + + if ( shaderStage === 'vertex' ) { + + flow += 'gl_Position = '; + flow += `${ flowSlotData.result };`; + + } else if ( shaderStage === 'fragment' ) { + + if ( ! node.outputNode.isOutputStructNode ) { + + flow += 'fragColor = '; + flow += `${ flowSlotData.result };`; + + } + + } + + } + + } + + const stageData = shadersData[ shaderStage ]; + + stageData.uniforms = this.getUniforms( shaderStage ); + stageData.attributes = this.getAttributes( shaderStage ); + stageData.varyings = this.getVaryings( shaderStage ); + stageData.vars = this.getVars( shaderStage ); + stageData.structs = this.getStructs( shaderStage ); + stageData.codes = this.getCodes( shaderStage ); + stageData.transforms = this.getTransforms( shaderStage ); + stageData.flow = flow; + + } + + if ( this.material !== null ) { + + this.vertexShader = this._getGLSLVertexCode( shadersData.vertex ); + this.fragmentShader = this._getGLSLFragmentCode( shadersData.fragment ); + + } else { + + this.computeShader = this._getGLSLVertexCode( shadersData.compute ); + + } + + } + + getUniformFromNode( node, type, shaderStage, name = null ) { + + const uniformNode = super.getUniformFromNode( node, type, shaderStage, name ); + const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache ); + + let uniformGPU = nodeData.uniformGPU; + + if ( uniformGPU === undefined ) { + + const group = node.groupNode; + const groupName = group.name; + + const bindings = this.getBindGroupArray( groupName, shaderStage ); + + if ( type === 'texture' ) { + + uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node, group ); + bindings.push( uniformGPU ); + + } else if ( type === 'cubeTexture' ) { + + uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group ); + bindings.push( uniformGPU ); + + } else if ( type === 'texture3D' ) { + + 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, group ); + buffer.name = node.name; + + bindings.push( buffer ); + + uniformGPU = buffer; + + } else { + + const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} ); + + let uniformsGroup = uniformsStage[ groupName ]; + + if ( uniformsGroup === undefined ) { + + uniformsGroup = new NodeUniformsGroup( shaderStage + '_' + groupName, group ); + //uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); + + uniformsStage[ groupName ] = uniformsGroup; + + bindings.push( uniformsGroup ); + + } + + uniformGPU = this.getNodeUniform( uniformNode, type ); + + uniformsGroup.addUniform( uniformGPU ); + + } + + nodeData.uniformGPU = uniformGPU; + + } + + return uniformNode; + + } + +} + +export default GLSLNodeBuilder; 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 392a9c495a5e28..32f9bfc66f98b0 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -1210,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 838ed6d1825ef8..3f03527e6a710a 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -449,21 +449,10 @@ class WGSLNodeBuilder extends NodeBuilder { let uniformGPU; - const groupName = node.groupNode.name; + const group = node.groupNode; + const groupName = group.name; - let bindings = this.bindings[ shaderStage ][ groupName ]; - - if ( bindings === undefined ) { - - if ( this.bindingsIndexes[ groupName ] === undefined ) { - - this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length }; - - } - - bindings = this.bindings[ shaderStage ][ groupName ] = []; - - } + const bindings = this.getBindGroupArray( groupName, shaderStage ); if ( type === 'texture' || type === 'cubeTexture' || type === 'storageTexture' || type === 'texture3D' ) { @@ -471,15 +460,15 @@ class WGSLNodeBuilder extends NodeBuilder { if ( type === 'texture' || type === 'storageTexture' ) { - texture = new NodeSampledTexture( uniformNode.name, uniformNode.node, uniformNode.groupNode, 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, uniformNode.groupNode, 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, uniformNode.groupNode, node.access ? node.access : null ); + texture = new NodeSampledTexture3D( uniformNode.name, uniformNode.node, group, node.access ? node.access : null ); } @@ -488,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, node.groupNode ); + const sampler = new NodeSampler( `${uniformNode.name}_sampler`, uniformNode.node, group ); sampler.setVisibility( gpuShaderStageLib[ shaderStage ] ); bindings.push( sampler, texture ); @@ -506,7 +495,7 @@ class WGSLNodeBuilder extends NodeBuilder { } else if ( type === 'buffer' || type === 'storageBuffer' ) { const bufferClass = type === 'storageBuffer' ? NodeStorageBuffer : NodeUniformBuffer; - const buffer = new bufferClass( node, node.groupNode ); + const buffer = new bufferClass( node, group ); buffer.setVisibility( gpuShaderStageLib[ shaderStage ] ); bindings.push( buffer ); @@ -515,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 ]; diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js index d81a5676869ce8..13230bc645991a 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js @@ -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; } From 9e44f9cb37a4bbf935f3d867897ee4f9d92048c1 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 20 Jun 2024 19:52:48 -0300 Subject: [PATCH 3/3] cleanup --- examples/jsm/nodes/core/NodeBuilder.js | 10 +- .../webgl/nodes/GLSLNodeBuilder copy.js | 899 ------------------ 2 files changed, 6 insertions(+), 903 deletions(-) delete mode 100644 examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js diff --git a/examples/jsm/nodes/core/NodeBuilder.js b/examples/jsm/nodes/core/NodeBuilder.js index 6c2672fe63ae43..db45d448fb2cf0 100644 --- a/examples/jsm/nodes/core/NodeBuilder.js +++ b/examples/jsm/nodes/core/NodeBuilder.js @@ -212,9 +212,11 @@ class NodeBuilder { getBindGroupArray( groupName, shaderStage ) { - let bindings = this.bindings[ shaderStage ][ groupName ]; + const bindings = this.bindings[ shaderStage ]; - if ( bindings === undefined ) { + let bindGroup = bindings[ groupName ]; + + if ( bindGroup === undefined ) { if ( this.bindingsIndexes[ groupName ] === undefined ) { @@ -222,11 +224,11 @@ class NodeBuilder { } - bindings = this.bindings[ shaderStage ][ groupName ] = []; + bindings[ groupName ] = bindGroup = []; } - return bindings; + return bindGroup; } diff --git a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js b/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js deleted file mode 100644 index 5117b0a83bdd93..00000000000000 --- a/examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder copy.js +++ /dev/null @@ -1,899 +0,0 @@ -import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js'; - -import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js'; -import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js'; - -import { NodeSampledTexture, NodeSampledCubeTexture, NodeSampledTexture3D } from '../../common/nodes/NodeSampledTexture.js'; - -import { ByteType, ShortType, RGBAIntegerFormat, RGBIntegerFormat, RedIntegerFormat, RGIntegerFormat, UnsignedByteType, UnsignedIntType, UnsignedShortType, RedFormat, RGFormat, IntType, DataTexture, RGBFormat, RGBAFormat, FloatType } from 'three'; - -const glslMethods = { - [ MathNode.ATAN2 ]: 'atan', - textureDimensions: 'textureSize', - equals: 'equal' -}; - -const precisionLib = { - low: 'lowp', - medium: 'mediump', - high: 'highp' -}; - -const supports = { - swizzleAssign: true, - storageBuffer: false -}; - -const defaultPrecisions = ` -precision highp float; -precision highp int; -precision highp sampler2D; -precision highp sampler3D; -precision highp samplerCube; -precision highp sampler2DArray; - -precision highp usampler2D; -precision highp usampler3D; -precision highp usamplerCube; -precision highp usampler2DArray; - -precision highp isampler2D; -precision highp isampler3D; -precision highp isamplerCube; -precision highp isampler2DArray; - -precision lowp sampler2DShadow; -`; - -class GLSLNodeBuilder extends NodeBuilder { - - constructor( object, renderer, scene = null ) { - - super( object, renderer, new GLSLNodeParser(), scene ); - - this.uniformGroups = {}; - this.transforms = []; - - this.instanceBindGroups = false; - - } - - getMethod( method ) { - - return glslMethods[ method ] || method; - - } - - getOutputStructName() { - - return ''; - - } - - buildFunctionCode( shaderNode ) { - - const layout = shaderNode.layout; - const flowData = this.flowShaderNode( shaderNode ); - - const parameters = []; - - for ( const input of layout.inputs ) { - - parameters.push( this.getType( input.type ) + ' ' + input.name ); - - } - - // - - const code = `${ this.getType( layout.type ) } ${ layout.name }( ${ parameters.join( ', ' ) } ) { - - ${ flowData.vars } - -${ flowData.code } - return ${ flowData.result }; - -}`; - - // - - return code; - - } - - setupPBO( storageBufferNode ) { - - const attribute = storageBufferNode.value; - - if ( attribute.pbo === undefined ) { - - const originalArray = attribute.array; - const numElements = attribute.count * attribute.itemSize; - - const { itemSize } = attribute; - - const isInteger = attribute.array.constructor.name.toLowerCase().includes( 'int' ); - - let format = isInteger ? RedIntegerFormat : RedFormat; - - - if ( itemSize === 2 ) { - - format = isInteger ? RGIntegerFormat : RGFormat; - - } else if ( itemSize === 3 ) { - - format = isInteger ? RGBIntegerFormat : RGBFormat; - - } else if ( itemSize === 4 ) { - - format = isInteger ? RGBAIntegerFormat : RGBAFormat; - - } - - const typeMap = { - Float32Array: FloatType, - Uint8Array: UnsignedByteType, - Uint16Array: UnsignedShortType, - Uint32Array: UnsignedIntType, - Int8Array: ByteType, - Int16Array: ShortType, - Int32Array: IntType, - Uint8ClampedArray: UnsignedByteType, - }; - - const width = Math.pow( 2, Math.ceil( Math.log2( Math.sqrt( numElements / itemSize ) ) ) ); - let height = Math.ceil( ( numElements / itemSize ) / width ); - if ( width * height * itemSize < numElements ) height ++; // Ensure enough space - - const newSize = width * height * itemSize; - - const newArray = new originalArray.constructor( newSize ); - - newArray.set( originalArray, 0 ); - - attribute.array = newArray; - - const pboTexture = new DataTexture( attribute.array, width, height, format, typeMap[ attribute.array.constructor.name ] || FloatType ); - pboTexture.needsUpdate = true; - pboTexture.isPBOTexture = true; - - const pbo = new UniformNode( pboTexture ); - pbo.setPrecision( 'high' ); - - attribute.pboNode = pbo; - attribute.pbo = pbo.value; - - this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); - - } - - } - - generatePBO( storageArrayElementNode ) { - - const { node, indexNode } = storageArrayElementNode; - const attribute = node.value; - - if ( this.renderer.backend.has( attribute ) ) { - - const attributeData = this.renderer.backend.get( attribute ); - attributeData.pbo = attribute.pbo; - - } - - - const nodeUniform = this.getUniformFromNode( attribute.pboNode, 'texture', this.shaderStage, this.context.label ); - const textureName = this.getPropertyName( nodeUniform ); - - indexNode.increaseUsage( this ); // force cache generate to be used as index in x,y - const indexSnippet = indexNode.build( this, 'uint' ); - - const elementNodeData = this.getDataFromNode( storageArrayElementNode ); - - let propertyName = elementNodeData.propertyName; - - if ( propertyName === undefined ) { - - // property element - - const nodeVar = this.getVarFromNode( storageArrayElementNode ); - - propertyName = this.getPropertyName( nodeVar ); - - // property size - - const bufferNodeData = this.getDataFromNode( node ); - - let propertySizeName = bufferNodeData.propertySizeName; - - if ( propertySizeName === undefined ) { - - propertySizeName = propertyName + 'Size'; - - this.getVarFromNode( node, propertySizeName, 'uint' ); - - this.addLineFlowCode( `${ propertySizeName } = uint( textureSize( ${ textureName }, 0 ).x )` ); - - bufferNodeData.propertySizeName = propertySizeName; - - } - - // - - const { itemSize } = attribute; - - const channel = '.' + vectorComponents.join( '' ).slice( 0, itemSize ); - const uvSnippet = `ivec2(${indexSnippet} % ${ propertySizeName }, ${indexSnippet} / ${ propertySizeName })`; - - const snippet = this.generateTextureLoad( null, textureName, uvSnippet, null, '0' ); - - // - - const typePrefix = attribute.array.constructor.name.toLowerCase().charAt( 0 ); - - let prefix = 'vec4'; - if ( typePrefix === 'u' ) { - - prefix = 'uvec4'; - - } else if ( typePrefix === 'i' ) { - - prefix = 'ivec4'; - - } - - this.addLineFlowCode( `${ propertyName } = ${prefix}(${ snippet })${channel}` ); - - elementNodeData.propertyName = propertyName; - - } - - return propertyName; - - } - - generateTextureLoad( texture, textureProperty, uvIndexSnippet, depthSnippet, levelSnippet = '0' ) { - - if ( depthSnippet ) { - - return `texelFetch( ${ textureProperty }, ivec3( ${ uvIndexSnippet }, ${ depthSnippet } ), ${ levelSnippet } )`; - - } else { - - return `texelFetch( ${ textureProperty }, ${ uvIndexSnippet }, ${ levelSnippet } )`; - - } - - } - - generateTexture( texture, textureProperty, uvSnippet, depthSnippet ) { - - if ( texture.isDepthTexture ) { - - return `texture( ${ textureProperty }, ${ uvSnippet } ).x`; - - } else { - - if ( depthSnippet ) uvSnippet = `vec3( ${ uvSnippet }, ${ depthSnippet } )`; - - return `texture( ${ textureProperty }, ${ uvSnippet } )`; - - } - - } - - generateTextureLevel( texture, textureProperty, uvSnippet, levelSnippet ) { - - return `textureLod( ${ textureProperty }, ${ uvSnippet }, ${ levelSnippet } )`; - - } - - generateTextureGrad( texture, textureProperty, uvSnippet, gradSnippet ) { - - return `textureGrad( ${ textureProperty }, ${ uvSnippet }, ${ gradSnippet[ 0 ] }, ${ gradSnippet[ 1 ] } )`; - - } - - generateTextureCompare( texture, textureProperty, uvSnippet, compareSnippet, depthSnippet, shaderStage = this.shaderStage ) { - - if ( shaderStage === 'fragment' ) { - - return `texture( ${ textureProperty }, vec3( ${ uvSnippet }, ${ compareSnippet } ) )`; - - } else { - - console.error( `WebGPURenderer: THREE.DepthTexture.compareFunction() does not support ${ shaderStage } shader.` ); - - } - - } - - getVars( shaderStage ) { - - const snippets = []; - - const vars = this.vars[ shaderStage ]; - - if ( vars !== undefined ) { - - for ( const variable of vars ) { - - snippets.push( `${ this.getVar( variable.type, variable.name ) };` ); - - } - - } - - return snippets.join( '\n\t' ); - - } - - getUniforms( shaderStage ) { - - const uniforms = this.uniforms[ shaderStage ]; - - const bindingSnippets = []; - const uniformGroups = {}; - - for ( const uniform of uniforms ) { - - let snippet = null; - let group = false; - - - if ( uniform.type === 'texture' ) { - - const texture = uniform.node.value; - - let typePrefix = ''; - - if ( texture.isPBOTexture === true ) { - - const prefix = texture.source.data.data.constructor.name.toLowerCase().charAt( 0 ); - - if ( prefix === 'u' || prefix === 'i' ) { - - typePrefix = prefix; - - } - - } - - if ( texture.compareFunction ) { - - snippet = `sampler2DShadow ${ uniform.name };`; - - } else if ( texture.isDataArrayTexture === true ) { - - snippet = `${typePrefix}sampler2DArray ${ uniform.name };`; - - } else { - - snippet = `${typePrefix}sampler2D ${ uniform.name };`; - - } - - } else if ( uniform.type === 'cubeTexture' ) { - - snippet = `samplerCube ${ uniform.name };`; - - } else if ( uniform.type === 'texture3D' ) { - - snippet = `sampler3D ${ uniform.name };`; - - } else if ( uniform.type === 'buffer' ) { - - const bufferNode = uniform.node; - const bufferType = this.getType( bufferNode.bufferType ); - const bufferCount = bufferNode.bufferCount; - - const bufferCountSnippet = bufferCount > 0 ? bufferCount : ''; - snippet = `${bufferNode.name} {\n\t${ bufferType } ${ uniform.name }[${ bufferCountSnippet }];\n};\n`; - - } else { - - const vectorType = this.getVectorType( uniform.type ); - - snippet = `${vectorType} ${uniform.name};`; - - group = true; - - } - - const precision = uniform.node.precision; - - if ( precision !== null ) { - - snippet = precisionLib[ precision ] + ' ' + snippet; - - } - - if ( group ) { - - snippet = '\t' + snippet; - - const groupName = uniform.groupNode.name; - const groupSnippets = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = [] ); - - groupSnippets.push( snippet ); - - } else { - - snippet = 'uniform ' + snippet; - - bindingSnippets.push( snippet ); - - } - - } - - let output = ''; - - for ( const name in uniformGroups ) { - - const groupSnippets = uniformGroups[ name ]; - - output += this._getGLSLUniformStruct( shaderStage + '_' + name, groupSnippets.join( '\n' ) ) + '\n'; - - } - - output += bindingSnippets.join( '\n' ); - - return output; - - } - - getTypeFromAttribute( attribute ) { - - let nodeType = super.getTypeFromAttribute( attribute ); - - if ( /^[iu]/.test( nodeType ) && attribute.gpuType !== IntType ) { - - let dataAttribute = attribute; - - if ( attribute.isInterleavedBufferAttribute ) dataAttribute = attribute.data; - - const array = dataAttribute.array; - - if ( ( array instanceof Uint32Array || array instanceof Int32Array || array instanceof Uint16Array || array instanceof Int16Array ) === false ) { - - nodeType = nodeType.slice( 1 ); - - } - - } - - return nodeType; - - } - - getAttributes( shaderStage ) { - - let snippet = ''; - - if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { - - const attributes = this.getAttributesArray(); - - let location = 0; - - for ( const attribute of attributes ) { - - snippet += `layout( location = ${ location ++ } ) in ${ attribute.type } ${ attribute.name };\n`; - - } - - } - - return snippet; - - } - - getStructMembers( struct ) { - - const snippets = []; - const members = struct.getMemberTypes(); - - for ( let i = 0; i < members.length; i ++ ) { - - const member = members[ i ]; - snippets.push( `layout( location = ${i} ) out ${ member} m${i};` ); - - } - - return snippets.join( '\n' ); - - } - - getStructs( shaderStage ) { - - const snippets = []; - const structs = this.structs[ shaderStage ]; - - if ( structs.length === 0 ) { - - return 'layout( location = 0 ) out vec4 fragColor;\n'; - - } - - for ( let index = 0, length = structs.length; index < length; index ++ ) { - - const struct = structs[ index ]; - - let snippet = '\n'; - snippet += this.getStructMembers( struct ); - snippet += '\n'; - - snippets.push( snippet ); - - } - - return snippets.join( '\n\n' ); - - } - - getVaryings( shaderStage ) { - - let snippet = ''; - - const varyings = this.varyings; - - if ( shaderStage === 'vertex' || shaderStage === 'compute' ) { - - for ( const varying of varyings ) { - - if ( shaderStage === 'compute' ) varying.needsInterpolation = true; - const type = varying.type; - const flat = type.includes( 'int' ) || type.includes( 'uv' ) || type.includes( 'iv' ) ? 'flat ' : ''; - - snippet += `${flat}${varying.needsInterpolation ? 'out' : '/*out*/'} ${type} ${varying.name};\n`; - - } - - } else if ( shaderStage === 'fragment' ) { - - for ( const varying of varyings ) { - - if ( varying.needsInterpolation ) { - - const type = varying.type; - const flat = type.includes( 'int' ) || type.includes( 'uv' ) || type.includes( 'iv' ) ? 'flat ' : ''; - - snippet += `${flat}in ${type} ${varying.name};\n`; - - } - - } - - } - - return snippet; - - } - - getVertexIndex() { - - return 'uint( gl_VertexID )'; - - } - - getInstanceIndex() { - - return 'uint( gl_InstanceID )'; - - } - - getFrontFacing() { - - return 'gl_FrontFacing'; - - } - - getFragCoord() { - - return 'gl_FragCoord'; - - } - - getFragDepth() { - - return 'gl_FragDepth'; - - } - - isAvailable( name ) { - - let result = supports[ name ]; - - if ( result === undefined ) { - - if ( name === 'float32Filterable' ) { - - const extentions = this.renderer.backend.extensions; - - if ( extentions.has( 'OES_texture_float_linear' ) ) { - - extentions.get( 'OES_texture_float_linear' ); - result = true; - - } else { - - result = false; - - } - - } - - supports[ name ] = result; - - } - - return result; - - } - - isFlipY() { - - return true; - - } - - registerTransform( varyingName, attributeNode ) { - - this.transforms.push( { varyingName, attributeNode } ); - - } - - getTransforms( /* shaderStage */ ) { - - const transforms = this.transforms; - - let snippet = ''; - - for ( let i = 0; i < transforms.length; i ++ ) { - - const transform = transforms[ i ]; - - const attributeName = this.getPropertyName( transform.attributeNode ); - - snippet += `${ transform.varyingName } = ${ attributeName };\n\t`; - - } - - return snippet; - - } - - _getGLSLUniformStruct( name, vars ) { - - return ` -layout( std140 ) uniform ${name} { -${vars} -};`; - - } - - _getGLSLVertexCode( shaderData ) { - - return `#version 300 es - -${ this.getSignature() } - -// precision -${ defaultPrecisions } - -// uniforms -${shaderData.uniforms} - -// varyings -${shaderData.varyings} - -// attributes -${shaderData.attributes} - -// codes -${shaderData.codes} - -void main() { - - // vars - ${shaderData.vars} - - // transforms - ${shaderData.transforms} - - // flow - ${shaderData.flow} - - gl_PointSize = 1.0; - -} -`; - - } - - _getGLSLFragmentCode( shaderData ) { - - return `#version 300 es - -${ this.getSignature() } - -// precision -${ defaultPrecisions } - -// uniforms -${shaderData.uniforms} - -// varyings -${shaderData.varyings} - -// codes -${shaderData.codes} - -${shaderData.structs} - -void main() { - - // vars - ${shaderData.vars} - - // flow - ${shaderData.flow} - -} -`; - - } - - buildCode() { - - const shadersData = this.material !== null ? { fragment: {}, vertex: {} } : { compute: {} }; - - for ( const shaderStage in shadersData ) { - - let flow = '// code\n\n'; - flow += this.flowCode[ shaderStage ]; - - const flowNodes = this.flowNodes[ shaderStage ]; - const mainNode = flowNodes[ flowNodes.length - 1 ]; - - for ( const node of flowNodes ) { - - const flowSlotData = this.getFlowData( node/*, shaderStage*/ ); - const slotName = node.name; - - if ( slotName ) { - - if ( flow.length > 0 ) flow += '\n'; - - flow += `\t// flow -> ${ slotName }\n\t`; - - } - - flow += `${ flowSlotData.code }\n\t`; - - if ( node === mainNode && shaderStage !== 'compute' ) { - - flow += '// result\n\t'; - - if ( shaderStage === 'vertex' ) { - - flow += 'gl_Position = '; - flow += `${ flowSlotData.result };`; - - } else if ( shaderStage === 'fragment' ) { - - if ( ! node.outputNode.isOutputStructNode ) { - - flow += 'fragColor = '; - flow += `${ flowSlotData.result };`; - - } - - } - - } - - } - - const stageData = shadersData[ shaderStage ]; - - stageData.uniforms = this.getUniforms( shaderStage ); - stageData.attributes = this.getAttributes( shaderStage ); - stageData.varyings = this.getVaryings( shaderStage ); - stageData.vars = this.getVars( shaderStage ); - stageData.structs = this.getStructs( shaderStage ); - stageData.codes = this.getCodes( shaderStage ); - stageData.transforms = this.getTransforms( shaderStage ); - stageData.flow = flow; - - } - - if ( this.material !== null ) { - - this.vertexShader = this._getGLSLVertexCode( shadersData.vertex ); - this.fragmentShader = this._getGLSLFragmentCode( shadersData.fragment ); - - } else { - - this.computeShader = this._getGLSLVertexCode( shadersData.compute ); - - } - - } - - getUniformFromNode( node, type, shaderStage, name = null ) { - - const uniformNode = super.getUniformFromNode( node, type, shaderStage, name ); - const nodeData = this.getDataFromNode( node, shaderStage, this.globalCache ); - - let uniformGPU = nodeData.uniformGPU; - - if ( uniformGPU === undefined ) { - - const group = node.groupNode; - const groupName = group.name; - - const bindings = this.getBindGroupArray( groupName, shaderStage ); - - if ( type === 'texture' ) { - - uniformGPU = new NodeSampledTexture( uniformNode.name, uniformNode.node, group ); - bindings.push( uniformGPU ); - - } else if ( type === 'cubeTexture' ) { - - uniformGPU = new NodeSampledCubeTexture( uniformNode.name, uniformNode.node, group ); - bindings.push( uniformGPU ); - - } else if ( type === 'texture3D' ) { - - 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, group ); - buffer.name = node.name; - - bindings.push( buffer ); - - uniformGPU = buffer; - - } else { - - const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} ); - - let uniformsGroup = uniformsStage[ groupName ]; - - if ( uniformsGroup === undefined ) { - - uniformsGroup = new NodeUniformsGroup( shaderStage + '_' + groupName, group ); - //uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); - - uniformsStage[ groupName ] = uniformsGroup; - - bindings.push( uniformsGroup ); - - } - - uniformGPU = this.getNodeUniform( uniformNode, type ); - - uniformsGroup.addUniform( uniformGPU ); - - } - - nodeData.uniformGPU = uniformGPU; - - } - - return uniformNode; - - } - -} - -export default GLSLNodeBuilder;