1

我有一个 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 {
  }
};

如果只涉及 GI 的操作可以简单地调用

  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 的副本:

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

不幸的是,这个解决方案不起作用,因为:

  • 访问器不能是 const 才能被访问
  • 但是 Functor::operator() 必须是一个 const 方法才能被 tools::foreach 使用

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

有解决问题的方法吗?例如不需要 operator() 为 const 的 tools::foreach。

4

1 回答 1

1

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

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
    } 
}

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

因为主体对象可能被复制,所以它的 operator() 不应该修改主体。否则,修改可能对调用parallel_for 的线程可见,也可能不可见,这取决于operator() 是作用于原始文件还是副本。作为对这种细微差别的提醒,parallel_for 要求将主体对象的 operator() 声明为 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)

于 2016-08-03T20:06:52.767 回答