Skip to content

Commit f80cc65

Browse files
authored
Addons: GPGPU - Fix Bitonic Sort JSDoc and add Ping/Pong Buffers (#31949)
* add jsdoc * bitonic sort adjustments * testing, fixing, removing left over gui buttons
1 parent 27bed72 commit f80cc65

File tree

2 files changed

+82
-52
lines changed

2 files changed

+82
-52
lines changed

examples/jsm/gpgpu/BitonicSort.js

Lines changed: 82 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -74,15 +74,14 @@ export const getBitonicDisperseIndices = /*@__PURE__*/ Fn( ( [ index, swapSpan ]
7474
]
7575
} );
7676

77-
// TODO: Add parameters for computing a buffer larger than vec4
7877
export class BitonicSort {
7978

8079
/**
8180
* Constructs a new light probe helper.
8281
*
8382
* @param {Renderer} renderer - The current scene's renderer.
84-
* @param {StorageBufferNode} [size=1] - The size of the helper.
85-
* @param {Object} [options={}] - The size of the helper.
83+
* @param {StorageBufferNode} dataBuffer - The data buffer to sort.
84+
* @param {Object} [options={}] - Options that modify the bitonic sort.
8685
*/
8786
constructor( renderer, dataBuffer, options = {} ) {
8887

@@ -167,18 +166,31 @@ export class BitonicSort {
167166
this.stepCount = this._getStepCount();
168167

169168
/**
170-
* A compute shader that executes a 'flip' swap within a global address space on elements in the data buffer.
169+
* The number of the buffer being read from.
171170
*
172-
* @type {ComputeNode}
171+
* @type {string}
173172
*/
174-
this.flipGlobalFn = this._getFlipGlobal();
173+
this.readBufferName = 'Data';
175174

176175
/**
177-
* A compute shader that executes a 'disperse' swap within a global address space on elements in the data buffer.
176+
* An object containing compute shaders that execute a 'flip' swap within a global address space on elements in the data buffer.
178177
*
179-
* @type {ComputeNode}
178+
* @type {Object<string, ComputeNode>}
179+
*/
180+
this.flipGlobalNodes = {
181+
'Data': this._getFlipGlobal( this.dataBuffer, this.tempBuffer ),
182+
'Temp': this._getFlipGlobal( this.tempBuffer, this.dataBuffer )
183+
};
184+
185+
/**
186+
* An object containing compute shaders that execute a 'disperse' swap within a global address space on elements in the data buffer.
187+
*
188+
* @type {Object<string, ComputeNode>}
180189
*/
181-
this.disperseGlobalFn = this._getDisperseGlobal();
190+
this.disperseGlobalNodes = {
191+
'Data': this._getDisperseGlobal( this.dataBuffer, this.tempBuffer ),
192+
'Temp': this._getDisperseGlobal( this.tempBuffer, this.dataBuffer )
193+
};
182194

183195
/**
184196
* A compute shader that executes a sequence of flip and disperse swaps within a local address space on elements in the data buffer.
@@ -190,9 +202,12 @@ export class BitonicSort {
190202
/**
191203
* A compute shader that executes a sequence of disperse swaps within a local address space on elements in the data buffer.
192204
*
193-
* @type {ComputeNode}
205+
* @type {Object<string, ComputeNode>}
194206
*/
195-
this.disperseLocalFn = this._getDisperseLocal();
207+
this.disperseLocalNodes = {
208+
'Data': this._getDisperseLocal( this.dataBuffer ),
209+
'Temp': this._getDisperseLocal( this.tempBuffer ),
210+
};
196211

197212
// Utility functions
198213

@@ -248,6 +263,7 @@ export class BitonicSort {
248263
* Get total number of distinct swaps that occur in a bitonic sort.
249264
*
250265
* @private
266+
* @returns {number} - The total number of distinct swaps in a bitonic sort
251267
*/
252268
_getSwapOpCount() {
253269

@@ -260,6 +276,7 @@ export class BitonicSort {
260276
* Get the number of steps it takes to execute a complete bitonic sort.
261277
*
262278
* @private
279+
* @returns {number} The number of steps it takes to execute a complete bitonic sort.
263280
*/
264281
_getStepCount() {
265282

@@ -292,8 +309,12 @@ export class BitonicSort {
292309

293310
/**
294311
* Compares and swaps two data points in the data buffer within the global address space.
295-
*
312+
* @param {Node<uint>} idxBefore - The index of the first data element in the data buffer.
313+
* @param {Node<uint>} idxAfter - The index of the second data element in the data buffer.
314+
* @param {StorageBufferNode} dataBuffer - The buffer of data to read from.
315+
* @param {StorageBufferNode} tempBuffer - The buffer of data to write to.
296316
* @private
317+
*
297318
*/
298319
_globalCompareAndSwapTSL( idxBefore, idxAfter, dataBuffer, tempBuffer ) {
299320

@@ -309,6 +330,8 @@ export class BitonicSort {
309330
* Compares and swaps two data points in the data buffer within the local address space.
310331
*
311332
* @private
333+
* @param {Node<uint>} idxBefore - The index of the first data element in the data buffer.
334+
* @param {Node<uint>} idxAfter - The index of the second data element in the data buffer
312335
*/
313336
_localCompareAndSwapTSL( idxBefore, idxAfter ) {
314337

@@ -327,17 +350,20 @@ export class BitonicSort {
327350
* Create the compute shader that performs a global disperse swap on the data buffer.
328351
*
329352
* @private
353+
* @param {StorageBufferNode} readBuffer - The data buffer to read from.
354+
* @param {StorageBufferNode} writeBuffer - The data buffer to read from.
355+
* @returns {ComputeNode} - A compute shader that performs a global disperse swap on the data buffer.
330356
*/
331-
_getDisperseGlobal() {
357+
_getDisperseGlobal( readBuffer, writeBuffer ) {
332358

333-
const { infoStorage, tempBuffer, dataBuffer } = this;
359+
const { infoStorage } = this;
334360

335361
const currentSwapSpan = infoStorage.element( 1 );
336362

337363
const fnDef = Fn( () => {
338364

339365
const idx = getBitonicDisperseIndices( instanceIndex, currentSwapSpan );
340-
this._globalCompareAndSwapTSL( idx.x, idx.y, dataBuffer, tempBuffer );
366+
this._globalCompareAndSwapTSL( idx.x, idx.y, readBuffer, writeBuffer );
341367

342368
} )().compute( this.dispatchSize, [ this.workgroupSize ] );
343369

@@ -349,17 +375,20 @@ export class BitonicSort {
349375
* Create the compute shader that performs a global flip swap on the data buffer.
350376
*
351377
* @private
378+
* @param {StorageBufferNode} readBuffer - The data buffer to read from.
379+
* @param {StorageBufferNode} writeBuffer - The data buffer to read from.
380+
* @returns {ComputeNode} - A compute shader that executes a global flip swap.
352381
*/
353-
_getFlipGlobal() {
382+
_getFlipGlobal( readBuffer, writeBuffer ) {
354383

355-
const { infoStorage, tempBuffer, dataBuffer } = this;
384+
const { infoStorage } = this;
356385

357386
const currentSwapSpan = infoStorage.element( 1 );
358387

359388
const fnDef = Fn( () => {
360389

361390
const idx = getBitonicFlipIndices( instanceIndex, currentSwapSpan );
362-
this._globalCompareAndSwapTSL( idx.x, idx.y, dataBuffer, tempBuffer );
391+
this._globalCompareAndSwapTSL( idx.x, idx.y, readBuffer, writeBuffer );
363392

364393
} )().compute( this.dispatchSize, [ this.workgroupSize ] );
365394

@@ -372,6 +401,7 @@ export class BitonicSort {
372401
* Create the compute shader that performs a complete local swap on the data buffer.
373402
*
374403
* @private
404+
* @returns {ComputeNode} - A compute shader that executes a full local swap.
375405
*/
376406
_getSwapLocal() {
377407

@@ -440,10 +470,12 @@ export class BitonicSort {
440470
* Create the compute shader that performs a local disperse swap on the data buffer.
441471
*
442472
* @private
473+
* @param {StorageBufferNode} readWriteBuffer - The data buffer to read from and write to.
474+
* @returns {ComputeNode} - A compute shader that executes a local disperse swap.
443475
*/
444-
_getDisperseLocal() {
476+
_getDisperseLocal( readWriteBuffer ) {
445477

446-
const { localStorage, dataBuffer, workgroupSize } = this;
478+
const { localStorage, workgroupSize } = this;
447479

448480
const fnDef = Fn( () => {
449481

@@ -454,8 +486,8 @@ export class BitonicSort {
454486
const localID1 = invocationLocalIndex.mul( 2 );
455487
const localID2 = invocationLocalIndex.mul( 2 ).add( 1 );
456488

457-
localStorage.element( localID1 ).assign( dataBuffer.element( localOffset.add( localID1 ) ) );
458-
localStorage.element( localID2 ).assign( dataBuffer.element( localOffset.add( localID2 ) ) );
489+
localStorage.element( localID1 ).assign( readWriteBuffer.element( localOffset.add( localID1 ) ) );
490+
localStorage.element( localID2 ).assign( readWriteBuffer.element( localOffset.add( localID2 ) ) );
459491

460492
// Ensure that all local data has been populated
461493
workgroupBarrier();
@@ -477,8 +509,8 @@ export class BitonicSort {
477509
// Ensure that all invocations have swapped their own regions of data
478510
workgroupBarrier();
479511

480-
dataBuffer.element( localOffset.add( localID1 ) ).assign( localStorage.element( localID1 ) );
481-
dataBuffer.element( localOffset.add( localID2 ) ).assign( localStorage.element( localID2 ) );
512+
readWriteBuffer.element( localOffset.add( localID1 ) ).assign( localStorage.element( localID1 ) );
513+
readWriteBuffer.element( localOffset.add( localID2 ) ).assign( localStorage.element( localID2 ) );
482514

483515
} )().compute( this.dispatchSize, [ this.workgroupSize ] );
484516

@@ -490,6 +522,7 @@ export class BitonicSort {
490522
* Create the compute shader that resets the sort's algorithm information.
491523
*
492524
* @private
525+
* @returns {ComputeNode} - A compute shader that resets the bitonic sort's algorithm information.
493526
*/
494527
_getResetFn() {
495528

@@ -512,9 +545,10 @@ export class BitonicSort {
512545
}
513546

514547
/**
515-
* Create the compute shader that copies the state of the global swap to the data buffer.
548+
* Create the compute shader that copies the state of the last global swap to the data buffer.
516549
*
517550
* @private
551+
* @returns {ComputeNode} - A compute shader that copies the state of the last global swap to the data buffer.
518552
*/
519553
_getAlignFn() {
520554

@@ -533,9 +567,10 @@ export class BitonicSort {
533567
}
534568

535569
/**
536-
* Create the compute shader that sets the algorithm's information.
570+
* Create the compute shader that sets the bitonic sort algorithm's information.
537571
*
538572
* @private
573+
* @returns {ComputeNode} - A compute shader that sets the bitonic sort algorithm's information.
539574
*/
540575
_getSetAlgoFn() {
541576

@@ -603,15 +638,24 @@ export class BitonicSort {
603638

604639
const swapType = this.globalOpsRemaining === this.globalOpsInSpan ? 'Flip' : 'Disperse';
605640

606-
await renderer.computeAsync( swapType === 'Flip' ? this.flipGlobalFn : this.disperseGlobalFn );
607-
await renderer.computeAsync( this.alignFn );
641+
await renderer.computeAsync( swapType === 'Flip' ? this.flipGlobalNodes[ this.readBufferName ] : this.disperseGlobalNodes[ this.readBufferName ] );
642+
643+
if ( this.readBufferName === 'Data' ) {
644+
645+
this.readBufferName = 'Temp';
646+
647+
} else {
648+
649+
this.readBufferName = 'Data';
650+
651+
}
608652

609653
this.globalOpsRemaining -= 1;
610654

611655
} else {
612656

613657
// Then run local disperses when we've finished all global swaps
614-
await renderer.computeAsync( this.disperseLocalFn );
658+
await renderer.computeAsync( this.disperseLocalNodes[ this.readBufferName ] );
615659

616660
const nextSpanGlobalOps = this.globalOpsInSpan + 1;
617661
this.globalOpsInSpan = nextSpanGlobalOps;
@@ -624,6 +668,15 @@ export class BitonicSort {
624668

625669
if ( this.currentDispatch === this.stepCount ) {
626670

671+
// If our last swap addressed only addressed the temp buffer, then re-allign it with the data buffer
672+
// to fulfill the requirement of an in-place sort.
673+
if ( this.readBufferName === 'Temp' ) {
674+
675+
await renderer.computeAsync( this.alignFn );
676+
this.readBufferName = 'Data';
677+
678+
}
679+
627680
// Just reset the algorithm information
628681
await renderer.computeAsync( this.resetFn );
629682

examples/webgpu_compute_sort_bitonic.html

Lines changed: 0 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -325,7 +325,6 @@
325325
currentElementsStorage,
326326
{
327327
workgroupSize: 64,
328-
sideEffectBuffers: [ 2, 3 ]
329328
}
330329
);
331330

@@ -337,28 +336,6 @@
337336

338337
await renderer.computeAsync( computeInit );
339338

340-
gui.add( effectController, 'stepBitonic' ).onChange( async () => {
341-
342-
if ( currentStep < bitonicSortModule.stepCount ) {
343-
344-
await bitonicSortModule.computeStep( renderer );
345-
346-
currentStep ++;
347-
348-
} else {
349-
350-
await renderer.computeAsync( computeReset );
351-
352-
currentStep = 0;
353-
354-
}
355-
356-
timestamps[ 'local_swap' ].innerHTML = constructInnerHTML( false, localColors );
357-
358-
} );
359-
360-
361-
362339
const stepAnimation = async function () {
363340

364341
renderer.info.reset();

0 commit comments

Comments
 (0)