metadata
Wenzel Jakob 2010-10-10 22:38:40 +02:00
parent bb83ad08cf
commit 0df829eede
1 changed files with 177 additions and 93 deletions

View File

@ -23,12 +23,16 @@
#include <boost/static_assert.hpp> #include <boost/static_assert.hpp>
#include <boost/tuple/tuple.hpp> #include <boost/tuple/tuple.hpp>
#define MTS_KD_MAX_DEPTH 48 ///< Compile-time KD-tree depth limit /// Compile-time KD-tree depth limit
#define MTS_KD_STATISTICS 1 ///< Collect statistics during building/traversal #define MTS_KD_MAX_DEPTH 48
#define MTS_KD_MINMAX_BINS 32 ///< Min-max bin count /// Collect statistics during building/traversal
#define MTS_KD_MIN_ALLOC 128 ///< Allocate memory in 128 KB chunks #define MTS_KD_STATISTICS 1
/// Min-max bin count
#define MTS_KD_MINMAX_BINS 32
/// Allocate memory in chunks of 512KB
#define MTS_KD_MIN_ALLOC 512*1024
#if 1 #if MTS_KD_DEBUG
#define KDAssert(expr) Assert(expr) #define KDAssert(expr) Assert(expr)
#define KDAssertEx(expr, text) AssertEx(expr, text) #define KDAssertEx(expr, text) AssertEx(expr, text)
#else #else
@ -58,6 +62,14 @@ public:
m_chunks.reserve(16); m_chunks.reserve(16);
} }
~OrderedChunkAllocator() {
cleanup();
}
inline void setMinAllocation(size_t minAllocation) {
m_minAllocation = minAllocation;
}
/** /**
* \brief Release all memory used by the allocator * \brief Release all memory used by the allocator
*/ */
@ -65,8 +77,9 @@ public:
for (std::vector<Chunk>::iterator it = m_chunks.begin(); for (std::vector<Chunk>::iterator it = m_chunks.begin();
it != m_chunks.end(); ++it) it != m_chunks.end(); ++it)
freeAligned((*it).start); freeAligned((*it).start);
m_chunks.clear();
} }
/** /**
* \brief Merge the chunks of another allocator into this one * \brief Merge the chunks of another allocator into this one
*/ */
@ -76,6 +89,15 @@ public:
other.m_chunks.end()); other.m_chunks.end());
} }
/**
* \brief Forget about all chunks without actually freeing them.
* This is useful when the chunks have been merged into another
* allocator.
*/
void forget() {
m_chunks.clear();
}
/** /**
* \brief Request a block of memory from the allocator * \brief Request a block of memory from the allocator
* *
@ -96,7 +118,7 @@ public:
/* No chunk had enough free memory */ /* No chunk had enough free memory */
size_t allocSize = std::max(size, size_t allocSize = std::max(size,
(size_t) (MTS_KD_MIN_ALLOC * 1024)); m_minAllocation);
Chunk chunk; Chunk chunk;
chunk.start = (uint8_t *) allocAligned(allocSize); chunk.start = (uint8_t *) allocAligned(allocSize);
@ -117,6 +139,7 @@ public:
return; return;
} }
} }
#if defined(MTS_KD_DEBUG)
/* Uh oh, allocation could not be found. Check if it has size==0 */ /* Uh oh, allocation could not be found. Check if it has size==0 */
for (std::vector<Chunk>::iterator it = m_chunks.begin(); for (std::vector<Chunk>::iterator it = m_chunks.begin();
it != m_chunks.end(); ++it) { it != m_chunks.end(); ++it) {
@ -126,6 +149,7 @@ public:
} }
SLog(EError, "OrderedChunkAllocator: Internal error while" SLog(EError, "OrderedChunkAllocator: Internal error while"
" releasing memory"); " releasing memory");
#endif
} }
/** /**
@ -142,6 +166,7 @@ public:
return; return;
} }
} }
#if defined(MTS_KD_DEBUG)
/* Uh oh, allocation could not be found. Check if it has size==0 */ /* Uh oh, allocation could not be found. Check if it has size==0 */
if (newSize == 0) { if (newSize == 0) {
for (std::vector<Chunk>::iterator it = m_chunks.begin(); for (std::vector<Chunk>::iterator it = m_chunks.begin();
@ -153,6 +178,7 @@ public:
} }
SLog(EError, "OrderedChunkAllocator: Internal error while" SLog(EError, "OrderedChunkAllocator: Internal error while"
" releasing memory"); " releasing memory");
#endif
} }
inline size_t getChunkCount() const { return m_chunks.size(); } inline size_t getChunkCount() const { return m_chunks.size(); }
@ -206,11 +232,12 @@ private:
std::string toString() const { std::string toString() const {
return formatString("0x%llx-0x%llx (size=" SIZE_T_FMT return formatString("0x%llx-0x%llx (size=" SIZE_T_FMT
", used=" SIZE_T_FMT ")", start, start+size, ", used=" SIZE_T_FMT ")", start, start+size,
size, getUsed()); size, getUsed());
} }
}; };
size_t m_minAllocation;
std::vector<Chunk> m_chunks; std::vector<Chunk> m_chunks;
}; };
@ -225,20 +252,24 @@ private:
*/ */
class ClassificationStorage { class ClassificationStorage {
public: public:
inline ClassificationStorage() : m_buffer(NULL), ClassificationStorage(size_t size = 0) : m_buffer(NULL), m_bufferSize(0) { }
m_bufferSize(0) {
}
inline ClassificationStorage(size_t size) { ~ClassificationStorage() {
m_bufferSize = size/4 + ((size % 4) > 0 ? 1 : 0);
m_buffer = new uint8_t[m_bufferSize];
}
inline ~ClassificationStorage() {
if (m_buffer) if (m_buffer)
delete[] m_buffer; delete[] m_buffer;
} }
void setPrimitiveCount(size_t size) {
if (m_buffer)
delete[] m_buffer;
if (size > 0) {
m_bufferSize = size/4 + ((size % 4) > 0 ? 1 : 0);
m_buffer = new uint8_t[m_bufferSize];
} else {
m_buffer = NULL;
}
}
inline void set(uint32_t index, uint8_t value) { inline void set(uint32_t index, uint8_t value) {
uint8_t *ptr = m_buffer + (index >> 2); uint8_t *ptr = m_buffer + (index >> 2);
uint8_t shift = (index & 3) << 1; uint8_t shift = (index & 3) << 1;
@ -281,16 +312,22 @@ public:
* \brief Create a new kd-tree instance initialized with * \brief Create a new kd-tree instance initialized with
* the default parameters. * the default parameters.
*/ */
GenericKDTree() : m_root(NULL) { GenericKDTree() : m_root(NULL), m_primIndices(NULL) {
m_traversalCost = 15; m_traversalCost = 15;
m_intersectionCost = 20; m_intersectionCost = 20;
m_emptySpaceBonus = 0.9f; m_emptySpaceBonus = 0.9f;
m_clip = true; m_clip = true;
m_stopPrims = 1; m_stopPrims = 4;
m_maxBadRefines = 2; m_maxBadRefines = 2;
m_exactPrimThreshold = 4096; m_exactPrimThreshold = 4096;
m_maxDepth = 0; m_maxDepth = 0;
m_retract = true; m_retract = true;
m_parallel = false;
}
virtual ~GenericKDTree() {
if (m_primIndices)
delete[] m_primIndices;
} }
/** /**
@ -301,7 +338,7 @@ public:
Log(EError, "The kd-tree has already been built!"); Log(EError, "The kd-tree has already been built!");
size_type primCount = downCast()->getPrimitiveCount(); size_type primCount = downCast()->getPrimitiveCount();
BuildContext ctx(primCount); m_mainContext.init(primCount);
/* Establish an ad-hoc depth cutoff value (Formula from PBRT) */ /* Establish an ad-hoc depth cutoff value (Formula from PBRT) */
if (m_maxDepth == 0) if (m_maxDepth == 0)
@ -311,7 +348,8 @@ public:
Log(EDebug, "Creating a preliminary index list (%.2f KiB)", Log(EDebug, "Creating a preliminary index list (%.2f KiB)",
primCount * sizeof(index_type) / 1024.0f); primCount * sizeof(index_type) / 1024.0f);
OrderedChunkAllocator &leftAlloc = ctx.leftAlloc, &nodeAlloc = ctx.nodeAlloc; OrderedChunkAllocator &leftAlloc = m_mainContext.leftAlloc,
&nodeAlloc = m_mainContext.nodeAlloc;
index_type *indices = leftAlloc.allocate<index_type>(primCount); index_type *indices = leftAlloc.allocate<index_type>(primCount);
ref<Timer> timer = new Timer(); ref<Timer> timer = new Timer();
@ -339,78 +377,106 @@ public:
Log(EDebug, ""); Log(EDebug, "");
size_type procCount = getProcessorCount(); size_type procCount = getProcessorCount();
m_builders.resize(procCount); if (procCount == 1)
for (size_type i=0; i<procCount; ++i) { m_parallel = false;
m_builders[i] = new SAHTreeBuilder(i+1, primCount, m_interface);
m_builders[i]->incRef(); if (m_parallel) {
m_builders[i]->start(); m_builders.resize(procCount);
for (size_type i=0; i<procCount; ++i) {
m_builders[i] = new SAHTreeBuilder(i+1, primCount, m_interface);
m_builders[i]->incRef();
m_builders[i]->start();
}
} }
Log(EInfo, "Constructing a SAH kd-tree (%i primitives) ..", primCount); Log(EInfo, "Constructing a SAH kd-tree (%i primitives) ..", primCount);
m_root = nodeAlloc.allocate<KDNode>(1); m_root = nodeAlloc.allocate<KDNode>(1);
Float finalSAHCost = buildTreeMinMax(ctx, 1, m_root, Float finalSAHCost = buildTreeMinMax(m_mainContext, 1, m_root,
m_aabb, m_aabb, indices, primCount, true, 0); m_aabb, m_aabb, indices, primCount, true, 0);
ctx.leftAlloc.release(indices); m_mainContext.leftAlloc.release(indices);
KDAssert(ctx.leftAlloc.getUsed() == 0); KDAssert(m_mainContext.leftAlloc.getUsed() == 0);
KDAssert(ctx.rightAlloc.getUsed() == 0); KDAssert(m_mainContext.rightAlloc.getUsed() == 0);
m_interface.done = true; if (m_parallel) {
m_interface.cond->broadcast(); m_interface.done = true;
for (size_type i=0; i<procCount; ++i) m_interface.cond->broadcast();
m_builders[i]->join(); for (size_type i=0; i<m_builders.size(); ++i)
Log(EDebug, ""); m_builders[i]->join();
}
Log(EInfo, "Finished -- took %i ms.", timer->getMilliseconds()); Log(EInfo, "Finished -- took %i ms.", timer->getMilliseconds());
Log(EDebug, "");
Log(EDebug, "Memory allocation statistics:"); Log(EDebug, "Memory allocation statistics:");
Log(EDebug, " Classification storage : %.2f KiB", Log(EDebug, " Temporary classification storage : %.2f KiB",
(ctx.storage.getSize() * (1+procCount)) / 1024.0f); (m_mainContext.storage.getSize() * (1+procCount)) / 1024.0f);
Log(EDebug, " Main:"); Log(EDebug, " Main:");
ctx.printStats(); m_mainContext.printStats();
m_mainContext.leftAlloc.cleanup();
m_mainContext.rightAlloc.cleanup();
for (size_type i=0; i<procCount; ++i) { for (size_type i=0; i<m_builders.size(); ++i) {
Log(EDebug, " Thread %i:", i+1); Log(EDebug, " Thread %i:", i+1);
BuildContext &subCtx = m_builders[i]->getContext(); BuildContext &subCtx = m_builders[i]->getContext();
subCtx.printStats(); subCtx.printStats();
ctx.accumulateStatistics(subCtx); subCtx.leftAlloc.cleanup();
ctx.nodeAlloc.merge(subCtx.nodeAlloc); subCtx.rightAlloc.cleanup();
m_builders[i]->decRef(); m_mainContext.nodeAlloc.merge(subCtx.nodeAlloc);
subCtx.nodeAlloc.forget();
m_mainContext.accumulateStatisticsFrom(subCtx);
}
Log(EDebug, "");
Log(EDebug, "Flattening index lists..");
m_mainContext.indexAlloc.cleanup();
for (size_type i=0; i<m_builders.size(); ++i)
m_builders[i]->getContext().indexAlloc.cleanup();
if (m_parallel) {
for (size_type i=0; i<m_builders.size(); ++i)
m_builders[i]->decRef();
m_builders.clear();
} }
m_builders.clear();
Log(EDebug, ""); Log(EDebug, "");
Float rootSA = m_aabb.getSurfaceArea(); Float rootSA = m_aabb.getSurfaceArea();
ctx.expTraversalSteps /= rootSA; // expTraversalSteps /= rootSA;
ctx.expLeavesVisited /= rootSA; // expLeavesVisited /= rootSA;
ctx.expPrimitivesIntersected /= rootSA; // expPrimitivesIntersected /= rootSA;
Log(EDebug, "Detailed kd-tree statistics:"); Log(EDebug, "Detailed kd-tree statistics:");
Log(EDebug, " Final SAH cost : %.2f", finalSAHCost); Log(EDebug, " Final SAH cost : %.2f", finalSAHCost);
Log(EDebug, " Inner nodes : %i", ctx.innerNodeCount); Log(EDebug, " Inner nodes : %i", m_mainContext.innerNodeCount);
Log(EDebug, " Leaf nodes : %i", ctx.leafNodeCount); Log(EDebug, " Leaf nodes : %i", m_mainContext.leafNodeCount);
Log(EDebug, " Nonempty leaf nodes : %i", ctx.nonemptyLeafNodeCount); Log(EDebug, " Nonempty leaf nodes : %i", m_mainContext.nonemptyLeafNodeCount);
Log(EDebug, " Retracted splits : %i", ctx.retractedSplits); Log(EDebug, " Retracted splits : %i", m_mainContext.retractedSplits);
Log(EDebug, " Pruned primitives : %i", ctx.pruned); Log(EDebug, " Pruned primitives : %i", m_mainContext.pruned);
Log(EDebug, " Exp. traversals : %.2f", ctx.expTraversalSteps); // Log(EDebug, " Exp. traversals : %.2f", expTraversalSteps);
Log(EDebug, " Exp. leaf visits : %.2f", ctx.expLeavesVisited); // Log(EDebug, " Exp. leaf visits : %.2f", expLeavesVisited);
Log(EDebug, " Exp. intersections : %.2f", ctx.expPrimitivesIntersected); // Log(EDebug, " Exp. intersections : %.2f", expPrimitivesIntersected);
Log(EDebug, " Indirection table : " SIZE_T_FMT " entries", Log(EDebug, " Indirection table : " SIZE_T_FMT " entries",
m_indirectionTable.size()); m_indirectionTable.size());
Log(EDebug, " Mem. usage (nodes) : %.2f KiB",
m_mainContext.nodeAlloc.getSize()/1024.0f);
Log(EDebug, ""); Log(EDebug, "");
ctx.leftAlloc.cleanup();
ctx.rightAlloc.cleanup();
m_aabb.getSurfaceArea();
} }
protected: protected:
/// Primitive classification during tree-construction /// Primitive classification during tree-construction
enum EClassificationResult { enum EClassificationResult {
///< Straddling primitive
EBothSides = 0, EBothSides = 0,
///< Primitive is entirely on the left side of the split
ELeftSide = 1, ELeftSide = 1,
///< Primitive is entirely on the right side of the split
ERightSide = 2, ERightSide = 2,
EBothSidesProcessed = 3 //< Used to indicate that edge events have already been generated for a straddling primitive //< Edge events have been generated for the straddling primitive
EBothSidesProcessed = 3
}; };
/** /**
@ -511,47 +577,53 @@ protected:
* also records some useful statistics. * also records some useful statistics.
*/ */
struct BuildContext { struct BuildContext {
OrderedChunkAllocator leftAlloc, rightAlloc, nodeAlloc; OrderedChunkAllocator leftAlloc, rightAlloc;
OrderedChunkAllocator nodeAlloc, indexAlloc;
ClassificationStorage storage; ClassificationStorage storage;
size_type leafNodeCount; size_type leafNodeCount;
size_type nonemptyLeafNodeCount; size_type nonemptyLeafNodeCount;
size_type innerNodeCount; size_type innerNodeCount;
size_type retractedSplits; size_type retractedSplits;
size_type pruned; size_type pruned;
Float expTraversalSteps; size_type indexCtr;
Float expLeavesVisited;
Float expPrimitivesIntersected;
inline BuildContext(size_type primCount) : storage(primCount) { BuildContext() {
retractedSplits = 0; retractedSplits = 0;
leafNodeCount = 0; leafNodeCount = 0;
nonemptyLeafNodeCount = 0; nonemptyLeafNodeCount = 0;
innerNodeCount = 0; innerNodeCount = 0;
pruned = 0; pruned = 0;
expTraversalSteps = 0; indexCtr = 0;
expLeavesVisited = 0; }
expPrimitivesIntersected = 0;
void init(size_type primCount) {
leftAlloc.setMinAllocation(MTS_KD_MIN_ALLOC);
rightAlloc.setMinAllocation(MTS_KD_MIN_ALLOC);
nodeAlloc.setMinAllocation(std::max(primCount/16,
(size_type) MTS_KD_MIN_ALLOC));
indexAlloc.setMinAllocation(std::max(primCount/16,
(size_type) MTS_KD_MIN_ALLOC));
storage.setPrimitiveCount(primCount);
} }
void printStats() { void printStats() {
Log(EDebug, " Left: " SIZE_T_FMT " chunks (%.2f KiB)", Log(EDebug, " Left: " SIZE_T_FMT " chunks (%.2f KiB)",
leftAlloc.getChunkCount(), leftAlloc.getSize() / 1024.0f); leftAlloc.getChunkCount(), leftAlloc.getSize() / 1024.0f);
Log(EDebug, " Right: " SIZE_T_FMT " chunks (%.2f KiB)", Log(EDebug, " Right: " SIZE_T_FMT " chunks (%.2f KiB)",
rightAlloc.getChunkCount(), rightAlloc.getSize() / 1024.0f); rightAlloc.getChunkCount(), rightAlloc.getSize() / 1024.0f);
Log(EDebug, " Nodes: " SIZE_T_FMT " chunks (%.2f KiB)", Log(EDebug, " Nodes: " SIZE_T_FMT " chunks (%.2f KiB)",
nodeAlloc.getChunkCount(), nodeAlloc.getSize() / 1024.0f); nodeAlloc.getChunkCount(), nodeAlloc.getSize() / 1024.0f);
Log(EDebug, " Indices: " SIZE_T_FMT " chunks (%.2f KiB)",
indexAlloc.getChunkCount(), indexAlloc.getSize() / 1024.0f);
} }
void accumulateStatistics(const BuildContext &ctx) { void accumulateStatisticsFrom(const BuildContext &ctx) {
leafNodeCount += ctx.leafNodeCount; leafNodeCount += ctx.leafNodeCount;
nonemptyLeafNodeCount += ctx.nonemptyLeafNodeCount; nonemptyLeafNodeCount += ctx.nonemptyLeafNodeCount;
innerNodeCount += ctx.innerNodeCount; innerNodeCount += ctx.innerNodeCount;
retractedSplits += ctx.retractedSplits; retractedSplits += ctx.retractedSplits;
pruned += ctx.pruned; pruned += ctx.pruned;
expTraversalSteps += ctx.expTraversalSteps;
expLeavesVisited += ctx.expLeavesVisited;
expPrimitivesIntersected += ctx.expPrimitivesIntersected;
} }
}; };
@ -713,7 +785,14 @@ protected:
class SAHTreeBuilder : public Thread { class SAHTreeBuilder : public Thread {
public: public:
SAHTreeBuilder(size_type idx, size_type primCount, BuildInterface &interface) SAHTreeBuilder(size_type idx, size_type primCount, BuildInterface &interface)
: Thread(formatString("bld%i", idx)), m_context(primCount), m_interface(interface) { } : Thread(formatString("bld%i", idx)), m_interface(interface) {
m_context.init(primCount);
}
~SAHTreeBuilder() {
KDAssert(m_context.leftAlloc.getUsed() == 0);
KDAssert(m_context.rightAlloc.getUsed() == 0);
}
void run() { void run() {
m_interface.mutex->lock(); m_interface.mutex->lock();
@ -796,12 +875,16 @@ protected:
* Total primitive count for the current node * Total primitive count for the current node
*/ */
void createLeaf(BuildContext &ctx, KDNode *node, const AABB &nodeAABB, size_type primCount) { void createLeaf(BuildContext &ctx, KDNode *node, const AABB &nodeAABB, size_type primCount) {
node->initLeafNode(0, primCount); node->initLeafNode(ctx.indexCtr, primCount);
ctx.leafNodeCount++; if (primCount > 0) {
ctx.expLeavesVisited += nodeAABB.getSurfaceArea();
ctx.expPrimitivesIntersected += primCount * nodeAABB.getSurfaceArea();
if (primCount > 0)
ctx.nonemptyLeafNodeCount++; ctx.nonemptyLeafNodeCount++;
OrderedChunkAllocator &alloc = ctx.indexAlloc;
index_type *alloc = alloc.allocate<index_type>(primCount);
ctx.indexCtr += primCount;
}
ctx.leafNodeCount++;
} }
@ -935,14 +1018,13 @@ protected:
if (isLeftChild) if (isLeftChild)
ctx.rightAlloc.release(boost::get<3>(partition)); ctx.rightAlloc.release(boost::get<3>(partition));
else else
ctx.leftAlloc.release(boost::get<1>(partition)); ctx.leftAlloc.release(boost::get<1>(partition));
/* ==================================================================== */ /* ==================================================================== */
/* Final decision */ /* Final decision */
/* ==================================================================== */ /* ==================================================================== */
if (!m_retract || finalSAHCost < primCount * m_intersectionCost) { if (!m_retract || finalSAHCost < primCount * m_intersectionCost) {
ctx.expTraversalSteps += nodeAABB.getSurfaceArea();
return finalSAHCost; return finalSAHCost;
} else { } else {
/* In the end, splitting didn't help to reduce the SAH cost. /* In the end, splitting didn't help to reduce the SAH cost.
@ -1110,12 +1192,11 @@ protected:
KDAssert(eventsByAxis[1]->axis == 1 && (eventsByAxis[1]-1)->axis == 0); KDAssert(eventsByAxis[1]->axis == 1 && (eventsByAxis[1]-1)->axis == 0);
KDAssert(eventsByAxis[2]->axis == 2 && (eventsByAxis[2]-1)->axis == 1); KDAssert(eventsByAxis[2]->axis == 2 && (eventsByAxis[2]-1)->axis == 1);
KDAssert(bestSplit.sahCost != std::numeric_limits<Float>::infinity());
/* "Bad refines" heuristic from PBRT */ /* "Bad refines" heuristic from PBRT */
if (bestSplit.sahCost >= leafCost) { if (bestSplit.sahCost >= leafCost) {
if ((bestSplit.sahCost > 4 * leafCost && primCount < 16) if ((bestSplit.sahCost > 4 * leafCost && primCount < 16)
|| badRefines >= m_maxBadRefines) { || badRefines >= m_maxBadRefines
|| bestSplit.sahCost == std::numeric_limits<Float>::infinity()) {
createLeaf(ctx, node, nodeAABB, primCount); createLeaf(ctx, node, nodeAABB, primCount);
return leafCost; return leafCost;
} }
@ -1365,7 +1446,6 @@ protected:
/* ==================================================================== */ /* ==================================================================== */
if (!m_retract || finalSAHCost < primCount * m_intersectionCost) { if (!m_retract || finalSAHCost < primCount * m_intersectionCost) {
ctx.expTraversalSteps += nodeAABB.getSurfaceArea();
return finalSAHCost; return finalSAHCost;
} else { } else {
/* In the end, splitting didn't help to reduce the SAH cost. /* In the end, splitting didn't help to reduce the SAH cost.
@ -1386,10 +1466,12 @@ protected:
*/ */
void tearUp(BuildContext &ctx, KDNode *node) { void tearUp(BuildContext &ctx, KDNode *node) {
if (node->isLeaf()) { if (node->isLeaf()) {
if (node->getPrimStart() != node->getPrimEnd()) size_type primCount = node->getPrimEnd() - node->getPrimStart();
if (primCount > 0) {
ctx.nonemptyLeafNodeCount--; ctx.nonemptyLeafNodeCount--;
ctx.indexCtr -= primCount;
}
ctx.leafNodeCount--; ctx.leafNodeCount--;
/// XXX Create primitive list for leaf
} else { } else {
KDNode *left; KDNode *left;
ctx.innerNodeCount--; ctx.innerNodeCount--;
@ -1398,8 +1480,8 @@ protected:
else else
left = m_indirectionTable[node->getIndirectionIndex()]; left = m_indirectionTable[node->getIndirectionIndex()];
tearUp(ctx, left);
tearUp(ctx, left+1); tearUp(ctx, left+1);
tearUp(ctx, left);
ctx.nodeAlloc.release(left); ctx.nodeAlloc.release(left);
} }
@ -1671,11 +1753,12 @@ protected:
private: private:
KDNode *m_root; KDNode *m_root;
index_type *m_primIndices;
std::vector<KDNode *> m_indirectionTable; std::vector<KDNode *> m_indirectionTable;
Float m_traversalCost; Float m_traversalCost;
Float m_intersectionCost; Float m_intersectionCost;
Float m_emptySpaceBonus; Float m_emptySpaceBonus;
bool m_clip, m_retract; bool m_clip, m_retract, m_parallel;
AABB m_aabb; AABB m_aabb;
size_type m_maxDepth; size_type m_maxDepth;
size_type m_stopPrims; size_type m_stopPrims;
@ -1683,6 +1766,7 @@ private:
size_type m_exactPrimThreshold; size_type m_exactPrimThreshold;
std::vector<SAHTreeBuilder *> m_builders; std::vector<SAHTreeBuilder *> m_builders;
BuildInterface m_interface; BuildInterface m_interface;
BuildContext m_mainContext;
}; };
MTS_NAMESPACE_END MTS_NAMESPACE_END