Glsl 如果'buffer'是'coherent',那么读取字段和执行'atomicAdd(字段,0)'之间有什么区别吗?

Glsl 如果'buffer'是'coherent',那么读取字段和执行'atomicAdd(字段,0)'之间有什么区别吗?,glsl,vulkan,Glsl,Vulkan,这就是Vulkan语义,如果它有任何区别的话 假设如下: layout(...) coherent buffer B { uint field; } b; 假设通过原子*()函数对同一着色器(或派生着色器)的其他调用正在修改该字段 如果着色器调用希望从此字段执行原子读取(语义与GLES中的atomicCounter()相同,如果这是一个原子单元),那么以下两个之间是否有任何区别(除了其中一个显然既执行写入又执行读取) 为了直接回答这个问题,这两行代码生成不同的指令,具有不同的性能特征和

这就是Vulkan语义,如果它有任何区别的话

假设如下:

layout(...) coherent buffer B
{
    uint field;
} b;
假设通过
原子*()
函数对同一着色器(或派生着色器)的其他调用正在修改该字段

如果着色器调用希望从此
字段执行原子读取(语义与GLES中的
atomicCounter()
相同,如果这是一个
原子单元
),那么以下两个之间是否有任何区别(除了其中一个显然既执行写入又执行读取)


为了直接回答这个问题,这两行代码生成不同的指令,具有不同的性能特征和硬件管道使用情况

uint read_value = b.field;                 // generates a load instruction
uint read_value2 = atomicAdd(b.field, 0);  // generates an atomic instruction
  • --
    缓冲区加载\u dword
    缓冲区原子添加
  • --
    LDG
    ATOM
第4.10节内存限定符指出,
contracted
仅涉及跨调用(着色器线程)的读写可见性。他们还就默示绩效发表了评论:

当使用未声明为一致的变量访问内存时,着色器访问的内存可由实现缓存,以服务将来对同一地址的访问。内存存储的缓存方式可以使写入的值对访问同一内存的其他着色器调用不可见。该实现可以缓存由内存读取获取的值,并将相同的值返回到访问相同内存的任何着色器调用,即使自第一次内存读取以来基础内存已被修改。虽然未声明为相干的变量对于着色器调用之间的通信可能不有用,但使用非相干访问可能会导致更高的性能

GPU内存系统中的一致性点通常是最后一级缓存(二级缓存),这意味着所有一致性访问都必须由二级缓存执行。这也意味着相干缓冲区不能缓存在L1或其他更靠近着色器处理器的缓存中。现代GPU在L2缓存中也有专用的原子硬件;普通加载不会使用这些,但是
atomicAdd(…,0)
将遍历这些。原子硬件的带宽通常低于完整的二级缓存。

SPIR-V有一条指令。据推测,至少有一种硬件,在这种硬件中,无论缓冲区描述符具有什么限定符,非原子负载都无法替代原子负载


不幸的是,据我所知,没有Vulkan GLSL构造可以转换为
OpAtomicLoad

这个问题需要更多细节。例如,“足够”是什么意思?“足够”执行什么操作
相干
具有与一对操作相关的含义。您希望通过什么操作使原子访问一致?这两个操作之间的关系是什么?等等如上所述的问题只是没有包含足够的信息。它在模拟原子计数器,因此在相同的
uint
(模拟
atomicCounterIncrement
atomicCounterDecrement
)上与其他
atomicAdd
操作一致。其他
atomicAdd
操作在哪里?在同一个着色器阶段?在派生着色器阶段中?在后续渲染命令的着色器阶段中?说你在“模拟原子计数器”并不能说明你正在做的实际操作。显然,后续的渲染命令需要其他障碍。我问的是关于同一着色器的其他调用(如一个问题项中所暗示的)。我没有考虑过派生着色器阶段,我很想知道这是否会带来不同(可能是在平铺GPU上?)对不起,我没有说得更具体一些,我认为很明显,
atomic_uint
将如何用
缓冲区AC{uint counters[];}AC我的观点是,如何模拟它取决于您的用例。这里没有一刀切的解决方案。谢谢你证实我的怀疑。我确实意识到它们生成不同的组件。我的问题是他们是否可能“表现”不同。换句话说,是否可以保证仅读取变量总是返回与atomicAdd(var,0)
相同的值?我越来越确信这是真的。顺便说一句,我非常熟悉GCN,在这种架构下,我可以断定它们是等效的(有一些驱动因素的假设)。我要寻找的是一个规格保证,这将永远是这样的情况。我猜你的话间接地说:“当使用未声明为一致的变量访问内存时,着色器访问的内存可能会被实现缓存,以服务将来对同一地址的访问。”因此(!concert=>cached),尽管从数学上我们无法推断(concert=>!cached),但我会把这更多地归咎于糟糕的措辞。
uint read_value = b.field;                 // generates a load instruction
uint read_value2 = atomicAdd(b.field, 0);  // generates an atomic instruction