Using RenderTarget2D in XNA on the Zune HD
/Have you been lying awake at night wondering why your texture rendering doesn’t work on the Zune HD? No? Oh, well it must be just me then.
Anyhoo, I’ve found the problem, and the story goes like this.
RenderTarget2D is a way that you can draw things on a texture rather than on the game display. Normally I’d write things like:
spriteBatch.Begin();
spriteBatch.Draw (lots of pretty stuff….)
spriteBatch.End();
When the End method runs it takes all my drawing operations, batches them up and pushes them onto the graphics hardware, so the screen now contains the results of all my drawing operations.
However, sometimes you want to do things like rear view mirrors in driving games. You want to draw the view you would see behind the game onto a texture and then draw the texture on top of the rear view mirror. In this case you need to create a thing called a RenderTarget2D which can accept the draw operations and put them onto a texture rather than the screen. Render targets are not that hard to use:
RenderTarget r = new RenderTarget2D(
GraphicsDevice, // my graphics device
800, // width of texture
600, // height of texture
1, // no. of levels (1 works)
SurfaceFormat.Color); // format
GraphicsDevice.SetRenderTarget(0,r);
spriteBatch.Begin();
spriteBatch.Draw (lots of pretty stuff….)
spriteBatch.End();
GraphicsDevices.SetRenderTarget(0,null);
Texture2D result = r.GetTexture();
In the example above I send a bunch of drawing operations to a render target which is 800 pixels wide and 600 pixels high. I set the GraphicsDevice to render to this target, and then at the end I point it at null, which means go back to drawing on the screen again. The render target that I create is called r, and it ends up providing me with a texture which holds the results of all the drawing operations. You can use this to make your whole game display slide about, or bounce around the screen and it is very effective.
I’m using it so that my game can create some textures when it starts, rather than having to load them. I got the rendering working just right on the Windows PC and then transferred my code over the the Zune HD and it broke. I could clear the texture with different colours but I couldn’t draw anything on them. Wah.
After some twiddling I’ve found the problem. When you get a graphics device to render to a texture it is doing something it isn’t particularly used to. Graphics devices are designed to put stuff on the screen, not into memory. This means that you have to be careful when you set up the target for the rendering, so that the graphics device is happy to write to it. You can find our more about this kind of thing here.
From the point of view of the Zune HD hardware, it is only happy to draw things if the target texture is either the size of the display or half the size of the display. Don’t ask me why, I don’t make the rules. Any other sizes don’t produce an error, but they don’t work either, which is no fun.
This is not actually a huge problem (except perhaps for memory usage I guess) in that although you have to create textures that are larger than you really want you can use a version of Draw to only pull out the actual part that you want when you finally render them onto the screen. So, if you want to render to textures on a Zune you need to create your render target thus:
drawBuffer =
new RenderTarget2D(
GraphicsDevice,
GraphicsDevice.Viewport.Width / 2,
GraphicsDevice.Viewport.Height / 2,
1,
SurfaceFormat.Color);
This is the smallest texture you can use, if you want to draw on the full screen just don’t divide by 2.