What is wrong with my compute shader array indexing? - c++

I'm currently having a problem with my compute shader failing to properly get an element at a certain index of an input array.
I've read the buffers manually using NVidia NSight and it seems to be input properly, the problem seems to be with indexing.
It's supposed to be drawing voxels on a grid, take this case as an example (What is supposed to be drawn is highlighted in red while blue is what I am getting):
And here is the SSBO buffer capture in NSight transposed:
This is the compute shader I'm currently using:
#version 430
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform image2D img_output;
layout(std430) buffer;
layout(binding = 0) buffer Input0 {
ivec2 mapSize;
};
layout(binding = 1) buffer Input1 {
bool mapGrid[];
};
void main() {
// base pixel colour for image
vec4 pixel = vec4(1, 1, 1, 1);
// get index in global work group i.e x,y position
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
vec2 normalizedPixCoords = vec2(gl_GlobalInvocationID.xy) / gl_NumWorkGroups.xy;
ivec2 voxel = ivec2(int(normalizedPixCoords.x * mapSize.x), int(normalizedPixCoords.y * mapSize.y));
float distanceFromMiddle = length(normalizedPixCoords - vec2(0.5, 0.5));
pixel = vec4(0, 0, mapGrid[voxel.x * mapSize.x + voxel.y], 1); // <--- Where I'm having the problem
// I index the voxels the same exact way on the CPU code and it works fine
// output to a specific pixel in the image
//imageStore(img_output, pixel_coords, pixel * vec4(vignettecolor, 1) * imageLoad(img_output, pixel_coords));
imageStore(img_output, pixel_coords, pixel);
}
NSight doc file: https://ufile.io/wmrcy1l4

I was able to fix the problem by completely ditching SSBOs and using a texture buffer, turns out the problem was that OpenGL treated each value as a 4-byte value and stepped 4 bytes instead of one for each index.
Based on this post: Shader storage buffer object with bytes

Related

OpenGL compute shader not putting value in uniform buffer

I'm trying to make a compute shader for computing texture samples. For now I just want it to sample a single point in a texture, and later on I will try to have it do so for an array of vectors. While testing this, I found that the shader doesn't seem to be setting the value I'm using as the output.
Shader:
#version 430
layout(local_size_x = 1) in;
layout(std430, binding = 1) buffer samplePoints {
vec2 pos;
};
layout(std430, binding = 2) buffer samples {
float samp;
};
uniform sampler2D tex;
void main() {
//samp = texture(tex, pos).r;
samp = 7.0f;
}
Setting samp to 7 is a test. I run the shader with PyOpenGL, the relevant part being:
shader = GL.glCreateShader(GL.GL_COMPUTE_SHADER)
GL.glShaderSource(shader, open("test.glsl").read())
GL.glCompileShader(shader)
program = GL.glCreateProgram()
GL.glAttachShader(program, shader)
GL.glLinkProgram(program)
points, samples = GL.glGenBuffers(2)
GL.glBindBuffer(GL.GL_UNIFORM_BUFFER, points)
GL.glBufferData(GL.GL_UNIFORM_BUFFER, 8, b"\0\0\0\0\0\0\0\0", GL.GL_STATIC_DRAW)
GL.glBindBuffer(GL.GL_UNIFORM_BUFFER, samples)
GL.glBufferData(GL.GL_UNIFORM_BUFFER, 4, b"\0\0\0\0", GL.GL_STATIC_DRAW)
GL.glUseProgram(program)
GL.glBindBufferBase(GL.GL_UNIFORM_BUFFER, 1, points)
GL.glBindBufferBase(GL.GL_UNIFORM_BUFFER, 2, samples)
GL.glDispatchCompute(1, 1, 1)
GL.glBindBuffer(GL.GL_UNIFORM_BUFFER, samples)
a = GL.glGetBufferSubData(GL.GL_UNIFORM_BUFFER, 0, 4).view("<f4")
print(a)
This results in just printing the float made from the 4 bytes I placed in the samples buffer earlier. 0 in this case. I've omitted various bits of error-checking, none of which report any errors along the way.
Where am I going wrong?
That looks like a storage buffer, not a uniform buffer, so wouldn't you need to use GL_SHADER_STORAGE_BUFFER? Also you need to use glMemoryBarrier before accessing the contents.

Array of structures into Compute Shader

