2012-03-17 138 views
1

片段着色器使用兩個原子計數器。它可能會或不會增加第一個,也可能不會增加第二個(但從來都不是)。然而,在修改計數器之前,它們的當前值總是被讀取,然後 - 如果計數器稍後被修改 - 那些先前讀取用於某些自定義邏輯的值。所有這些都發生在一個(很可能無法展開的)循環中。片段着色器中的GLSL原子計數器(和分支)

設想一個流程大致是這樣的:在一些小unrollable環

  • ,說0-20(編譯時解析常數)...
  • 爲AC1和AC2
  • 獲取計數器值
  • 檢查某個值:
  • 如果x:在指數AC1,增量AC1
  • 在其他設置uimage1D_A紋理像素:在uimage1D_B設置在紋素指數(imgwidth-AC2-1),增量AC2

問題:着色器查詢當前計數器值 - 它是否始終獲得「最新」值?我在這裏丟失了片段着色器的大規模並行性(僅針對當前代和未來的GPU和驅動程序)?

至於分支(如果x) - 我在另一個(readonly restrict uniformuimage1D紋素比作(uniformuint。所以一個操作數絕對是一個統一的標量,但另一個操作數是一個imageLoad().x,儘管圖像是統一的 - 這種分支仍然是「完全並行化」的嗎?你可以看到兩個分支都是兩個完全相同的指令。假設一個「完美優化」的GLSL編譯器,這種分支可能引入一個失速?

回答

4

原子計數器是原子的。但是每個原子操作都是原子操作。

所以,如果你想確保每個着色器會從一個櫃檯一個獨特的價值,那麼每個着色器必須訪問計數器atomicCounterIncrement(或Decrement,但它們都必須使用同一個)。

正確的方法做你所建議的是:

  1. 檢查某個值:
  2. 如果x:
    1. atomicCounterIncrement(AC1),存儲返回值。
    2. 使用存儲的值作爲設置uimage1D_A的texel。
  3. 其他:
    1. atomicCounterIncrement(AC2),存儲返回值。
    2. 使用存儲的值計算texel(imgwidth - val - 1),以便在其中將某些內容設置爲uimage1D_B。

你的「獲取和後來的增量」的策略是等待發生的競爭條件。沒關係,如果它的「完全並行」,因爲它是打破。在想知道它是否快速運行之前,您需要它來運行。

我強烈建議在嘗試解決GPU問題之前,先熟悉原子和線程CPU。這是新手使用原子時常犯的一個錯誤。如果要成功使用GLSL原子和圖像加載/存儲,則需要成爲線程專家(或至少是中級)。

+0

謝謝你的澄清和警告:) – metaleap 2012-03-18 23:11:22

1

由於尼科爾流星錘建議,如果你想確保你從原子計數器讀永遠不會被另一個內核中讀取的值,你需要執行單位遞增,並使用返回的值,這是任何其他內核除非他們執行atomicCounter(AC1)它檢查值而不遞增。你原子方式增加價值,並找回昔日的價值的那一刻,您確保其他人誰做同樣只會得到增加後的值。

你似乎在做一個A-緩衝區,我很好奇,爲什麼你需要第二個計數器。我認爲uimage1D_A是指針指向分片列表的屏幕大小的地圖存儲在uimage1D_B,對嗎?您可以使用AC2產生一個指向uimage1D_B的一個新的未使用的內存部分,但你的AC1建議你逐漸acessing uimage1D_A,所以我可能是完全錯誤的:)