Reading a texture inside if statement

Started by
8 comments, last by Volgogradetzzz 9 years, 7 months ago

Hello. I have a theory question. I'm programming in flash stage3d and it's forbidden to read a texture inside conditionals. But why? I searched in goggle but what I could find is that behavior caused by derivatives (but how ddx/ddy relates to texture read?) - it's impossible to get them in if-statement. But, again, why? As far as I know pixel shader runs on 2x2 pixel grid. If there's a condition in a shader - both parts of it run anyway. So since it runs anyway we have final color in the end. So what's the problem to calculate derivatives?

Advertisement
imagin a shader like this


if(UV.x>=0)
 color=Tex1D(texture,sqrt(UV.x));
what would the proper derivatives be in that case? you as a human might think of a work around or a better solution, but the shader compiler and the HW don't really know.
also, that error is just a desperate try to avoid bad results, you can of course do a
color=Tex1D(texture,sqrt(UV.x));
and skip the compile error, yet you've just hidden the problem, it's still there and the compiler just doesn't know and the hw will show you eventually bad results.
Hi, Krypt0n. Thank you. But I still don't get it. Why derivative inside inside 'if' impossible if that 'if' computes anyway? And how derivative relates to texture fetch? It's just uv I need - interpolated value from vertex shader.

No idea what either of you are talking about. You can sample a texture how ever you want whenever you want. Are you suggesting "stage3d" (no idea what this is), will not let you do it? If so then maybe that's a restriction on that language.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

dpadam450, stage3d is a wraper that uses directx (9 or 11) on windows, opengl on mac and opengl es on mobiles.

No idea what either of you are talking about.

From http://msdn.microsoft.com/en-us/library/windows/desktop/bb509610%28v=vs.85%29.aspx:

There are certain cases where using the branch or flatten attributes may generate a compile error. The branch attribute may fail if either side of the if statement contains a gradient function, such as tex2D.

And I can't understand this - why sampling uses gradient functions? And what's the problem if both sides of the branch executes.


If there's a condition in a shader - both parts of it run anyway.

That's not always true; for example, you can control this behavior as shown here in HLSL. However, even when it is, you don't keep both parts just because you evaluated both parts.

MJP's reply in this thread does a good job of explaining why the problem exists and how to work around it. Chapter 24.2 of this also explains how ddx/ddy are related to texture sampling if you're interested in the why.

Thank you. I've read all your links. And I understood that I'm totally confused. Why ddx/ddy can't be calculated ith branch? The pixel have have it's output color, it have interpolated values from vertex shader. Even if shader calculate both sides of the branch, it applies a mask for choosing correct color for pixel. What keeps it to apply that mask to a ddx/ddy values?

Most GPU's are really just SIMD machines. What this means for you, is that the GPU will effectively execute the same instruction for multiple "lanes" simultaneously (usually referred to as "threads"), with each lane having different data. So if you do write code that does "y = x + z", the GPU will execute an add instruction for multiple lanes simultaneously, where x and z might have different values.

This makes branching complicated. Take a simple branch like this:


if(x > 1.0f)
    x = x * 10.0f;

On a GPU, multiple nearby SIMD lanes will be executing these instructions with different values of x. So for some lanes x might be greater than 1.0, and for some it might not. Since all of the lanes execute the same instructions, the GPU can't just multiply the value by 10.0. Instead what it does is it will evaluate the condition for all lanes, and use that to set a per-lane mask. This mask then controls whether any executed instructions actually have any effect. So the GPU will execute the multiply instruction, but it won't actually do anything unless the mask bit is set for that lane. This ends up giving you the same result as if you actually excecuted the branching behavior for each lane individually. The only time the GPU doesn't have to mess with this masking business is if you all lanes take the same path, resulting in a mask of all 0's or all 1's. In this case if the mask was all 0's, then the GPU could actually skip the multiply instruction.

So now we get to sampling and derivatives. In order to pick which mip level to use, GPU's look at nearby pixels in a 2x2 quad and calculate the difference in texture coordinates. This difference gives you the partial screen-space derivatives. If the difference is very small relative to the texture size, then the GPU can use a higher-resolution mip level. If the difference is large, it uses a low-resolution mip level. In practice, this "looking at nearby pixels" is done by packing these nearby pixels in nearby SIMD lanes, and executing special instructions that allow one lane to get the value of something for another lane. Most of the time this is perfectly okay, since the GPU always executes the same instructions for all lanes. Where it goes awry, is with the conditional masking that I explained above. If one lane is masked off but another is masked off, the masked off lane won't be able to give it's neighbor the value of its texture coordinate. Without that value it can't compute the partial derivatives, and so it can't sample with mipmaps.

The typical workaround is to compute the derivatives manually outside of the branch, and then pass them into a version of sample() that takes explicit derivatives. Or if you don't need automatic mipmap selection, you can use a version of sample() that lets you pick the mip level yourself.

The if/branch has nothing specifically to do with texture sampling. I don't know much about the compile error DX doc says it may push, that could be if you are using a lot of samples/branch samples where it only allows you to say perform 8 samples and because of flattening you actually have 16.

I can tell you my experience with OpenGL an if() statement has sped up my pixel shader in various cases if not speeding up every case, so I don't know when it will actually flatten vs allow dynamic branching.

I was going to say the derivatives have nothing to do with branching, but the above posts suggests something otherwise. But what I dont get is those deriviates are based on the uv values relative to screen space pixels.......if I sample 2 textures 1024 and 512, and only sample one of them and they both use the same uv coords, or not, how does that matter? Mip map selection should still be fine for both those textures. What am I missing. They should be able to fetch properly. I don't know I've used if's all over the place surrounding what textures to sample or not and never seen anything crazy, unless I just havent benchmarked them enough.

NBA2K, Madden, Maneater, Killing Floor, Sims http://www.pawlowskipinball.com/pinballeternal

MJP, thanks a lot. Your explanation is indeed very good. It seems that it's a problem with me smile.png . How does texture coordinates relates to your example:


if(x > 1.0f)
    x = x * 10.0f;

Sorry, I can't understand it - if shader will execute both parts of a branch and mask out wrong result - it will have the correct data anyway.

This topic is closed to new replies.

Advertisement