Tuesday, January 24, 2006

It's depth of field cap'n, but not as we know it

Recently graphics cards have got enough grunt to do nice special effects in real time, like depth of field - this simulates a camera having a focal point; stuff drawn at different depths from the focal point can be blurred out a bit to give us what looks like a camera being in-focus and out of focus.

So far, so good.

This effect is pretty expensive though and if you are doing a 2.5d scene or game (full of billboarded polygons), there is a neat little trick that is super quick and looks ace. This is for DirectX by the way, i'm sure you can do a similar thing in OpenGL but i'm not particularly familiar with the API.

If we have a texture of size 256 x 256 say, then we can get 9 mip map levels. At its simplest level, one can just set D3DSAMP_MIPMAPLODBIAS to be bigger than zero according to the distance of the billboard from the focal point, and voila!

Incidentally if you do do this then this particualar sampler state expects a float value so you have to do *((DWORD*)(&value_f)) to get passed properly, as the argument type is a DWORD.

So, we have our blur. The box filter that D3D uses is a bit bobbins though, so you can make this a bit better by setting D3DSAMP_MIPFILTER to be something other than D3DTEXF_LINEAR (maybe D3DTEXF_GAUSSIAN).

If the objects are going to stay a fixed distance from the camera then we can probably fiddle with these and get nice enough results. Even so, we can make it look better than this.

Here we get a bit shady and pretend like we don't care about how much memory we are going to use. Let's say that instead of having 1 texture we have a whole bunch, maybe 20, maybe 50. Then, offline, we do a gaussian blur on these textures, each one being a little blurrier than the last. The result is a nice list of textures which get blurrier as we got, and we can just set the texture for the object depending on its distance from the focal point.

Once you have generated the textures (which takes a while) this is nice and fast, and looks ace. Hurrah!

Wednesday, January 18, 2006

Ummm.... I forget

As i mentioned before, memory on a console is a precious commodity. it is up to the programmer to ensure that any memory they have allocated "dynamically" is cleaned up - the computer doesn't know when you are finished with it. Failure to do so results in a memory leak.

Today i updated Steve's PS2 memory leak tracking code to work on the PC build of the game, hopefully to make finding memory leaks a bit more painless. Essentially what Steve's code does is store the return address of the new call, a pointer to the allocated memory and a bunch of other data (amount allocated etc) in a list. Then, whenever the memory is deallocated it is removed from the list.

So, to find a memory leak we just dump the contents on the list - anything left on it is, at the point of the dump, a leak. Of course it's not 100% accurate as you may have allocated memory during the time of tracking the allocations which isn't supposed to have been deallocated. But usually it's pretty easy to tell which one is which.

So here's some technical bits. The key to the whole technique is knowing where the new call is made (new is the function used to get a chunk of memory, if you don't know). So we need to get the point at which function x calls new, and we can do this by looking at the stack. To cut a long story short you need two incredibly complex lines of assembler:
mov eax, [ebp+4]
mov ReturnAddr, eax
you need ebp+4 because at this point ebp points to the calling function's base pointer. This all works according to the __cdecl calling convention. This is a good reference for calling conventions and what they mean and what they do.

I intended this blog to be reasonably non-technical / jargony and to require little previous knowledge about the subject, but this was just quite interesting so i thought i'd share it.

Monday, January 16, 2006

Render Where?

That is the question.

Since we are developing a game for 3 separate consoles simultaneously it makes sense to try and write code that will work on all 3 in one go, if we can. In order to lighten the workload for us we use a "bought in" graphics engine called Renderware (hence the hilarious pun in the title). This has different versions for different consoles but at the top level looks very similar. Which is nice as one can write code which uses Renderware and it should, hopefully, fingers crossed, look pretty similar on consoles across the board.

Quite whether Renderware or Sony are to blame for the extreme state of stress i have experienced today is open to debate. I want to blame Sony for making the PS2 a nightmare to work with. However, i also want to blame Renderware for being thoroughly shady in some of its functionality. Perhaps i shall blame Sony and Renderware. Yes, i think that does well.

The problem arises when one attempts to examine the pixel data contained in a texture. For the uninitiated, in computer terms a texture is a collection of data (typically square, for our purposes) which when viewed on the screen is a picture. Within the texture is the colour data, organised, according to the texture format, into pixels. So, each block of memory which makes up a pixel can contain Red, Green, Blue and Alpha (transparency) information. Renderware gives us a "pointer" to the start of the texture, and reading can commence. However the playstation in particular doesn't store its data linearly, preferring to "swizzle" it in order to make it faster to access. This means essentially that certain values get swapped about, in a confusing manner. This is all well and good as Renderware is supposed to sort it out for the programmer, to make life easier and less error prone.

I am discovering that life is rarely less error prone.

The particular situation which i am dealing with (i won't go into massive depth, but it's a palettised texture if you are interested, and i want to write directly to the palette) is probably the point where the Renderware programmers got bored of making things sensible and decided to really shaft you.

Suffice to say that combined with the enormous compile times for the PS2, i am not the happiest of bunnies with Renderware at the moment.

Friday, January 13, 2006

Ich Heisse Super Fantastiche

