Skip to content

Commit c1909c3

Browse files
committed
work
1 parent f07c613 commit c1909c3

File tree

2 files changed

+197
-4
lines changed

2 files changed

+197
-4
lines changed

examples/jsm/gpgpu/PrefixSum.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ export class PrefixSum {
5858
* Constructs a new light probe helper.
5959
*
6060
* @param {Renderer} renderer - A renderer with the ability to execute compute operations.
61-
* @param {number[]} inputArray - The data buffer to sum.
61+
* @param {number[] | TypedArray | StorageInstancedBufferAttribute } inputArray - The data buffer to sum.
6262
* @param {'uint' | 'float'} inputArrayType - Type of input array
6363
* @param {Object} [options={}] - Options that modify the behavior of the prefix sum.
6464
*/

examples/webgpu_compute_birds.html

Lines changed: 196 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,9 @@
3535
<script type="module">
3636

3737
import * as THREE from 'three/webgpu';
38-
import { uniform, varying, vec4, add, sub, max, dot, sin, mat3, uint, negate, instancedArray, cameraProjectionMatrix, cameraViewMatrix, positionLocal, modelWorldMatrix, sqrt, float, Fn, If, cos, Loop, Continue, normalize, instanceIndex, length, vertexIndex } from 'three/tsl';
39-
38+
import { uniform, varying, vec4, add, abs, sub, max, dot, sin, mat3, uint, negate, instancedArray, cameraProjectionMatrix, cameraViewMatrix, positionLocal, modelWorldMatrix, sqrt, float, Fn, If, cos, Loop, Continue, normalize, instanceIndex, length, vertexIndex } from 'three/tsl';
4039
import { Inspector } from 'three/addons/inspector/Inspector.js';
40+
import { PrefixSum } from 'three/addons/gpgpu/PrefixSum.js';
4141

4242
import { OrbitControls } from 'three/addons/controls/OrbitControls.js';
4343

@@ -49,9 +49,10 @@
4949
let last = performance.now();
5050

5151
let pointer, raycaster;
52-
let computeVelocity, computePosition, effectController;
52+
let computeVelocity, computePosition, effectController, computeHash;
5353

5454
const BIRDS = 16384;
55+
const TABLE_SIZE = BIRDS * 2;
5556
const SPEED_LIMIT = 9.0;
5657
const BOUNDS = 800, BOUNDS_HALF = BOUNDS / 2;
5758

@@ -175,6 +176,8 @@
175176
const velocityArray = new Float32Array( BIRDS * 3 );
176177
const phaseArray = new Float32Array( BIRDS );
177178

179+
180+
178181
for ( let i = 0; i < BIRDS; i ++ ) {
179182

180183
const posX = Math.random() * BOUNDS - BOUNDS_HALF;
@@ -287,6 +290,196 @@
287290

288291
// Define GPU Compute shaders.
289292
// Shaders are computationally identical to their GLSL counterparts outside of texture destructuring.
293+
const intCoord = Fn( ( [ x, spacing ] ) => {
294+
295+
return floor( x.div( spacing ) );
296+
297+
} ).setLayout( {
298+
name: 'intCoord',
299+
type: 'int',
300+
inputs: [
301+
{ name: 'x', type: 'float' },
302+
{ name: 'spacing', type: 'float' }
303+
]
304+
} );
305+
306+
const hash3DFloatCoord = Fn( ( [ v ] ) => {
307+
308+
const x = intCoord( v.x );
309+
const y = intCoord( v.y );
310+
const z = intCoord( v.z );
311+
312+
const xMul = x.mul( 92837111 );
313+
const yMul = y.mul( 689287499 );
314+
const zMul = z.mul( 283923481 );
315+
316+
const h = xMul.pow( yMul ).pow( zMul );
317+
318+
return abs( h ).modInt( TABLE_SIZE );
319+
320+
} ).setLayout( {
321+
name: 'hash3DCoord',
322+
type: 'int',
323+
inputs: [
324+
{ name: 'v', type: 'vec3' },
325+
{ name: 'spacing', type: 'float' }
326+
]
327+
} );
328+
329+
const hash3DIntCoord = Fn( ( [ x, y, z ] ) => {
330+
331+
const xMul = x.mul( 92837111 );
332+
const yMul = y.mul( 689287499 );
333+
const zMul = z.mul( 283923481 );
334+
335+
const h = xMul.pow( yMul ).pow( zMul );
336+
337+
return abs( h ).modInt( TABLE_SIZE );
338+
339+
} ).setLayout( {
340+
name: 'hash3DIntCoord',
341+
type: 'int',
342+
inputs: [
343+
{ name: 'x', type: 'int' },
344+
{ name: 'y', type: 'int'}
345+
{ name: 'z', type: 'int'}
346+
]
347+
} );
348+
349+
class Hash {
350+
351+
constructor( renderer, spacing, maxNumObjects ) {
352+
353+
this.spacing = spacing;
354+
this.tableSize = maxNumObjects * 2;
355+
this.cellStartArrayBuffer = new Int32Array( this.tableSize + 1 );
356+
this.cellStartStorage = instancedArray( this.cellStartArrayBuffer, 'int' );
357+
this.cellEntriesArrayBuffer = new Int32Array( maxNumObjects );
358+
this.cellEntriesStorage = instancedArray( this.cellEntriesArrayBuffer, 'int' );
359+
this.queryIdsArrayBuffer = new Int32Array( maxNumObjects );
360+
this.queryIdsStorage = instancedArray( this.queryIdsArrayBuffer, 'int' );
361+
this.querySize = 0;
362+
363+
// TODO: Check if faster to replace with .fill() operation
364+
this.resetFn = this._createResetFn( maxNumObjects );
365+
this.calculateHashFn = this._createCalculateHashFn( maxNumObjects );
366+
this.prefixSumModule = new PrefixSum( renderer, [ 1, 2, 3, 4, 5, 6, 7, 8 ], 'uint' );
367+
this.calculateCellEntriesFn = this._createCalculateCellEntriesFn( maxNumObjects );
368+
369+
}
370+
371+
_createResetFn( maxNumObjects ) {
372+
373+
const fnDef = Fn( () => {
374+
375+
const { cellStartStorage, cellEntriesStorage } = this;
376+
377+
cellStartStorage.element( instanceIndex ).assign( 0 );
378+
cellEntriesStorage.element( instanceIndex ).assign( 0 );
379+
380+
} )().compute( maxNumObjects );
381+
382+
return fnDef;
383+
384+
}
385+
386+
_createCalculateHashFn( maxNumObjects ) {
387+
388+
const fnDef = Fn( () => {
389+
390+
const { cellStartStorage } = this;
391+
392+
const hashedCoord = hash3DFloatCoord( positionStorage.element( instanceIndex ) );
393+
cellStartStorage.element( hashedCoord ).addAssign( 1 );
394+
395+
} )().compute( maxNumObjects );
396+
397+
return fnDef;
398+
399+
}
400+
401+
_createCalculateCellEntriesFn( maxNumObjects ) {
402+
403+
const fnDef = Fn( () => {
404+
405+
const { cellStartStorage, cellEntriesStorage } = this;
406+
407+
const hashedCoord = hash3DCoord( positionStorage.element( instanceIndex ) );
408+
cellStartStorage.element( hashedCoord ).subAssign( 1 );
409+
410+
// TODO: Prevent errors from two threads substracting the same cellStart entry at the same time
411+
const cellStart = cellStartStorage.element( hashedCoord ).toVar();
412+
cellEntriesStorage.element( cellStart ).assign( instanceIndex );
413+
414+
} )().compute( maxNumObjects );
415+
416+
return fnDef;
417+
418+
}
419+
420+
query() {
421+
422+
const fnDef = Fn( ( [ position, maxDist ] ) => {
423+
424+
const {queryIdsStorage, cellEntriesStorage} = this;
425+
426+
const x0 = intCoord( position.x.sub( maxDist ) );
427+
const y0 = intCoord( position.y.sub( maxDist ) );
428+
const z0 = intCoord( position.z.sub( maxDist ) );
429+
430+
const x1 = intCoord( position.x.sub( maxDist ) );
431+
const y1 = intCoord( position.y.sub( maxDist ) );
432+
const z1 = intCoord( position.z.sub( maxDist ) );
433+
434+
const xi = uint( x0 ).toVar();
435+
const yi = uint( y0 ).toVar();
436+
const zi = uint( z0 ).toVar();
437+
438+
const querySize = uint(0).toVar();
439+
440+
Loop( xi.lessThanEqual( x1 ), () => {
441+
442+
Loop( yi.lessThanEqual( y1 ), () => {
443+
444+
Loop( zi.lessThanEqual( z1 ), () => {
445+
446+
const hashedCoord = hash3DIntCoord(xi, yi, zi);
447+
const start = cellStartStorage.element(hashedCoord);
448+
const end = cellStartStorage.element(hashedCoord.add(1))
449+
450+
Loop({start: start, end: end, condition: '<', type: 'int'}, ({i}) => {
451+
452+
queryIdsStorage.element(querySize).assign(cellEntriesStorage.element(i))
453+
querySize.addAssign(1)
454+
455+
})
456+
457+
zi.addAssign( 1 );
458+
459+
} );
460+
461+
yi.addAssign( 1 );
462+
463+
} );
464+
465+
xi.addAssign( 1 );
466+
467+
} );
468+
469+
} ).setLayout( {
470+
name: 'queryPosition',
471+
type: 'float',
472+
inputs: [
473+
{ name: 'pos', type: 'vec3' },
474+
{ name: 'maxDist', type: 'float' }
475+
]
476+
} );
477+
478+
return fnDef;
479+
480+
}
481+
482+
}
290483

291484
computeVelocity = Fn( () => {
292485

0 commit comments

Comments
 (0)