Initial commit: Final state of the master project
This commit is contained in:
507
Research/scene/PoolBuilder/VirtualNodePoolBuilder.cpp
Normal file
507
Research/scene/PoolBuilder/VirtualNodePoolBuilder.cpp
Normal file
@@ -0,0 +1,507 @@
|
||||
#include "VirtualNodePoolBuilder.h"
|
||||
#include <algorithm>
|
||||
#include "../../inc/tbb/parallel_sort.h"
|
||||
#include "../../core/Util/BoolArray.h"
|
||||
#include <numeric>
|
||||
|
||||
std::string VirtualNodePoolBuilder::GetFullFileName(const std::string& filename) const
|
||||
{
|
||||
return filename + ".v.pool";
|
||||
}
|
||||
|
||||
size_t VirtualNodePoolBuilder::GetFullNodeSize(const BaseTree* tree, const unsigned8& level, const unsigned8& pointerSize) const
|
||||
{
|
||||
// 1 byte for childmask
|
||||
// 1 byte for "virtual mask" (indicating which nodes are virtual)
|
||||
// pointerSize bytes for pointer to the first child
|
||||
// + Additional node info
|
||||
if (level > tree->GetMaxLevel()) return 0;
|
||||
return 1 + 1 + pointerSize + tree->GetAdditionalBytesPerNode(level);
|
||||
}
|
||||
size_t VirtualNodePoolBuilder::GetVirtualNodeSize(const BaseTree* tree, const unsigned8& level, const unsigned8& pointerSize) const
|
||||
{
|
||||
if (level == 0) return 0;
|
||||
return pointerSize;
|
||||
}
|
||||
|
||||
size_t VirtualNodePoolBuilder::GetFullNodeSize(const BaseTree* tree, const unsigned8& level, const std::vector<unsigned8>& pointerSizesPerLevel) const
|
||||
{
|
||||
if (level > tree->GetMaxLevel()) return 0;
|
||||
return GetFullNodeSize(tree, level, pointerSizesPerLevel[level]);
|
||||
}
|
||||
|
||||
size_t VirtualNodePoolBuilder::GetVirtualNodeSize(const BaseTree* tree, const unsigned8& level, const std::vector<unsigned8>& pointerSizesPerLevel) const
|
||||
{
|
||||
// pointerSize bytes for pointer to the first child
|
||||
return GetVirtualNodeSize(tree, level, pointerSizesPerLevel[level - 1]);
|
||||
}
|
||||
|
||||
size_t VirtualNodePoolBuilder::GetNormalNodeSize(const BaseTree* tree, const unsigned32& nodeId, const std::vector<unsigned8>& pointerSizesPerLevel, std::vector<unsigned8>& additionalPointerInfoSizesPerLevel, const bool& includingAdditionalPointerInfo) const
|
||||
{
|
||||
// 1 byte for childmask
|
||||
// Additional node info
|
||||
// pointerSize bytes for each pointer needed.
|
||||
const Node* node = tree->GetNode(nodeId);
|
||||
unsigned8 level = node->GetLevel();
|
||||
return 1 + tree->GetAdditionalBytesPerNode(level) + node->GetChildCount() * pointerSizesPerLevel[level] +
|
||||
(includingAdditionalPointerInfo ? (additionalPointerInfoSizesPerLevel[level] * node->GetChildCount()) : 0);
|
||||
}
|
||||
|
||||
std::vector<unsigned8> VirtualNodePoolBuilder::CalculatePointerSizesPerLevel(const BaseTree* tree, const std::vector<size_t>& parentsPerNode, const std::vector<bool>& useVirtualNodes) const
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
|
||||
// Calculate some counts per level (needed to calculate the size of each level)
|
||||
std::vector<size_t> virtualNodesPerLevel = CalculateVirtualNodesPerLevel(tree, parentsPerNode);
|
||||
std::vector<size_t> fullNodesPerLevel = CalculateFullNodesPerLevel(tree);
|
||||
std::vector<size_t> pointersToLevel = CalculatePointersToPerLevel(tree);
|
||||
std::vector<unsigned8> additionalBytesPerPointer = tree->GetAdditionalBytesPerPointer();
|
||||
|
||||
// Now bottom-up calculate the pointer sizees required to point to each level
|
||||
std::vector<unsigned8> res(depth + 1, 0);
|
||||
for (unsigned8 level = depth; level > 0; level--)
|
||||
{
|
||||
// Keep increasing the pointersize until we can point to all nodes within a level
|
||||
bool fits = false;
|
||||
while (!fits)
|
||||
{
|
||||
res[level - 1]++;
|
||||
size_t requiredSize = CalculateSizeOfLevel(tree, level, virtualNodesPerLevel[level], fullNodesPerLevel[level],
|
||||
pointersToLevel[level], level == depth ? 0 : pointersToLevel[level + 1],
|
||||
level == 0 ? 0 : res[level - 1], res[level],
|
||||
level == 0 ? 0 : additionalBytesPerPointer[level - 1], additionalBytesPerPointer[level],
|
||||
useVirtualNodes[level], level == 0 ? 0 : useVirtualNodes[level - 1]);
|
||||
size_t availableSize = BitHelper::Exp2(res[level - 1] * 8); // Available size is how much bytes we can reach with a pointer
|
||||
fits = requiredSize < availableSize;
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculateVirtualNodesPerLevel(const BaseTree* tree, const std::vector<size_t>& parentsPerNode) const
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
unsigned32 nodeCount = (unsigned32)tree->GetNodeCount();
|
||||
std::vector<size_t> virtualNodesPerLevel(depth + 1);
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
const Node* node = tree->GetNode(i);
|
||||
unsigned8 level = node->GetLevel();
|
||||
if (parentsPerNode[i] > 1)
|
||||
virtualNodesPerLevel[level] += parentsPerNode[i] - 1;
|
||||
}
|
||||
return virtualNodesPerLevel;
|
||||
}
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculateFullNodesPerLevel(const BaseTree* tree) const
|
||||
{
|
||||
// Every node appears exactly once in full
|
||||
return tree->GetNodesPerLevel();
|
||||
}
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculatePointersToPerLevel(const BaseTree* tree) const
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
unsigned32 nodeCount = (unsigned32)tree->GetNodeCount();
|
||||
std::vector<size_t> pointersToPerLevel(depth + 1);
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
const Node* node = tree->GetNode(i);
|
||||
unsigned8 level = node->GetLevel();
|
||||
if (level < depth)
|
||||
pointersToPerLevel[level + 1] += node->GetChildCount();
|
||||
}
|
||||
return pointersToPerLevel;
|
||||
}
|
||||
size_t VirtualNodePoolBuilder::CalculateSizeOfLevel(const BaseTree* tree, const unsigned8& level,
|
||||
const size_t& virtualNodesThisLevel, const size_t& fullNodesThisLevel, const size_t& pointersToThisLevel, const size_t& pointersFromThisLevel,
|
||||
const unsigned8& pointerSizeToThisLevel, const unsigned8& pointerSizeFromThisLevel,
|
||||
const unsigned8& additionalBytesPointersToThisLevel, const unsigned8& additionalBytesPointersFromThisLevel,
|
||||
const bool& useVirtualNodesThisLevel, const bool& useVirtualNodesPreviousLevel) const
|
||||
{
|
||||
size_t requiredSize = 0;
|
||||
// Calculate size of virtual nodes placed in this level by the previous level
|
||||
if (useVirtualNodesPreviousLevel)
|
||||
requiredSize += virtualNodesThisLevel * GetVirtualNodeSize(tree, level, pointerSizeToThisLevel) + pointersToThisLevel * additionalBytesPointersToThisLevel;
|
||||
// Calculate the size of full nodes (or normal nodes) occupying this level
|
||||
if (useVirtualNodesThisLevel)
|
||||
requiredSize += fullNodesThisLevel * GetFullNodeSize(tree, level, pointerSizeFromThisLevel);
|
||||
else
|
||||
requiredSize += (1 + tree->GetAdditionalBytesPerNode(level)) * fullNodesThisLevel + pointersFromThisLevel * pointerSizeFromThisLevel + pointersFromThisLevel * additionalBytesPointersFromThisLevel;
|
||||
return requiredSize;
|
||||
}
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculateSizePerLevel(const BaseTree* tree, const std::vector<unsigned8> pointerSizesPerLevel, const std::vector<size_t>& parentsPerNode, const std::vector<bool>& useVirtualNodes) const
|
||||
{
|
||||
// Calculate some statistics needed to find the size of each level in memory
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
std::vector<size_t> virtualNodesPerLevel = CalculateVirtualNodesPerLevel(tree, parentsPerNode);
|
||||
std::vector<size_t> fullNodesPerLevel = CalculateFullNodesPerLevel(tree);
|
||||
std::vector<size_t> pointersToLevel = CalculatePointersToPerLevel(tree);
|
||||
std::vector<unsigned8> additionalBytesPerPointer = tree->GetAdditionalBytesPerPointer();
|
||||
|
||||
// Calculate the actual size per level
|
||||
std::vector<size_t> sizePerLevel(depth + 1);
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
sizePerLevel[level] = CalculateSizeOfLevel(tree, level, virtualNodesPerLevel[level], fullNodesPerLevel[level],
|
||||
pointersToLevel[level], level == depth ? 0 : pointersToLevel[level + 1],
|
||||
level == 0 ? 0 : pointerSizesPerLevel[level - 1], pointerSizesPerLevel[level],
|
||||
level == 0 ? 0 : additionalBytesPerPointer[level - 1], additionalBytesPerPointer[level],
|
||||
useVirtualNodes[level], level == 0 ? 0 : useVirtualNodes[level - 1]);
|
||||
return sizePerLevel;
|
||||
}
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculateApproximateSizePerLevelVirtualNodes(const BaseTree* tree, const std::vector<size_t>& parentsPerNode) const
|
||||
{
|
||||
// Calculate some statistics needed to find the size of each level in memory
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
std::vector<size_t> virtualNodesPerLevel = CalculateVirtualNodesPerLevel(tree, parentsPerNode);
|
||||
std::vector<size_t> fullNodesPerLevel = CalculateFullNodesPerLevel(tree);
|
||||
std::vector<size_t> pointersToLevel = CalculatePointersToPerLevel(tree);
|
||||
std::vector<unsigned8> pointerSizesPerLevel(depth + 1, 4); // Assume 4 bytes pointers per level
|
||||
std::vector<unsigned8> additionalBytesPerPointer = tree->GetAdditionalBytesPerPointer();
|
||||
|
||||
// Calculate the actual size per level
|
||||
std::vector<size_t> sizePerLevel(depth + 1);
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
sizePerLevel[level] =
|
||||
fullNodesPerLevel[level] * GetFullNodeSize(tree, level, pointerSizesPerLevel) + // Full nodes size
|
||||
(level == depth ? 0 : (virtualNodesPerLevel[level + 1] * GetVirtualNodeSize(tree, level + 1, pointerSizesPerLevel))) + // Virtual nodes size
|
||||
(level == depth ? 0 : (pointersToLevel[level + 1] * additionalBytesPerPointer[level])); // additional pointer bytes size
|
||||
return sizePerLevel;
|
||||
}
|
||||
std::vector<size_t> VirtualNodePoolBuilder::CalculateApproximateSizePerLevelStandardNodes(const BaseTree* tree) const
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
unsigned32 nodeCount = (unsigned32)tree->GetNodeCount();
|
||||
std::vector<unsigned8> pointerSizesPerLevel(depth + 1, 4); // Assume 4 bytes pointers per level
|
||||
std::vector<unsigned8> additionalBytesPerPointer = tree->GetAdditionalBytesPerPointer();
|
||||
std::vector<size_t> sizePerLevel(depth + 1);
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
const Node* node = tree->GetNode(i);
|
||||
unsigned8 level = node->GetLevel();
|
||||
sizePerLevel[level] += GetNormalNodeSize(tree, i, pointerSizesPerLevel, additionalBytesPerPointer, true);;
|
||||
}
|
||||
return sizePerLevel;
|
||||
}
|
||||
std::vector<bool> VirtualNodePoolBuilder::DecideVirtualPointersPerLevel(const BaseTree* tree, const std::vector<size_t>& parentsPerNode) const
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
std::vector<size_t> sizePerStandardLevel = CalculateApproximateSizePerLevelStandardNodes(tree);
|
||||
std::vector<size_t> sizePerVirtualNodesLevel = CalculateApproximateSizePerLevelVirtualNodes(tree, parentsPerNode);
|
||||
std::vector<bool> useVirtualNodes(depth + 1);
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
useVirtualNodes[level] = sizePerVirtualNodesLevel[level] < sizePerStandardLevel[level];
|
||||
//useVirtualNodes = std::vector<bool>(depth + 1, false);
|
||||
//useVirtualNodes[0] = true;
|
||||
//useVirtualNodes[1] = true;
|
||||
//useVirtualNodes[2] = true;
|
||||
return useVirtualNodes;
|
||||
}
|
||||
size_t VirtualNodePoolBuilder::CalculatePoolInfoSize(const BaseTree* tree)
|
||||
{
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
// Each tree contains at least the level offsets (4 bytes per level) and pointer sizes per level (1 byte per level)
|
||||
// 1 byte per level for sizes of additional information per level
|
||||
// 4 bytes are used to indicate which levels use virtual nodes
|
||||
size_t poolInfoSize = (depth + 1) * 5 + 4;
|
||||
if (HasAdditionalBytesPerNode(tree)) poolInfoSize += depth + 1;
|
||||
if (HasAdditionalBytesPerPointer(tree)) poolInfoSize += depth + 1;
|
||||
// Additional pool info from the tree
|
||||
poolInfoSize += tree->GetAdditionalTreeInfoSize();
|
||||
return poolInfoSize;
|
||||
}
|
||||
size_t VirtualNodePoolBuilder::GetPoolSize(const BaseTree* tree)
|
||||
{
|
||||
// Calculate the pool info size
|
||||
size_t minSize = CalculatePoolInfoSize(tree);
|
||||
|
||||
// Calculate the main pool size
|
||||
std::vector<size_t> parentsPerNode = tree->GetParentCounts();
|
||||
std::vector<bool> useVirtualNodes = DecideVirtualPointersPerLevel(tree, parentsPerNode);
|
||||
std::vector<unsigned8> pointerSizesPerLevel = CalculatePointerSizesPerLevel(tree, parentsPerNode, useVirtualNodes);
|
||||
std::vector<size_t> sizesPerLevel = CalculateSizePerLevel(tree, pointerSizesPerLevel, parentsPerNode, useVirtualNodes);
|
||||
minSize += std::accumulate(sizesPerLevel.begin(), sizesPerLevel.end(), size_t(0));
|
||||
|
||||
std::vector<size_t> virtualNodesPerLevel = CalculateVirtualNodesPerLevel(tree, parentsPerNode);
|
||||
std::vector<size_t> fullNodesPerLevel = CalculateFullNodesPerLevel(tree);
|
||||
size_t virtualNodesSum = 0;
|
||||
size_t fullNodesSum = 0;
|
||||
size_t normalNodesSum = 0;
|
||||
for (unsigned8 level = 0; level < tree->GetMaxLevel(); level++)
|
||||
{
|
||||
if (useVirtualNodes[level]) fullNodesSum += fullNodesPerLevel[level];
|
||||
else normalNodesSum += fullNodesPerLevel[level];
|
||||
if (level > 0 && useVirtualNodes[level - 1]) virtualNodesSum += virtualNodesPerLevel[level - 1];
|
||||
}
|
||||
printf("Virtual nodes: %llu, Complete nodes: %llu, Normal Nodes: %llu, Percentage virtual: %f\n", (unsigned64)virtualNodesSum, (unsigned64)fullNodesSum, (unsigned64)normalNodesSum, (double(virtualNodesSum) / double(virtualNodesSum + fullNodesSum)) * 100.0);
|
||||
return minSize;
|
||||
}
|
||||
|
||||
//************************************
|
||||
// Insert all nodes into final node pool and updates pointers
|
||||
//************************************
|
||||
bool VirtualNodePoolBuilder::BuildPool(const BaseTree* tree, std::vector<unsigned8>& pool) {
|
||||
if (tree == NULL) return false;
|
||||
mIsBuilding = true;
|
||||
|
||||
unsigned32 nodeCount = (unsigned32)tree->GetNodeCount();
|
||||
unsigned8 depth = tree->GetMaxLevel();
|
||||
|
||||
// Initialize the pool
|
||||
pool = std::vector<unsigned8>(GetPoolSize(tree));
|
||||
|
||||
// Acquire some information about the pool
|
||||
std::vector<size_t> parentsPerNode = tree->GetParentCounts();
|
||||
std::vector<bool> useVirtualNodes = DecideVirtualPointersPerLevel(tree, parentsPerNode);
|
||||
std::vector<unsigned8> pointerSizesPerLevel = CalculatePointerSizesPerLevel(tree, parentsPerNode, useVirtualNodes);
|
||||
std::vector<size_t> sizePerLevel = CalculateSizePerLevel(tree, pointerSizesPerLevel, parentsPerNode, useVirtualNodes);
|
||||
std::vector<unsigned8> additionalBytesPerPointer = tree->GetAdditionalBytesPerPointer();
|
||||
std::vector<unsigned8> additionalBytesPerNode = tree->GetAdditionalBytesPerNode();
|
||||
std::vector<size_t> nodePointers(nodeCount);
|
||||
|
||||
// Calculate the level offsets
|
||||
std::vector<size_t> levelOffsets(depth + 1);
|
||||
size_t curIndex = CalculatePoolInfoSize(tree);
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
{
|
||||
levelOffsets[level] = curIndex;
|
||||
curIndex += sizePerLevel[level];
|
||||
}
|
||||
|
||||
// Calculate the node pointers for nodes in non-switch levels not using virtual nodes
|
||||
bool switchlevel = true;
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
{
|
||||
if (!switchlevel)
|
||||
{
|
||||
curIndex = levelOffsets[level];
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
const Node* node = tree->GetNode(i);
|
||||
if (node->GetLevel() == level)
|
||||
{
|
||||
nodePointers[i] = curIndex;
|
||||
curIndex += GetNormalNodeSize(tree, i, pointerSizesPerLevel, additionalBytesPerPointer, true);
|
||||
}
|
||||
}
|
||||
}
|
||||
if (useVirtualNodes[level]) switchlevel = true;
|
||||
else if (switchlevel == true) switchlevel = false;
|
||||
}
|
||||
|
||||
|
||||
curIndex = 0;
|
||||
// Write the level offsets
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
BitHelper::SplitInBytesAndMove(levelOffsets[level], pool, level * 4, 4);
|
||||
curIndex += 4 * (depth + 1);
|
||||
|
||||
// Write the pointer sizes per level
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
pool[curIndex++] = pointerSizesPerLevel[level];
|
||||
|
||||
// Write 4 bytes indicating which levels use virtual nodes
|
||||
unsigned32 levelsUsingVirtualNodesMask = 0;
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
BitHelper::SetLS(levelsUsingVirtualNodesMask, level, useVirtualNodes[level]);
|
||||
BitHelper::SplitInBytesAndMove(levelsUsingVirtualNodesMask, pool, curIndex);
|
||||
curIndex += 4;
|
||||
|
||||
// Write additional bytes per node
|
||||
if (HasAdditionalBytesPerNode(tree))
|
||||
{
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
pool[curIndex++] = additionalBytesPerNode[level];
|
||||
}
|
||||
|
||||
// Write additional bytes per pointer
|
||||
if (HasAdditionalBytesPerPointer(tree))
|
||||
{
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
pool[curIndex++] = additionalBytesPerPointer[level];
|
||||
}
|
||||
|
||||
// Leave some space for the additional pool info.
|
||||
// As the actual node pointers are not yet known, we write them later
|
||||
size_t additionalTreeInfoStart = curIndex;
|
||||
curIndex += tree->GetAdditionalTreeInfoSize();
|
||||
|
||||
// Find all roots (to make sure we write all reachable nodes from any root).
|
||||
std::vector<NodeToWrite> nextLevelNodes;
|
||||
std::vector<NodeToWrite> thisLevelNodes;
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
if (tree->GetNode(i)->GetLevel() == 0) thisLevelNodes.push_back(NodeToWrite(i, 0, useVirtualNodes[0] ? FULL : NORMAL, 0, 0));
|
||||
size_t nextLevelIndex;
|
||||
BoolArray writtenNodes(nodeCount);
|
||||
|
||||
// Write the full node pool
|
||||
for (unsigned8 level = 0; level <= depth; level++)
|
||||
{
|
||||
assert(curIndex = levelOffsets[level]);
|
||||
nextLevelNodes.clear();
|
||||
nextLevelIndex = 0;
|
||||
unsigned8 additionalNodeBytes = additionalBytesPerNode[level];
|
||||
unsigned8 additionalBytesForPointersToThisLevel = level > 0 ? additionalBytesPerPointer[level - 1] : 0;
|
||||
unsigned8 additionalBytesForPointersFromThisLevel = additionalBytesPerPointer[level];
|
||||
size_t childFullNodeSize = GetFullNodeSize(tree, level + 1, pointerSizesPerLevel);
|
||||
size_t childVirtualNodeSize = GetVirtualNodeSize(tree, level + 1, pointerSizesPerLevel);
|
||||
for (NodeToWrite nodeInfo : thisLevelNodes)
|
||||
{
|
||||
const Node* node = nodeInfo.GetNode(tree);
|
||||
unsigned32 nodeId = nodeInfo.nodeId;
|
||||
assert(level == node->GetLevel());
|
||||
if (nodeInfo.type == VIRTUAL)
|
||||
{ // Write a virtual node
|
||||
size_t virtualNodeSize = GetVirtualNodeSize(tree, level, pointerSizesPerLevel);
|
||||
BitHelper::SplitInBytesAndMove(nodePointers[nodeId] - levelOffsets[level], pool, curIndex, virtualNodeSize);
|
||||
curIndex += virtualNodeSize;
|
||||
}
|
||||
else if (nodeInfo.type == FULL)
|
||||
{ // Write a full node
|
||||
assert(useVirtualNodes[level]);
|
||||
nodePointers[nodeId] = curIndex;
|
||||
|
||||
WriteFullNode(tree, nodeId, (unsigned32)nextLevelIndex, pointerSizesPerLevel[level], writtenNodes, additionalNodeBytes, pool, curIndex);
|
||||
// Tell the next level which nodes should be written and in what order
|
||||
unsigned8 vMask = pool[curIndex + 1 + additionalNodeBytes];
|
||||
size_t nextLevelNodesOffset = nextLevelNodes.size();
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
if (node->HasChild(c))
|
||||
{
|
||||
NodeType type;
|
||||
if (!useVirtualNodes[level + 1]) type = BitHelper::GetLS(vMask, c) ? VIRTUAL : NORMAL;
|
||||
else type = BitHelper::GetLS(vMask, c) ? VIRTUAL : FULL;
|
||||
nextLevelNodes.push_back(NodeToWrite(node->GetChildIndex(c), level + 1, type, nodeId, c));
|
||||
}
|
||||
// Calculate the size of those nodes in the next layer.
|
||||
for (auto c = nextLevelNodes.begin() + nextLevelNodesOffset; c != nextLevelNodes.end(); c++)
|
||||
{
|
||||
switch (c->type)
|
||||
{
|
||||
case NORMAL: nextLevelIndex += GetNormalNodeSize(tree, c->nodeId, pointerSizesPerLevel, additionalBytesPerPointer, true); break;
|
||||
case FULL: nextLevelIndex += childFullNodeSize; break;
|
||||
case VIRTUAL: nextLevelIndex += childVirtualNodeSize; break;
|
||||
}
|
||||
}
|
||||
nextLevelIndex += node->GetChildCount() * additionalBytesForPointersFromThisLevel;
|
||||
curIndex += GetFullNodeSize(tree, level, pointerSizesPerLevel);
|
||||
}
|
||||
else if (nodeInfo.type == NORMAL)
|
||||
{
|
||||
assert(nodePointers[nodeId] == 0 || nodePointers[nodeId] == curIndex);
|
||||
nodePointers[nodeId] = curIndex;
|
||||
WriteNormalNode(tree, nodeId, additionalNodeBytes, pointerSizesPerLevel[level], nodePointers, level == depth ? 0 : levelOffsets[level + 1], pool, curIndex);
|
||||
curIndex += GetNormalNodeSize(tree, nodeId, pointerSizesPerLevel, additionalBytesPerPointer, false);
|
||||
// Write additional bytes per pointer
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (node->HasChild(c))
|
||||
{
|
||||
WriteAdditionalPointerInfo(tree, nodeId, c, additionalBytesForPointersFromThisLevel, pool, curIndex);
|
||||
curIndex += additionalBytesForPointersFromThisLevel;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (level > 0 && additionalBytesForPointersToThisLevel != 0 && useVirtualNodes[level - 1])
|
||||
{
|
||||
WriteAdditionalPointerInfo(tree, nodeInfo.parentId, nodeInfo.childIndexOfParent, additionalBytesForPointersToThisLevel, pool, curIndex);
|
||||
curIndex += additionalBytesForPointersToThisLevel;
|
||||
}
|
||||
}
|
||||
if (useVirtualNodes[level])
|
||||
thisLevelNodes = nextLevelNodes;
|
||||
else
|
||||
{
|
||||
thisLevelNodes.clear();
|
||||
for (unsigned32 i = 0; i < nodeCount; i++)
|
||||
{
|
||||
const Node* node = tree->GetNode(i);
|
||||
if (node->GetLevel() == level + 1)
|
||||
thisLevelNodes.push_back(NodeToWrite(i, level + 1, NORMAL, 0, 0));
|
||||
}
|
||||
}
|
||||
}
|
||||
std::vector<unsigned8> additionalTreeInfo = tree->GetAdditionalTreeInfo(nodePointers);
|
||||
std::move(additionalTreeInfo.begin(), additionalTreeInfo.end(), pool.begin() + additionalTreeInfoStart);
|
||||
mIsBuilding = false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void VirtualNodePoolBuilder::WriteFullNode(const BaseTree* tree, const unsigned32& nodeId, const unsigned32& childPointer, const unsigned8& childPointerSize, BoolArray& writtenNodes,
|
||||
const unsigned8& additionalNodeBytes, std::vector<unsigned8>& pool, const size_t& offset) const
|
||||
{
|
||||
size_t curIndex = offset;
|
||||
|
||||
const Node* node = tree->GetNode(nodeId);
|
||||
pool[curIndex++] = node->GetChildmask().mask;
|
||||
|
||||
// Write additional node info (if any)
|
||||
if (additionalNodeBytes != 0)
|
||||
{
|
||||
auto nodeBytes = tree->GetAdditionalNodeBytes(node);
|
||||
std::move(nodeBytes.begin(), nodeBytes.end(), pool.begin() + curIndex);
|
||||
assert(nodeBytes.size() == additionalNodeBytes);
|
||||
curIndex += additionalNodeBytes;
|
||||
}
|
||||
|
||||
// Build the "virtual mask" indicating which nodes have already been written in the next level and are virtual in this level
|
||||
pool[curIndex++] = GetVMask(tree, nodeId, writtenNodes);
|
||||
|
||||
// Write the pointer to the first child
|
||||
BitHelper::SplitInBytesAndMove(childPointer, pool, curIndex, childPointerSize);
|
||||
curIndex += 4;
|
||||
}
|
||||
|
||||
void VirtualNodePoolBuilder::WriteNormalNode(const BaseTree* tree, const unsigned32& nodeId, const unsigned8& additionalNodeBytes, const unsigned8& pointerSize, const std::vector<size_t>& nodePointers, const size_t& nextLevelOffset, std::vector<unsigned8>& pool, const size_t& offset) const
|
||||
{
|
||||
size_t curIndex = offset;
|
||||
|
||||
const Node* node = tree->GetNode(nodeId);
|
||||
pool[curIndex++] = node->GetChildmask().mask;
|
||||
|
||||
// Write additional node info (if any)
|
||||
if (additionalNodeBytes != 0)
|
||||
{
|
||||
auto nodeBytes = tree->GetAdditionalNodeBytes(node);
|
||||
std::move(nodeBytes.begin(), nodeBytes.end(), pool.begin() + curIndex);
|
||||
assert(nodeBytes.size() == additionalNodeBytes);
|
||||
curIndex += additionalNodeBytes;
|
||||
}
|
||||
|
||||
// Write the child pointers
|
||||
unsigned32* children = node->GetChildren();
|
||||
for (ChildIndex c = 0; c < node->GetChildCount(); c++)
|
||||
{
|
||||
unsigned32 child = children[c];
|
||||
size_t pointer = nodePointers[child] - nextLevelOffset;
|
||||
BitHelper::SplitInBytesAndMove(pointer, pool, curIndex, pointerSize);
|
||||
curIndex += pointerSize;
|
||||
}
|
||||
}
|
||||
|
||||
void VirtualNodePoolBuilder::WriteAdditionalPointerInfo(const BaseTree* tree, const unsigned32& nodeId, const ChildIndex& childId, const unsigned8& additionalPointerBytes, std::vector<unsigned8>& pool, const size_t& offset) const
|
||||
{
|
||||
auto pointerInfo = tree->GetAdditionalPointerBytes(tree->GetNode(nodeId), childId);
|
||||
std::move(pointerInfo.begin(), pointerInfo.end(), pool.begin() + offset);
|
||||
}
|
||||
|
||||
unsigned8 VirtualNodePoolBuilder::GetVMask(const BaseTree* tree, const unsigned32& nodeId, BoolArray& writtenNodes) const
|
||||
{
|
||||
const Node* node = tree->GetNode(nodeId);
|
||||
unsigned8 vMask = 0;
|
||||
for (ChildIndex c = 0; c < 8; c++)
|
||||
{
|
||||
if (node->HasChild(c))
|
||||
{
|
||||
unsigned32 childIndex = node->GetChildIndex(c);
|
||||
// A node is virtual if it has been written before
|
||||
BitHelper::SetLS(vMask, c, writtenNodes[childIndex]);
|
||||
writtenNodes.Set(childIndex, true);
|
||||
}
|
||||
}
|
||||
return vMask;
|
||||
}
|
||||
|
||||
bool VirtualNodePoolBuilder::VerifyPool(std::vector<unsigned8>& pool, const unsigned8& treeDepth) const
|
||||
{
|
||||
// TODO: Do some verification here.
|
||||
return true;
|
||||
}
|
||||
Reference in New Issue
Block a user