1

我需要找到關於統一着色器陣列如何訪問GPU內存以瞭解如何有效使用它的信息。我的顯卡架構的圖像沒有清楚地顯示出來。在OpenCL/C++ Amp中訪問GPU內存

enter image description here

我需要使用C++安培到一個大的圖像加載到GPU存儲器,並將其分成小塊(如4×4像素)。每件作品都應該使用不同的線程進行計算。我不知道這些線程是如何分享對圖像的訪問權限的。

enter image description here

是否有這樣的方式,線程不阻止對方在訪問圖像做的方法嗎?也許他們有自己的記憶,可以獨佔訪問?

enter image description here

或者,也許訪問統一的內存是如此之快,我不應該關心它(但是我不相信的它)?這非常重要,因爲我需要爲每個圖像計算大約10k個子集。

回答

1

對於C++ AMP,您希望在開始卷積計算之前將瓦片中每個線程使用的數據加載到tile_static內存中。由於每個線程都訪問其他線程也讀取的像素,因此您可以對來自(慢速)全局內存的每個像素執行一次讀取,並將其緩存到(快速)tile靜態內存中,以便後續讀取速度更快。你可以看到一個example of tiling for convolution hereDetectEdgeTiled方法加載它需要的所有數據,並調用idx.barrier.wait()以確保所有線程都已完成將數據寫入到靜態內存中。然後利用tile_static內存執行邊緣檢測代碼。樣本中還有很多這種模式的例子。請注意,DetectEdgeTiled中的加載代碼很複雜,只是因爲它必須考慮當前圖塊中正在寫入的像素邊緣附近的附加像素,並且本質上是一個展開的循環,因此它是長度。

我不確定您是否正在以正確的方式思考問題。這裏有兩個級別的分區。爲了計算每個像素的新值,執行此工作的線程將讀取周圍像素的塊。另外,線程的塊(瓦片)將更大的像素數據塊加載到存儲器的tile_static中。瓦片上的每個線程然後計算塊內一個像素的結果。

void ApplyEdgeDetectionTiledHelper(const array<ArgbPackedPixel, 2>& srcFrame, 
            array<ArgbPackedPixel, 2>& destFrame) 
{  
    tiled_extent<tileSize, tileSize> computeDomain = GetTiledExtent(srcFrame.extent); 
    parallel_for_each(computeDomain.tile<tileSize, tileSize>(), [=, &srcFrame, &destFrame, &orgFrame](tiled_index<tileSize, tileSize> idx) restrict(amp) 
    { 
     DetectEdgeTiled(idx, srcFrame, destFrame, orgFrame); 
    }); 
} 

void DetectEdgeTiled(
    tiled_index<tileSize, tileSize> idx, 
    const array<ArgbPackedPixel, 2>& srcFrame, 
    array<ArgbPackedPixel, 2>& destFrame) restrict(amp) 
{ 
    const UINT shift = imageBorderWidth/2; 
    const UINT startHeight = 0; 
    const UINT startWidth = 0; 
    const UINT endHeight = srcFrame.extent[0];  
    const UINT endWidth = srcFrame.extent[1]; 

    tile_static RgbPixel localSrc[tileSize + imageBorderWidth ] 
     [tileSize + imageBorderWidth]; 

    const UINT global_idxY = idx.global[0]; 
    const UINT global_idxX = idx.global[1]; 
    const UINT local_idxY = idx.local[0]; 
    const UINT local_idxX = idx.local[1]; 

    const UINT local_idx_tsY = local_idxY + shift; 
    const UINT local_idx_tsX = local_idxX + shift; 

    // Copy image data to tile_static memory. The if clauses are required to deal with threads that own a 
    // pixel close to the edge of the tile and need to copy additional halo data. 

    // This pixel 
    index<2> gNew = index<2>(global_idxY, global_idxX); 
    localSrc[local_idx_tsY][local_idx_tsX] = UnpackPixel(srcFrame[gNew]); 

    // Left edge 
    if (local_idxX < shift) 
    { 
     index<2> gNew = index<2>(global_idxY, global_idxX - shift); 
     localSrc[local_idx_tsY][local_idx_tsX-shift] = UnpackPixel(srcFrame[gNew]); 
    } 
    // Right edge 
    // Top edge 
    // Bottom edge 
    // Top Left corner 
    // Bottom Left corner 
    // Bottom Right corner 
    // Top Right corner 

    // Synchronize all threads so that none of them start calculation before 
    // all data is copied onto the current tile. 

    idx.barrier.wait(); 

    // Make sure that the thread is not referring to a border pixel 
    // for which the filter cannot be applied. 
    if ((global_idxY >= startHeight + 1 && global_idxY <= endHeight - 1) && 
     (global_idxX >= startWidth + 1 && global_idxX <= endWidth - 1)) 
    { 
     RgbPixel result = Convolution(localSrc, index<2>(local_idx_tsY, local_idx_tsX)); 
     destFrame[index<2>(global_idxY, global_idxX)] = result; 
    } 
} 

這段代碼是從CodePlex中提取出來的,我剝去了很多真正的實現來使它更清晰。

WRT @ sharpneli的回答你可以在C++ AMP中使用texture<>來達到與OpenCL圖像相同的效果。 CodePlex上還有一個例子。

+0

但是,當使用OpenCL紋理<>'我將如何分割瓷磚/塊,以便它們也可以緩存部分圖像?至於你的例子,它並沒有完全清楚我的變化和偏移量是如何工作的。 –

+1

還有另一個例子,如果這有助於http://blogs.msdn.com/b/nativeconcurrency/archive/2011/11/01/convolution-sample.aspx –

0

在這種特殊情況下,您不必擔心。只需使用OpenCL圖像。 GPU非常擅長簡單讀取圖像(由於紋理)。但是,這種方法需要將結果寫入單獨的映像,因爲您無法在單個內核中讀取和寫入同一圖像。如果您可以將計算作爲單個傳遞執行(不需要迭代),則應該使用它。

另一種方法是將其作爲普通的內存緩衝區訪問,將wavefront內的部分(同步運行的線程組)加載到本地內存(此內存非常快),執行計算並將完整的最終結果寫回到計算後的統一內存。如果您需要在計算時讀取和寫入同一圖像的值,則應該使用此方法。如果你沒有內存綁定,你仍然可以從紋理中讀取原始值,然後在本地內存中迭代並將最終結果寫入單獨的圖像中。

只有當它不是const * restrict且多線程讀取同一位置時,從統一內存中讀取數據纔會很慢。一般來說,如果後續的線程ID讀取後續的位置,它是相當快的。但是,如果你的線程既能寫入又能讀取到統一的內存,那麼速度會很慢。

+0

我想盡快做到這一點。我想寫入與源圖像不同的輸出圖像。據我瞭解,你建議我應該將每個子集存儲在不同的地方。我並不完全理解你將**加載到波前(同步運行的線程組)到本地內存**中的部分**。你能再說一些嗎?也許一個圖像呈現這個解決方案 –

+0

這個頁面給出了2d卷積形式的一個很好的例子。 http://www.cmsoft.com.br/index.php?option=com_content&view=category&layout=blog&id=142&Itemid=201。它也沒有解釋這個概念的圖像。基本上每組線程都有一堆可用的內存,與全局內存相比,它非常快。所以它被用作用戶管理的緩存。 – sharpneli