OpenGL - Calling glBindBufferBase with index = 1 breaks rendering (Pitch black) - opengl

There's an array of uniform blocks in my shader which is defined as such:
layout (std140) uniform LightSourceBlock
{
int shadowMapID;
int type;
vec3 position;
vec4 color;
float dist;
vec3 direction;
float cutoffOuter;
float cutoffInner;
float attenuation;
} LightSources[12];
To be able to bind my buffer objects to each LightSource, I've bound each uniform to a uniform block index:
for(unsigned int i=0;i<12;i++)
glUniformBlockBinding(program,locLightSourceBlock[i],i); // locLightSourceBlock contains the locations of each element in LightSources[]
When rendering, I'm binding my buffers to the respective index using:
glBindBufferBase(GL_UNIFORM_BUFFER,i,buffer);
This works fine, as long as I only bind a single buffer to the binding index 0. As soon as there's more, everything is pitch black (Even things that use entirely different shaders). (glGetError returns no errors)
If I change the block indices range from 0-11 to 2-13 (Skipping index 1), everything works as it should. I figured if I use index 1, I'm overwriting something, but I don't have any other uniform blocks in my shader, and I'm not using glUniformBlockBinding or glBindBufferBase anywhere else in my code, so I'm not sure.
What could be causing such behavior? Is the index 1 reserved for something?

1) Dont use multiple blocks. Use one block with array. Something like this:
struct Light{
...
}
layout(std430, binding=0) uniform lightBuffer{
Light lights[42];
}
skip glUniformBlockBinding and only glBindBufferBase to index specified in shader
2) Read up on alignment for std140, std430. In short, buffer variable are aligned so they dont cross 128bit boundaries. So in your case position would start at byte 16 (not 8). This results in mismatch of CPU/GPU side access. (Reorder variables or add padding)

Related

Unexpeced value upon accessing an SSBO float

I am trying to calculate a morph offset for a gpu driven animation.
To that effect I have the following function (and SSBOS):
layout(std140, binding = 7) buffer morph_buffer
{
vec4 morph_targets[];
};
layout(std140, binding = 8) buffer morph_weight_buffer
{
float morph_weights[];
};
vec3 GetMorphOffset()
{
vec3 offset = vec3(0);
for(int target_index=0; target_index < target_count; target_index++)
{
float w1 = morph_weights[1];
offset += w1 * morph_targets[target_index * vertex_count + gl_VertexIndex].xyz;
}
return offset;
}
I am seeing strange behaviour so I opened renderdoc to trace the state:
As you can see, index 1 of the morph_weights SSBO is 0. However if I step over in the built in debugger for renderdoc I obtain:
Or in short, the variable I get back is 1, not 0.
So I did a little experiment and changed one of the values and now the SSBO looks like this:
And now I get this:
So my SSBO of type float is being treated like an ssbo of vec4's it seems. I am aware of alignment issues with vec3's, but IIRC floats are fair game. What is happenning?
Upon doing a little bit of asking around.
The issue is the SSBO is marked as std140, the correct std for a float array is std430.
For the vulkan GLSL dialect, an alternative is to use the scalar qualifier.

Dynamic indexing into uniform array of sampler2D doesn't work

