Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

WebGPURenderer: Introduce Bind Groups #28705

Merged
merged 3 commits into from
Jun 21, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 16 additions & 9 deletions examples/jsm/nodes/accessors/CameraNode.js
Original file line number Diff line number Diff line change
@@ -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 ) );
113 changes: 96 additions & 17 deletions examples/jsm/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
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
Expand All @@ -28,7 +30,7 @@

import PMREMGenerator from '../../renderers/common/extras/PMREMGenerator.js';

const uniformsGroupCache = new ChainMap();
const bindGroupsCache = new ChainMap();

const typeFromLength = new Map( [
[ 2, 'vec2' ],
Expand Down Expand Up @@ -87,9 +89,9 @@
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 = [];
Expand All @@ -101,6 +103,8 @@
this.stacks = [];
this.tab = '\t';

this.instanceBindGroups = true;

this.currentFunctionNode = null;

this.context = {
Expand Down Expand Up @@ -144,54 +148,129 @@

}

_getSharedBindings( bindings ) {
_getBindGroup( groupName, bindings ) {

// cache individual uniforms group

const bindingsArray = [];

const shared = [];
let sharedGroup = true;

for ( const binding of bindings ) {

if ( binding.shared === true ) {
if ( binding.groupNode.shared === true ) {

// nodes is the chainmap key
const nodes = binding.getNodes();

let sharedBinding = uniformsGroupCache.get( nodes );
let sharedBinding = bindGroupsCache.get( nodes );

if ( sharedBinding === undefined ) {

uniformsGroupCache.set( nodes, binding );
bindGroupsCache.set( nodes, binding );

sharedBinding = binding;

}

shared.push( sharedBinding );
bindingsArray.push( sharedBinding );

} else {

shared.push( binding );
bindingsArray.push( binding );

sharedGroup = false;

}

}

return shared;
//

let bindGroup;

if ( sharedGroup ) {

bindGroup = bindGroupsCache.get( bindingsArray );

if ( bindGroup === undefined ) {

bindGroup = new BindGroup( groupName, bindingsArray );
bindGroupsCache.set( bindingsArray, bindGroup );

}

} else {

bindGroup = new BindGroup( groupName, bindingsArray );

}

return bindGroup;

}

getBindGroupArray( groupName, shaderStage ) {

const bindings = this.bindings[ shaderStage ];

let bindGroup = bindings[ groupName ];

if ( bindGroup === undefined ) {

if ( this.bindingsIndexes[ groupName ] === undefined ) {

this.bindingsIndexes[ groupName ] = { binding: 0, group: Object.keys( this.bindingsIndexes ).length };

}

bindings[ groupName ] = bindGroup = [];
Dismissed Show dismissed Hide dismissed

}

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;

}

Expand Down Expand Up @@ -1280,7 +1359,7 @@

getSignature() {

return `// Three.js r${ REVISION } - NodeMaterial System\n`;
return `// Three.js r${ REVISION } - Node System\n`;

}

Expand Down
3 changes: 1 addition & 2 deletions examples/jsm/nodes/core/NodeUniform.js
Original file line number Diff line number Diff line change
@@ -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;

}

Expand Down
2 changes: 1 addition & 1 deletion examples/jsm/renderers/common/Backend.js
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ class Backend {

createBindings( renderObject ) { }

updateBindings( renderObject ) { }
_setupBindingsIndexes( renderObject ) { }

// pipeline

Expand Down
16 changes: 16 additions & 0 deletions examples/jsm/renderers/common/BindGroup.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
let _id = 0;

class BindGroup {

constructor( name = '', bindings = [] ) {

this.name = name;
this.bindings = bindings;

this.id = _id ++;

}

}

export default BindGroup;
61 changes: 38 additions & 23 deletions examples/jsm/renderers/common/Bindings.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,61 +22,77 @@ class Bindings extends DataMap {

const bindings = renderObject.getBindings();

const data = this.get( renderObject );
for ( const bindGroup of bindings ) {

if ( data.bindings !== bindings ) {
const groupData = this.get( bindGroup );

// each object defines an array of bindings (ubos, textures, samplers etc.)
if ( groupData.bindGroup === undefined ) {

data.bindings = bindings;
// each object defines an array of bindings (ubos, textures, samplers etc.)

this._init( bindings );
this._init( bindGroup );

this.backend.createBindings( bindings );
this.backend.createBindings( bindGroup, bindings );

groupData.bindGroup = bindGroup;

}

}

return data.bindings;
return bindings;

}

getForCompute( computeNode ) {

const data = this.get( computeNode );
const bindings = this.nodes.getForCompute( computeNode ).bindings;

if ( data.bindings === undefined ) {
for ( const bindGroup of bindings ) {

const nodeBuilderState = this.nodes.getForCompute( computeNode );
const groupData = this.get( bindGroup );

const bindings = nodeBuilderState.bindings;
if ( groupData.bindGroup === undefined ) {

data.bindings = bindings;
this._init( bindGroup );

this._init( bindings );
this.backend.createBindings( bindGroup, bindings );

this.backend.createBindings( bindings );
groupData.bindGroup = bindGroup;

}

}

return data.bindings;
return bindings;

}

updateForCompute( computeNode ) {

this._update( computeNode, this.getForCompute( computeNode ) );
this._updateBindings( computeNode, this.getForCompute( computeNode ) );

}

updateForRender( renderObject ) {

this._update( renderObject, this.getForRender( renderObject ) );
this._updateBindings( renderObject, this.getForRender( renderObject ) );

}

_init( bindings ) {
_updateBindings( object, bindings ) {

for ( const bindGroup of bindings ) {

for ( const binding of bindings ) {
this._update( object, bindGroup, bindings );

}

}

_init( bindGroup ) {

for ( const binding of bindGroup.bindings ) {

if ( binding.isSampledTexture ) {

Expand All @@ -94,15 +110,15 @@ class Bindings extends DataMap {

}

_update( object, bindings ) {
_update( object, bindGroup, bindings ) {

const { backend } = this;

let needsBindingsUpdate = false;

// 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 ) {

Expand Down Expand Up @@ -152,7 +168,6 @@ class Bindings extends DataMap {

}


if ( texture.isStorageTexture === true ) {

const textureData = this.get( texture );
Expand All @@ -179,7 +194,7 @@ class Bindings extends DataMap {

const pipeline = this.pipelines.getForRender( object );

this.backend.updateBindings( bindings, pipeline );
this.backend.updateBindings( bindGroup, bindings, pipeline );

}

Expand Down
Loading