Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Binary file modified examples/screenshots/webgpu_compute_particles_rain.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/screenshots/webgpu_compute_particles_snow.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file modified examples/screenshots/webgpu_tsl_compute_attractors_particles.jpg
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
32 changes: 31 additions & 1 deletion src/nodes/core/ConstNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,14 @@ class ConstNode extends InputNode {
*/
this.isConstNode = true;

/**
* Whether this constant is an implicit number whose type can adapt to other operands.
*
* @type {boolean}
* @default false
*/
this.isWeak = false;

}

/**
Expand All @@ -54,14 +62,36 @@ class ConstNode extends InputNode {

if ( _regNum.test( type ) && _regNum.test( output ) ) {

return builder.generateConst( output, this.value );
let value = this.value;

// Preserve the declared integer value before converting to the output type.
if ( type === 'int' ) value = Math.trunc( value );
else if ( type === 'uint' ) value = value >= 0 ? Math.trunc( value ) : 0;

return builder.generateConst( output, value );

}

return builder.format( this.generateConst( builder ), type, output );

}

serialize( data ) {

super.serialize( data );

data.isWeak = this.isWeak;

}

deserialize( data ) {

super.deserialize( data );

this.isWeak = data.isWeak === true;

}

}

export default ConstNode;
64 changes: 62 additions & 2 deletions src/nodes/core/NodeBuilder.js
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,8 @@ const _toFloat = ( value ) => {

};

const _componentTypeRanks = { bool: 0, uint: 1, int: 2, float: 3 };

const _checkWriteUsage = ( data ) => {

if ( data.writeUsageCount > 0 ) return true;
Expand Down Expand Up @@ -1527,8 +1529,8 @@ class NodeBuilder {
}

if ( type === 'float' ) return _toFloat( value );
if ( type === 'int' ) return `${ Math.round( value ) }`;
if ( type === 'uint' ) return value >= 0 ? `${ Math.round( value ) }u` : '0u';
if ( type === 'int' ) return `${ Math.trunc( value ) }`;
if ( type === 'uint' ) return value >= 0 ? `${ Math.trunc( value ) }u` : '0u';
if ( type === 'bool' ) return value ? 'true' : 'false';
if ( type === 'color' ) return `${ this.getType( 'vec3' ) }( ${ _toFloat( value.r ) }, ${ _toFloat( value.g ) }, ${ _toFloat( value.b ) } )`;

Expand Down Expand Up @@ -1639,6 +1641,18 @@ class NodeBuilder {

}

/**
* Whether the given type is a scalar type or not.
*
* @param {string} type - The type to check.
* @return {boolean} Whether the given type is a scalar type or not.
*/
isScalar( type ) {

return type === 'float' || type === 'bool' || type === 'int' || type === 'uint';

}

/**
* Returns whether the given name is a reserved keyword of the backend's
* shading language. Backends override this method to provide their
Expand Down Expand Up @@ -1901,6 +1915,52 @@ class NodeBuilder {

}

/**
* Returns the common component type of the input nodes. Explicit types are
* promoted first, then weak constants adopt that type, being truncated like other
* integer constants. Only weak constants out of the integer range promote it.
* Inputs consisting only of weak constants default to float.
*
* @param {...?Node} nodes - The input nodes.
* @return {string} The promoted component type.
*/
getPromotedComponentType( ...nodes ) {

let type = null;
let hasWeak = false;
let fitsUint = true;
let fitsInt = true;

for ( const node of nodes ) {

if ( node === null ) continue;

if ( node.isWeak === true ) {

const value = Math.trunc( node.value );

hasWeak = true;
fitsUint = fitsUint && value >= 0 && value <= 0xffffffff;
fitsInt = fitsInt && value >= - 0x80000000 && value <= 0x7fffffff;

} else {

const componentType = this.getComponentType( node.getNodeType( this ) );

if ( type === null || _componentTypeRanks[ componentType ] > _componentTypeRanks[ type ] ) type = componentType;

}

}

if ( type === null || ( type === 'bool' && hasWeak ) ) return 'float';
if ( type === 'uint' && fitsUint === false ) return fitsInt ? 'int' : 'float';
if ( type === 'int' && fitsInt === false ) return 'float';

return type;

}

