Unity 3D : ComputeShader 全面詳解

前言:

會寫一些簡單的 ComputeShader, 但還是很多人可能還搞不懂

  1. Dispatch (x, y, z)
  2. [numthreads (x, y, z)]
  3. SV_GroupID
  4. SV_GroupThreadID
  5. SV_GroupIndex
  6. SV_DispatchThreadID

這些是啥,今天我來為大家做一個比較全面的說明,畢竟 ComputeShader 的資源還算是比較少的。

另外我當大家已經運行過一些簡單的 ComputeShader 代碼了,只是不了解其中的奧秘。

如果你沒運行過,那可以先到這邊爽一下:https://blog.csdn.net/weixin_38884324/article/details/79285371

※ 需要注意的是,本篇文章是用圖片的方式來說明,並非使用矩陣的方式來講解,因為我覺得圖片的方式會比較好理解。而且輸入(Texture2D)與輸出(RWTexture2D) 的圖片是同一個解析度。


第一節:

Dispatch (x, y, z) 與 numthreads (x, y, z) 的關係

很多人把 ComputeShader 代碼 Run 起來了,但或多或少會產生一個疑問,就是在 C# 中的 DispatchComputeShader 中的 numthreads 是啥關係,感覺其中數字設定有一些奧妙所在 ? 試著亂調其中的數字圖片還會有部分會無法顯示….WTF?

假設你輸入(Texture2D)與輸出(RWTexture2D) 的圖片是同一個解析度,你只是要做一些單像素的計算 (例如轉成灰階),那麼 Dispatch 與 numthreads 要如何設置才能顯示完整的圖片? 而不會缺一角?

其實很簡單,假設圖片解析度是 256 x 128,那麼以下幾種設置都能完整顯示圖片:

Dispatch (k, 256, 128, 1)         [numthreads (1, 1, 1)]
Dispatch (k, 32, 16, 1)           [numthreads (8, 8, 1)]
Dispatch (k, 256/8, 128/8, 1)     [numthreads (8, 8, 1)]
Dispatch (k, 8, 4, 1)             [numthreads (32, 32, 1)]

哇靠,這麼多種 ? 那有甚麼規律呢 ?

有的,你會發現 Dispatch.x * numthreads.x = 256 ( 圖片寬度 ),Dispatch.y * numthreads.y = 128 ( 圖片高度 )。只要 Dispatch * numthreads 是圖片大小,那麼圖片就能完全顯示,不會少漏一個像素。

But ! But ! But !

但是非常重要的事情是,numthreads 使用上是有限制的,numthreads.x * numthreads.y * numthreads.z 必須小於等於 1024

以下是錯誤例子,乖孩子別學:

[numthreads (64, 32, 1)]
[numthreads (16, 16, 16)]
[numthreads (256, 128, 1)]

好了,現在你已經能夠完美顯示圖片了,但是並不瞭解其中的原理,所以下一章開始會更詳細的說明。


第二節:

更深入的了解 Group 與 Thread 的關係

我們第一節使用 Dispatch(x, y, z),其實這就是在設定 Group 數量;而 numthreads(x, y, z) 就是在設定 Thread 數量。

假設我們有一張 4X4的圖像,使用參數 Dispatch(k, 2, 2, 1) 與 [numthreads(2, 2, 1)] 。那麼在 GPU 中劃分就如同下圖 ( 四個 Group, 每個 Group 中又包四個 Thread ):

Group 的 長、寬、高 是由 numthreads 所設定的,同理一個Group的大小是不能超過 1024 ( 見第一節 )。

同理 [numthreads(32, 32, 1)] 的 Group 大小就是 32 * 32 * 1 ( 也許你可以理解 32 * 32 像素 )

然而如果你的 GPU 有 64 核心,而你的 Group 也有 64 個,那麼每個核心將能處理一個 Group。

假設 ThreadSize = ( numthreads.x * numthreads.y * numthreads.z )

那麼不同的 ThreadSize 在不同硬件廠下分配的資源是不同的

建議:
AMD:ThreadSize 使用 64 的倍數 ( wavefront 架構 )
NVIDIA:ThreadSize 使用 32 的倍數 ( SIMD32 (Warp) 架構 )

另外使用 Group 有個好處,就是可以等待所有 Thread 同步。不過不同的 Group 是無法同步的。

如果你要在一個 Group 中同步所有 Thread,你可以用下面這語法

GroupMemoryBarrierWithGroupSync(); 

第三節:

SV_GroupID、SV_GroupThreadID、SV_GroupIndex 關係

由上圖可知,我們在 ComputeShader 函式中可以放入四個值,分別是

SV_GroupID : 線程組 ID
SV_GroupThreadID : 線程組內的線程 ID (三維,你可以理解為 Group 內的座標)
SV_GroupIndex : 線程組內的線程 ID (一維)
SV_DispatchThreadID : 唯一ID (你可以理解成整張圖片座標)

用下圖可以知道他們的座標關係:

看不懂 ? 好吧,我實際 Run 結果應該就明白啦 ! 如下圖:

Group ID 一看就懂 :

來看一下索米 :

Group Thread ID 一看就懂 :

Group Index 一看就懂 :


第四節:

DispatchThreadID

公式:DispatchThreadID = GroupID * numthreads + GroupThreadID

// 敬請期待…


第五節:

性能優化

// 敬請期待…


第六節

原子鎖

// 敬請期待…

=第七節

共享內存

// 敬請期待…

groupshared float4 gCache[256]
最大空间为32kb
用 Group Thread ID 索引


結語:

好累哈,花了一整天總算寫完 一 ~ 三節 ( 後面節數慢慢補充 ),也算是自己思緒地整理,雖然一些地方表達得不是很好,但我盡量用最簡單的方式說明,如果你覺得有更好的建議或不完美的地方,歡迎在下面留言。如果你喜歡本文章就按讚吧,如果你要轉載請註明出處吧。

參考

參考:https://blog.csdn.net/YgritteSnow/article/details/54988931

  • 22
    点赞
  • 59
    收藏
    觉得还不错? 一键收藏
  • 9
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值