Writing a simple compute shader in OpenGL to understand how it works, I can't manage to obtain the wanted result.
I want to pass to my compute shader an array of structures colourStruct to color an output texture.
I would like to have a red image when "wantedColor" = 0 in my compute shader and a green image "wantedColor" = 1, blue for 2.
But I actually have only red when "wantedColor" = 1 or 2 or 3 and black when "wantedColor" > 2...
If someone has an idea, or maybe I did not understand the compute shader inputs ideas.
Thank you for your help, here is the interesting part of my code.
My compute shader :
#version 430 compatibility
layout(std430, binding=4) buffer Couleureuh
{
vec3 Coul[3]; // array of structures
};
layout(local_size_x = 1, local_size_y = 1) in;
layout(rgba32f, binding = 0) uniform image2D img_output;
void main() {
// base pixel colour for image
vec4 pixel = vec4(0.0, 0.0, 0.0, 1.0);
// get index in global work group i.e x,y, position
ivec2 pixel_coords = ivec2(gl_GlobalInvocationID.xy);
ivec2 dims = imageSize (img_output);
int colorWanted = 0;
pixel = vec4(Coul[colorWanted], 1.0);
// output to a secific pixel in the image
imageStore (img_output, pixel_coords, pixel);
}
Compute shader and SSBO initialization:
GLuint structBuffer;
glGenBuffers(1, &structBuffer);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, structBuffer);
glBufferData(GL_SHADER_STORAGE_BUFFER, 3*sizeof(colorStruct), NULL, GL_STATIC_DRAW);
GLint bufMask = GL_MAP_WRITE_BIT | GL_MAP_INVALIDATE_BUFFER_BIT; // invalidate makes a ig difference when re-writting
colorStruct *coul;
coul = (colorStruct *) glMapBufferRange(GL_SHADER_STORAGE_BUFFER, 0, 3*sizeof(colorStruct), bufMask);
coul[0].r = 1.0f;
coul[0].g = 0.0f;
coul[0].b = 0.0f;
coul[1].r = 0.0f;
coul[1].g = 1.0f;
coul[1].b = 0.0f;
coul[2].r = 0.0f;
coul[2].g = 0.0f;
coul[2].b = 1.0f;
glUnmapBuffer(GL_SHADER_STORAGE_BUFFER);
glBindBufferBase(GL_SHADER_STORAGE_BUFFER, 4, structBuffer);
m_out_texture.bindImage();
// Launch compute shader
m_shader.use();
glDispatchCompute(m_tex_w, m_tex_h, 1);
// Prevent samplign before all writes to image are done
glMemoryBarrier(GL_SHADER_IMAGE_ACCESS_BARRIER_BIT);
vec3 are always 16-byte aligned. As such, when they're in an array, they act like vec4s. Even with std430 layout.
Never use vec3 in interface blocks. You should either use an array of floats (individually access the 3 members you want) or an array of vec4 (with an unused element).

OpenGL Reading from a texture unit currently bound to a framebuffer

