Showing posts with label game dev. Show all posts
Showing posts with label game dev. Show all posts

2008/05/21

Fragmentation And You!

In the continuing series of posts about Stuff They Never Tell You About Game Development, I'm going to rant a bit about fragmentation, why it's evil, and how you can stop its nefariousness.

Fragmentation is basically where you've got a something that's X big but all the wee tiny bits where you can place your something are too small even though your something would totally fit if the space weren't partitioned so badly. Consider me trying to park in Fell's Point (this is near the water in Baltimore for those wondering):

This happens ALL the time.

If you try to park on the street in Fell's Point, you will rapidly learn that there are no lines on the street. This means that people with giant SUVs getting less than 10 mpg highway have NO IDEA where to park their environment destroyingly inefficient vehicles. I drive a tiny Saturn which is mostly made of plastic. It does not ever fit in the spaces left. If only (heaven forbid) I could rearrange these parking challenged bozos' vehicles, I would have plenty of room!

Like you've never had this thought before.

This, friends, is fragmentation at its ugliest. It also happens in your computer's memory (among other places).

Backing Stores and Virtual Memory and Consoles, Oh My!
Memory fragmentation is basically like the two images above: you've got X amount free but it's all broken up into tiny bits that you can't really use. It's a Bad Thing (TM). Occasionally you need to allocate something big and contiguous like an image or something and those tiny bits just aren't going to cut it even though it would totally fit if you could add up all the tiny bits. This requires another chunk of memory to be grabbed from the free store to satisfy your request.

For a normal modern OS, this usually isn't a big deal because we have a) a backing store like a hard drive, and b) a good virtual memory system that can page things in and out if you get near the end of physical RAM a la your undergraduate OS course. The worst that usually happens is the program is a little more chuggy (or a lot more chuggy, depending on your system) and you get fragged more often because your framerate dips and you just can't dodge that guy's rail anymore (bastard).

On a console or embedded device (like the iPhone) it's a whole lot more catastrophic. We may or may not have a backing store. We may or may not even have a basic virtual memory system. It probably doesn't use the backing store that we may or may not have to page RAM even if it has the hardware to do so. So we may or may not be screwed when trying to allocate a giant piece of memory for the screen grab of your most recent death because railboy is totally hacking. By "screwed" here I mean "crash" because that's usually what such devices do. And, sadly, in this situation, we're usually screwed.

How It Be Happenin'
Consider the following horribly-contrived-yet-so-close-to-actual-production-code-I've-seen-recently-that-it-makes-me-shiver-just-typing-it example:


vector StuffToLoad;
StuffToLoad.push_back( "some_prefs.xml" );
StuffToLoad.push_back( "a_texture.dds" );
StuffToLoad.push_back( "this_is_temp.dds" );
StuffToLoad.push_back( "big_honkin_asset.stuff" );
StuffToLoad.push_back( "another_asset.stuff" );
for ( uint ii=0; ii<stufftoload.size(); i++ )
{
LoadAsset( StuffToLoad[ii] );
}



The basic idea is that it's trying to load a bunch of stuff. During the loading of those assets, it really needs the "this_is_temp.dds" texture prior to loading "big_honking_asset.stuff" but then gets rid of both of it and the prefs XML file. Those of you who have dealt with this issue are probably headpalming right now (just play along). For this exercise, we'll assume that LoadAsset allocates exactly once per asset. and UnloadAsset properly deallocates that one allocation. So how swiss-cheeseified does this snippet make your memory?

Seems simple enough?

That's the big blocks, certainly. What about the rest of it? The bad news is that depending on your particular compiler and the version of the Standard C++ Library you're using (otherwise known as the STL), it might be much, much worse. At the very least, the vector is going to allocate at least once but more likely two or three times. Each string pushed into the vector is probably going to allocate once for the string. If LoadAsset's prototype looks like this:


BOOL LoadAsset( string AssetToLoad );


it probably allocates once per function call for the pass by value (hooray for copy constructors!) as well. So by the time you get to allocating the one chunk for your optimzed LoadAsset, you've probably blown two tiny temporary allocations on strings! Load and unload a bunch of assets and you're nickling and diming your memory to screwedness!

