cleanups
parent
bb83ad08cf
commit
0df829eede
|
@ -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
|
||||||
|
|
Loading…
Reference in New Issue