Posts
67
Comments
120
Trackbacks
0
July 2010 Entries
WP7, XNA, the GC, and You (and Silverlight too!)

Some of you may have come across this helpful blog post recently, discussing when the garbage collector (a/k/a the GC) runs on Windows Phone 7 devices. Anyone who has programmed in XNA for the Xbox 360 before knows the GC well. For those of you new to XNA, there are several tricks and tips for slaying the evil GC monster that eats frames. This applies to Silverlight apps too, though since Silverlight does quite a lot for you automatically, there’s only so much control you have.

First, you must leave this site. Not permanently, but just temporarily to go read this post by Shawn Hargreaves: http://blogs.msdn.com/b/shawnhar/archive/2007/07/02/twin-paths-to-garbage-collector-nirvana.aspx.

One of the first things you must understand is the difference between value types and reference types. Value types such as int, float, Vector3, Matrix, and struct (this includes nullable types, btw – a nullable type like bool? is just a special struct) live on the stack. The GC does not care about the stack. Well, technically it cares slightly, but only to the extent that the system begins to run low on memory, and you would have to be trying awfully hard to get enough items on the stack to cause the system to run low on memory. So don’t worry about calling “new Vector3()” or “Matrix.CreateTranslation()” in your methods that run regularly (like Update and Draw) – it’s just a stack allocation and it won’t anger the GC.

Classes are an entirely different matter. Classes, arrays (including arrays of value types, e.g. int[ ]), collections (List<>, Dictionary<,>, etc.), and strings (yes, strings) are all reference types and they live on the heap. The heap is the GC’s turf. It pays attention to everything that shows up on the heap and to everything that no longer has any business but is still hanging around. The latter are things that have “gone out of scope”. As an example, in this bit of code:

void CheckForTrue(bool value)
{
    string trueText = "The value is true.";
    string falseText = "The value is false.";
 
    if (value == true)
    {
        Console.WriteLine(trueText);
    }
    else
    {
        Console.WriteLine(falseText);
    }
 
    return;
}

every time this method runs, trueText and falseText will both be allocated on the heap and will both “go out of scope” when the method is finished running. In other words, gone out of scope just means that there are no more references to an object. A string declared with “const” never goes out of scope and thus doesn’t matter to the GC for all practical purposes. This is also true of any object declared as “static readonly” since once it is created it exists forever. The same is not true for a normal “static”, though many might mistakenly assume so. A “static” object without the “readonly” keyword applied to it will generally exist for the life of a program. However, if it is ever set to null, then unless there is some other reference to it, it goes out of scope and is subject to garbage collection.

As you’ve read in the first article, on WP7 (and on the Xbox 360), the GC runs for every 1 MB of heap allocation. Whenever the GC does run, it takes time to comb through the heap and destroy any objects that are no longer in scope. Depending on how many references you have and how complex your nesting of objects within objects within objects is, this can take a bit of time. In XNA, the clock is on a fixed time-step by default and on WP7, the default frame rate is 30FPS. This means that there are 33.3333333 milliseconds available for Update and Draw to finish their CPU-side tasks. (Draw prepares things on the CPU-side then hands over the actual drawing to the GPU which, being a separate processor, doesn’t usually affect the Update/Draw side of things – except for stalls, but those are beyond the scope of this post and most people will never run into them anyway). If they finish ahead of time, the CPU hangs out and waits until it’s time to run Update again. If not, then the system takes notice that it’s running behind and will skip as many draws as necessary to catch back up.

