Unity环境下计算着色器读取groupshared变量回CPU问题咨询
核心结论
- 直接读取GPU侧的
groupshared数据回CPU是不可行的,groupshared数据必须先显式复制到可写的RW类型缓冲区(比如RWStructuredBuffer、RWByteAddressBuffer)后,才能从CPU侧回读。 - RW类型缓冲区不能声明为
groupshared和编译期大小无关,二者是完全不同的存储类型:groupshared是线程组内共享的临时高速缓存,生命周期仅存续于对应线程组的执行周期内,线程组执行完毕后空间会被自动回收;而RWBuffer是全局的持久化显存资源,可用于CPU/GPU跨端数据交互,二者本身就不允许混合声明,这也是你遇到groupshared variables cannot hold resources报错的根本原因。
回读数据全为0的原因
你在C#侧通过SetBuffer绑定的ComputeBuffer,根本没有和着色器里声明的groupshared uint _FoWMap建立关联:groupshared类型的变量不属于可绑定资源,你这一步绑定操作是完全无效的。着色器运行时写入的_FoWMap是线程组本地的临时缓存,执行完就被清空了,你最后GetData读取的是ComputeBuffer初始化时的默认0值,所以结果全为0。
修复方案
1. 修改着色器代码,拆分临时共享缓存和持久化可写缓冲区
// 4096 uints * 4 bytes per uint = 16kb #define FoWMap_Size 4096 // 线程组内临时共享缓存,仅用于线程组内数据交互 groupshared uint _FoWMap_Shared[FoWMap_Size]; // 全局可写缓冲区,用于和CPU侧绑定交互,和你用的Raw类型Buffer对应 RWByteAddressBuffer _FoWMap; [numthreads(32,1,1)] void CSMain(uint3 id : SV_DispatchThreadID, uint groupThreadId : SV_GroupThreadID) { // 原有的写入groupshared逻辑 for (uint i = 0; i < FoWMap_Size; i++) { _FoWMap_Shared[i] = i; } // 组内存屏障,等待当前线程组所有线程完成共享内存写入 GroupMemoryBarrierWithGroupSync(); // 由组内0号线程将共享内存数据同步到全局可写缓冲区 if (groupThreadId.x == 0) { for (uint i = 0; i < FoWMap_Size; i++) { _FoWMap.Store(i * 4, _FoWMap_Shared[i]); } } }
2. C#侧代码无需做核心逻辑修改
原有SetBuffer、Dispatch、GetData逻辑保持不变即可,绑定的缓冲区现在可以正确拿到同步后的数据。
内容的提问来源于stack exchange,提问作者Semanresu
相关产品推荐
相关产品推荐

