У меня есть сетка 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 как изменчивую. Это решение не работает в Debug из-за сбоя утверждения openvdb (скорее всего, утечка памяти).
Есть ли решение проблемы? Например. a tools :: foreach, который не требует, чтобы operator () был const.
Не безопасно использовать то же самое 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 документация является:
Поскольку объект 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)
на аксессор.
Других решений пока нет …