diff --git a/src/materials/nodes/manager/NodeMaterialObserver.js b/src/materials/nodes/manager/NodeMaterialObserver.js index 4441a083264e22..8c21812e43b667 100644 --- a/src/materials/nodes/manager/NodeMaterialObserver.js +++ b/src/materials/nodes/manager/NodeMaterialObserver.js @@ -65,6 +65,14 @@ const refreshUniforms = [ */ const _lightsCache = new WeakMap(); +/** + * Holds the material data for comparison. + * + * @private + * @type {WeakMap} + */ +const _materialCache = new WeakMap(); + /** * This class is used by {@link WebGPURenderer} as management component. * It's primary purpose is to determine whether render objects require a @@ -166,10 +174,9 @@ class NodeMaterialObserver { if ( data === undefined ) { - const { geometry, material, object } = renderObject; + const { geometry, object } = renderObject; data = { - material: this.getMaterialData( material ), geometry: { id: geometry.id, attributes: this.getAttributesData( geometry.attributes ), @@ -198,7 +205,7 @@ class NodeMaterialObserver { } - if ( data.material.transmission > 0 ) { + if ( renderObject.material.transmission > 0 ) { const { width, height } = renderObject.context; @@ -277,32 +284,40 @@ class NodeMaterialObserver { */ getMaterialData( material ) { - const data = {}; + let data = _materialCache.get( material ); - for ( const property of this.refreshUniforms ) { + if ( data === undefined ) { - const value = material[ property ]; + data = { _renderId: - 1, _equal: false }; - if ( value === null || value === undefined ) continue; + for ( const property of this.refreshUniforms ) { - if ( typeof value === 'object' && value.clone !== undefined ) { + const value = material[ property ]; - if ( value.isTexture === true ) { + if ( value === null || value === undefined ) continue; - data[ property ] = { id: value.id, version: value.version }; + if ( typeof value === 'object' && value.clone !== undefined ) { - } else { + if ( value.isTexture === true ) { - data[ property ] = value.clone(); + data[ property ] = { id: value.id, version: value.version }; - } + } else { - } else { + data[ property ] = value.clone(); - data[ property ] = value; + } + + } else { + + data[ property ] = value; + + } } + _materialCache.set( material, data ); + } return data; @@ -314,9 +329,10 @@ class NodeMaterialObserver { * * @param {RenderObject} renderObject - The render object. * @param {Array} lightsData - The current material lights. + * @param {number} renderId - The current render ID. * @return {boolean} Whether the given render object has changed its state or not. */ - equals( renderObject, lightsData ) { + equals( renderObject, lightsData, renderId ) { const { object, material, geometry } = renderObject; @@ -334,57 +350,78 @@ class NodeMaterialObserver { // material - const materialData = renderObjectData.material; + const materialData = this.getMaterialData( renderObject.material ); - for ( const property in materialData ) { + // check the material for the "equal" state just once per render for all render objects - const value = materialData[ property ]; - const mtlValue = material[ property ]; + if ( materialData._renderId !== renderId ) { - if ( value.equals !== undefined ) { + materialData._renderId = renderId; - if ( value.equals( mtlValue ) === false ) { + for ( const property in materialData ) { - value.copy( mtlValue ); + const value = materialData[ property ]; + const mtlValue = material[ property ]; - return false; + if ( property === '_renderId' ) continue; + if ( property === '_equal' ) continue; - } + if ( value.equals !== undefined ) { - } else if ( mtlValue.isTexture === true ) { + if ( value.equals( mtlValue ) === false ) { - if ( value.id !== mtlValue.id || value.version !== mtlValue.version ) { + value.copy( mtlValue ); - value.id = mtlValue.id; - value.version = mtlValue.version; + materialData._equal = false; + return false; - return false; + } - } + } else if ( mtlValue.isTexture === true ) { - } else if ( value !== mtlValue ) { + if ( value.id !== mtlValue.id || value.version !== mtlValue.version ) { - materialData[ property ] = mtlValue; + value.id = mtlValue.id; + value.version = mtlValue.version; - return false; + materialData._equal = false; + return false; + + } + + } else if ( value !== mtlValue ) { + + materialData[ property ] = mtlValue; + + materialData._equal = false; + return false; + + } } - } + if ( materialData.transmission > 0 ) { - if ( materialData.transmission > 0 ) { + const { width, height } = renderObject.context; - const { width, height } = renderObject.context; + if ( renderObjectData.bufferWidth !== width || renderObjectData.bufferHeight !== height ) { - if ( renderObjectData.bufferWidth !== width || renderObjectData.bufferHeight !== height ) { + renderObjectData.bufferWidth = width; + renderObjectData.bufferHeight = height; - renderObjectData.bufferWidth = width; - renderObjectData.bufferHeight = height; + materialData._equal = false; + return false; - return false; + } } + materialData._equal = true; + + } else { + + if ( materialData._equal === false ) return false; + } // geometry @@ -612,7 +649,7 @@ class NodeMaterialObserver { return false; const lightsData = this.getLights( renderObject.lightsNode, renderId ); - const notEqual = this.equals( renderObject, lightsData ) !== true; + const notEqual = this.equals( renderObject, lightsData, renderId ) !== true; return notEqual;