首先,关于你的陈述:
“我想我的问题是为什么不能在计算着色器中使用 gimage2D 访问附加到 FBO 的纹理?”
您不使用gimage2D
,如果您g
在 GLSL 文档中看到前缀为 的类型,则它是泛型类型。(例如gvec<N>
,gsampler...
等)这意味着该函数对每种vec<N>
or都有重载sampler...
。在这种情况下,是“......这个函数接受,或”gimage2D
的简写方式。image2D
iimage2D
uimage2D
没有实际gimage2D
的类型,g
前缀是为了保持 GLSL 文档简短易读而发明的;)
我想您已经知道这一点,因为问题中列出的唯一实际代码是 using image2D
,但我不确定事情的编写方式。
至于您的实际问题,您应该查看内存障碍。
特别注意:GL_FRAMEBUFFER_BARRIER_BIT
。
计算着色器与渲染管道的各个阶段分开调度;他们有自己的单级管道。这意味着如果您将某些内容绘制到 FBO 附件中,您的计算机着色器可能会在您开始绘制之前运行,或者计算着色器可能会使用(无效的)数据缓存视图,因为渲染管道中所做的更改对计算管道。内存屏障将有助于同步渲染管道和计算管道以获取两者之间共享的资源。
渲染管道有很多隐式同步和多阶段数据流,为着色器提供了非常简单的顺序排序(例如glDraw*
启动顶点->几何->片段),但计算管道实际上取消了所有这些,有利于显式同步。计算着色器和图像加载/存储需要考虑各种危险,而传统的顶点/几何/细分/片段则没有。
换句话说,虽然在计算着色器中声明某些内容以及在着色器级别coherent
的适当屏障将负责计算着色器调用之间的同步,因为计算管道与渲染管道是分开的,它不会同步图像加载/存储之间一个计算着色器和一个片段着色器。为此,您需要在命令级别同步对内存资源的访问。(渲染管道的入口点)是与(计算管道的入口点)分开的命令,您需要确保这些单独的命令正确排序,以使图像加载/存储表现出一致的行为。glMemoryBarrier (...)
glDraw* (...)
glDispatch* (...)
没有内存屏障,就无法保证命令的执行顺序;只是它们产生的结果与您发出的命令一致。在为每个着色器阶段严格定义输入/输出的渲染管道中,GL 实现可以智能地重新排序命令,同时相对轻松地保持此属性。通常使用计算着色器以及图像加载/存储,其中 I/O 完全由运行时流确定,如果没有一些帮助(内存屏障)是不可能的。
TL;DR:如果您使用采样器而不是图像加载/存储,它的工作原理归结为一致性保证(或缺乏一致性保证)。图像加载/存储根本不保证从图像读取的内容与写入图像的任何内容一致(严格排序),而是要求您显式同步对图像的访问。这实际上是有益的,因为它允许您同时读取/写入相同的图像而不会导致未定义的行为,但它需要您付出一些额外的努力才能使其工作。