This is where the GC comes in. Normally your code will complete just fine within the 33.33 milliseconds, thereby maintaining a nice even 30FPS (if your code doesn’t normally complete within that time you’ll see serious constant performance problems that may even cause your game to crash after a little while if XNA gets so far behind that it throws up its hands and surrenders). However when the GC runs, it eats into that time. If you’ve kept the heap nice and simple (the second path in Shawn Hargreaves’ post up above), the GC will run nice and fast and this likely won’t matter. But keeping a simple heap that the GC can run through quickly is a difficult programming task that requires a lot of planning and/or rewriting and even then isn’t fool proof (sometimes you just have a lot of stuff on the heap in a complex game with many assets). Much simpler, assuming you can do it, is to limit or even eliminate all allocations during gameplay. Note that I said during gameplay. You’ll obviously be allocating heap memory when you first startup the game (e.g. when loading assets in the LoadContent method), and you’ll be allocating memory when loading levels if you have a game with levels and decide to load each one in an interstitial screen. You’ll also be allocating memory when changing GameScreens if you use the Game State Management sample (phone version) as your starting point (and I recommend at least considering it – it’s a very nice starting point for a game, though you’ll want to customize it of course). But a small stutter from a couple of dropped frames in between levels or while switching screens isn’t a big concern – the player isn’t going to accidentally fall off a cliff or get hit by an enemy projectile or anything when those things are happening. In fact, sometimes it makes a lot of sense to intentionally trigger the GC right before the game is going to (re)start. Triggering the GC resets the 1MB counter and can prevent situations where the counter is at .94MB when the level begins such that even a small number of minimal allocations that would otherwise be perfectly acceptable can cause problems.

So the goal is to minimize heap allocations. How do we do that? Well, the biggest contributors are needlessly creating new objects in your Update/Draw cycle and boxing value types. First a quick note on boxing/unboxing. The simplest example of boxing is casting a value type like int or enum to “object” in order to pass it as a state. Rather than try to explain it further, I’m instead going refer you to the C# Programming Guide's entry on Boxing and Unboxing. Boxing is a great feature of .NET, but is very bad for game programming because of the heap allocations that can trigger the GC. So keep an eye out for it and try not to do it.

The other big contributor is creating new reference types. Every new instance of an object causes a heap allocation and increases that counter ever so slightly. There are several coding practices that will help you to eliminate needless heap allocation and create better performance for your game.

  1. Make any strings that never change into const strings.
  2. Where you do need strings that change, consider using System.Text.StringBuilder instead. All XNA methods that take a string (e.g. SpriteBatch.DrawString) will also take a StringBuilder object. Make sure to use one of the constructors that takes a default capacity and set it high enough that it will hold as many characters as you plan to ever have in it plus a few extra for good measure. If the internal array is large enough, it will never have to resize it self and thus will never generate any heap allocations after it is created!
  3. If you need to draw an int value such as a score or the number of lives a player has left, consider using this great extension method from Stephen Styrchak, one of the XNA team members at Microsoft.
  4. If you have a class for, e.g., projectiles, create an object pool to reuse them rather than letting them fall out of scope and creating new ones each time one ceases to exist in the game and each time you need a new one. As an example create a generic List<> of your projectile class. Use the List<> constructor overload that takes a default capacity and make sure to set it high enough to contain all the objects of that sort that will ever exist at one time in your game (e.g. 300). Then use a for loop to go through and create all of the objects in the list up to the capacity. Add a “public bool IsAlive { get; set; }” property to your class to keep track of which ones are being used at any particular time. When you need a new one, loop through the list until you find one where IsAlive is false. Take that one, set IsAlive to true, set the other properties (such as its position, direction, etc.) to their appropriate values, and continue. When doing collision detection, loop through using a for or a foreach loop and only process the objects for which IsAlive is true. Same goes for updating them and for drawing them. Whenever one is no longer needed (e.g. it collides with something or it goes off screen), simply set its IsAlive to false and it’s now available for reuse without any memory allocation. If you want to be creative, you can expand on this further in several different ways. You could keep a count of the number of live objects so that once you’ve processed that number in your update and draw methods, you can use the “break” keyword to get out of the loop early rather than go all the way to the end. Or you could keep two lists, one of alive objects and one of dead objects and move objects between the two lists as appropriate. Or your could use what my friend Brian calls a recycle array to automatically keep a “sorted” array in which live objects are all kept at the front.
  5. When using enums as keys in a dictionary, make sure to implement your own IEqualityComparer<T> for the enum using that great example from Nick Gravelyn, another XNA team member. Without creating and using your own IEqualityComparer<T>, .NET will create a generic one for you and the generic one it creates will box the enum, creating heap trash that will eventually bring the GC to life. With it, though, no trash!
  6. If you do want to create something you can just create a new “instance” of each Update or Draw, try creating a struct instead of a class. Structs can do most of the same things that classes can (the major limitation being that they cannot inherit from another struct or a class or anything else (but they can implement interfaces)) and struct live on the stack, not the heap and so unless you have a reference type like a string or a class as a field or property of the struct, you will not generate any trash using a struct. Remember, though, that an array of structs is a reference type (as are all arrays) and thus lives on the heap and counts towards the GC trigger limit whenever created.
  7. Do not use LINQ. It looks cool. It makes your code shorter, simpler, and perhaps even easier to read. But LINQ queries can easily become a big source of trash. They’re fine in your startup code since you’re going to generate trash there anyway just by loading assets and preparing game resources. But don’t use it in Update, Draw, or any other method that gets called during gameplay.
  8. Minimize use of ToString(). At a minimum it creates a string, which lives on the heap. See above about how to draw an int to the screen without generating any garbage. If you do need to use ToString, try to limit how often it’s called. If the string only changes every level, only generate it once at the beginning of the level. If it only changes when a certain value changes, only generate it when that value changes. Any limits you can put are worth it. The amount of time it takes to check a boolean condition is so small as to be almost nonexistent. You could probably fit tens and even hundreds of thousands of true/false checks in the amount of time it can take the GC to run on a complex heap.

