Friday, September 21, 2007

Window System for XNA - New Release

Just to let everybody know that there should be a new release within the next week, basically when my internet connection is back up. The reason for the sudden release is because I've finally managed to fix the GUI rendering, with correct alpha values. The most important impact is that anti-aliased SpriteFont will be used from now on, and the old XNAExtras stuff is to be removed.

Project Site

If anyone is interested in what the problem was, and how I fixed it, then read on.

In the old system, every control was rendered to its own texture using render targets. Each child control would have its texture combined with those of the other children, to make up the texture of the parent control (after drawing itself). It was quite efficient because only the top level controls (generally windows) actually get their textures drawn to the screen, with the textures only redrawn when necessary, such as when the mouse hovers over a button, and a different image must be displayed. Render targets were also an easy way to perform clipping of child controls within their parent, and allowed the alpha value of a control to affect all children as well.

The problem with render targets was that control textures had to be drawn to a transparent texture, causing incorrect colour and alpha values to be produced during the alpha blending operation. In practical terms, it means that the edges of sprites with partial transparency and anti-aliased text would be mixed with white (or any other colour depending on the clear colour of the render target). This made either anti-aliased text unreadable, or made the edges of some controls the wrong colour. The reason I couldn't use SpriteFont, was because it created anti-aliased fonts. With XNAExtras I could make a simple font bitmap with colour keyed transparency, which made the text problem go away.

After playing with render states and pre multiplied alpha for a long time, I finally got fed up and tried to find another way to get it working. I was advised to draw directly to the screen, and use scissor rectangles to handle clipping. This worked great, except performance was abysmal. With just 4 complex windows on the screen, the framerate would start to drop. That was without any game running in the background! The reason for the poor performance was because scissor rectangles must be the same until SpriteBatch.End() is called. This means that I had a SpriteBatch.Begin() and End() call for every single control!

Next I decided to implement my own texture clipping, which all worked great, but there wasn't an obvious way to clip text. When using scissor rectangles for just text, the performance was still really bad. The final method was to use a hybrid approach. I would clip sprites on the CPU, and render text to textures which would be cached to allow fast draw calls. The text texture can then be clipped in the same way as the rest of the GUI sprites. Of course there was still the problem of partial alpha pixels being blended with the transparent texture colour. Basically I set the render states to draw with pre multiplied alpha, and set the background clear colour of the texture to the text colour, with the alpha channel set to 0. This means that the blending still takes place, but it produces the correct results. The performance is good once again!

So yeah, the project isn't dead, I've just had some big problems to deal with. Now that this is working, I will go back to adding more features like Xbox 360 support, and more widgets.

1 comment:

Anthony H said...

It's been a VERY long time, but wondering what ever happened to using the hosted site.

Sorry for not being able to do more. Hope it was helpful at least.

-Anthony

PS: we just revived the project we were working on, and currently it's being fixed up to work with GS3 and GS2.