optimize CEGUI::System::injectTimePulse

If you found a bug in our library or on our website, please report it in this section. In this forum you can also make concrete suggestions or feature requests.

Moderators: CEGUI MVP, CEGUI Team

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Mon Nov 16, 2009 07:33

Hiya,

I'm making an RPG-style game, and it has a *lot* of UI.

I recently upgraded to 0.7.1, and I've been toying around with some UI optimizations. In the process, I discovered my per-tick call to CEGUI::System::getSingleton().injectTimePulse(..) was taking a lot of time. This function recurses through every CEGUI Window (i.e. every widget) that exists in your application, and calls Window::update(..). In my game, this was a significant bottleneck. I think this bottleneck was always around - not caused by the 0.7.1 upgrade.

Even though my game has a lot of UI, the vast majority of it is not visible at any given time. (A common trait of RPG games).

So, this optimization made a huge difference to the frame rate in my game:

Old Code:

(CEGUIWindow.cpp)

Code: Select all

void Window::update(float elapsed)
{
    // ...

    // update child windows
    for (size_t i = 0; i < getChildCount(); ++i)
        d_children[i]->update(elapsed);
}


New Code:

Code: Select all

void Window::update(float elapsed)
{
    // ...

    // update child windows
    for (size_t i = 0; i < getChildCount(); ++i)
    {
        if ( d_children[i]->isVisible( true ) )
        {
            d_children[i]->update(elapsed);
        }
    }
}


Note the additional isVisible check.

This doesn't appear to have any bad side-effects in my testing, it just speeds everything up.
Last edited by Jabberwocky on Mon Nov 16, 2009 17:18, edited 1 time in total.
The Salvation Prophecy
Space Combat. Planet Exploration. Strategic Domination.

User avatar
scriptkid
Home away from home
Home away from home
Posts: 1178
Joined: Wed Jan 12, 2005 12:06
Location: The Hague, The Netherlands
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby scriptkid » Mon Nov 16, 2009 09:14

Hi,

Sounds like a good catch, thanks :) Performance issues are sometimes only noticed when talking about large numbers. I will add it to the branch and/or trunk.

Bye.
Check out my released snake game using Cegui!

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Mon Nov 16, 2009 17:21

Cool, thanks.

I forgot to mention the code change is in CEGUIWindow.cpp. I edited my post above to indicate that too.
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.

User avatar
scriptkid
Home away from home
Home away from home
Posts: 1178
Joined: Wed Jan 12, 2005 12:06
Location: The Hague, The Netherlands
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby scriptkid » Mon Nov 16, 2009 19:56

Great, it's in the 0.7 branch now :)
Check out my released snake game using Cegui!

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Mon Nov 16, 2009 20:56

Thanks scriptkid,
That was fast! :D
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.

User avatar
DtD
Just popping in
Just popping in
Posts: 14
Joined: Fri Oct 09, 2009 18:54
Location: Kansas, United States
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby DtD » Tue Nov 17, 2009 00:23

Great find on that optimization!

~DtD

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Tue Nov 17, 2009 02:35

Dang. Found an edge case.

Tooltips need to update themselves while invisible. This optimization breaks tooltips. So the optimization code will need to be a little smarter.

There's a simple approach and complex approach we could take.

The simple approach: Explicitly test for tooltips.

Code: Select all

    for (size_t i = 0; i < getChildCount(); ++i)
    {
        // Note:  Tooltips are the only CEGUI widget type that needs to be updated while invisible.
        if ( d_children[i]->isVisible( true ) || dynamic_cast<CEGUI::Tooltip*>( d_children[i] ) )
        {
            d_children[i]->update(elapsed);
        }
    }



The more complex approach would be to add a new CEGUI::Window property, something like "UpdateWhileInvisible", which could then be set explicitly on a per-widget basis.

Sorry I missed this in my testing.
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.

User avatar
scriptkid
Home away from home
Home away from home
Posts: 1178
Joined: Wed Jan 12, 2005 12:06
Location: The Hague, The Netherlands
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby scriptkid » Tue Nov 17, 2009 08:11

Okay good one. I have a re-think about this myself. i will apply your change first though, because i am not sure how many branch users we have.
Check out my released snake game using Cegui!

User avatar
DtD
Just popping in
Just popping in
Posts: 14
Joined: Fri Oct 09, 2009 18:54
Location: Kansas, United States
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby DtD » Tue Nov 17, 2009 19:27

Perhaps a new "isActivated" function and an "activated" property would be better. That way the widget can decide if it wants to run when it is invisible or not.

~DtD

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby CrazyEddie » Wed Dec 02, 2009 10:29

