[blog] Whats clamping? And why do we need it?
#1
In pcsx2's advanced options dialog (if you've dared to look), you've might have noticed there's "FPU Clamp Mode" and "VU Clamp Mode" settings. You may even have experimented with some of these modes and found out they fix or break games.
But what are they doing? And why so many options?

Well first you have to know what a floating point number is. A float is simply a representation of a rational number from a string of bits, whose decimal point can change or 'float'. (This isn't the exact definition, but good enough for this conversation).

Basically there's something called the "The IEEE Standard for Floating-Point Arithmetic" (or IEEE 754), which defines floating point representation; and most systems abide by those rules (of course not our wonderful PS2).

There's 32-bit "Single Precision Floats" (commonly referred to as 'float'), 64-bit "Double Precision Floats" (commonly referred to as 'double'), and there's even 128-bit floats called Quads (most processors don't support these natively).

The one we're concerned about is the Single Precision Float, which is 32bits and has a format like this

S x 1bit | E x 8bits | M x 23bits

S = Sign Bit (0 = Positive Number; 1 = Negative Number)
E = Exponent (Biased according to some formula)
M = Mantissa (Normalized with a hidden bit)
I'm not going to go into detail on how this stuff works, but wikipedia is quite helpful Biggrin

Anyways what we need to know is that the IEEE 754 standard defines values with the Exponent bits "11111111", as "NaN" values (short for "Not a Number").

To be more precise, there's also INF (infinity) values, qNaN (quiet NaN), and sNaN (signaling NaN).
Basically NaNs are given when the result of an operation is undefined.

Like if you divide 1 / 0, you get a NaN result, because you can't divide a number by 0.
Or if you take the Square-Root of a negative number, you get a NaN value.

INF values are basically what it sounds like, its an attempt to represent infinity.

The tricky part is that now that you have a NaN or INF value, if you ever try and do a calculation with them, you'll get a NaN or INF value as the result.

Example:
NaN + 500 = NaN (generally the same NaN, since it propagates)
INF - 19999999999 = INF

This is how the IEEE 754 standard defined this stuff (I honestly don't like it, but w/e)

The ps2 however doesn't support NaN's and Infinities!
So basically, the numbers in the range of INF/NaN on a IEEE 754 pc, are just very-large numbers (or very small numbers if negative (if the sign bit is 1)).

For comparison of what your x86-32 CPU does VS the ps2 we have:

Your PC:
1/0 = INF
sqrt (-4) = NaN
INF - 1000 = INF

The PS2:
1/0 = Greatest Positive Float Number (0x7ffffffff)
sqrt (-4) = sqrt(abs(-4)) = 2
INF - 1000 = Some Large Value - 100

And there's many more combinations, but the point is that the results are not the same between PC and the PS2.


So how do we solve these problems?
Well there's no 'fast' way to emulate the ps2 behavior 100% correct (this would require a software FPU, which will be very-slow).

What we do instead is use a combination of clamping, simulation, and cleverness.

The way clamping works is if a value is INF or NaN, we 'clamp' it into the closest value that's not a INF or NaN.
Basically we force values into 'normal numbers' so that it doesn't mess up operations as much.

This isn't 100% exact, but its generally 'good enough' to fix many problems with games.
Let me give you a quick example of something clamping can solve:

Code:
int main() {
    float value = INF;
    do {
        value = value / 2;
        if (value <= 1)  { print("Hello World"); break; }
    } while (true);
}

If the above code was ran on the ps2, it will eventually print "Hello World", because INF is treated as a regular large number, and will continue to be divided by 2 until its less than 1.

But if done on your x86-32 PC, it will never print "Hello World" but continue to loop forever, because Infinity divided by 2 is still Infinity!
Ever seen games that just hang in pcsx2? Sometimes its caused by situations like above.

Clamping can solve this problem like this:
Code:
int main() {
    float value = INF;
    do {
        value = clamp(value); // If value is NaN/INF, make value into a big normal number
        value = value / 2;
        if (value <= 1)  { print("Hello World"); break; }
    } while (true);
}

The clamping will convert the INF into a normal (ordered) number. Then that normal number can be divided by 2 repetitively until its less than 1. Then it will eventually satify (value less or equal 1), and print "Hello World".


Sometimes clamping breaks games (because of a lot of reasons that will take too long to explain), but the basic problem is:
The ps2's floats have a larger range of valid-values than your PC's floats. Therefore we have to make values 'as close as possible' on the PC to try and make games happy.

Because its impossible to know if clamping will break or help a game in some situations, we have different clamp modes in pcsx2's advanced options.
Also clamping is SLOW if done a lot, so different clamp modes are faster than others ('None' is the fastest of course Tongue2)


I also should mention that in earlier examples i said:
sqrt(-4) = NaN; // on your PC
sqrt(-4) = 2; // on the ps2

In this case, we simulate the ps2's sqrt instruction by doing this
Code:
float ps2_sqrt(float value) {
    value = clamp(value); // Clamp Value if NaN or Inf to an ordered/normal number
    value = abs(value); // Make Positive
    value = sqrt(value); // Get sqrt of now-positive value
    return value;
}

so:
ps2_sqrt(-4) = 2;


Now this post is getting really large, and I tried to simplify things a lot (which may have made things more confusing).
Anyways I hope some of this info was useful/interesting Tongue
Check out my blog: Trashcan of Code
Reply

Sponsored links

#2
Useful for me? Probably not. Interesting? Definitely. I love all these technical explainations that appear on the blog from time to time.
[Image: 2748844.png]
Reply
#3
It's always nice to see the process and the method behind the madness, even if I don't fully understand it right away. Learning new things is always a plus. Thanks for these dev blogs, guys. We really appreciate them.

Hopefully it also illustrates how difficult this project is to the people that come in every now and then saying: "wow dis prgrm sux u need 2 lern 2 code beter or sumthin"
www.twitch.tv/krazytrumpeter05
Want to stream your games? Let me know and I can help you get set up with Open Broadcaster Software.
Reply
#4
not even a week public but easily the most interesting section on this forum^^
Chicken is not Vegan?

NO VEGAN DIET NO VEGAN POWERS!!!!

http://www.flixxy.com/my-blackberry-is-not-working.htm
Reply
#5
(08-31-2009, 12:16 PM)kabooz Wrote: not even a week public but easily the most interesting section on this forum^^

Yeah, I certainly think so. We've needed a place for non pcsx2 team members to talk about development for a while.

And anyone interested in clamping may find Zerofrogs 2006 blog post , Nightmare on Floating Point Street interesting, as well.

I actually find most of Zerofrogs old blog posts interesting, actually.
Reply
#6
(08-31-2009, 12:16 PM)kabooz Wrote: not even a week public but easily the most interesting section on this forum^^

I agree, this is definitely interesting to read about. Keep these dev blogs coming Biggrin
ChenZen - A blog for games, technology, gadgets, anime, manga, music, or about anything else that interests the average geek.
Reply
#7
I don't know if you guys knew but for Final Fantasy X, putting the Clamp mode in the EE Recs options in Full, fixes the Bosses facing us backwards, that old funny bug. Any idea why ?
Reply
#8
Yes. That's why the devs thought to implement Full Clamping. It fixes FFX, and others, for the reasons described in the blog. See, we really do care.

... sorta. Tongue2
Jake Stine (Air) - Programmer - PCSX2 Dev Team
Reply
#9
Although full clamping is not always the best option. I just recently found Mega Man X command mission in the bargain bin at gamestop and as an RPG fan it was a good chance to try it out for the same price as a McDonalds extra value meal. Anyway it had an annoying graphic glitch that seemed to stay no matter what Gsdx settings I used. All the characters had black triangles all over them. After a search I found that setting clamping for both EE and VU to none fixed the problem. First time I had to change any advanced settings from the default.

I am definitely loving this new board. Next up I would love to know what logarithmic Z and Alpha correction do in the gsdx plugin. Also what about rounding mode and what exactly does it affect in the games? Also the flush and denormals are zero options as well.
Reply
#10
Dadaluma83:
When I have the time + energy, I may make a blog about some of those options you described.

Also I should note that the "Full" clamp mode isn't really clamping.
Nneeve rewrote the pcsx2 FPU instructions to convert to double, then do operations, then convert back to single floats.
This is similar to the 'software fpu' idea I was talking about in my article (the full-mode isn't 100% correct though either).

By converting to 64bit doubles, the PC is finally able to simulate the ps2's greater range of values. However this is slow, and I don't know how bit accurate Nneeve coded it.

If a similar thing was done on the VU's it'd be very slow and alot of work just to fix a few games that other clamp modes couldn't fix. Also, I personally wouldn't be able to stand the ugliness/bloat it will make the VU's code into.

I should also note that the "triace gamefix" that fixed triace games, was due to tiny miscalculations on the VU's.
Nneeve coded a bit-accurate ADD instruction that allowed triace games to work. (the triace games were using some encryption key or doing some decompression that needed bit-perfect results...)

This bit-accurate ADD function is super slow. The way the triace gamefix works is we only use the custum ADD function in the necessary spots to make triace games work.
We experimented with using the function in more spots, and it slowed emulation down 50%. A software FPU would basically be doing the same thing for ADD/SUB/MUL/DIV/SQRT/RSQRT and other instructions, which would make emulation crawl to under 1fps on highend systems.
Check out my blog: Trashcan of Code
Reply




Users browsing this thread: 1 Guest(s)