/**
* Returns the integer type pendant for the given type.
*
Expand Down
32 changes: 27 additions & 5 deletions src/nodes/math/MathNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,21 +109,31 @@ class MathNode extends Node {
const bLen = builder.isMatrix( bType ) ? 0 : builder.getTypeLength( bType );
const cLen = builder.isMatrix( cType ) ? 0 : builder.getTypeLength( cType );

let type;

if ( aLen > bLen && aLen > cLen ) {

return aType;
type = aType;

} else if ( bLen > cLen ) {

return bType;
type = bType;

} else if ( cLen > aLen ) {

return cType;
type = cType;

} else {

type = aType;

}

return aType;
if ( builder.isMatrix( type ) ) return type;

const promotedType = _floatMethods.has( this.method ) ? 'float' : builder.getPromotedComponentType( this.aNode, this.bNode, this.cNode );

return builder.changeComponentType( type, promotedType );

}

Expand Down Expand Up @@ -173,7 +183,7 @@ class MathNode extends Node {

} else if ( method === MathNode.RECIPROCAL ) {

outputNode = div( 1.0, aNode );
outputNode = div( float( 1 ), aNode );

} else if ( method === MathNode.DIFFERENCE ) {

Expand Down Expand Up @@ -391,6 +401,18 @@ MathNode.REFRACT = 'refract';
MathNode.SMOOTHSTEP = 'smoothstep';
MathNode.FACEFORWARD = 'faceforward';

// Methods that are only defined for floating-point types.

const _floatMethods = new Set( [
MathNode.RADIANS, MathNode.DEGREES, MathNode.EXP, MathNode.EXP2, MathNode.LOG, MathNode.LOG2,
MathNode.SQRT, MathNode.INVERSE_SQRT, MathNode.FLOOR, MathNode.CEIL, MathNode.NORMALIZE, MathNode.FRACT,
MathNode.SIN, MathNode.SINH, MathNode.COS, MathNode.COSH, MathNode.TAN, MathNode.TANH,
MathNode.ASIN, MathNode.ASINH, MathNode.ACOS, MathNode.ACOSH, MathNode.ATAN, MathNode.ATANH,
MathNode.LENGTH, MathNode.DFDX, MathNode.DFDY, MathNode.ROUND, MathNode.TRUNC, MathNode.FWIDTH, MathNode.RECIPROCAL,
MathNode.STEP, MathNode.REFLECT, MathNode.DISTANCE, MathNode.DOT, MathNode.CROSS, MathNode.POW,
MathNode.MIX, MathNode.REFRACT, MathNode.SMOOTHSTEP, MathNode.FACEFORWARD
] );

export default MathNode;

// 1 inputs
Expand Down
51 changes: 17 additions & 34 deletions src/nodes/math/OperatorNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -120,10 +120,6 @@ class OperatorNode extends Node {

return output || 'void';

} else if ( op === '%' ) {

return typeA;

} else if ( op === '~' || op === '&' || op === '|' || op === '^' || op === '>>' || op === '<<' ) {

return builder.getIntegerType( typeA );
Expand All @@ -144,13 +140,17 @@ class OperatorNode extends Node {

return typeLength > 1 ? `bvec${ typeLength }` : 'bool';

} else if ( typeB === null ) {

return typeA;

} else {

// Handle matrix operations

if ( builder.isMatrix( typeA ) ) {

if ( typeB === 'float' ) {
if ( builder.isScalar( typeB ) ) {

return typeA; // matrix * scalar = matrix

Expand All @@ -166,7 +166,7 @@ class OperatorNode extends Node {

} else if ( builder.isMatrix( typeB ) ) {

if ( typeA === 'float' ) {
if ( builder.isScalar( typeA ) ) {

return typeB; // scalar * matrix = matrix

Expand All @@ -180,15 +180,13 @@ class OperatorNode extends Node {

// Handle non-matrix cases

if ( builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ) {
// anytype x anytype: use the greater length vector

// anytype x anytype: use the greater length vector
const type = builder.getTypeLength( typeB ) > builder.getTypeLength( typeA ) ? typeB : typeA;

return typeB;
const promotedType = builder.getPromotedComponentType( aNode, bNode );

}

return typeA;
return builder.changeComponentType( type, promotedType );

}

Expand All @@ -212,33 +210,18 @@ class OperatorNode extends Node {

if ( op === '<' || op === '>' || op === '<=' || op === '>=' || op === '==' || op === '!=' ) {

if ( builder.isVector( typeA ) ) {

typeB = typeA;
const length = Math.max( builder.getTypeLength( typeA ), builder.getTypeLength( typeB ) );

} else if ( builder.isVector( typeB ) ) {

typeA = typeB;

} else if ( typeA !== typeB ) {

typeA = typeB = 'float';

}
typeA = typeB = builder.getTypeFromLength( length, builder.getPromotedComponentType( aNode, bNode ) );

} else if ( op === '>>' || op === '<<' ) {

typeA = type;
typeB = builder.changeComponentType( typeB, 'uint' );

} else if ( op === '%' ) {

typeA = type;
typeB = builder.isInteger( typeA ) && builder.isInteger( typeB ) ? typeB : typeA;

} else if ( builder.isMatrix( typeA ) ) {

if ( typeB === 'float' ) {
if ( builder.isScalar( typeB ) ) {

// Keep matrix type for typeA, but ensure typeB stays float

Expand All @@ -261,7 +244,7 @@ class OperatorNode extends Node {

} else if ( builder.isMatrix( typeB ) ) {

if ( typeA === 'float' ) {
if ( builder.isScalar( typeA ) ) {

// Keep matrix type for typeB, but ensure typeA stays float

Expand Down Expand Up @@ -362,11 +345,11 @@ class OperatorNode extends Node {

// Handle matrix operations

if ( builder.isMatrix( typeA ) && typeB === 'float' ) {
if ( builder.isMatrix( typeA ) && builder.isScalar( typeB ) ) {

return builder.format( `( ${ b } ${ op } ${ a } )`, type, output );

} else if ( typeA === 'float' && builder.isMatrix( typeB ) ) {
} else if ( builder.isScalar( typeA ) && builder.isMatrix( typeB ) ) {

return builder.format( `${ a } ${ op } ${ b }`, type, output );

Expand Down Expand Up @@ -394,7 +377,7 @@ class OperatorNode extends Node {

} else {

if ( builder.isMatrix( typeA ) && typeB === 'float' ) {
if ( builder.isMatrix( typeA ) && builder.isScalar( typeB ) ) {

return builder.format( `${ b } ${ op } ${ a }`, type, output );

Expand Down
11 changes: 9 additions & 2 deletions src/nodes/tsl/TSLCore.js
Original file line number Diff line number Diff line change
Expand Up @@ -883,7 +883,7 @@ for ( const float of floats ) floatsCacheMap.set( - float, new ConstNode( - floa

const cacheMaps = { bool: boolsCacheMap, uint: uintsCacheMap, ints: intsCacheMap, float: floatsCacheMap };

const constNodesCacheMap = new Map( [ ...boolsCacheMap, ...floatsCacheMap ] );
const constNodesCacheMap = new Map( boolsCacheMap );

const getConstNode = ( value, type ) => {

Expand All @@ -897,12 +897,19 @@ const getConstNode = ( value, type ) => {

} else {

return new ConstNode( value, type );
const node = new ConstNode( value, type );

// Implicit numbers are weak and can adapt to the type of other operands.
node.isWeak = ! type && typeof value === 'number';

return node;

}

};

for ( const value of floatsCacheMap.keys() ) constNodesCacheMap.set( value, getConstNode( value ) );

const ConvertType = function ( type, cacheMap = null ) {

return ( ...params ) => {
Expand Down
Loading
Loading