Dirty Coding tricks used in Production Video Games
gamasutra.com
gamasutra.com
We were still 1.5 MB over the memory limit!
At this point one of the most experienced programmers in the team, one who had survived many years of development in the "good old days," decided to take matters into his own hands. He called me into his office, and we set out upon what I imagined would be another exhausting session of freeing up memory.
Instead, he brought up a source file and pointed to this line:
static char buffer[1024 * 1024 * 2];
"See this?" he said. And then deleted it with a single keystroke. Done!
He probably saw the horror in my eyes, so he explained to me that he had put aside those two megabytes of memory early in the development cycle. He knew from experience that it was always impossible to cut content down to memory budgets, and that many projects had come close to failing because of it. So now, as a regular practice, he always put aside a nice block of memory to free up when it's really needed.
The intention is not to hide these from other engineers, but to keep a bit of performance in the bag for when needed at the end of the project. When you absolutely must hit 60fps, or fit everything into 32mb of RAM, it can be invaluable.
One random link which discusses them- http://ubuntuforums.org/showthread.php?t=215177
I beleive it wouldn't be something that would be done, it would follow a similar pattern to RAM memory, ie. Run out, you get a hard OOM error, game over.
Of course, people also tend to get used to that pretty quickly and compensate accordingly, which doesn't sound like a problem here.
My business partner moves the time on his bedside clock around by 15 or 20 minutes semi-randomly every couple weeks to keep himself off balance - that didn't work for me at all but it's been doing fine for him for years now.
Back on Wing Commander 1 we were getting an exception from our EMM386 memory manager when we exited the game. We'd clear the screen and a single line would print out, something like "EMM386 Memory manager error. Blah blah blah." We had to ship ASAP. So I hex edited the error in the memory manager itself to read "Thank you for playing Wing Commander."
QA discovered that if you rapidly toggled back and forth between two menus in the front end you might get a hang, though it could take 10 minutes.
I was eventually able to reproduce it in the debugger, and it was bizarre. A local variable getting corrupted to strange values, but there was no memory corruption visible at all, just the register was incorrect, when it was only ever assigned to once.
No matter what test cases I tried to cut the code down into, it didn't crash. Inspecting the disassembly everything seemed fine.
Finally, I noticed something strange. The suspect variable was in r19, which thanks to the allocation strategy was very unusual, it was most often unused. That gave me enough of a clue to create a cut-down test case that included all the function arguments and local variables without the code, to make sure I got the same register allocation.
I was able to write a single main.c that set the variable once and sat in an infinite loop waiting until it changed. Bingo! _Something_ crept in and changed that value, and it sure wasn't our code. There wasn't much of an OS on the Gamecube, but there must have been some interrupt that wasn't cleaning up its changes to r19 like it should.
I sent off the minimal code to a Nintendo developer support engineer, and monkeyed with our code to avoid having so many local variables in that one function, which fixed our problem. The Nintendo engineer fessed up that it must be their issue, but then the report vanished into the Nintendo bunker, never to be seen again...
Despite being a nightmare I'm glad I escaped, there were some sheer moments of coding joy in the game industry.
It does make it interesting and challenging, though.
I used to work at a major video game studio where one guy in the building was moving from production to production as a "reverse concept-artist"
I explain: Some modelers that design characters for video game sometime skip the concept-art phase (paper pencil drawings) and move directly to 3D CAD. But those rough sketches are really good for gaming magazines or online articles to show the progression of the artistic direction.
So that guy's job was to render the final 3D model of the character in wireframe mode and then re-touching it in photoshop to make it look like an original paper drawing. The result was quite convincing and appeared in several magazines as the initial artistic sketches for the game characters.
-----
(Author: Hélder Gomes Filho)
And I was thinking that only newbies like me shipped those horrendous code... :P
At university there was a team (not related to me, but these guys are the perfect example :P) that made a FPS flash game...
For some bizarre reason, the programmer instead of checking if you was colliding with the wall and not allow you go there, he made the inverse, he checked if there was a wall, and allowed you to move parallel to it...
This sparked a bizarre bug: In crossings, you could not actually cross, only turn to the passage on your left or right.
The deadline was closing, and they had no idea on how to fix it...
Then the team writer fixed the issue! He told the artist to draw a animation of hands touching the walls, and then he wrote in the story that the protagonist was blind and needed to touch the walls to know where he was going.
And the pictures were more than just about framerate. It was a reminder that we were all a team and that we were all working very hard. It humanized the problem and really made everyone aware that we had to help each other out to ship the game.
However -- In this case, it's likely the number of integers required (controller ids, which I guess might be 1 to 4?) would fit in almost any ptr you can think of.
Conversions aren't guaranteed, but if you copy/pack it yourself there shouldn't be a problem -- e.g. do a memcpy of one byte.
More likely the author was pointing at the fact that this value was later 'free'd' by the event loop. Which is fairly nasty.
In this case, issues of pointer size are irrelevant since I think the target was a specific console, but of course that's still "dirty".
You could set the pointer's value to be the controler's ID and then set it to null while you handle the event, and before you pass it down the chain. Or, the OS might be ok (and do nothing) when trying to free memory that's in the middle of a block of allocated memory.
If you had an allocator like this, you could use the addresses 0 through 3, and presumably the deallocator would mask these all down to zero, and then do a check to make sure it doesn't free 0, and end up doing nothing.
Good article.