With regards to this, I wonder if we can drill down a bit and see if it's the Window::update call that's causing the hit, or whether it's the event trigger that's causing it (i.e. the event fired in the Window::update call). What I mean by that is a simple function call should not be all that expensive (though admittedly, a simple call made often enough can be), though the overhead of firing an event will be much more substantial due to looking up the name of the event every time for every window - IMO it's likely this that needs the optimisation. If it is the event fire that's the real culprit, until events are optimised in general, we could add a setting to enable / disable that event (either runtime or compile time, or both).

CE.

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Fri Dec 11, 2009 03:06

I ran a few simple tests, measuring framerate in the same scene using various different forms of this optimization.

Test 1: Using unaltered CEGUI code (no optimization)
Frame rate: 94

Test 2: Using the optimization I posted above (with a hack to solve the tooltip problem)

Code: Select all

void Window::update(float elapsed)
{
    // perform update for 'this' Window
    updateSelf(elapsed);

    // update underlying RenderingWinodw if needed
    if (d_surface && d_surface->isRenderingWindow())
        static_cast<RenderingWindow*>(d_surface)->update(elapsed);

    UpdateEventArgs e(this,elapsed);
    fireEvent(EventWindowUpdated,e,EventNamespace);

    for (size_t i = 0; i < getChildCount(); ++i)
    {
        // Note:  Tooltips are the only CEGUI widget type that needs to be updated while invisible.
        if ( d_children[i]->isVisible( true ) || dynamic_cast<CEGUI::Tooltip*>( d_children[i] ) )
        {
            d_children[i]->update(elapsed);
        }
    }
}


Frame rate: 190

Test 3: using a different optimization to squelch the fireEvent call on invisible windows

Code: Select all

void Window::update(float elapsed)
{
    // perform update for 'this' Window
    updateSelf(elapsed);

    // update underlying RenderingWinodw if needed
    if (d_surface && d_surface->isRenderingWindow())
        static_cast<RenderingWindow*>(d_surface)->update(elapsed);

   // Test optimization:  only fire an event if we're visible
    if ( isVisible() )
    {
       UpdateEventArgs e(this,elapsed);
       fireEvent(EventWindowUpdated,e,EventNamespace);
    }

    // update child windows
    for (size_t i = 0; i < getChildCount(); ++i)
        d_children[i]->update(elapsed);
}


Frame rate: 130

So in my usage of CEGUI (*lots* of non-visible windows), the optimization in the child update loop (test 2) is quite a bit faster.
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby CrazyEddie » Sat Dec 12, 2009 09:26

Hi,

Thanks for this, it really does help me/us a lot, as well as being quite interesting on more than one level :)

I will take some time shortly to look into this, and the various possibilities - presently I think we will likely end up with a setting or option to enable / disable this call on a per-window basis, I believe this should give the best and most flexible solution without needing to special case specific window types (i.e. ToolTip). This being said, I'll not add that blindly, rather I'll do some proper testing and additional analyses to (hopefully) get the 'right' solution.

Watch this space...

CE.

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Sat Dec 12, 2009 18:37

Glad I could help. It's one of the great benefits of having open source code to work with. The per-window option sounds like a good approach.
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.

User avatar
CrazyEddie
CEGUI Project Lead
Posts: 6760
Joined: Wed Jan 12, 2005 12:06
Location: England
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby CrazyEddie » Mon Jan 25, 2010 11:00

Hi!

I have just now committed (in v0-7 branch) the - hopefully - final version of this fix / optimisation. What I have done is added Window::setUpdateMode and Window::getUpdateMode functions, along with an UpdateMode property. These can be set to WUM_ALWAYS, WUM_NEVER, WUM_VISIBLE (or "Always", "Never" and "Visible" for the property), in order to control the conditions under which the Window::update call is made for child content. It is set to WUM_VISIBLE by default, though individual window types (like Tooltip) can override this is code or looknfeel, and users can override the setting for any window however and whenever they see fit.

Thanks again for raising this, and the subsequent analyses of possible solutions.

CE.

User avatar
Jabberwocky
Quite a regular
Quite a regular
Posts: 86
Joined: Wed Oct 31, 2007 18:16
Location: Canada
Contact:

Re: optimize CEGUI::System::injectTimePulse

Postby Jabberwocky » Tue Jan 26, 2010 23:38

Nice work. Thanks CE!
The Salvation Prophecy

Space Combat. Planet Exploration. Strategic Domination.


Return to “Bug Reports, Suggestions, Feature Requests”

Who is online

Users browsing this forum: No registered users and 9 guests