Home

Advertisement

Previous Entry | Next Entry

I love Perforce!

  • Oct. 18th, 2008 at 11:55 AM
burgerbecky, heineman, smile
I use a Perforce server for maintaining my source code and it was a lifesaver. Over the last few weeks, I've been making extensive upgrades to Burgerlib, inserting hand tuned assembly here and there and general clean up. I had several unit tests to verify my work and it's been working out great.

Until last Wednesday...

I ran numerous tests on the game Aliens vs. Predator to make sure that nothing was broken and it was running flawlessly. So I switched over to the Mac PowerPC version and ran it and to my horror, I found the 3D rendering engine was drawing all the textures black. A quick look at the source history showed me that I had made hundreds of changes to the code and finding out why the gamma tables were all screwed up was going to be a nightmare.

Then Perforce came to the rescue. I did a "revert" and went back in time, grabbing a source tree revision, rebuilding and testing until I found that it was a change made about 6 weeks ago that broke AvP MacOS PowerPC. With the possible suspects narrowed down, I quickly traced it to an inline assembly gotcha that happens with the Metrowerks CodeWarrior compiler for PowerPC. Shortly, I found that my Wii version also suffered from the compiler "glitch".

Here's the errorneous code...

static Int32 BURGER_INLINE FromFloatFloor(register float fInput)
{
    Int32 iResult;
    fInput = fInput-0.5f;
    register Int32 *pResult = &iResult;
    asm { fctiw fInput,fInput; stfiwx fInput,r0,pResult }
    return iResult;
}


In a controlled unit test, this code generated the assembly I was expecting, but in a gamma table creation function in Aliens Vs. Predator, the "return iResult" operation was optimized out and as such, it was filling the table with whatever was in register r3 at the time (In this case, it was always zero). Gamma = 0 means textures are all black.

The fix was to force the compiler to never get rid of iResult and here's where "volatile" has its uses.

static Int32 BURGER_INLINE FromFloatFloor(register float fInput)
{
    volatile Int32 iResult;
    fInput = fInput-0.5f;
    register volatile Int32 *pResult = &iResult;
    asm { fctiw fInput,fInput; stfiwx fInput,r0,pResult }
    return iResult;
}


Adding the keyword fixed the code and I double check the Wii compiler and it solved the issue there too.

Lesson learned... No matter how many safeguards you put in your development process, something always gets through. Being able to "go back in time" through source reversion saved me hours, even days, of crazy coding detective work.

Tags:

Comments

(Anonymous) wrote:
Oct. 22nd, 2008 09:49 pm (UTC)
Heh
That's what I dislike about inline assembly: you think you know how the compiler is going to handle it, and then you realize the way you've written it, the compiler thinks there is no dependency when there is in fact one, and optimises away a bit too much from what you intended. That's why (except for such special, small cases) I tend to prefer assembly functions, at least in this way the interface between C and assembly is well-defined by the ABI.
(Anonymous) wrote:
Apr. 8th, 2009 02:57 am (UTC)
AvP!? No way!
Holy shit, I remember testing AvP for Mac PPC. One of my favorite games to test from back then. I always thought that it was amazing that one person did the port. Helped inspire me to start investigate game programming.