diff --git a/include/mitsuba/core/octree.h b/include/mitsuba/core/octree.h index 8cf687d9..7bef0e33 100644 --- a/include/mitsuba/core/octree.h +++ b/include/mitsuba/core/octree.h @@ -20,10 +20,50 @@ #define __OCTREE_H #include -#include +#include +#include MTS_NAMESPACE_BEGIN + +/** + * \brief Implements a lock-free singly linked list. + */ +template class LockFreeList { +public: + struct ListItem { + T value; + ListItem *next; + + inline ListItem(const T &value) : + value(value), next(NULL) { } + }; + + inline LockFreeList() : m_head(NULL) {} + + ~LockFreeList() { + ListItem *cur = m_head; + while (cur) { + ListItem *next = cur->next; + delete cur; + cur = next; + } + } + + inline const ListItem *head() const { + return m_head; + } + + void append(const T &value) { + ListItem *item = new ListItem(value); + ListItem **cur = &m_head; + while (!atomicCompareAndExchangePtr(cur, item, NULL)) + cur = &((*cur)->next); + } +private: + ListItem *m_head; +}; + /** * \brief Generic multiple-reference octree. * @@ -67,29 +107,34 @@ private: struct OctreeNode { public: OctreeNode() { - pthread_rwlock_init(&lock, NULL); for (int i=0; i<8; ++i) children[i] = NULL; } ~OctreeNode() { - pthread_rwlock_destroy(&lock); for (int i=0; i<8; ++i) { if (children[i]) delete children[i]; } } - inline void readLock() const { pthread_rwlock_rdlock(&lock); } - inline void readUnlock() const { pthread_rwlock_unlock(&lock); } - inline void writeLock() { pthread_rwlock_wrlock(&lock); } - inline void writeUnlock() { pthread_rwlock_unlock(&lock); } - OctreeNode *children[8]; - mutable pthread_rwlock_t lock; - std::vector data; + LockFreeList data; }; + /// Return the AABB for a child of the specified index + inline AABB childBounds(int child, const AABB &nodeAABB, const Point ¢er) const { + AABB childAABB; + childAABB.min.x = (child & 4) ? center.x : nodeAABB.min.x; + childAABB.max.x = (child & 4) ? nodeAABB.max.x : center.x; + childAABB.min.y = (child & 2) ? center.y : nodeAABB.min.y; + childAABB.max.y = (child & 2) ? nodeAABB.max.y : center.y; + childAABB.min.z = (child & 1) ? center.z : nodeAABB.min.z; + childAABB.max.z = (child & 1) ? nodeAABB.max.z : center.z; + return childAABB; + } + + void insert(OctreeNode *node, const AABB &nodeAABB, const T &value, const AABB &coverage, Float diag2, int depth) { /* Add the data item to the current octree node if the max. tree @@ -97,51 +142,32 @@ private: than the current node size */ if (depth == m_maxDepth || (nodeAABB.getExtents().lengthSquared() < diag2)) { - node->writeLock(); - node->data.push_back(value); - node->writeUnlock(); + node->data.append(value); return; } /* Otherwise: test for overlap */ const Point center = nodeAABB.getCenter(); - bool over[8]; - AABB childAABB; - over[0] = over[1] = over[2] = over[3] = (coverage.min.x <= center.x); - over[4] = over[5] = over[6] = over[7] = (coverage.max.x > center.x); - over[0] &= (coverage.min.y <= center.y); - over[1] &= (coverage.min.y <= center.y); - over[4] &= (coverage.min.y <= center.y); - over[5] &= (coverage.min.y <= center.y); - over[2] &= (coverage.max.y > center.y); - over[3] &= (coverage.max.y > center.y); - over[6] &= (coverage.max.y > center.y); - over[7] &= (coverage.max.y > center.y); - over[0] &= (coverage.min.z <= center.z); - over[2] &= (coverage.min.z <= center.z); - over[4] &= (coverage.min.z <= center.z); - over[6] &= (coverage.min.z <= center.z); - over[1] &= (coverage.max.z > center.z); - over[3] &= (coverage.max.z > center.z); - over[5] &= (coverage.max.z > center.z); - over[7] &= (coverage.max.z > center.z); + /* Otherwise: test for overlap */ + bool x[2] = { coverage.min.x <= center.x, coverage.max.x > center.x }; + bool y[2] = { coverage.min.y <= center.y, coverage.max.y > center.y }; + bool z[2] = { coverage.min.z <= center.z, coverage.max.z > center.z }; + bool over[8] = { x[0] & y[0] & z[0], x[0] & y[0] & z[1], + x[0] & y[1] & z[0], x[0] & y[1] & z[1], + x[1] & y[0] & z[0], x[1] & y[0] & z[1], + x[1] & y[1] & z[0], x[1] & y[1] & z[1] }; /* Recurse */ for (int child=0; child<8; ++child) { if (!over[child]) continue; if (!node->children[child]) { - node->writeLock(); - node->children[child] = new OctreeNode(); - node->writeUnlock(); + OctreeNode *newNode = new OctreeNode(); + if (!atomicCompareAndExchangePtr(&node->children[child], newNode, NULL)) + delete newNode; } - childAABB.min.x = (child & 4) ? center.x : nodeAABB.min.x; - childAABB.max.x = (child & 4) ? nodeAABB.max.x : center.x; - childAABB.min.y = (child & 2) ? center.y : nodeAABB.min.y; - childAABB.max.y = (child & 2) ? nodeAABB.max.y : center.y; - childAABB.min.z = (child & 1) ? center.z : nodeAABB.min.z; - childAABB.max.z = (child & 1) ? nodeAABB.max.z : center.z; + const AABB childAABB(childBounds(child, nodeAABB, center)); insert(node->children[child], childAABB, value, coverage, diag2, depth+1); } @@ -152,25 +178,20 @@ private: const AABB &nodeAABB, const Point &p, Functor &functor) const { const Point center = nodeAABB.getCenter(); - node->readLock(); - for (size_t i=0; idata.size(); ++i) - functor(node->data[i]); + const typename LockFreeList::ListItem *item = node->data.head(); + while (item) { + functor(item->value); + item = item->next; + } int child = (p.x > center.x ? 4 : 0) + (p.y > center.y ? 2 : 0) + (p.z > center.z ? 1 : 0); OctreeNode *childNode = node->children[child]; - node->readUnlock(); if (childNode) { - AABB childAABB; - childAABB.min.x = (child & 4) ? center.x : nodeAABB.min.x; - childAABB.max.x = (child & 4) ? nodeAABB.max.x : center.x; - childAABB.min.y = (child & 2) ? center.y : nodeAABB.min.y; - childAABB.max.y = (child & 2) ? nodeAABB.max.y : center.y; - childAABB.min.z = (child & 1) ? center.z : nodeAABB.min.z; - childAABB.max.z = (child & 1) ? nodeAABB.max.z : center.z; + const AABB childAABB(childBounds(child, nodeAABB, center)); lookup(node->children[child], childAABB, p, functor); } } @@ -180,22 +201,16 @@ private: Functor &functor) { const Point center = nodeAABB.getCenter(); - node->readLock(); - for (size_t i=0; idata.size(); ++i) - functor(node->data[i]); - node->readUnlock(); + const typename LockFreeList::ListItem *item = node->data.head(); + while (item) { + functor(item->value); + item = item->next; + } // Potential for much optimization.. for (int child=0; child<8; ++child) { if (node->children[child]) { - AABB childAABB; - childAABB.min.x = (child & 4) ? center.x : nodeAABB.min.x; - childAABB.max.x = (child & 4) ? nodeAABB.max.x : center.x; - childAABB.min.y = (child & 2) ? center.y : nodeAABB.min.y; - childAABB.max.y = (child & 2) ? nodeAABB.max.y : center.y; - childAABB.min.z = (child & 1) ? center.z : nodeAABB.min.z; - childAABB.max.z = (child & 1) ? nodeAABB.max.z : center.z; - + const AABB childAABB(childBounds(child, nodeAABB, center)); if (childAABB.overlaps(sphere)) searchSphere(node->children[child], childAABB, sphere, functor); }