Jump to content
  • Advertisement
Sign in to follow this  
Numsgil

HLSL clamp infinity and nan to 0?

This topic is 2444 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

What's the best way to handle infinity and NaNs in shader code? It's cropped up a few times in the stuff I'm doing where I get either divides by 0, or 0/0, which produce +inf and nan, respectively.

In both cases my algorithm could handle it if I could set those values to 0. But I can't figure out a good way to do that.

Is there some function in HLSL that'll set a number to either itself or 0 if it's infinity or NaN?

Share this post


Link to post
Share on other sites
Advertisement
I'd also like to know.
For the time being, I'd just branch on [font="'Courier New"]isnan[/font]/[font="'Courier New"]isinf[/font]/[font="'Courier New"]isfinite[/font], although I'd be much happier if they returned a per-component result instead of a scalar...

Share this post


Link to post
Share on other sites
The best way is to avoid producing NaNs in the first place -- instead of checking that your result is NaN, check that your divisor is zero.

Share this post


Link to post
Share on other sites

The best way is to avoid producing NaNs in the first place -- instead of checking that your result is NaN, check that your divisor is zero.


It's not always easy if you're trying to avoid branches. Lots of numerical algorithms are "do A unless B then do C instead". If you can robustly wrap up all the logic into a single code path regardless of wonky input you often get wins (performance and/or code size).

...

Anyway, this seems to work on my machine (it's hard to get a definite test going). I'm not sure definitively if this is defined behavior or not, though.


A = step(A, A) * A;


'A' can be a scalar, vector, or matrix. It works on NaNs on the assumption that they won't be >= to themselves. I'm not sure why it'd work on infinities but it does seem to.

Here's the exact test I'm running:

float x = 0;
float y = 0;

float2 vec = float2(x/y, 1/x);

vec = step(vec, vec) * vec;

CheckEqual(float2(0,0), vec);


Where CheckEqual sets some colors up for output. It's possible the compiler is doing some magic stuff behind my back, and that it wouldn't work in a production environment. I haven't looked at the produced assembly.

Share this post


Link to post
Share on other sites
Which shader model are you targeting BTW?
I was thinking of something like: float2 inputs = float2(0, 0);
float2 vec = lerp( saneValues, inputs, abs(sign(inputs)) );
float2 vec = float2(inputs.x/inputs.y, 1/inputs.x);
But without knowing the problem being solved, I don't know how appropriate this is wink.gif

I just tested Krohm's suggestion out too, and it does compile to a conditional move (no branches). vec.x = isinf(vec.x) ? 0 : vec.x;
vec.y = isinf(vec.y) ? 0 : vec.y;
Anyway, this seems to work on my machine (it's hard to get a definite test going). I'm not sure definitively if this is defined behavior or not, though.A = step(A, A) * A;[/quote]Interesting solution! It seems a bit strange though -- both [font="Courier New"]0*inf[/font] and [font="Courier New"]0*NaN[/font] should result in [font="Courier New"]NaN[/font], according to the IEEE standard. Pretty much any operation where one operand is [font="Courier New"]NaN[/font] will produce another [font="Courier New"]NaN[/font], and most operations where one operand is [font="Courier New"]inf[/font] will produce either [font="Courier New"]inf[/font] or [font="Courier New"]NaN[/font]. The fact that you're getting a 0 out of it shows that the GPU isn't following the IEEE spec properly. FXC has the [font="'Courier New"]/Gis[/font] switch to force strict IEEE compliance -- does this affect the result?

Share this post


Link to post
Share on other sites
This might be an odd solution but I just add an epsilon like 0.0001 to my vectors. Always annoyed me that GPU designers didn't just make x/0 equal to zero. It's a trick that comes up a lot when trying to get rid of branches I'd imagine. (That is x/(x + 0.00001) will return 0 or 1 essentially to be used with lerp and other crazy stuff).

Anyone else do this or am I insane?

Share this post


Link to post
Share on other sites

Which shader model are you targeting BTW?


2 or 3. Probably 3 just for instruction count issues (it's a monolithic shader).


Interesting solution! It seems a bit strange though -- both [font="Courier New"]0*inf[/font] and [font="Courier New"]0*NaN[/font] should result in [font="Courier New"]NaN[/font], according to the IEEE standard. Pretty much any operation where one operand is [font="Courier New"]NaN[/font] will produce another [font="Courier New"]NaN[/font], and most operations where one operand is [font="Courier New"]inf[/font] will produce either [font="Courier New"]inf[/font] or [font="Courier New"]NaN[/font]. The fact that you're getting a 0 out of it shows that the GPU isn't following the IEEE spec properly.
[/quote]

Oh, haha. Funny thing is I should have remembered 0*inf and 0*NaN produce NaNs, as I've hit it enough on the CPU side. I'm just so used to using 'step' as a ternary operator.

So yeah, no idea why it works at all then, or if it's cross platform at all.


FXC has the [font="Courier New"]/Gis[/font] switch to force strict IEEE compliance -- does this affect the result?
[/quote]

FX Composer? I used it for a bit but anymore I switched over to a special unit testing harness I built for HLSL, which feeds things through the XNA HLSL compiler. I'm not sure if it has those sorts of options to tweak.

Share this post


Link to post
Share on other sites

This might be an odd solution but I just add an epsilon like 0.0001 to my vectors. Always annoyed me that GPU designers didn't just make x/0 equal to zero. It's a trick that comes up a lot when trying to get rid of branches I'd imagine. (That is x/(x + 0.00001) will return 0 or 1 essentially to be used with lerp and other crazy stuff).

Anyone else do this or am I insane?


I was trying to avoid epsilons where I could. It always feels like a bad hack for shader code.

Share this post


Link to post
Share on other sites
FXC has the [font="Courier New"]/Gis[/font] switch to force strict IEEE compliance -- does this affect the result?
FX Composer? I switched over to the XNA HLSL compiler. I'm not sure if it has those sorts of options to tweak.[/quote]fxc.exe is the D3D offline shader compiler. I haven't used any XNA tools, but I'd hope that they expose the underlying options wink.gif
Anyone else do this or am I insane?
Yeah, I add epsillons to the divisor a lot of places where it's at risk of becomming zero, but 'near zero' still provides a valid result. IIRC, IEEE-float was designed so that in some circumstances you can use specific epsilon values for some purposes like this without adversely affecting 'good' values at all.

Share this post


Link to post
Share on other sites
I haven't used any XNA tools, but I'd hope that they expose the underlying options wink.gif


You'd hope, but you'd be wrong. :P

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement
×

Important Information

By using GameDev.net, you agree to our community Guidelines, Terms of Use, and Privacy Policy.

Participate in the game development conversation and more when you create an account on GameDev.net!

Sign me up!