Explorar o código

WebGPURenderer: Remove duplication of interleaved attribute buffers. (#26351)

* add support for meshPhongNodeMaterial

* wip handle interleaved buffers efficiently

* convert mat4 into proper instancedinterleavedBufferAttributes for InstanceMesh

* make var const

* remove debug code

* missed initialisation and address code comments

* address code comments

---------

Co-authored-by: aardgoose <[email protected]>
aardgoose %!s(int64=2) %!d(string=hai) anos
pai
achega
37b932fef4

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

@@ -29,7 +29,7 @@ class BufferAttributeNode extends InputNode {
 		const stride = this.bufferStride || itemSize;
 		const offset = this.bufferOffset;
 
-		const buffer = new InterleavedBuffer( array, stride );
+		const buffer = array.isInterleavedBuffer === true ? array : new InterleavedBuffer( array, stride );
 		const bufferAttribute = new InterleavedBufferAttribute( buffer, itemSize, offset );
 
 		buffer.setUsage( this.usage );

+ 8 - 8
examples/jsm/nodes/accessors/InstanceNode.js

@@ -3,7 +3,7 @@ import { instancedBufferAttribute, instancedDynamicBufferAttribute } from './Buf
 import { normalLocal } from './NormalNode.js';
 import { positionLocal } from './PositionNode.js';
 import { nodeProxy, vec3, mat3, mat4 } from '../shadernode/ShaderNode.js';
-import { DynamicDrawUsage } from 'three';
+import { DynamicDrawUsage, InstancedInterleavedBuffer } from 'three';
 
 class InstanceNode extends Node {
 
@@ -24,17 +24,17 @@ class InstanceNode extends Node {
 		if ( instanceMatrixNode === null ) {
 
 			const instanceMesh = this.instanceMesh;
-			const instaceAttribute = instanceMesh.instanceMatrix;
-			const array = instaceAttribute.array;
+			const instanceAttribute = instanceMesh.instanceMatrix;
+			const buffer = new InstancedInterleavedBuffer( instanceAttribute.array, 16, 1 );
 
-			const bufferFn = instaceAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;
+			const bufferFn = instanceAttribute.usage === DynamicDrawUsage ? instancedDynamicBufferAttribute : instancedBufferAttribute;
 
 			const instanceBuffers = [
 				// F.Signature -> bufferAttribute( array, type, stride, offset )
-				bufferFn( array, 'vec4', 16, 0 ),
-				bufferFn( array, 'vec4', 16, 4 ),
-				bufferFn( array, 'vec4', 16, 8 ),
-				bufferFn( array, 'vec4', 16, 12 )
+				bufferFn( buffer, 'vec4', 16, 0 ),
+				bufferFn( buffer, 'vec4', 16, 4 ),
+				bufferFn( buffer, 'vec4', 16, 8 ),
+				bufferFn( buffer, 'vec4', 16, 12 )
 			];
 
 			instanceMatrixNode = mat4( ...instanceBuffers );

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

@@ -21,6 +21,7 @@ export default class RenderObject {
 		this.attributes = null;
 		this.context = null;
 		this.pipeline = null;
+		this.vertexBuffers = null;
 
 		this._materialVersion = - 1;
 		this._materialCacheKey = '';
@@ -69,19 +70,34 @@ export default class RenderObject {
 		const geometry = this.geometry;
 
 		const attributes = [];
+		const vertexBuffers = new Set();
 
 		for ( const nodeAttribute of nodeAttributes ) {
 
-			attributes.push( nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name ) );
+			const attribute = nodeAttribute.node && nodeAttribute.node.attribute ? nodeAttribute.node.attribute : geometry.getAttribute( nodeAttribute.name );
+
+			attributes.push( attribute );
+
+			const bufferAttribute = attribute.isInterleavedBufferAttribute ? attribute.data : attribute;
+			vertexBuffers.add( bufferAttribute );
 
 		}
 
 		this.attributes = attributes;
+		this.vertexBuffers = Array.from( vertexBuffers.values() );
 
 		return attributes;
 
 	}
 
+	getVertexBuffers() {
+
+		if ( this.vertexBuffers === null ) this.getAttributes();
+
+		return this.vertexBuffers;
+
+	}
+
 	getCacheKey() {
 
 		const { material, scene, lightsNode } = this;

+ 6 - 6
examples/jsm/renderers/webgpu/WebGPUBackend.js

@@ -438,18 +438,18 @@ class WebGPUBackend extends Backend {
 
 		// vertex buffers
 
-		const attributes = renderObject.getAttributes();
+		const vertexBuffers = renderObject.getVertexBuffers();
 
-		for ( let i = 0, l = attributes.length; i < l; i ++ ) {
+		for ( let i = 0, l = vertexBuffers.length; i < l; i ++ ) {
 
-			const attribute = attributes[ i ];
+			const vertexBuffer = vertexBuffers[ i ];
 
-			if ( attributesSet[ i ] !== attribute ) {
+			if ( attributesSet[ i ] !== vertexBuffer ) {
 
-				const buffer = this.get( attribute ).buffer;
+				const buffer = this.get( vertexBuffer ).buffer;
 				passEncoderGPU.setVertexBuffer( i, buffer );
 
-				attributesSet[ i ] = attribute;
+				attributesSet[ i ] = vertexBuffer;
 
 			}
 

+ 53 - 35
examples/jsm/renderers/webgpu/utils/WebGPUAttributeUtils.js

@@ -34,23 +34,30 @@ class WebGPUAttributeUtils {
 		const bufferAttribute = this._getBufferAttribute( attribute );
 
 		const backend = this.backend;
-		const device = backend.device;
+		const bufferData = backend.get( bufferAttribute );
 
-		const array = bufferAttribute.array;
-		const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
+		let buffer = bufferData.buffer;
+
+		if ( buffer === undefined ) {
 
-		const buffer = device.createBuffer( {
-			label: bufferAttribute.name,
-			size: size,
-			usage: usage,
-			mappedAtCreation: true
-		} );
+			const device = backend.device;
 
-		new array.constructor( buffer.getMappedRange() ).set( array );
+			const array = bufferAttribute.array;
+			const size = array.byteLength + ( ( 4 - ( array.byteLength % 4 ) ) % 4 ); // ensure 4 byte alignment, see #20441
+
+			buffer = device.createBuffer( {
+				label: bufferAttribute.name,
+				size: size,
+				usage: usage,
+				mappedAtCreation: true
+			} );
 
-		buffer.unmap();
+			new array.constructor( buffer.getMappedRange() ).set( array );
 
-		backend.get( attribute ).buffer = buffer;
+			buffer.unmap();
+
+			bufferData.buffer = buffer;
+		}
 
 	}
 
@@ -61,7 +68,7 @@ class WebGPUAttributeUtils {
 		const backend = this.backend;
 		const device = backend.device;
 
-		const buffer = backend.get( attribute ).buffer;
+		const buffer = backend.get( bufferAttribute ).buffer;
 
 		const array = bufferAttribute.array;
 		const updateRange = bufferAttribute.updateRange;
@@ -93,51 +100,64 @@ class WebGPUAttributeUtils {
 
 	}
 
-	createShaderAttributes( renderObject ) {
+	createShaderVertexBuffers( renderObject ) {
 
 		const attributes = renderObject.getAttributes();
-		const shaderAttributes = [];
+		const vertexBuffers = new Map();
 
 		for ( let slot = 0; slot < attributes.length; slot ++ ) {
 
 			const geometryAttribute = attributes[ slot ];
 			const bytesPerElement = geometryAttribute.array.BYTES_PER_ELEMENT;
+			const bufferAttribute = this._getBufferAttribute( geometryAttribute );
 
-			const format = this._getVertexFormat( geometryAttribute );
+			let vertexBufferLayout = vertexBuffers.get( bufferAttribute );
+
+			if ( vertexBufferLayout === undefined ) {
 
-			let arrayStride = geometryAttribute.itemSize * bytesPerElement;
-			let offset = 0;
-			let stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
+				let arrayStride, stepMode;
 
-			if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
+				if ( geometryAttribute.isInterleavedBufferAttribute === true ) {
 
-				// @TODO: It can be optimized for "vertexBuffers" on RenderPipeline
+					arrayStride = geometryAttribute.data.stride * bytesPerElement;
+					stepMode = geometryAttribute.data.isInstancedInterleavedBuffer ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
 
-				arrayStride = geometryAttribute.data.stride * bytesPerElement;
-				offset = geometryAttribute.offset * bytesPerElement;
-				if ( geometryAttribute.data.isInstancedInterleavedBuffer ) stepMode = GPUInputStepMode.Instance;
+				} else {
+
+					arrayStride = geometryAttribute.itemSize * bytesPerElement;
+					stepMode = geometryAttribute.isInstancedBufferAttribute ? GPUInputStepMode.Instance : GPUInputStepMode.Vertex;
+
+				}
+
+				vertexBufferLayout = {
+					arrayStride,
+					attributes: [],
+					stepMode
+				};
+
+				vertexBuffers.set( bufferAttribute, vertexBufferLayout );
 
 			}
 
-			shaderAttributes.push( {
-				geometryAttribute,
-				arrayStride,
-				stepMode,
+			const format = this._getVertexFormat( geometryAttribute );
+			const offset = ( geometryAttribute.isInterleavedBufferAttribute === true ) ? geometryAttribute.offset * bytesPerElement : 0;
+
+			vertexBufferLayout.attributes.push( {
+				shaderLocation: slot,
 				offset,
-				format,
-				slot
+				format
 			} );
 
 		}
 
-		return shaderAttributes;
+		return Array.from( vertexBuffers.values() );
 
 	}
 
 	destroyAttribute( attribute ) {
 
 		const backend = this.backend;
-		const data = backend.get( attribute );
+		const data = backend.get( this._getBufferAttribute( attribute ) );
 
 		data.buffer.destroy();
 
@@ -150,9 +170,7 @@ class WebGPUAttributeUtils {
 		const backend = this.backend;
 		const device = backend.device;
 
-		const data = backend.get( attribute );
-
-		//const bufferAttribute = this._getBufferAttribute( attribute );
+		const data = backend.get( this._getBufferAttribute( attribute ) );
 
 		const bufferGPU = data.buffer;
 		const size = bufferGPU.size;

+ 1 - 15
examples/jsm/renderers/webgpu/utils/WebGPUPipelineUtils.js

@@ -34,23 +34,9 @@ class WebGPUPipelineUtils {
 
 		const pipelineData = backend.get( pipeline );
 
-		// determine shader attributes
-
-		const shaderAttributes = backend.attributeUtils.createShaderAttributes( renderObject );
-
 		// vertex buffers
 
-		const vertexBuffers = [];
-
-		for ( const attribute of shaderAttributes ) {
-
-			vertexBuffers.push( {
-				arrayStride: attribute.arrayStride,
-				attributes: [ { shaderLocation: attribute.slot, offset: attribute.offset, format: attribute.format } ],
-				stepMode: attribute.stepMode
-			} );
-
-		}
+		const vertexBuffers = backend.attributeUtils.createShaderVertexBuffers( renderObject );
 
 		// blending