I need to index into array of 2 uniform sampler2D. The index is dynamic per frame.That's,I have a dynamic uniform buffer which provides that index to a fragment shader. I use Vulkan API 1.2. In device feature listing I have:
shaderSampledImageArrayDynamicIndexing = 1
I am not sure 100% but It looks like this feature is core in 1.2.Nevertheless I did try to enable it during device creation like this:
VkPhysicalDeviceFeatures features = {};
features.shaderSampledImageArrayDynamicIndexing = VK_TRUE;
Then plugging into device creation:
VkDeviceCreateInfo deviceCreateInfo = {};
deviceCreateInfo.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;
deviceCreateInfo.pQueueCreateInfos = queueCreateInfos;
deviceCreateInfo.queueCreateInfoCount = 1;
deviceCreateInfo.pEnabledFeatures = &features ;
deviceCreateInfo.enabledExtensionCount = NUM_DEVICE_EXTENSIONS;
deviceCreateInfo.ppEnabledExtensionNames = deviceExtensionNames;
In the shader it looks like this:
layout(std140,set=0,binding=1)uniform Material
{
vec4 fparams0;
vec4 fparams1;
uvec4 iparams; //.z - array texture idx
uvec4 iparams1;
}material;
layout (set=1,binding = 0)uniform sampler2D u_ColorMaps[2];
layout (location = 0)in vec2 texCoord;
layout(location = 0) out vec4 outColor;
void main()
{
outColor = texture(u_ColorMaps[material.iparams.z],texCoord);
}
What I get is a combination of image pixels with some weird color. If I change to fixed indices - it works correctly. material.iparams.z param has been verified,it provides correct index number every frame (0 or 1). No idea what else is missing.Validation layers say nothing.
Mys setup: Windows, RTX3000 ,NVIDIA beta driver 443.41 (Vulkan 1.2)
Update:
I also found that dynamically indexed sampler return a value in Red channel (r)
which is close to one and zeros in GB. I don't set red color anyway,also the textures I fetch don't contain red. Here are two sreenshot, the upper is correct result which I get when indexing with constant value. Second is what happens when I index with dynamic uint which comes from dynamic UBO:
Correct
Wrong
The problem was due to usage of Y′CBCR samplers. It appears that Vulkan disallows indexing dynamically into array of such uniforms.
Here is what Vulkan specs says:
If the combined image sampler enables sampler Y′CBCR conversion or
samples a subsampled image, it must be indexed only by constant
integral expressions when aggregated into arrays in shader code,
irrespective of the shaderSampledImageArrayDynamicIndexing feature.
So,the solution for me was to provide two separately bound samplers and use dynamic indices with if()..else condition to decide which sampler to use. Push constants would also work,but in this case I have to re-record command buffers all the time. Hopefully this info will be helpful to other people working with video formats in Vulkan API.

Wrong alignment for floats array

Im passing uniform buffer to compute shader in vulkan. The buffer contains an array of 49 floating point numbers (gaussian matrix). Everything is fine, but when I read array in the shader, it gives only 13 values, the others are 0 or gunk, and they correspond to 0, 4, 8, etc. of initial array. I think its some kind of alignment problem
Shader layouts are
struct Pixel
{
vec4 value;
};
layout(push_constant) uniform params_t
{
int width;
int height;
} params;
layout(std140, binding = 0) buffer buf
{
Pixel imageData[];
};
layout (binding = 1) uniform sampler2D inputTex;
layout (binding = 2) uniform unf_t
{
float gauss[SAMPLE_SIZE*SAMPLE_SIZE];
};
Could that be binding 0 influencing binding 2? and if so how can I copy array to buffer with needed alignment? Currently I use
vkCmdUpdateBuffer(a_cmdBuff, a_uniform, 0, a_gaussSize, (const uint32_t *)gauss)
or may be better to split on different sets?
Edit: by expanding buffer and array i manage to pass it with alignment of 16 and all great, but it looks like a waste of memory. How can I align floats by 4?
Uniform blocks require that array elements are aligned to vec4 (16 bytes).
To work around this you use a vec4 instead and you can pass 52 floats and then take the correct component based on index/4 and index%4.

Only get garbage from Shader Storage Block?

