From 0faeeaf9436300d0a0c5c4b7b5d669022f334fdf Mon Sep 17 00:00:00 2001 From: sunag Date: Wed, 4 Mar 2026 23:49:19 -0300 Subject: [PATCH 1/3] TSL: Improves compilation performance by 3.0x (#33120) --- examples/webgpu_loader_materialx.html | 2 + src/nodes/Nodes.js | 1 - src/nodes/TSL.js | 2 +- src/nodes/accessors/BufferAttributeNode.js | 12 +- src/nodes/accessors/Object3DNode.js | 2 +- src/nodes/accessors/ReferenceBaseNode.js | 4 +- src/nodes/accessors/ReferenceNode.js | 8 +- src/nodes/accessors/StorageBufferNode.js | 14 ++- src/nodes/accessors/TextureNode.js | 2 +- src/nodes/accessors/UniformArrayNode.js | 4 +- src/nodes/code/FunctionCallNode.js | 2 +- src/nodes/code/FunctionNode.js | 2 +- src/nodes/core/ArrayNode.js | 2 +- src/nodes/core/AssignNode.js | 2 +- src/nodes/core/AttributeNode.js | 2 +- src/nodes/core/BypassNode.js | 2 +- src/nodes/core/ContextNode.js | 2 +- src/nodes/core/InputNode.js | 2 +- src/nodes/core/InspectorNode.js | 2 +- src/nodes/core/IsolateNode.js | 2 +- src/nodes/core/Node.js | 95 ++++++++++++++-- src/nodes/core/OutputStructNode.js | 2 +- src/nodes/core/ParameterNode.js | 2 +- src/nodes/core/StackNode.js | 2 +- src/nodes/core/StructNode.js | 2 +- src/nodes/core/StructTypeNode.js | 2 +- src/nodes/core/SubBuildNode.js | 2 +- src/nodes/core/VarNode.js | 2 +- src/nodes/core/VaryingNode.js | 2 +- src/nodes/display/ScreenNode.js | 2 +- src/nodes/geometry/RangeNode.js | 2 +- src/nodes/gpgpu/AtomicFunctionNode.js | 2 +- src/nodes/gpgpu/ComputeBuiltinNode.js | 2 +- src/nodes/gpgpu/SubgroupFunctionNode.js | 2 +- src/nodes/math/BitcastNode.js | 2 +- src/nodes/math/ConditionalNode.js | 2 +- src/nodes/math/MathNode.js | 2 +- src/nodes/math/OperatorNode.js | 2 +- src/nodes/math/PackFloatNode.js | 2 +- src/nodes/math/UnpackFloatNode.js | 2 +- src/nodes/tsl/TSLBase.js | 2 +- src/nodes/tsl/TSLCore.js | 24 +++- src/nodes/utils/ArrayElementNode.js | 2 +- src/nodes/utils/ConvertNode.js | 2 +- src/nodes/utils/DebugNode.js | 2 +- src/nodes/utils/FlipNode.js | 2 +- src/nodes/utils/FunctionOverloadingNode.js | 2 +- src/nodes/utils/JoinNode.js | 2 +- src/nodes/utils/MemberNode.js | 2 +- src/nodes/utils/Remap.js | 48 ++++++++ src/nodes/utils/RemapNode.js | 125 --------------------- src/nodes/utils/RotateNode.js | 2 +- src/nodes/utils/SetNode.js | 2 +- src/nodes/utils/SplitNode.js | 2 +- 54 files changed, 224 insertions(+), 199 deletions(-) create mode 100644 src/nodes/utils/Remap.js delete mode 100644 src/nodes/utils/RemapNode.js diff --git a/examples/webgpu_loader_materialx.html b/examples/webgpu_loader_materialx.html index 6d349caaf4df20..1f3dbd769accb3 100644 --- a/examples/webgpu_loader_materialx.html +++ b/examples/webgpu_loader_materialx.html @@ -260,6 +260,8 @@ } + await renderer.compileAsync( model, camera, scene ); + } function addGUI() { diff --git a/src/nodes/Nodes.js b/src/nodes/Nodes.js index 22f4af25e0f5ed..71862de56be083 100644 --- a/src/nodes/Nodes.js +++ b/src/nodes/Nodes.js @@ -154,7 +154,6 @@ export { default as LoopNode } from './utils/LoopNode.js'; export { default as MaxMipLevelNode } from './utils/MaxMipLevelNode.js'; export { default as MemberNode } from './utils/MemberNode.js'; export { default as ReflectorNode } from './utils/ReflectorNode.js'; -export { default as RemapNode } from './utils/RemapNode.js'; export { default as RotateNode } from './utils/RotateNode.js'; export { default as RTTNode } from './utils/RTTNode.js'; export { default as SampleNode } from './utils/SampleNode.js'; diff --git a/src/nodes/TSL.js b/src/nodes/TSL.js index 6f47628c02449a..8849c8a449c680 100644 --- a/src/nodes/TSL.js +++ b/src/nodes/TSL.js @@ -35,7 +35,7 @@ export * from './utils/MatcapUV.js'; export * from './utils/MaxMipLevelNode.js'; export * from './utils/Oscillators.js'; export * from './utils/Packing.js'; -export * from './utils/RemapNode.js'; +export * from './utils/Remap.js'; export * from './utils/UVUtils.js'; export * from './utils/SpriteUtils.js'; export * from './utils/ViewportUtils.js'; diff --git a/src/nodes/accessors/BufferAttributeNode.js b/src/nodes/accessors/BufferAttributeNode.js index a09d4144f6cff2..874439fa019a67 100644 --- a/src/nodes/accessors/BufferAttributeNode.js +++ b/src/nodes/accessors/BufferAttributeNode.js @@ -165,6 +165,8 @@ class BufferAttributeNode extends InputNode { */ getHash( builder ) { + let id; + if ( this.bufferStride === 0 && this.bufferOffset === 0 ) { let bufferData = builder.globalCache.getData( this.value ); @@ -179,11 +181,15 @@ class BufferAttributeNode extends InputNode { } - return bufferData.node.uuid; + id = bufferData.node.id; + + } else { + + id = this.id; } - return this.uuid; + return String( id ); } @@ -194,7 +200,7 @@ class BufferAttributeNode extends InputNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.bufferType === null ) { diff --git a/src/nodes/accessors/Object3DNode.js b/src/nodes/accessors/Object3DNode.js index add6f07d90d9e7..24fbfa307bb390 100644 --- a/src/nodes/accessors/Object3DNode.js +++ b/src/nodes/accessors/Object3DNode.js @@ -75,7 +75,7 @@ class Object3DNode extends Node { * * @return {('mat4'|'vec3'|'float')} The node type. */ - getNodeType() { + generateNodeType() { const scope = this.scope; diff --git a/src/nodes/accessors/ReferenceBaseNode.js b/src/nodes/accessors/ReferenceBaseNode.js index 08bae8b1277015..0cdaab53d31247 100644 --- a/src/nodes/accessors/ReferenceBaseNode.js +++ b/src/nodes/accessors/ReferenceBaseNode.js @@ -57,7 +57,7 @@ class ReferenceElementNode extends ArrayElementNode { * * @return {string} The node type. */ - getNodeType() { + generateNodeType() { return this.referenceNode.uniformType; @@ -230,7 +230,7 @@ class ReferenceBaseNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.node === null ) { diff --git a/src/nodes/accessors/ReferenceNode.js b/src/nodes/accessors/ReferenceNode.js index 4ca30be59ab737..583d6bd361265f 100644 --- a/src/nodes/accessors/ReferenceNode.js +++ b/src/nodes/accessors/ReferenceNode.js @@ -62,7 +62,7 @@ class ReferenceElementNode extends ArrayElementNode { * * @return {string} The node type. */ - getNodeType() { + generateNodeType() { return this.referenceNode.uniformType; @@ -71,8 +71,8 @@ class ReferenceElementNode extends ArrayElementNode { generate( builder ) { const snippet = super.generate( builder ); - const arrayType = this.referenceNode.getNodeType(); - const elementType = this.getNodeType(); + const arrayType = this.referenceNode.getNodeType( builder ); + const elementType = this.getNodeType( builder ); return builder.format( snippet, arrayType, elementType ); @@ -296,7 +296,7 @@ class ReferenceNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.node === null ) { diff --git a/src/nodes/accessors/StorageBufferNode.js b/src/nodes/accessors/StorageBufferNode.js index 7179df66a62fcf..785366c3124219 100644 --- a/src/nodes/accessors/StorageBufferNode.js +++ b/src/nodes/accessors/StorageBufferNode.js @@ -168,6 +168,8 @@ class StorageBufferNode extends BufferNode { */ getHash( builder ) { + let id; + if ( this.bufferCount === 0 ) { let bufferData = builder.globalCache.getData( this.value ); @@ -182,11 +184,15 @@ class StorageBufferNode extends BufferNode { } - return bufferData.node.uuid; + id = bufferData.node.id; + + } else { + + id = this.id; } - return this.uuid; + return String( id ); } @@ -317,7 +323,7 @@ class StorageBufferNode extends BufferNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.structTypeNode !== null ) { @@ -327,7 +333,7 @@ class StorageBufferNode extends BufferNode { if ( builder.isAvailable( 'storageBuffer' ) || builder.isAvailable( 'indirectStorageBuffer' ) ) { - return super.getNodeType( builder ); + return super.generateNodeType( builder ); } diff --git a/src/nodes/accessors/TextureNode.js b/src/nodes/accessors/TextureNode.js index d6ae40867e49e6..fae43e7165971d 100644 --- a/src/nodes/accessors/TextureNode.js +++ b/src/nodes/accessors/TextureNode.js @@ -217,7 +217,7 @@ class TextureNode extends UniformNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( /*builder*/ ) { + generateNodeType( /*builder*/ ) { if ( this.value.isDepthTexture === true ) return 'float'; diff --git a/src/nodes/accessors/UniformArrayNode.js b/src/nodes/accessors/UniformArrayNode.js index e05d8ace7a1fcc..db7383e4a011fb 100644 --- a/src/nodes/accessors/UniformArrayNode.js +++ b/src/nodes/accessors/UniformArrayNode.js @@ -41,7 +41,7 @@ class UniformArrayElementNode extends ArrayElementNode { generate( builder ) { const snippet = super.generate( builder ); - const type = this.getNodeType(); + const type = this.getNodeType( builder ); const paddedType = this.node.getPaddedType(); return builder.format( snippet, paddedType, type ); @@ -135,7 +135,7 @@ class UniformArrayNode extends BufferNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( /*builder*/ ) { + generateNodeType( /*builder*/ ) { return this.paddedType; diff --git a/src/nodes/code/FunctionCallNode.js b/src/nodes/code/FunctionCallNode.js index 9f22cd11b4ff1d..3d163908b19bbc 100644 --- a/src/nodes/code/FunctionCallNode.js +++ b/src/nodes/code/FunctionCallNode.js @@ -76,7 +76,7 @@ class FunctionCallNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @returns {string} The type of this node. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.functionNode.getNodeType( builder ); diff --git a/src/nodes/code/FunctionNode.js b/src/nodes/code/FunctionNode.js index 190e120b0ffc32..9331df54b780c6 100644 --- a/src/nodes/code/FunctionNode.js +++ b/src/nodes/code/FunctionNode.js @@ -53,7 +53,7 @@ class FunctionNode extends CodeNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.getNodeFunction( builder ).type; diff --git a/src/nodes/core/ArrayNode.js b/src/nodes/core/ArrayNode.js index f6727918cf3853..9011caf708be79 100644 --- a/src/nodes/core/ArrayNode.js +++ b/src/nodes/core/ArrayNode.js @@ -77,7 +77,7 @@ class ArrayNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The type of the node. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.nodeType === null ) { diff --git a/src/nodes/core/AssignNode.js b/src/nodes/core/AssignNode.js index c6cca706b58cd3..4b5b52ba29b652 100644 --- a/src/nodes/core/AssignNode.js +++ b/src/nodes/core/AssignNode.js @@ -63,7 +63,7 @@ class AssignNode extends TempNode { } - getNodeType( builder, output ) { + generateNodeType( builder, output ) { return output !== 'void' ? this.targetNode.getNodeType( builder ) : 'void'; diff --git a/src/nodes/core/AttributeNode.js b/src/nodes/core/AttributeNode.js index e1a2f0b600ee2d..3c781fef863713 100644 --- a/src/nodes/core/AttributeNode.js +++ b/src/nodes/core/AttributeNode.js @@ -43,7 +43,7 @@ class AttributeNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { let nodeType = this.nodeType; diff --git a/src/nodes/core/BypassNode.js b/src/nodes/core/BypassNode.js index fe3f67f574b1c7..f7214aa32d0187 100644 --- a/src/nodes/core/BypassNode.js +++ b/src/nodes/core/BypassNode.js @@ -55,7 +55,7 @@ class BypassNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.outputNode.getNodeType( builder ); diff --git a/src/nodes/core/ContextNode.js b/src/nodes/core/ContextNode.js index 045d8aee4660ba..149b40deb8e5b7 100644 --- a/src/nodes/core/ContextNode.js +++ b/src/nodes/core/ContextNode.js @@ -79,7 +79,7 @@ class ContextNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.node.getNodeType( builder ); diff --git a/src/nodes/core/InputNode.js b/src/nodes/core/InputNode.js index 7984fc31bdbadb..771ec3802ab2ac 100644 --- a/src/nodes/core/InputNode.js +++ b/src/nodes/core/InputNode.js @@ -51,7 +51,7 @@ class InputNode extends Node { } - getNodeType( /*builder*/ ) { + generateNodeType( /*builder*/ ) { if ( this.nodeType === null ) { diff --git a/src/nodes/core/InspectorNode.js b/src/nodes/core/InspectorNode.js index 3b862eba109a10..44ea52bed0db62 100644 --- a/src/nodes/core/InspectorNode.js +++ b/src/nodes/core/InspectorNode.js @@ -72,7 +72,7 @@ class InspectorNode extends Node { * @param {NodeBuilder} builder - The node builder. * @returns {string} */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.node.getNodeType( builder ); diff --git a/src/nodes/core/IsolateNode.js b/src/nodes/core/IsolateNode.js index fb55617c6d5244..e567444e05a781 100644 --- a/src/nodes/core/IsolateNode.js +++ b/src/nodes/core/IsolateNode.js @@ -53,7 +53,7 @@ class IsolateNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { const previousCache = builder.getCache(); const cache = builder.getCacheFromNode( this, this.parent ); diff --git a/src/nodes/core/Node.js b/src/nodes/core/Node.js index de0670b4a18157..f583b1076e1f36 100644 --- a/src/nodes/core/Node.js +++ b/src/nodes/core/Node.js @@ -68,14 +68,6 @@ class Node extends EventDispatcher { */ this.updateAfterType = NodeUpdateType.NONE; - /** - * The UUID of the node. - * - * @type {string} - * @readonly - */ - this.uuid = MathUtils.generateUUID(); - /** * The version of the node. The version automatically is increased when {@link Node#needsUpdate} is set to `true`. * @@ -133,6 +125,15 @@ class Node extends EventDispatcher { */ this._cacheKey = null; + /** + * The UUID of the node. + * + * @type {string} + * @default null + * @private + */ + this._uuid = null; + /** * The cache key's version. * @@ -142,7 +143,13 @@ class Node extends EventDispatcher { */ this._cacheKeyVersion = 0; - Object.defineProperty( this, 'id', { value: _nodeId ++ } ); + /** + * The unique ID of the node. + * + * @type {number} + * @readonly + */ + this.id = _nodeId ++; /** * The stack trace of the node for debugging purposes. @@ -177,6 +184,24 @@ class Node extends EventDispatcher { } + /** + * The UUID of the node. + * + * @type {string} + * @readonly + */ + get uuid() { + + if ( this._uuid === null ) { + + this._uuid = MathUtils.generateUUID(); + + } + + return this._uuid; + + } + /** * The type of the class. The value is usually the constructor name. * @@ -469,7 +494,7 @@ class Node extends EventDispatcher { */ getHash( /*builder*/ ) { - return this.uuid; + return String( this.id ); } @@ -540,15 +565,61 @@ class Node extends EventDispatcher { * Returns the node's type. * * @param {NodeBuilder} builder - The current node builder. + * @param {string} [output=null] - The output of the node. + * @return {string} The type of the node. + */ + getNodeType( builder, output = null ) { + + const nodeData = builder.getDataFromNode( this ); + + let type; + + if ( output !== null ) { + + nodeData.typeFromOutput = nodeData.typeFromOutput || {}; + + type = nodeData.typeFromOutput[ output ]; + + if ( type === undefined ) { + + type = this.generateNodeType( builder, output ); + + nodeData.typeFromOutput[ output ] = type; + + } + + } else { + + type = nodeData.type; + + if ( type === undefined ) { + + type = this.generateNodeType( builder ); + + nodeData.type = type; + + } + + } + + return type; + + } + + /** + * Returns the node's type. + * + * @param {NodeBuilder} builder - The current node builder. + * @param {string} [output=null] - The output of the node. * @return {string} The type of the node. */ - getNodeType( builder ) { + generateNodeType( builder, output = null ) { const nodeProperties = builder.getNodeProperties( this ); if ( nodeProperties.outputNode ) { - return nodeProperties.outputNode.getNodeType( builder ); + return nodeProperties.outputNode.getNodeType( builder, output ); } diff --git a/src/nodes/core/OutputStructNode.js b/src/nodes/core/OutputStructNode.js index f8130ea22899fd..2413076a46b949 100644 --- a/src/nodes/core/OutputStructNode.js +++ b/src/nodes/core/OutputStructNode.js @@ -42,7 +42,7 @@ class OutputStructNode extends Node { } - getNodeType( /*builder*/ ) { + generateNodeType( /*builder*/ ) { return 'OutputType'; diff --git a/src/nodes/core/ParameterNode.js b/src/nodes/core/ParameterNode.js index d854138cbb6e6b..bcdf91b4a01f57 100644 --- a/src/nodes/core/ParameterNode.js +++ b/src/nodes/core/ParameterNode.js @@ -68,7 +68,7 @@ class ParameterNode extends PropertyNode { getHash() { - return this.uuid; + return String( this.id ); } diff --git a/src/nodes/core/StackNode.js b/src/nodes/core/StackNode.js index 85418417ada7b6..d8762d3541b0a9 100644 --- a/src/nodes/core/StackNode.js +++ b/src/nodes/core/StackNode.js @@ -95,7 +95,7 @@ class StackNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.hasOutput( builder ) ? this.outputNode.getNodeType( builder ) : 'void'; diff --git a/src/nodes/core/StructNode.js b/src/nodes/core/StructNode.js index 71f012e98ba6a0..646c6d1efce98d 100644 --- a/src/nodes/core/StructNode.js +++ b/src/nodes/core/StructNode.js @@ -40,7 +40,7 @@ class StructNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.structTypeNode.getNodeType( builder ); diff --git a/src/nodes/core/StructTypeNode.js b/src/nodes/core/StructTypeNode.js index 14c0299b1f1376..862b9594a44dbd 100644 --- a/src/nodes/core/StructTypeNode.js +++ b/src/nodes/core/StructTypeNode.js @@ -122,7 +122,7 @@ class StructTypeNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { const structType = builder.getStructTypeFromNode( this, this.membersLayout, this.name ); diff --git a/src/nodes/core/SubBuildNode.js b/src/nodes/core/SubBuildNode.js index cf99d37d3fa1c2..a62ba80488226a 100644 --- a/src/nodes/core/SubBuildNode.js +++ b/src/nodes/core/SubBuildNode.js @@ -46,7 +46,7 @@ class SubBuildNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.nodeType !== null ) return this.nodeType; diff --git a/src/nodes/core/VarNode.js b/src/nodes/core/VarNode.js index 11b5fffa77c60f..20bed3ac10c5cb 100644 --- a/src/nodes/core/VarNode.js +++ b/src/nodes/core/VarNode.js @@ -151,7 +151,7 @@ class VarNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.node.getNodeType( builder ); diff --git a/src/nodes/core/VaryingNode.js b/src/nodes/core/VaryingNode.js index 662644fd597b6d..4349d2ab100fcf 100644 --- a/src/nodes/core/VaryingNode.js +++ b/src/nodes/core/VaryingNode.js @@ -104,7 +104,7 @@ class VaryingNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { // VaryingNode is auto type diff --git a/src/nodes/display/ScreenNode.js b/src/nodes/display/ScreenNode.js index 4c6dd959ef68a3..aebe1767c750c7 100644 --- a/src/nodes/display/ScreenNode.js +++ b/src/nodes/display/ScreenNode.js @@ -72,7 +72,7 @@ class ScreenNode extends Node { * * @return {('float'|'vec2'|'vec4')} The node type. */ - getNodeType() { + generateNodeType() { if ( this.scope === ScreenNode.DPR ) return 'float'; if ( this.scope === ScreenNode.VIEWPORT ) return 'vec4'; diff --git a/src/nodes/geometry/RangeNode.js b/src/nodes/geometry/RangeNode.js index e49ba624eb0a36..e13c863287a97a 100644 --- a/src/nodes/geometry/RangeNode.js +++ b/src/nodes/geometry/RangeNode.js @@ -84,7 +84,7 @@ class RangeNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return builder.object.count > 1 ? builder.getTypeFromLength( this.getVectorLength( builder ) ) : 'float'; diff --git a/src/nodes/gpgpu/AtomicFunctionNode.js b/src/nodes/gpgpu/AtomicFunctionNode.js index 2a137b49dfac5c..26464b996c2add 100644 --- a/src/nodes/gpgpu/AtomicFunctionNode.js +++ b/src/nodes/gpgpu/AtomicFunctionNode.js @@ -82,7 +82,7 @@ class AtomicFunctionNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.getInputType( builder ); diff --git a/src/nodes/gpgpu/ComputeBuiltinNode.js b/src/nodes/gpgpu/ComputeBuiltinNode.js index fed5c7301585c2..69ec6bc2511573 100644 --- a/src/nodes/gpgpu/ComputeBuiltinNode.js +++ b/src/nodes/gpgpu/ComputeBuiltinNode.js @@ -55,7 +55,7 @@ class ComputeBuiltinNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( /*builder*/ ) { + generateNodeType( /*builder*/ ) { return this.nodeType; diff --git a/src/nodes/gpgpu/SubgroupFunctionNode.js b/src/nodes/gpgpu/SubgroupFunctionNode.js index a53d1c582b257c..dad8f164182093 100644 --- a/src/nodes/gpgpu/SubgroupFunctionNode.js +++ b/src/nodes/gpgpu/SubgroupFunctionNode.js @@ -75,7 +75,7 @@ class SubgroupFunctionNode extends TempNode { } - getNodeType( builder ) { + generateNodeType( builder ) { const method = this.method; diff --git a/src/nodes/math/BitcastNode.js b/src/nodes/math/BitcastNode.js index a2d0e3b084110b..1091d2bec5448b 100644 --- a/src/nodes/math/BitcastNode.js +++ b/src/nodes/math/BitcastNode.js @@ -60,7 +60,7 @@ class BitcastNode extends TempNode { } - getNodeType( builder ) { + generateNodeType( builder ) { // GLSL aliasing if ( this.inputType !== null ) { diff --git a/src/nodes/math/ConditionalNode.js b/src/nodes/math/ConditionalNode.js index 9ef0b9db9b0bf9..031576c73137c5 100644 --- a/src/nodes/math/ConditionalNode.js +++ b/src/nodes/math/ConditionalNode.js @@ -66,7 +66,7 @@ class ConditionalNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { const { ifNode, elseNode } = builder.getNodeProperties( this ); diff --git a/src/nodes/math/MathNode.js b/src/nodes/math/MathNode.js index dcf7ac6fe37458..22720e143dcdf2 100644 --- a/src/nodes/math/MathNode.js +++ b/src/nodes/math/MathNode.js @@ -133,7 +133,7 @@ class MathNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { const method = this.method; diff --git a/src/nodes/math/OperatorNode.js b/src/nodes/math/OperatorNode.js index fcd2aeea1c74ac..c06a906ba73c4d 100644 --- a/src/nodes/math/OperatorNode.js +++ b/src/nodes/math/OperatorNode.js @@ -108,7 +108,7 @@ class OperatorNode extends TempNode { * @param {?string} [output=null] - The output type. * @return {string} The node type. */ - getNodeType( builder, output = null ) { + generateNodeType( builder, output = null ) { const op = this.op; diff --git a/src/nodes/math/PackFloatNode.js b/src/nodes/math/PackFloatNode.js index 3a71450bb33bc0..da24ed3c389e07 100644 --- a/src/nodes/math/PackFloatNode.js +++ b/src/nodes/math/PackFloatNode.js @@ -48,7 +48,7 @@ class PackFloatNode extends TempNode { } - getNodeType() { + generateNodeType() { return 'uint'; diff --git a/src/nodes/math/UnpackFloatNode.js b/src/nodes/math/UnpackFloatNode.js index 05c70afcf7ad41..4a56fdcfd1a2ae 100644 --- a/src/nodes/math/UnpackFloatNode.js +++ b/src/nodes/math/UnpackFloatNode.js @@ -48,7 +48,7 @@ class UnpackFloatNode extends TempNode { } - getNodeType() { + generateNodeType() { return 'vec2'; diff --git a/src/nodes/tsl/TSLBase.js b/src/nodes/tsl/TSLBase.js index af95aecb2c3394..2aa12105192108 100644 --- a/src/nodes/tsl/TSLBase.js +++ b/src/nodes/tsl/TSLBase.js @@ -21,7 +21,7 @@ export * from '../accessors/BufferAttributeNode.js'; // .toAttribute() export * from '../gpgpu/ComputeNode.js'; // .compute() export * from '../core/IsolateNode.js'; // .isolate() export * from '../core/BypassNode.js'; // .bypass() -export * from '../utils/RemapNode.js'; // .remap(), .remapClamp() +export * from '../utils/Remap.js'; // .remap(), .remapClamp() export * from '../code/ExpressionNode.js'; // expression() export * from '../utils/Discard.js'; // Discard(), Return() export * from '../display/RenderOutputNode.js'; // .renderOutput() diff --git a/src/nodes/tsl/TSLCore.js b/src/nodes/tsl/TSLCore.js index 0f5407d404d12f..10d5b5bbfa587f 100644 --- a/src/nodes/tsl/TSLCore.js +++ b/src/nodes/tsl/TSLCore.js @@ -461,7 +461,7 @@ class ShaderCallNodeInternal extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.shaderNode.nodeType || this.getOutputNode( builder ).getNodeType( builder ); @@ -917,7 +917,25 @@ const ConvertType = function ( type, cacheMap = null ) { // exports -export const defined = ( v ) => typeof v === 'object' && v !== null ? v.value : v; // TODO: remove boolean conversion and defined function +export function defined( value ) { + + if ( value && value.isNode ) { + + value.traverse( ( node ) => { + + if ( node.isConstNode ) { + + value = node.value; + + } + + } ); + + } + + return Boolean( value ); + +} // utils @@ -1018,7 +1036,7 @@ class FnNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.shaderNode.getNodeType( builder ) || 'float'; diff --git a/src/nodes/utils/ArrayElementNode.js b/src/nodes/utils/ArrayElementNode.js index c6794fdd3e8fd7..6f62c8413b0460 100644 --- a/src/nodes/utils/ArrayElementNode.js +++ b/src/nodes/utils/ArrayElementNode.js @@ -55,7 +55,7 @@ class ArrayElementNode extends Node { // @TODO: If extending from TempNode it br * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.node.getElementType( builder ); diff --git a/src/nodes/utils/ConvertNode.js b/src/nodes/utils/ConvertNode.js index 1ffde9b9ed0d3e..118bd5ab645d30 100644 --- a/src/nodes/utils/ConvertNode.js +++ b/src/nodes/utils/ConvertNode.js @@ -48,7 +48,7 @@ class ConvertNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { const requestType = this.node.getNodeType( builder ); diff --git a/src/nodes/utils/DebugNode.js b/src/nodes/utils/DebugNode.js index 3b67d00a66c115..f1c4cbc213d0d4 100644 --- a/src/nodes/utils/DebugNode.js +++ b/src/nodes/utils/DebugNode.js @@ -19,7 +19,7 @@ class DebugNode extends TempNode { } - getNodeType( builder ) { + generateNodeType( builder ) { return this.node.getNodeType( builder ); diff --git a/src/nodes/utils/FlipNode.js b/src/nodes/utils/FlipNode.js index 9eb19271a26755..6e6f796160109a 100644 --- a/src/nodes/utils/FlipNode.js +++ b/src/nodes/utils/FlipNode.js @@ -56,7 +56,7 @@ class FlipNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.sourceNode.getNodeType( builder ); diff --git a/src/nodes/utils/FunctionOverloadingNode.js b/src/nodes/utils/FunctionOverloadingNode.js index a654c3c2e7726d..e980f73b94188d 100644 --- a/src/nodes/utils/FunctionOverloadingNode.js +++ b/src/nodes/utils/FunctionOverloadingNode.js @@ -65,7 +65,7 @@ class FunctionOverloadingNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { const candidateFn = this.getCandidateFn( builder ); diff --git a/src/nodes/utils/JoinNode.js b/src/nodes/utils/JoinNode.js index 979bd6aba881d9..2a89b7357351b0 100644 --- a/src/nodes/utils/JoinNode.js +++ b/src/nodes/utils/JoinNode.js @@ -42,7 +42,7 @@ class JoinNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.nodeType !== null ) { diff --git a/src/nodes/utils/MemberNode.js b/src/nodes/utils/MemberNode.js index a79ee53a86a17f..7a2c40f8aa9a3e 100644 --- a/src/nodes/utils/MemberNode.js +++ b/src/nodes/utils/MemberNode.js @@ -66,7 +66,7 @@ class MemberNode extends Node { } - getNodeType( builder ) { + generateNodeType( builder ) { if ( this.hasMember( builder ) === false ) { diff --git a/src/nodes/utils/Remap.js b/src/nodes/utils/Remap.js new file mode 100644 index 00000000000000..b55bc10a3dc8ea --- /dev/null +++ b/src/nodes/utils/Remap.js @@ -0,0 +1,48 @@ +import { float, addMethodChaining, Fn, bool, defined } from '../tsl/TSLCore.js'; + +/** + * This node allows to remap a node value from one range into another. E.g a value of + * `0.4` in the range `[ 0.3, 0.5 ]` should be remapped into the normalized range `[ 0, 1 ]`. + * `remap` takes care of that and converts the original value of `0.4` to `0.5`. + * + * @tsl + * @function + * @param {Node} node - The node that should be remapped. + * @param {Node} inLowNode - The source or current lower bound of the range. + * @param {Node} inHighNode - The source or current upper bound of the range. + * @param {?Node} [outLowNode=float(0)] - The target lower bound of the range. + * @param {?Node} [outHighNode=float(1)] - The target upper bound of the range. + * @returns {Node} + */ +export const remap = /*@__PURE__*/ Fn( ( [ node, inLowNode, inHighNode, outLowNode = float( 0 ), outHighNode = float( 1 ), doClamp = bool( false ) ] ) => { + + let t = node.sub( inLowNode ).div( inHighNode.sub( inLowNode ) ); + + if ( defined( doClamp ) ) t = t.clamp(); + + return t.mul( outHighNode.sub( outLowNode ) ).add( outLowNode ); + +} ); + +/** + * This node allows to remap a node value from one range into another but with enabled clamping. E.g a value of + * `0.4` in the range `[ 0.3, 0.5 ]` should be remapped into the normalized range `[ 0, 1 ]`. + * `remapClamp` takes care of that and converts the original value of `0.4` to `0.5`. + * + * @tsl + * @function + * @param {Node} node - The node that should be remapped. + * @param {Node} inLowNode - The source or current lower bound of the range. + * @param {Node} inHighNode - The source or current upper bound of the range. + * @param {?Node} [outLowNode=float(0)] - The target lower bound of the range. + * @param {?Node} [outHighNode=float(1)] - The target upper bound of the range. + * @returns {Node} + */ +function remapClamp( node, inLowNode, inHighNode, outLowNode = float( 0 ), outHighNode = float( 1 ) ) { + + return remap( node, inLowNode, inHighNode, outLowNode, outHighNode, true ); + +} + +addMethodChaining( 'remap', remap ); +addMethodChaining( 'remapClamp', remapClamp ); diff --git a/src/nodes/utils/RemapNode.js b/src/nodes/utils/RemapNode.js deleted file mode 100644 index 19efdf4f25b6f0..00000000000000 --- a/src/nodes/utils/RemapNode.js +++ /dev/null @@ -1,125 +0,0 @@ -import Node from '../core/Node.js'; -import { float, addMethodChaining, nodeProxy } from '../tsl/TSLCore.js'; - -/** - * This node allows to remap a node value from one range into another. E.g a value of - * `0.4` in the range `[ 0.3, 0.5 ]` should be remapped into the normalized range `[ 0, 1 ]`. - * `RemapNode` takes care of that and converts the original value of `0.4` to `0.5`. - * - * @augments Node - */ -class RemapNode extends Node { - - static get type() { - - return 'RemapNode'; - - } - - /** - * Constructs a new remap node. - * - * @param {Node} node - The node that should be remapped. - * @param {Node} inLowNode - The source or current lower bound of the range. - * @param {Node} inHighNode - The source or current upper bound of the range. - * @param {Node} [outLowNode=float(0)] - The target lower bound of the range. - * @param {Node} [outHighNode=float(1)] - The target upper bound of the range. - */ - constructor( node, inLowNode, inHighNode, outLowNode = float( 0 ), outHighNode = float( 1 ) ) { - - super(); - - /** - * The node that should be remapped. - * - * @type {Node} - */ - this.node = node; - - /** - * The source or current lower bound of the range. - * - * @type {Node} - */ - this.inLowNode = inLowNode; - - /** - * The source or current upper bound of the range. - * - * @type {Node} - */ - this.inHighNode = inHighNode; - - /** - * The target lower bound of the range. - * - * @type {Node} - * @default float(0) - */ - this.outLowNode = outLowNode; - - /** - * The target upper bound of the range. - * - * @type {Node} - * @default float(1) - */ - this.outHighNode = outHighNode; - - /** - * Whether the node value should be clamped before - * remapping it to the target range. - * - * @type {boolean} - * @default true - */ - this.doClamp = true; - - } - - setup() { - - const { node, inLowNode, inHighNode, outLowNode, outHighNode, doClamp } = this; - - let t = node.sub( inLowNode ).div( inHighNode.sub( inLowNode ) ); - - if ( doClamp === true ) t = t.clamp(); - - return t.mul( outHighNode.sub( outLowNode ) ).add( outLowNode ); - - } - -} - -export default RemapNode; - -/** - * TSL function for creating a remap node. - * - * @tsl - * @function - * @param {Node} node - The node that should be remapped. - * @param {Node} inLowNode - The source or current lower bound of the range. - * @param {Node} inHighNode - The source or current upper bound of the range. - * @param {?Node} [outLowNode=float(0)] - The target lower bound of the range. - * @param {?Node} [outHighNode=float(1)] - The target upper bound of the range. - * @returns {RemapNode} - */ -export const remap = /*@__PURE__*/ nodeProxy( RemapNode, null, null, { doClamp: false } ).setParameterLength( 3, 5 ); - -/** - * TSL function for creating a remap node, but with enabled clamping. - * - * @tsl - * @function - * @param {Node} node - The node that should be remapped. - * @param {Node} inLowNode - The source or current lower bound of the range. - * @param {Node} inHighNode - The source or current upper bound of the range. - * @param {?Node} [outLowNode=float(0)] - The target lower bound of the range. - * @param {?Node} [outHighNode=float(1)] - The target upper bound of the range. - * @returns {RemapNode} - */ -export const remapClamp = /*@__PURE__*/ nodeProxy( RemapNode ).setParameterLength( 3, 5 ); - -addMethodChaining( 'remap', remap ); -addMethodChaining( 'remapClamp', remapClamp ); diff --git a/src/nodes/utils/RotateNode.js b/src/nodes/utils/RotateNode.js index 16873eb1fd7a8e..30e443d6c319e2 100644 --- a/src/nodes/utils/RotateNode.js +++ b/src/nodes/utils/RotateNode.js @@ -49,7 +49,7 @@ class RotateNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node's type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.positionNode.getNodeType( builder ); diff --git a/src/nodes/utils/SetNode.js b/src/nodes/utils/SetNode.js index 2407c8fc6603be..7a033024a3f4ea 100644 --- a/src/nodes/utils/SetNode.js +++ b/src/nodes/utils/SetNode.js @@ -60,7 +60,7 @@ class SetNode extends TempNode { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return this.sourceNode.getNodeType( builder ); diff --git a/src/nodes/utils/SplitNode.js b/src/nodes/utils/SplitNode.js index f1fa5aeec2a84c..0b55b38c7f0522 100644 --- a/src/nodes/utils/SplitNode.js +++ b/src/nodes/utils/SplitNode.js @@ -94,7 +94,7 @@ class SplitNode extends Node { * @param {NodeBuilder} builder - The current node builder. * @return {string} The node type. */ - getNodeType( builder ) { + generateNodeType( builder ) { return builder.getTypeFromLength( this.components.length, this.getComponentType( builder ) ); From 9dcea42983491dab99f454c4d34e269723ff8fc6 Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 5 Mar 2026 00:21:15 -0300 Subject: [PATCH 2/3] WebGPURenderer: Use node events to update the binding groups (#33047) --- src/nodes/core/NodeBuilder.js | 6 +-- src/nodes/core/UniformGroupNode.js | 42 ++++++++++++++--- src/renderers/common/BindGroup.js | 9 +--- .../common/nodes/NodeBuilderState.js | 2 +- src/renderers/common/nodes/NodeManager.js | 47 +------------------ src/renderers/webgl-fallback/WebGLBackend.js | 38 ++++++++++----- src/renderers/webgpu/WebGPUBackend.js | 27 +++++------ 7 files changed, 78 insertions(+), 93 deletions(-) diff --git a/src/nodes/core/NodeBuilder.js b/src/nodes/core/NodeBuilder.js index d475e54dd267c6..08d1cc24f2e0df 100644 --- a/src/nodes/core/NodeBuilder.js +++ b/src/nodes/core/NodeBuilder.js @@ -621,7 +621,7 @@ class NodeBuilder { if ( bindGroup === undefined ) { - bindGroup = new BindGroup( groupName, bindings, this.bindingsIndexes[ groupName ].group ); + bindGroup = new BindGroup( groupName, bindings ); bindingGroupsCache.set( cacheKey, bindGroup ); @@ -629,7 +629,7 @@ class NodeBuilder { } else { - bindGroup = new BindGroup( groupName, bindings, this.bindingsIndexes[ groupName ].group ); + bindGroup = new BindGroup( groupName, bindings ); } @@ -736,8 +736,6 @@ class NodeBuilder { const bindingGroup = bindingsGroups[ i ]; this.bindingsIndexes[ bindingGroup.name ].group = i; - bindingGroup.index = i; - } } diff --git a/src/nodes/core/UniformGroupNode.js b/src/nodes/core/UniformGroupNode.js index 3ee852abfe8a63..e04e8b47b42674 100644 --- a/src/nodes/core/UniformGroupNode.js +++ b/src/nodes/core/UniformGroupNode.js @@ -1,4 +1,5 @@ import Node from './Node.js'; +import { NodeUpdateType } from './constants.js'; /** * This node can be used to group single instances of {@link UniformNode} @@ -27,8 +28,9 @@ class UniformGroupNode extends Node { * @param {string} name - The name of the uniform group node. * @param {boolean} [shared=false] - Whether this uniform group node is shared or not. * @param {number} [order=1] - Influences the internal sorting. + * @param {string|null} [updateType=null] - The update type of the uniform group node. */ - constructor( name, shared = false, order = 1 ) { + constructor( name, shared = false, order = 1, updateType = null ) { super( 'string' ); @@ -56,6 +58,14 @@ class UniformGroupNode extends Node { */ this.order = order; + /** + * The update type of the uniform group node. + * + * @type {string|null} + * @default null + */ + this.updateType = updateType; + /** * This flag can be used for type testing. * @@ -67,6 +77,21 @@ class UniformGroupNode extends Node { } + /** + * Marks the uniform group node as needing an update. + * This will trigger the necessary updates in the rendering process. + */ + update() { + + this.needsUpdate = true; + + } + + /** + * Serializes the uniform group node to a JSON object. + * + * @param {Object} data - The object to store the serialized data. + */ serialize( data ) { super.serialize( data ); @@ -77,6 +102,11 @@ class UniformGroupNode extends Node { } + /** + * Deserializes the uniform group node from a JSON object. + * + * @param {Object} data - The object containing the serialized data. + */ deserialize( data ) { super.deserialize( data ); @@ -99,7 +129,7 @@ export default UniformGroupNode; * @param {string} name - The name of the uniform group node. * @returns {UniformGroupNode} */ -export const uniformGroup = ( name ) => new UniformGroupNode( name ); +export const uniformGroup = ( name, order = 1, updateType = null ) => new UniformGroupNode( name, false, order, updateType ); /** * TSL function for creating a shared uniform group node with the given name and order. @@ -110,7 +140,7 @@ export const uniformGroup = ( name ) => new UniformGroupNode( name ); * @param {number} [order=0] - Influences the internal sorting. * @returns {UniformGroupNode} */ -export const sharedUniformGroup = ( name, order = 0 ) => new UniformGroupNode( name, true, order ); +export const sharedUniformGroup = ( name, order = 0, updateType = null ) => new UniformGroupNode( name, true, order, updateType ); /** * TSL object that represents a shared uniform group node which is updated once per frame. @@ -118,7 +148,7 @@ export const sharedUniformGroup = ( name, order = 0 ) => new UniformGroupNode( n * @tsl * @type {UniformGroupNode} */ -export const frameGroup = /*@__PURE__*/ sharedUniformGroup( 'frame' ); +export const frameGroup = /*@__PURE__*/ sharedUniformGroup( 'frame', 0, NodeUpdateType.FRAME ); /** * TSL object that represents a shared uniform group node which is updated once per render. @@ -126,7 +156,7 @@ export const frameGroup = /*@__PURE__*/ sharedUniformGroup( 'frame' ); * @tsl * @type {UniformGroupNode} */ -export const renderGroup = /*@__PURE__*/ sharedUniformGroup( 'render' ); +export const renderGroup = /*@__PURE__*/ sharedUniformGroup( 'render', 0, NodeUpdateType.RENDER ); /** * TSL object that represents a uniform group node which is updated once per object. @@ -134,4 +164,4 @@ export const renderGroup = /*@__PURE__*/ sharedUniformGroup( 'render' ); * @tsl * @type {UniformGroupNode} */ -export const objectGroup = /*@__PURE__*/ uniformGroup( 'object' ); +export const objectGroup = /*@__PURE__*/ uniformGroup( 'object', 1, NodeUpdateType.OBJECT ); diff --git a/src/renderers/common/BindGroup.js b/src/renderers/common/BindGroup.js index 43afc93a7855a7..6240a4b0e531cf 100644 --- a/src/renderers/common/BindGroup.js +++ b/src/renderers/common/BindGroup.js @@ -16,7 +16,7 @@ class BindGroup { * @param {Array} bindings - An array of bindings. * @param {number} index - The group index. */ - constructor( name = '', bindings = [], index = 0 ) { + constructor( name = '', bindings = [] ) { /** * The bind group's name. @@ -32,13 +32,6 @@ class BindGroup { */ this.bindings = bindings; - /** - * The group index. - * - * @type {number} - */ - this.index = index; - /** * The group's ID. * diff --git a/src/renderers/common/nodes/NodeBuilderState.js b/src/renderers/common/nodes/NodeBuilderState.js index 4b0dfd3b800412..d59209fb7fb114 100644 --- a/src/renderers/common/nodes/NodeBuilderState.js +++ b/src/renderers/common/nodes/NodeBuilderState.js @@ -126,7 +126,7 @@ class NodeBuilderState { if ( shared !== true ) { - const bindingsGroup = new BindGroup( instanceGroup.name, [], instanceGroup.index ); + const bindingsGroup = new BindGroup( instanceGroup.name, [] ); bindings.push( bindingsGroup ); for ( const instanceBinding of instanceGroup.bindings ) { diff --git a/src/renderers/common/nodes/NodeManager.js b/src/renderers/common/nodes/NodeManager.js index 82eef1c9e3673f..99911b10bc12ce 100644 --- a/src/renderers/common/nodes/NodeManager.js +++ b/src/renderers/common/nodes/NodeManager.js @@ -4,7 +4,7 @@ import NodeBuilderState from './NodeBuilderState.js'; import NodeMaterial from '../../../materials/nodes/NodeMaterial.js'; import { cubeMapNode } from '../../../nodes/utils/CubeMapNode.js'; import { NodeFrame, StackTrace } from '../../../nodes/Nodes.js'; -import { objectGroup, renderGroup, frameGroup, cubeTexture, texture, texture3D, vec3, fog, rangeFogFactor, densityFogFactor, reference, pmremTexture, screenUV } from '../../../nodes/TSL.js'; +import { renderGroup, cubeTexture, texture, texture3D, vec3, fog, rangeFogFactor, densityFogFactor, reference, pmremTexture, screenUV } from '../../../nodes/TSL.js'; import { builtin } from '../../../nodes/accessors/BuiltinNode.js'; import { CubeUVReflectionMapping, EquirectangularReflectionMapping, EquirectangularRefractionMapping } from '../../../constants.js'; @@ -111,51 +111,6 @@ class NodeManager extends DataMap { updateGroup( nodeUniformsGroup ) { const groupNode = nodeUniformsGroup.groupNode; - const name = groupNode.name; - - // objectGroup is always updated - - if ( name === objectGroup.name ) return true; - - // renderGroup is updated once per render/compute call - - if ( name === renderGroup.name ) { - - const uniformsGroupData = this.get( nodeUniformsGroup ); - const renderId = this.nodeFrame.renderId; - - if ( uniformsGroupData.renderId !== renderId ) { - - uniformsGroupData.renderId = renderId; - - return true; - - } - - return false; - - } - - // frameGroup is updated once per frame - - if ( name === frameGroup.name ) { - - const uniformsGroupData = this.get( nodeUniformsGroup ); - const frameId = this.nodeFrame.frameId; - - if ( uniformsGroupData.frameId !== frameId ) { - - uniformsGroupData.frameId = frameId; - - return true; - - } - - return false; - - } - - // other groups are updated just when groupNode.needsUpdate is true _chainKeys[ 0 ] = groupNode; _chainKeys[ 1 ] = nodeUniformsGroup; diff --git a/src/renderers/webgl-fallback/WebGLBackend.js b/src/renderers/webgl-fallback/WebGLBackend.js index 8cbd544a866062..7271e9fef781b4 100644 --- a/src/renderers/webgl-fallback/WebGLBackend.js +++ b/src/renderers/webgl-fallback/WebGLBackend.js @@ -1189,7 +1189,20 @@ class WebGLBackend extends Backend { } - const cameraIndexData = this.get( cameraIndex ); + let cameraIndexBufferIndex = 0; + + bindingsSearch: for ( const bindGroup of renderObject.getBindings() ) { + + for ( const binding of bindGroup.bindings ) { + + if ( binding === cameraIndex ) break bindingsSearch; + + if ( binding.isUniformsGroup || binding.isUniformBuffer ) cameraIndexBufferIndex ++; + + } + + } + const pixelRatio = this.renderer.getPixelRatio(); const renderTarget = this._currentContext.renderTarget; @@ -1258,7 +1271,7 @@ class WebGLBackend extends Backend { } - state.bindBufferBase( gl.UNIFORM_BUFFER, cameraIndexData.index, cameraData.indexesGPU[ i ] ); + state.bindBufferBase( gl.UNIFORM_BUFFER, cameraIndexBufferIndex, cameraData.indexesGPU[ i ] ); this._draw( object, renderer, firstVertex, vertexCount, instanceCount ); @@ -1801,11 +1814,6 @@ class WebGLBackend extends Backend { const { gl } = this; - const bindGroupData = this.get( bindGroup ); - - let i = bindGroupData.uniformBuffers; - let t = bindGroupData.textures; - for ( const binding of bindGroup.bindings ) { const map = this.get( binding ); @@ -1862,7 +1870,6 @@ class WebGLBackend extends Backend { } - map.index = i ++; map.bufferGPU = bufferGPU; this.set( binding, map ); @@ -1871,7 +1878,6 @@ class WebGLBackend extends Backend { const { textureGPU, glTextureType } = this.get( binding.texture ); - map.index = t ++; map.textureGPU = textureGPU; map.glTextureType = glTextureType; @@ -2546,20 +2552,22 @@ class WebGLBackend extends Backend { const gl = this.gl; + let uniformBuffers = 0; + let textures = 0; + for ( const bindGroup of bindings ) { for ( const binding of bindGroup.bindings ) { - const bindingData = this.get( binding ); - const index = bindingData.index; - if ( binding.isUniformsGroup || binding.isUniformBuffer ) { + const index = uniformBuffers ++; const location = gl.getUniformBlockIndex( programGPU, binding.name ); gl.uniformBlockBinding( programGPU, location, index ); } else if ( binding.isSampledTexture ) { + const index = textures ++; const location = gl.getUniformLocation( programGPU, binding.name ); gl.uniform1i( location, index ); @@ -2581,20 +2589,24 @@ class WebGLBackend extends Backend { const { gl, state } = this; + let uniformBuffers = 0; + let textures = 0; + for ( const bindGroup of bindings ) { for ( const binding of bindGroup.bindings ) { const bindingData = this.get( binding ); - const index = bindingData.index; if ( binding.isUniformsGroup || binding.isUniformBuffer ) { + const index = uniformBuffers ++; // TODO USE bindBufferRange to group multiple uniform buffers state.bindBufferBase( gl.UNIFORM_BUFFER, index, bindingData.bufferGPU ); } else if ( binding.isSampledTexture ) { + const index = textures ++; state.bindTexture( bindingData.glTextureType, bindingData.textureGPU, gl.TEXTURE0 + index ); } diff --git a/src/renderers/webgpu/WebGPUBackend.js b/src/renderers/webgpu/WebGPUBackend.js index b4e4de9dcef989..0329caca979c6e 100644 --- a/src/renderers/webgpu/WebGPUBackend.js +++ b/src/renderers/webgpu/WebGPUBackend.js @@ -1499,17 +1499,16 @@ class WebGPUBackend extends Backend { * @param {Info} info - Holds a series of statistical information about the GPU memory and the rendering process. * @param {Object} renderContextData - The render context data object, holding current pass state and occlusion query tracking. * @param {GPURenderPipeline} pipelineGPU - The GPU render pipeline. + * @param {Array} bindings - The bind groups. * @param {Array} vertexBuffers - The vertex buffers. * @param {{vertexCount: number, firstVertex: number, instanceCount: number, firstInstance: number}} drawParams - The draw parameters. * @param {GPURenderPassEncoder|GPURenderBundleEncoder} passEncoderGPU - The GPU pass encoder used for recording draw commands. * @param {Object} currentSets - Tracking object for currently set pipeline, attributes, bind groups, and index state. */ - _draw( renderObject, info, renderContextData, pipelineGPU, vertexBuffers, drawParams, passEncoderGPU, currentSets ) { + _draw( renderObject, info, renderContextData, pipelineGPU, bindings, vertexBuffers, drawParams, passEncoderGPU, currentSets ) { const { object, material, context } = renderObject; - const bindings = renderObject.getBindings(); - const index = renderObject.getIndex(); const hasIndex = ( index !== null ); @@ -1523,10 +1522,10 @@ class WebGPUBackend extends Backend { const bindGroup = bindings[ i ]; const bindingsData = this.get( bindGroup ); - if ( currentBindingGroups[ bindGroup.index ] !== bindGroup.id ) { + if ( currentBindingGroups[ i ] !== bindGroup.id ) { - passEncoderGPU.setBindGroup( bindGroup.index, bindingsData.group ); - currentBindingGroups[ bindGroup.index ] = bindGroup.id; + passEncoderGPU.setBindGroup( i, bindingsData.group ); + currentBindingGroups[ i ] = bindGroup.id; } @@ -1693,6 +1692,8 @@ class WebGPUBackend extends Backend { const drawParams = renderObject.getDrawParameters(); if ( drawParams === null ) return; + const bindings = renderObject.getBindings(); + // vertex buffers const vertexBuffers = renderObject.getVertexBuffers(); @@ -1736,8 +1737,6 @@ class WebGPUBackend extends Backend { const vp = subCamera.viewport; - - let pass = renderContextData.currentPass; let sets = renderContextData.currentSets; if ( renderContextData.bundleEncoders ) { @@ -1749,8 +1748,6 @@ class WebGPUBackend extends Backend { } - - if ( vp ) { pass.setViewport( @@ -1764,16 +1761,16 @@ class WebGPUBackend extends Backend { } - // Set camera index binding for this layer if ( cameraIndex && cameraData.indexesGPU ) { - pass.setBindGroup( cameraIndex.index, cameraData.indexesGPU[ i ] ); - sets.bindingGroups[ cameraIndex.index ] = cameraIndex.id; + const indexPos = bindings.indexOf( cameraIndex ); + pass.setBindGroup( indexPos, cameraData.indexesGPU[ i ] ); + sets.bindingGroups[ indexPos ] = cameraIndex.id; } - this._draw( renderObject, info, renderContextData, pipelineGPU, vertexBuffers, drawParams, pass, sets ); + this._draw( renderObject, info, renderContextData, pipelineGPU, bindings, vertexBuffers, drawParams, pass, sets ); } @@ -1810,7 +1807,7 @@ class WebGPUBackend extends Backend { } - this._draw( renderObject, info, renderContextData, pipelineGPU, vertexBuffers, drawParams, renderContextData.currentPass, renderContextData.currentSets ); + this._draw( renderObject, info, renderContextData, pipelineGPU, bindings, vertexBuffers, drawParams, renderContextData.currentPass, renderContextData.currentSets ); } From 2984131058a048f3e46823dea584eeaed6fc446a Mon Sep 17 00:00:00 2001 From: sunag Date: Thu, 5 Mar 2026 01:34:12 -0300 Subject: [PATCH 3/3] Inspector: Add `Capture Stack Trace` for `Settings` (#33122) --- examples/jsm/inspector/tabs/Settings.js | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/examples/jsm/inspector/tabs/Settings.js b/examples/jsm/inspector/tabs/Settings.js index 71037dcb42d65e..4bf887e9f41c71 100644 --- a/examples/jsm/inspector/tabs/Settings.js +++ b/examples/jsm/inspector/tabs/Settings.js @@ -1,5 +1,5 @@ import { Parameters } from './Parameters.js'; -import { WebGPURenderer, WebGLBackend } from 'three/webgpu'; +import { WebGPURenderer, WebGLBackend, Node } from 'three/webgpu'; const _init = WebGPURenderer.prototype.init; @@ -45,7 +45,8 @@ function loadState() { } const state = { - forceWebGL: settings.forceWebGL || false + forceWebGL: settings.forceWebGL || false, + captureStackTrace: settings.captureStackTrace || false }; return state; @@ -79,6 +80,14 @@ if ( state.forceWebGL ) { } +if ( state.captureStackTrace ) { + + Node.captureStackTrace = true; + +} + +// + class Settings extends Parameters { constructor() { @@ -98,6 +107,15 @@ class Settings extends Parameters { } ); + rendererGroup.add( state, 'captureStackTrace' ).name( 'Capture Stack Trace' ).onChange( ( enable ) => { + + Node.captureStackTrace = enable; + saveState( state ); + + location.reload(); + + } ); + } }