the octree-class is now completely lock-free (using atomic exchange operations)

metadata
Wenzel Jakob 2010-10-16 14:10:10 +02:00
parent 2db91b43f9
commit fbfe3e395d
1 changed files with 80 additions and 65 deletions

View File

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