Re: CStatic - OwnerDraw Performance Problems
Joseph,
Wow. Thanks for taking the time. At the risk of sounding obsequious, let
me just say I'm a huge fan of your work. I use your book on Win32 all the
time; as well as Flounder.com and I also try to read all the great feedback
you give here....So, enough grovelling....It was starting me right in the
face (right after you pointed it out to me).
I'm a huge fan of tracing and assert but have gotten sloppy about thinking
about their impact of performance (a la Heisenberg)....It was the trace
message!
Average performance (for 10 runs) per paint, per control with ATLTRACE was
54 ms.
Without it was ATLTRACE, 1/10 ms. {Using tick counts -- wanted to use
inline assembly to time it but ARM processor doesn't support it}
Just for grins, I also timed using just vanilla CStatic controls and
SetWindowText().....
Average of 10 runs this was 3/10 ms.
Either the difference isn't statistically relevant (due to the grain of
GetTickCount()) or, if anything, is due to my using a local variable for the
string and not using Get/SetWindowText to access the control's text.
I also removed the unneeded ASSERT on m_hWnd. Also reviewed the message
pump but its clean. I'm not injecting anything into it and there's nothing
else dragging performance. It was entirely due to tracing. Obvious but one
of the things I guess I've aken for granted -- something this lesson proves
you really can't do with time critical blocks such as GDI.....
Thanks again Joseph et al....
tim
"Joseph M. Newcomer" <newcomer@flounder.com> wrote in message
news:4r8gg39t7ee29j9vil1hvm7ct4c45h26vb@4ax.com...
See below...
On Fri, 5 Oct 2007 18:35:30 -0700, "Tim" <usna91@msn.com> wrote:
All,
I'm having some trouble with an owner drawn static control. Basically the
performance is awful when I owner draw (Windows Mobile targeted app but
the
issue isn't specific to mobile).
Yes, performance totally depends on what you do. In my case even if I do
nothing more than write the text using DrawText(), adding more than a
couple
of my derived CStatic classes runs so slowly you can watch them being
individually drawn.
I have about 10 of them I'm adding to a form view to use to display data.
I
want to use them to do some additional formatting like changing color and
underlining them (the whole control not just the text). I know about
OnCtrlColor and have been using it. (Interesting thing is that using
either
OnCtrlColor or just subclassing them to CStatic and writing text both
perform very well with any number of instances. )But the moment I go to
doing my own paint it starts to run like molasses.
Here's the basic code:
{OK....code is WTL but almost exactly the same as the equivalent MFC - I'm
more looking to understand if I'm doing this correctly and WTL forums are
too sparse on this topic....thanks for understanding}
Background Erase Override:
LRESULT OnEraseBackground(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL&
bHandled)
{
ATLTRACE(_T("CColorStatic::OnEraseBackground()\n"));
return TRUE; // indicates we took care of drawing
}
Most simple OnPaint Override (that still sucks performance-wise):
LRESULT OnPaint(UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL& bHandled)
{
ATLTRACE(_T("CColorStatic::OnPaint());
****
Do you have any idea what an ATLTRACE call takes in terms of performance?
****
ATLASSERT(m_hWnd);
****
Unnecessary; you won't get here with a NULL window
****
CPaintDC dc(this->m_hWnd);
CRect rc;
GetClientRect(&rc);
// Set colors
COLORREF crOldBack = dc.SetBkColor(m_crBack);
COLORREF crOldText = dc.SetTextColor(m_crText);
// Draw
int oldMode = dc.SetBkMode(OPAQUE);
dc.FillSolidRect(&rc, m_crBack); // prevents bleed-thru behind text
dc.DrawText(m_text, m_text.GetLength(), rc, DT_LEFT);
// Clean up
dc.SetTextColor(crOldText);
dc.SetBkColor(crOldBack);
dc.SetBkMode(oldMode);
return TRUE; // indicates we took care of drawing
}
*****
There is nothing here that can cause a performance problem, EXCEPT the
possibility of the
ATLTRACE taking time.
****
Inside my form view, I subclass the controls. And I set the values,
forcing
a repaint, in another method based on user making a selection from a
combobox. As I said, just using a vanilla subclassed CStatic or
overriding
OnCtrlColor run very fast independent of the number of static controls.
But
even doing minimal GDI changes w/in OnPaint in the derived version just
sucks.
****
Remove the ATLTRACE and see if the performance is still a problem. The
code you show has
nothing that would cause a performance problem.
*****
I'm hoping that I'm just doing something stupid -- because I really want
to
be able to customize the darn thing.
I have also tried:
1) Not using SetWindowText() and GetWindowText() to add the text to write
and retrieve in OnPaint -- instead using a private member string with
Get/Set methods. No difference in performance.
****
That's what I'd expect.
****
2) I am currently inheriting from CStatic...... but I have also tried
inherting from CWindow (CWnd) with no real difference in performance.
****
That's what I'd expect
****
3) Casting the param passed in to hdc instead of using the this pointer in
the CPaintDC constructor. Also no difference....
****
I was wondering why you were doing it the hard way, but you said it was
WTL, so I was
cutting some slack here
****
4) Returning all four combinations of TRUE/FALSE from
OnEraseBackground/OnPaint -- I know what the return values are for but
someone else suggested returning FALSE was suggested in the SDK -- I doubt
that it was because FALSE would indicate that the window still needs to be
painted -- but I was desparate. Anyway, no difference in performance for
any combination....
****
That's what I'd expect
****
Would appreciate any insight. I have also tried converted the whole form
into an OwnerDrawn listbox -- and it performs better that way but you can
see a jitter as the rows are painted....I guess the final solution will be
to go to OwnerDrawn List control -- which I've found can draw anything
fast....
****
I have never seen a performance problem in code like this. I have no idea
what CE might
be doing, but the first thing to do with performance issues is to remove
trace statements.
Next, I'd try to measure performance. Add a counter to see how often it
is called, for
example; and if CE has a QueryPerformanceCounter equivalent, see if you
can measure actual
performance. Note: are you sure you don't have something that is blocking
responsiveness,
such as a sequence of PostMessage calls that are glutting up your message
pump? You have
not really defined "slow"; did you perhaps mean "non-responsive"? Try
forcing
UpdateWindow right after the Invalidate, which should force the painting
immediately,
instead of waiting for the WM_PAINT to be handled.
joe
****
But I digress....I think my current approach is be sound and I would hate
to
have to force another control to do this work....
cheers all,
tim
Joseph M. Newcomer [MVP]
email: newcomer@flounder.com
Web: http://www.flounder.com
MVP Tips: http://www.flounder.com/mvp_tips.htm