Virtualizing the GraphicsDevice in XNA Game Studio 2.0

Virtualizing the GraphicsDevice in XNA Game Studio 2.0

Rate This
  • Comments 8

In the 2.0 XNA Framework, we virtualized the graphics device.

That sounds pretty cool, huh? But what does it actually mean, and why should you care?

In summary, you no longer have to care about a bunch of stuff you used to have to care about. You can stop reading this post right now, and everything will "just work" (tm).

Still here? You're obviously the kind of person who likes to understand how things work under the covers. Let me explain...

 

How things work in 1.0

When your game starts up, we call LoadGraphicsContent(true), in response to which you should load all your content, create textures, vertex buffers, rendertargets, etc.

If your game switches between windowed and fullscreen mode, or the user alt+tabs away from a fullscreen game, or some other game runs in fullscreen mode, or the user locks their desktop, the graphics device becomes lost. You cannot use a lost device, so we suspend calling Draw while it is unavailable. When the graphics device returns from this lost state, it needs to be reset, and when it is reset, some of your graphics resources become invalid. Specifically:

  • Most textures, vertex buffers, effects, etc, remain valid
  • Any ResourceManagementMode.Manual textures and vertex buffers are destroyed
  • All rendertargets are destroyed

When this happens, we call LoadGraphicsContent(false), which tells you that some of your content needs to be reloaded or recreated, even though other content is still valid.

This is confusing to people. Most programmers don't understand which content is which, so they don't know what to do with that "loadAllContent" boolean parameter. If they get it wrong, their game might crash, or they might waste time needlessly reloading things that didn't need to be reloaded.

It can also be a pain to support LoadGraphicsContent getting called at arbitrary times after your game starts up. If you passed references to your content objects around, sharing them between many different game objects, you must track down all the places that stored a reference and update them to use the reloaded content.

A worse problem occurs if the user drags a game from one screen to another on a dual monitor machine. In that case, we must destroy the graphics device entirely, then create a new device on the second monitor. When this happens, all graphics resources are destroyed, so we call LoadGraphicsContent(true). If you stored a reference to the old GraphicsDevice object, that will no longer be valid. To handle this situation correctly, games must look up the current device each time they want to use it. This can be done using the IGraphicsDeviceService interface, the DrawableGameComponent.GraphicsDevice property, or the GraphicsDeviceManager class. The point is that you cannot store permanent references to the device, because this could change out from under you at any point.

 

How things work in 2.0

In the 2.0 framework, the graphics device and its associated resources always remain valid. You can store references to any objects you like, in confidence that those objects will not go away. The LoadContent method (which used to be called LoadGraphicsContent) still exists, but now it is only called once when your program starts up.

It is obviously still possible that the underlying native device might need to be reset or recreated. But in 2.0, the framework takes care of this internally. If it needs to recreate any graphics objects, it creates a new native object, copies across any data such as the contents of textures and vertex buffers, destroys the old, invalid native object, then changes the internal state of the managed wrapper object so this will reference the new native object in place of the old one. Your game code can go on using the same managed object, and does not need to care that the underlying native object was recreated behind the scenes.

There is only one fly in this ointment. For some kinds of resource, it is not technically possible for us to read existing data out of the old native objects. This applies to resource types that are designed to be updated rapidly on the fly or changed by the GPU, which are allocated in special memory that cannot be read back by the CPU. Specifically, this problem occurs with the DynamicVertexBuffer, DynamicIndexBuffer, RenderTarget*, and ResolveTexture2D classes.

This sounds bad, but in practice you usually don't need to care. We still do the trickery to update your managed wrapper objects, replacing their old underlying native objects with recreated versions. The only problem is that the contents of these objects have been lost. Where a 1.0 rendertarget would need to be recreated from scratch, a 2.0 rendertarget always remains a valid rendertarget, and keeps the same size, format, etc: it just won't contain the same image as before the device was reset or recreated.

For most programs, this turns out not to be a problem. For instance if your game uses a rendertarget for drawing shadow maps, or for bloom postprocessing, it won't care if the contents of that rendertarget are lost when the device is reset, because it was going to overwrite the whole thing during the next frame in any case.

Losing the contents of dynamic resources is only a problem if you update those resources on a sporadic basis, using them as a cache to store data from one frame to the next. For instance this would cause trouble if you were using rendertargets to cache 2D imposter images. If you fall into this (fortunately rather small) category, you can use the IsContentLost property or ContentLost event to detect when your dynamic resources have lost their contents.

 

How things work on Xbox

Xbox is not a multitasking operating system, so it does not suffer from lost graphics devices. When only one game can run at a time, you do not need to worry about other programs temporarily taking away the graphics card.

On Xbox, LoadGraphicsContent (or LoadContent in 2.0) is called just once at startup, then never again. There was no need for us to virtualize the Xbox graphics device, because the problem did not exist in the first place.

  • Is there any chance to entertain the user while XNA is recreating its graphics device resources behind the scenes? (something like a progress bar or just a 'please wait' message.)

    Or will this process be reasonably fast (say, below 5 seconds) even with massive amounts or graphics device resources (~400 MB)?

  • The process will be reasonably fast unless your app has been swapped out to disk (but in that case switching back to it could take a while regardless of the graphics resources, because all your code and data is swapped out, too).

    There's no way to hook this process. That would be a major can of worms since the graphics device is in a confusing, badly defined and constantly changing state while this is happening! It's hard enough for us to keep everything straight internally, let alone allowing third parties to render using some subset of the device at the same time :-)

  • Tip: if you're running a windowed app, you could throw a (modal, buttonless) dialog box notifying the user that the device is being reset. At least let the user know his/her system hasn't frozen.

  • That was for the initial poster and future visitors, not Shawn.

  • Revising for a test and couldn't find a decent explanation on LoadGraphicsContent anywhere...this was fantastic and really helpful for an absolute beginner!

    THANK-YOU :D

  • Hi.I have some trouble with GraphicsDevice.Basically I have small 2D grass textures.I want to tile them to 1280x720 resolution and scroll them.And I did it.You can see the code here(in LoadContent method): http://paste2.org/p/762839 . This is working fine except when the resolution change or toggle full screen i'm getting AccessViolationException.I'm pretty sure it is cause of my render target textures disappearing when the graphics device reseted.I have try to reload it on graphics device lost, reset or reseting events.No one worked.Do u have any idea to solve this?

  • Oops sorry,I forget to say I'm using XNA Game Studio 3.1

  • Hi Shawn,

    Thanks for all you do for xna. Your articles have helped me many times.

    I am having an issue randomly when using ToggleFullScreen(). Sometimes it ends up in a state where UnloadContent has been called, but LoadContent has not been called and GraphicsDeviceManager.GraphicsDevice is null. The game contines to run without calling Draw, but Update is still being called. See: <a href="forums.create.msdn.com/.../551965.aspx

    I have created EventHandlers for DeviceLost, DeviceResetting, and DeviceReset that only print debug messages just to get an idea of the behavior. I am not sure how to proceed. Any advice would be appreciated.

    Thanks

Page 1 of 1 (8 items)
Leave a Comment
  • Please add 6 and 5 and type the answer here:
  • Post