From e7f41f980f3daab7a6fe558e0a6a7d8a411ca8c7 Mon Sep 17 00:00:00 2001 From: Michael Herzog Date: Tue, 5 May 2026 18:03:01 +0200 Subject: [PATCH 1/2] TSL: Make sure structs are built when compiling functions. (#33524) Co-authored-by: sunag --- src/nodes/core/NodeBuilder.js | 30 +++++++++++++++--- src/nodes/tsl/TSLCore.js | 58 +++++++++++++++++++++++++---------- 2 files changed, 67 insertions(+), 21 deletions(-) diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index 908c0e8685ceda..14e8e79bdd4b31 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -35,6 +35,7 @@ import { warn, error, yieldToMain } from '../../utils.js'; let _id = 0; const _bindingGroupsCache = new WeakMap(); +const _functionNodeCache = new WeakMap(); const sharedNodeData = new WeakMap(); @@ -2378,15 +2379,34 @@ class NodeBuilder { */ buildFunctionNode( shaderNode ) { - const fn = new FunctionNode(); + const backend = this.renderer.backend; - const previous = this.currentFunctionNode; + let cache = _functionNodeCache.get( backend ); - this.currentFunctionNode = fn; + if ( cache === undefined ) { - fn.code = this.buildFunctionCode( shaderNode ); + cache = new WeakMap(); + _functionNodeCache.set( backend, cache ); - this.currentFunctionNode = previous; + } + + let fn = cache.get( shaderNode ); + + if ( fn === undefined ) { + + fn = new FunctionNode(); + + const previous = this.currentFunctionNode; + + this.currentFunctionNode = fn; + + fn.code = this.buildFunctionCode( shaderNode ); + + this.currentFunctionNode = previous; + + cache.set( shaderNode, fn ); + + } return fn; diff --git a/src/nodes/tsl/TSLCore.js b/src/nodes/tsl/TSLCore.js index 08d62ec0d56614..1146029dd77939 100644 --- a/src/nodes/tsl/TSLCore.js +++ b/src/nodes/tsl/TSLCore.js @@ -291,8 +291,6 @@ Object.defineProperties( Node.prototype, proto ); // --- FINISH --- -const nodeBuilderFunctionsCacheMap = new WeakMap(); - const ShaderNodeObject = function ( obj, altType = null ) { const type = getValueType( obj ); @@ -506,35 +504,59 @@ class ShaderCallNodeInternal extends Node { if ( shaderNode.layout ) { - const backend = builder.renderer.backend; + // build inputs first - let functionNodesCacheMap = nodeBuilderFunctionsCacheMap.get( backend ); + if ( rawInputs ) { - if ( functionNodesCacheMap === undefined ) { + // use layout inputs to ensure that no extra parameters are built - functionNodesCacheMap = new WeakMap(); + const inputs = shaderNode.layout.inputs; - nodeBuilderFunctionsCacheMap.set( backend, functionNodesCacheMap ); + if ( isArrayAsParameter( rawInputs ) ) { - } + const rawArrayParameters = rawInputs; + + for ( let i = 0; i < inputs.length; i ++ ) { + + const rawParameter = rawArrayParameters[ i ]; + + if ( rawParameter && rawParameter.isNode ) { + + rawParameter.build( builder ); + + } + + } + + } else { + + const rawObjectParameters = rawInputs[ 0 ]; - let functionNode = functionNodesCacheMap.get( shaderNode ); + for ( const param of inputs ) { - if ( functionNode === undefined ) { + const rawParameter = rawObjectParameters[ param.name ]; - functionNode = nodeObject( builder.buildFunctionNode( shaderNode ) ); + if ( rawParameter && rawParameter.isNode ) { - functionNodesCacheMap.set( shaderNode, functionNode ); + rawParameter.build( builder ); + + } + + } + + } } + const functionNode = builder.buildFunctionNode( shaderNode ); + builder.addInclude( functionNode ); // const inputs = rawInputs ? getLayoutParameters( rawInputs ) : null; - result = nodeObject( functionNode.call( inputs ) ); + result = functionNode.call( inputs ); } else { @@ -681,15 +703,19 @@ class ShaderCallNodeInternal extends Node { } +function isArrayAsParameter( params ) { + + return params[ 0 ] && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); + +} + function getLayoutParameters( params ) { let output; nodeObjects( params ); - const isArrayAsParameter = params[ 0 ] && ( params[ 0 ].isNode || Object.getPrototypeOf( params[ 0 ] ) !== Object.prototype ); - - if ( isArrayAsParameter ) { + if ( isArrayAsParameter( params ) ) { output = [ ...params ]; From d838a05c7fec929575b5865a06da0c37d2ce236e Mon Sep 17 00:00:00 2001 From: thelazylama <67310144+thelazylamaGit@users.noreply.github.com> Date: Wed, 6 May 2026 02:33:03 +1000 Subject: [PATCH 2/2] TSL: Fix compute `.needsUpdate` (#33530) --- src/renderers/common/Bindings.js | 14 +++++++++++--- src/renderers/common/nodes/NodeManager.js | 3 ++- 2 files changed, 13 insertions(+), 4 deletions(-) diff --git a/src/renderers/common/Bindings.js b/src/renderers/common/Bindings.js index e0902417499a3b..f296ef62b7ae52 100644 --- a/src/renderers/common/Bindings.js +++ b/src/renderers/common/Bindings.js @@ -106,13 +106,20 @@ class Bindings extends DataMap { const bindings = this.nodes.getForCompute( computeNode ).bindings; const computeNodeData = this.get( computeNode ); - if ( computeNodeData.initialized !== true ) { + if ( computeNodeData.initialized !== true || computeNodeData.bindings !== bindings ) { - // bind groups are created once per object + // bind groups are created once per compute node version + + if ( computeNodeData.bindings !== undefined ) { + + this._destroyBindings( computeNodeData.bindings ); + + } this._createBindings( bindings ); computeNodeData.initialized = true; + computeNodeData.bindings = bindings; } @@ -149,7 +156,8 @@ class Bindings extends DataMap { */ deleteForCompute( computeNode ) { - const bindings = this.nodes.getForCompute( computeNode ).bindings; + const computeNodeData = this.get( computeNode ); + const bindings = computeNodeData.bindings || this.nodes.getForCompute( computeNode ).bindings; this._destroyBindings( bindings ); diff --git a/src/renderers/common/nodes/NodeManager.js b/src/renderers/common/nodes/NodeManager.js index 7d40e968c80bf9..6a8864bae8bbc3 100644 --- a/src/renderers/common/nodes/NodeManager.js +++ b/src/renderers/common/nodes/NodeManager.js @@ -448,7 +448,7 @@ class NodeManager extends DataMap { let nodeBuilderState = computeData.nodeBuilderState; - if ( nodeBuilderState === undefined ) { + if ( nodeBuilderState === undefined || computeData.version !== computeNode.version ) { const nodeBuilder = this.backend.createNodeBuilder( computeNode, this.renderer ); nodeBuilder.build(); @@ -456,6 +456,7 @@ class NodeManager extends DataMap { nodeBuilderState = this._createNodeBuilderState( nodeBuilder ); computeData.nodeBuilderState = nodeBuilderState; + computeData.version = computeNode.version; }