The Wayback Machine - https://web.archive.org/web/20200914065644/http://github.com/microsoft/Win2D/issues/612
Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ComException 0x80004005 thrown by CanvasVirtualControl Invalidate() if there is an active CanvasDrawingSession #612

Closed
mglogan opened this issue May 22, 2018 · 3 comments
Labels

Comments

@mglogan
Copy link

@mglogan mglogan commented May 22, 2018

I am using a background thread to update a CanvasVirtualControl because the redraw time has started to affect the UI thread (there are a lot of complex objects and text).

I am using option 1 from the CreateDrawingSession remarks:

On an arbitrary non-UI thread:
CreateDrawingSession
Issue drawing calls
SuspendDrawingSession(CanvasDrawingSession)
Back on the UI thread:
drawingSession.Dispose()

When the update is lightweight, everything seems to be fine. Once the update starts to consume time, I start getting ComException 0x80004005. I get the exception consistently by calling Invalidate while the drawing session is open.

Below are the page SizeChanged method, the RegionsInvalidated method (which enqueues the invalid rectangles and verifies the background task is running) and the actual redraw method.

    private void Page_SizeChanged(object sender, SizeChangedEventArgs e)
    {
        scalingFactor = (float)Math.Max(1F, Math.Min(e.NewSize.Height / 20, e.NewSize.Width / 40));

        lock (((System.Collections.ICollection)redrawRegions).SyncRoot)
        {
            redrawRegions.Clear();
        }

        try
        {
            MyCanvas.Invalidate(); // <== this will throw a COMException 0x8004005 if a redraw is active
        }
        catch (Exception ex )
        {
            throw;
        }
    }

    private void BackgroundCanvasRedraw(CanvasVirtualControl sender)
    {
        bool keepGoing = true;
        while ( keepGoing )
        {
            Rect r;
            lock (((System.Collections.ICollection)redrawRegions).SyncRoot)
            {
                if ( redrawRegions.Count > 0 )
                {
                    r = redrawRegions.Dequeue();
                }
                else
                {
                    keepGoing = false;
                }
            } // lock

            if (keepGoing)
            {
                CanvasDrawingSession cds;
                try
                {
                     cds = sender.CreateDrawingSession(r); // Step 1, Create drawing session
                }
                catch( System.ArgumentException ) 
                { // rect no longer fits inside current canvas / canvas must have changed size
                    cds = null;
                }

                if ( cds != null )
                {
                    Matrix3x2 m = Matrix3x2.CreateScale(scalingFactor);
                    cds.Transform = m;

                    cds.FillCircle(10, 10, 10, Colors.Red ); // Step 2, drawing calls

                    // comment out the below line and it works
                    Task.Delay(1000).Wait(); // simulate heavy computation / lots of drawing calls

                    cds.FillCircle(20, 10, 10, Colors.Blue); // last drawing call

                    sender.SuspendDrawingSession(cds); // Step 3, suspend

                    IAsyncAction aa = Windows.ApplicationModel.Core.CoreApplication.MainView.CoreWindow.Dispatcher.RunAsync(Windows.UI.Core.CoreDispatcherPriority.Normal,
                                 () => { cds.Dispose(); }); // Step 4, dispose on UI thread

                    aa.AsTask().Wait();  // Is it bad to open a new drawing session before the previous is disposed?  Guarantee completion for now
                }
            }
        }
    }

    private void MyCanvas_RegionsInvalidated(CanvasVirtualControl sender, CanvasRegionsInvalidatedEventArgs args)
    {
        lock (((System.Collections.ICollection)redrawRegions).SyncRoot)
        {
            foreach (Rect r in args.InvalidatedRegions)
            {
                redrawRegions.Enqueue(r);
            }
        }

        if ( tBackgroundRedraw.IsCompleted )
        {
            tBackgroundRedraw = Task.Run( () => BackgroundCanvasRedraw(sender));
        }
    }
@shawnhar shawnhar added the question label May 23, 2018
@shawnhar
Copy link
Member

@shawnhar shawnhar commented May 23, 2018

I don't believe it's supported to Invalidate while inside a drawing operation. Can you synchronize to avoid doing that?

@mglogan
Copy link
Author

@mglogan mglogan commented May 30, 2018

Unfortunately, no. I noticed intermittent errors and tracked it down to the outer container issuing redraw requests as other controls moved/resized or whatever. I found I could reliably recreate the problem if Invalidate fires while the drawing session is open, hence the code above.

Since I'm deliberately not blocking the UI thread, requests can trickle through and I don't know of a way to intercept invalidate requests. I can't overload the CanvasVirtualControl's Invalidate because it is sealed.

I'll give it some thought. Perhaps I can redesign where the canvas sits to minimize the possibility the app asks for a redraw (or find some way to intercept the request).

@shawnhar
Copy link
Member

@shawnhar shawnhar commented Apr 23, 2019

We're not planning on making any changes to Win2D to alter the behavior of this scenario, so you will have to synchronize this in app code. Suggest keeping track of when you are in the middle of a drawing operation, and deferring any incoming invalidate requests until the draw has completed.

@shawnhar shawnhar closed this Apr 23, 2019
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
2 participants
You can’t perform that action at this time.
Morty Proxy This is a proxified and sanitized view of the page, visit original site.