Как использовать openvdb (параллельный) foreach для доступа к нескольким сеткам?

У меня есть сетка 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**);

К сожалению, это решение не работает, так как:

  • доступ не должен быть постоянным для доступа
  • но Functor :: operator () должен быть константным методом для использования tools :: foreach

Второй грязный Решение состояло в том, чтобы объявить копию средства доступа Functor как изменчивую. Это решение не работает в Debug из-за сбоя утверждения openvdb (скорее всего, утечка памяти).

Есть ли решение проблемы? Например. a tools :: foreach, который не требует, чтобы operator () был const.

1

Решение

Не безопасно использовать то же самое ValueAccessor в разных темах. Вместо этого вы хотите иметь уникальный ValueAccessors для каждого потока, но разделяют основное дерево.

Определите свой 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 документация является:

Поскольку объект body может быть скопирован, его оператор () не должен изменять тело. В противном случае модификация может или не может стать видимой для потока, который вызвал parallel_for, в зависимости от того, действует ли operator () на оригинал или копию. В качестве напоминания об этом нюансе parallel_for требует, чтобы оператор объекта body () был объявлен как 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) на аксессор.

1

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]