Kaynağa Gözat

WebGPURenderer: Better skinning performance (#27753)

* Better skinning performance

* TSL: Rename morph -> morphReference

* cleanup
sunag 1 yıl önce
ebeveyn
işleme
319e3144cd

+ 2 - 2
examples/jsm/nodes/Nodes.js

@@ -87,7 +87,7 @@ 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';
@@ -95,7 +95,7 @@ export { default as NormalNode, normalGeometry, normalLocal, normalView, normalW
 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';

+ 2 - 2
examples/jsm/nodes/accessors/MaterialNode.js

@@ -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 );
 

+ 2 - 12
examples/jsm/nodes/accessors/MaterialReferenceNode.js

@@ -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;

+ 3 - 3
examples/jsm/nodes/accessors/MorphNode.js

@@ -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';
@@ -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 ) {
 
@@ -240,6 +240,6 @@ class MorphNode extends Node {
 
 export default MorphNode;
 
-export const morph = nodeProxy( MorphNode );
+export const morphReference = nodeProxy( MorphNode );
 
 addNodeClass( 'MorphNode', MorphNode );

+ 86 - 28
examples/jsm/nodes/accessors/ReferenceNode.js

@@ -2,36 +2,63 @@ 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 ) ) );
 
 	}
 
@@ -39,17 +66,21 @@ class ReferenceNode extends Node {
 
 		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 );
 
 		}
 
@@ -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 );

+ 31 - 10
examples/jsm/nodes/accessors/SkinningNode.js

@@ -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;
 
@@ -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;
 
 	}
 
@@ -88,9 +106,11 @@ class SkinningNode extends Node {
 
 	}
 