So i just finished profiling my new font system and it's over twice as fast as the old one - hurrah!

When i first profiled it it was a bit slower than the old one and i was slightly confused - how could code that demonstrably did less actually be slower?

Well i found the answer hidden away in a clear() function for our vector class. A vector in this case is a list of items (an array) which dynamically grows as more items are added. A specification called the Standard Template Library (STL) exists which implements a std::vector class but this is not particularly console friendly so we avoid using it and have our own (CVector, funnily enough). STL specifies that when one clear()s an stl vector, the data contained should be scrapped but the allocated memory retained. Handily, however, the Microsoft implementation of std::vector (as i have been testing on a PC thus far) doesn't conform to this, and does de-allocate the memory. And oddly, our CVector class does too. Bizarre. I would have thought that for a piece of code which to all intents and purposes attempts to duplicate STL functionality (at least, from the outside) this would be a reasonably salient point to adhere to.

Now, i don't dare change it as the code base is quite large and it may result in a lot of extra memory being used up! However, at some juncture i shall probably test it.

Any road up, as they say, the problem was that i was calling clear() on the CVector. It should be noted that the old version of the font stuff was using a STL vector, and here we can use erase(begin(), end()) to get the required functionality. However, like i said, CVector is a better choice here.

Rather than rewrite the clear() functionality of CVector i decided to add another data structure, CStaticVector. It gives us vector like functionality (seemingly dynamic allocation) on a fixed size array. The other advantage of this is that it allows us to do bounds checking too, to ensure we don't attempt to read or write to memory outside of the array. C and C++, unlike Java, don't do this for us, which is one of the reasons that they are faster.

Tempting... But No

So today i got offered a job at the local Anonymous Baby Killing Fuel Conglomerate petrol station. I was flattered but had to sadly decline. Of course, in times like these one's rapier wit always fails, so i stammered the incredibly humorous "Sorry i've already got one".

The other lady (not the coffee lady who asked me if i was looking for employment) proceeded to say that you needed to be an octopus to do the job, clearly referring to the manic nature of the morning. However this only served to confuse me further as there seemed to be only two things to do - serve coffee and press the beeping button which exists in every petrol station - while needing an octopus would imply 8 different activities.

In conclusion, one needs only two, albeit very long, arms, to do the job. An orangutan perhaps.

Thursday, January 12, 2006

What The Font?!

Another day begins at Anonymous Surrey Based Game Development Company. I'm a tech programmer here, and at the moment I'm rewriting the font rendering system for the game I'm working on.

The existing system was not amazing in a number of ways; it was confusing, pretty slow and didn't look great. The latter factor especially is not a plus point for a text renderer. The one redeeming feature I could see was that it had an intriguing compression system.

The Playstation 2 is not as powerful or as good as most people think. The games that come out on it look amazing largely because of incredible amounts of hard work by the programmers to squeeze every last drop of performance out of it. The game we are working on is being developed simultaneously for Playstation, XBox and Gamecube and the Playstation is "the lowest common denominator" of the three - if it works well on that it is expected to work well on the other two platforms.

However, back to fonts. The main limitation of working on a console (when compared to a PC) is the lack of memory. The PC gives us virtually unlimited memory (called, coincidentally enough "Virtual Memory") as the operating system manages the memory for us and "pages" it on and off the hard disc. This is slow but it means that using more memory than is physically available is not a disaster. Programs like Word and Adobe Acrobat use clever vector methods of drawing text, but this is too slow for real-time games, and so we basically store what the individual letters look like in a texture, and then sample that to get words. So far, so obvious. However, textures take up a fair bit of memory, and when you have lots of letters that means a fair bit of memory.

So, how to make the impact as minimal as possible? The idea behind the previous system was to use as little memory as possible by ignoring any anti-aliasing information. Aliasing is an effect that causes continuous signals (such as what we see) to become indistinguishable when placed in a discrete space (such as a computer screen). In our discrete space we have only an approximation to the continuous space, and this is when we get aliasing; detail from the original continuous space is lost via the approximation. For instance, when you see a car on telly and it looks like the wheels are spinning backwards, this is aliasing. The tv captures motion at 24 frames a second or whatever, but this isn't enough to actually capture the true motion of the wheel spinning really fast, so we get aliasing - the signals become indistinguishable and detail is lost. Anti-aliasing is the name given to methods used to counteract this.

What this means for fonts is essentially smoothing:You can see that when we zoom into the letter its edges are smoothed out, so the line doesn't look so "jaggy" when viewed at normal size. If we ignore the smoothing the text looks very blocky. The advantage is that every pixel can be just "on" or "off" (1 bit per pixel) whereas the smoothed version needs values inbetween on or off, which logically takes more space to store on the computer. However! the old version was still storing at as a full colour value (32 bits per pixel) anyway for some reason, either pure white or pure black, so what i've done is store it as 4 bits per pixel so we have 16 possible smoothing values.

There are other caveats to both systems but i've written quite a long first post (during the excessively long compile times) so i'll finish there.

And oh yes, i am aware that "what the font" is a website, and yes, i am aware that it is great: www.whatthefont.com

Tuesday, January 10, 2006

Uhuh This My Ship

All the girls stomp your feet like this