Closer!

For people not used to thinking about this kind of optimization, this often comes as a complete shock. This kind of thing usually requires someone (typically me), to go through piles of code to root these things out of them because memory is prodigiously perforated and the game crashes if you play it a lot. This is both tedious and error prone and a better solution is to not write it like this in the first place.

This is one way fragmentation happens and it's really, really painful to deal with after the fact. Such issues tend to not manifest themselves until the very end of the game which tends to only get played near the end of the project which is where disasters tend to collect leading to some very, very long work weeks. Don't ask how I know this. To make matters worse, it only takes one dood to sprinkle such gems throughout a significant portion of your codebase.

If You Were Looking For an Easy Fix, You Will Be Disappointed
WIthout going into a lot of gory details about How To Write A Memory Manager in C++, I'm going to have to gloss over some details (besides, that's for a different post). The basic gist of how to deal with fragmentation is to have a decent idea of how your program's memory is going to be used and be uber-careful with anything that might allocate.

For the example above, we know that both string and vector are going to allocate at least some memory. We can provide an allocator to it or remove them completely since, at least in this example, we don't actually need most of what they do and our list is hardcoded anyway. We can further make this better by having a temporary heap that we know is going to be churning a bunch and pass that into the LoadAsset function so our two temporary assets don't fragment the main heap either. At that point, we can optimize the heap function for the temp heap to prevent fragmentation since we're going to hammer it (and believe me, once you have such a heap, you will hammer it). Here's a somewhat less fragmenty version:


struct AssetLoadInfo
{
const char *mAssetName;
uint mHeapIndex;
};
AssetLoadInfo *c_StuffToLoad[] =
{
{ "some_prefs.xml", c_tempHeap },
{ "a_texture.dds", c_defaultHeap },
{ "this_is_temp.dds", c_tempHeap },
{ "big_honkin_asset.stuff", c_defaultHeap },
{ "another_asset.stuff", c_defaultHeap },
{ NULL, 0 }
};

BOOL LoadAsset( const AssetLoadInfo &rfAssetInfo );
BOOL UnloadAsset( const char *AssetName );
for ( uint ii=0; NULL != c_StuffToLoad[ii].mAssetName; ii++ )
{
LoadAsset( c_StuffToLoad[ii] );
}
UnloadAsset( "some_prefs.xml" );
UnloadAsset( "this_is_temp.jpg" );


We aren't talking rocket science here, but it does require programmers to carefully consider what they're doing. C++ makes it stupifyingly easy to make a mess of things and you just don't want to get yourself backed into a corner with fragmentation when your game is being released on a console. People don't like it when their games crash (believe me on this one).

There's WAY more to it than just small optimizations like the one above. Having a proper memory strategy, making sure that you have a proper memory budget, and making your own memory allocation devices are all super important in memory limited situations and can save you an awful lot of painful debugging. See you next time for the next installment of "Stuff They Never Tell You About Game Development"!

2008/05/09

MIND THE GAP!

Ever been on one of those projects where someone (usually the leadership) thinks it's going way better than it actually is? If you've spent a reasonable amount of time in software, you probably have. Here, then, is my sole addition to the software engineering lexicon. Feel free to use this whenever appropriate.

    The distance between the actual doneness of a project and the perception of doneness is hereby defined as the Reality Gap.


So, assuming you had some magical way to determine precisely how far along on a project (or task, or whatever) you are, the distance between there and where you think you are is the Reality Gap. If the Reality Gap is large, then, well, your team may be tremendously optimistic or, sadly, in denial. Welcome to deathmarch.

I mention this for no reason whatsoever. None.

2007/11/10

What You Should Know About Load Times

(cleverly abbreviated as WYSKALT)

If I have time to get pissed off and say "I can't help but notice that I'm not playing your game RIGHT NOW," then it means your load times suck and I'm probably wondering why I bought your game in the first place.

This seems bad!

