一次调用可以在 glsl 计算着色器中进行乒乓传播吗?

Posted

技术标签:

【中文标题】一次调用可以在 glsl 计算着色器中进行乒乓传播吗?【英文标题】:Ping pong propagation in glsl compute shader possible in one call? 【发布时间】:2013-09-13 04:18:28 【问题描述】:

我尝试使用 glsl 计算着色器实现 32x32x32 3D 纹理的传播方案,如果我可以只执行一次着色器进行 x 次迭代,那就太好了。

我有 3 个纹理,一个是源,一个是目标,第三个是累积一切。每次迭代都必须交换源和目标。 伪代码看起来像 OpenGL:

glUseProgram(computeShaderId);
glBindImageTexture(0, srcTexId, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F);
glBindImageTexture(1, targetTexId, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F);
glBindImageTexture(2, accumulateTexId, 0, GL_TRUE, 0, GL_READ_WRITE, GL_RGBA32F);
glDispatchCompute(32,32,32);

GLSL:

#version 430
layout (local_size_x = 1, local_size_y = 1, local_size_z =1) in;
layout(rgba32f) uniform image3D srcTex;
layout(rgba32f) uniform image3D targetTex;
layout(rgba32f) uniform image3D accumulateTex;

void main() 
  ivec3 currentPos = ivec3(gl_GlobalInvocationID.xyz);

  for (int i=0;i<8;i++)
    //accumulate the values of the 6 neighbours (top,bottom,left,right,front,back)
    //by usind the current sourceTexture
    //this involes  loadImage 
    vec4 neighbourValues=getValuesFrom6Neighbours(currentPos, currentSource);

    storeImage(currentTarget,currentPos,neighbourValues);

    vec4 value=loadImage(accumTex,currentPos);
    storeImage(accumTex,currentPos,neighbourValues+value);

    //the texture are swapped, which I have a solution for so no problem here
    swapSrcAndTarget();

    //here is the Problem how to synchronize all different shader invocations?
    someKindOfBarrier();
  

问题是由于纹理的大小,我无法在一个工作组中完成所有这些工作。它会在一个工作组中,我可以使用 barrier() 就可以了。 由于纹理的交换,我需要在从下一次迭代再次读取之前更新所有值。 有人知道这是否可能吗?

谢谢 马克

【问题讨论】:

【参考方案1】:

正如您所说的那样,所有内容都不适合活动线程,所以我不认为这是直接可能的,除非您接受会有错误(当您读取的一半值可能来自更新之前或之后) .换句话说,所有线程都必须完成第一次 ping,然后才能继续进行 pong。由于一次只物理执行了一部分线程,因此将 pass 放入循环中是行不通的。

我能想到两件事。

    将问题分解为可以容纳的图块,但在完成内核/调度之前,图块边缘之间将没有通信(邻居可能是陈旧的)。 实现您自己的调度,并使用 atomic opts 尝试获取任务,直到完成完整的 ping 操作(意味着手动同步)。只有在 memoryBarrier() 之后才能继续进行乒乓球比赛。根据经验,这可能比将 glDispatchCompute 放入 for 循环中慢很多。

【讨论】:

是的,我尝试通过使用 atmoic 增量递增变量来手动同步,并尝试等待该变量达到表示完成一次迭代的值(在本例中为 32768)。问题是我没有找到暂停执行的好方法,我尝试了一个while循环,但这几乎立即破坏了图形驱动程序。我认为您是对的,最好的方法是使用一个 glDispatchCompute 进行一次迭代。谢谢 我以前曾在着色器工作中看到过这样的循环:blog.icare3d.org/2010/07/…,但用新的东西让 GLSL 面对它并不需要太多。例如,当我尝试使用“while (!done()) no-op(); then-execute();”它曾经失败,但将代码放入循环中似乎有效:“while (waiting) if(done()) execute(); waiting=false;”。我已经开始查看着色器二进制文件(它会吐出一些类似汇编的文本,至少在 NVIDIA 上)只是为了确保没有发生真正奇怪的事情。祝你好运:)

以上是关于一次调用可以在 glsl 计算着色器中进行乒乓传播吗?的主要内容,如果未能解决你的问题,请参考以下文章

满足条件时是不是可以在 GLSL 着色器中回调 C/C++ 函数/代码? [关闭]

为啥我的着色器中的 GLSL 纹理坐标不是线性的?

在 GLSL 顶点着色器中,您可以访问索引缓冲区中顶点的索引,而不仅仅是顶点缓冲区吗?

在 GLSL 着色器中访问 VBO/VAO 数据

在 GLSL 中让相机正常

如果未绑定,统一值是不是保留在 GLSL 着色器中?