Professional UI Solutions
Site Map   /  Register
 
 

Forum

Please Log In to post a new message or reply to an existing one. If you are not registered, please register.

NOTE: Some forums may be read-only if you are not currently subscribed to our technical support services.

Forums » Prof-UIS General Discussion » Performance problem wrt __EXTTAB_MDI_UPDATE_TIMER_ID and _SyncAllItems() Collapse All
Subject Author Date
Niklas Bergh Jul 17, 2013 - 12:47 PM

Hi guys,

We have run into a rather noticeable performance issues wrt UI drawing close to our applications startup and wanted to check with you if you have any idea on what may be a suitable workaround/fix.

During our application startup we are opening say maybe 20-30 MDI windows representing files. This is done as the first step in the startup process. What we noticed was that all tabs opened up in maybe 10 seconds or so, but after that process the application seemed to do something similar to a busy-loop for another 20-30 seconds before the UI actually becomes responsive. In researching this we have come up with this information:

Durning the opening of the MDI documents a total of around 130 calls to this code in CExtTMWI::OnHookWndMsg() is invoked
                // setup MDI update timer
                PostMessage(WM_TIMER,__EXTTAB_MDI_UPDATE_TIMER_ID,0    );
Typically this is due to handling of messages for WM_MDIREFRESHMENU as well as as you can expect a number of WM_MDICREATE and WM_MDIACTIVATE window messages. In a few cases posted due to activities in our application, but mostly posted somewhere internally (opening a single doc for instance seem to trigger maybe 5-6 such postings in code outside our control).

These messages are all queued for execution and hence sequentially later on handled in CExtTMWI::WindowProc(). This method in turn invokes this logic: bKillTimer = _SyncAllItems(); and this logic is relatively slow. It also seems to become even slower the more Tabs that are open in the application, leading to a non-linear slowdown as more documents are being opened.

Netnet, our application becomes slower and slower the more documents we have open, and it very quickly adds up to several tens of seconds in our common usecases.

Does anyone have any thought on what can possibly be done to improve this?

Regards
/Niklas

Niklas Bergh Jul 18, 2013 - 11:27 PM

For the benefit of others, and possible feedback here is how we managed to address the issue. Seems to work so far, an cut down our time from 70 seconds to about 8 for a common test case.

To outline what the change does:
-- CTfxTabMdiWnd inherits from CExtTMWI
-- CTfxTabMdiWnd overrides the method OnHookWndMsg from the base class with what essentially is a clone or the base class method, with the important difference that instead of Posting a window message it starts a timer when a message would originally have been directly posted.
-- When the timer fires it will post the message instead.
-- There is also a class variable which keeps track on whether a timer has already been started, but not yet fired, and if so avoids creating another timer and thereby resetting the existing one (something which theoretically could have deferred updates indefinitely)

The net result is that the hordes (relatively speaking) of messages that earlier arrived in a rapid-fire fashion will now be aggregated into one single message with mostly no more than 10ms delay.



void CALLBACK ProcessUpdateTimer(
HWND hWnd, // handle of CWnd that called SetTimer
UINT nMsg, // WM_TIMER
UINT nIDEvent, // timer identification
DWORD dwTime // system time
)
{
    // setup MDI update timer
    PostMessage(hWnd,WM_TIMER,__EXTTAB_MDI_UPDATE_TIMER_ID,0);
    ((CTfxTabMdiWnd*)nIDEvent)->KillUpdateTimer();
}

void CTfxTabMdiWnd::KillUpdateTimer()
{
    KillTimer((UINT_PTR)this);
    m_bHasUpdateTimer = false;
}

