BitonicSort.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715
  1. import { Fn, uvec2, If, instancedArray, instanceIndex, invocationLocalIndex, Loop, workgroupArray, workgroupBarrier, workgroupId, uint, select, min, max } from 'three/tsl';
  2. const StepType = {
  3. NONE: 0,
  4. // Swap all values within the local range of workgroupSize * 2
  5. SWAP_LOCAL: 1,
  6. DISPERSE_LOCAL: 2,
  7. // Swap values within global data buffer.
  8. FLIP_GLOBAL: 3,
  9. DISPERSE_GLOBAL: 4,
  10. };
  11. /**
  12. * Returns the indices that will be compared in a bitonic flip operation.
  13. *
  14. * @tsl
  15. * @private
  16. * @param {Node<uint>} index - The compute thread's invocation id.
  17. * @param {Node<uint>} blockHeight - The height of the block within which elements are being swapped.
  18. * @returns {Node<uvec2>} The indices of the elements in the data buffer being compared.
  19. */
  20. export const getBitonicFlipIndices = /*@__PURE__*/ Fn( ( [ index, blockHeight ] ) => {
  21. const blockOffset = ( index.mul( 2 ).div( blockHeight ) ).mul( blockHeight );
  22. const halfHeight = blockHeight.div( 2 );
  23. const idx = uvec2(
  24. index.mod( halfHeight ),
  25. blockHeight.sub( index.mod( halfHeight ) ).sub( 1 )
  26. );
  27. idx.x.addAssign( blockOffset );
  28. idx.y.addAssign( blockOffset );
  29. return idx;
  30. } ).setLayout( {
  31. name: 'getBitonicFlipIndices',
  32. type: 'uvec2',
  33. inputs: [
  34. { name: 'index', type: 'uint' },
  35. { name: 'blockHeight', type: 'uint' }
  36. ]
  37. } );
  38. /**
  39. * Returns the indices that will be compared in a bitonic sort's disperse operation.
  40. *
  41. * @tsl
  42. * @private
  43. * @param {Node<uint>} index - The compute thread's invocation id.
  44. * @param {Node<uint>} swapSpan - The maximum span over which elements are being swapped.
  45. * @returns {Node<uvec2>} The indices of the elements in the data buffer being compared.
  46. */
  47. export const getBitonicDisperseIndices = /*@__PURE__*/ Fn( ( [ index, swapSpan ] ) => {
  48. const blockOffset = ( ( index.mul( 2 ) ).div( swapSpan ) ).mul( swapSpan );
  49. const halfHeight = swapSpan.div( 2 );
  50. const idx = uvec2(
  51. index.mod( halfHeight ),
  52. ( index.mod( halfHeight ) ).add( halfHeight )
  53. );
  54. idx.x.addAssign( blockOffset );
  55. idx.y.addAssign( blockOffset );
  56. return idx;
  57. } ).setLayout( {
  58. name: 'getBitonicDisperseIndices',
  59. type: 'uvec2',
  60. inputs: [
  61. { name: 'index', type: 'uint' },
  62. { name: 'blockHeight', type: 'uint' }
  63. ]
  64. } );
  65. export class BitonicSort {
  66. /**
  67. * Constructs a new light probe helper.
  68. *
  69. * @param {Renderer} renderer - The current scene's renderer.
  70. * @param {StorageBufferNode} dataBuffer - The data buffer to sort.
  71. * @param {Object} [options={}] - Options that modify the bitonic sort.
  72. */
  73. constructor( renderer, dataBuffer, options = {} ) {
  74. /**
  75. * A reference to the renderer.
  76. *
  77. * @type {Renderer}
  78. */
  79. this.renderer = renderer;
  80. /**
  81. * A reference to the StorageBufferNode holding the data that will be sorted .
  82. *
  83. * @type {StorageBufferNode}
  84. */
  85. this.dataBuffer = dataBuffer;
  86. /**
  87. * The size of the data.
  88. *
  89. * @type {StorageBufferNode}
  90. */
  91. this.count = dataBuffer.value.count;
  92. /**
  93. *
  94. * The size of each compute dispatch.
  95. * @type {number}
  96. */
  97. this.dispatchSize = this.count / 2;
  98. /**
  99. * The workgroup size of the compute shaders executed during the sort.
  100. *
  101. * @type {StorageBufferNode}
  102. */
  103. this.workgroupSize = options.workgroupSize ? Math.min( this.dispatchSize, options.workgroupSize ) : Math.min( this.dispatchSize, 64 );
  104. /**
  105. * A node representing a workgroup scoped buffer that holds locally sorted elements.
  106. *
  107. * @type {WorkgroupInfoNode}
  108. */
  109. this.localStorage = workgroupArray( dataBuffer.nodeType, this.workgroupSize * 2 );
  110. this._tempArray = new Uint32Array( this.count );
  111. for ( let i = 0; i < this.count; i ++ ) {
  112. this._tempArray[ i ] = 0;
  113. }
  114. /**
  115. * A node representing a storage buffer used for transferring the result of the global sort back to the original data buffer.
  116. *
  117. * @type {StorageBufferNode}
  118. */
  119. this.tempBuffer = instancedArray( this.count, dataBuffer.nodeType ).setName( 'TempStorage' );
  120. /**
  121. * A node containing the current algorithm type, the current swap span, and the highest swap span.
  122. *
  123. * @type {StorageBufferNode}
  124. */
  125. this.infoStorage = instancedArray( new Uint32Array( [ 1, 2, 2 ] ), 'uint' ).setName( 'BitonicSortInfo' );
  126. /**
  127. * The number of distinct swap operations ('flips' and 'disperses') executed in an in-place
  128. * bitonic sort of the current data buffer.
  129. *
  130. * @type {number}
  131. */
  132. this.swapOpCount = this._getSwapOpCount();
  133. /**
  134. * The number of steps (i.e prepping and/or executing a swap) needed to fully execute an in-place bitonic sort of the current data buffer.
  135. *
  136. * @type {number}
  137. */
  138. this.stepCount = this._getStepCount();
  139. /**
  140. * The number of the buffer being read from.
  141. *
  142. * @type {string}
  143. */
  144. this.readBufferName = 'Data';
  145. /**
  146. * An object containing compute shaders that execute a 'flip' swap within a global address space on elements in the data buffer.
  147. *
  148. * @type {Object<string, ComputeNode>}
  149. */
  150. this.flipGlobalNodes = {
  151. 'Data': this._getFlipGlobal( this.dataBuffer, this.tempBuffer ),
  152. 'Temp': this._getFlipGlobal( this.tempBuffer, this.dataBuffer )
  153. };
  154. /**
  155. * An object containing compute shaders that execute a 'disperse' swap within a global address space on elements in the data buffer.
  156. *
  157. * @type {Object<string, ComputeNode>}
  158. */
  159. this.disperseGlobalNodes = {
  160. 'Data': this._getDisperseGlobal( this.dataBuffer, this.tempBuffer ),
  161. 'Temp': this._getDisperseGlobal( this.tempBuffer, this.dataBuffer )
  162. };
  163. /**
  164. * A compute shader that executes a sequence of flip and disperse swaps within a local address space on elements in the data buffer.
  165. *
  166. * @type {ComputeNode}
  167. */
  168. this.swapLocalFn = this._getSwapLocal();
  169. /**
  170. * A compute shader that executes a sequence of disperse swaps within a local address space on elements in the data buffer.
  171. *
  172. * @type {Object<string, ComputeNode>}
  173. */
  174. this.disperseLocalNodes = {
  175. 'Data': this._getDisperseLocal( this.dataBuffer ),
  176. 'Temp': this._getDisperseLocal( this.tempBuffer ),
  177. };
  178. // Utility functions
  179. /**
  180. * A compute shader that sets up the algorithm and the swap span for the next swap operation.
  181. *
  182. * @type {ComputeNode}
  183. */
  184. this.setAlgoFn = this._getSetAlgoFn();
  185. /**
  186. * A compute shader that aligns the result of the global swap operation with the current buffer.
  187. *
  188. * @type {ComputeNode}
  189. */
  190. this.alignFn = this._getAlignFn();
  191. /**
  192. * A compute shader that resets the algorithm and swap span information.
  193. *
  194. * @type {ComputeNode}
  195. */
  196. this.resetFn = this._getResetFn();
  197. /**
  198. * The current compute shader dispatch within the list of dispatches needed to complete the sort.
  199. *
  200. * @type {number}
  201. */
  202. this.currentDispatch = 0;
  203. /**
  204. * The number of global swap operations that must be executed before the sort
  205. * can swap in local address space.
  206. *
  207. * @type {number}
  208. */
  209. this.globalOpsRemaining = 0;
  210. /**
  211. * The total number of global operations needed to sort elements within the current swap span.
  212. *
  213. * @type {number}
  214. */
  215. this.globalOpsInSpan = 0;
  216. }
  217. /**
  218. * Get total number of distinct swaps that occur in a bitonic sort.
  219. *
  220. * @private
  221. * @returns {number} - The total number of distinct swaps in a bitonic sort
  222. */
  223. _getSwapOpCount() {
  224. const n = Math.log2( this.count );
  225. return ( n * ( n + 1 ) ) / 2;
  226. }
  227. /**
  228. * Get the number of steps it takes to execute a complete bitonic sort.
  229. *
  230. * @private
  231. * @returns {number} The number of steps it takes to execute a complete bitonic sort.
  232. */
  233. _getStepCount() {
  234. const logElements = Math.log2( this.count );
  235. const logSwapSpan = Math.log2( this.workgroupSize * 2 );
  236. const numGlobalFlips = logElements - logSwapSpan;
  237. // Start with 1 for initial sort over all local elements
  238. let numSteps = 1;
  239. let numGlobalDisperses = 0;
  240. for ( let i = 1; i <= numGlobalFlips; i ++ ) {
  241. // Increment by the global flip that starts each global block
  242. numSteps += 1;
  243. // Increment by number of global disperses following the global flip
  244. numSteps += numGlobalDisperses;
  245. // Increment by local disperse that occurs after all global swaps are finished
  246. numSteps += 1;
  247. // Number of global disperse increases as swapSpan increases by factor of 2
  248. numGlobalDisperses += 1;
  249. }
  250. return numSteps;
  251. }
  252. /**
  253. * Compares and swaps two data points in the data buffer within the global address space.
  254. * @param {Node<uint>} idxBefore - The index of the first data element in the data buffer.
  255. * @param {Node<uint>} idxAfter - The index of the second data element in the data buffer.
  256. * @param {StorageBufferNode} dataBuffer - The buffer of data to read from.
  257. * @param {StorageBufferNode} tempBuffer - The buffer of data to write to.
  258. * @private
  259. *
  260. */
  261. _globalCompareAndSwapTSL( idxBefore, idxAfter, dataBuffer, tempBuffer ) {
  262. const data1 = dataBuffer.element( idxBefore );
  263. const data2 = dataBuffer.element( idxAfter );
  264. tempBuffer.element( idxBefore ).assign( min( data1, data2 ) );
  265. tempBuffer.element( idxAfter ).assign( max( data1, data2 ) );
  266. }
  267. /**
  268. * Compares and swaps two data points in the data buffer within the local address space.
  269. *
  270. * @private
  271. * @param {Node<uint>} idxBefore - The index of the first data element in the data buffer.
  272. * @param {Node<uint>} idxAfter - The index of the second data element in the data buffer
  273. */
  274. _localCompareAndSwapTSL( idxBefore, idxAfter ) {
  275. const { localStorage } = this;
  276. const data1 = localStorage.element( idxBefore ).toVar();
  277. const data2 = localStorage.element( idxAfter ).toVar();
  278. localStorage.element( idxBefore ).assign( min( data1, data2 ) );
  279. localStorage.element( idxAfter ).assign( max( data1, data2 ) );
  280. }
  281. /**
  282. * Create the compute shader that performs a global disperse swap on the data buffer.
  283. *
  284. * @private
  285. * @param {StorageBufferNode} readBuffer - The data buffer to read from.
  286. * @param {StorageBufferNode} writeBuffer - The data buffer to read from.
  287. * @returns {ComputeNode} - A compute shader that performs a global disperse swap on the data buffer.
  288. */
  289. _getDisperseGlobal( readBuffer, writeBuffer ) {
  290. const { infoStorage } = this;
  291. const currentSwapSpan = infoStorage.element( 1 );
  292. const fnDef = Fn( () => {
  293. const idx = getBitonicDisperseIndices( instanceIndex, currentSwapSpan );
  294. this._globalCompareAndSwapTSL( idx.x, idx.y, readBuffer, writeBuffer );
  295. } )().compute( this.dispatchSize, [ this.workgroupSize ] );
  296. return fnDef;
  297. }
  298. /**
  299. * Create the compute shader that performs a global flip swap on the data buffer.
  300. *
  301. * @private
  302. * @param {StorageBufferNode} readBuffer - The data buffer to read from.
  303. * @param {StorageBufferNode} writeBuffer - The data buffer to read from.
  304. * @returns {ComputeNode} - A compute shader that executes a global flip swap.
  305. */
  306. _getFlipGlobal( readBuffer, writeBuffer ) {
  307. const { infoStorage } = this;
  308. const currentSwapSpan = infoStorage.element( 1 );
  309. const fnDef = Fn( () => {
  310. const idx = getBitonicFlipIndices( instanceIndex, currentSwapSpan );
  311. this._globalCompareAndSwapTSL( idx.x, idx.y, readBuffer, writeBuffer );
  312. } )().compute( this.dispatchSize, [ this.workgroupSize ] );
  313. return fnDef;
  314. }
  315. /**
  316. * Create the compute shader that performs a complete local swap on the data buffer.
  317. *
  318. * @private
  319. * @returns {ComputeNode} - A compute shader that executes a full local swap.
  320. */
  321. _getSwapLocal() {
  322. const { localStorage, dataBuffer, workgroupSize } = this;
  323. const fnDef = Fn( () => {
  324. // Get ids of indices needed to populate workgroup local buffer.
  325. // Use .toVar() to prevent these values from being recalculated multiple times.
  326. const localOffset = uint( workgroupSize ).mul( 2 ).mul( workgroupId.x ).toVar();
  327. const localID1 = invocationLocalIndex.mul( 2 );
  328. const localID2 = invocationLocalIndex.mul( 2 ).add( 1 );
  329. localStorage.element( localID1 ).assign( dataBuffer.element( localOffset.add( localID1 ) ) );
  330. localStorage.element( localID2 ).assign( dataBuffer.element( localOffset.add( localID2 ) ) );
  331. // Ensure that all local data has been populated
  332. workgroupBarrier();
  333. // Perform a chunk of the sort in a single pass that operates entirely in workgroup local space
  334. // SWAP_LOCAL will always be first pass, so we start with known block height of 2
  335. const flipBlockHeight = uint( 2 );
  336. Loop( { start: uint( 2 ), end: uint( workgroupSize * 2 ), type: 'uint', condition: '<=', update: '<<= 1' }, () => {
  337. // Ensure that last dispatch block executed
  338. workgroupBarrier();
  339. const flipIdx = getBitonicFlipIndices( invocationLocalIndex, flipBlockHeight );
  340. this._localCompareAndSwapTSL( flipIdx.x, flipIdx.y );
  341. const localBlockHeight = flipBlockHeight.div( 2 );
  342. Loop( { start: localBlockHeight, end: uint( 1 ), type: 'uint', condition: '>', update: '>>= 1' }, () => {
  343. // Ensure that last dispatch op executed
  344. workgroupBarrier();
  345. const disperseIdx = getBitonicDisperseIndices( invocationLocalIndex, localBlockHeight );
  346. this._localCompareAndSwapTSL( disperseIdx.x, disperseIdx.y );
  347. localBlockHeight.divAssign( 2 );
  348. } );
  349. // flipBlockHeight *= 2;
  350. flipBlockHeight.shiftLeftAssign( 1 );
  351. } );
  352. // Ensure that all invocations have swapped their own regions of data
  353. workgroupBarrier();
  354. dataBuffer.element( localOffset.add( localID1 ) ).assign( localStorage.element( localID1 ) );
  355. dataBuffer.element( localOffset.add( localID2 ) ).assign( localStorage.element( localID2 ) );
  356. } )().compute( this.dispatchSize, [ this.workgroupSize ] );
  357. return fnDef;
  358. }
  359. /**
  360. * Create the compute shader that performs a local disperse swap on the data buffer.
  361. *
  362. * @private
  363. * @param {StorageBufferNode} readWriteBuffer - The data buffer to read from and write to.
  364. * @returns {ComputeNode} - A compute shader that executes a local disperse swap.
  365. */
  366. _getDisperseLocal( readWriteBuffer ) {
  367. const { localStorage, workgroupSize } = this;
  368. const fnDef = Fn( () => {
  369. // Get ids of indices needed to populate workgroup local buffer.
  370. // Use .toVar() to prevent these values from being recalculated multiple times.
  371. const localOffset = uint( workgroupSize ).mul( 2 ).mul( workgroupId.x ).toVar();
  372. const localID1 = invocationLocalIndex.mul( 2 );
  373. const localID2 = invocationLocalIndex.mul( 2 ).add( 1 );
  374. localStorage.element( localID1 ).assign( readWriteBuffer.element( localOffset.add( localID1 ) ) );
  375. localStorage.element( localID2 ).assign( readWriteBuffer.element( localOffset.add( localID2 ) ) );
  376. // Ensure that all local data has been populated
  377. workgroupBarrier();
  378. const localBlockHeight = uint( workgroupSize * 2 );
  379. Loop( { start: localBlockHeight, end: uint( 1 ), type: 'uint', condition: '>', update: '>>= 1' }, () => {
  380. // Ensure that last dispatch op executed
  381. workgroupBarrier();
  382. const disperseIdx = getBitonicDisperseIndices( invocationLocalIndex, localBlockHeight );
  383. this._localCompareAndSwapTSL( disperseIdx.x, disperseIdx.y );
  384. localBlockHeight.divAssign( 2 );
  385. } );
  386. // Ensure that all invocations have swapped their own regions of data
  387. workgroupBarrier();
  388. readWriteBuffer.element( localOffset.add( localID1 ) ).assign( localStorage.element( localID1 ) );
  389. readWriteBuffer.element( localOffset.add( localID2 ) ).assign( localStorage.element( localID2 ) );
  390. } )().compute( this.dispatchSize, [ this.workgroupSize ] );
  391. return fnDef;
  392. }
  393. /**
  394. * Create the compute shader that resets the sort's algorithm information.
  395. *
  396. * @private
  397. * @returns {ComputeNode} - A compute shader that resets the bitonic sort's algorithm information.
  398. */
  399. _getResetFn() {
  400. const fnDef = Fn( () => {
  401. const { infoStorage } = this;
  402. const currentAlgo = infoStorage.element( 0 );
  403. const currentSwapSpan = infoStorage.element( 1 );
  404. const maxSwapSpan = infoStorage.element( 2 );
  405. currentAlgo.assign( StepType.SWAP_LOCAL );
  406. currentSwapSpan.assign( 2 );
  407. maxSwapSpan.assign( 2 );
  408. } )().compute( 1 );
  409. return fnDef;
  410. }
  411. /**
  412. * Create the compute shader that copies the state of the last global swap to the data buffer.
  413. *
  414. * @private
  415. * @returns {ComputeNode} - A compute shader that copies the state of the last global swap to the data buffer.
  416. */
  417. _getAlignFn() {
  418. const { dataBuffer, tempBuffer } = this;
  419. // TODO: Only do this in certain instances by ping-ponging which buffer gets sorted
  420. // And only aligning if numDispatches % 2 === 1
  421. const fnDef = Fn( () => {
  422. dataBuffer.element( instanceIndex ).assign( tempBuffer.element( instanceIndex ) );
  423. } )().compute( this.count, [ this.workgroupSize ] );
  424. return fnDef;
  425. }
  426. /**
  427. * Create the compute shader that sets the bitonic sort algorithm's information.
  428. *
  429. * @private
  430. * @returns {ComputeNode} - A compute shader that sets the bitonic sort algorithm's information.
  431. */
  432. _getSetAlgoFn() {
  433. const fnDef = Fn( () => {
  434. const { infoStorage, workgroupSize } = this;
  435. const currentAlgo = infoStorage.element( 0 );
  436. const currentSwapSpan = infoStorage.element( 1 );
  437. const maxSwapSpan = infoStorage.element( 2 );
  438. If( currentAlgo.equal( StepType.SWAP_LOCAL ), () => {
  439. const nextHighestSwapSpan = uint( workgroupSize * 4 );
  440. currentAlgo.assign( StepType.FLIP_GLOBAL );
  441. currentSwapSpan.assign( nextHighestSwapSpan );
  442. maxSwapSpan.assign( nextHighestSwapSpan );
  443. } ).ElseIf( currentAlgo.equal( StepType.DISPERSE_LOCAL ), () => {
  444. currentAlgo.assign( StepType.FLIP_GLOBAL );
  445. const nextHighestSwapSpan = maxSwapSpan.mul( 2 );
  446. currentSwapSpan.assign( nextHighestSwapSpan );
  447. maxSwapSpan.assign( nextHighestSwapSpan );
  448. } ).Else( () => {
  449. const nextSwapSpan = currentSwapSpan.div( 2 );
  450. currentAlgo.assign(
  451. select(
  452. nextSwapSpan.lessThanEqual( uint( workgroupSize * 2 ) ),
  453. StepType.DISPERSE_LOCAL,
  454. StepType.DISPERSE_GLOBAL
  455. ).uniformFlow()
  456. );
  457. currentSwapSpan.assign( nextSwapSpan );
  458. } );
  459. } )().compute( 1 );
  460. return fnDef;
  461. }
  462. /**
  463. * Executes a step of the bitonic sort operation.
  464. *
  465. * @param {Renderer} renderer - The current scene's renderer.
  466. */
  467. computeStep( renderer ) {
  468. // Swap local only runs once
  469. if ( this.currentDispatch === 0 ) {
  470. renderer.compute( this.swapLocalFn );
  471. this.globalOpsRemaining = 1;
  472. this.globalOpsInSpan = 1;
  473. } else if ( this.globalOpsRemaining > 0 ) {
  474. const swapType = this.globalOpsRemaining === this.globalOpsInSpan ? 'Flip' : 'Disperse';
  475. renderer.compute( swapType === 'Flip' ? this.flipGlobalNodes[ this.readBufferName ] : this.disperseGlobalNodes[ this.readBufferName ] );
  476. if ( this.readBufferName === 'Data' ) {
  477. this.readBufferName = 'Temp';
  478. } else {
  479. this.readBufferName = 'Data';
  480. }
  481. this.globalOpsRemaining -= 1;
  482. } else {
  483. // Then run local disperses when we've finished all global swaps
  484. renderer.compute( this.disperseLocalNodes[ this.readBufferName ] );
  485. const nextSpanGlobalOps = this.globalOpsInSpan + 1;
  486. this.globalOpsInSpan = nextSpanGlobalOps;
  487. this.globalOpsRemaining = nextSpanGlobalOps;
  488. }
  489. this.currentDispatch += 1;
  490. if ( this.currentDispatch === this.stepCount ) {
  491. // If our last swap addressed only addressed the temp buffer, then re-align it with the data buffer
  492. // to fulfill the requirement of an in-place sort.
  493. if ( this.readBufferName === 'Temp' ) {
  494. renderer.compute( this.alignFn );
  495. this.readBufferName = 'Data';
  496. }
  497. // Just reset the algorithm information
  498. renderer.compute( this.resetFn );
  499. this.currentDispatch = 0;
  500. this.globalOpsRemaining = 0;
  501. this.globalOpsInSpan = 0;
  502. } else {
  503. // Otherwise, determine what next swap span is
  504. renderer.compute( this.setAlgoFn );
  505. }
  506. }
  507. /**
  508. * Executes a complete bitonic sort on the data buffer.
  509. *
  510. * @param {Renderer} renderer - The current scene's renderer.
  511. */
  512. compute( renderer ) {
  513. this.globalOpsRemaining = 0;
  514. this.globalOpsInSpan = 0;
  515. this.currentDispatch = 0;
  516. for ( let i = 0; i < this.stepCount; i ++ ) {
  517. this.computeStep( renderer );
  518. }
  519. }
  520. }
粤ICP备19079148号