Loading your game off media isn't rocket science. I'm going to assume that we're loading off some kind of disc with a 'c' media like in a console game rather than your hard drive like on your PC of choice. However, anything applied to optimize load times on disc-with-a-c will also optimize disk-as-in-hard-drive as well. I'm not going to cover everything with load times in this post, but this is the absolute bare minimum of stuff you need to know but I couldn't fit it in quite as catchy an acronym. There's lots more stuff that will make load times even better which I might rant about at a later date.

So at any rate, here we go.

Opening Files Sucks
Most times we don't notice this. Occasionally you run into something where you need to load N files which are kinda small but where N is something gigantic (like 1000). Then you notice--even if you have an uber fast machine. On this generation of console, N is usually on the order of thousands for a given level load for those who might be curious.

Fixing this is uber-easy: pack all your files into one giant uber-file so you only ever need to have one or at most a small number of file handles open. We often know of these as zips, wads, paks, pigs, etc. (The one I use for BEER is "keg". Clever, no?)

This makes load times suck less which is the goal. Sadly, they still suck.

Loading Non-Sequential Data Sucks
So now you've got your data all archived and it's time to load it. Surprise! It still takes a long time because you're constantly seeking around in your uber file. I can hear you smug Win32 people scoffing--but remember that your file mapping eats virtual address space and I know that since you're still using WinXP that you only get two gigs of VM. Ha!

This is pretty easy conceptually: stick all the data in your archive in order. The bad news is that depending on your particular data set and load time trickery, this might be really hard in practice. Chances are good that it leans toward the really hard side because you might only load some subset of your data and probably don't have a fixed order for it. If you can make your loading respect a fixed order then the problem moves pretty quickly to the easy side again.

I've found that in most cases the best we can hope for is that some subset can be made to lean toward "most" for fixed sequentiality. Sometimes you can change the loading code to do nicer things; sometimes you can't for <fill in the blank> reason that fits your situation. In either case you need to generate a sequence list for some set of your data then stick all the data in your archive in that order as best you can. Neither of these are particularly difficult assuming you have source code and enough time to do stuff and an archive builder that doesn't totally blow chunks.

Why does this work? Once file open issues are dealt with, seek time tends to be the dominant factor in unruly data sets. On discs-with-a-c, each seek is going to weigh in at roughly between 50 and 150 milliseconds depending on your particular hardware. So, if you're loading 10MB of data but seeking 100 times, your load bandwidth for that data can be no better than 1MB/s in the best case (assuming a flat 100ms/seek which is normal). If you try to load a gig of data with that bandwidth, then, well, I'll not be alone in returning your game.

So: if you can significantly reduce the number of seeks your game needs to do, you can significantly reduce the load time of your game. I've seen particularly poorly loading data sets extend the load time of a 100MB data set more than five minutes. For those in the audience reaching for calculators, that's a bandwidth of less than 1MB/3seconds. Glacial.

But Our Game Is On the Hard Drive!
Don't care. These will still make your loads better. Your seeks are still on the order of 8ms and your bandwidth isn't free. Wait till your users start fragmenting the crap out out their drives. Galactic Civilizations is a reasonably fun game but its load times are absolutely brutal. Why? Tons of tiny files none of which seem to be in easy-to-load formats just sittin' there on the disk. Lots of games are like this for the PC and it's a damned shame.

Oh! You did that for your modders, did you? Guess what; you can still put most of your stuff in order in archive. You can even give modders your packing tools so their stuff loads fast too! Sure, you have to design for it but you're already designing extra stuff in for modders, right?

Odds, Ends, and Disclaimers
As mentioned atop, this is the bare minimum you need to know about load times. There are lots of other things you can optimize to make your loads suck less but I'll save that for another rant.

This has been a public service announcement paid for in part by the "Your Game Loads Too Slowly" foundation of Maryland and made possible by viewers like you!

2007/10/04

Random or Not?

Here's a not-quite-hypothetical problem: you have a fixed size graph and want to arrange N points on that graph in a random manner. You could use such a thing for lots of different stuff not limited to distributing player positions on a map, etc.

So how do you do it? Feel free to code along at home.

Totally random.
First try: completely random. We'll just use our good friend rand() with some "proper" seed to choose X and Y values and mod them into the map space. Smashing. If you do that, it looks something like those on the left.

