2016-07-28 66 views
1

我有一個openvdb網格,我想迭代使用函子和openvdb :: tools :: foreach。如何使用openvdb(並行)的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 { 
    } 
}; 

如果只涉及胃腸道的操作可以基於迭代的計算值都簡稱

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

在每個體素(迭代),雖然我需要訪問和修改其他電網(S)步。

我inital解決方案涉及提供給仿函數的附加電網(S)的訪問:

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 op(G2->getAccessor()); 
    openvdb::tools::foreach(G1->cbeginValueOn(), op, true, **false**); 

不幸的是這種解決方案不起作用,因爲:

  • 存取器必須不能被訪問
  • 常量但函子::運算符()必須是一個const方法通過工具一起使用的foreach ::

第二溶液申報函子訪問器複製爲可變。由於openvdb斷言失敗(很可能是內存泄漏),此解決方案在Debug中不起作用。

有問題的解決方案嗎?例如。一個工具:: foreach不要求operator()是const。

回答

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是非const)。如果你知道沒有其他人被寫入同一存儲空間,你可以做一個小的黑客:

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)來緩存某個葉節點。

+0

好點!通過每次實例化訪問器,儘管我失去了上一次訪問的緩存樹路徑。這給了我一個表演處罰 – Pierluigi

+0

@Pierluigi這是真的。我應該添加的是,您可以通過值而不是通過引用傳遞訪問器,這樣可以保持緩存完好無損。 – pingul

+0

@Pierluigi在編輯中添加了另一個示例。 – pingul