SAH costs seem more reasonable now
parent
2144ce41bb
commit
577a732cd0
|
@ -23,10 +23,10 @@
|
||||||
#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
|
#define MTS_KD_MAX_DEPTH 48 ///< Compile-time KD-tree depth limit
|
||||||
#define MTS_KD_STATISTICS 1 ///< Collect statistics during building/traversal
|
#define MTS_KD_STATISTICS 1 ///< Collect statistics during building/traversal
|
||||||
#define MTS_KD_MINMAX_BINS 32 ///< Min-max bin count
|
#define MTS_KD_MINMAX_BINS 1024 ///< Min-max bin count
|
||||||
#define MTS_KD_MIN_ALLOC 128 ///< Allocate memory in 128 KB chunks
|
#define MTS_KD_MIN_ALLOC 128 ///< Allocate memory in 128 KB chunks
|
||||||
|
|
||||||
MTS_NAMESPACE_BEGIN
|
MTS_NAMESPACE_BEGIN
|
||||||
|
|
||||||
|
@ -53,19 +53,28 @@ public:
|
||||||
/**
|
/**
|
||||||
* \brief Release all memory used by the allocator
|
* \brief Release all memory used by the allocator
|
||||||
*/
|
*/
|
||||||
inline void cleanup() {
|
void cleanup() {
|
||||||
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);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* \brief Merge the chunks of another allocator into this one
|
||||||
|
*/
|
||||||
|
void merge(const OrderedChunkAllocator &other) {
|
||||||
|
m_chunks.reserve(m_chunks.size() + other.m_chunks.size());
|
||||||
|
m_chunks.insert(m_chunks.end(), other.m_chunks.begin(),
|
||||||
|
other.m_chunks.end());
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* \brief Request a block of memory from the allocator
|
* \brief Request a block of memory from the allocator
|
||||||
*
|
*
|
||||||
* Walks through the list of chunks to find one with enough
|
* Walks through the list of chunks to find one with enough
|
||||||
* free memory. If no chunk could be found, a new one is created.
|
* free memory. If no chunk could be found, a new one is created.
|
||||||
*/
|
*/
|
||||||
template <typename T> T *allocate(size_t size) {
|
template <typename T> T * __restrict__ allocate(size_t size) {
|
||||||
size *= sizeof(T);
|
size *= sizeof(T);
|
||||||
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) {
|
||||||
|
@ -118,7 +127,7 @@ public:
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SLog(EError, "OrderedChunkAllocator: Internal error in shrinkLast");
|
SLog(EError, "OrderedChunkAllocator: Internal error in shrinkAllocation");
|
||||||
}
|
}
|
||||||
|
|
||||||
inline size_t getChunkCount() const { return m_chunks.size(); }
|
inline size_t getChunkCount() const { return m_chunks.size(); }
|
||||||
|
@ -188,14 +197,12 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
inline void set(uint32_t index, uint8_t value) {
|
inline void set(uint32_t index, uint8_t value) {
|
||||||
SAssert((index >> 2) < m_bufferSize);
|
|
||||||
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;
|
||||||
*ptr = (*ptr & ~(3 << shift)) | (value << shift);
|
*ptr = (*ptr & ~(3 << shift)) | (value << shift);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline uint8_t get(uint32_t index) const {
|
inline uint8_t get(uint32_t index) const {
|
||||||
SAssert((index >> 2) < m_bufferSize);
|
|
||||||
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;
|
||||||
return (*ptr >> shift) & 3;
|
return (*ptr >> shift) & 3;
|
||||||
|
@ -218,6 +225,7 @@ template <typename Derived> class GenericKDTree : public Object {
|
||||||
protected:
|
protected:
|
||||||
struct KDNode;
|
struct KDNode;
|
||||||
struct EdgeEvent;
|
struct EdgeEvent;
|
||||||
|
struct EdgeEventOrdering;
|
||||||
|
|
||||||
public:
|
public:
|
||||||
/// Index number format (max 2^32 prims)
|
/// Index number format (max 2^32 prims)
|
||||||
|
@ -238,7 +246,7 @@ public:
|
||||||
m_stopPrims = 2;
|
m_stopPrims = 2;
|
||||||
m_maxBadRefines = 3;
|
m_maxBadRefines = 3;
|
||||||
m_exactDepth = 1;
|
m_exactDepth = 1;
|
||||||
m_maxDepth = 0;
|
m_maxDepth = 7;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -253,7 +261,8 @@ public:
|
||||||
|
|
||||||
/* 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)
|
||||||
m_maxDepth = std::min((int) (8 + 1.3f * log2i(primCount)), MTS_KD_MAX_DEPTH);
|
m_maxDepth = (int) (8 + 1.3f * log2i(primCount));
|
||||||
|
m_maxDepth = std::min(m_maxDepth, (size_type) MTS_KD_MAX_DEPTH);
|
||||||
|
|
||||||
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);
|
||||||
|
@ -288,6 +297,7 @@ public:
|
||||||
m_builders.resize(procCount);
|
m_builders.resize(procCount);
|
||||||
for (size_type i=0; i<procCount; ++i) {
|
for (size_type i=0; i<procCount; ++i) {
|
||||||
m_builders[i] = new SAHTreeBuilder(i+1, primCount, m_interface);
|
m_builders[i] = new SAHTreeBuilder(i+1, primCount, m_interface);
|
||||||
|
m_builders[i]->incRef();
|
||||||
m_builders[i]->start();
|
m_builders[i]->start();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -301,12 +311,12 @@ public:
|
||||||
Assert(ctx.leftAlloc.getUsed() == 0);
|
Assert(ctx.leftAlloc.getUsed() == 0);
|
||||||
Assert(ctx.rightAlloc.getUsed() == 0);
|
Assert(ctx.rightAlloc.getUsed() == 0);
|
||||||
|
|
||||||
Log(EInfo, "Finished -- took %i ms.", timer->getMilliseconds());
|
|
||||||
m_interface.done = true;
|
m_interface.done = true;
|
||||||
m_interface.cond->broadcast();
|
m_interface.cond->broadcast();
|
||||||
for (size_type i=0; i<procCount; ++i)
|
for (size_type i=0; i<procCount; ++i)
|
||||||
m_builders[i]->join();
|
m_builders[i]->join();
|
||||||
Log(EDebug, "");
|
Log(EDebug, "");
|
||||||
|
Log(EInfo, "Finished -- took %i ms.", timer->getMilliseconds());
|
||||||
|
|
||||||
Log(EDebug, "Memory allocation statistics:");
|
Log(EDebug, "Memory allocation statistics:");
|
||||||
Log(EDebug, " Classification storage : %.2f KiB",
|
Log(EDebug, " Classification storage : %.2f KiB",
|
||||||
|
@ -318,9 +328,12 @@ public:
|
||||||
for (size_type i=0; i<procCount; ++i) {
|
for (size_type i=0; i<procCount; ++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();
|
||||||
ctx.accumulateStatistics(subCtx);
|
|
||||||
subCtx.printStats();
|
subCtx.printStats();
|
||||||
|
ctx.accumulateStatistics(subCtx);
|
||||||
|
ctx.nodeAlloc.merge(subCtx.nodeAlloc);
|
||||||
|
m_builders[i]->decRef();
|
||||||
}
|
}
|
||||||
|
m_builders.clear();
|
||||||
Log(EDebug, "");
|
Log(EDebug, "");
|
||||||
|
|
||||||
Float rootSA = m_aabb.getSurfaceArea();
|
Float rootSA = m_aabb.getSurfaceArea();
|
||||||
|
@ -374,6 +387,27 @@ protected:
|
||||||
inline EdgeEvent(uint16_t type, uint16_t axis, float pos, index_type index)
|
inline EdgeEvent(uint16_t type, uint16_t axis, float pos, index_type index)
|
||||||
: pos(pos), index(index), type(type), axis(axis) { }
|
: pos(pos), index(index), type(type), axis(axis) { }
|
||||||
|
|
||||||
|
/// Return a string representation
|
||||||
|
std::string toString() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "EdgeEvent[" << endl
|
||||||
|
<< " pos = " << pos << "," << endl
|
||||||
|
<< " index = " << index << "," << endl
|
||||||
|
<< " type = ";
|
||||||
|
if (type == EEdgeEnd)
|
||||||
|
oss << "end";
|
||||||
|
else if (type == EEdgePlanar)
|
||||||
|
oss << "planar";
|
||||||
|
else if (type == EEdgeStart)
|
||||||
|
oss << "start";
|
||||||
|
else
|
||||||
|
oss << "unknown!";
|
||||||
|
oss << "," << endl
|
||||||
|
<< " axis = " << axis << endl
|
||||||
|
<<"]";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
|
|
||||||
/// Plane position
|
/// Plane position
|
||||||
float pos;
|
float pos;
|
||||||
/// Primitive index
|
/// Primitive index
|
||||||
|
@ -412,6 +446,19 @@ protected:
|
||||||
sahCost(std::numeric_limits<Float>::infinity()),
|
sahCost(std::numeric_limits<Float>::infinity()),
|
||||||
pos(0), axis(0), numLeft(0), numRight(0), planarLeft(false) {
|
pos(0), axis(0), numLeft(0), numRight(0), planarLeft(false) {
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::string toString() const {
|
||||||
|
std::ostringstream oss;
|
||||||
|
oss << "SplitCandidate[" << endl
|
||||||
|
<< " sahCost=" << sahCost << "," << endl
|
||||||
|
<< " pos=" << pos << "," << endl
|
||||||
|
<< " axis=" << axis << "," << endl
|
||||||
|
<< " numLeft=" << numLeft << "," << endl
|
||||||
|
<< " numRight=" << numRight << "," << endl
|
||||||
|
<< " planarLeft=" << (planarLeft ? "yes" : "no") << endl
|
||||||
|
<< "]";
|
||||||
|
return oss.str();
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -784,6 +831,12 @@ protected:
|
||||||
++badRefines;
|
++badRefines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
cout << "Depth " << depth << endl;
|
||||||
|
cout << "AABB: " << nodeAABB.toString() << endl;
|
||||||
|
cout << "SAH cost: " << leafCost << " -> " << bestSplit.toString() << endl;
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
/* Partitioning */
|
/* Partitioning */
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
|
@ -888,7 +941,6 @@ protected:
|
||||||
Float buildTreeSAH(BuildContext &ctx, unsigned int depth, KDNode *node,
|
Float buildTreeSAH(BuildContext &ctx, unsigned int depth, KDNode *node,
|
||||||
const AABB &nodeAABB, EdgeEvent *eventStart, EdgeEvent *eventEnd,
|
const AABB &nodeAABB, EdgeEvent *eventStart, EdgeEvent *eventEnd,
|
||||||
size_type primCount, bool isLeftChild, size_type badRefines) {
|
size_type primCount, bool isLeftChild, size_type badRefines) {
|
||||||
cout << "Depth: " << depth << endl;
|
|
||||||
|
|
||||||
Float leafCost = primCount * m_intersectionCost;
|
Float leafCost = primCount * m_intersectionCost;
|
||||||
if (primCount <= m_stopPrims || depth >= m_maxDepth) {
|
if (primCount <= m_stopPrims || depth >= m_maxDepth) {
|
||||||
|
@ -896,6 +948,33 @@ protected:
|
||||||
return leafCost;
|
return leafCost;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#if 0
|
||||||
|
EdgeEventOrdering ord;
|
||||||
|
int primCounts[3];
|
||||||
|
primCounts[0] = 0;
|
||||||
|
primCounts[1] = 0;
|
||||||
|
primCounts[2] = 0;
|
||||||
|
for (EdgeEvent *event = eventStart; event < eventEnd; ++event) {
|
||||||
|
Assert(event->axis >= 0 && event->axis < 3);
|
||||||
|
Assert(event->type >= 0 && event->type < 3);
|
||||||
|
if (event->type == EdgeEvent::EEdgePlanar
|
||||||
|
|| event->type == EdgeEvent::EEdgeStart)
|
||||||
|
primCounts[event->axis]++;
|
||||||
|
EdgeEvent *next = event+1;
|
||||||
|
if (next < eventEnd) {
|
||||||
|
if (!ord(*event, *next)) {
|
||||||
|
cout << event->toString() << endl;
|
||||||
|
cout << next->toString() << endl;
|
||||||
|
Assert(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Assert(primCounts[0] == primCount);
|
||||||
|
Assert(primCounts[1] == primCount);
|
||||||
|
Assert(primCounts[2] == primCount);
|
||||||
|
#endif
|
||||||
|
|
||||||
SplitCandidate bestSplit;
|
SplitCandidate bestSplit;
|
||||||
Float invSA = 1.0f / nodeAABB.getSurfaceArea();
|
Float invSA = 1.0f / nodeAABB.getSurfaceArea();
|
||||||
|
|
||||||
|
@ -928,19 +1007,19 @@ protected:
|
||||||
size_type numStart = 0, numEnd = 0, numPlanar = 0;
|
size_type numStart = 0, numEnd = 0, numPlanar = 0;
|
||||||
|
|
||||||
/* Count "end" events */
|
/* Count "end" events */
|
||||||
while (event != eventEnd && event->pos == pos && event->axis == axis
|
while (event < eventEnd && event->pos == pos && event->axis == axis
|
||||||
&& event->type == EdgeEvent::EEdgeEnd) {
|
&& event->type == EdgeEvent::EEdgeEnd) {
|
||||||
++numEnd; ++event;
|
++numEnd; ++event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Count "planar" events */
|
/* Count "planar" events */
|
||||||
while (event != eventEnd && event->pos == pos && event->axis == axis
|
while (event < eventEnd && event->pos == pos && event->axis == axis
|
||||||
&& event->type == EdgeEvent::EEdgePlanar) {
|
&& event->type == EdgeEvent::EEdgePlanar) {
|
||||||
++numPlanar; ++event;
|
++numPlanar; ++event;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Count "start" events */
|
/* Count "start" events */
|
||||||
while (event != eventEnd && event->pos == pos && event->axis == axis
|
while (event < eventEnd && event->pos == pos && event->axis == axis
|
||||||
&& event->type == EdgeEvent::EEdgeStart) {
|
&& event->type == EdgeEvent::EEdgeStart) {
|
||||||
++numStart; ++event;
|
++numStart; ++event;
|
||||||
}
|
}
|
||||||
|
@ -957,7 +1036,7 @@ protected:
|
||||||
|
|
||||||
/* Calculate a score using the surface area heuristic */
|
/* Calculate a score using the surface area heuristic */
|
||||||
if (EXPECT_TAKEN(pos >= nodeAABB.min[axis] && pos <= nodeAABB.max[axis])) {
|
if (EXPECT_TAKEN(pos >= nodeAABB.min[axis] && pos <= nodeAABB.max[axis])) {
|
||||||
Float tmp = m_aabb.max[axis];
|
Float tmp = nodeAABB.max[axis];
|
||||||
aabb.max[axis] = pos;
|
aabb.max[axis] = pos;
|
||||||
Float pLeft = invSA * aabb.getSurfaceArea();
|
Float pLeft = invSA * aabb.getSurfaceArea();
|
||||||
aabb.max[axis] = tmp;
|
aabb.max[axis] = tmp;
|
||||||
|
@ -1008,6 +1087,11 @@ protected:
|
||||||
|
|
||||||
Assert(bestSplit.sahCost != std::numeric_limits<Float>::infinity());
|
Assert(bestSplit.sahCost != std::numeric_limits<Float>::infinity());
|
||||||
|
|
||||||
|
cout << "Depth " << depth << endl;
|
||||||
|
cout << "AABB: " << nodeAABB.toString() << endl;
|
||||||
|
cout << "SAH cost: " << leafCost << " -> " << bestSplit.toString() << endl;
|
||||||
|
cout << endl;
|
||||||
|
|
||||||
/* "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)
|
||||||
|
@ -1015,6 +1099,7 @@ protected:
|
||||||
createLeaf(ctx, node, nodeAABB, primCount);
|
createLeaf(ctx, node, nodeAABB, primCount);
|
||||||
return leafCost;
|
return leafCost;
|
||||||
}
|
}
|
||||||
|
cout << "Increasing bad refines " << primCount << ", leafCost=" << leafCost << ", sahCost=" << bestSplit.sahCost << endl;
|
||||||
++badRefines;
|
++badRefines;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1188,31 +1273,44 @@ protected:
|
||||||
rightAlloc.release(newEventsRightStart);
|
rightAlloc.release(newEventsRightStart);
|
||||||
rightAlloc.release(rightEventsTempStart);
|
rightAlloc.release(rightEventsTempStart);
|
||||||
} else {
|
} else {
|
||||||
for (EdgeEvent *event = eventStart; event<eventEnd; ++event) {
|
for (EdgeEvent *event = eventStart; event < eventEnd; ++event) {
|
||||||
uint8_t classification = storage.get(event->index);
|
uint8_t classification = storage.get(event->index);
|
||||||
if (classification == ELeftSide) {
|
if (classification == ELeftSide) {
|
||||||
/* Left-only primitive. Move to the left list and advance */
|
/* Left-only primitive. Move to the left list and advance */
|
||||||
*leftEventsEnd++ = *event;
|
if (leftEventsEnd == event)
|
||||||
|
leftEventsEnd++;
|
||||||
|
else
|
||||||
|
*leftEventsEnd++ = *event;
|
||||||
} else if (classification == ERightSide) {
|
} else if (classification == ERightSide) {
|
||||||
/* Right-only primitive. Move to the right list and advance */
|
/* Right-only primitive. Move to the right list and advance */
|
||||||
*rightEventsEnd++ = *event;
|
if (rightEventsEnd == event)
|
||||||
|
rightEventsEnd++;
|
||||||
|
else
|
||||||
|
*rightEventsEnd++ = *event;
|
||||||
} else if (classification == EBothSides) {
|
} else if (classification == EBothSides) {
|
||||||
/* The primitive overlaps the split plane. Its edge events
|
/* The primitive overlaps the split plane. Its edge events
|
||||||
must be added to both lists. */
|
must be added to both lists. */
|
||||||
*leftEventsEnd++ = *event;
|
if (leftEventsEnd == event)
|
||||||
*rightEventsEnd++ = *event;
|
leftEventsEnd++;
|
||||||
|
else
|
||||||
|
*leftEventsEnd++ = *event;
|
||||||
|
if (rightEventsEnd == event)
|
||||||
|
rightEventsEnd++;
|
||||||
|
else
|
||||||
|
*rightEventsEnd++ = *event;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Assert(leftEventsEnd - leftEventsStart <= bestSplit.numLeft * 6);
|
||||||
|
Assert(rightEventsEnd - rightEventsStart <= bestSplit.numRight * 6);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Shrink the edge event storage now that we know exactly how
|
/* Shrink the edge event storage now that we know exactly how
|
||||||
many are on each side */
|
many are on each side */
|
||||||
ctx.leftAlloc.shrinkAllocation(leftEventsStart,
|
// ctx.leftAlloc.shrinkAllocation(leftEventsStart,
|
||||||
leftEventsEnd - leftEventsStart);
|
// leftEventsEnd - leftEventsStart);
|
||||||
|
|
||||||
ctx.rightAlloc.shrinkAllocation(rightEventsStart,
|
// ctx.rightAlloc.shrinkAllocation(rightEventsStart,
|
||||||
rightEventsEnd - rightEventsStart);
|
// rightEventsEnd - rightEventsStart);
|
||||||
|
|
||||||
/* ==================================================================== */
|
/* ==================================================================== */
|
||||||
/* Recursion */
|
/* Recursion */
|
||||||
|
|
Loading…
Reference in New Issue