|
| 1 | +import TempNode from '../core/TempNode.js'; |
| 2 | +import { addMethodChaining, Fn, nodeProxyIntent, uint } from '../tsl/TSLCore.js'; |
| 3 | +import { bitcast } from './BitcastNode.js'; |
| 4 | +import MathNode from './MathNode.js'; |
| 5 | +/** |
| 6 | + * This node represents an operation that reinterprets the bit representation of a value |
| 7 | + * in one type as a value in another type. |
| 8 | + * |
| 9 | + * @augments TempNode |
| 10 | + */ |
| 11 | +class BitcountNode extends MathNode { |
| 12 | + |
| 13 | + static get type() { |
| 14 | + |
| 15 | + return 'BitcountNode'; |
| 16 | + |
| 17 | + } |
| 18 | + |
| 19 | + /** |
| 20 | + * Constructs a new math node. |
| 21 | + * |
| 22 | + * @param {string} method - The method name. |
| 23 | + * @param {Node} aNode - The first input. |
| 24 | + */ |
| 25 | + constructor( method, aNode ) { |
| 26 | + |
| 27 | + super( method, aNode ); |
| 28 | + |
| 29 | + /** |
| 30 | + * This flag can be used for type testing. |
| 31 | + * |
| 32 | + * @type {boolean} |
| 33 | + * @readonly |
| 34 | + * @default true |
| 35 | + */ |
| 36 | + this.isBitcountNode = true; |
| 37 | + |
| 38 | + } |
| 39 | + |
| 40 | + getNodeType( /*builder*/ ) { |
| 41 | + |
| 42 | + return 'uint'; |
| 43 | + |
| 44 | + } |
| 45 | + |
| 46 | + /** |
| 47 | + * Constructs a new math node. |
| 48 | + * |
| 49 | + * @param {NodeBuilder} builder - The method name. |
| 50 | + * @return {?Node} The output node. |
| 51 | + */ |
| 52 | + setup( builder ) { |
| 53 | + |
| 54 | + const { method, aNode } = this; |
| 55 | + |
| 56 | + const { renderer } = builder; |
| 57 | + |
| 58 | + if ( renderer.backend.isWebGPUBackend ) { |
| 59 | + |
| 60 | + return super.setup( builder ); |
| 61 | + |
| 62 | + } |
| 63 | + |
| 64 | + const inputType = this.getInputType(); |
| 65 | + const elementType = builder.getElementType( inputType ); |
| 66 | + |
| 67 | + const typeLength = builder.getTypeLength(); |
| 68 | + |
| 69 | + console.log( inputType, elementType, typeLength ); |
| 70 | + |
| 71 | + |
| 72 | + if ( method === BitcountNode.COUNT_LEADING_ZEROS ) { |
| 73 | + |
| 74 | + |
| 75 | + |
| 76 | + |
| 77 | + } else if ( method === BitcountNode.COUNT_TRAILING_ZEROS ) { |
| 78 | + |
| 79 | + |
| 80 | + |
| 81 | + } else if ( method === BitcountNode.COUNT_ONE_BITS ) { |
| 82 | + |
| 83 | + const bitCountBase = Fn( ( [ value ] ) => { |
| 84 | + |
| 85 | + const v = uint( 0.0 ); |
| 86 | + |
| 87 | + if ( elementType === 'int' ) { |
| 88 | + |
| 89 | + v.assign( bitcast( value, 'uint' ) ); |
| 90 | + |
| 91 | + } |
| 92 | + |
| 93 | + v.assign( v.sub( v.shiftRight( uint( 1 ) ).bitAnd( uint( 0x55555555 ) ) ) ); |
| 94 | + v.assign( v.bitAnd( uint( 0x33333333 ) ).add( v.shiftRight( uint( 2 ) ).bitAnd( uint( 0x33333333 ) ) ) ); |
| 95 | + |
| 96 | + return v.add( v.shiftRight( uint( 4 ) ) ).bitAnd( uint( 0xF0F0F0F ) ).mul( uint( 0x1010101 ) ).shiftRight( uint( 24 ) ); |
| 97 | + |
| 98 | + } ).setLayout( { |
| 99 | + name: `bitcount_${elementType}`, |
| 100 | + type: 'uint', |
| 101 | + inputs: [ |
| 102 | + { name: 'value', type: elementType } |
| 103 | + ] |
| 104 | + } ); |
| 105 | + |
| 106 | + |
| 107 | + const bitCountFn = Fn( ( [ value ] ) => { |
| 108 | + |
| 109 | + const v = uint( 0.0 ); |
| 110 | + |
| 111 | + if ( typeLength === 1 ) { |
| 112 | + |
| 113 | + v.addAssign( bitCountBase( value ) ); |
| 114 | + |
| 115 | + } else { |
| 116 | + |
| 117 | + const components = [ 'x', 'y', 'z', 'w' ]; |
| 118 | + |
| 119 | + for ( let i = 0; i < typeLength; i ++ ) { |
| 120 | + |
| 121 | + const component = components[ i ]; |
| 122 | + |
| 123 | + v.addAssign( bitCountBase( value[ component ] ) ); |
| 124 | + |
| 125 | + } |
| 126 | + |
| 127 | + } |
| 128 | + |
| 129 | + return v; |
| 130 | + |
| 131 | + } ).setLayout( { |
| 132 | + name: `bitcount_main_${this.nodeType}`, |
| 133 | + type: 'uint', |
| 134 | + inputs: [ |
| 135 | + { name: 'value', type: inputType } |
| 136 | + ] |
| 137 | + } ); |
| 138 | + |
| 139 | + console.log( 'test' ); |
| 140 | + |
| 141 | + const exec = bitCountFn( aNode ).toVar( 'testVar' ); |
| 142 | + |
| 143 | + return exec; |
| 144 | + |
| 145 | + } |
| 146 | + |
| 147 | + } |
| 148 | + |
| 149 | + generate( builder ) { |
| 150 | + |
| 151 | + this.method += 'test'; |
| 152 | + |
| 153 | + super.generate( builder ); |
| 154 | + |
| 155 | + |
| 156 | + |
| 157 | + } |
| 158 | + |
| 159 | +} |
| 160 | + |
| 161 | +export default BitcountNode; |
| 162 | + |
| 163 | +BitcountNode.COUNT_TRAILING_ZEROS = 'countTrailingZeros'; |
| 164 | +BitcountNode.COUNT_LEADING_ZEROS = 'countLeadingZeros'; |
| 165 | +BitcountNode.COUNT_ONE_BITS = 'countOneBits'; |
| 166 | + |
| 167 | +/** |
| 168 | + * Finds the number of consecutive 0 bits from the least significant bit of the input value, |
| 169 | + * which is also the index of the least significant bit of the input value. |
| 170 | + * |
| 171 | + * Can only be used with {@link WebGPURenderer} and a WebGPU backend. |
| 172 | + * |
| 173 | + * @tsl |
| 174 | + * @function |
| 175 | + * @param {Node | number} x - The input value. |
| 176 | + * @returns {Node} |
| 177 | + */ |
| 178 | +export const countTrailingZeros = /*@__PURE__*/ nodeProxyIntent( BitcountNode, BitcountNode.COUNT_TRAILING_ZEROS ).setParameterLength( 1 ); |
| 179 | + |
| 180 | +/** |
| 181 | + * Finds the number of consecutive 0 bits starting from the most significant bit of the input value. |
| 182 | + * |
| 183 | + * Can only be used with {@link WebGPURenderer} and a WebGPU backend. |
| 184 | + * |
| 185 | + * @tsl |
| 186 | + * @function |
| 187 | + * @param {Node | number} x - The input value. |
| 188 | + * @returns {Node} |
| 189 | + */ |
| 190 | +export const countLeadingZeros = /*@__PURE__*/ nodeProxyIntent( BitcountNode, BitcountNode.COUNT_LEADING_ZEROS ).setParameterLength( 1 ); |
| 191 | + |
| 192 | +/** |
| 193 | + * Finds the number of '1' bits set in the input value |
| 194 | + * |
| 195 | + * Can only be used with {@link WebGPURenderer} and a WebGPU backend. |
| 196 | + * |
| 197 | + * @tsl |
| 198 | + * @function |
| 199 | + * @returns {Node} |
| 200 | + */ |
| 201 | +export const countOneBits = /*@__PURE__*/ nodeProxyIntent( BitcountNode, BitcountNode.COUNT_ONE_BITS ).setParameterLength( 1 ); |
| 202 | + |
| 203 | +// GLSL alias function |
| 204 | + |
| 205 | +export const findLSB = countTrailingZeros; |
| 206 | +export const findMSB = countLeadingZeros; |
| 207 | +export const bitCount = countOneBits; |
| 208 | + |
| 209 | +addMethodChaining( 'countTrailingZeros', countTrailingZeros ); |
| 210 | +addMethodChaining( 'countLeadingZeros', countLeadingZeros ); |
| 211 | +addMethodChaining( 'countOneBits', countOneBits ); |
0 commit comments