Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Parallel Worker Construction #627

Merged
merged 42 commits into from
Jan 29, 2024
Merged
Show file tree
Hide file tree
Changes from 19 commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
81155e1
Small cleanup
gkjohnson Jan 26, 2024
2e469b5
Add worker base class
gkjohnson Jan 27, 2024
779d715
Some progress
gkjohnson Jan 27, 2024
c7ffe50
updates
gkjohnson Jan 27, 2024
533aee8
Merge remote-tracking branch 'origin' into parallel-construction
gkjohnson Jan 27, 2024
cc647d8
Simplify build functions
gkjohnson Jan 27, 2024
37b64b7
More simplification
gkjohnson Jan 27, 2024
6f9e927
More hashing out
gkjohnson Jan 27, 2024
4b52232
More updates
gkjohnson Jan 27, 2024
7641d49
Updates
gkjohnson Jan 27, 2024
d21310d
Cleanup
gkjohnson Jan 27, 2024
e50976b
Updates
gkjohnson Jan 27, 2024
a9c074b
Updates
gkjohnson Jan 27, 2024
977c981
Fixes
gkjohnson Jan 27, 2024
7271a77
Small fixes
gkjohnson Jan 28, 2024
a2d0621
Get it working
gkjohnson Jan 28, 2024
643c319
Small error
gkjohnson Jan 28, 2024
04da351
Small cleanup
gkjohnson Jan 28, 2024
9f88610
Get onProgress working
gkjohnson Jan 28, 2024
a3a465b
Lint updates
gkjohnson Jan 28, 2024
2c31699
Fix helper order
gkjohnson Jan 28, 2024
a684718
Update variable
gkjohnson Jan 28, 2024
85b1ece
Handle max depth case
gkjohnson Jan 28, 2024
bb886c0
Add null check
gkjohnson Jan 28, 2024
5c3f8b3
Update file names
gkjohnson Jan 28, 2024
bcd2d56
error update
gkjohnson Jan 28, 2024
b8feb82
Fixes
gkjohnson Jan 28, 2024
86f5599
Rearrange code
gkjohnson Jan 28, 2024
9ed3122
Remove copy of triangleBounds
gkjohnson Jan 28, 2024
03dcfbb
Remove unused import
gkjohnson Jan 28, 2024
dd45aa7
Bump parcel
gkjohnson Jan 28, 2024
519763a
Merge remote-tracking branch 'origin/master' into parallel-construction
gkjohnson Jan 29, 2024
f5e101d
Add support for parallel triangle bounds construction
gkjohnson Jan 29, 2024
fa060d8
comment
gkjohnson Jan 29, 2024
5ab2928
Update the async option
gkjohnson Jan 29, 2024
2097bf8
Add fallback
gkjohnson Jan 29, 2024
9eef8bf
Rearrange files
gkjohnson Jan 29, 2024
f8b54d1
Move more files
gkjohnson Jan 29, 2024
34f2d69
Update error message
gkjohnson Jan 29, 2024
02d6736
Error message update
gkjohnson Jan 29, 2024
a2233dd
Update async demo page
gkjohnson Jan 29, 2024
79edfd9
Update docs
gkjohnson Jan 29, 2024
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
20 changes: 12 additions & 8 deletions src/core/MeshBVH.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,17 @@

const obb = /* @__PURE__ */ new OrientedBox();
const tempBox = /* @__PURE__ */ new Box3();
export const DEFAULT_OPTIONS = {
strategy: CENTER,
maxDepth: 40,
maxLeafTris: 10,
verbose: true,
Fixed Show fixed Hide fixed
useSharedArrayBuffer: false,
setBoundingBox: true,
onProgress: null,
indirect: false,
verbose: true,
};

