views:

1194

answers:

3

I'm currently creating a window system for XNA games from scratch. I'm developing primarily for Windows, but who knows what platforms I might support in the future. Feel free to answer if you know this for native Direct3D, since the performance semantics should be similar. If possible, consider what would change if the target platform was X-Box 360.

I'm making good progress, but now I am unsure on how to exactly render the windows. I came up with four approaches:

  • Just render all controls directly onto the screen. This is what I do now. Controls can be animated by blending between states as long as they are not semi-transparent. I did not find a good way to animate between an arbitrary number of states (suppose a button that is currently animating from button-up to button-down and from mouse-out to mouse-over, and then it is being disabled. It should smoothly blend from its last state to the new state. With this approach, this only works if one animation is played after the last one finished, or you'll have jumps in animation.

  • Render each top-level window and all controls into a render target, and then use that to render the top-level windows with semi-transparency onto the screen. This makes semi-transparency at top-level work and is easy to manage, but doesn't change the thing with the animations.

  • Render each control into a render target, which is only updated when the control becomes dirty (i.e. must animate or the text has been changed). This way, per-control semi-transparency would work.

  • Like the previous, but in addition to solve the animation problem have a second render target for each control. Whenever an animation starts, swap render targets, so we have the state when the animation starts, and blend it with the destination state into the other render target. This should not add overhead over the previous approach, we just had twice as many render targets, of which in any given frame only one would be rendered to (at maximum). But here comes the problem: For this to work, I would need to have the "old" render target preserve its contents. This should work with good performance on Windows, but appears to have a serious performance impact on X-Box 360. On the other hand, the "preserve" bit is only necessary while an animation is active.

And here come the actual questions. Anything that clarifies is welcome. With the performance questions, remember that this would just be the window system of a game - the game behind might use many render targets and suck up performance as well, and likely much more than the window system. Assume that we might have five top-level windows with 20-40 controls each on the screen in absolute worst-case.

  • Which of these approaches, if any, would you recommend and why? Feel free, of course, to add another approach.
  • Is there a performance impact when just having let's say 200 or 400 render targets available, provided that only maybe 20 of them are being rendered to each frame?
  • Is the performance impact of PreserveContents really that bad on X-Box 360? How bad is it on Windows?
  • The RenderTarget2D.RenderTargetUsage property can be written to. Is switching this at runtime a good idea, to enable PreserveContents only as needed?
  • Would you (as a player) mind if control animations would jump in certain situations, like hovering over a button, moving the mouse out and then in again, so the "normal->hover" animation is played twice from the beginning because it is slower than you?
+3  A: 
  • If you want that level of control with animations (ie. having multiple going on the same control at once) then you're going to have do multiple passes. So either enable this kind of thing in a shader and do multiple passes with the shader, or do the standard Render->Resolve->Rerender loop.
  • There is indeed a performance hit creating that many rendertargets. It's not a good idea to create that many. You're better off having a smaller number of render targets of a larger size, and write multiple controls to that target.
  • It's been ages since I did any 360 dev (bugger :( ), but from what i can rememeber, it's not really that great. I'd avoid this option unless absolutely necessary. Hopefully someone with more up to date experience can help here.
  • This might work at runtime, but may involve a fair bit of work behind the scenes. I wouldn't expect that to perform too well. I'm sure it's fine if you're not doing it in the middle of a render loop.
  • I think that jumpy animations would definitely suck. Imho :)
OJ
Thanks for your input. I didn't have the idea to render multiple controls into one large render target, though now that you said that it seems obvious. I'll look deeper into this when I have the time, and will then likely accept your answer.
OregonGhost
+2  A: 

If you're developing for Xbox 360, you must be careful with render targets. The Xbox 360 has a special memory (10MB) to hold render targets, including the one used to render on the screen.

As long as you don't exceed 10MB, switching from one render target to another has no impact, even with PreserveContents, because all the render targets are stored in this special memory.

However, when you have more than 10MB of render targets with PreserveContents, this attribute must be emulated by constantly switching a render target back and from normal memory.

So the # of render targets is not as important as the total size of those ones. You can know the size of a render target with this formula :

size (bits) = width  x height x color data size (bits)

The color data size is 32 bits on Xbox 360 so, as an example, if the user of your library is developing his game in HD with only no other render target than the main one, he will use :

3,515625MB = 29491200 bits = 1280 x 760 x 32 bits

Also, you should avoid creating render targets dynamically. It cost too much. You should create a pool of static render targets allocated at the beginning of the game and have your GUI components request them.

Jodi
Thanks for your input. The render target pool is an interesting idea. I'm not actually developing for Xbox 360, I just don't want to exclude that option. 10 MB for render targets does not seem much. That's almost full with a 1080p screen.
OregonGhost
Yes it's very limited. You can have more than 10MB of render targets thought but updating more than 10MB of render targets each tick begins to cost CPU time.
Jodi
+1  A: 

The Xbox360 has 10MB of special memory used for the current render target. But the rest said about how it works is not quite accurate. Whatever render target is being used sits in that 10MB of space. Otherwise you can have as many render targets as you want. If your render target is bigger than the 10MB (like 1280x720 with some AA multisampling is) then the xbox360 uses a technique called Predicated Tiling: http://msdn.microsoft.com/en-us/library/bb464139.aspx

I am well into writing a windowing system. I am rendering the widgets to a render target, then rendering the widget target to the window target, the window target to the backbuffer. This allows me to easily add scroll bars and render only a part of the widget render target onto the window render target. If there is a better way to deal with getting scrolling of the window content working let me know. Not sure if this is the best in performance.

Sounds good. Let me hear how it works out performance-wise, once there's something resource-intensive running below the GUI :)
OregonGhost