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

Some optimisations for reducing number of memory allocations and improving BVH build speed. #1319

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
117 changes: 65 additions & 52 deletions Jolt/AABBTree/AABBTreeBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,82 +10,81 @@ JPH_NAMESPACE_BEGIN

AABBTreeBuilder::Node::Node()
{
mChild[0] = nullptr;
mChild[1] = nullptr;
mChildIndices[0] = invalidNodeIndex();
mChildIndices[1] = invalidNodeIndex();
num_tris = 0;
}

AABBTreeBuilder::Node::~Node()
{
delete mChild[0];
delete mChild[1];
}

uint AABBTreeBuilder::Node::GetMinDepth() const
uint AABBTreeBuilder::Node::GetMinDepth(const Array<Node>& nodes) const
{
if (HasChildren())
{
uint left = mChild[0]->GetMinDepth();
uint right = mChild[1]->GetMinDepth();
uint left = nodes[mChildIndices[0]].GetMinDepth(nodes);
uint right = nodes[mChildIndices[1]].GetMinDepth(nodes);
return min(left, right) + 1;
}
else
return 1;
}

uint AABBTreeBuilder::Node::GetMaxDepth() const
uint AABBTreeBuilder::Node::GetMaxDepth(const Array<Node>& nodes) const
{
if (HasChildren())
{
uint left = mChild[0]->GetMaxDepth();
uint right = mChild[1]->GetMaxDepth();
uint left = nodes[mChildIndices[0]].GetMaxDepth(nodes);
uint right = nodes[mChildIndices[1]].GetMaxDepth(nodes);
return max(left, right) + 1;
}
else
return 1;
}

uint AABBTreeBuilder::Node::GetNodeCount() const
uint AABBTreeBuilder::Node::GetNodeCount(const Array<Node>& nodes) const
{
if (HasChildren())
return mChild[0]->GetNodeCount() + mChild[1]->GetNodeCount() + 1;
return nodes[mChildIndices[0]].GetNodeCount(nodes) + nodes[mChildIndices[1]].GetNodeCount(nodes) + 1;
else
return 1;
}

uint AABBTreeBuilder::Node::GetLeafNodeCount() const
uint AABBTreeBuilder::Node::GetLeafNodeCount(const Array<Node>& nodes) const
{
if (HasChildren())
return mChild[0]->GetLeafNodeCount() + mChild[1]->GetLeafNodeCount();
return nodes[mChildIndices[0]].GetLeafNodeCount(nodes) + nodes[mChildIndices[1]].GetLeafNodeCount(nodes);
else
return 1;
}

uint AABBTreeBuilder::Node::GetTriangleCountInTree() const
uint AABBTreeBuilder::Node::GetTriangleCountInTree(const Array<Node>& nodes) const
{
if (HasChildren())
return mChild[0]->GetTriangleCountInTree() + mChild[1]->GetTriangleCountInTree();
return nodes[mChildIndices[0]].GetTriangleCountInTree(nodes) + nodes[mChildIndices[1]].GetTriangleCountInTree(nodes);
else
return GetTriangleCount();
}

void AABBTreeBuilder::Node::GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const
void AABBTreeBuilder::Node::GetTriangleCountPerNode(const Array<Node>& nodes, float &outAverage, uint &outMin, uint &outMax) const
{
outMin = INT_MAX;
outMax = 0;
outAverage = 0;
uint avg_divisor = 0;
GetTriangleCountPerNodeInternal(outAverage, avg_divisor, outMin, outMax);
GetTriangleCountPerNodeInternal(nodes, outAverage, avg_divisor, outMin, outMax);
if (avg_divisor > 0)
outAverage /= avg_divisor;
}

