BeginPaint issues
-
I've created a custom control, using the low level win32 api, which displays an animation. Using a thread I render a frame, or rather update the parts need it, and call InvalidateRect passing the area that was changed. In the main thread I handle WM_PAINT with the BeginPaint EndPaint pair AND use the paintstruct's rect to repaint only the area required. The rendering is backed by a TimerQueue, an event and a thread that waits on the event. I couldn't work out why my CPU usage was hovering around 80%. Each frame involves a lot of alpha blending hence the need to only update the changed parts. After 2 days I've narrowed the problem to BeginPaint. MSDN states, and this is GDI 101, when handling a WM_PAINT message BeginPaint and EndPaint should be called to validate the invalid rect, otherwise windows will continue to post the message. Well, it's just not happening. The window has not been validated and my code has been rendering the full client area (lot's of work) on each call. If I add a ValidateRect in my handler the problem goes away and CPU down to 0-1%. My question is why? Is this a bug or am I missing something?
Waldermort
-
I've created a custom control, using the low level win32 api, which displays an animation. Using a thread I render a frame, or rather update the parts need it, and call InvalidateRect passing the area that was changed. In the main thread I handle WM_PAINT with the BeginPaint EndPaint pair AND use the paintstruct's rect to repaint only the area required. The rendering is backed by a TimerQueue, an event and a thread that waits on the event. I couldn't work out why my CPU usage was hovering around 80%. Each frame involves a lot of alpha blending hence the need to only update the changed parts. After 2 days I've narrowed the problem to BeginPaint. MSDN states, and this is GDI 101, when handling a WM_PAINT message BeginPaint and EndPaint should be called to validate the invalid rect, otherwise windows will continue to post the message. Well, it's just not happening. The window has not been validated and my code has been rendering the full client area (lot's of work) on each call. If I add a ValidateRect in my handler the problem goes away and CPU down to 0-1%. My question is why? Is this a bug or am I missing something?
Waldermort
WalderMort wrote:
a ValidateRect in my handler the problem goes away
I think that's the correct way. Don't know if it's a bug or not.
If your actions inspire others to dream more, learn more, do more and become more, you are a leader." - John Quincy Adams
You must accept one of two basic premises: Either we are alone in the universe, or we are not alone in the universe. And either way, the implications are staggering” - Wernher von Braun -
I've created a custom control, using the low level win32 api, which displays an animation. Using a thread I render a frame, or rather update the parts need it, and call InvalidateRect passing the area that was changed. In the main thread I handle WM_PAINT with the BeginPaint EndPaint pair AND use the paintstruct's rect to repaint only the area required. The rendering is backed by a TimerQueue, an event and a thread that waits on the event. I couldn't work out why my CPU usage was hovering around 80%. Each frame involves a lot of alpha blending hence the need to only update the changed parts. After 2 days I've narrowed the problem to BeginPaint. MSDN states, and this is GDI 101, when handling a WM_PAINT message BeginPaint and EndPaint should be called to validate the invalid rect, otherwise windows will continue to post the message. Well, it's just not happening. The window has not been validated and my code has been rendering the full client area (lot's of work) on each call. If I add a ValidateRect in my handler the problem goes away and CPU down to 0-1%. My question is why? Is this a bug or am I missing something?
Waldermort
Well, if the documentation says you have to do it and the experimentation shows that you have to do it, seems like it's working as designed. Not a bug, the rules.
-
WalderMort wrote:
a ValidateRect in my handler the problem goes away
I think that's the correct way. Don't know if it's a bug or not.
If your actions inspire others to dream more, learn more, do more and become more, you are a leader." - John Quincy Adams
You must accept one of two basic premises: Either we are alone in the universe, or we are not alone in the universe. And either way, the implications are staggering” - Wernher von BraunThe BeginPaint function automatically validates the entire client area.[^] So having to manually add a ValidateRect is not the correct way because BeginPaint should have done it for me.
Waldermort
-
Well, if the documentation says you have to do it and the experimentation shows that you have to do it, seems like it's working as designed. Not a bug, the rules.
Chuck O'Toole wrote:
Well, if the documentation says you have to do it and the experimentation shows that you have to do it, seems like it's working as designed. Not a bug, the rules.
But the documentation doesn't say that, infact it says that since I am calling BeginPaint there is no need to call ValidateRect. http://msdn.microsoft.com/en-us/library/dd145194(VS.85).aspx[^]
Waldermort
-
Chuck O'Toole wrote:
Well, if the documentation says you have to do it and the experimentation shows that you have to do it, seems like it's working as designed. Not a bug, the rules.
But the documentation doesn't say that, infact it says that since I am calling BeginPaint there is no need to call ValidateRect. http://msdn.microsoft.com/en-us/library/dd145194(VS.85).aspx[^]
Waldermort
Well, you don't show how you do the re-drawing of the area in your WM_PAINT processing. Where did you place the call to ValidateRect()? Immediately on entry or just before the EndPaint()? Is it possible that you are calling some other functions that cause the invalidate to happen (explicitly or implicitly) before the call to ValidateRect()?
-
Well, you don't show how you do the re-drawing of the area in your WM_PAINT processing. Where did you place the call to ValidateRect()? Immediately on entry or just before the EndPaint()? Is it possible that you are calling some other functions that cause the invalidate to happen (explicitly or implicitly) before the call to ValidateRect()?
It's too much code to simply paste here. You'll have to take my word for it that there is no way for the rect to be invalidated other than when I explicitly call for it. My code works like this. bitmap A is updated from a thread. Thread calls InvalidateRect when done updating a frame. bitmap B contains the window background and doesn't change. bitmap C is a double buffer. When WM_PAINT arrives, call BeginPaint, alpha blend bitmap A with B onto C and BitBlt bitmap C to the screen. When done call EndPaint. Like I said this is GDI 101, and I have never had a problem like this with previous versions of windows or Visual Studio. As it stands now, calling ValidateRect from anywhere in the WM_PAINT handler solves the problem. I currently have it after the call to EndPaint. Without this call, WM_PAINT is being sent from window creation and always with the full client area. Even though at that time my drawing routines have not yet begun. And yes I do call Begin End on EVERY WM_PAINT. On another note, passing the message onto DefWindowProc, after those two calls, also results in undesired behaviour. rectangles are being validated but not those that are in the paint struct. I'm really beginning to think this is a bug.
Waldermort
-
The BeginPaint function automatically validates the entire client area.[^] So having to manually add a ValidateRect is not the correct way because BeginPaint should have done it for me.
Waldermort
Hmpf! I stand corrected. I was going off of faint memory. Like I said, "I thought".
If your actions inspire others to dream more, learn more, do more and become more, you are a leader." - John Quincy Adams
You must accept one of two basic premises: Either we are alone in the universe, or we are not alone in the universe. And either way, the implications are staggering” - Wernher von Braun -
It's too much code to simply paste here. You'll have to take my word for it that there is no way for the rect to be invalidated other than when I explicitly call for it. My code works like this. bitmap A is updated from a thread. Thread calls InvalidateRect when done updating a frame. bitmap B contains the window background and doesn't change. bitmap C is a double buffer. When WM_PAINT arrives, call BeginPaint, alpha blend bitmap A with B onto C and BitBlt bitmap C to the screen. When done call EndPaint. Like I said this is GDI 101, and I have never had a problem like this with previous versions of windows or Visual Studio. As it stands now, calling ValidateRect from anywhere in the WM_PAINT handler solves the problem. I currently have it after the call to EndPaint. Without this call, WM_PAINT is being sent from window creation and always with the full client area. Even though at that time my drawing routines have not yet begun. And yes I do call Begin End on EVERY WM_PAINT. On another note, passing the message onto DefWindowProc, after those two calls, also results in undesired behaviour. rectangles are being validated but not those that are in the paint struct. I'm really beginning to think this is a bug.
Waldermort
Of course, if this were a newly introduced bug in Windows (not saying that it isn't) then we'd expect to see a lot more programs going into high cpu usage processing WM_PAINT messages. Theory goes that the EndPaint() call will take care of the ValidateRect() on the .rcPaint member of the PAINTSTRUCT you received in BeginPaint(). Is that the same thing you are using in your manual ValidateRect() call? Is there a possibility that it is being clobbered during your paint processing? If .rcPaint changed between Begin and End that would explain what you see. You say you use that member to do your re-drawing, read-only?
-
Of course, if this were a newly introduced bug in Windows (not saying that it isn't) then we'd expect to see a lot more programs going into high cpu usage processing WM_PAINT messages. Theory goes that the EndPaint() call will take care of the ValidateRect() on the .rcPaint member of the PAINTSTRUCT you received in BeginPaint(). Is that the same thing you are using in your manual ValidateRect() call? Is there a possibility that it is being clobbered during your paint processing? If .rcPaint changed between Begin and End that would explain what you see. You say you use that member to do your re-drawing, read-only?
Chuck O'Toole wrote:
Theory goes that the EndPaint() call will take care of the ValidateRect() on the .rcPaint member of the PAINTSTRUCT you received in BeginPaint(). Is that the same thing you are using in your manual ValidateRect() call? Is there a possibility that it is being clobbered during your paint processing? If .rcPaint changed between Begin and End that would explain what you see. You say you use that member to do your re-drawing, read-only?
BeginPaint() actually. Like you said though, if it were a bug... which makes me think there must be a problem in my code somewhere. Anyway, found it! Deep in there was a legacy debug routine to update the entire screen. Now to find out who made it and then... Thanks for the suggestions though.
Waldermort