Incomprehensible GLSL behavior

Hi,

To identify a 3D object in the view, I transform the object identifier into color. As the identifier is an integer, it is converted to RGBA (integer to 4 floats values) in the shader. When the number of vertices is large, rendering is greatly slowed down. For real color rendering and 3D object identifier color rendering I use the same shader. To switch between renderings I just use a uniform variable and it works fine.
The behavior I would expect is fast rendering in real color and slow rendering in calculated id colors.
It turns out that rendering is still slow.
The uniform variable is set correctly with glUniform1i () before each call to glDrawElements () and whatever its value, everything behaves as if the color calculation was invoked for each vertex.

With the shader that follows, when the uniform variable “isColorId” is set to 1, the 3D objects are made slightly shifted with the color calculated from the identifier. When “isColorId” is set to 0 the 3D objects are rendered in their place and in black.
This is the expected visual behavior but even with isColorId == 0 rendering performances are slow.

 #version 330 core

layout(location = 0) in vec3 vertex;
layout(location = 1) in uint objectid;
layout(location = 2) in vec4 color;

out vec4 vertexColor;

uniform mat4 mvp;
uniform int isColorId;

const uint UINT_0XFF = uint(255);
const float FLOAT_255 = float(255);

vec4 getColorFromId(uint id) {
	float a = 1;
	float b = float(id         & UINT_0XFF) / FLOAT_255;
	float v = float((id >>  8) & UINT_0XFF) / FLOAT_255;
	float r = float((id >> 16) & UINT_0XFF) / FLOAT_255;

	return vec4(r, v, b, a);
}

void main() {

	if (isColorId == 1) {
		vertexColor = getColorFromId(objectid);
		gl_Position = mvp * vec4(vertex.x + 33, vertex.y + 33, vertex.z, 1.0);
	}
	else {
		vertexColor = vec4(0,0,0,1);
		gl_Position = mvp * vec4(vertex, 1.0);
	}
}

This shader behaves as if it were invoked once for each vertex with isColorId == 1 (whereas its value is in principle 0) but without returning the result in the view then invoked again with isColorId == 0 (the correct value) and the correct result in the view.

If I replace the test on “if (isColorId == 1)” by “if (false)”, rendering by color calculated from the identifier is no longer possible since isColorId is no longer tested but the performance returns to those expected for real color rendering.

void main() {

	if (false) {
		vertexColor = getColorFromId(objectid);
		gl_Position = mvp * vec4(vertex.x + 33, vertex.y + 33, vertex.z, 1.0);
	}
	else {
		vertexColor = vec4(0,0,0,1);
		gl_Position = mvp * vec4(vertex, 1.0);
	}
}

I use OpenGL 3.3 on mac with Radeon Pro 580 8 Go.

Any idea?

With a card that recent, I would not expect you to be able to detect any performance change from such trivial code. How are you measuring the performance difference?

Also, you may want to consider using unpackUnorm4x8 instead of your getColorFromId function, assuming your GLSL version can handle it.

Do you mean that if you run the same program (with same scene) but with a shader that only has something like what follows, it is fast ?

#version 330 core

layout(location = 0) in vec3 vertex;

out vec4 vertexColor;

uniform mat4 mvp;

void main() {
   vertexColor = vec4(0,0,0,1);
   gl_Position = mvp * vec4(vertex, 1.0);
}

I add a few thousand cubes to the scene and apply a rotation to the camera. If the movement is fluid, it goes fast and if the movement jerks then it’s slow :slightly_smiling_face:.


IMAGE 1

Thanks for the info but unfortunately it is not supported in OpenGl 3.3 version. On the other hand, this is another argument that prompts me to upgrade to the OpenGL 4 version.

However, the subject of this post is not exactly the performance issue but the fact that although a uniform variable is set to a value, the test on that value seems to be partially taken into account.

This line of code slows down the rendering (jerks during camera movements) but gives the expected rendering (the color of each cube corresponds to the value of its identifier):

vertexColor = getColorFromId(objectid);


IMAGE 2

This line of code does not slow down the rendering (fluid movements of the camera) and gives the expected rendering (see IMAGE 1).

...
layout(location = 2) in vec4 color;
...
vertexColor = color;

But the combination of the two lines of code dependent on the test on the uniform variable isColorId slows down the rendering regardless of the value of isColorId.

...
	if (isColorId == 1)
		vertexColor = getCouleurIdObjet(objectid);
	else
		vertexColor = color;
...

The value of isColorId is always 0, the rendering gives IMAGE 1, it is correct but the camera movements are jerky. So why since the slowing down treatment is ruled out by the test?

If isColorId is set to 1, the rendering gives IMAGE 2, it is correct and the camera movements are jerky which is normal in my opinion since there is additional processing for each vertex.

Now I comment out the processing that slows down:

...
	if (isColorId == 1)
		; //vertexColor = getCouleurIdObjet(objectid);
	else
		vertexColor = color;
...

The value of isColorId is always 0, rendering is IMAGE 1, and camera movements are smooth.

So that’s the real reason for this post. Why is the processing that slows down (the one tested for isColorId == 1) executed (that’s what I deduce) while the isColorId variable is still set to 0 ?
It is above all this problem of test which disturbs me but I remain interested in the information allowing the improvement of the performances.

Not exactly or at least that is not the real subject of my post. I hope I was clearer in my previous answer.

Finally the transition from OpenGL 3.3 to OpenGL 4.1 did not take more than two minutes, I tested unpackUnorm4x8. Unfortunately the difference is not visible. Too bad .

You cannot expect that shaders do not process all branches of a condition. If it is easy for the shader compiler to discard a branch, it will. But the compiler cannot guess about uniforms.

Depending on how often you are changing the uniform, it might be more fast to just change the shader.

It doesn’t need to guess. The point of a uniform is that its value won’t change between different invocations of a shader within a draw call.

The first generation of programmable GPUs managed to optimise this by effectively treating uniform conditions as #if/#else, compiling different variants of the shader depending upon the values of uniform conditions. This isn’t necessary with modern GPUs having actual branch instructions, but it appears that OP’s compiler fails to spot that this is a uniform condition and treats it as dynamic.

So this is normal behavior, I take note. I hadn’t noticed it until now as the treatments between the branches of the conditions were of similar performance.

Yes I will rethink my code in this sense.
For this specific case, to keep good performance, I would make the change in the vertex buffer by passing the identifier in RGB. So same shader, same vertices, not the same colors and no uniform variable isColorId.

And this is the case here :slightly_smiling_face:.

Thank you all for your explanations and for your responsiveness (one more Sunday). :+1:

Thanks to GClements topic, it seems it might not be so normal. My assumption was somewhat outdated.

And you cannot do it differently.

You might have better understanding of what’s going on under the hoods by enabling debug output.

Well, not everything is perfect in the best of all worlds anyway. I can work around the problem and the explanations given to me are satisfactory.

Thank you for the info. I will look at this documentation. :+1:

This topic was automatically closed 183 days after the last reply. New replies are no longer allowed.