float AABBTreeBuilder::Node::CalculateSAHCost(float inCostTraversal, float inCostLeaf) const
float AABBTreeBuilder::Node::CalculateSAHCost(const Array<Node>& nodes, float inCostTraversal, float inCostLeaf) const
{
float surface_area = mBounds.GetSurfaceArea();
return surface_area > 0.0f? CalculateSAHCostInternal(inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;
return surface_area > 0.0f? CalculateSAHCostInternal(nodes, inCostTraversal / surface_area, inCostLeaf / surface_area) : 0.0f;
}

void AABBTreeBuilder::Node::GetNChildren(uint inN, Array<const Node *> &outChildren) const
void AABBTreeBuilder::Node::GetNChildren(const Array<Node>& nodes, uint inN, Array<const Node*> &outChildren) const
{
JPH_ASSERT(outChildren.empty());

Expand All @@ -94,8 +93,8 @@ void AABBTreeBuilder::Node::GetNChildren(uint inN, Array<const Node *> &outChild
return;

// Start with the children of this node
outChildren.push_back(mChild[0]);
outChildren.push_back(mChild[1]);
outChildren.push_back(&nodes[mChildIndices[0]]);
outChildren.push_back(&nodes[mChildIndices[1]]);

size_t next = 0;
bool all_triangles = true;
Expand All @@ -116,8 +115,8 @@ void AABBTreeBuilder::Node::GetNChildren(uint inN, Array<const Node *> &outChild
if (to_expand->HasChildren())
{
outChildren.erase(outChildren.begin() + next);
outChildren.push_back(to_expand->mChild[0]);
outChildren.push_back(to_expand->mChild[1]);
outChildren.push_back(&nodes[to_expand->mChildIndices[0]]);
outChildren.push_back(&nodes[to_expand->mChildIndices[1]]);
all_triangles = false;
}
else
Expand All @@ -127,22 +126,22 @@ void AABBTreeBuilder::Node::GetNChildren(uint inN, Array<const Node *> &outChild
}
}

float AABBTreeBuilder::Node::CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const
float AABBTreeBuilder::Node::CalculateSAHCostInternal(const Array<Node>& nodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const
{
if (HasChildren())
return inCostTraversalDivSurfaceArea * mBounds.GetSurfaceArea()
+ mChild[0]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea)
+ mChild[1]->CalculateSAHCostInternal(inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);
+ nodes[mChildIndices[0]].CalculateSAHCostInternal(nodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea)
+ nodes[mChildIndices[1]].CalculateSAHCostInternal(nodes, inCostTraversalDivSurfaceArea, inCostLeafDivSurfaceArea);
else
return inCostLeafDivSurfaceArea * mBounds.GetSurfaceArea() * GetTriangleCount();
}

void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const
void AABBTreeBuilder::Node::GetTriangleCountPerNodeInternal(const Array<Node>& nodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const
{
if (HasChildren())
{
mChild[0]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax);
mChild[1]->GetTriangleCountPerNodeInternal(outAverage, outAverageDivisor, outMin, outMax);
nodes[mChildIndices[0]].GetTriangleCountPerNodeInternal(nodes, outAverage, outAverageDivisor, outMin, outMax);
nodes[mChildIndices[1]].GetTriangleCountPerNodeInternal(nodes, outAverage, outAverageDivisor, outMin, outMax);
}
else
{
Expand All @@ -159,31 +158,41 @@ AABBTreeBuilder::AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTriangl
{
}

AABBTreeBuilder::Node *AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)
uint AABBTreeBuilder::Build(AABBTreeBuilderStats &outStats)
{
TriangleSplitter::Range initial = mTriangleSplitter.GetInitialRange();
Node *root = BuildInternal(initial);

// Worst case for number of nodes: 1 leaf node per triangle, so num leaf nodes = num tris
// For N leaf nodes there are max N - 1 internal nodes. So <= 2N nodes overall where N = num tris.
// TODO: test on a few different meshes, use a conservative emperical bound.
mNodes.reserve(2 * initial.Count());

mLeafTriangles.reserve(initial.Count());

const uint root_node_index = BuildInternal(initial);

Node *root = &mNodes[root_node_index];

float avg_triangles_per_leaf;
uint min_triangles_per_leaf, max_triangles_per_leaf;
root->GetTriangleCountPerNode(avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);
root->GetTriangleCountPerNode(mNodes, avg_triangles_per_leaf, min_triangles_per_leaf, max_triangles_per_leaf);

mTriangleSplitter.GetStats(outStats.mSplitterStats);

outStats.mSAHCost = root->CalculateSAHCost(1.0f, 1.0f);
outStats.mMinDepth = root->GetMinDepth();
outStats.mMaxDepth = root->GetMaxDepth();
outStats.mNodeCount = root->GetNodeCount();
outStats.mLeafNodeCount = root->GetLeafNodeCount();
outStats.mSAHCost = root->CalculateSAHCost(mNodes, 1.0f, 1.0f);
outStats.mMinDepth = root->GetMinDepth(mNodes);
outStats.mMaxDepth = root->GetMaxDepth(mNodes);
outStats.mNodeCount = root->GetNodeCount(mNodes);
outStats.mLeafNodeCount = root->GetLeafNodeCount(mNodes);
outStats.mMaxTrianglesPerLeaf = mMaxTrianglesPerLeaf;
outStats.mTreeMinTrianglesPerLeaf = min_triangles_per_leaf;
outStats.mTreeMaxTrianglesPerLeaf = max_triangles_per_leaf;
outStats.mTreeAvgTrianglesPerLeaf = avg_triangles_per_leaf;

return root;
return root_node_index;
}

AABBTreeBuilder::Node *AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles)
uint AABBTreeBuilder::BuildInternal(const TriangleSplitter::Range &inTriangles)
{
// Check if there are too many triangles left
if (inTriangles.Count() > mMaxTrianglesPerLeaf)
Expand Down Expand Up @@ -214,26 +223,30 @@ AABBTreeBuilder::Node *AABBTreeBuilder::BuildInternal(const TriangleSplitter::Ra
}

// Recursively build
Node *node = new Node();
node->mChild[0] = BuildInternal(left);
node->mChild[1] = BuildInternal(right);
node->mBounds = node->mChild[0]->mBounds;
node->mBounds.Encapsulate(node->mChild[1]->mBounds);
return node;
const uint node_index = (uint)mNodes.size();
mNodes.push_back(Node());
mNodes[node_index].mChildIndices[0] = BuildInternal(left);
mNodes[node_index].mChildIndices[1] = BuildInternal(right);
mNodes[node_index].mBounds = mNodes[mNodes[node_index].mChildIndices[0]].mBounds;
mNodes[node_index].mBounds.Encapsulate(mNodes[mNodes[node_index].mChildIndices[1]].mBounds);
return node_index;
}

// Create leaf node
Node *node = new Node();
node->mTriangles.reserve(inTriangles.Count());
const uint node_index = (uint)mNodes.size();
mNodes.push_back(Node());
mNodes[node_index].tris_begin = (uint)mLeafTriangles.size();
mNodes[node_index].num_tris = inTriangles.mEnd - inTriangles.mBegin;
for (uint i = inTriangles.mBegin; i < inTriangles.mEnd; ++i)
{
const IndexedTriangle &t = mTriangleSplitter.GetTriangle(i);
const VertexList &v = mTriangleSplitter.GetVertices();
node->mTriangles.push_back(t);
node->mBounds.Encapsulate(v, t);
mLeafTriangles.push_back(t);

mNodes[node_index].mBounds.Encapsulate(v, t);
}

return node;
return node_index;
}

JPH_NAMESPACE_END
43 changes: 25 additions & 18 deletions Jolt/AABBTree/AABBTreeBuilder.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ class JPH_EXPORT AABBTreeBuilder
{
public:
/// A node in the tree, contains the AABox for the tree and any child nodes or triangles
class Node : public NonCopyable
class Node
{
public:
JPH_OVERRIDE_NEW_DELETE
Expand All @@ -45,64 +45,71 @@ class JPH_EXPORT AABBTreeBuilder
Node();
~Node();

static inline uint invalidNodeIndex() { return UINT32_MAX; }

/// Get number of triangles in this node
inline uint GetTriangleCount() const { return uint(mTriangles.size()); }
inline uint GetTriangleCount() const { return num_tris; }

/// Check if this node has any children
inline bool HasChildren() const { return mChild[0] != nullptr || mChild[1] != nullptr; }
inline bool HasChildren() const { return mChildIndices[0] != invalidNodeIndex() || mChildIndices[1] != invalidNodeIndex(); }

/// Min depth of tree
uint GetMinDepth() const;
uint GetMinDepth(const Array<Node>& nodes) const;

/// Max depth of tree
uint GetMaxDepth() const;
uint GetMaxDepth(const Array<Node>& nodes) const;

/// Number of nodes in tree
uint GetNodeCount() const;
uint GetNodeCount(const Array<Node>& nodes) const;

/// Number of leaf nodes in tree
uint GetLeafNodeCount() const;
uint GetLeafNodeCount(const Array<Node>& nodes) const;

/// Get triangle count in tree
uint GetTriangleCountInTree() const;
uint GetTriangleCountInTree(const Array<Node>& nodes) const;

/// Calculate min and max triangles per node
void GetTriangleCountPerNode(float &outAverage, uint &outMin, uint &outMax) const;
void GetTriangleCountPerNode(const Array<Node>& nodes, float &outAverage, uint &outMin, uint &outMax) const;

/// Calculate the total cost of the tree using the surface area heuristic
float CalculateSAHCost(float inCostTraversal, float inCostLeaf) const;
float CalculateSAHCost(const Array<Node>& nodes, float inCostTraversal, float inCostLeaf) const;

/// Recursively get children (breadth first) to get in total inN children (or less if there are no more)
void GetNChildren(uint inN, Array<const Node *> &outChildren) const;
void GetNChildren(const Array<Node>& nodes, uint inN, Array<const Node*> &outChildren) const;

/// Bounding box
AABox mBounds;

/// Triangles (if no child nodes)
IndexedTriangleList mTriangles;
uint tris_begin; // Index into mLeafTriangles
uint num_tris;

/// Child nodes (if no triangles)
Node * mChild[2];
/// Child node indices (if no triangles)
uint mChildIndices[2];

private:
friend class AABBTreeBuilder;

/// Recursive helper function to calculate cost of the tree
float CalculateSAHCostInternal(float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const;
float CalculateSAHCostInternal(const Array<Node>& nodes, float inCostTraversalDivSurfaceArea, float inCostLeafDivSurfaceArea) const;

/// Recursive helper function to calculate min and max triangles per node
void GetTriangleCountPerNodeInternal(float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const;
void GetTriangleCountPerNodeInternal(const Array<Node>& nodes, float &outAverage, uint &outAverageDivisor, uint &outMin, uint &outMax) const;
};

/// Constructor
AABBTreeBuilder(TriangleSplitter &inSplitter, uint inMaxTrianglesPerLeaf = 16);

/// Recursively build tree, returns the root node of the tree
Node * Build(AABBTreeBuilderStats &outStats);
uint Build(AABBTreeBuilderStats &outStats);

private:
Node * BuildInternal(const TriangleSplitter::Range &inTriangles);
uint BuildInternal(const TriangleSplitter::Range &inTriangles);

public:
Array<Node> mNodes;
Array<IndexedTriangle> mLeafTriangles;
private:
TriangleSplitter & mTriangleSplitter;
const uint mMaxTrianglesPerLeaf;
};
Expand Down
14 changes: 7 additions & 7 deletions Jolt/AABBTree/AABBTreeToBuffer.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,14 +37,14 @@ class AABBTreeToBuffer
static const int TriangleHeaderSize = TriangleCodec::TriangleHeaderSize;

/// Convert AABB tree. Returns false if failed.
bool Convert(const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
bool Convert(const Array<IndexedTriangle>& inTriangles, const Array<AABBTreeBuilder::Node>& inNodes, const VertexList &inVertices, const AABBTreeBuilder::Node *inRoot, bool inStoreUserData, const char *&outError)
{
const typename NodeCodec::EncodingContext node_ctx;
typename TriangleCodec::EncodingContext tri_ctx(inVertices);

// Estimate the amount of memory required
uint tri_count = inRoot->GetTriangleCountInTree();
uint node_count = inRoot->GetNodeCount();
uint tri_count = inRoot->GetTriangleCountInTree(inNodes);
uint node_count = inRoot->GetNodeCount(inNodes);
uint nodes_size = node_ctx.GetPessimisticMemoryEstimate(node_count);
uint total_size = HeaderSize + TriangleHeaderSize + nodes_size + tri_ctx.GetPessimisticMemoryEstimate(tri_count, inStoreUserData);
mTree.reserve(total_size);
Expand All @@ -70,8 +70,8 @@ class AABBTreeToBuffer
uint * mParentTrianglesStart = nullptr; // Where to store mTriangleStart (to patch mChildTrianglesStart of my parent)
};

Deque<NodeData *> to_process;
Deque<NodeData *> to_process_triangles;
Array<NodeData *> to_process;
Array<NodeData *> to_process_triangles;
Array<NodeData> node_list;

node_list.reserve(node_count); // Needed to ensure that array is not reallocated, so we can keep pointers in the array
Expand Down Expand Up @@ -100,7 +100,7 @@ class AABBTreeToBuffer

// Collect the first NumChildrenPerNode sub-nodes in the tree
child_nodes.clear(); // Won't free the memory
node_data->mNode->GetNChildren(NumChildrenPerNode, child_nodes);
node_data->mNode->GetNChildren(inNodes, NumChildrenPerNode, child_nodes);
node_data->mNumChildren = (uint)child_nodes.size();

// Fill in default child bounds
Expand Down Expand Up @@ -157,7 +157,7 @@ class AABBTreeToBuffer
else
{
// Add triangles
node_data->mTriangleStart = tri_ctx.Pack(node_data->mNode->mTriangles, inStoreUserData, mTree, outError);
node_data->mTriangleStart = tri_ctx.Pack(&inTriangles[node_data->mNode->tris_begin], node_data->mNode->num_tris, inStoreUserData, mTree, outError);
if (node_data->mTriangleStart == uint(-1))
return false;
}
Expand Down
Loading