That’s all the tips and tricks I have for now. There are others I’m sure you’ll think of once your mind is properly tuned in. One nice feature of XNA is that it makes it very easy to create cross-platform versions of your game. Oftentimes, it’s as simple as going to the “Project” menu and selecting “Create Copy of MyMagicGame for _________”. Input might take a little bit of tweaking, and you might want to become familiar with the #if WINDOWS and #if WINDOWS_PHONE preprocessor directives to keep code that only belongs in one version just in that version. But by doing so, you can use tools like CLR Profiler to examine memory allocations to help find sources of garbage, and you can use sampling profilers like ANTS or VSTS to see where CPU time is being spent. Have a look at another one of Shawn Hargreaves’ posts, the sarcastically titled "Why measure when you can guess?", for more information about how to find out why your game might be running slow at certain places so that you can focus your optimizations on the right things. There will be some differences between Windows and Windows Phone 7, of course, but some of the techniques he recommends, including one that’s not on that page - profiling with Stopwatch – will help you see what’s going on directly on the phone itself. Also remember that your emulated graphics, especially if you have a really fancy graphics card in your computer, may actually be quite a bit faster than the GPU of the phone itself. So be wary of really pushing the limits of the emulator and make sure to leave room to scale back if it proves necessary when you finally get your hands on a real phone to test with (by the way, if anyone has a spare one they need stored somewhere… :) ). And if you haven’t picked up on it yet, Shawn Hargreaves’ blog on MSDN is one you’ll want to bookmark, subscribe to, and read thoroughly and regularly. Even posts from 3+ years ago are relevant and useful today!

Thanks for reading, go create some great games (and Silverlight apps – remember these heap allocation minimization techniques transfer over into your code-behind code for performance sensitive Silverlight apps too), and good luck!

Posted On Friday, July 30, 2010 8:28 AM | Comments (2)
Windows Marketplace for Mobile

My registration just came through for the WP7 Marketplace. Took several days and because of some weird system glitch, I had gotten an instantaneous form rejection email when I first submitted and my status was set to “suspended” instead of “pending” or something of the sort. But a quick email confirmed that everything was fine and that if things on my end were still processing along (they were) then just to disregard it and they were sorry for the weird glitch. I’m about half way through my first game for the marketplace currently. I’m hoping to have two games and at least one app ready for launch. The games should be simple enough since I know what I’m doing in XNA and the games are easy yet fun, but both ideas I have for apps so far are “not easy” (according to my brain) and will take quite a bit of work to really do properly. I’d much rather do them properly from the get-go than just push something out so we’ll see. Things’ll be very busy for me for the next few months, that’s for sure.

Posted On Thursday, July 29, 2010 8:14 PM | Comments (0)
WinForms Content Loading Sample with XNA 4.0 Beta

