rendering UI elements can be very time consuming – especially when multiple layers of stacked objects such as containers and windows are being drawn semi-transparently.
One way of reducing rendering time i<pre lang=”cpp”>s to cache certain UI elements – a window for example usually remains the same until it has changed its size or displays new content. Since that happens extremely rarely when compared to the ~60 times a second it is being rendered, caching seems like a sensible move.
There’s only one problem:
rendering (semi-)transparent elements to a render target doesn’t exactly work the way you’d expect it to. This is due to the fact that during rendering, pixels get blended together using a set of render states and texture stage states. If the color of a pixel gets computed using a certain blend function, its alpha value gets computed the same way. This usually results in oddly looking UI elements with parts that have a higher transparency than intended.
Googling for a while has brought up the idea of premultiplied alpha channels which – in some cases – help. The idea behind this is to (as the name implies) premultiply a pixel’s alpha value with its color components.
This seems to work fine for single-layered elements. As soon as several UI elements overlap (a transparent button inside a window for example), this method fails as well.
Some more research brought up a DirectX9 renderstate that can be activated to enable a different blending operation for alpha values.
D3DRS_SEPARATEALPHABLENDENABLE
What this renderstate – once enabled – does is allow to set a different blend function for alpha values of a pixel.
pd3dDevice->SetRenderState(D3DRS_SRCBLEND, D3DBLEND_SRCALPHA)
pd3dDevice->SetRenderState(D3DRS_DESTBLEND, D3DBLEND_INVSRCALPHA);
pd3dDevice->SetRenderState(D3DRS_SRCBLENDALPHA, D3DBLEND_ONE);
pd3dDevice->SetRenderState(D3DRS_DESTBLENDALPHA, D3DBLEND_INVSRCALPHA);
pd3dDevice->SetRenderState(D3DRS_SEPARATEALPHABLENDENABLE, TRUE);
This snippet blends the color components of a pixel together using their respective alpha values as one would expect:
res.rgb =p0.rgb * (1 – p1.a) + p1.rgb * p1.a;
But the alpha component of the resulting pixel gets computed differently:
res.a = p0.a * (1 – p1.a) Â + p1.a;
I’m still experimenting with this, but it seems to be a step in the right direction.
p.s.
a quick performance check of caching UI elements vs. not caching them turned out to reduce rendering time for my example scene to almost 50%.