PDA

View Full Version : break/return statement inside loop reacts unexpectedly



Kleinkind87
02-08-2017, 03:35 AM
I have a very strange problem. I condensed the code down to this:



#version 450

uniform int umsch;//umschalten
layout(location = 0) out vec4 Helligkeit;

void main( void ){
do {
if(umsch == 0){
Helligkeit = vec4(0.0, 1.0, 1.0, 1.0);
return;
}
//break;
} while(false);
Helligkeit = vec4(0.5f);
return;
}


umsch is a uniform which I can toggle by pressing a key through the OpenGL API.

Now the problem is this. The program behaves differently whether the break statement is commented out or not. But it really shouldn't, right?
Can someone confirm or deny this happening in their setup?

PS:
The vertex shader is trivial; just put any triangle on screen for the FS.

PPS:
Interestingly everything works as expected when I replace the break (or 1s return) statement by a discard statement. What is going on here?!

Silence
02-08-2017, 05:10 AM
Don't really know since I haven't tried it.

But one thing comes to my mind. Why doing such a construct:



do{...} while(false);

Kleinkind87
02-08-2017, 05:28 AM
But one thing comes to my mind. Why doing such a construct:

Of course I'm not actually using that construct in my working code. This is just a boiled down version to demonstrate what's happening here.

The inner if-body should be executed (given the condition is met) whether the break statement exists or not, but that's not what I observe.
I can replace the
do{...} while(false); with whatever loop you want. It's always the same misbehaviour. I'm thinking it might be a GLSL compiler bug with my drivers.

GClements
02-09-2017, 06:07 AM
But one thing comes to my mind. Why doing such a construct:


do{...} while(false);


This construct allows you to use a break or continue statement to jump to the end of the block. Other than that, it's the same as a bare block.

Aside: it's also fairly common to see do-while(0) used in C macros to make the macro behave more like a function. The macro definition doesn't include the final semicolon, so if a macro invocation is followed by a semicolon, they form a single statement. This can avoid surprises if the macro invocation occurs within nested conditionals: Consider:


#define foo() { /* several statements */ }
...
if (a)
if (b)
foo();
else
bar();


As it stands, this is parsed as:


#define foo() { /* several statements */ }
...
if (a)
if (b)
foo()
;
else
bar();


The body of foo() is a single block statement, a subsequent semicolon will be parsed as an empty statement. So foo(); is two statements. Only the first will form the body of the if(b) statement, and the empty statement will prevent the else from being part of it.

By changing the macro definition to a do-while statement without its terminating semicolon, the semicolon following the macro invocation will be "absorbed" into the statement.

Kleinkind87
02-09-2017, 10:26 AM
I've checked with a different GPU now and the problem still exists.
I've tested on a AMD HD5800 and and AMD R470 now with the latest drivers.

Can someone test if the problem exists with Nvidia as well?

Silence
02-10-2017, 12:19 AM
This construct allows you to use a break or continue statement to jump to the end of the block. Other than that, it's the same as a bare block.

Aside: it's also fairly common to see do-while(0) used in C macros to make the macro behave more like a function. The macro definition doesn't include the final semicolon, so if a macro invocation is followed by a semicolon, they form a single statement. This can avoid surprises if the macro invocation occurs within nested conditionals: Consider:


#define foo() { /* several statements */ }
...
if (a)
if (b)
foo();
else
bar();


As it stands, this is parsed as:


#define foo() { /* several statements */ }
...
if (a)
if (b)
foo()
;
else
bar();


The body of foo() is a single block statement, a subsequent semicolon will be parsed as an empty statement. So foo(); is two statements. Only the first will form the body of the if(b) statement, and the empty statement will prevent the else from being part of it.

By changing the macro definition to a do-while statement without its terminating semicolon, the semicolon following the macro invocation will be "absorbed" into the statement.

Understood. However, all loops require extra-work for the CPU/GPU whereas conditions are very, very fast.

There might have any interesting situations where one might do such a construct. But I must admit that after a first read of your post, I'm not that convinced. I'll read it more carefully later on. Thanks.

Silence
02-10-2017, 10:09 AM
This construct allows you to use a break or continue statement to jump to the end of the block. Other than that, it's the same as a bare block.

Well, after a new read plus some researches on the internet, right this construct looks used. Certainly more by low-level coders. It's just a goto-like statement for coders who need such a construct but don't want to goto... In C++ you can throw an exception (which might be more slow). This can also be achieved by calling a function which will return (instead of breaking a loop) and continue from the caller. If functions are inlined they have chance to run even more fast than with this loop...
The thing with the semicolon might also be helpful with programmers using a lot of preprocessor macros.

I guess this kind of construct has not the same intent in GLSL than in C for example.

PS: Note that I'm not saying that this is bad or so. Just being curious.