One of my hobbies seems to be creating map editors for XNA games. With the new XNA 4.0 Beta(yes, it’s called Windows Phone Developer Tools – don’t worry, it’s the full XNA and builds Windows and Xbox 360 games too, though the current beta doesn’t deploy to the Xbox 360 yet so stick with XNA 3.1 for Xbox games for now)… anyway, with the new XNA 4.0, I thought I’d take the stock WinForms 2 sample (the one that includes building content on the fly using MSBuild) and build it to see how much conversion work it would take. I was expecting that it would take a lot, with all of the changes that scrapped the old mish-mash of RenderState and individual settings on the GraphicsDevice etc., but surprisingly it just took one and I thought I’d share it with anyone who might be interested. Download the sample, open it up using Visual Studio 2010 Express for Windows Phone, and go through the normal conversion process. At this point, it will build with a couple of warning messages but no errors. However, try to run it and you will get:

FileLoadException was unhandled

Mixed mode assembly is built against version 'v2.0.50727' of the runtime and cannot be loaded in the 4.0 runtime without additional configuration information.

When the converter migrates the project, it switches it to the .NET 4.0 Framework. However, the XNA Framework is built against .NET 2.0 (presumably since that’s the version of the Compact Framework built for the 360 and the time and money it’d take to update it to 3.5 or 4.0 wouldn’t be worth whatever small benefits might accrue – it is the Compact Framework after all). I tried a complicated solution a few months back after the April CTP was released, but this time I was clever and realized the easy fix. And here it is.

In the Solution Explorer (accessible from the View menu if you closed it for some reason), right click on the project (not the solution) and choose “Properties”. In the window/tab that opens up, you should be in the “Application” tab. In there you will see “Target Framework:” and it will be set to “.NET Framework 4”. Use the drop-down menu and change this to “.NET Framework 2.0”. You’ll get a confirmation message box, click “Yes”. Then simply build the solution and run it.

If you don’t have the 2.0 version of the .NET Framework installed on your computer, you can get it here. (Presumably it’s installed with XNA 4, but I ended up installing the SDK before I realized that since I was trying to manually change the references at first until I realized the simpler, better way of fixing it). Anyway, this is likely a bug and should hopefully disappear in the next beta or with the RTM at the latest, but for now, this should do it. Good luck!

Posted On Thursday, July 29, 2010 2:31 AM | Comments (3)
Windows Phone 7 and learning Silverlight

Recently I wrapped up primary programming on a library for Windows Phone 7 development, which you can find here: http://btiphone.codeplex.com/ . I started it around 6 weeks ago as a quick way to learn Silverlight and replicate some controls that don't exist on WP7 (SaveFileDialog, OpenFileDialog, and FolderBrowserDialog). Quick it wasn’t, but I feel like I understand Silverlight now and I created some nice controls along the way so I’m happy with how it turned out, even if it took about 3-4 weeks longer than I was expecting.

The first version was a UserControl because that's what the tools include as a template and I didn't know any better. Then I learned all about how controls you intend to share with others or even use for anything other than a quick mockup should be put together.

The first useful bit of information I came across was Creating a New Control by Creating a ControlTemplate. This introduced me to the concept of the Parts and States model, though it didn’t really explain it in terms I understood. I ultimately found this set of articles by Karen Corby, a product manager at Microsoft, that explained how it all works. Essentially, parts are a declaration using  of the different named controls and named layout elements your code is expecting to find (e.g. a TextBlock with an x:Name=”UserInput”, a Grid with x:Name=“LayoutRoot”) – these are the “parts” – and the different states of being that the control you are creating can be in (Pressed, Released, Disabled, … there are certain strongly recommended standards for naming and grouping these; have a look at the Control Styles and Templates of many of the standard Silverlight controls to learn what they are and how you only need to use what your control actually will use).

Armed with this knowledge, I completely rewrote the control as an ItemsControl, which inherits from Control and, along with ContentControl, is one of the two standard classes to derive from when writing a control. Along the way I learned about generic.xaml – a resource dictionary that you should use to define the default template for your control and which you should put in a folder called “Themes” that you should create in the base of your project (i.e. not within some other folder). All a “template” is, incidentally, is a bunch of XAML that lays out and positions the parts of your control relative to one another and defines the visual states and any Storyboard animations that accompany them within a VisualStateManager – this is explained well in the Control Authoring Overview, but when I first came across that page it looked like Greek (or worse, in my case, since I actually understand a bit of Attic Greek). I was smart enough to bookmark it though and coming back to it later it made a lot more sense. If you already know Silverlight, it’ll probably be much more accessible right from the get-go.

