Multiple thread image compositing

1.2k views Asked by At

I'm using WPF imaging to composite text, 3d graphics and images together using a DrawingVisual. The result is then rendered into a RenderTargetBitmap and saved to disk as a jpeg. All of this happens in code with no visual window.

It's working well as long as I only try to do one thing at a time. However I'd like to see if I can speed things up by using multiple threads to do the rendering. Each thread uses an object that creates its own DrawingContext, DrawingVisual, etc. However there's clearly some shared state somewhere as I get spurious errors when I attempt to access them in parallel. Sometimes it's "the calling thread cannot access this object because another thread created it". Other times it's more evil looking NullReferenceExceptions bubbling up from the bowels of WPF when I'm adding, say, points to a 3D geometry.

Is there a way to ensure each thread stays off of each other with WPF? Or is shared state unavoidable?

3

There are 3 answers

0
LadderLogic On

Is it possible that you are accidentally using the same resources across the threads? Are there any lamda expressions or anonymous methods involved in your processing code?

0
Libor On

From MSDN:

Objects that derive from DispatcherObject have thread affinity.

Objects that derive from Freezable are free-threaded when they are frozen. For more information, see the Freezable Objects Overview.

I have encountered similar problem when working with bitmaps, brushes and other classes that should be really used (not only created) on UI thread.

This is annoying because one would like to leverage powers of WPF rendering to parallel processing realm, but this seems to be impossible (except for some scenarios like updating WriteableBitmap with pointers).

0
roufamatic On

Eureka. I was creating my objects within threadpool threads, which is apparently a no-no when working with WPF. Instead, all objects need to be created from dispatcher threads. Incidentally this also cleared up a horrific memory leak I hadn't discovered.

My original class definition looked like this:

public class Compositor
{
    private int _width;
    private int _height;
    private DrawingVisual _drawingVisual;
    private DrawingContext _drawingContext;
    private bool _isReady = false;
    public void Reset(int width, int height)
    {
        _width = width;
        _height = height;
        _drawingVisual = new DrawingVisual();
        _drawingContext = _drawingVisual.RenderOpen();
        _isReady = true;
    }
    // ... compositing methods below
}

To rectify, I had my class inherit from DispatcherObject, then used the Dispatcher property to instantiate my objects.

public class Compositor : DispatcherObject
{
    private int _width;
    private int _height;
    private DrawingVisual _drawingVisual;
    private DrawingContext _drawingContext;
    private bool _isReady = false;
    public void Reset(int width, int height)
    {
        Dispatcher.Invoke(
            new Action(
                () =>
                    {
                        _width = width;
                        _height = height;
                        _drawingVisual = new DrawingVisual();
                        _drawingContext = _drawingVisual.RenderOpen();
                        _isReady = true;
                    }));
    }
    // ... compositing methods below
}