#define __EXTTAB_MDI_TRIGGERUPATE_TIMER_ID 27
bool CTfxTabMdiWnd::OnHookWndMsg(LRESULT & lResult,HWND hWndHooked,UINT nMessage,WPARAM & wParam,LPARAM & lParam)
{
    //This code is a copy of CExtTabMdiWnd::OnHookWndMsg() with the exception for refined handling re posting of the update message.
    __PROF_UIS_MANAGE_STATE;
    if( CWnd::FromHandlePermanent(GetSafeHwnd()) == NULL )
        return
            CExtHookSink::OnHookWndMsg(
                lResult,
                hWndHooked,
                nMessage,
                wParam,
                lParam
                );
    if( hWndHooked == _GetHwndMdiArea() )
    {
        bool bDelayUpdateMdiTabCtrl = false;
        switch( nMessage )
        {
        case WM_WINDOWPOSCHANGING:
        {
            LPWINDOWPOS lpWindowPos =
                reinterpret_cast < LPWINDOWPOS > (lParam);
            ASSERT( lpWindowPos != NULL );
            lpWindowPos->flags |= SWP_FRAMECHANGED;
            break;
        }
        break;
        case WM_NCCALCSIZE:
        {
            OnMdiTabImplAdjustBorderSpaces();
            lResult = 0;
            NCCALCSIZE_PARAMS * pNCCSP =
                reinterpret_cast < NCCALCSIZE_PARAMS * > ( lParam );
            ASSERT( pNCCSP != NULL );
            DWORD dwWndStyle = GetStyle();
            bool bVisibleTab =
                ( (dwWndStyle & WS_VISIBLE) != 0 ) ? true : false;
            CRect rcMdiTabWnd( pNCCSP->rgrc[0] );
            DWORD dwOrientation = OrientationGet();
            rcMdiTabWnd.DeflateRect(
                (bVisibleTab && dwOrientation == __ETWS_ORIENT_LEFT) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DX_L,
                (bVisibleTab && dwOrientation == __ETWS_ORIENT_TOP) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DY_T,
                (bVisibleTab && dwOrientation == __ETWS_ORIENT_RIGHT) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DX_R,
                (bVisibleTab && dwOrientation == __ETWS_ORIENT_BOTTOM) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DY_B
                );
            rcMdiTabWnd.DeflateRect(
                m_rcReservedOuterSpace.left + m_rcReservedBorderSpace.left,
                m_rcReservedOuterSpace.top + m_rcReservedBorderSpace.top,
                m_rcReservedOuterSpace.right + m_rcReservedBorderSpace.right,
                m_rcReservedOuterSpace.bottom + m_rcReservedBorderSpace.bottom
                );
            ::CopyRect( &(pNCCSP->rgrc[0]), rcMdiTabWnd );
            return true;
        }
        break;
        case WM_NCPAINT:
        {
            lResult = 0;
            CWnd * pWndMdiArea = CWnd::FromHandle( _GetHwndMdiArea() );
            CRect rcMdiAreaWnd, rcMdiAreaClient;
            pWndMdiArea->GetWindowRect( &rcMdiAreaWnd );
            pWndMdiArea->GetClientRect( &rcMdiAreaClient );
            pWndMdiArea->ClientToScreen( &rcMdiAreaClient );
            if( rcMdiAreaWnd == rcMdiAreaClient )
                return true;
            CPoint ptDevOffset = -rcMdiAreaWnd.TopLeft();
            rcMdiAreaWnd.OffsetRect( ptDevOffset );
            rcMdiAreaClient.OffsetRect( ptDevOffset );
            rcMdiAreaWnd.DeflateRect(
                m_rcReservedOuterSpace.left,
                m_rcReservedOuterSpace.top,
                m_rcReservedOuterSpace.right,
                m_rcReservedOuterSpace.bottom
                );
            CWindowDC dcWindow( pWndMdiArea );
            ASSERT( dcWindow.GetSafeHdc() != NULL );
            CRect rcOuterBorder = rcMdiAreaWnd;
            rcMdiAreaWnd.DeflateRect(
                m_rcReservedBorderSpace.left,
                m_rcReservedBorderSpace.top,
                m_rcReservedBorderSpace.right,
                m_rcReservedBorderSpace.bottom
                );
            dcWindow.ExcludeClipRect( &rcMdiAreaClient );
            CExtMemoryDC dc( &dcWindow );
            dc.FillSolidRect( &rcOuterBorder, PmBridge_GetPM()->GetColor( COLOR_3DFACE, this ) );
            if( rcOuterBorder != rcMdiAreaClient )
                OnMdiTabImplDrawOuterBorder(
                    dc,
                    rcOuterBorder,
                    rcMdiAreaClient,
                    rcMdiAreaWnd,
                    hWndHooked
                    );
            DWORD dwWndStyle = GetStyle();
            bool bInvisibleTab =
                ( (dwWndStyle & WS_VISIBLE) == 0 ) ? true : false;
            DWORD dwOrientation = OrientationGet();
            rcOuterBorder.InflateRect(
                (bInvisibleTab || dwOrientation != __ETWS_ORIENT_LEFT) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DX_L,
                (bInvisibleTab || dwOrientation != __ETWS_ORIENT_TOP) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DY_T,
                (bInvisibleTab || dwOrientation != __ETWS_ORIENT_RIGHT) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DX_R,
                (bInvisibleTab || dwOrientation != __ETWS_ORIENT_BOTTOM) ? 0: __EXTTAB_MDI_NC_AREA_GAP_DY_B
                );
            PmBridge_GetPM()->PaintTabNcAreaRect(
                dc,
                rcOuterBorder,
                this
                );
            return true;
        }
        case WM_MDIDESTROY:
        {
            HWND hWndMdiChild = (HWND)wParam;
            if( hWndMdiChild != NULL )
            {
                LONG nIndex = ItemFindByHWND( hWndMdiChild, -1, true, true );
                if( nIndex >= 0 )
                {
                    m_bInSyncLayout = true;
                    ItemRemove( nIndex, 1, true );
                    m_bInSyncLayout = false;
                }
            }
            bDelayUpdateMdiTabCtrl = true;
        } // if( nMessage == WM_MDIDESTROY )
        break;
        case WM_MDIACTIVATE:
        {
            HWND hWndMdiChild = (HWND)wParam;
            if( hWndMdiChild != NULL )
            {
                LONG nIndex = ItemFindByHWND( hWndMdiChild, -1, true, true );
                if( nIndex >= 0 )
                {
                    m_bInSyncLayout = true;
                    SelectionSet( nIndex, true, true );
                    m_bInSyncLayout = false;
                }
            }
            bDelayUpdateMdiTabCtrl = true;
        } // else if( nMessage == WM_MDIACTIVATE )
        break;
        case WM_MDIREFRESHMENU:
        case WM_MDICREATE:
            bDelayUpdateMdiTabCtrl = true;
        break;
        } // switch( nMessage )
        if( bDelayUpdateMdiTabCtrl && !m_bHasUpdateTimer )
        {
            // Since we doe not have and MDI update timer we need to set it up, with a slight delay to aggregate subsequent messages rather than immediately posting it
            m_bHasUpdateTimer = true;
            SetTimer((UINT_PTR)this,__EXTTAB_MDI_UPDATE_TIMER_PERIOD,&ProcessUpdateTimer);
        }
    } // if( hWndHooked == _GetHwndMdiArea() )
    return
        CExtHookSink::OnHookWndMsg(
            lResult,
            hWndHooked,
            nMessage,
            wParam,
            lParam
            );
}