0

I am using WriteableBitmap to implement video display of a streaming video (need to re-write it up to 30 times per sec)
There are several of them on the screen and each has its own video to display.
The video also needs to be decoded so I am using worker threads (one per bitmap)to do decoding and writing into the BackBuffer and then UI thread to Lock AddDirtyRect and Unlock like so

m_WrtblBtmp.Dispatcher.Invoke(() =>
{
    Debug.WriteLine("About to lock Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
    m_WrtblBtmp.Lock();                     
});


if (ReadNextVideoFrame(m_VideoDecoder, m_BackBuffer))
{
    m_WrtblBtmp.Dispatcher.Invoke(() => 
    {
        Debug.WriteLine("About to dirty up Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
        m_WrtblBtmp.AddDirtyRect(new Int32Rect(0, 0, 1280, 1024));
    });
}

m_WrtblBtmp.Dispatcher.Invoke(() => 
{
    Debug.WriteLine("About to Unlock Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
    m_WrtblBtmp.Unlock();
});

ReadNextVideoFrame obtains,decodes and writes the video frame into the bitmap via the m_BackBuffer pointer (copy of the WriteableBitmap.BackBuffer) The problem I ran into is that once you have enough threads this thing deadlocks. Without Thread.Sleep I can deadlock it on 2 threads very quickly. With a bit of sleep it gets through but adding more threads will bring the problem back and I can only sleep so much before the video is slowed down to below live speed (it has to be live).

This practice is supposedly encouraged by Microsoft
http://msdn.microsoft.com/en-us/library/system.windows.media.imaging.writeablebitmap.lock(v=vs.110).aspx
Have I found a bug?

If I write to the front buffer via WritePixels there is no deadlock, but for that I need to copy the data one extra time and also tie up GUI thread for the duration of WritePixels so I would prefer to use BackBuffer if I can.

Here is the output of the Debug.WriteLine while running 2 threads:
About to lock Bitmap53653601 About to lock Bitmap31265986 About to dirty up Bitmap53653601 About to dirty up Bitmap31265986 About to Unlock Bitmap53653601 About to Unlock Bitmap31265986 ... more of the same ... About to dirty up Bitmap50546581 About to Unlock Bitmap50546581 About to dirty up Bitmap9315575 About to lock Bitmap50546581 About to Unlock Bitmap9315575 About to lock Bitmap9315575 About to dirty up Bitmap50546581 About to Unlock Bitmap50546581 About to dirty up Bitmap9315575 About to lock Bitmap50546581 ... then nothing (deadlock) ...

helb
  • 7,609
  • 8
  • 36
  • 58
ILIA BROUDNO
  • 1,539
  • 17
  • 24
  • do you need the 3rd invoke that does the unlock? can't you do the unlock immediately after setting the dirty rect? you've already done your write by then? (not sure if will make any difference, just trying to simplify) – John Gardner Mar 07 '14 at 00:25

1 Answers1

0

The page you link also says this:

The UI thread can block when the render thread acquires a lock on the back buffer to copy it forward to the front buffer. If the latency from this block is too long, use the TryLock method to wait for a short time and then unblock the UI thread to perform other tasks while the back buffer > is locked.

so i'm presuming one of your threads is stuck in that lock, like they say. maybe something like this?

while (SomethingIsTrue) // i'm presuming you have a loop like this already?)
{
    bool gotLock = false;

    m_WrtblBtmp.Dispatcher.Invoke(() =>
    {
        Debug.WriteLine("About to try to lock Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
        gotLock = m_WrtblBtmp.TryLock(SomeTimeoutConstant);                     
        Debug.WriteLine((gotLock ? "got lock on " : "DID NOT lock ") + m_WrtblBtmp.GetHashCode().ToString());

    });

    if (gotLock && ReadNextVideoFrame(m_VideoDecoder, m_BackBuffer))
    {
        m_WrtblBtmp.Dispatcher.Invoke(() => 
        {
            Debug.WriteLine("About to dirty up Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
            m_WrtblBtmp.AddDirtyRect(new Int32Rect(0, 0, 1280, 1024));
            Debug.WriteLine("About to Unlock Bitmap" + m_WrtblBtmp.GetHashCode().ToString());
            m_WrtblBtmp.Unlock();
        });
    }

}

John Gardner
  • 24,225
  • 5
  • 58
  • 76
  • Thank you for the reply John. I only noticed it now, but earlier I did something very similar to what you (and Microsoft) suggested. It did not resolve the problem. It did "improve" it a little as in it still locks but with longer running time or heavier load. But that just makes it harder to get at the real issue. What you quoted makes perfect sense, of course the UI thread can block on a call to Lock, the question is why is it DEADLOCKED as in not just blocked but never gets passed the call to Lock? – ILIA BROUDNO May 01 '14 at 17:05
  • if you're still getting a deadlock, that's by definition a case where you can no longer move forward. someone holds a lock that will never be release, because they're waiting for a lock that someone else has (or something similar). I suggest simplifying all the way back to *exactly* what Microsoft recommends, and see if your problem goes away. Mistakes in multithreading are unforgiving. – John Gardner May 02 '14 at 23:11