diff --git a/examples/jsm/nodes/accessors/CameraNode.js b/examples/jsm/nodes/accessors/CameraNode.js index 674e2666d6e0a2..6a2cc0cb7452bc 100644 --- a/examples/jsm/nodes/accessors/CameraNode.js +++ b/examples/jsm/nodes/accessors/CameraNode.js @@ -1,12 +1,33 @@ import { uniform } from '../core/UniformNode.js'; +import { renderGroup } 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 updateNear = ( { camera } ) => camera.near; +const updateFar = ( { camera } ) => camera.far; +const updateViewMatrix = ( { camera } ) => camera.matrixWorldInverse; +const updateWorldMatrix = ( { camera } ) => camera.matrixWorld; +const updateLogDepth = ( { camera } ) => 2.0 / ( Math.log( camera.far + 1.0 ) / Math.LN2 ); +const updateProjectionMatrix = ( { camera } ) => camera.projectionMatrix; +const updateProjectionMatrixInverse = ( { camera } ) => camera.projectionMatrixInverse; +const updateCameraPosition = ( { camera }, self ) => self.value.setFromMatrixPosition( camera.matrixWorld ); +const updateNormalMatrix = ( { camera } ) => camera.normalMatrix; + +export const cameraNear = /*#__PURE__*/ uniform( 'float' ).label( 'cameraNear' ).setGroup( renderGroup ).onRenderUpdate( updateNear ); +export const cameraFar = /*#__PURE__*/ uniform( 'float' ).label( 'cameraFar' ).setGroup( renderGroup ).onRenderUpdate( updateFar ); +export const cameraLogDepth = /*#__PURE__*/ uniform( 'float' ).label( 'cameraLogDepth' ).setGroup( renderGroup ).onRenderUpdate( updateLogDepth ); +export const cameraProjectionMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrix' ).setGroup( renderGroup ).onRenderUpdate( updateProjectionMatrix ); +export const cameraProjectionMatrixInverse = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraProjectionMatrixInverse' ).setGroup( renderGroup ).onRenderUpdate( updateProjectionMatrix ); +export const cameraViewMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraViewMatrix' ).setGroup( renderGroup ).onRenderUpdate( updateViewMatrix ); +export const cameraWorldMatrix = /*#__PURE__*/ uniform( 'mat4' ).label( 'cameraWorldMatrix' ).setGroup( renderGroup ).onRenderUpdate( updateWorldMatrix ); +export const cameraNormalMatrix = /*#__PURE__*/ uniform( 'mat3' ).label( 'cameraNormalMatrix' ).setGroup( renderGroup ).onRenderUpdate( updateNormalMatrix ); +export const cameraPosition = /*#__PURE__*/ uniform( new Vector3() ).label( 'cameraPosition' ).setGroup( renderGroup ).onRenderUpdate( updateCameraPosition ); + +renderGroup.register( 'cameraViewMatrix', 'mat4', updateViewMatrix ); +renderGroup.register( 'cameraWorldMatrix', 'mat4', updateWorldMatrix ); +renderGroup.register( 'cameraProjectionMatrix', 'mat4', updateProjectionMatrix ); +renderGroup.register( 'cameraProjectionMatrixInverse', 'mat4', updateProjectionMatrixInverse ); +renderGroup.register( 'cameraNormalMatrix', 'mat3', updateNormalMatrix ); +renderGroup.register( 'cameraPosition', 'vec3', updateCameraPosition ); +renderGroup.register( 'cameraNear', 'float', updateNear ); +renderGroup.register( 'cameraFar', 'float', updateFar ); +renderGroup.register( 'cameraLogDepth', 'float', updateLogDepth ); diff --git a/examples/jsm/nodes/core/NodeFrame.js b/examples/jsm/nodes/core/NodeFrame.js index 6d30fd88a346d3..e892cb54f35bb2 100644 --- a/examples/jsm/nodes/core/NodeFrame.js +++ b/examples/jsm/nodes/core/NodeFrame.js @@ -162,6 +162,11 @@ class NodeFrame { node.update( this ); + } else if ( updateType === NodeUpdateType.ONCE ) { + + node.update( this ); + node.updateType = NodeUpdateType.NONE; + } } diff --git a/examples/jsm/nodes/core/UniformGroupNode.js b/examples/jsm/nodes/core/UniformGroupNode.js index f35a002bd1fa75..4404949dc1a68b 100644 --- a/examples/jsm/nodes/core/UniformGroupNode.js +++ b/examples/jsm/nodes/core/UniformGroupNode.js @@ -14,6 +14,8 @@ class UniformGroupNode extends Node { this.isUniformGroup = true; + this.registered = []; + } set needsUpdate( value ) { @@ -22,6 +24,12 @@ class UniformGroupNode extends Node { } + register( name, type, callback = () => {} ) { + + this.registered.push( { name, type, callback } ); + + } + } export const uniformGroup = ( name ) => new UniformGroupNode( name ); diff --git a/examples/jsm/nodes/core/constants.js b/examples/jsm/nodes/core/constants.js index 1ef0448f8dccca..2e41a4c4c9c3ca 100644 --- a/examples/jsm/nodes/core/constants.js +++ b/examples/jsm/nodes/core/constants.js @@ -7,7 +7,8 @@ export const NodeUpdateType = { NONE: 'none', FRAME: 'frame', RENDER: 'render', - OBJECT: 'object' + OBJECT: 'object', + ONCE: 'once' }; export const NodeType = { diff --git a/examples/jsm/renderers/common/Renderer.js b/examples/jsm/renderers/common/Renderer.js index 4cfa51b39f2985..36c86d3b889a32 100644 --- a/examples/jsm/renderers/common/Renderer.js +++ b/examples/jsm/renderers/common/Renderer.js @@ -299,6 +299,7 @@ class Renderer { // this._background.update( sceneRef, renderList, renderContext ); + this._nodes.updateRendererBindings( renderContext, camera ); // process render lists @@ -639,6 +640,8 @@ class Renderer { // + this._nodes.updateRendererBindings( renderContext, camera ); + this._background.update( sceneRef, renderList, renderContext ); // diff --git a/examples/jsm/renderers/common/UniformsGroup.js b/examples/jsm/renderers/common/UniformsGroup.js index 21bb2cd33010f4..9b0bea0d56aa2c 100644 --- a/examples/jsm/renderers/common/UniformsGroup.js +++ b/examples/jsm/renderers/common/UniformsGroup.js @@ -270,7 +270,9 @@ class UniformsGroup extends UniformBuffer { let updated = false; const a = this.buffer; + const e = uniform.getValue().elements; + const offset = uniform.offset; if ( arraysEqual( a, e, offset ) === false ) { diff --git a/examples/jsm/renderers/common/nodes/Nodes.js b/examples/jsm/renderers/common/nodes/Nodes.js index d126d0ff01430a..b80ea345484084 100644 --- a/examples/jsm/renderers/common/nodes/Nodes.js +++ b/examples/jsm/renderers/common/nodes/Nodes.js @@ -1,8 +1,14 @@ import DataMap from '../DataMap.js'; import ChainMap from '../ChainMap.js'; import NodeBuilderState from './NodeBuilderState.js'; -import { EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping, SRGBColorSpace } from 'three'; -import { NodeFrame, vec4, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, viewportBottomLeft, normalWorld, pmremTexture, viewportTopLeft } from '../../../nodes/Nodes.js'; +import { EquirectangularReflectionMapping, EquirectangularRefractionMapping, NoToneMapping, SRGBColorSpace, Vector3 } from 'three'; +import { NodeFrame, vec4, objectGroup, renderGroup, frameGroup, cubeTexture, texture, rangeFog, densityFog, reference, viewportBottomLeft, normalWorld, pmremTexture, viewportTopLeft, uniform, NodeUniform, NodeUpdateType } from '../../../nodes/Nodes.js'; +import { + FloatNodeUniform, Vector2NodeUniform, Vector3NodeUniform, Vector4NodeUniform, + ColorNodeUniform, Matrix3NodeUniform, Matrix4NodeUniform +} from './NodeUniform.js'; +import NodeUniformsGroup from './NodeUniformsGroup.js'; +import { getValueFromType } from '../../../nodes/core/NodeUtils.js'; class Nodes extends DataMap { @@ -28,6 +34,7 @@ class Nodes extends DataMap { if ( name === objectGroup.name ) return true; + /* // renderGroup is updated once per render/compute call if ( name === renderGroup.name ) { @@ -46,6 +53,7 @@ class Nodes extends DataMap { return false; } + */ // frameGroup is updated once per frame @@ -114,6 +122,7 @@ class Nodes extends DataMap { nodeBuilder.environmentNode = this.getEnvironmentNode( renderObject.scene ); nodeBuilder.fogNode = this.getFogNode( renderObject.scene ); nodeBuilder.clippingContext = renderObject.clippingContext; + nodeBuilder.renderNodeUniformsGroup = renderObject.context.bindings[ 0 ]; nodeBuilder.build(); nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); @@ -188,6 +197,35 @@ class Nodes extends DataMap { } + _createRenderBindings( renderContext ) { + + const group = new NodeUniformsGroup( 'render', renderGroup ); + + const registeredUniforms = renderGroup.registered; + + for ( let i = 0, l = registeredUniforms.length; i < l; i ++ ) { + + const uniformDesc = registeredUniforms[ i ]; + + const t = getValueFromType( uniformDesc.type ); + // const v = uniformDesc.type === 'vec3' ? new Vector3() : uniformDesc.type; + + const tempUniform = uniform( t ).label( uniformDesc.name ).onUpdate( uniformDesc.callback, NodeUpdateType.ONCE ); + const nodeUniform = this.getNodeUniform( new NodeUniform( uniformDesc.name, uniformDesc.type, tempUniform ), uniformDesc.type ); + + group.addUniform( nodeUniform ); + + } + + // FIXME hardcode WebGPU visibility + group.setVisibility( 1 ); + group.setVisibility( 2 ); + + renderContext.bindings = [ group ]; + this.backend.createBindings( renderContext.bindings, 'render' ); + + }; + getEnvironmentNode( scene ) { return scene.environmentNode || this.get( scene ).environmentNode || null; @@ -245,6 +283,51 @@ class Nodes extends DataMap { } + updateRendererBindings( renderContext, camera ) { + + if ( renderContext.bindings === undefined ) { + + this._createRenderBindings( renderContext ); + + } + + const binding = renderContext.bindings[ 0 ]; + + const nodes = binding.getNodes(); + const nodeFrame = this.getNodeFrame( this.renderer, null, null, camera, null ); + + for ( let i = 0, l = nodes.length; i < l; i ++ ) { + + nodeFrame.updateNode( nodes[ i ] ); + + } + + if ( binding.update() ) { + + this.backend.updateBinding( binding ); + + } + + } + + getNodeUniform( uniformNode, type ) { + + // duplicated from NodeBuilder - move to NodeUtils for reuse? + + if ( type === 'float' ) return new FloatNodeUniform( uniformNode ); + if ( type === 'vec2' ) return new Vector2NodeUniform( uniformNode ); + if ( type === 'vec3' ) return new Vector3NodeUniform( uniformNode ); + if ( type === 'vec4' ) return new Vector4NodeUniform( uniformNode ); + if ( type === 'color' ) return new ColorNodeUniform( uniformNode ); + if ( type === 'mat3' ) return new Matrix3NodeUniform( uniformNode ); + if ( type === 'mat4' ) return new Matrix4NodeUniform( uniformNode ); + + if ( type.isVector3 ) return new Vector3NodeUniform( uniformNode ); + + throw new Error( `Uniform "${type}" not declared.` ); + + } + get isToneMappingState() { return this.renderer.getRenderTarget() ? false : true; diff --git a/examples/jsm/renderers/webgpu/WebGPUBackend.js b/examples/jsm/renderers/webgpu/WebGPUBackend.js index f28e1bc41e0426..ac47504086e7e5 100644 --- a/examples/jsm/renderers/webgpu/WebGPUBackend.js +++ b/examples/jsm/renderers/webgpu/WebGPUBackend.js @@ -446,6 +446,11 @@ class WebGPUBackend extends Backend { } + const bindingsData = this.get( renderContext.bindings ); + const bindGroupGPU = bindingsData.group; + + currentPass.setBindGroup( 0, bindGroupGPU ); + } finishRender( renderContext ) { @@ -747,7 +752,7 @@ class WebGPUBackend extends Backend { const groupGPU = this.get( computeGroup ); - const descriptor = {}; + const descriptor = { label: 'compute_pass' }; this.initTimestampQuery( computeGroup, descriptor ); @@ -825,8 +830,15 @@ class WebGPUBackend extends Backend { // bind group + if ( this.renderer._currentRenderBundle ) { + + const bindGroupGPU = this.get( context.bindings ).group; + passEncoderGPU.setBindGroup( 0, bindGroupGPU ); + + } + const bindGroupGPU = bindingsData.group; - passEncoderGPU.setBindGroup( 0, bindGroupGPU ); + passEncoderGPU.setBindGroup( 1, bindGroupGPU ); // attributes @@ -1198,9 +1210,9 @@ class WebGPUBackend extends Backend { // bindings - createBindings( bindings ) { + createBindings( bindings, label ) { - this.bindingUtils.createBindings( bindings ); + this.bindingUtils.createBindings( bindings, label ); } @@ -1282,7 +1294,7 @@ class WebGPUBackend extends Backend { dstY = dstPosition.y; } - + const encoder = this.device.createCommandEncoder( { label: 'copyTextureToTexture_' + srcTexture.id + '_' + dstTexture.id } ); const sourceGPU = this.get( srcTexture ).texture; @@ -1378,6 +1390,11 @@ class WebGPUBackend extends Backend { renderContextData.currentPass = encoder.beginRenderPass( descriptor ); renderContextData.currentSets = { attributes: {} }; + const bindingsData = this.get( renderContext.bindings ); + const bindGroupGPU = bindingsData.group; + + renderContextData.currentPass.setBindGroup( 0, bindGroupGPU ); + } } diff --git a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js index 7964f4299b913e..4065ab28c72b3c 100644 --- a/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js +++ b/examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js @@ -130,6 +130,9 @@ fn threejs_repeatWrapping( uv : vec2, dimension : vec2 ) -> vec2 ` ) }; +const RENDER_BINDING_GROUP = 0; +const OBJECT_BINDING_GROUP = 1; + class WGSLNodeBuilder extends NodeBuilder { constructor( object, renderer, scene = null ) { @@ -467,22 +470,48 @@ class WGSLNodeBuilder extends NodeBuilder { const uniformsStage = this.uniformGroups[ shaderStage ] || ( this.uniformGroups[ shaderStage ] = {} ); - let uniformsGroup = uniformsStage[ groupName ]; + if ( group.shared == true ) { - if ( uniformsGroup === undefined ) { + const rnug = this.renderNodeUniformsGroup; + const nodeUniforms = rnug.uniforms; - uniformsGroup = new NodeUniformsGroup( groupName, group ); - uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); + uniformsStage[ groupName ] = rnug; - uniformsStage[ groupName ] = uniformsGroup; + for ( let i = 0, l = nodeUniforms.length; i < l; i ++ ) { - bindings.push( uniformsGroup ); + const nodeUniform = nodeUniforms[ i ]; - } + if ( nodeUniform.name === uniformNode.name ) { + + nodeUniform.nodeUniform = uniformNode; + uniformGPU = nodeUniform; + + break; + + } + + } + + } else { + + let uniformsGroup = uniformsStage[ groupName ]; - uniformGPU = this.getNodeUniform( uniformNode, type ); + if ( uniformsGroup === undefined ) { - uniformsGroup.addUniform( uniformGPU ); + uniformsGroup = new NodeUniformsGroup( groupName, group ); + uniformsGroup.setVisibility( gpuShaderStageLib[ shaderStage ] ); + + uniformsStage[ groupName ] = uniformsGroup; + + bindings.push( uniformsGroup ); + + } + + uniformGPU = this.getNodeUniform( uniformNode, type ); + + uniformsGroup.addUniform( uniformGPU ); + + } } @@ -749,7 +778,6 @@ ${ flowData.code } attributesSnippet += ' @interpolate( flat )'; - } snippets.push( `${ attributesSnippet } ${ varying.name } : ${ this.getType( varying.type ) }` ); @@ -783,6 +811,8 @@ ${ flowData.code } const structSnippets = []; const uniformGroups = {}; + const object_binding_group = shaderStage === 'compute' ? RENDER_BINDING_GROUP : OBJECT_BINDING_GROUP; + let index = this.bindingsOffset[ shaderStage ]; for ( const uniform of uniforms ) { @@ -795,11 +825,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( ${index ++} ) @group( ${ object_binding_group } ) var ${uniform.name}_sampler : sampler_comparison;` ); } else { - bindingSnippets.push( `@binding( ${index ++} ) @group( 0 ) var ${uniform.name}_sampler : sampler;` ); + bindingSnippets.push( `@binding( ${index ++} ) @group( ${ object_binding_group } ) var ${uniform.name}_sampler : sampler;` ); } @@ -842,7 +872,7 @@ ${ flowData.code } } - bindingSnippets.push( `@binding( ${index ++} ) @group( 0 ) var ${uniform.name} : ${textureType};` ); + bindingSnippets.push( `@binding( ${index ++} ) @group( ${ object_binding_group } ) var ${uniform.name} : ${textureType};` ); } else if ( uniform.type === 'buffer' || uniform.type === 'storageBuffer' ) { @@ -854,19 +884,34 @@ ${ flowData.code } 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, index ++, object_binding_group ) ); } else { const vectorType = this.getType( this.getVectorType( uniform.type ) ); const groupName = uniform.groupNode.name; - const group = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = { - index: index ++, - snippets: [] - } ); + if ( ! uniform.groupNode.shared ) { + + const group = uniformGroups[ groupName ] || ( uniformGroups[ groupName ] = { + index: index ++, + snippets: [] + } ); - group.snippets.push( `\t${ uniform.name } : ${ vectorType }` ); + group.snippets.push( `\t${ uniform.name } : ${ vectorType }` ); + + } else { + + if ( uniformGroups[ groupName ] === undefined ) { + + uniformGroups[ groupName ] = { + index: index, + snippets: [] + }; + + } + + } } @@ -876,7 +921,30 @@ ${ flowData.code } const group = uniformGroups[ name ]; - structSnippets.push( this._getWGSLStructBinding( name, group.snippets.join( ',\n' ), 'uniform', group.index ) ); + const nodeUniformsGroup = this.uniformGroups[ shaderStage ][ name ]; + + if ( nodeUniformsGroup.shared ) { + + const registered = nodeUniformsGroup.groupNode.registered; + const snippets = []; + + for ( let i = 0, l = registered.length; i < l; i ++ ) { + + const registeredUniform = registered[ i ]; + const type = this.getType( registeredUniform.type ); + + snippets.push( `\t${ registeredUniform.name } : ${ type }` ); + + } + + structSnippets.push( this._getWGSLStructBinding( name, snippets.join( ',\n' ), 'uniform', 0, RENDER_BINDING_GROUP ) ); + + } else { + + structSnippets.push( this._getWGSLStructBinding( name, group.snippets.join( ',\n' ), 'uniform', group.index, object_binding_group ) ); + + } + } diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUBindingUtils.js index 6359f78b593dcc..5930d09dbe6c04 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( bindings, label ) { const backend = this.backend; const device = backend.device; @@ -122,19 +122,19 @@ class WebGPUBindingUtils { } - return device.createBindGroupLayout( { entries } ); + return device.createBindGroupLayout( { entries, label } ); } - createBindings( bindings ) { + createBindings( bindings, label = 'object' ) { const backend = this.backend; const bindingsData = backend.get( bindings ); // setup (static) binding layout and (dynamic) binding group - const bindLayoutGPU = this.createBindingsLayout( bindings ); - const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU ); + const bindLayoutGPU = this.createBindingsLayout( bindings, label ); + const bindGroupGPU = this.createBindGroup( bindings, bindLayoutGPU, label ); bindingsData.layout = bindLayoutGPU; bindingsData.group = bindGroupGPU; @@ -154,7 +154,7 @@ class WebGPUBindingUtils { } - createBindGroup( bindings, layoutGPU ) { + createBindGroup( bindings, layoutGPU, label ) { const backend = this.backend; const device = backend.device; @@ -257,7 +257,8 @@ class WebGPUBindingUtils { return device.createBindGroup( { layout: layoutGPU, - entries: entriesGPU + entries: entriesGPU, + label } ); } diff --git a/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js index d78112e3675f09..bbf7c250045dfb 100644 --- a/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js +++ b/examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js @@ -55,6 +55,7 @@ class WebGPUPipelineUtils { const pipelineData = backend.get( pipeline ); const bindingsData = backend.get( renderObject.getBindings() ); + const renderBindingData = backend.get( renderObject.context.bindings ); // vertex buffers @@ -145,7 +146,7 @@ class WebGPUPipelineUtils { alphaToCoverageEnabled: material.alphaToCoverage }, layout: device.createPipelineLayout( { - bindGroupLayouts: [ bindingsData.layout ] + bindGroupLayouts: [ renderBindingData.layout, bindingsData.layout ] } ) };