Loading Resources in the XNA Framework

Loading Resources in the XNA Framework

  • Comments 5

Scott Miller
XNA Documentation

 

We've had a number of questions in the XNA forums about the missing Load Resources How To, so I thought I'd try to clear up the issue.   

The Load Resources How To was merged with the Simple Game Loop How To because a game loop wouldn't really be complete without being set up as originally outlined in the Load Resources How To.

Due to some confusion (it is a beta after all), the dead links that pointed to the original How To remained in the beta and the Simple Game Loop How To doesn't quite get things right.

So, here's what you need to know to make sure your game's graphics resources will be loaded at the appropriate times. Resources that depend on the graphics device can become invalid when the graphics device is reset or recreated.  This makes it necessary for the code that creates these resources to be called in event handlers rather than just being called when the game first starts. 

There are two GraphicsComponent events that can result in resources needing to be created/recreated, GraphicsComponent.DeviceCreated and GraphicsComponent.DeviceReset.

The GraphicsComponent.DeviceCreated event is raised when the graphics device is first created and subsequently anytime the device is recreated.  When the graphics device is recreated all graphics resources become invalid and need to be recreated.  Every game needs to handle the GraphicsComponent.DeviceCreated event.

Before I get to GraphicsComponet.DeviceReset, I need to take a slight detour to explain the Graphics.ResourcePool enumeration types.  Most constructors for graphics resources take a ResourcePool type as a parameter.  The ResourcePool value passed to these constructors determine what type of memory (system memory, video memory, local video memory, AGP memory) the resource will be placed in and whether XNA will manage that resource on a device reset.  

There are four types in the ResourcePool enumeration: Default, Managed, Scratch and SystemMemory.  Of these four, the two we're most interested in are Default and Managed. 

Default is something of a misnomer as it is not the desired default choice for XNA (Managed is usually a better choice).  Default places the graphics resource in the memory pool that is most appropriate for the set of usages requested for the given resource. This is usually video memory, including both local video memory and AGP memory. The Default pool is separate from Managed and SystemMemory, and it specifies that the resource is placed in the preferred memory for device access.

Managed is the choice that should be used whenever possible.  Managed resources are backed by system memory and are copied automatically to device-accessible memory as needed.

Now, back to GraphicsComponent.DeviceReset and how ResourcePool types relate to it.  When a DeviceReset event is raised, the graphics device is reset but its properties have not changed.  This means that resources that are backed by system memory can simply be copied back to device-accessible memory.  For resources in the Managed ResourcePool, XNA takes care of the copying for you.  Resources that are in the Default ResourcePool are not backed by system memory and need to be recreated manually when the graphics device is reset.

Here is the result of all this:

·         Graphics resources should be created in the Managed resource pool if possible.

·         Applications should separate the creation of Managed and non-Managed resources into methods such as LoadManagedResources and LoadNonManagedResources.

·         The GraphicsComponent.DeviceCreated event needs to have a handler.

·         Both LoadManagedResources and LoadNonManagedResources should be called in the method that is handling the GraphicsComponent.DeviceCreated event.

·         If LoadNonManagedResources exists it should be called in the method handling the GraphicsComponent.DeviceReset event.

Here's a sample of the pattern described above:

      //Contents of Game1.cs

    partial class Game1 : Microsoft.Xna.Framework.Game

    {

        //for creating a vertex buffer by hand

        VertexDeclaration decl = null;

        VertexPositionColor[] vpc = null;

        VertexBuffer vb;

 

 

        public Game1()

        {

            InitializeComponent();

        }

 

        protected override void Update()

        {

            // ...

        }

 

        void InitVertexBuffer()

        {

            GraphicsDevice device = graphics.GraphicsDevice;

            decl = new VertexDeclaration(device, VertexPositionColor.VertexElements);

            vb = new VertexBuffer(device, typeof(VertexPositionColor), 3, ResourceUsage.WriteOnly, ResourcePool.Managed);

            vpc = new VertexPositionColor[3];

            vpc[0].Position = new Vector3(0, 0, 0);

            vpc[1].Position = new Vector3(0, 3, 0);

            vpc[2].Position = new Vector3(3, 0, 0);

            vpc[0].Color = Color.Red;

            vpc[1].Color = Color.Green;

            vpc[2].Color = Color.Blue;

 

            vb.SetData<VertexPositionColor>(vpc);

        }

 

 

        protected override void Draw()

        {

            // ...

        }

 

        void LoadManagedResources()

        {

            //Create resources that are in the Managed pool

            InitVertexBuffer();

 

        }

 

 

        void LoadNonManagedResources()

        {

            //Create resources that are in the Default pool

        }

 

        private void graphics_DeviceCreated(object sender, EventArgs e)

        {

            LoadManagedResources();

            LoadNonManagedResources();

        }

 

        private void graphics_DeviceReset(object sender, EventArgs e)

        {

            LoadNonManagedResources();

        }

 

    }

    //Contents of Game1.Designer.cs

    partial class Game1

    {

        /// <summary>

        /// Required method for Designer support - do not modify

        /// the contents of this method with the code editor.

        /// </summary>

        private void InitializeComponent()

        {

            this.graphics = new Microsoft.Xna.Framework.Components.GraphicsComponent();

            //

            // graphics

            //

            this.graphics.DeviceReset += new System.EventHandler(this.graphics_DeviceReset);

            this.graphics.DeviceCreated += new System.EventHandler(this.graphics_DeviceCreated);

            this.GameComponents.Add(this.graphics);

 

        }

 

        private Microsoft.Xna.Framework.Components.GraphicsComponent graphics;

    }

---

This posting is provided "AS IS" with no warranties, and confers no rights. Use of included script samples are subject to the terms specified at http://www.microsoft.com/info/cpyright.htm.

Leave a Comment
  • Please add 8 and 4 and type the answer here:
  • Post
  • You wrote graphics resources should be created in the Managed resource pool if possible. But Xbox360 has 512MB of system memory (and many of PCs nowadays too). It can be better to use Default resource pool and use system memory for another purpose. I suppose that DeviceReset event is raised very rarely (when user switchs into another application and so on).
    So is it better to implement into engine support for graphics resources created in the Managed resource pool? I think so.
  • PingBack from http://www.360brew.com/blog/?p=14
  • PingBack from http://www.gamedevnews.com/?p=171
  • PingBack from http://www.xnatutorial.com/?p=17
  • Augi,

    The choice between the Managed and the Default pool is mainly a performance vs memory usage issue.  By using the Default pool you could save memory, but the trade off would be that any time you had a DeviceReset event you would need to reload your graphics resources from disk.  You are correct that the DeviceReset will occur less often on the Xbox than on the PC so it could be worth it depending on your requirements.  

    You'll probably be interested to know that the content pipeline will use the Managed pool when loading resources.

    -Scott

Page 1 of 1 (5 items)