瀏覽代碼

WebGPURenderer: Added BufferAttributeNode (#26016)

* Added BufferAttributeNode and cleanups

* InstanceNode: buffer -> bufferAttribute

* RangeNode: attribute -> bufferAttribute

* cleanup
sunag 2 年之前
父節點
當前提交
9c1c302c45

+ 1 - 0
examples/jsm/nodes/Nodes.js

@@ -63,6 +63,7 @@ export * from './shadernode/ShaderNode.js';
 
 // accessors
 export { default as BitangentNode, bitangentGeometry, bitangentLocal, bitangentView, bitangentWorld, transformedBitangentView, transformedBitangentWorld } from './accessors/BitangentNode.js';
+export { default as BufferAttributeNode, bufferAttribute } from './accessors/BufferAttributeNode.js';
 export { default as BufferNode, buffer } from './accessors/BufferNode.js';
 export { default as CameraNode, cameraProjectionMatrix, cameraViewMatrix, cameraNormalMatrix, cameraWorldMatrix, cameraPosition } from './accessors/CameraNode.js';
 export { default as CubeTextureNode, cubeTexture } from './accessors/CubeTextureNode.js';

+ 74 - 0
examples/jsm/nodes/accessors/BufferAttributeNode.js

@@ -0,0 +1,74 @@
+import InputNode from '../core/InputNode.js';
+import { addNodeClass } from '../core/Node.js';
+import { varying } from '../core/VaryingNode.js';
+import { nodeObject } from '../shadernode/ShaderNode.js';
+import { InterleavedBufferAttribute, InterleavedBuffer } from 'three';
+
+class BufferAttributeNode extends InputNode {
+
+	constructor( value, bufferType, bufferStride = 0, bufferOffset = 0 ) {
+
+		super( value, bufferType );
+
+		this.isBufferNode = true;
+
+		this.bufferType = bufferType;
+		this.bufferStride = bufferStride;
+		this.bufferOffset = bufferOffset;
+
+	}
+
+	construct( builder ) {
+
+		const type = this.getNodeType( builder );
+		const array = this.value;
+		const itemSize = builder.getTypeLength( type );
+		const stride = this.bufferStride || itemSize;
+		const offset = this.bufferOffset;
+
+		const buffer = new InterleavedBuffer( array, stride );
+		const bufferAttribute = new InterleavedBufferAttribute( buffer, itemSize, offset );
+
+		this.attribute = bufferAttribute;
+		this.attribute.isInstancedBufferAttribute = true; // @TODO: Add a possible: InstancedInterleavedBufferAttribute
+
+	}
+
+	generate( builder ) {
+
+		const nodeType = this.getNodeType( builder );
+
+		const nodeUniform = builder.getBufferAttributeFromNode( this, nodeType );
+		const propertyName = builder.getPropertyName( nodeUniform );
+
+		let output = null;
+
+		if ( builder.isShaderStage( 'vertex' ) ) {
+
+			output = propertyName;
+
+		} else {
+
+			const nodeVarying = varying( this );
+
+			output = nodeVarying.build( builder, nodeType );
+
+		}
+
+		return output;
+
+	}
+
+	getInputType( /*builder*/ ) {
+
+		return 'bufferAttribute';
+
+	}
+
+}
+
+export default BufferAttributeNode;
+
+export const bufferAttribute = ( array, type, stride, offset ) => nodeObject( new BufferAttributeNode( array, type, stride, offset ) );
+
+addNodeClass( BufferAttributeNode );

+ 15 - 9
examples/jsm/nodes/accessors/InstanceNode.js

@@ -1,10 +1,8 @@
 import Node, { addNodeClass } from '../core/Node.js';
-import { instanceIndex } from '../core/InstanceIndexNode.js';
-import { temp } from '../core/VarNode.js';
-import { buffer } from './BufferNode.js';
+import { bufferAttribute } from './BufferAttributeNode.js';
 import { normalLocal } from './NormalNode.js';
 import { positionLocal } from './PositionNode.js';
-import { nodeProxy, vec3, mat3 } from '../shadernode/ShaderNode.js';
+import { nodeProxy, vec3, mat3, mat4 } from '../shadernode/ShaderNode.js';
 
 class InstanceNode extends Node {
 
@@ -16,13 +14,19 @@ class InstanceNode extends Node {
 
 		//
 
-		const instanceBufferNode = buffer( instanceMesh.instanceMatrix.array, 'mat4', instanceMesh.count );
+		const instanceBuffers = [
+			// F.Signature -> bufferAttribute( array, type, stride, offset )
+			bufferAttribute( instanceMesh.instanceMatrix.array, 'vec4', 16, 0 ),
+			bufferAttribute( instanceMesh.instanceMatrix.array, 'vec4', 16, 4 ),
+			bufferAttribute( instanceMesh.instanceMatrix.array, 'vec4', 16, 8 ),
+			bufferAttribute( instanceMesh.instanceMatrix.array, 'vec4', 16, 12 )
+		];
 
-		this.instanceMatrixNode = temp( instanceBufferNode.element( instanceIndex ) ); // @TODO: a possible caching issue here?
+		this.instanceMatrixNode = mat4( ...instanceBuffers );
 
 	}
 
-	generate( builder ) {
+	construct( builder ) {
 
 		const { instanceMatrixNode } = this;
 
@@ -40,8 +44,10 @@ class InstanceNode extends Node {
 
 		// ASSIGNS
 
-		positionLocal.assign( instancePosition ).build( builder );
-		normalLocal.assign( instanceNormal ).build( builder );
+		builder.stack.assign( positionLocal, instancePosition );
+		builder.stack.assign( normalLocal, instanceNormal );
+
+		return builder.stack;
 
 	}
 

+ 2 - 1
examples/jsm/nodes/core/NodeAttribute.js

@@ -1,11 +1,12 @@
 class NodeAttribute {
 
-	constructor( name, type ) {
+	constructor( name, type, node = null ) {
 
 		this.isNodeAttribute = true;
 
 		this.name = name;
 		this.type = type;
+		this.node = node;
 
 	}
 

+ 30 - 1
examples/jsm/nodes/core/NodeBuilder.js

@@ -69,6 +69,7 @@ class NodeBuilder {
 		this.uniforms = { vertex: [], fragment: [], compute: [], index: 0 };
 		this.codes = { vertex: [], fragment: [], compute: [] };
 		this.attributes = [];
+		this.bufferAttributes = [];
 		this.varyings = [];
 		this.vars = { vertex: [], fragment: [], compute: [] };
 		this.flow = { code: '' };
@@ -541,7 +542,29 @@ class NodeBuilder {
 
 	}
 
-	getUniformFromNode( node, shaderStage, type ) {
+	getBufferAttributeFromNode( node, type ) {
+
+		const nodeData = this.getDataFromNode( node );
+
+		let bufferAttribute = nodeData.bufferAttribute;
+
+		if ( bufferAttribute === undefined ) {
+
+			const index = this.uniforms.index ++;
+
+			bufferAttribute = new NodeAttribute( 'nodeAttribute' + index, type, node );
+
+			this.bufferAttributes.push( bufferAttribute );
+
+			nodeData.bufferAttribute = bufferAttribute;
+
+		}
+
+		return bufferAttribute;
+
+	}
+
+	getUniformFromNode( node, type, shaderStage = this.shaderStage ) {
 
 		const nodeData = this.getDataFromNode( node, shaderStage );
 
@@ -732,6 +755,12 @@ class NodeBuilder {
 
 	}
 
+	getAttributesArray() {
+
+		return this.attributes.concat( this.bufferAttributes );
+
+	}
+
 	getAttributes( /*shaderStage*/ ) {
 
 		console.warn( 'Abstract function.' );

+ 1 - 1
examples/jsm/nodes/core/UniformNode.js

@@ -36,7 +36,7 @@ class UniformNode extends InputNode {
 
 		const sharedNodeType = sharedNode.getInputType( builder );
 
-		const nodeUniform = builder.getUniformFromNode( sharedNode, builder.shaderStage, sharedNodeType );
+		const nodeUniform = builder.getUniformFromNode( sharedNode, sharedNodeType, builder.shaderStage );
 		const propertyName = builder.getPropertyName( nodeUniform );
 
 		return builder.format( propertyName, type, output );

+ 19 - 32
examples/jsm/nodes/geometry/RangeNode.js

@@ -1,9 +1,9 @@
 import Node, { addNodeClass } from '../core/Node.js';
 import { getValueType } from '../core/NodeUtils.js';
-import { attribute } from '../core/AttributeNode.js';
+import { bufferAttribute } from '../accessors/BufferAttributeNode.js';
 import { nodeProxy, float } from '../shadernode/ShaderNode.js';
 
-import { MathUtils, InstancedBufferAttribute } from 'three';
+import { MathUtils } from 'three';
 
 class RangeNode extends Node {
 
@@ -39,8 +39,6 @@ class RangeNode extends Node {
 
 		if ( object.isInstancedMesh === true ) {
 
-			const geometry = builder.geometry;
-
 			let min = this.minNode.value;
 			let max = this.maxNode.value;
 
@@ -53,55 +51,44 @@ class RangeNode extends Node {
 			else if ( maxLength > minLength && minLength === 1 ) min = new max.constructor().setScalar( min );
 
 			const vectorLength = this.getVectorLength( builder );
-			const attributeName = 'node' + this.id;
 
 			const length = vectorLength * object.count;
 			const array = new Float32Array( length );
 
-			const attributeGeometry = geometry.getAttribute( attributeName );
-
-			if ( attributeGeometry === undefined || attributeGeometry.array.length < length ) {
-
-				if ( vectorLength === 1 ) {
-
-					for ( let i = 0; i < length; i ++ ) {
+			if ( vectorLength === 1 ) {
 
-						array[ i ] = MathUtils.lerp( min, max, Math.random() );
+				for ( let i = 0; i < length; i ++ ) {
 
-					}
+					array[ i ] = MathUtils.lerp( min, max, Math.random() );
 
-				} else if ( min.isColor ) {
+				}
 
-					for ( let i = 0; i < length; i += 3 ) {
+			} else if ( min.isColor ) {
 
-						array[ i ] = MathUtils.lerp( min.r, max.r, Math.random() );
-						array[ i + 1 ] = MathUtils.lerp( min.g, max.g, Math.random() );
-						array[ i + 2 ] = MathUtils.lerp( min.b, max.b, Math.random() );
+				for ( let i = 0; i < length; i += 3 ) {
 
-					}
+					array[ i ] = MathUtils.lerp( min.r, max.r, Math.random() );
+					array[ i + 1 ] = MathUtils.lerp( min.g, max.g, Math.random() );
+					array[ i + 2 ] = MathUtils.lerp( min.b, max.b, Math.random() );
 
-				} else {
+				}
 
-					for ( let i = 0; i < length; i ++ ) {
+			} else {
 
-						const index = i % vectorLength;
+				for ( let i = 0; i < length; i ++ ) {
 
-						const minValue = min.getComponent( index );
-						const maxValue = max.getComponent( index );
+					const index = i % vectorLength;
 
-						array[ i ] = MathUtils.lerp( minValue, maxValue, Math.random() );
+					const minValue = min.getComponent( index );
+					const maxValue = max.getComponent( index );
 
-					}
+					array[ i ] = MathUtils.lerp( minValue, maxValue, Math.random() );
 
 				}
 
-				geometry.setAttribute( attributeName, new InstancedBufferAttribute( array, vectorLength ) );
-
-				geometry.dispose();
-
 			}
 
-			output = attribute( attributeName, builder.getTypeFromLength( vectorLength ) );
+			output = bufferAttribute( array, builder.getTypeFromLength( vectorLength ) );
 
 		} else {
 

+ 6 - 7
examples/jsm/renderers/webgpu/WebGPUGeometries.js

@@ -105,7 +105,7 @@ class WebGPUGeometries {
 			this.info.memory.geometries --;
 
 			const index = geometry.index;
-			const geometryAttributes = geometry.attributes;
+			const geometryAttributes = renderObject.getAttributes();
 
 			if ( index !== null ) {
 
@@ -113,9 +113,9 @@ class WebGPUGeometries {
 
 			}
 
-			for ( const name in geometryAttributes ) {
+			for ( const geometryAttribute of geometryAttributes ) {
 
-				this.attributes.remove( geometryAttributes[ name ] );
+				this.attributes.remove( geometryAttribute );
 
 			}
 
@@ -154,12 +154,11 @@ class WebGPUGeometries {
 
 	updateAttributes( renderObject ) {
 
-		const geometry = renderObject.geometry;
-		const geometryAttributes = geometry.attributes;
+		const attributes = renderObject.getAttributes();
 
-		for ( const name in geometryAttributes ) {
+		for ( const attribute of attributes ) {
 
-			this.attributes.update( geometryAttributes[ name ] );
+			this.attributes.update( attribute );
 
 		}
 

+ 24 - 0
examples/jsm/renderers/webgpu/WebGPURenderObject.js

@@ -4,6 +4,7 @@ export default class WebGPURenderObject {
 
 		this.renderer = renderer;
 		this.nodes = nodes;
+
 		this.object = object;
 		this.material = material;
 		this.scene = scene;
@@ -12,11 +13,34 @@ export default class WebGPURenderObject {
 
 		this.geometry = object.geometry;
 
+		this.attributes = null;
+
 		this._materialVersion = - 1;
 		this._materialCacheKey = '';
 
 	}
 
+	getAttributes() {
+
+		if ( this.attributes !== null ) return this.attributes;
+
+		const nodeAttributes = this.nodes.get( this ).getAttributesArray();
+		const geometry = this.geometry;
+
+		const attributes = [];
+
+		for ( const nodeAttribute of nodeAttributes ) {
+
+			attributes.push( nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name ) );
+
+		}
+
+		this.attributes = attributes;
+
+		return attributes;
+
+	}
+
 	getCacheKey() {
 
 		const { material, scene, lightsNode } = this;

+ 11 - 15
examples/jsm/renderers/webgpu/WebGPURenderPipeline.js

@@ -45,13 +45,13 @@ class WebGPURenderPipeline {
 
 	}
 
-	init( cacheKey, stageVertex, stageFragment, renderObject, nodeBuilder ) {
+	init( renderObject, cacheKey, stageVertex, stageFragment ) {
 
 		const { object, material, geometry } = renderObject;
 
 		// determine shader attributes
 
-		const shaderAttributes = this._getShaderAttributes( nodeBuilder, geometry );
+		const shaderAttributes = this._getShaderAttributes( renderObject );
 
 		// vertex buffers
 
@@ -59,8 +59,7 @@ class WebGPURenderPipeline {
 
 		for ( const attribute of shaderAttributes ) {
 
-			const name = attribute.name;
-			const geometryAttribute = geometry.getAttribute( name );
+			const geometryAttribute = attribute.geometryAttribute;
 			const stepMode = ( geometryAttribute !== undefined && geometryAttribute.isInstancedBufferAttribute ) ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
 
 			vertexBuffers.push( {
@@ -627,17 +626,14 @@ class WebGPURenderPipeline {
 
 	}
 
-	_getShaderAttributes( nodeBuilder, geometry ) {
+	_getShaderAttributes( renderObject ) {
 
-		const nodeAttributes = nodeBuilder.attributes;
-		const attributes = [];
+		const attributes = renderObject.getAttributes();
+		const shaderAttributes = [];
 
-		for ( let slot = 0; slot < nodeAttributes.length; slot ++ ) {
+		for ( let slot = 0; slot < attributes.length; slot ++ ) {
 
-			const nodeAttribute = nodeAttributes[ slot ];
-			const name = nodeAttribute.name;
-
-			const geometryAttribute = geometry.getAttribute( name );
+			const geometryAttribute = attributes[ slot ];
 			const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
 
 			const format = this._getVertexFormat( geometryAttribute );
@@ -654,8 +650,8 @@ class WebGPURenderPipeline {
 
 			}
 
-			attributes.push( {
-				name,
+			shaderAttributes.push( {
+				geometryAttribute,
 				arrayStride,
 				offset,
 				format,
@@ -664,7 +660,7 @@ class WebGPURenderPipeline {
 
 		}
 
-		return attributes;
+		return shaderAttributes;
 
 	}
 

+ 5 - 5
examples/jsm/renderers/webgpu/WebGPURenderPipelines.js

@@ -60,7 +60,7 @@ class WebGPURenderPipelines {
 
 			// determine render pipeline
 
-			currentPipeline = this._acquirePipeline( stageVertex, stageFragment, renderObject );
+			currentPipeline = this._acquirePipeline( renderObject, stageVertex, stageFragment );
 			cache.currentPipeline = currentPipeline;
 
 			// keep track of all used times
@@ -92,14 +92,14 @@ class WebGPURenderPipelines {
 
 	}
 
-	_acquirePipeline( stageVertex, stageFragment, renderObject ) {
+	_acquirePipeline( renderObject, stageVertex, stageFragment ) {
 
 		let pipeline;
 		const pipelines = this.pipelines;
 
 		// check for existing pipeline
 
-		const cacheKey = this._computeCacheKey( stageVertex, stageFragment, renderObject );
+		const cacheKey = this._computeCacheKey( renderObject, stageVertex, stageFragment );
 
 		for ( let i = 0, il = pipelines.length; i < il; i ++ ) {
 
@@ -117,7 +117,7 @@ class WebGPURenderPipelines {
 		if ( pipeline === undefined ) {
 
 			pipeline = new WebGPURenderPipeline( this.device, this.utils );
-			pipeline.init( cacheKey, stageVertex, stageFragment, renderObject, this.nodes.get( renderObject ) );
+			pipeline.init( renderObject, cacheKey, stageVertex, stageFragment );
 
 			pipelines.push( pipeline );
 
@@ -127,7 +127,7 @@ class WebGPURenderPipelines {
 
 	}
 
-	_computeCacheKey( stageVertex, stageFragment, renderObject ) {
+	_computeCacheKey( renderObject, stageVertex, stageFragment ) {
 
 		const { object, material } = renderObject;
 		const utils = this.utils;

+ 13 - 18
examples/jsm/renderers/webgpu/WebGPURenderer.js

@@ -985,13 +985,13 @@ class WebGPURenderer {
 
 		if ( hasIndex === true ) {
 
-			this._setupIndexBuffer( index, passEncoder );
+			this._setupIndexBuffer( renderObject );
 
 		}
 
 		// vertex buffers
 
-		this._setupVertexBuffers( geometry.attributes, passEncoder, renderPipeline );
+		this._setupVertexBuffers( renderObject );
 
 		// draw
 
@@ -1061,32 +1061,27 @@ class WebGPURenderer {
 
 	}
 
-	_setupIndexBuffer( index, encoder ) {
+	_setupIndexBuffer( renderObject ) {
+
+		const index = this._geometries.getIndex( renderObject );
+		const passEncoder = this._currentRenderState.currentPassGPU;
 
 		const buffer = this._attributes.get( index ).buffer;
 		const indexFormat = ( index.array instanceof Uint16Array ) ? GPUIndexFormat.Uint16 : GPUIndexFormat.Uint32;
 
-		encoder.setIndexBuffer( buffer, indexFormat );
+		passEncoder.setIndexBuffer( buffer, indexFormat );
 
 	}
 
-	_setupVertexBuffers( geometryAttributes, encoder, renderPipeline ) {
-
-		const shaderAttributes = renderPipeline.shaderAttributes;
-
-		for ( const shaderAttribute of shaderAttributes ) {
+	_setupVertexBuffers( renderObject ) {
 
-			const name = shaderAttribute.name;
-			const slot = shaderAttribute.slot;
-
-			const attribute = geometryAttributes[ name ];
-
-			if ( attribute !== undefined ) {
+		const passEncoder = this._currentRenderState.currentPassGPU;
+		const attributes = renderObject.getAttributes();
 
-				const buffer = this._attributes.get( attribute ).buffer;
-				encoder.setVertexBuffer( slot, buffer );
+		for ( let i = 0, l = attributes.length; i < l; i ++ ) {
 
-			}
+			const buffer = this._attributes.get( attributes[ i ] ).buffer;
+			passEncoder.setVertexBuffer( i, buffer );
 
 		}
 

+ 4 - 5
examples/jsm/renderers/webgpu/nodes/WebGPUNodeBuilder.js

@@ -258,9 +258,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 	}
 
-	getUniformFromNode( node, shaderStage, type ) {
+	getUniformFromNode( node, type, shaderStage ) {
 
-		const uniformNode = super.getUniformFromNode( node, shaderStage, type );
+		const uniformNode = super.getUniformFromNode( node, type, shaderStage );
 		const nodeData = this.getDataFromNode( node, shaderStage );
 
 		if ( nodeData.uniformGPU === undefined ) {
@@ -446,10 +446,9 @@ class WebGPUNodeBuilder extends NodeBuilder {
 
 			}
 
-			const attributes = this.attributes;
-			const length = attributes.length;
+			const attributes = this.getAttributesArray();
 
-			for ( let index = 0; index < length; index ++ ) {
+			for ( let index = 0, length = attributes.length; index < length; index ++ ) {
 
 				const attribute = attributes[ index ];
 				const name = attribute.name;

+ 8 - 0
examples/webgpu_cubemap_adjustments.html

@@ -134,6 +134,14 @@
 
 					scene.add( gltf.scene );
 
+
+					setTimeout( () => {
+
+						gltf.scene.children[0].material.dispose();
+						gltf.scene.children[0].geometry.dispose();
+
+					}, 2000 )
+
 				} );
 
 				const sphereGeometry = new THREE.SphereGeometry( .5, 64, 32 );