I've encountered an issue trying to read data from a texture unit currently attached to the draw framebuffer. The errors are removed as long as i use a glTextureBarrier between the draw calls. However I'm trying to eliminate draw calls so this is an unoptimal solution. The OpenGL 4.5 specification (section 9.3 Feedback Loops Between Textures and the Framebuffer) states that
The mechanisms for attaching textures to a framebuffer object do not prevent a
one- or two-dimensional texture level, a face of a cube map texture level, or a
layer of a two-dimensional array or three-dimensional texture from being attached to the draw framebuffer while the same texture is bound to a texture unit. While this condition holds, texturing operations accessing that image will produce unde-fined results, as described at the end of section 8.14.
...
Specifically, the values of rendered fragments are undefined if any shader stage fetches texels and the same texels are written via fragment shader outputs, even if the reads and writes are not in the same draw call, unless any of the following exceptions apply:
The reads and writes are from/to disjoint sets of texels (after accounting for texture filtering rules).
There is only a single read and write of each texel, and the read is in
the fragment shader invocation that writes the same texel (e.g. using texelFetch2D(sampler, ivec2(gl_FragCoord.xy), 0);).
If a texel has been written, then in order to safely read the result a texel fetch must be in a subsequent draw call separated by the command void TextureBarrier( void );
TextureBarrier will guarantee that writes have completed and caches have
been invalidated before subsequent draw calls are executed.
I do this to 4 other textures without problems. For these textures I only do one read and one write from the same texel, so they are covered by the 2nd exception. The texture causing the issue requires some filtering, thus I need more than one read and from different texels before writing. I figured this could be allowed through the 1st exception, so I put them in an array texture alternating which layer was read and written to. The idea was that this would create a setting where the reads and writes are from/to disjoint set of texels. It didn't help.
I also tried to do a glTextureBarrier only every other draw call to see if it was the third draw call that was causing the issue. This gave different (Still wrong) results from when I used barriers all the time and when i didn't.
The draw calls are drawing one point in 0,0,0 that is exploded to a fullscreen quad in a geometry shader.
Update
I am raytracing through volume data.
Framebuffer Setup
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, rayPositionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT1, rayDirectionTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT2, IORTexture, 0);
glFramebufferTexture(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT3, resultTexture, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT4, rayOffsetTexture, 0, 0);
glFramebufferTextureLayer(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT5, rayOffsetTexture, 0, 1);
Shader
...
uniform sampler2D RayPositionTexture;
uniform sampler2D RayDirectionTexture;
uniform sampler2DArray RayOffsetTexture;
uniform sampler2D IORTexture;
uniform sampler2D ColorTexture;
...
flat in uint offsetIndex;
layout(location = 0) out vec4 outRayPosition;
layout(location = 1) out vec4 outRayDirection;
layout(location = 2) out float outIOR;
layout(location = 3) out vec4 outColor;
layout(location = 4) out vec4 outRayOffsetA;
layout(location = 5) out vec4 outRayOffsetB;
...
vec3 filteredOffset(ivec2 Pixel)
{
vec3 Offset = vec3(0.0);
float TotalWeight = 0.0;
for(int i = -KernelRadius; i <= KernelRadius; i++)
{
for (int j = -KernelRadius; j <= KernelRadius; j++)
{
ivec2 SampleOffset = ivec2(i,j);
ivec3 SampleLocation = ivec3(Pixel + SampleOffset, offsetIndex);
vec3 Sample = texelFetch(RayOffsetTexture, SampleLocation, 0).xyz;
float Weight = KernelRadius > 0 ? gauss(SampleOffset) : 1.0f;
Offset += Sample * Weight;
TotalWeight += Weight;
}
}
Offset = Offset / TotalWeight;
return Offset;
}
...
//if (offsetIndex == 1)
outRayOffsetA = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
//else
outRayOffsetB = vec4(RayDirection.xyz - RayDirectionNew, 0.0);
outIOR = IOR;
} else {
// if (offsetIndex == 1)
outRayOffsetA = vec4(0.0);
// else
outRayOffsetB = vec4(0.0);
outIOR = PreviousIOR;
//imageStore(VolumeBackTexture, Pixel, vec4(1.0));
}
outColor = Color;
...
Draw calls
GLenum drawBuffers[] = { GL_COLOR_ATTACHMENT0, GL_COLOR_ATTACHMENT1, GL_COLOR_ATTACHMENT2, GL_COLOR_ATTACHMENT3, GL_COLOR_ATTACHMENT4, GL_COLOR_ATTACHMENT5 };
glDrawBuffers(6, drawBuffers);
// Run shader
glBindVertexArray(pointVAO);
float distance = 0.0f;
for (int i = 0; distance < 1.73205080757f; i++)
{
glTextureBarrier();
glDrawArrays(GL_POINTS, i, 1);
distance += fUnitStep;
}
glBindVertexArray(0);
The above without the texture barrier gives the same results (wrong result) as
glBindVertexArray(pointVAO);
glDrawArrays(GL_POINTS, 0, int(std::ceil(1.73205080757f / fUnitStep)));
glBindVertexArray(0);
To answer this, the TextureBarrier is required here because I wanted to read from texels that had just been drawn. This can only be done safely if the previous draw call has completed and texture caches have been invalidated, which is exactly what TextureBarrier guarantees.

Why is texelFetch always returning 0 for a 1D single channel texture?

I'm using a 1D texture to store single-channel integer data which needs to be accessed by the fragment shader. Coming from the application, the integer data type is GLubyte and needs to be accessed as an unsigned integer in the shader. Here is how the texture is created (note that there are other texture units being bound after, which I'm hoping are unrelated to the problem):
GLuint mTexture[2];
std::vector<GLubyte> data;
///... populate with 289 elements, all with value of 1
glActiveTexture(GL_TEXTURE0);
{
glGenTextures(1, &mTexture[0]);
glBindTexture(GL_TEXTURE_1D, mTexture[0]);
{
glTexImage1D(GL_TEXTURE_1D, 0, GL_R8UI, data.size(), 0,
GL_RED_INTEGER, GL_UNSIGNED_BYTE, &data[0]);
}
glBindTexture(GL_TEXTURE_1D, 0);
}
glActiveTexture(GL_TEXTURE1);
{
//Setup the other texture using mTexture[1]
}
The fragment shader looks like this:
#version 420 core
smooth in vec2 tc;
out vec4 color;
layout (binding = 0) uniform usampler1D buffer;
layout (binding = 1) uniform sampler2DArray sampler;
uniform float spacing;
void main()
{
vec3 pos;
pos.x = tc.x;
pos.y = tc.y;
if (texelFetch(buffer, 0, 0).r == 1)
pos.z = 3.0;
else
pos.z = 0.0;
color = texture(sampler, pos);
}
The value returned from texelFetch in this example basicaly dictates which texture layer to use from the 2D array for the final output color. I want it to return the value 1, but it always returns 0 and hits the else clause in the fragment shader. Using NVIDIA's Nsight tool, I can see the texture does contain the value 1, 289 times:

