refresh problem

  1. 3 years ago

    michel b

    15 Jun 2016 Pre-Release Testers France

    Hello,

    When I update a large number of objet in a window ( texfield or canvas backdrop for example), in Windows the display is very slow and we can see the objects refresh one after the other unlike to OSX where the refresh is immediate and general.

    Is there a way to solve this in Windows ?

    Thanks

    I believe that you could send a WM_SETREDRAW message to the window using the Win32 API, in order to block repaints prior to updating. A second WM_SETREDRAW message after the update will re-enable the window repaint. Your window will repaint much faster. WM_SETREDRAW has parameters to stop or start window redraws.

    Here is one relevant link .

  2. Dave S

    15 Jun 2016 San Diego, California USA

    are you using "object.refresh" or "object.invalidate"?

    if using REFRESH, change them all to INVALIDATE

    Refresh "attempts" to update the object the instant it gets that command, so if you have "a large number", they may be competing for resources, or redrawing multiple times depending on how they are laid out.

    INVALIDATE, marks each object as "dirty", and the update will occur at the next run cycle

  3. michel b

    15 Jun 2016 Pre-Release Testers France

    >>are you using "object.refresh" or "object.invalidate"?
    No

    >>INVALIDATE, marks each object as "dirty", and the update will occur at the next run cycle
    so if i invalidate objects i can change properties without updating them and update all the object with a refresh after ?
    If yes is it working with a container control ?

  4. Dave S

    15 Jun 2016 San Diego, California USA

    DO NOT USE "REFRESH"... let the system decide when based on the use of INVALIDATE

    Invalidate say "hey System, when you have a microsecond, this control could use an visual update"
    Refresh says "HEY SYSTEM!!! Stop what you are doing, and redraw this control NOW!!!" [this is NOT a good idea]

  5. michel b

    15 Jun 2016 Pre-Release Testers France

    ok so my problem still the same.

    For example :
    Take 30 canvas with backdrop, make a loop to change all the backdrop (without any specific refresh command).
    In OSX you see all the picture changing together in a clink.
    In Windows, the picture change one after another and it's awful.

    Is there a way on windows not to update the picture in the loop but only at the end of the loop.

  6. Louis D

    15 Jun 2016 Pre-Release Testers, Xojo Pro Answer Montreal, QC, Canada

    I believe that you could send a WM_SETREDRAW message to the window using the Win32 API, in order to block repaints prior to updating. A second WM_SETREDRAW message after the update will re-enable the window repaint. Your window will repaint much faster. WM_SETREDRAW has parameters to stop or start window redraws.

    Here is one relevant link .

  7. michel b

    18 Jun 2016 Pre-Release Testers France

    Thanks a lot Louis, this is working very well for me. You made my day.

  8. Michel B

    18 Jun 2016 Pre-Release Testers RubberViews.com
    Edited 3 years ago

    For completeness, Brock Nash posted a nicely compact declare here :
    https://forum.xojo.com/6484-tip-removing-flicker-on-windows/14

  9. 4 months ago

    Frank S

    Feb 1 Australia

    Hi,

    I know that this is an old thread, but I was hoping someone could please walk me through how to implement Brock Nash's declare properly? I have incorporated a calendar date picker into one of my projects, from the example project Examples > Desktop > Custom Controls > CalendarWindow. On Mac, when you change the month or year, the day buttons update instantaneously but on Windows you see the buttons change sequentially.

    I have declared Brock Nash's method in a global module:

    Sub AllowRedraw (extends w as Window, allow as Boolean)
    #If TargetWindows
      Const WM_SETREDRAW = &h000B
      Declare Sub SendMessage Lib "User32" alias "SendMessageW" (hwnd As Integer, msg As Integer, wParam As Boolean, lParam As Integer)
      If allow Then
        SendMessage(w.handle, WM_SETREDRAW, True, 0)
        w.Refresh
      Else
        SendMessage(w.handle, WM_SETREDRAW, False, 0)
      End
    #EndIf
    End Sub

    In the Update method of the Calendar Window I have surrounded the update loop with calls to the AllowRedraw function:

     Me.AllowRedraw(False)
    
    // Update all the captions of the buttons and hide as appropriate
    Dim dayOfWeek As Integer
    dayOfWeek = mSelectedDate.DayOfWeek
    
    Dim dayNum As Integer
    For calendarButton As Integer = 0 To 36
      DayNum = calendarButton + 2 - dayOfWeek
      If dayNum > 0 And dayNum <= monthDays Then
        CalendarDateButton(calendarButton).Caption = Str(dayNum)
        CalendarDateButton(calendarButton).Visible = True
      Else
        CalendarDateButton(calendarButton).Visible = False
      End If
    Next
    
    Me.AllowRedraw(True)

    I was really hopeful that this would do the trick, but the buttons still update sequentially. Could someone point out what the error is? Does it matter that the calendar window is shown as a modal window?

    Thanks. Frank

  10. Julian S

    Feb 1 Pre-Release Testers, Xojo Pro UK

    Here you go Frank. I used the example you mentioned above, added a timer in a tight loop to slow things down enough to see the draw delay then implemented the freeze. Search for JHS in the code to see where I put things.

    https://www.dropbox.com/s/u5wcggd8jwii9gw/TestForFrankWM_SETREDRAW.xojo_binary_project?dl=1

    The only problem you have doing it this way is that the window can allow the mouse clicks to pass through right as the window is frozen so if you spam the > fast enough you can click through the window to something behind, like another window, thus potentially bringing that forward. To get around this, put all the controls on a canvas, container control, group box or other such "container type control" and call the freeze on that containing control rather than the whole window then your clicks will only pass through to the window which wont cause potential issues of click through. If you have a fast running thread that is causing UI redraw delays it might be best to set its priority down and/or Sleep that for 100ms while you open the window or perform button clicks on the window, this will also aid the speed of the UI updates.

    I did a lot of testing on this just before Christmas when fixing a redraw issue for someones project, I have a whole big post/article/video in the works on this when I can muster the motivation to finish it off.

    Let me know if you have any questions.

  11. 3 months ago

    Frank S

    Feb 3 Australia
    Edited 3 months ago

    Hi Julian,

    Thanks so much for taking the time to make the demo for me. The way you labelled the changes makes it very easy to follow what you'd done. However, when I run your demo, the window freezing doesn't seem to be working, I can still see the buttons change sequentially - more so now because you added in the thread to slow things down! Here is a video showing what I see when I run your demo:

    https://www.dropbox.com/s/najqwtcid8i73pt/CalendarRedrawLag.mov?dl=0

    I am running the demo remotely from my macbook, macOS Mojave, using Xojo 2018r3, and sending it to a Windows 10 virtual machine running on Parallels Desktop. I have also tried building the demo and running it on the Windows 10 virtual machine, with the same result.

    Do you think it is because I'm running it through virtualization software that it doesn't look as expected? I will try it on a real Windows PC tomorrow and see that makes a difference.

  12. Julian S

    Feb 3 Pre-Release Testers, Xojo Pro UK

    Ah, you're seeing finer issues than I thought, ok, that will need another level of code breakdown, where to start...

    1) Adding system.DebugLog(CurrentMethodName) to the top of CalendarWindow.Update shows that it is called 5!! times during the window Open.

    2) When pressing the arrows, there's multiple updates, which is what you're seeing. There are 2 update when you move one month and 4!! when you move across a year boundary.

    Both of these problems should really be coded out but to get around the issue we need to do a little work. VMs will make this more visible as will hidpi, the more work that needs to be done, the more visible the problems are.

    Getting the freeze on the window at the right time is a little tricky with ShowModal so I've Interfaced that and put the freeze in there. I had to do it this way to get around not being able to step into the framework at the required time, a little more thought might provide an alternative method but this is my current thinking at the moment.

    I then wrapped the button < > clicks in freezes so we didn't have to consider refactoring the control, this will freeze during the entire duration of all updates.

    I refactored AllowRedraw to only trigger on the first and last call so any recursion wasn't prematurely unfreezing on its way out of the recursion until it got to the last unfreeze which was allowed to take place.

    I think that about covers it all.

    I'm not being negative/nasty here but unfortunately most of all this boils down to quick/sloppy/untidy (call it what you will) code. You can get away with so many things on macos because everything is double buffered, you just don't notice things like this (the multiples updates per click). As Windows isn't double buffered by default you have to be a little more precise with things.

    I've done two examples for you, one using the controls on a window and the other with the controls on a canvas. The later will not suffer from random mouse click-throughs to background windows as the canvas is being frozen rather than the window.

    It sure would be nice if all this was taken care of in the framework.

    Controls On Window
    https://www.dropbox.com/s/mv38dxugye13jli/TestForFrankWM_SETREDRAW2.xojo_binary_project?dl=1
    Controls On Canvas
    https://www.dropbox.com/s/hkrh8484op42did/TestForFrankWM_SETREDRAW3.xojo_binary_project?dl=1

    You might need to do a little work to make these macos safe as I've not really been keeping track.

  13. Frank S

    Feb 12 Australia
    Edited 3 months ago

    Thanks again Julian for going to all the trouble to make the various demos. I can see the performance difference between the versions. I've tried to work my way through them to follow how it works, but as you say, it's all a bit messy and was hard for me to figure out exactly what's going on. So I thought I should first try to make a simpler example to experiment on. I've taken your SETREDRAW2 example and created a simpler scenario:

    I put a control set of 32 canvases on the main window and in the paint event, put g.forecolor = RectColor (a window property) and put g.drawfillrect. When the window first opens, you can see the canvases paint sequentially. I've played around with using self.AllowRedraw(false) and self.AllowRedraw(true) in the window open event and I've been surprised - and confused! - by the results.

    If I use self.AllowRedraw(false), the window still draws the canvases (slowly) - I thought this should stop the window from drawing.
    If I use self.AllowRedraw(true), the window draws the canvases quickly - but why?
    Also, if I use self.AllowRedraw(true), but comment out the "If allow Then w.Refresh(false)" then the window doesn't draw at all.

    I've also added a button that changes the color of the canvases and again AllowRedraw(true) can be used to make it faster.

    Using WM_SetRedraw is obviously much more nuanced than I had imagined, it doesn't work how I imagined it would - though my limited knowledge comes entirely from https://docs.microsoft.com/en-au/windows/desktop/gdi/wm-setredraw .

    Here is my project:
    https://www.dropbox.com/s/2e94kjk1qbiqx71/WM_SETREDRAWNewTest.xojo_binary_project?dl=0
    I've followed your example and labelled all my additions with "// Frank"

    Is it possible to explain to me this behaviour? Any help would be greatly appreciated.

  14. Julian S

    Feb 12 Pre-Release Testers, Xojo Pro UK
    Edited 3 months ago

    @Frank S If I use self.AllowRedraw(true), the window draws the canvases quickly - but why?

    If you put this code on its own inside Window.Open then it will force the Window to be visible before the Xojo framework has actually made it visible (to see this put a breakpoint on SendMessage and step once to see the window open). This is a side effect of using WM_SETREDRAW and is documented on the MSDN page for this message. When the window is made visible no messages are being processed as we're in the middle of a method so nothing is drawn on the window. As we move onto the next line which calls the Refresh that refresh will not allow any superfluous code execution until the redraw has taken place which is why you don't see any slow down in the paint.

    @Frank S Also, if I use self.AllowRedraw(true), but comment out the "If allow Then w.Refresh(false)" then the window doesn't draw at all.

    That is because the window has been opened outside of the framework and no paints will take place as the framework (I assume) doesn't do anything because it doesn't go through the motions of finalising the open.

    @Frank S I've also added a button that changes the color of the canvases and again AllowRedraw(true) can be used to make it faster.

    That doesn't need to be there, all you need to do it call Self.Refresh(false). When you say this is slow, do you mean that you can just slightly see the cavnas' paint or is it as slow as the delay from the thread? I only see the slight canvas paints, if this is the case for you then that is as good as it will get I'm afraid. The framework needs some time to process those paint events and draw the squares, if this wasn't a test, you would do this all as a single canvas then you wouldn't have those issues. You would only benefit from freezing here if you made more complex changes to the controls, size, position, number of them etc.

    I see you're been playing around with LockWindowUpdate, don't use it to stop this type of window redraw.

    If you want the fastest, safest, and more cross-platform route, do away with the vast number of controls and use a single canvas. Double buffer your updates manually, so you actually draw them to a picture rather than drawing them in the paint event, call an invalidate which would fire a paint at which time you draw that picture onto the canvas inside the paint event. You can then process vast amount of updates without causing visual artefacts or UI slow downs and at the worst "cpu moments", like during a window resize, all the window has to do is paint that picture onto the window, no complicated calculations have to take place during the paint. I wish more people would think of doing things this way as it would alleviate a lot of the "gloopy" window issues I see. If you then get super adventurous and implement your canvas.paint to take into consideration the areas passed in, you can just call canvas.refreshrect and only refresh the exact parts you need to update saving even more time and improving speed of things, this will also allow paints to be quicker during general use a areas are quite often send to the paint but people just redraw the whole canvas anyway.

  15. Frank S

    Feb 15 Australia

    Another enlightening post, cheers! Double buffering, how frameworks interact... This is all above my current level of knowledge, but I'll work on it :) You've given me enough to start on. I'll see if I can implement this in my project to achieve what I want - rather than you having to keep spoonfeeding me. Much appreciated!

or Sign Up to reply!