Faster window/gadget redrawing! (Windows)

Share your advanced PureBasic knowledge/code with the community.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Faster window/gadget redrawing! (Windows)

Post by Rescator »

Code updated For 5.20+

There is a SmartWindowRefresh() but I do not really see any difference!

So while fooling around I noticed my resize routine for a program was
generating a whole bunch of erasebackground windows messages.

i was not just using ResizeGadget() but also
SendMessage_(GadgetID(#Gadget),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)
to make my listiconview look nice by automatically sizing the column header.

Sadly this mean that I ended up with twice as many erase background messages being recieved.

Then I came up with this solution...

At the start of your resize/redraw routine or procedure or whatever do this:

SendMessage_(WindowID(#Window),#WM_SETREDRAW,#False,0)

This will disable all redrawing/repainting of the window,
do all the resizing and column adjusting whatever you want now.

Then at the end when your done with all resize and other stuff,
do the following.

SendMessage_(WindowID(#Window),#WM_SETREDRAW,#True,0)
RedrawWindow_(WindowID(#Window),#Null,#Null,#RDW_INVALIDATE)

This will turn redrawing back on,
and then you invalidate the entire window to force a redraw.

This gives (at least me) the functionality that I expected from SmartWindowRefresh()
and I suspect it might also reduce the flicker some people experience.
Because if you do not disable te redrawing
and then do a bunch of gadget resizing or window resizing,
a redraw will occur for each command that modify a gadget or window.

Code: Select all

SendMessage_(WindowID(#Window),#WM_SETREDRAW,#False,0)

;put your gadget and window resizing and similar code here.

SendMessage_(WindowID(#Window),#WM_SETREDRAW,#True,0)
RedrawWindow_(WindowID(#Window),#Null,#Null,#RDW_INVALIDATE)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

PS! the same method should also work for gadgets directly.

i.e:

Code: Select all

SendMessage_(GadgetID(#Gadget),#WM_SETREDRAW,#False,0)

;put your gadget updating code here.

SendMessage_(GadgetID(#Gadget),#WM_SETREDRAW,#True,0)
RedrawWindow_(GadgetID(#Gadget),#Null,#Null,#RDW_INVALIDATE)
You may or may not need the RedrawWindow_()

I found doing this for gadget's speed up the filling up of really huuge listviews/listiconviews with thousands of lines.
Otherwise the gadget would update each time you add a list item. *ugh*
Dare2
Moderator
Moderator
Posts: 3321
Joined: Sat Dec 27, 2003 3:55 am
Location: Great Southern Land

Post by Dare2 »

Thank you for this approach.
@}--`--,-- A rose by any other name ..
Edwin Knoppert
Addict
Addict
Posts: 1073
Joined: Fri Apr 25, 2003 11:13 pm
Location: Netherlands
Contact:

Post by Edwin Knoppert »

Afaik MSDN states that the listview should not be treated this way.
Fred
Administrator
Administrator
Posts: 18252
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

WM_REDRAW is known to have serious problems when coming to reduce the flickering. Why SmartWindowRefresh() doesn't work for you (if you use panels, that's for sure) ? Do you have a working example of your approach ?
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Edwin Knoppert wrote:Afaik MSDN states that the listview should not be treated this way.
From PSDK:
WM_SETREDRAW
This message can be useful if an application must add several items to a list box. The application can call this message with wParam set to FALSE, add the items, and then call the message again with wParam set to TRUE. Finally, the application can call the InvalidateRect function to cause the list box to be repainted.

So I'll probably have to use InvalidRect instead of RedrawWindow for the gadgets.

but, if the MSDN state something else, then what should I use instead?
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Fred wrote:WM_REDRAW is known to have serious problems when coming to reduce the flickering. Why SmartWindowRefresh() doesn't work for you (if you use panels, that's for sure) ? Do you have a working example of your approach ?
Well other than some of those minor glitches that was reported about SmartWindowRefresh() for previous betas it seems to work.

The main problem here is, what is SmartWindowRefresh() ment to do?
Documentation says nothing and I see no difference if I use it or not!
*scratches head*

If I knew what to look for that is ment to be different then that would help :)

*goes to start the xp theme service to test stuff*
Yeah, I actualy run with the service shut down normaly :)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

I had to actually start the Themes service,
and turn on showing of contents while dragging the window.

I saw a slight improvement using SmartWindowRefresh() then,
but I saw even better improvement if I also used
SendMessage_(WindowID(window),#WM_SETREDRAW,#False,0)
at start of my refresh procedure,
and
SendMessage_(WindowID(window),#WM_SETREDRAW,#True,0)
at end of it,
and at the very end of it all I added a UpdateWindow_(WindowID(#Window)

This seemingly gets rid of all redrawing glitches fully.
The only problem now is that windows (at least my system) is not quite able
to keep the framerate up as I scale the size of the window.
Very now and again what is behind it shows, but usually just on the right hand side and near bottom.
But the window itself has zero flickering thanks to the various tricks I just mentioned.
Also, moving the window is rock steady, not even the framerate issues resizing has.

Kinda ironic really, I never used Themes service, I never turn on show contents while dragging window either.
And yet I'm wasting a day or two, to make tweaks for it. *sigh*

*kicks MicroSoft in the nuts for "inventing" that feature*

:lol:
Fred
Administrator
Administrator
Posts: 18252
Joined: Fri May 17, 2002 4:39 pm
Location: France
Contact:

Post by Fred »

Please post a code so we can all show ;).
Edwin Knoppert
Addict
Addict
Posts: 1073
Joined: Fri Apr 25, 2003 11:13 pm
Location: Netherlands
Contact:

Post by Edwin Knoppert »

I must have been mistaken somehow, MSDN:
If you are adding a large number of items to a list view control, you can speed up the process by disabling redrawing before adding the items, then enable redrawing after the items are added. Use the WM_SETREDRAW message to enable and disable redrawing.
Usually the combination InValidateRect() and UpdateWindow() should do.
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

(repeat of the post in the bug thread, sorry for double posting it)

Fred, you wanted an example. here is one :P

This is what I have to do to get these listsiconviews to be flicker free.
Please note as I entioned elsewhere here that this is still imperfect.
There appears to be a framerate issue, but at least the flickering is gone.
In the loop change MainWindow_Resize(#True) to MainWindow_Resize(#False) to turn off my "trick".

At least on my system using just SmartWindowRefresh() or not using it at all
causes the lists to flicker, esp. the scrollbars seem to flicker if you size the window slowly.

Obviously this only happens with "solid" windows, classic look has hardly any gfx issues. :)

If anyone know how to get rid of that list little glitch (low frame rate for refrash)
I'd be very happy, but I can actualy live with this now.
SmartWindowRefresh() plus my tweaks as seen in this example,
and things are pretty darn flicker free here.

Hopefully the rest of you get similar results!

Code: Select all

Procedure MainWindow_Resize(test=#False)
Protected ww.l,wh.l,w.l,h.l,x.l,y.l

 If test=#True
  ;disable redrawing for window 0
  SendMessage_(WindowID(0),#WM_SETREDRAW,#False,0)
 EndIf

 ww=WindowWidth(0)
 wh=WindowHeight(0)

 w=ww/4
 h=(wh/2)
 x=0
 y=0
 ResizeGadget(0,x,y,w,h)
 SendMessage_(GadgetID(0),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(1,x,y,w,h)
 SendMessage_(GadgetID(1),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(2,x,y,w,h)
 SendMessage_(GadgetID(2),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x=GadgetX(2)+GadgetWidth(2)
 w=ww-x
 ResizeGadget(3,x,y,w,h)
 SendMessage_(GadgetID(3),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 y=h
 x=0
 w=ww/4
 h=wh-h
 ResizeGadget(4,x,y,w,h)
 SendMessage_(GadgetID(4),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(5,x,y,w,h)
 SendMessage_(GadgetID(5),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(6,x,y,w,h)
 SendMessage_(GadgetID(6),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x=GadgetX(6)+GadgetWidth(6)
 w=ww-x
 ResizeGadget(7,x,y,w,h)
 SendMessage_(GadgetID(7),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 If test=#True
  ;enable redrawing for window 0 again
  SendMessage_(WindowID(0),#WM_SETREDRAW,#True,0)
  ;invalidate the entire window, erase background and repaint
  RedrawWindow_(WindowID(0),#Null,#Null,#RDW_INVALIDATE|#RDW_UPDATENOW|#RDW_ERASE)
 EndIf
EndProcedure

 w_width=800
 w_height=600

 OpenWindow(0, 0, 0, w_width,  w_height, "", #PB_Window_Invisible|#PB_Window_MaximizeGadget|#PB_Window_MinimizeGadget|#PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_SizeGadget)
 CreateGadgetList(WindowID(0))
 SmartWindowRefresh(0, 1)
 
 params=#LVS_EDITLABELS|#LVS_SORTASCENDING|#LVS_NOSORTHEADER|#PB_ListIcon_FullRowSelect
 w=w_width/4
 h=(( w_height-MenuHeight())/4)
 x=0
 y=0
 ListIconGadget(0,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(0,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(0,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(1,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(1,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(1,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(2,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(2,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(2,#PB_Gadget_FrontColor,0)

 x=GadgetX(2)+GadgetWidth(2)
 w=w_width-x
 ListIconGadget(3,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(3,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(3,#PB_Gadget_FrontColor,0)

 y=GadgetY(0)+GadgetHeight(0)
 x=0
 w=w_width/4
 h=(w_height-MenuHeight())-y
 ListIconGadget(4,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(4,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(4,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(5,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(5,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(5,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(6,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(6,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(6,#PB_Gadget_FrontColor,0)

 x=GadgetX(6)+GadgetWidth(6)
 w=w_width-x
 ListIconGadget(7,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(7,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(7,#PB_Gadget_FrontColor,0)

 HideWindow(0,0)

 SetActiveGadget(0)

 Repeat
   Select WaitWindowEvent()
     Case #PB_Event_SizeWindow
       MainWindow_Resize(#True) ;change this to #False to turn of my repaint "trick"
     Case #PB_Event_CloseWindow
       Break
   EndSelect
 ForEver
EDIT:
PS! There is no way to implement some of those tweaks into SmartWindowRefresh() I assume?

WHat if you also added a repaint callback similar to how the window callback is.
That way we could just create a procedure, do all our resising/calculations etc. in that.
And each time PB gets a paint of redraw event, it will just call that callback procedure.
It certainly would be very cpu efficient and system friendly to do it this way or?
So that you could disable repaint at start and do a paint at the end of the callback internally in PB.

EDIT:
Either something like that or my idea in http://www.purebasic.fr/english/viewtopic.php?t=20531
I assume Linux and Mac do NOT have have this issue?
But if we can get near flicker free
(and my code above is pretty close except for the slow "frame rate")
you will definetly give PB a major edge over other development environmets :)
User avatar
Rescator
Addict
Addict
Posts: 1769
Joined: Sat Feb 19, 2005 5:05 pm
Location: Norway

Post by Rescator »

Fred, I think I finaly nailed it! And it was even simpler than I thought!

LockWindowUpdate_() is the magic plus UpdateWindow_().
It seems the framerate glitch is gone now.
Also notice that SmartWindowRefresh() is still needed to have it all flicker free.

Amazing what one can find lurking around in the PSDK :lol:
Any chance of native functions for those two Fred?
I don't care even if they are just simple wrappers,
it sure would make things easier for newbies that suffer the flicker syndrome :P

Code: Select all

Procedure MainWindow_Resize(test=#False)
Protected ww.l,wh.l,w.l,h.l,x.l,y.l

 If test=#True
  ;disable updating for window 0
  LockWindowUpdate_(WindowID(0))
 EndIf

 ww=WindowWidth(0)
 wh=WindowHeight(0)

 w=ww/4
 h=(wh/2)
 x=0
 y=0
 ResizeGadget(0,x,y,w,h)
 SendMessage_(GadgetID(0),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(1,x,y,w,h)
 SendMessage_(GadgetID(1),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(2,x,y,w,h)
 SendMessage_(GadgetID(2),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x=GadgetX(2)+GadgetWidth(2)
 w=ww-x
 ResizeGadget(3,x,y,w,h)
 SendMessage_(GadgetID(3),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 y=h
 x=0
 w=ww/4
 h=wh-h
 ResizeGadget(4,x,y,w,h)
 SendMessage_(GadgetID(4),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(5,x,y,w,h)
 SendMessage_(GadgetID(5),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x+w
 ResizeGadget(6,x,y,w,h)
 SendMessage_(GadgetID(6),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 x=GadgetX(6)+GadgetWidth(6)
 w=ww-x
 ResizeGadget(7,x,y,w,h)
 SendMessage_(GadgetID(7),#LVM_SETCOLUMNWIDTH,0,#LVSCW_AUTOSIZE_USEHEADER)

 If test=#True
  ;enable redrawing for window 0 again, then update it
 LockWindowUpdate_(0)
 UpdateWindow_(WindowID(0))
 EndIf
EndProcedure

 w_width=800
 w_height=600

 OpenWindow(0, 0, 0, w_width,  w_height, "", #PB_Window_Invisible|#PB_Window_MaximizeGadget|#PB_Window_MinimizeGadget|#PB_Window_SystemMenu|#PB_Window_TitleBar|#PB_Window_SizeGadget)
 CreateGadgetList(WindowID(0))
 SmartWindowRefresh(0, 1)
 
 params=#LVS_EDITLABELS|#LVS_SORTASCENDING|#LVS_NOSORTHEADER|#PB_ListIcon_FullRowSelect
 w=w_width/4
 h=(( w_height-MenuHeight())/4)
 x=0
 y=0
 ListIconGadget(0,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(0,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(0,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(1,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(1,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(1,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(2,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(2,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(2,#PB_Gadget_FrontColor,0)

 x=GadgetX(2)+GadgetWidth(2)
 w=w_width-x
 ListIconGadget(3,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(3,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(3,#PB_Gadget_FrontColor,0)

 y=GadgetY(0)+GadgetHeight(0)
 x=0
 w=w_width/4
 h=(w_height-MenuHeight())-y
 ListIconGadget(4,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(4,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(4,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(5,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(5,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(5,#PB_Gadget_FrontColor,0)

 x+w
 ListIconGadget(6,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(6,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(6,#PB_Gadget_FrontColor,0)

 x=GadgetX(6)+GadgetWidth(6)
 w=w_width-x
 ListIconGadget(7,x,y,w,h,"LIST: 0",100,params)
 SetGadgetColor(7,#PB_Gadget_BackColor,RGB(255,255,213))
 SetGadgetColor(7,#PB_Gadget_FrontColor,0)

 HideWindow(0,0)

 SetActiveGadget(0)

 Repeat
   Select WaitWindowEvent()
     Case #PB_Event_SizeWindow
       MainWindow_Resize(#True) ;change this to #False to turn of my repaint "trick"
     Case #PB_Event_CloseWindow
       Break
   EndSelect
 ForEver
Edwin Knoppert
Addict
Addict
Posts: 1073
Joined: Fri Apr 25, 2003 11:13 pm
Location: Netherlands
Contact:

Post by Edwin Knoppert »

I didn't knew it was using LockWindowUpdate()
Under w95 and 98 (and maybe higher) it results in annoying desktop flashes (redrawing shortcuts and so).
Therefore i consider lwu as unusable, unless if a user would have maximized it's window.

What i ever did was resetting the WS_VISIBLE style.
The controls has the idea it's invisible while it still shows :)
Post Reply