performance optimizations and code-structure cleanup

The problem

each object potentially carries one or more triggers.

When evaluating triggers, this means iterating through every object available and test for any triggers. That’s the naïve approach and has a huge drawback: only a tiny subset of the available objects are interactive and actually carry triggers.

Having to check every single object is a big performance hit, since we’re performing 99% of unnecessary checks.

The solution

Triggers

Since the introduction of the action- and trigger-manager classes, continuing to carry trigger-evaluation within the scenegraph-manager has become sub-optimal.

The new approach would add a method to the trigger manager that checks all of its created triggers for any matches and, if applicable, fires them.

Since any triggers are instanciated using the manager, we can store their pointers and evaluate them whenever necessary. This is the minimum effort needed for trigger evaluation: perform a check on every (active) trigger that is potentially able to fire.

Actions

A similar mechanism will be implemented in the action-manager: Blended actions shouldn’t be carried within the scenegraph-manager anymore, but should be managed by the action-manager from now on.

Meaning whenever an action is triggered that is in ‘bleded’ state, it is added to the action-manager’s internal processing list instead, leaving the scenegraph-manager with only having to call the action-manager’s new ‘process’ method.

actions&triggers: local and global IDs

an action needs to be able to reference any action or trigger that has been created in order to manipulate it.

For example:

if the player is standing in front of a door and presses X, the door should open.

If he presses X again, the door shouldn’t open again but close instead.

This means the ‘open’ action, once triggered, needs to deactivate itself and activate its sibling action instead.

In order to allow actions such a behaviour, each action needs a global unique identifier.

The action manager manages those IDs and is able to find the according pointer when queried with an ID.

Problem

This is all fine and well if an action wants to manipulate a very specific action or trigger in the global context. But what if the action wants to manipulate a local action?

The above example works fine with global IDs, but imagine what happens when the object containing the ‘open’ and ‘close’ actions gets copied and moved elsewhere.

If the player approaches the copied door and presses X, the door opens. That’s ok so far. The just triggered ‘open’ action deactivates itself as expected, but instead of activating its sibling action, it activates the ‘close’ action of the original door object.

Why? Because the just triggered ‘open’ action references the ‘close’ action by its global ID, which got copied along with the rest of the object.

Introducing Local IDs

In order to prevent exactly this problem from happening, aside from its global ID, each action also contains a ‘local ID‘.

This ID identifies the action within its parent object.

So if the door object’s ‘open’-action references its sibling action by its local ID,  copying the object would be no problem, the copied action would enable the correct sibling action.

The same system will be implemented for triggers.

technical details

the actionmanager class contains a map: “id <-> action pointer” for global IDs

the parent object contains a map: “id <-> action pointer” for local IDs

Whenever a local or global ID changes, the actionmanager or parent object gets notified.

If an action changes objects from one parent to another, the old parent gets notified and deregisters the action, before the new parent registers it.

both actionmanager and object get new methods that return the action pointer corresponding to a certain ID.

in addition to these changes, actions and triggers get the ability to reference target object not by their global ID but by using a constant or string telling them to use their parent object as their target.

actions & triggers

recent additions to the UI and the action/trigger mechanism has enabled me to add triggers to objects which in turn start actions as they fire.
Actions can have a set of follow-up actions which are executed once the first action has finished.

All of this can be configured in the user interface, using a generic configuration window approach.
The only thing(s) left to do are:

  • write access modifiers for each of the action’s / trigger’s properties
  • update the methods that create the generic editor boxes to reflect the interface change (so that the properties can be configured using the new access modifiers)
  • ultimately write stream/destream operators or save/load methods for the actions & triggers so the whole configuration can be stored in the world file.

quick video of the UI in action:

Alpha blending in render targets

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%.