-	update() {
+	update( frame ) {
+
+		const object = this.useReference ? frame.object : this.skinnedMesh;
 
-		this.skinnedMesh.skeleton.update();
+		object.skeleton.update();
 
 	}
 
@@ -98,6 +118,7 @@ class SkinningNode extends Node {
 
 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 );

+ 1 - 1
examples/jsm/nodes/accessors/TextureNode.js

@@ -55,7 +55,7 @@ class TextureNode extends UniformNode {
 
 	}
 
-	updateReference( /*frame*/ ) {
+	setReference( /*state*/ ) {
 
 		return this.value;
 

+ 3 - 1
examples/jsm/nodes/core/Node.js

@@ -40,7 +40,7 @@ class Node extends EventDispatcher {
 
 	}
 
-	updateReference() {
+	setReference( /*state*/ ) {
 
 		return this;
 
@@ -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 ) {

+ 2 - 2
examples/jsm/nodes/core/NodeFrame.js

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

+ 4 - 4
examples/jsm/nodes/materials/NodeMaterial.js

@@ -7,8 +7,8 @@ import { modelViewProjection } from '../accessors/ModelViewProjectionNode.js';
 import { transformedNormalView } from '../accessors/NormalNode.js';
 import { instance } from '../accessors/InstanceNode.js';
 import { positionLocal, positionView } from '../accessors/PositionNode.js';
-import { skinning } from '../accessors/SkinningNode.js';
-import { morph } from '../accessors/MorphNode.js';
+import { skinningReference } from '../accessors/SkinningNode.js';
+import { morphReference } from '../accessors/MorphNode.js';
 import { texture } from '../accessors/TextureNode.js';
 import { cubeTexture } from '../accessors/CubeTextureNode.js';
 import { lightsNode } from '../lighting/LightsNode.js';
@@ -188,13 +188,13 @@ class NodeMaterial extends ShaderMaterial {
 
 		if ( geometry.morphAttributes.position || geometry.morphAttributes.normal || geometry.morphAttributes.color ) {
 
-			morph( object ).append();
+			morphReference( object ).append();
 
 		}
 
 		if ( object.isSkinnedMesh === true ) {
 
-			skinning( object ).append();
+			skinningReference( object ).append();
 
 		}
 

+ 1 - 1
examples/jsm/renderers/common/RenderObject.js

@@ -176,7 +176,7 @@ export default class RenderObject {
 
 		if ( object.skeleton ) {
 
-			cacheKey += object.skeleton.uuid + ',';
+			cacheKey += object.skeleton.bones.length + ',';
 
 		}
 

+ 23 - 0
examples/jsm/renderers/common/nodes/NodeStorageBuffer.js

@@ -0,0 +1,23 @@
+import StorageBuffer from '../StorageBuffer.js';
+
+let _id = 0;
+
+class NodeStorageBuffer extends StorageBuffer {
+
+	constructor( nodeUniform ) {
+
+		super( 'StorageBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
+
+		this.nodeUniform = nodeUniform;
+
+	}
+
+	get buffer() {
+
+		return this.nodeUniform.value;
+
+	}
+
+}
+
+export default NodeStorageBuffer;

+ 23 - 0
examples/jsm/renderers/common/nodes/NodeUniformBuffer.js

@@ -0,0 +1,23 @@
+import UniformBuffer from '../UniformBuffer.js';
+
+let _id = 0;
+
+class NodeUniformBuffer extends UniformBuffer {
+
+	constructor( nodeUniform ) {
+
+		super( 'UniformBuffer_' + _id ++, nodeUniform ? nodeUniform.value : null );
+
+		this.nodeUniform = nodeUniform;
+
+	}
+
+	get buffer() {
+
+		return this.nodeUniform.value;
+
+	}
+
+}
+
+export default NodeUniformBuffer;

+ 5 - 6
examples/jsm/renderers/webgl/nodes/GLSLNodeBuilder.js

@@ -1,11 +1,10 @@
 import { MathNode, GLSLNodeParser, NodeBuilder, UniformNode, vectorComponents } from '../../../nodes/Nodes.js';
 
-import UniformBuffer from '../../common/UniformBuffer.js';
+import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js';
 import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js';
 
 import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js';
 
-
 import { RedFormat, RGFormat, IntType, DataTexture, RGBAFormat, FloatType } from 'three';
 
 const glslMethods = {
@@ -755,11 +754,11 @@ void main() {
 
 			} else if ( type === 'buffer' ) {
 
-				node.name = `NodeBuffer_${node.id}`;
-
-				const buffer = new UniformBuffer( node.name, node.value );
+				node.name = `NodeBuffer_${ node.id }`;
+				uniformNode.name = `buffer${ node.id }`;
 
-				uniformNode.name = `buffer${node.id}`;
+				const buffer = new NodeUniformBuffer( node );
+				buffer.name = node.name;
 
 				this.bindings[ shaderStage ].push( buffer );
 

+ 4 - 4
examples/jsm/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -5,8 +5,8 @@ import NodeUniformsGroup from '../../common/nodes/NodeUniformsGroup.js';
 import NodeSampler from '../../common/nodes/NodeSampler.js';
 import { NodeSampledTexture, NodeSampledCubeTexture } from '../../common/nodes/NodeSampledTexture.js';
 
-import UniformBuffer from '../../common/UniformBuffer.js';
-import StorageBuffer from '../../common/StorageBuffer.js';
+import NodeUniformBuffer from '../../common/nodes/NodeUniformBuffer.js';
+import NodeStorageBuffer from '../../common/nodes/NodeStorageBuffer.js';
 
 import { NodeBuilder, CodeNode } from '../../../nodes/Nodes.js';
 
@@ -382,8 +382,8 @@ class WGSLNodeBuilder extends NodeBuilder {
 
 			} else if ( type === 'buffer' || type === 'storageBuffer' ) {
 
-				const bufferClass = type === 'storageBuffer' ? StorageBuffer : UniformBuffer;
-				const buffer = new bufferClass( 'NodeBuffer_' + node.id, node.value );
+				const bufferClass = type === 'storageBuffer' ? NodeStorageBuffer : NodeUniformBuffer;
+				const buffer = new bufferClass( node );
 				buffer.setVisibility( gpuShaderStageLib[ shaderStage ] );
 
 				bindings.push( buffer );