Results 1 to 4 of 4

Thread: [RESOLVED] The Cost of Refreshing

  1. #1

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Resolved [RESOLVED] The Cost of Refreshing

    This comes from an XNA/MonoGame specific question, which isn't something folks here seem to deal with all that much, but the underlying question has more to do with Windows and hardware than any specific language or technology, so there may be some insight from this group.

    I have a project that draws controls using XNA. While doing some profiling in a test app, I realized something interesting: I was paying a cost of 17ms every time I refreshed a control, but this was not a per control cost. For example, I had this test loop:

    Code:
            Private Sub Test1()
    		For x = 0 To 10
    			mTestControl.Refresh()
    			mTestControl2.Refresh()
    		Next
    	End Sub
    It didn't matter whether I had two controls being refreshed or just one, the cost was always the same. Each control does some moderately intensive composition, but I was able to isolate that and time it to take about 0.01ms, or thereabouts. This would be paid per control, but the difference between 0.01 and 0.02ms was being lost in the noise of the fact that the method was taking roughly 17ms per iteration (plus or minus 2ms, which is why 0.01 was lost in the noise).

    I then tried the same thing with a pair of standard WinForms buttons. These had a refresh cost of 0.5ms, or so, and that was per control, so refreshing two controls cost 0.1ms, and three would cost 0.15ms.

    A per cost control makes plenty of sense, to me, and there is one both for the standard WinForms button (0.5ms) and my composed XNA control (0.01ms). But what was that one time 17ms that was getting paid for refreshing the XNA controls?

    I commented out the whole body of that method, just to make sure that it WAS that code that was incurring the cost, and the cost dropped to 0, as it should (calling a function with no body takes virtually no time). I also changed the number of iterations from 10 to 1, then to 100. That had the impact expected. One iteration took 17ms (roughly), 10 took roughly 170ms, and 100 took roughly 1,700ms. These values remain the same whether I refresh one control or two.

    I then doubled the number of refreshes a different way:

    Code:
            Private Sub Test1()
    		For x = 0 To 10
    			mTestControl.Refresh()
    			mTestControl2.Refresh()
    			mTestControl.Refresh()
    			mTestControl2.Refresh()
    		Next
    	End Sub
    In other words, refresh each control twice. This doubled the cost to around 34ms. Commenting out both refreshes of mTestControl2 didn't change the cost at all.

    This kind of rules out the improbable answer that the system recognized that the two controls were identical (they are, but they are not the same control, they just have the same appearance) and just copied the drawing of the first for the second.

    I can't think of anything further to test. There's a cost for refreshing anything in XNA, and once you set out to refresh, it doesn't seem to matter how many things get refreshed.

    Would anybody venture an explanation as to what one time cost I could be seeing?
    My usual boring signature: Nothing

  2. #2
    Angel of Code Niya's Avatar
    Join Date
    Nov 2011
    Posts
    8,598

    Re: The Cost of Refreshing

    There is actually quite a lot happening when you call Refresh on a Control. Internally Control.Refresh makes two API calls. First it calls a .Net implementation of Invalidate to invalidate the control then a makes a call to the UpdateWindow API.

    What I found interesting is how all this is done. The RedrawWindow and UpdateWindow APIs both use Window Messages to handle redrawing the window, WM_PAINT being particularly prominent. Now the docs for UpdateWindow says that the WM_Paint message is posted directly to the window which makes me suspect that a call to Control.Refresh should lead directly to a call Paint event in the control which I am assuming you have XNA doing all the drawing. However, experience has taught me the hard way that what you expect Windows to do and what it actually does, on many occasions are two entirely different things.

    I cannot say for sure where your 17 ms is coming from but what I would have done is look more closely at what is happening here with these calls to RedrawWindow and UpdateWindow. I would single step the call to Control.Refresh to see if it code in the Paint event gets called before the function returns. If it doesn't, I'd then check to see when it gets called. If memory serves, I don't think Controls actually refresh themselves until the application's message loop is free to process WM_PAINT messages. It's been quite a while since I actually dealt with these nuts and bolts so I can't remember all the tiny details of how control painting works. But my instinct here is point towards the window messages sent by Control.Refresh. It's the only thing that can explain the consistent 17 ms despite the number of Controls being refreshed. I'm thinking that it's the one time cost being paid when your application's message loop actually gets around to processing the messages sent by Control.Refresh. The only thing that makes me doubt it is the fact that according to MS, UpdateWindow sends the message directly to the Window. But perhaps it doesn't mean what I think it does.

    In any case, I would have been trying different ways to refreshing using these APIs to see what happens.
    Treeview with NodeAdded/NodesRemoved events | BlinkLabel control | Calculate Permutations | Object Enums | ComboBox with centered items | .Net Internals article(not mine) | Wizard Control | Understanding Multi-Threading | Simple file compression | Demon Arena

    Copy/move files using Windows Shell | I'm not wanted

    C++ programmers will dismiss you as a cretinous simpleton for your inability to keep track of pointers chained 6 levels deep and Java programmers will pillory you for buying into the evils of Microsoft. Meanwhile C# programmers will get paid just a little bit more than you for writing exactly the same code and VB6 programmers will continue to whitter on about "footprints". - FunkyDexter

    There's just no reason to use garbage like InputBox. - jmcilhinney

    The threads I start are Niya and Olaf free zones. No arguing about the benefits of VB6 over .NET here please. Happiness must reign. - yereverluvinuncleber

  3. #3
    New Member
    Join Date
    Apr 2020
    Posts
    12

    Re: The Cost of Refreshing

    I imagine that if you measure the timing more precisely, it won't be 17 ms per refresh, but 16.7 ms. This is standard frame time for 60 fps display.

    On modern versions of Windows, WDM coalesces WM_PAINT requests to reduce power consumption. Displays that support more than 60 fps are still relatively rare "in the wild", so there's little point in Windows issuing more paint requests than one every "frame", or one every 16.7 ms.

    XNA may have ways around this, or workarounds through your video card settings may also be possible. See this StackExchange question, for example.

    (That said, you really shouldn't be refreshing more than 60 fps for reasons that are hopefully obvious.)

  4. #4

    Thread Starter
    Super Moderator Shaggy Hiker's Avatar
    Join Date
    Aug 2002
    Location
    Idaho
    Posts
    38,989

    Re: The Cost of Refreshing

    I hadn't done the calculation until I noticed that this thread had some replies. This is almost certainly a vsync issue just due to the timing. A 60 Hz refresh rate is what XNA targets, and all that is necessary. That means 16.7ms, as citation-needed pointed out. The timing was only in the vicinity of 17ms, because other things would vary the timing around that 16.7, which obscured that number, as would be expected in the WinForms event driven model (XNA is not normally event driven, but polling, so it would be more precise for this, but WinForms is event driven, so the timing will be fuzzier).

    I haven't quite 'solved' this, yet, but that's because I have been doing other things and haven't gotten back to that. Back when I first wrote this, I turned off v-sync because I wasn't seeing a difference. Moving from XNA to MonoGame is a pretty painless transition, but there ARE some changes, since I also moved from DX9 (which XNA used, as far as I can tell), to DX11, which is kind of the point of MonoGame. One of the changes is probably just enough that what once was smooth is not, now. Back then, I should have seen tearing, but didn't. I never fully understood why not. What is happening now is that I am seeing tearing, and whether or not I want to solve this is something I haven't quite decided on. The visual effect isn't all that bad.
    My usual boring signature: Nothing

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width