opengl 3d texture issue

I'm trying to use a 3d texture in opengl to implement volume rendering. Each voxel has an rgba colour value and is currently rendered as a screen facing quad.(for testing purposes). I just can't seem to get the sampler to give me a colour value in the shader. The quads always end up black. When I change the shader to generate a colour (based on xyz coords) then it works fine. I'm loading the texture with the following code:
glGenTextures(1, &tex3D);
glBindTexture(GL_TEXTURE_3D, tex3D);
unsigned int colours[8];
colours[0] = Colour::AsBytes<unsigned int>(Colour::Blue);
colours[1] = Colour::AsBytes<unsigned int>(Colour::Red);
colours[2] = Colour::AsBytes<unsigned int>(Colour::Green);
colours[3] = Colour::AsBytes<unsigned int>(Colour::Magenta);
colours[4] = Colour::AsBytes<unsigned int>(Colour::Cyan);
colours[5] = Colour::AsBytes<unsigned int>(Colour::Yellow);
colours[6] = Colour::AsBytes<unsigned int>(Colour::White);
colours[7] = Colour::AsBytes<unsigned int>(Colour::Black);
glTexImage3D(GL_TEXTURE_3D, 0, GL_RGBA, 2, 2, 2, 0, GL_RGBA, GL_UNSIGNED_BYTE, colours);
The colours array contains the correct data, i.e. the first four bytes have values 0, 0, 255, 255 for blue. Before rendering I bind the texture to the 2nd texture unit like so:
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_3D, tex3D);
And render with the following code:
shaders["DVR"]->Use();
shaders["DVR"]->Uniforms["volTex"].SetValue(1);
shaders["DVR"]->Uniforms["World"].SetValue(Mat4(vl_one));
shaders["DVR"]->Uniforms["viewProj"].SetValue(cam->GetViewTransform() * cam->GetProjectionMatrix());
QuadDrawer::DrawQuads(8);
I have used these classes for setting shader params before and they work fine. The quaddrawer draws eight instanced quads. The vertex shader code looks like this:
#version 330
layout(location = 0) in vec2 position;
layout(location = 1) in vec2 texCoord;
uniform sampler3D volTex;
ivec3 size = ivec3(2, 2, 2);
uniform mat4 World;
uniform mat4 viewProj;
smooth out vec4 colour;
void main()
{
vec3 texCoord3D;
int num = gl_InstanceID;
texCoord3D.x = num % size.x;
texCoord3D.y = (num / size.x) % size.y;
texCoord3D.z = (num / (size.x * size.y));
texCoord3D /= size;
texCoord3D *= 2.0;
texCoord3D -= 1.0;
colour = texture(volTex, texCoord3D);
//colour = vec4(texCoord3D, 1.0);
gl_Position = viewProj * World * vec4(texCoord3D, 1.0) + (vec4(position.x, position.y, 0.0, 0.0) * 0.05);
}
uncommenting the line where I set the colour value equal to the texcoord works fine, and makes the quads coloured. The fragment shader is simply:
#version 330
smooth in vec4 colour;
out vec4 outColour;
void main()
{
outColour = colour;
}
So my question is, what am I doing wrong, why is the sampler not getting any colour values from the 3d texture?
[EDIT]
Figured it out but can't self answer (new user):
As soon as I posted this I figured it out, I'll put the answer up to help anyone else (it's not specifically a 3d texture issue, and i've also fallen afoul of it before, D'oh!). I didn't generate mipmaps for the texture, and the default magnification/minification filters weren't set to either GL_LINEAR, or GL_NEAREST. Boom! no textures. Same thing happens with 2d textures.
As soon as I posted this I figured it out, I'll put the answer up to help anyone else (it's not specifically a 3d texture issue, and i've also fallen afoul of it before, D'oh!). I didn't generate mipmaps for the texture, and the default magnification/minification filters weren't set to either GL_LINEAR, or GL_NEAREST. Boom! no textures. Same thing happens with 2d textures.