I have bound the shader storage buffer to the shader storage block like so
GLuint index = glGetProgramResourceIndex(myprogram, GL_SHADER_STORAGE_BLOCK, name);
glShaderStorageBlockBinding(myprogram, index, mybindingpoint);
glBindBuffer(GL_SHADER_STORAGE_BUFFER, mybuffer)
glBindBufferRange(GL_SHADER_STORAGE_BUFFER, mybindingpoint, mybuffer, 0, 48);
glBufferSubData(GL_SHADER_STORAGE_BUFFER, 0, 48, &mydata);
mydata points to a std::vector containing 4 glm::vec3 objects.
Because I bound 48 bytes as the buffer range I expect lights[] to hold 48/(4*3) = 4 vec3s.
layout(std430) buffer light {
vec3 lights[];
};
The element at index 1 in my std::vector holds the data x=1.0, y=1.0, z=1.0.
But viewing the output by doing
gl_FragColor = vec4(lights[1], 1.0);
I see yellow (x=1.0, y=1.0, z=0.0) pixels. This is not what I loaded into the buffer.
Can somebody tell me what I am doing wrong?
EDIT
I just changend the shader storage block to
layout(std430) buffer light {
float lights[];
};
and output
gl_FragColor = vec4(lights[3],lights[4],lights[5],1.0);
and it works (white pixels).
If somebody can explain this, that would still be great.
It's because people don't take this simple advice: never use a vec3 in a UBO/SSBO.
The base alignment of a vec3 is 16 bytes. Always. Therefore, when it is arrayed, the array stride (the number of bytes from one element to the next) is always 16. Exactly the same as a vec4.
Yes, std430 layout is different from std140. But it's not that different. Specifically, it only prevents the base alignment and stride of array elements (and base alignment of structs) from being rounded up to that of a vec4. But since the base alignment of vec3 is always equal to that of a vec4, it changes nothing about them. It only affects scalars and vec2's.

QGLShaderProgram::setAttributeArray(0, ...) VERSUS QGLShaderProgram::setAttributeArray("position", ...)

I have a vertex shader:
#version 430
in vec4 position;
void main(void)
{
//gl_Position = position; => works in ALL cases
gl_Position = vec4(0,0,0,1);
}
if I do:
m_program.setAttributeArray(0, m_vertices.constData());
m_program.enableAttributeArray(0);
everything works fine. However, if I do:
m_program.setAttributeArray("position", m_vertices.constData());
m_program.enableAttributeArray("position");
NOTE: m_program.attributeLocation("position"); returns -1.
then, I get an empty window.
Qt help pages state:
void QGLShaderProgram::setAttributeArray(int location, const QVector3D
* values, int stride = 0)
Sets an array of 3D vertex values on the attribute at location in this shader program. The stride indicates the
number of bytes between vertices. A default stride value of zero
indicates that the vertices are densely packed in values.
The array will become active when enableAttributeArray() is called on
the location. Otherwise the value specified with setAttributeValue()
for location will be used.
and
void QGLShaderProgram::setAttributeArray(const char * name, const
QVector3D * values, int stride = 0)
This is an overloaded function.
Sets an array of 3D vertex values on the attribute called name in this
shader program. The stride indicates the number of bytes between
vertices. A default stride value of zero indicates that the vertices
are densely packed in values.
The array will become active when enableAttributeArray() is called on
name. Otherwise the value specified with setAttributeValue() for name
will be used.
So why is it working when using the "int version" and not when using the "const char * version"?
It returns -1 because you commented out the only line in your shader that actually uses position.
This is not an error, it is a consequence of a misunderstanding how attribute locations are assigned. Uniforms and attributes are only assigned locations after all shader stages are compiled and linked. If a uniform or attribute is not used in an active code path it will not be assigned a location. Even if you use the variable to do something like this:
#version 130
in vec4 dead_pos; // Location: N/A
in vec4 live_pos; // Location: Probably 0
void main (void)
{
vec4 not_used = dead_pos; // Not used for vertex shader output, so this is dead.
gl_Position = live_pos;
}
It actually goes even farther than this. If something is output from a vertex shader but not used in a geometry, tessellation or fragment shader, then its code path is considered inactive.
Vertex attribute location 0 is implicitly vertex position, by the way. It is the only vertex attribute that the GLSL spec. allows to alias to a fixed-function pointer function (e.g. glVertexPointer (...) == glVertexAttribPointer (0, ...))