计算着色器共享内存包含工件
Compute shader shared memory contains artifacts
我一直在尝试编写通用计算着色器高斯模糊实现。
它基本上可以工作,但是它包含伪影,即使场景是静态的,每一帧都会发生变化。在过去的几个小时里,我一直在尝试调试它。我已经尽可能确保不超出界限,展开所有循环,用常量替换制服,但工件仍然存在。
我已经在 3 个不同的 machines/GPUs(2 个 nvidia,1 个 intel)上用工件测试了原始代码,它们都产生了相同的结果。使用纯 C++ 代码向前和向后执行的工作组模拟代码执行的 unrolled/constant 版本不会产生这些错误。
通过分配 [96][96] 而不是 [16][48] 的共享数组,我可以消除大部分工件。
这让我想到我遗漏了一个逻辑错误,因此我设法制作了一个非常简单的着色器,它仍然会在较小的范围内产生错误,如果有人能指出原因。我检查了很多文档,没有发现任何不正确的地方。
分配了一个 16x48 浮点数的共享数组,这是 3072 字节,大约是最小共享内存限制的 10%。
着色器在 16x16 工作组中启动,因此每个线程将写入 3 个唯一位置,并从一个唯一位置读回
然后将纹理渲染为 HSV,其中 0-1 之间的值将映射到色调 0-360(红-青-红),超出范围的值将为红色。
#version 430
//Execute in 16x16 sized thread blocks
layout(local_size_x=16,local_size_y=16) in;
uniform layout (r32f) restrict writeonly image2D _imageOut;
shared float hoz[16][48];
void main ()
{
//Init shared memory with a big out of bounds value we can identify
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 20000.0f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 20000.0f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 20000.0f;
//Sync shared memory
memoryBarrierShared();
//Write the values we want to actually read back
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 0.5f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 0.5f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 0.5f;
//Sync shared memory
memoryBarrierShared();
//i=0,8,16 work
//i=1-7,9-5,17 don't work (haven't bothered testing further
const int i = 17;
imageStore(_imageOut, ivec2(gl_GlobalInvocationID.xy), vec4(hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+i]));
//Sync shared memory (can't hurt)
memoryBarrierShared();
}
以大于 8x8 的启动尺寸启动此着色器会在图像的受影响区域产生伪影。
glDispatchCompute(9, 9, 0);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
我不得不断点和步进帧来捕捉这个,花了大约 14 帧
glDispatchCompute(512/16, 512/16, 0);//Full image is 512x512
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
再次,我不得不断点和步进帧来捕捉这一点,当 运行 在 60FPS(vsync)时伪影出现得更多 frequently/simultaneously。
memoryBarrierShared();
不,这只会使对其他调用的写入 可见。如果您希望能够从其他调用的数据中读取,则必须确保所有写入实际上都已 发生 。
这是用 the barrier
function 完成的。 在 memoryBarrierShared
.
之后应该被称为
我一直在尝试编写通用计算着色器高斯模糊实现。
它基本上可以工作,但是它包含伪影,即使场景是静态的,每一帧都会发生变化。在过去的几个小时里,我一直在尝试调试它。我已经尽可能确保不超出界限,展开所有循环,用常量替换制服,但工件仍然存在。
我已经在 3 个不同的 machines/GPUs(2 个 nvidia,1 个 intel)上用工件测试了原始代码,它们都产生了相同的结果。使用纯 C++ 代码向前和向后执行的工作组模拟代码执行的 unrolled/constant 版本不会产生这些错误。
通过分配 [96][96] 而不是 [16][48] 的共享数组,我可以消除大部分工件。
这让我想到我遗漏了一个逻辑错误,因此我设法制作了一个非常简单的着色器,它仍然会在较小的范围内产生错误,如果有人能指出原因。我检查了很多文档,没有发现任何不正确的地方。
分配了一个 16x48 浮点数的共享数组,这是 3072 字节,大约是最小共享内存限制的 10%。
着色器在 16x16 工作组中启动,因此每个线程将写入 3 个唯一位置,并从一个唯一位置读回
然后将纹理渲染为 HSV,其中 0-1 之间的值将映射到色调 0-360(红-青-红),超出范围的值将为红色。
#version 430
//Execute in 16x16 sized thread blocks
layout(local_size_x=16,local_size_y=16) in;
uniform layout (r32f) restrict writeonly image2D _imageOut;
shared float hoz[16][48];
void main ()
{
//Init shared memory with a big out of bounds value we can identify
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 20000.0f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 20000.0f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 20000.0f;
//Sync shared memory
memoryBarrierShared();
//Write the values we want to actually read back
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y] = 0.5f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+16] = 0.5f;
hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+32] = 0.5f;
//Sync shared memory
memoryBarrierShared();
//i=0,8,16 work
//i=1-7,9-5,17 don't work (haven't bothered testing further
const int i = 17;
imageStore(_imageOut, ivec2(gl_GlobalInvocationID.xy), vec4(hoz[gl_LocalInvocationID.x][gl_LocalInvocationID.y+i]));
//Sync shared memory (can't hurt)
memoryBarrierShared();
}
以大于 8x8 的启动尺寸启动此着色器会在图像的受影响区域产生伪影。
glDispatchCompute(9, 9, 0);
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
我不得不断点和步进帧来捕捉这个,花了大约 14 帧
glDispatchCompute(512/16, 512/16, 0);//Full image is 512x512
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
再次,我不得不断点和步进帧来捕捉这一点,当 运行 在 60FPS(vsync)时伪影出现得更多 frequently/simultaneously。
memoryBarrierShared();
不,这只会使对其他调用的写入 可见。如果您希望能够从其他调用的数据中读取,则必须确保所有写入实际上都已 发生 。
这是用 the barrier
function 完成的。 在 memoryBarrierShared
.