I saw the results and wondered where my bug was cause that sure as crap doesn't look random. Hint: there was no bug.

Small influence circles.
Hokay. Welp, I don't want those points to be within N distance of one another where N is something I can calculate easily because, er, I want it to run fast (not because I'm lazy--no not that at all).

Closer...

If you were looking for a point, you may have just found it: when I say random in this particular context, really mean evenly distributed rather than really random. In fact, truly random is not what I want at all.

Final results.
On the left here are my final results after a mess of hacky heuristics. It's not the prettiest code but it seems to give good results on every map I've generated and it has a calculable upper bound on computation time. As a final step not shown here, I jitter each position inside the grid line so even points that are on the same row or column on the grid appear a little less regular (left as an exercise for the reader).

Now, I freely admit that I'm one to run off and code something because a) I figure I might learn something, b) I need the practice, and c) I reall like coding. Try as I might I was unable to find a better solution than the hackery I came up with. Apparently my google-fu is not as righteous as I thought. I'm pretty convinced that there's some crazy technique to do this in like 3.47 instructions on a 68020 or whatever but I sure as crap can't find it. If anyone knows of one, let me know.

(If anyone wants the code, just ask.)

2007/09/11

Free Software

I'm always on the lookout for stuff that will make my computing experience better. And Lo! I have found not one, but two such things in times recent.

The first, is paint.net which is a wonderfully full featured image editor. It reminds me a lot of an ancient program I used to use called PhotoFinish crossed with an equally ancient version of Photoshop (3.0). It does layers and has an unlimited command history. Want the source code? BAM. How bout a blog with details of the development and business side? Got that too.

If only I'd known about this before I'd shelled big cashmoney for CSR2. MS Paint quakes in its boots in mortal terror.

The other one is ToDoList which is exactly what you might expect from a program of that name. As it turns out, this kind of thing is pretty rare in the software wild for whatever reason. This one has all manner of sorting and searching and you can set recurring tasks and priorities and all 'a that. It's swank. The only downside is that you'll need codeproject.com logins. Goodness lives here.

I'm using this to track features, bugs, and development of my home projects as well as tasks at work. And for those of you playing buzzword bingo: it stores its files as XML.

2007/02/06

A Tale of Pastries And Danger

Unless something terrible happens this month, I'll be building a game (yeah, I decided). Here is a snippet from the design doc:

3.1: The Story
You play the part of Thomas Eugene Howard Chuck, otherwise known as T.E.H. Chuck.

T.E.H. Chuck is Head Cakey Taster at the Intergalactic Cakey Tastes Corporation and has just been asked to oversee the Chocolate Covered Sponge Cake (With Tasty Cream Filling) plant on Alpha Nebulon 3. Alpha Nebulon 3 has a large native population that sustains itself on the very tasty chocolate veins that run through the planet; hence the Chocolate Covered Sponge Cake (With Tasty Cream Filling) plant located there.

The good news is that since chocolate is available in a mining state in great quantity, that it greatly reduces ICTC’s production costs and allows for a large degree of automation. The bad news is that the indigenous inhabitants of Alpha Nebulon 3 are both extremely hostile and not very happy about the corporation stealing their foodstuffs . Hence, any ICTC personnel found in the field tend to meet a terrible fate.

The plant on Alpha Nebulon 3 is in a remote area near a particularly rich chocolate deposit but the rest of the planet is undeveloped. Luckily, the surveyors left limited range transporters around in random places that can be used to move back and forth between the survey areas. Not so luckily, ICTC, in a further attempt to reduce costs, had the sentry guard systems in areas around the transporters installed by interns who weren’t very careful. So instead of installing a sentry guard system to protect the loyal employees of the ICTC, they instead ended up installing a veritable obstacle course of deadly disposition that isn’t quite smart enough to pick out human targets to not neutralize.

On the way to the plant, T.E.H. Chuck crashed his large shiny black sport utility spacecraft on Alpha Nebulon 3 while backing up trying to park. Luckily, the ship crashed a short distance away from the plant. Not so luckily, it was a short distance for a spacecraft which is quite a long distance for a Head Cakey Taster through a dangerous environment filled with hostile creatures who dislike the whole “stealing their food” bit.

