Rendertarget changes in XNA Game Studio 2.0

Shawn Hargreaves wrote about the changes of the Rendertarget in XNA 2.0, and more important, why.

From the article:

Rendertarget changes in XNA Game Studio 2.0

The bad news:

If you had a program using rendertargets that worked with the XNA Framework 1.0, it might not still work with 2.0.

The good news:

Things are actually much more consistent now, honest!

Let me explain…

How rendertargets used to work (1.0)

On Windows:

* Each rendertarget lives in a separate piece of video memory
* After you select the rendertarget, you can draw onto that video memory
* When you are done drawing, you call GetTexture to reuse that same area of video memory as a texture
* You can draw onto the same rendertarget as many times as you like, and its contents will always remain valid

On Xbox:

* All rendertargets share a single special piece of EDRAM memory
* This means only one of them can physically exist at a time
* When you finish drawing to a rendertarget, the GraphicsDevice.ResolveRenderTarget method copies from EDRAM to a separate area of texture memory
* You can then use this texture in any way you like
* But EDRAM is now being reused by some other rendertarget!
* This won’t work like you expect:
o Draw to backbuffer (EDRAM contains what you just drew)
o Switch to rendertarget
o Draw to rendertarget (EDRAM contains what you just drew)
o Resolve rendertarget (RenderTarget.GetTexture() contains a copy of what you drew)
o Switch back to backbuffer (problem! the act of selecting a different rendertarget has overwritten what you previously drew to the backbuffer, so the EDRAM no longer contains that backbuffer image)

* The rules in summary:
o Any time you change rendertarget, the contents of EDRAM are overwritten, so all previous rendertargets (including the backbuffer) are clobbered
o Rendertarget data which was resolved into the associated texture remains valid, however
o This is ok:
+ Draw to rendertarget A
+ Draw to rendertarget B
+ Draw textures from rendertargets A and B onto rendertarget C
o But this is not:
+ Draw to rendertarget A
+ Draw to rendertarget B
+ Switch back to A and continue drawing over the top of it

Problem with the 1.0 behavior:

It was far too easy to write a program that worked fine on one platform, but then rendered incorrectly when you run it on the other!

How rendertargets work now (2.0)

By default:

* You get what used to be the Xbox behavior
* On Xbox, it works exactly the same as before
* On Windows, we automatically clear your rendertargets at the right times to emulate the Xbox behavior
* This is fast on both platforms (Clear is very cheap)

If you don’t like that default:

* You can specify a different RenderTargetUsage
o RenderTarget2D constructor parameter
o To change it for the backbuffer, use the GraphicsDeviceManager.PreparingDeviceSettings event to alter GraphicsDeviceInformation.PresentationParameters.RenderTargetUsage
* Specify RenderTargetUsage.PreserveContents to get what used to be the Windows behavior
o Works exactly the same as before on Windows
o On Xbox, we automatically copy data back from the resolved texture into EDRAM to restore its contents when you change rendertarget
o This is not cheap! Use it if you must, but be aware of the performance penalty
* Specify RenderTargetUsage.PlatformContents to get the exact same behavior as 1.0, which is different on Xbox versus Windows

Shawn recommends:

* If at all possible, use the default RenderTargetUsage.DiscardContents mode. This gives good performance and consistent behavior on both platforms.

Other good stuff:

* In 2.0, you no longer need to call the GraphicsDevice.ResolveRenderTarget method. In fact you can’t, because we removed it. We now do this automatically when you switch away from the rendertarget.
* In 2.0, we now support multiple simultaneous rendertargets (MRT) on Xbox.