2

I am struggling with an issue which is raised by this piece of code:

    private int FPS = 60;

    void WebView_LoadCompleted(object sender, NavigationEventArgs e)
    {
        WebviewContentWorker();
    }

    private async void WebviewContentWorker()
    {
        WebViewBrush wvb = new WebViewBrush();
        wvb.SetSource(WebView);
        wvb.Redraw(); //we must redraw at least once before collapsing the WebView
        WebView.Visibility = Windows.UI.Xaml.Visibility.Collapsed;

        while (true)
        {
            webViewContent.Background = wvb; //webViewContent is a canvas
            await Task.Delay(1000 / FPS);
            wvb.Redraw();
        }
    }

What I'm trying to achieve here is to find a workaround for XAML's WebView which I find very sloppy. I want to be able to draw things on top of it but I can't so what I'm basically doing is taking snapshots of the WebView (using the WebViewBrush) repeatedly (based on the int FPS field) and then setting the Background property of the canvas named "webViewContent" with this snapshot. The aim is to have animations displayed on the canvas while still being able to draw on top of it (if I don't do these fast snapshots, the canvas will display a still image).

It is working fine now (I successfully redirect any Tapped event to the inside of the WebView so that clicks on buttons/links/... are properly handled) but it is somehow laggy. The slow bit being wvb.Redraw() I was wondering how I could improve the performance of my thread. It looks like the UI is responsive during the Task.Delay but is blocked otherwise...

Any input/advice is very welcome!

Edit: Here is how I timed the Redraw call (which I believe is what causes the problem since removing it makes the application very responsive):

        while (true)
        {
            webViewContent.Background = wvb;
            await Task.Delay(1000 / FPS);
            sw.Reset();
            sw.Start();
            wvb.Redraw();
            sw.Stop();
            System.Diagnostics.Debug.WriteLine(sw.Elapsed.TotalMilliseconds);
        }

Which gives me these results in the output window:

0,094
0,058
0,041
0,053
0,057
0,038
0,032
0,033
0,032
0,038
0,035
0,03
0,042
0,028
0,044
0,031
0,033
0,029
0,034
0,03
0,052
0,029

So not so much after all...

4
  • About how long does your Redraw method take to run? Also, is it really imperative to have 60 FPS? Can you lower it a bit without the display being too jerky? Commented Jun 20, 2013 at 15:21
  • I tried using StopWatch ealier today and the (double) Elapsed.TotalMilliseconds property returned values between 0,1XXX and 0,3XXX I believe. Commented Jun 20, 2013 at 15:37
  • If it's taking a third of a millisecond then I can't see how it would be blocking the UI for long enough to be perceptible by a person, even if done 60 times a second. That's still plenty of time for the UI's message loop to stay responsive. Perhaps you weren't properly timing it under the same conditions that you're actually using it under. Commented Jun 20, 2013 at 15:39
  • I edited my first post with the correct results and how I did timed the operation, have a look! Commented Jun 20, 2013 at 15:44

2 Answers 2

8

It looks like the UI is responsive during the Task.Delay but is blocked otherwise...

Well yes. That's exactly what's happening. Task.Delay is the only chance you're giving the UI thread to work. Your asynchronous method is executing on the UI thread - as soon as the "delaying" task completes, you'll end up with a continuation waiting to execute on the UI thread which will redraw and then delay again.

Fundamentally, if your Redraw method is too slow to be called ~60 times per second, you need a different approach.

It's very important to understand that async doesn't put the method onto a different thread - it just allows you to act asynchronously. (Your description and title suggest that you expected your method not to be using the UI thread for any significant time.)

Additionally, as Stephen Cleary says, using a DispatcherTimer is a generally-better way of executing code periodically on the UI thread.

Sign up to request clarification or add additional context in comments.

3 Comments

Thank you for the explanation, I indeed thought that async automatically built an FSM on a different thread. You wrote about finding a different approach, do you have any clues or ideas about that?
@RedPolygon: Well basically you need to either make Redraw faster or execute it less often - it's as simple as that. Or maybe punt some of the redraw work to a different thread if you can, but it's hard to know without more information.
Doing some of Redraw work in another thread would be great but I did not write the method so I'll see if I feel brave enough to dive into the MSIL (I just started learning WinRT). Let's hope that a better WebView will be released along with the 8.1, maybe we'll know more at the BUILD event next week. Thanks anyway for the advices.
2

Task.Delay is not particularly efficient for repeatedly doing many short timeouts. You'll generate a lot of garbage.

I would recommend using a dispatcher timer or similar for this scenario.

2 Comments

While I agree that Task.Delay isn't an ideal fit here, I wouldn't have expected the amount of garbage generated by Task.Delay to really be significant. It's only going to be called 60 times per second at most, after all... and the GC can handle really quite a lot of garbage...
Thank you for the tip, it works a bit better but it's still too much laggy. I've got a custom control on top of the canvas which you can move around and you can definetely feel the delay.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.