2007/01/07

Thousander Club

The Thousander Club is a challenge issued last year by some of the folks at the Low Poly Coop based on an article on GBGames. Original article here with 2007 followup here. The basics of the article was that you need roughly 10,000 hours over the course of 10 years to achieve basic mastery of a subject. Numbers aside, this doesn't seem unreasonable so long as those putting in the time are actually trying to learn, get better, don't get their brains smooshed by aliens or whatever. So the Thousander Club is a group of people trying to put 1K hours into their choice of things to practice while posting their progress in a public place.

I ran into this last year and was fascinated by it, but the ups and downs of stuff and the lateness in the year (Octoberish) meant that I wouldn't put up big numbers. I did manage to log 187 hours total from September 14th to December 31st, notably the first day of BEER development and the last day of GWA. So this year I've thrown my hat in the ring with these guys to see what I can do. I expect that this will be a hard year working two jobs and working through a year of crunch so, sadly, I already forecast a pretty major failure to hit 1000. Nonetheless, it'll be an adventure and I'm looking forward to it.

2007/01/02

GWA2006 Release

I ended up not finishing the postmortem until very late and wanted to read it over when not falling asleep. Here are two links that you can use to download the game and/or the docs courtesy of filefront:

Download gwa_v100.zip from FileFront!
Download gwa_docs.zip from FileFront!

The first is just version 1.00 of the game (about 1MB). The second contains the original design doc, the annotated design doc, and the postmortem in MS Word .doc files.

Excerpts from the postmortem, "Lessons Learned":
- Reliable and expedient tools are a MUST.
- Keeping the deadline in mind when evaluating new systems is crucial.
- Being able to throw features out or rework them is key.
- Reinventing the wheel is a lost cause.

January will be off of doing a game; I need some time to finish up some housework and to fix some of the issues of my engine. I expect to try again in February.

2007/01/01

Happy New Year

Welp, another year has come and gone. Here's hoping that y'all had an awesome holiday season this time 'round.

I know it's a tradition to list your New Year's Resolutions somewhere (like, say, your blog) but I'm not going to. Oh no. Given my past track record for sticking to them (exactly 0.000%), I'm not going to jinx it. Instead, I'll be keeping them private, thanks much, in order that I might actually accomplish some of it. Though, to be painfully honest, they're the same things I've been working toward for a while so it's not like you're missing any huge bold statements or anything.

For the few people that are wondering how last month's project went, I present the following screen shots:
I hate writing UIs.

OMGWTFKABLOOIE!
If all goes well, I'll be following this post with a download link and more information. Contained within "more information" would be a postmortem of the project selections of which will be posted here. Also therein will be two versions of the design doc, one as designed, and one old version annotated with my notes.

2006/12/10

A New Tack

Tacking is a maneuver that sailing vessels do to put the wind on the other side of the boat. This is often used to move against the direction of the wind.

My new tack then, is to build a bunch of simple games and then release them for free in an attempt to improve my skills. This obviously isn't ideal since I'd still lose out on learning about all the marketing things and project->product transformations. This does, however, seem to be OK in the light of the corporate copyright law under which I toil.

This guy tries to do a game a month. Gamasutra has some info on how to prototype in under a week (with additional bonus track commentary at Lost Garden). These guys make a game in a day. Kloonigames in that top link has a couple great posts with links to other rapid prototyping resources.

I'm going to try to prototype something each month and post it somewhere for free. I hope that this will get me some of what I'm looking for. It might not be pretty and almost certainly won't be polished but it'll be something which is better than slacking off.

So for my first game, I'm going to rebuild this:GWAGL
This is GWAGL, the example game I built when exploring the info in
Scott Bilas's GDC Talk in 2002 about data driven game objects. It's even a rebuild of the arcade game I did in college for the one games class I took (I can't post a screenshot of that because it doesn't run anymore). I'm going to pretty it up some, make it a single player game, and bang it out before the end of this year barring any unforseen badness. I might even post updates on how it's going here (but don't hold your breath).

So here we go.