Compute Shader reading and writing to the same image causing errors

I am creating a 3D cellular automata for a personal project, and I am using a compute shader to read/write to a uimage3D, but I’m having some weird errors when it comes to checking adjacent voxels. As a test, I decided to set each voxel to the value of the voxel to it’s ‘left’ with wrap around. Here’s the code:

#version 460 core

layout (local_size_x = 1, local_size_y = 1, local_size_z = 1) in;
 //This is the size of the width/height/depth of the 3D texture
uniform int uGridSize;

layout(r32ui, binding = 0) uniform uimage3D image;  

void main()
{
uint x = gl_GlobalInvocationID.x;
uint y = gl_GlobalInvocationID.y;
uint z = gl_GlobalInvocationID.z;

uvec4 State = imageLoad(image, ivec3(((x - 1) + uGridSize) % uGridSize, y, z ));

imageStore(image, ivec3(x, y, z), State);
}

This works as expected for the first few seconds, but here’s how it looks after a few seconds: (Note, only one voxel has a value of one, the rest are zero, and if a voxel has a value of zero, I don’t render it as a little cube)


As you can see, there’s some kind of smearing happening. I’m not sure where the problem lies. Here’s my texture initialization code and my compute shader code.

glGenTextures(1, &m_3DTexture);

glBindTexture(GL_TEXTURE_3D, m_3DTexture);

glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_S, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_T, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_WRAP_R, GL_REPEAT);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
glTexParameteri(GL_TEXTURE_3D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

glTexImage3D(GL_TEXTURE_3D, 0, GL_R32UI, m_GridSize, m_GridSize, m_GridSize, 0, GL_RED_INTEGER, GL_UNSIGNED_INT, m_CellData.data());

glBindImageTexture(0, m_3DTexture, 0, GL_TRUE, 0, GL_READ_WRITE, GL_R32UI);

And the compute shader dispatch code:

glUseProgram(m_ComputeProgramID);

glUniform1i(glGetUniformLocation(m_ComputeProgramID, "uGridSize"), m_GridSize);
glUniform1i(glGetUniformLocation(m_ComputeProgramID, "uStates"), m_States);
glUniform1i(glGetUniformLocation(m_ComputeProgramID, "uMooreNeighborhood"), m_MooreNeighborhood);

glUniform1i(glGetUniformLocation(m_ComputeProgramID, "uSurviveCount"), m_SurviveCount);
glUniform1i(glGetUniformLocation(m_ComputeProgramID, "uBirthCount"), m_BirthCount);

glDispatchCompute((unsigned int)m_GridSize, (unsigned int)m_GridSize, (unsigned int)m_GridSize);

glMemoryBarrier(GL_ALL_BARRIER_BITS);

Thanks for your help!

There is no defined ordering between the invocations of the compute shader kernel within the same dispatch, therefore the imageLoad may read a value that has already been updated by the invocation writing to that location or it may read a previous value - or possibly any value because this is at least skirting undefined behavior territory :wink:

A common way to solve this is to use two textures: one that you read values from the previous computation step from and another that you write the updated values to. For the next iteration you reverse the roles of these two textures - aka you ping-pong between them.

1 Like