export class MeshBVH {

Expand Down Expand Up @@ -118,14 +129,7 @@
// default options
options = Object.assign( {

strategy: CENTER,
maxDepth: 40,
maxLeafTris: 10,
verbose: true,
useSharedArrayBuffer: false,
setBoundingBox: true,
onProgress: null,
indirect: false,
...DEFAULT_OPTIONS,

// undocumented options

Expand Down
2 changes: 2 additions & 0 deletions src/core/MeshBVHNode.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,8 @@ export class MeshBVHNode {
// internal nodes have boundingData, left, right, and splitAxis
// leaf nodes have offset and count (referring to primitives in the mesh geometry)

this.boundingData = new Float32Array( 6 );

}

}
129 changes: 22 additions & 107 deletions src/core/build/buildTree.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,13 @@ import { ensureIndex, getFullGeometryRange, getRootIndexRanges, getTriCount, has
import { getBounds, computeTriangleBounds } from './computeBoundsUtils.js';
import { getOptimalSplit } from './splitUtils.js';
import { MeshBVHNode } from '../MeshBVHNode.js';
import { BYTES_PER_NODE, IS_LEAFNODE_FLAG } from '../Constants.js';
import { BYTES_PER_NODE } from '../Constants.js';

import { partition } from './sortUtils.generated.js';
import { partition_indirect } from './sortUtils_indirect.generated.js';
import { countNodes, populateBuffer } from './buildUtils.js';

function generateIndirectBuffer( geometry, useSharedArrayBuffer ) {
export function generateIndirectBuffer( geometry, useSharedArrayBuffer ) {

const triCount = ( geometry.index ? geometry.index.count : geometry.attributes.position.count ) / 3;
const useUint32 = triCount > 2 ** 16;
Expand All @@ -25,42 +26,31 @@ function generateIndirectBuffer( geometry, useSharedArrayBuffer ) {

}

function buildTree( bvh, options ) {
export function buildTree( bvh, triangleBounds, offset, count, options ) {

// Compute the full bounds of the geometry at the same time as triangle bounds because
// we'll need it for the root bounds in the case with no groups and it should be fast here.
// We can't use the geometry bounding box if it's available because it may be out of date.
// epxand variables
const {
maxDepth,
verbose,
maxLeafTris,
strategy,
onProgress,
indirect,
} = options;
const indirectBuffer = bvh._indirectBuffer;
const geometry = bvh.geometry;
const indexArray = geometry.index ? geometry.index.array : null;
const totalTriangles = getTriCount( geometry );
let reachedMaxDepth = false;
const partionFunc = indirect ? partition_indirect : partition;

// generate intermediate variables
const totalTriangles = getTriCount( geometry );
const cacheCentroidBoundingData = new Float32Array( 6 );
const triangleBounds = computeTriangleBounds( geometry );
const partionFunc = options.indirect ? partition_indirect : partition;

const roots = [];
const ranges = options.indirect ? getFullGeometryRange( geometry ) : getRootIndexRanges( geometry );
for ( let range of ranges ) {

const root = new MeshBVHNode();
root.boundingData = new Float32Array( 6 );
getBounds( triangleBounds, range.offset, range.count, root.boundingData, cacheCentroidBoundingData );

splitNode( root, range.offset, range.count, cacheCentroidBoundingData );
roots.push( root );

}
let reachedMaxDepth = false;

return roots;
const root = new MeshBVHNode();
getBounds( triangleBounds, offset, count, root.boundingData, cacheCentroidBoundingData );
splitNode( root, offset, count, cacheCentroidBoundingData );
return root;

function triggerProgress( trianglesProcessed ) {

Expand Down Expand Up @@ -127,7 +117,6 @@ function buildTree( bvh, options ) {
const lstart = offset;
const lcount = splitOffset - offset;
node.left = left;
left.boundingData = new Float32Array( 6 );

getBounds( triangleBounds, lstart, lcount, left.boundingData, cacheCentroidBoundingData );
splitNode( left, lstart, lcount, cacheCentroidBoundingData, depth + 1 );
Expand All @@ -137,7 +126,6 @@ function buildTree( bvh, options ) {
const rstart = splitOffset;
const rcount = count - lcount;
node.right = right;
right.boundingData = new Float32Array( 6 );

getBounds( triangleBounds, rstart, rcount, right.boundingData, cacheCentroidBoundingData );
splitNode( right, rstart, rcount, cacheCentroidBoundingData, depth + 1 );
Expand Down Expand Up @@ -174,91 +162,18 @@ export function buildPackedTree( bvh, options ) {

}

// boundingData : 6 float32
// right / offset : 1 uint32
// splitAxis / isLeaf + count : 1 uint32 / 2 uint16
const roots = buildTree( bvh, options );

let float32Array;
let uint32Array;
let uint16Array;
const packedRoots = [];
const BufferConstructor = options.useSharedArrayBuffer ? SharedArrayBuffer : ArrayBuffer;
for ( let i = 0; i < roots.length; i ++ ) {

const root = roots[ i ];
let nodeCount = countNodes( root );
const triangleBounds = computeTriangleBounds( geometry );
const geometryRanges = options.indirect ? getFullGeometryRange( geometry ) : getRootIndexRanges( geometry );
bvh._roots = geometryRanges.map( range => {

const root = buildTree( bvh, triangleBounds, range.offset, range.count, options );
const nodeCount = countNodes( root );
const buffer = new BufferConstructor( BYTES_PER_NODE * nodeCount );
float32Array = new Float32Array( buffer );
uint32Array = new Uint32Array( buffer );
uint16Array = new Uint16Array( buffer );
populateBuffer( 0, root );
packedRoots.push( buffer );

}

bvh._roots = packedRoots;
return;

function countNodes( node ) {

if ( node.count ) {

return 1;

} else {

return 1 + countNodes( node.left ) + countNodes( node.right );

}
populateBuffer( 0, root, buffer );
return buffer;

}

function populateBuffer( byteOffset, node ) {

const stride4Offset = byteOffset / 4;
const stride2Offset = byteOffset / 2;
const isLeaf = ! ! node.count;
const boundingData = node.boundingData;
for ( let i = 0; i < 6; i ++ ) {

float32Array[ stride4Offset + i ] = boundingData[ i ];

}

if ( isLeaf ) {

const offset = node.offset;
const count = node.count;
uint32Array[ stride4Offset + 6 ] = offset;
uint16Array[ stride2Offset + 14 ] = count;
uint16Array[ stride2Offset + 15 ] = IS_LEAFNODE_FLAG;
return byteOffset + BYTES_PER_NODE;

} else {

const left = node.left;
const right = node.right;
const splitAxis = node.splitAxis;

let nextUnusedPointer;
nextUnusedPointer = populateBuffer( byteOffset + BYTES_PER_NODE, left );

if ( ( nextUnusedPointer / 4 ) > Math.pow( 2, 32 ) ) {

throw new Error( 'MeshBVH: Cannot store child pointer greater than 32 bits.' );

}

uint32Array[ stride4Offset + 6 ] = nextUnusedPointer / 4;
nextUnusedPointer = populateBuffer( nextUnusedPointer, right );

uint32Array[ stride4Offset + 7 ] = splitAxis;
return nextUnusedPointer;

}

}
} );

}
103 changes: 103 additions & 0 deletions src/core/build/buildUtils.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
import { BYTES_PER_NODE, IS_LEAFNODE_FLAG } from '../Constants.js';
import { IS_LEAF } from '../utils/nodeBufferUtils.js';

let float32Array, uint32Array, uint16Array, uint8Array;
const MAX_POINTER = Math.pow( 2, 32 );

export function countNodes( node ) {

if ( node.count ) {

return 1;

} else {

return 1 + countNodes( node.left ) + countNodes( node.right );

}

}

export function populateBuffer( byteOffset, node, buffer ) {

float32Array = new Float32Array( buffer );
uint32Array = new Uint32Array( buffer );
uint16Array = new Uint16Array( buffer );
uint8Array = new Uint8Array( buffer );

return _populateBuffer( byteOffset, node );

}

// pack structure
// boundingData : 6 float32
// right / offset : 1 uint32
// splitAxis / isLeaf + count : 1 uint32 / 2 uint16
function _populateBuffer( byteOffset, node ) {

const stride4Offset = byteOffset / 4;
const stride2Offset = byteOffset / 2;
const isLeaf = ! ! node.count;
const boundingData = node.boundingData;
for ( let i = 0; i < 6; i ++ ) {

float32Array[ stride4Offset + i ] = boundingData[ i ];

}

if ( isLeaf ) {

if ( node.buffer ) {

const buffer = node.buffer;
uint8Array.set( new Uint8Array( buffer ), byteOffset );

for ( let offset = byteOffset, l = byteOffset + buffer.byteLength; offset < l; offset += BYTES_PER_NODE ) {

const offset2 = offset / 2;
if ( ! IS_LEAF( offset2, uint16Array ) ) {

uint32Array[ ( offset / 4 ) + 6 ] += byteOffset / 4;


}

}

return byteOffset + buffer.byteLength;

} else {

const offset = node.offset;
const count = node.count;
uint32Array[ stride4Offset + 6 ] = offset;
uint16Array[ stride2Offset + 14 ] = count;
uint16Array[ stride2Offset + 15 ] = IS_LEAFNODE_FLAG;
return byteOffset + BYTES_PER_NODE;

}

} else {

const left = node.left;
const right = node.right;
const splitAxis = node.splitAxis;

let nextUnusedPointer;
nextUnusedPointer = _populateBuffer( byteOffset + BYTES_PER_NODE, left );

if ( ( nextUnusedPointer / 4 ) > MAX_POINTER ) {

throw new Error( 'MeshBVH: Cannot store child pointer greater than 32 bits.' );

}

uint32Array[ stride4Offset + 6 ] = nextUnusedPointer / 4;
nextUnusedPointer = _populateBuffer( nextUnusedPointer, right );

uint32Array[ stride4Offset + 7 ] = splitAxis;
return nextUnusedPointer;

}

}
32 changes: 32 additions & 0 deletions src/utils/BufferUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,35 @@ export function isSharedArrayBufferSupported() {
return typeof SharedArrayBuffer !== 'undefined';

}

export function convertToBufferType( array, BufferConstructor ) {

if ( array.buffer ) {

const buffer = array.buffer;
if ( buffer.constructor === BufferConstructor ) {

return array;

}

const ArrayConstructor = array.constructor;
const result = new ArrayConstructor( new BufferConstructor( buffer.byteLength ) );
result.set( array );
return result;

} else {

if ( array.constructor === BufferConstructor ) {

return array;

}

const result = new BufferConstructor( array.byteLength );
new Uint8Array( result ).set( new Uint8Array( array ) );
return result;

}

}
Loading
Loading