Posts tagged: XNA

Managing Focus Across Different Platforms

With the upcoming release of FGF/Thrust comes the return of a more traditional GUI. One of the most problematic requirements of a large GUI system is the notion of focus. The question remains how do you efficiently determine who has focus and how do you pass focus between controls? On Windows this is incredibly easy because we have the mouse pointer. Focus is changed whenever your mouse acts on a control. What about on the Zune or the Xbox 360 though? On both of these systems their is no mouse (although Thrust supports a virtual mouse).

The answer is to look at what Thrust currently supports. What built in system supports all three systems seamlessly? (~ means some support, X means full support)

Event Zune Windows Xbox 360
Mouse   X  
Keyboard   X X
GamePad ~ X X
UniversalButton X X X

The problem is we can’t rely on each individual hardware controller being present and useable. For example we can’t rely on a keyboard being a focusing mechanism on the Xbox 360 because it isn’t a guarantee it exists. Likewise, we cannot rely on the GamePad on the Zune because of the lack of buttons.

To get to the point, the UniversalButton system was meant as a virtualization of the various hardware supported for each platform. It turns GamePad, Mouse, and Keyboard events into simple events like Up, Left, Down, Right, Select and Cancel. Because of this we can rely on it and implement a few more events.

  • TabPreviousLocal
  • TabNextLocal
  • TabPreviousGlobal
  • TabNextGlobal

You can consider local tabbing to be much like you would on a Windows form: hitting the tab key (or shift-tab) will move you from one control to another based on some order and only in the context of the global focus point (you never tab to a control in another window). The global tabbing can be considered like an Alt-Tab (or shift-alt-tab) where you can switch between windows.

So how do we implement this? We need a managing class (sorry Bjoern) to produce a bottleneck for the input events. As events are channeled through this class, it massages the data and figures out what to do. For instance if a global tab next event is received it has to switch focus to the next window in the system on the same level as the current window. If it receives a local tab event it will pass a message to the current focal point to tab to the next control.

What does this do for us? For one, it unifies the approach to focusing across all the UI subsystems. This means that a Window/Form implementation will focus in much the same way a simple screen will. Unfortunately it also means a complexity requirement for implementation developers. The age of the simple StateManager class is coming to an end. Elements on the screen now need to have a basic state for animation as well as a state for focus (or lack thereof). While it may still be possible to simply unload an element through the StateManager, the reality is that UI elements will have to do a little more management under the hood. Whether this is exposed to the user / developer is still to be decided.

Reference Counting in Managed Code

Today, while coding Galactic Wars v2.0, I stumbled upon a problem I have been having with content since I started working with XNA. The following diagram shows how easy it is to create the problem and how devastating it can be to any application. In my case, I have two (or more) menus that are referencing a piece of content.

Multiple References

Each menu references the same managed object which is really a wrapper for a second managed object (Texture2D in this case). The second object is a managed wrapper for the underlying unmanaged data, again in this case it is texture data. The problem is when one menu is being phased out (unloaded) and another is being phased in (loaded). The menu being loaded grabs a reference to the content before the other menu can unload it. The ordering is done this way to ensure that a menu actually loads its content before the current menu is unloaded.

After the menu is done loading content, the second menu is unloaded which drops the content and releases the unmanaged memory. Aha! The problem stems from the last point: I am trying to manage unmanaged memory when I should just let the GC take care of it. The reason I do this is for performance reasons, primarily on the Zune. Instead of letting textures just sit around, I release them when they are no longer needed to avoid piling up the garbage.

So what is the solution?

  1. Dereference the Managed Object

    I would love to do this but unfortunately it introduces a second problem. If every menu (object using the content) dereferences the asset, it still will not be collected by the GC. The AssetManager still holds a reference which is now being unused and thus memory is wasted. The upside to this is that I do not need to implement the second option…

  2. Reference Counting!

    By implementing a counter on the asset itself and asking developers to always call Unload or Unload(ILoadable) on the AssetManager, I can guarantee that no Asset will be disposed while another object is using it. The downside is that it is an ugly implementation of something that is done in the GC anyways. The major upside is that even if developers use the previous method, the GC will still eventually catch the wasted memory.

So now Thrust’s AssetManager and IAsset require (and implement) reference counting to avoid the problem described above. And it works wonderfully.

Garbage Collection Nightmares…

I was pushing Galactic Defense to the Zune today to do some minor version testing (I develop on the PC and then push to Zune for platform specific tests) and ran into some major problems. The GC was collecting about once an update which was slowing the game down tremendously. So I dug in and started searching my code.

One of the best ways to figure out where you are creating garbage (when no tool can do it for you) is just to start commenting things out. For starters, I commented out the draw code for my map. This worked, but not enough. I ended up adding a line that removed the InputManager from the component collection (effectively commenting out its update code) and I had it: 0 allocations per update.

So I looked at the draw code again and realized (with the help from guys in #xna) that I was using an enumeration in a Dictionary. A big no no since the framework allocates every time you do a lookup. A quick switch to ints (cast all lookups to integers) fixed that problem. Now onto the InputManager.

To make a long story short, I followed the trail and cornered this line of code:

1
if(!repeatKeys.Contains(button))

It is this line of code that actively makes sure that I have all the buttons in the repeatKey list so I can check and update the repeated keys later on. I removed this line of code and added the following:

1
2
3
4
5
6
7
8
9
10
11
12
bool found = false;
for (int i = 0; i < repeatKeys.Count; i++)
{
    if (repeatKeys[i] == button)
    {
        found = true;
        break;
    }
}
 
if (!found)
    repeatKeys.Add(button);

And now I am back to a normal amount of allocations (some for strings) per update. I wonder why Contains is causing an allocation. I am too lazy to figure that one out right now…

I wrote a book!

I co-authored a book (Building Xna 2.0 Games) with James Silva. It is totally FTW and you should buy it. It is about creating a game with Microsoft XNA as a one man team. It essentially guides the reader through the process of creating a game similar to James Silva’s award winning Dishwasher.

Me With Book

WordPress Themes