Просмотр исходного кода

WebGPURenderer: Introduced `.toConst()`, `Const()`, `Var()` (#30251)

* WebGPURenderer: Introduced variable.toLet()

* change API to toConst()

* const/let both backends?

* const/let handle both backends

* cleanup code

* more cleanup

* cleanup

* add NodeBuilder.isDeterministic() and updates

* readonly suffix

* improve naming

* added `Const`, `Var`

---------

Co-authored-by: sunag <sunagbrasil@gmail.com>
Renaud Rohlinger 1 год назад
Родитель
Сommit
73eddcbc3e

+ 3 - 0
src/Three.TSL.js

@@ -114,6 +114,7 @@ export const colorSpaceToWorking = TSL.colorSpaceToWorking;
 export const colorToDirection = TSL.colorToDirection;
 export const compute = TSL.compute;
 export const cond = TSL.cond;
+export const Const = TSL.Const;
 export const context = TSL.context;
 export const convert = TSL.convert;
 export const convertColorSpace = TSL.convertColorSpace;
@@ -219,6 +220,7 @@ export const lights = TSL.lights;
 export const linearDepth = TSL.linearDepth;
 export const linearToneMapping = TSL.linearToneMapping;
 export const localId = TSL.localId;
+export const globalId = TSL.globalId;
 export const log = TSL.log;
 export const log2 = TSL.log2;
 export const logarithmicDepthToViewZ = TSL.logarithmicDepthToViewZ;
@@ -496,6 +498,7 @@ export const uv = TSL.uv;
 export const uvec2 = TSL.uvec2;
 export const uvec3 = TSL.uvec3;
 export const uvec4 = TSL.uvec4;
+export const Var = TSL.Var;
 export const varying = TSL.varying;
 export const varyingProperty = TSL.varyingProperty;
 export const vec2 = TSL.vec2;

+ 48 - 4
src/nodes/core/NodeBuilder.js

@@ -1604,9 +1604,11 @@ class NodeBuilder {
 	 * @param {String?} name - The variable's name.
 	 * @param {String} [type=node.getNodeType( this )] - The variable's type.
 	 * @param {('vertex'|'fragment'|'compute'|'any')} [shaderStage=this.shaderStage] - The shader stage.
+	 * @param {Boolean} [readOnly=false] - Whether the variable is read-only or not.
+	 *
 	 * @return {NodeVar} The node variable.
 	 */
-	getVarFromNode( node, name = null, type = node.getNodeType( this ), shaderStage = this.shaderStage ) {
+	getVarFromNode( node, name = null, type = node.getNodeType( this ), shaderStage = this.shaderStage, readOnly = false ) {
 
 		const nodeData = this.getDataFromNode( node, shaderStage );
 
@@ -1614,13 +1616,26 @@ class NodeBuilder {
 
 		if ( nodeVar === undefined ) {
 
+			const idNS = readOnly ? '_const' : '_var';
+
 			const vars = this.vars[ shaderStage ] || ( this.vars[ shaderStage ] = [] );
+			const id = this.vars[ idNS ] || ( this.vars[ idNS ] = 0 );
+
+			if ( name === null ) {
+
+				name = ( readOnly ? 'nodeConst' : 'nodeVar' ) + id;
+
+				this.vars[ idNS ] ++;
+
+			}
 
-			if ( name === null ) name = 'nodeVar' + vars.length;
+			nodeVar = new NodeVar( name, type, readOnly );
 
-			nodeVar = new NodeVar( name, type );
+			if ( ! readOnly ) {
 
-			vars.push( nodeVar );
+				vars.push( nodeVar );
+
+			}
 
 			nodeData.variable = nodeVar;
 
@@ -1630,6 +1645,35 @@ class NodeBuilder {
 
 	}
 
+	/**
+	 * Returns whether a Node or its flow is deterministic, useful for use in `const`.
+	 *
+	 * @param {Node} node - The varying node.
+	 * @return {Boolean} Returns true if deterministic.
+	 */
+	isDeterministic( node ) {
+
+		if ( node.isMathNode ) {
+
+			return this.isDeterministic( node.aNode ) &&
+				( node.bNode ? this.isDeterministic( node.bNode ) : true ) &&
+				( node.cNode ? this.isDeterministic( node.cNode ) : true );
+
+		} else if ( node.isOperatorNode ) {
+
+			return this.isDeterministic( node.aNode ) &&
+				( node.bNode ? this.isDeterministic( node.bNode ) : true );
+
+		} else if ( node.isConstNode ) {
+
+			return true;
+
+		}
+
+		return false;
+
+	}
+
 	/**
 	 * Returns an instance of {@link NodeVarying} for the given varying node.
 	 *

+ 9 - 1
src/nodes/core/NodeVar.js

@@ -11,8 +11,9 @@ class NodeVar {
 	 *
 	 * @param {String} name - The name of the variable.
 	 * @param {String} type - The type of the variable.
+	 * @param {Boolean} [readOnly=false] - The read-only flag.
 	 */
-	constructor( name, type ) {
+	constructor( name, type, readOnly = false ) {
 
 		/**
 		 * This flag can be used for type testing.
@@ -37,6 +38,13 @@ class NodeVar {
 		 */
 		this.type = type;
 
+		/**
+		 *  The read-only flag.
+		 *
+		 * @type {boolean}
+		 */
+		this.readOnly = readOnly;
+
 	}
 
 }

+ 75 - 7
src/nodes/core/VarNode.js

@@ -26,8 +26,9 @@ class VarNode extends Node {
 	 *
 	 * @param {Node} node - The node for which a variable should be created.
 	 * @param {String?} name - The name of the variable in the shader.
+	 * @param {Boolean?} readOnly - The read-only flag.
 	 */
-	constructor( node, name = null ) {
+	constructor( node, name = null, readOnly = false ) {
 
 		super();
 
@@ -64,6 +65,15 @@ class VarNode extends Node {
 		 */
 		this.isVarNode = true;
 
+		/**
+		 *
+		 * The read-only flag.
+		 *
+		 * @type {Boolean}
+		 * @default false
+		 */
+		this.readOnly = readOnly;
+
 	}
 
 	getHash( builder ) {
@@ -80,15 +90,50 @@ class VarNode extends Node {
 
 	generate( builder ) {
 
-		const { node, name } = this;
+		const { node, name, readOnly } = this;
+		const { renderer } = builder;
+
+		const isWebGPUBackend = renderer.backend.isWebGPUBackend === true;
+
+		let isDeterministic = false;
+		let shouldTreatAsReadOnly = false;
+
+		if ( readOnly ) {
+
+			isDeterministic = builder.isDeterministic( node );
 
-		const nodeVar = builder.getVarFromNode( this, name, builder.getVectorType( this.getNodeType( builder ) ) );
+			shouldTreatAsReadOnly = isWebGPUBackend ? readOnly : isDeterministic;
+
+		}
+
+		const vectorType = builder.getVectorType( this.getNodeType( builder ) );
+		const snippet = node.build( builder, vectorType );
+
+		const nodeVar = builder.getVarFromNode( this, name, vectorType, undefined, shouldTreatAsReadOnly );
 
 		const propertyName = builder.getPropertyName( nodeVar );
 
-		const snippet = node.build( builder, nodeVar.type );
+		let declarationPrefix = propertyName;
+
+		if ( shouldTreatAsReadOnly ) {
+
+			const type = builder.getType( nodeVar.type );
+
+			if ( isWebGPUBackend ) {
+
+				declarationPrefix = isDeterministic
+					? `const ${ propertyName }`
+					: `let ${ propertyName }`;
+
+			} else {
+
+				declarationPrefix = `const ${ type } ${ propertyName }`;
 
-		builder.addLineFlowCode( `${propertyName} = ${snippet}`, this );
+			}
+
+		}
+
+		builder.addLineFlowCode( `${ declarationPrefix } = ${ snippet }`, this );
 
 		return propertyName;
 
@@ -108,13 +153,36 @@ export default VarNode;
  */
 const createVar = /*@__PURE__*/ nodeProxy( VarNode );
 
-addMethodChaining( 'toVar', ( ...params ) => createVar( ...params ).append() );
+/**
+ * TSL function for creating a var node.
+ *
+ * @function
+ * @param {Node} node - The node for which a variable should be created.
+ * @param {String?} name - The name of the variable in the shader.
+ * @returns {VarNode}
+ */
+export const Var = ( node, name = null ) => createVar( node, name ).append();
+
+/**
+ * TSL function for creating a const node.
+ *
+ * @function
+ * @param {Node} node - The node for which a constant should be created.
+ * @param {String?} name - The name of the constant in the shader.
+ * @returns {VarNode}
+ */
+export const Const = ( node, name = null ) => createVar( node, name, true ).append();
+
+// Method chaining
+
+addMethodChaining( 'toVar', Var );
+addMethodChaining( 'toConst', Const );
 
 // Deprecated
 
 export const temp = ( node ) => { // @deprecated, r170
 
-	console.warn( 'TSL: "temp" is deprecated. Use ".toVar()" instead.' );
+	console.warn( 'TSL: "temp( node )" is deprecated. Use "Var( node )" or "node.toVar()" instead.' );
 
 	return createVar( node );
 

+ 8 - 0
src/nodes/gpgpu/ComputeBuiltinNode.js

@@ -204,6 +204,14 @@ export const numWorkgroups = /*@__PURE__*/ computeBuiltin( 'numWorkgroups', 'uve
  */
 export const workgroupId = /*@__PURE__*/ computeBuiltin( 'workgroupId', 'uvec3' );
 
+/**
+ * TSL function for creating a `globalId` builtin node. A non-linearized 3-dimensional
+ * representation of the current invocation's position within a 3D global grid.
+ *
+ * @function
+ * @returns {ComputeBuiltinNode<uvec3>}
+ */
+export const globalId = /*@__PURE__*/ computeBuiltin( 'globalId', 'uvec3' );
 /**
  * TSL function for creating a `localId` builtin node. A non-linearized 3-dimensional
  * representation of the current invocation's position within a 3D workgroup grid.

+ 9 - 0
src/nodes/math/MathNode.js

@@ -65,6 +65,15 @@ class MathNode extends TempNode {
 		 */
 		this.cNode = cNode;
 
+		/**
+		 * This flag can be used for type testing.
+		 *
+		 * @type {Boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isMathNode = true;
+
 	}
 
 	/**

+ 9 - 0
src/nodes/math/OperatorNode.js

@@ -65,6 +65,15 @@ class OperatorNode extends TempNode {
 		 */
 		this.bNode = bNode;
 
+		/**
+		 * This flag can be used for type testing.
+		 *
+		 * @type {Boolean}
+		 * @readonly
+		 * @default true
+		 */
+		this.isOperatorNode = true;
+
 	}
 
 	/**

+ 5 - 3
src/renderers/webgpu/WebGPUBackend.js

@@ -948,7 +948,7 @@ class WebGPUBackend extends Backend {
 
 		//
 
-		const encoder = device.createCommandEncoder( {} );
+		const encoder = device.createCommandEncoder( { label: 'clear' } );
 		const currentPass = encoder.beginRenderPass( {
 			colorAttachments,
 			depthStencilAttachment
@@ -973,11 +973,13 @@ class WebGPUBackend extends Backend {
 		const groupGPU = this.get( computeGroup );
 
 
-		const descriptor = {};
+		const descriptor = {
+			label: 'computeGroup_' + computeGroup.id
+		};
 
 		this.initTimestampQuery( computeGroup, descriptor );
 
-		groupGPU.cmdEncoderGPU = this.device.createCommandEncoder();
+		groupGPU.cmdEncoderGPU = this.device.createCommandEncoder( { label: 'computeGroup_' + computeGroup.id } );
 
 		groupGPU.passEncoderGPU = groupGPU.cmdEncoderGPU.beginComputePass( descriptor );
 

+ 3 - 3
src/renderers/webgpu/nodes/WGSLNodeBuilder.js

@@ -1364,7 +1364,7 @@ ${ flowData.code }
 
 		if ( shaderStage === 'compute' ) {
 
-			this.getBuiltin( 'global_invocation_id', 'id', 'vec3<u32>', 'attribute' );
+			this.getBuiltin( 'global_invocation_id', 'globalId', 'vec3<u32>', 'attribute' );
 			this.getBuiltin( 'workgroup_id', 'workgroupId', 'vec3<u32>', 'attribute' );
 			this.getBuiltin( 'local_invocation_id', 'localId', 'vec3<u32>', 'attribute' );
 			this.getBuiltin( 'num_workgroups', 'numWorkgroups', 'vec3<u32>', 'attribute' );
@@ -1729,7 +1729,7 @@ ${ flowData.code }
 
 					if ( flow.length > 0 ) flow += '\n';
 
-					flow += `\t// flow -> ${ slotName }\n\t`;
+					flow += `\t// flow -> ${ slotName }\n`;
 
 				}
 
@@ -2007,7 +2007,7 @@ ${shaderData.codes}
 fn main( ${shaderData.attributes} ) {
 
 	// system
-	instanceIndex = id.x + id.y * numWorkgroups.x * u32(${workgroupSize}) + id.z * numWorkgroups.x * numWorkgroups.y * u32(${workgroupSize});
+	instanceIndex = globalId.x + globalId.y * numWorkgroups.x * u32(${workgroupSize}) + globalId.z * numWorkgroups.x * numWorkgroups.y * u32(${workgroupSize});
 
 	// vars
 	${shaderData.vars}

粤ICP备19079148号