GLSL规范声明,对于“相干”内存限定符:“内存变量,其中读取和写入与来自其他着色器调用的读取和写入一致”。
在实践中,我不确定现代GPU驱动程序如何解释这是多个渲染过程。当GLSL规范声明“其他着色器调用”时,是否指的是仅在当前传递期间运行的着色器调用,或者过去或将来传递中的任何可能的着色器调用?为了我的目的,我将一个传递定义为“glBindFramebuffer-glViewPort-glUseProgram-glDrawABC-glDrawXYZ-glDraw123”循环;我现在每次“渲染循环迭代”执行2次此类传递,但稍后可能会有更多次传递。
答案 0 :(得分:9)
当GLSL规范声明“其他着色器调用”时,它是指仅在当前传递期间运行的着色器调用,还是过去或将来传递中的任何可能的着色器调用?
这正是它所说的:“其他着色器调用”。它可能是相同的程序代码。它可能是不同的代码。没关系:着色器调用不是这个。
通常,OpenGL会为您处理同步,因为OpenGL可以相当容易地跟踪它。如果您映射一个缓冲区对象的范围,修改它并取消映射它,OpenGL就会知道您(可能)更改了多少内容。如果你使用glTexSubImage2D,它知道你改变了多少东西。如果您确实转换了反馈,它可以确切地知道将多少数据写入缓冲区。
如果你确实将反馈转换为缓冲区,然后将其绑定为顶点数据的源,OpenGL知道这会使管道停顿。它必须等到变换反馈完成后,然后清除一些缓存才能将缓冲区用于顶点数据。
当您处理图片加载/存储时,会丢失所有这些。因为可以用完全随机,未知和不可知的方式编写这么多内容,OpenGL通常会非常松散地遵守规则,以便您可以灵活地获得最大可能的性能。这会触发许多未定义的行为。
通常,您可以遵循的唯一规则是OpenGL 4.2规范的2.11.13节中概述的规则。最大的一个(用于着色器到着色器的谈话)是舞台上的规则。如果您在片段着色器中,可以安全地假设专门计算您的三角形的点/线/三角形的顶点着色器已完成。因此,您可以自由加载它们存储的值。但只能来自那些让你的人。
你的着色器不能假设在之前的渲染命令中执行的着色器已经完成(我知道这听起来很奇怪,考虑到刚才所说的,但请记住:“只有那些使你成功的”)。除非上述情况适用,否则您的着色器无法假设在同一渲染命令中使用相同的制服,图像,纹理等完成同一着色器的其他调用。
你唯一可以假设的是,你自己制作的着色器实例本身是可见的。因此,如果您通过相同的变量执行imageStore
并对同一内存位置执行imageLoad
,那么您可以保证获得相同的值。
好吧,除非有人在此期间写信给他。
您的着色器不能假设稍后的渲染命令当然获取前一个写入的值(通过图像存储或原子更新)。 无论多久以后!您对上下文的约束并不重要。你上传或下载的内容并不重要(技术上。在某些情况下你会得到正确的行为,但是未定义的行为仍然是未定义的)。
如果您需要这种保证,如果您需要发出一个渲染命令来获取由图像存储/原子更新写入的值,您必须在发出写入调用之后和发出读取调用之前显式请求同步内存。这是通过glMemoryBarrier
完成的。
因此,如果您渲染了一些用于存储图像的内容,那么无法渲染使用存储数据的内容,直到发送了适当的屏障(在着色器中显式显示或在OpenGL代码中显式显示)。这可能是图像加载操作。但它可能是由着色器代码编写的缓冲区对象渲染的。它可能是纹理提取。它可以与附加到FBO的图像进行混合。没关系;你不能这样做。
请注意,这适用于处理图像加载/存储/原子的所有操作,而不仅仅是着色器操作。因此,如果您使用图像存储区写入图像,除非使用GL_TEXTURE_UPDATE_BARRIER_BIT
屏障,否则您不一定会读取正确的数据。