Skip to content

Commit

Permalink
WebGPURenderer: Better skinning performance (#27753)
Browse files Browse the repository at this point in the history
* Better skinning performance

* TSL: Rename morph -> morphReference

* cleanup
  • Loading branch information
sunag authored Feb 17, 2024
1 parent a9db0d3 commit 319e314
Show file tree
Hide file tree
Showing 15 changed files with 192 additions and 76 deletions.
4 changes: 2 additions & 2 deletions examples/jsm/nodes/Nodes.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,15 +87,15 @@ export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTexture
export { default as InstanceNode, instance } from './accessors/InstanceNode.js';
export { default as MaterialNode, materialAlphaTest, materialColor, materialShininess, materialEmissive, materialOpacity, materialSpecularColor, materialSpecularStrength, materialReflectivity, materialRoughness, materialMetalness, materialNormal, materialClearcoat, materialClearcoatRoughness, materialClearcoatNormal, materialRotation, materialSheen, materialSheenRoughness, materialIridescence, materialIridescenceIOR, materialIridescenceThickness, materialLineScale, materialLineDashSize, materialLineGapSize, materialLineWidth, materialLineDashOffset, materialPointWidth } from './accessors/MaterialNode.js';
export { default as MaterialReferenceNode, materialReference } from './accessors/MaterialReferenceNode.js';
export { default as MorphNode, morph } from './accessors/MorphNode.js';
export { default as MorphNode, morphReference } from './accessors/MorphNode.js';
export { default as TextureBicubicNode, textureBicubic } from './accessors/TextureBicubicNode.js';
export { default as ModelNode, modelDirection, modelViewMatrix, modelNormalMatrix, modelWorldMatrix, modelPosition, modelViewPosition, modelScale } from './accessors/ModelNode.js';
export { default as ModelViewProjectionNode, modelViewProjection } from './accessors/ModelViewProjectionNode.js';
export { default as NormalNode, normalGeometry, normalLocal, normalView, normalWorld, transformedNormalView, transformedNormalWorld, transformedClearcoatNormalView } from './accessors/NormalNode.js';
export { default as Object3DNode, objectDirection, objectViewMatrix, objectNormalMatrix, objectWorldMatrix, objectPosition, objectScale, objectViewPosition } from './accessors/Object3DNode.js';
export { default as PointUVNode, pointUV } from './accessors/PointUVNode.js';
export { default as PositionNode, positionGeometry, positionLocal, positionWorld, positionWorldDirection, positionView, positionViewDirection } from './accessors/PositionNode.js';
export { default as ReferenceNode, reference, referenceIndex } from './accessors/ReferenceNode.js';
export { default as ReferenceNode, reference, referenceBuffer } from './accessors/ReferenceNode.js';
export { default as ReflectVectorNode, reflectVector } from './accessors/ReflectVectorNode.js';
export { default as SkinningNode, skinning } from './accessors/SkinningNode.js';
export { default as SceneNode, backgroundBlurriness, backgroundIntensity } from './accessors/SceneNode.js';
Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/nodes/accessors/MaterialNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,11 +227,11 @@ class MaterialNode extends Node {

} else if ( scope === MaterialNode.IRIDESCENCE_THICKNESS ) {

const iridescenceThicknessMaximum = reference( 1, 'float', material.iridescenceThicknessRange );
const iridescenceThicknessMaximum = reference( '1', 'float', material.iridescenceThicknessRange );

if ( material.iridescenceThicknessMap ) {

const iridescenceThicknessMinimum = reference( 0, 'float', material.iridescenceThicknessRange );
const iridescenceThicknessMinimum = reference( '0', 'float', material.iridescenceThicknessRange );

node = iridescenceThicknessMaximum.sub( iridescenceThicknessMinimum ).mul( this.getTexture( scope ).g ).add( iridescenceThicknessMinimum );

Expand Down
14 changes: 2 additions & 12 deletions examples/jsm/nodes/accessors/MaterialReferenceNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,24 +24,14 @@ class MaterialReferenceNode extends ReferenceNode {
}*/

updateReference( frame ) {
setReference( state ) {

this.reference = this.material !== null ? this.material : frame.material;
this.reference = this.material !== null ? this.material : state.material;

return this.reference;

}

setup( builder ) {

const material = this.material !== null ? this.material : builder.material;

this.node.value = material[ this.property ];

return super.setup( builder );

}

}

export default MaterialReferenceNode;
Expand Down
6 changes: 3 additions & 3 deletions examples/jsm/nodes/accessors/MorphNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy, tslFn } from '../shadernode/ShaderNode.js';
import { uniform } from '../core/UniformNode.js';
import { referenceIndex } from './ReferenceNode.js';
import { reference } from './ReferenceNode.js';
import { positionLocal } from './PositionNode.js';
import { normalLocal } from './NormalNode.js';
import { textureLoad } from './TextureNode.js';
Expand Down Expand Up @@ -188,7 +188,7 @@ class MorphNode extends Node {

loop( morphTargetsCount, ( { i } ) => {

const influence = referenceIndex( 'morphTargetInfluences', i, 'float' );
const influence = reference( 'morphTargetInfluences', 'float' ).element( i );

if ( hasMorphPosition === true ) {

Expand Down Expand Up @@ -240,6 +240,6 @@ class MorphNode extends Node {

export default MorphNode;

export const morph = nodeProxy( MorphNode );
export const morphReference = nodeProxy( MorphNode );

addNodeClass( 'MorphNode', MorphNode );
114 changes: 86 additions & 28 deletions examples/jsm/nodes/accessors/ReferenceNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,54 +2,85 @@ import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { uniform } from '../core/UniformNode.js';
import { texture } from './TextureNode.js';
import { buffer } from './BufferNode.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
import { uniforms } from './UniformsNode.js';
import ArrayElementNode from '../utils/ArrayElementNode.js';

class ReferenceElementNode extends ArrayElementNode {

constructor( referenceNode, indexNode ) {

super( referenceNode, indexNode );

this.referenceNode = referenceNode;

this.isReferenceElementNode = true;

}

getNodeType() {

return this.referenceNode.uniformType;

}

generate( builder ) {

const snippet = super.generate( builder );
const arrayType = this.referenceNode.getNodeType();
const elementType = this.getNodeType();

return builder.format( snippet, arrayType, elementType );

}

}

class ReferenceNode extends Node {

constructor( property, uniformType, object = null, indexNode = null ) {
constructor( property, uniformType, object = null, count = null ) {

super();

this.property = property;
this.indexNode = indexNode;

this.uniformType = uniformType;

this.object = object;
this.reference = null;
this.count = count;

this.properties = property.split( '.' );
this.reference = null;
this.node = null;

this.updateType = NodeUpdateType.OBJECT;

this.setNodeType( uniformType );

}

updateReference( frame ) {
element( indexNode ) {

this.reference = this.object !== null ? this.object : frame.object;

return this.reference;
return nodeObject( new ReferenceElementNode( this, nodeObject( indexNode ) ) );

}

setNodeType( uniformType ) {

let node = null;

if ( uniformType === 'texture' ) {
if ( this.count !== null ) {

node = texture( null );
node = buffer( null, uniformType, this.count );

} else if ( Array.isArray( this.getValueFromReference() ) ) {

node = uniforms( null, uniformType );

} else if ( this.indexNode !== null ) {
} else if ( uniformType === 'texture' ) {

node = uniforms( null, uniformType ).element( this.indexNode );
node = texture( null );

} else {

node = uniform( uniformType );
node = uniform( null, uniformType );

}

Expand All @@ -63,40 +94,67 @@ class ReferenceNode extends Node {

}

update( /*frame*/ ) {
getValueFromReference( object = this.reference ) {

const value = this.reference[ this.property ];
const { properties } = this;

if ( this.indexNode !== null ) {
let value = object[ properties[ 0 ] ];

this.node.node.array = value;
for ( let i = 1; i < properties.length; i ++ ) {

} else {

this.node.value = value;
value = value[ properties[ i ] ];

}

return value;

}

setup( builder ) {
setReference( state ) {

if ( this.indexNode !== null ) {
this.reference = this.object !== null ? this.object : state.object;

this.node.node.array = ( this.object !== null ? this.object : builder.object )[ this.property ];
return this.reference;

}
}

setup() {

this.updateValue();

return this.node;

}

update( /*frame*/ ) {

this.updateValue();

}

updateValue() {

if ( this.node === null ) this.setNodeType( this.uniformType );

const value = this.getValueFromReference();

if ( Array.isArray( value ) ) {

this.node.array = value;

} else {

this.node.value = value;

}

}

}

export default ReferenceNode;

export const reference = ( name, type, object ) => nodeObject( new ReferenceNode( name, type, object ) );
export const referenceIndex = ( name, index, type, object ) => nodeObject( new ReferenceNode( name, type, object, index ) );
export const referenceBuffer = ( name, type, count, object ) => nodeObject( new ReferenceNode( name, type, object, count ) );

addNodeClass( 'ReferenceNode', ReferenceNode );
41 changes: 31 additions & 10 deletions examples/jsm/nodes/accessors/SkinningNode.js
Original file line number Diff line number Diff line change
@@ -1,21 +1,23 @@
import Node, { addNodeClass } from '../core/Node.js';
import { NodeUpdateType } from '../core/constants.js';
import { nodeProxy } from '../shadernode/ShaderNode.js';
import { nodeObject } from '../shadernode/ShaderNode.js';
import { attribute } from '../core/AttributeNode.js';
import { uniform } from '../core/UniformNode.js';
import { reference, referenceBuffer } from './ReferenceNode.js';
import { add } from '../math/OperatorNode.js';
import { buffer } from './BufferNode.js';
import { normalLocal } from './NormalNode.js';
import { positionLocal } from './PositionNode.js';
import { tangentLocal } from './TangentNode.js';
import { uniform } from '../core/UniformNode.js';
import { buffer } from './BufferNode.js';

class SkinningNode extends Node {

constructor( skinnedMesh ) {
constructor( skinnedMesh, useReference = false ) {

super( 'void' );

this.skinnedMesh = skinnedMesh;
this.useReference = useReference;

this.updateType = NodeUpdateType.OBJECT;

Expand All @@ -24,9 +26,25 @@ class SkinningNode extends Node {
this.skinIndexNode = attribute( 'skinIndex', 'uvec4' );
this.skinWeightNode = attribute( 'skinWeight', 'vec4' );

this.bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
this.bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
this.boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );
let bindMatrixNode, bindMatrixInverseNode, boneMatricesNode;

if ( useReference ) {

bindMatrixNode = reference( 'bindMatrix', 'mat4' );
bindMatrixInverseNode = reference( 'bindMatrixInverse', 'mat4' );
boneMatricesNode = referenceBuffer( 'skeleton.boneMatrices', 'mat4', skinnedMesh.skeleton.bones.length );

} else {

bindMatrixNode = uniform( skinnedMesh.bindMatrix, 'mat4' );
bindMatrixInverseNode = uniform( skinnedMesh.bindMatrixInverse, 'mat4' );
boneMatricesNode = buffer( skinnedMesh.skeleton.boneMatrices, 'mat4', skinnedMesh.skeleton.bones.length );

}

this.bindMatrixNode = bindMatrixNode;
this.bindMatrixInverseNode = bindMatrixInverseNode;
this.boneMatricesNode = boneMatricesNode;

}

Expand Down Expand Up @@ -88,16 +106,19 @@ class SkinningNode extends Node {

}

update() {
update( frame ) {

const object = this.useReference ? frame.object : this.skinnedMesh;

this.skinnedMesh.skeleton.update();
object.skeleton.update();

}

}

export default SkinningNode;

export const skinning = nodeProxy( SkinningNode );
export const skinning = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh ) );
export const skinningReference = ( skinnedMesh ) => nodeObject( new SkinningNode( skinnedMesh, true ) );

addNodeClass( 'SkinningNode', SkinningNode );
2 changes: 1 addition & 1 deletion examples/jsm/nodes/accessors/TextureNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ class TextureNode extends UniformNode {

}

updateReference( /*frame*/ ) {
setReference( /*state*/ ) {

return this.value;

Expand Down
4 changes: 3 additions & 1 deletion examples/jsm/nodes/core/Node.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ class Node extends EventDispatcher {

}

updateReference() {
setReference( /*state*/ ) {

return this;

Expand Down Expand Up @@ -231,6 +231,8 @@ class Node extends EventDispatcher {

if ( buildStage === 'setup' ) {

this.setReference( builder );

const properties = builder.getNodeProperties( this );

if ( properties.initialized !== true || builder.context.tempRead === false ) {
Expand Down
4 changes: 2 additions & 2 deletions examples/jsm/nodes/core/NodeFrame.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ class NodeFrame {
updateBeforeNode( node ) {

const updateType = node.getUpdateBeforeType();
const reference = node.updateReference( this );
const reference = node.setReference( this );

if ( updateType === NodeUpdateType.FRAME ) {

Expand Down Expand Up @@ -86,7 +86,7 @@ class NodeFrame {
updateNode( node ) {

const updateType = node.getUpdateType();
const reference = node.updateReference( this );
const reference = node.setReference( this );

if ( updateType === NodeUpdateType.FRAME ) {

Expand Down
Loading

0 comments on commit 319e314

Please sign in to comment.