如何使用openvdb(并行)foreach访问多个网格?

时间:2016-07-28 07:59:14

标签: c++ foreach parallel-processing openvdb

我有一个openvdb网格,我想使用仿函数和openvdb :: tools :: foreach迭代。

//the grid I am iterating on
Grid G;

//the operator used to update each single voxel of G
struct Functor{
  inline void operator()(const Grid::ValueOnCIter& iter) const {
  }
};

如果操作仅涉及G我可以简单地调用

  Functor op;
  openvdb::tools::foreach(visibleGrid->cbeginValueOn(), op, true, true);

在每个体素(迭代),但我需要根据迭代步骤的计算值访问和修改其他网格。

我的初始解决方案涉及向仿函数提供附加网格的访问者:

struct Functor{
  Grid2::Accessor grid2_accessor;

  Functor( Grid2::Accessor& a) : grid2_accessor(a){}

  inline void operator()(const Grid::ValueOnCIter& iter) const {
      //use grid2_accessor based on iter.getCoord()
  }
};

访问者在构造时提供给Functor,而且并行的每个线程都获得了仿函数的副本:

  Functor op(G2->getAccessor() );
  openvdb::tools::foreach(G1->cbeginValueOn(), op, true, **false**);

不幸的是,此解决方案无效:

  • 访问者不得为const
  • 但Functor :: operator()必须是tools :: foreach
  • 使用的const方法

第二个解决方案是将Functor访问者副本声明为可变。由于openvdb断言失败(最可能是内存泄漏),此解决方案在Debug中不起作用。

这个问题有解决方法吗?例如。一个不需要operator()为const的工具:: foreach。

1 个答案:

答案 0 :(得分:1)

在不同的线程中使用相同的ValueAccessor是不安全的。相反,您希望每个线程都有唯一的ValueAccessor,但共享底层树。

相反定义您的Functor

struct Functor {
    Grid2& mGrid2;
    Functor(Grid2& grid2) : mGrid2(grid2) {}

    void operator()(const Grid::ValueOnCIter& iter) const {
        Grid2::Accessor grid2Acc(grid2.getAccessor()); // This is allowed because Grid2 is a reference
        // Do what you want
    } 
}

您找不到运算符的非const版本的原因是因为底层实现依赖于tbb。他们在tbb documentation中给出的动机是:

  

由于可能会复制正文对象,因此其operator()不应修改正文。否则,修改可能会或可能不会对调用parallel_for的线程可见,具体取决于operator()是作用于原始还是副本。作为这种细微差别的提醒,parallel_for要求将body对象的operator()声明为const。

因此,您不应该很快就会看到非const版本。

编辑:如评论中所述,可以在ValueAccessor中重复使用缓存。但是,由于它现在是类的成员,因此在运算符中使用它来修改树会有问题(因为setValue是非常量的)。如果你知道没有其他人写到同一个内存位置你可以做一个小小的黑客:

struct Functor {
    Grid2::ValueAccessor mGrid2Acc;
    Functor(Grid2::ValueAccessor grid2Acc) : mGrid2Acc(grid2Acc) {}

    void operator()(const Grid::ValueOnCIter& iter) const {
        const Grid2::ValueType& v = mGrid2Acc.getValue(iter.getCoord());
        Grid2::ValueType& non_const_v = const_cast<Grid2::ValueType&>(v);
        // modify the value as you please, however a race condition will occur
        // if more than 1 thread write to the same location
    } 
}

我仍然更喜欢第一种解决方案。您可以通过调用访问器上的probeLeaf(openvdb::Coord& ijk)来缓存某个叶节点。