I also learned the foolishness of relying too much on a CTP. I had been working with the April CTP, and the way that TextBox behaved when disabled worked perfectly for me and looked good for my purposes (to represent file and directory items within the control). When the Beta came out this month and I upgraded… it still behaved mostly the way I wanted it to, but the look was different and no longer usable. At first I toyed around with trying to retemplate it, but I quickly came to my senses: I was trying to use TextBox for something that TextBox wasn’t really meant for. So I took a few days and created a BorderedTextBlock control, which is simply a TextBlock inside a Border. I then created keyed templates that included a transparent background brush so that it would register presses anywhere within the control rather than only on the TextBlock or along the border itself, and voila, the control I should’ve been using from the outset. I also reworked a control I had created to handle text overflow. The end result was the ScrollingTextBlock control, which tests the length of the string and if it’s larger than the space it has been allotted by the arrangement system, it uses some simple animation to ping-pong the text back and forth. Actually, the default behavior of the control just loop scrolls it like a news ticker; originally it had been a ping-pong only system but I realized that it’d be far more useful to expand its capabilities.

Which leads me to DependencyProperty objects. Whenever you right click on a control and choose “Properties” and you see things like Background, Foreground, MinHeight, etc., what you are seeing are dependency properties. Each control has their own, and yours need be no different. You’ll find that many are there automatically since they come along with the variant of Control that you’ve inherited from and everything that it inherits from, and even from some of the parts within your control. Sometimes you’ll have to do some internal connection using StaticResource to make sure that everything reflects changes properly – that part (for me, at least) is a trial-and-error process and will be for quite a bit of time. Rather than explain the nitty-gritty details of dependency properties here, I’ll direct you to Custom Dependency Properties, which explains and shows it at least as well as I can (you can also take a look at my project code using the CodePlex link at the top of this post). Just remember that you need to actually use the values that are set: simply adding a dependency property isn’t enough since all it does is set or change the value of a property – you need to subsequently do something with that value.

The last thing I’ll include here for now is one last important thing to know about Parts and States and controls in general. A key part of the Parts and States model is that the programming is divorceable from the design. A skilled programmer can write all the code to make the control work, and a skilled designer can develop a beautiful, intuitive, user-friendly interface. The designer isn’t limited only to what you include as parts (though states do require the code to recognize the underlying action that triggers the state and change states accordingly). Unfortunately, the designer isn’t compelled to include all of the parts you do list as mandatory or to properly connect things internally that need to be properly connected. As such, you need to runtime check if the named part actually exists and is of the type you are expecting (i.e. a TextBox and not a TextBlock or a ScrollViewer). If the parts all exist, great! If not, you are obligated to, as I’ve termed it, “fail silently”. It’s acceptable for the control to lose some or even all functionality. It’s not acceptable for the control to throw exceptions. This runs contrary to most programming you’ve learned in .NET. But controls get added to a XAML page often by dragging them into place, setting some properties, and moving on to the next part of the design. Later they might get retemplated in order to make them look prettier or fit within space confines or whatever. Nowhere in this process do any catch blocks get added nor is there such a thing as an easy way to add them since the layout process happens behind the scenes unless you decide to take a very complicated and time-consuming active role in it. So it’s very unacceptable for a control to ever cause an exception since it’s almost guaranteed to crash the app itself. Which is why I had to learn everything there is to know about how IsolatedStorage works on WP7 and why I ultimately ended up writing the FileOps class as a wrapper around IsolatedStorage in order to throw much more useful exceptions than “IsolatedStorageException: something bad happened somewhere” (paraphrasing, but only slightly). But FileOps is another topic for another time.

Posted On Wednesday, July 28, 2010 11:49 AM | Comments (0)
Bob Taco Industries is an ISV focused on game and app development for Microsoft platforms headed up by Michael B. McLaughlin. Mike is a Microsoft Visual C++ MVP (previously an XNA/DirectX MVP from 2011-2013), a developer, a writer, a consultant, and a retired lawyer. If you're a developer who is just getting started, consider checking out the BTI website's section for developers for links to code samples and other helpful sites.
Tag Cloud