6

Description of implementation :

On each CompositionTarget.Rendering event i draw 4 adjacent lines using a Writablebitmap , (This is a plotter for a "Real Time" line graph).

Problem :

This works great until the UI-Thread seems to be busy and then the next CompositionTarget.Rendering event takes longer to fire.

Question :

Is There some mechanism to keep a constant rendering interval which would be prioritized above any other UI-operations ?

Possible soulotion :

I was thinking of creating a HostVisual and some how assigning it a DispatcherTimer ,
would this approach work ?

any other approach's which come to mind ?

thanks in advance .

eran otzap
  • 12,293
  • 20
  • 84
  • 139
  • Wouldn't it be more appropriate to keep track of the actual time and then draw as many adjencent lines as fit in the last rendering interval? E.g. 4 lines at 60 fps but 8 lines if frame rate drops to 30. – Clemens May 25 '14 at 12:37
  • I'm doing that , but the problem is that rendering 8 lines at a time after the Thread was busy does not look like a real time drawing it would look like it gets stuck for a moment and then draws the all thing , for example : the next interval took 4 times as long as the relatively constant one then you would draw 16 lines which would not appear "Real Time" to the human eye. – eran otzap May 25 '14 at 13:33
  • If you are still stuck then could you share as working sample of what you have. I have some exp in rendering stuff. – pushpraj Jun 25 '14 at 08:19
  • @pushpraj Iv'e actually solved this issue , But i'd like to see what you did any ways and i'll share with you what i did which is actually using a HostVisual. – eran otzap Jun 25 '14 at 09:39

1 Answers1

4

If you are already rendering into a WritableBitmap anyways, why not spin off an async Task on the ThreadPool? WritableBitmap supports updates from different threads, check for example this question or the info on the class documentation remarks. Alternatively just run your own thread, if the thread pool is not your thing. This way you are in control of the timing and don't need to rely on the UI thread for anything else besides syncing the image.

Here's some crude example code I just made up which should give you an idea what I mean:

public partial class MainWindow : Window
{
    private WriteableBitmap mImage;
    private bool mShutdown;
    private object mUpdateLock = new object();
    private IntPtr mBuffer;
    private int mStride;

    public MainWindow()
    {
        InitializeComponent();

        mImage = new WriteableBitmap(10, 2, 96, 96, PixelFormats.Bgr32, null);
        wImage.Source = mImage;

        Closed += delegate {
            CompositionTarget.Rendering -= CompositionTarget_Rendering;
            lock (mUpdateLock)
            {
                mShutdown = true;
                mImage.Unlock();
            }
        };

        mImage.Lock();
        mBuffer = mImage.BackBuffer;
        mStride = mImage.BackBufferStride;
        CompositionTarget.Rendering += CompositionTarget_Rendering;

        UpdateAsync();
    }

    private void CompositionTarget_Rendering(object sender, EventArgs e)
    {
        lock (mUpdateLock)
        {
            // for a large image you can optimize that by only updating the parts that changed,
            // collect dirty-areas in a list or something (while under the lock of course!)
            mImage.AddDirtyRect(new Int32Rect(0, 0, 10, 2));
            mImage.Unlock();
            mImage.Lock();

            // I don't know if these can changes, but docs say to acquire them after locking ...
            mBuffer = mImage.BackBuffer;
            mStride = mImage.BackBufferStride;
        }
    }

    private async void UpdateAsync()
    {
        int x = 0;
        int y = 0;
        int color = 0xFF0000;

        for (; ;)
        {
            lock (mUpdateLock)
            {
                if (mShutdown)
                    return;

                // feel free to do 'unsafe' code here if you know what you're doing
                Marshal.WriteInt32(new IntPtr(mBuffer.ToInt64() + x * 4 + y * mStride), color);
            }

            if (++x == 10)
            {
                x = 0;
                if (++y == 2)
                {
                    y = 0;
                    color ^= 0xFF00FF;
                }
            }

            await Task.Delay(500).ConfigureAwait(false);
        }
    }

    private void Button_Click(object sender, RoutedEventArgs e)
    {
        Thread.Sleep(2000);
    }
}

And the corresponding XAML

<Window x:Class="WpfApplication2.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
        Title="MainWindow" Height="350" Width="525">
    <Grid>
        <Button Content="Slow" HorizontalAlignment="Left" VerticalAlignment="Top" Click="Button_Click"/>
        <Image x:Name="wImage"/>
    </Grid>
</Window>
Community
  • 1
  • 1
Zarat
  • 2,584
  • 22
  • 40
  • That's a very good approach i might try that , But the rendering itself is still done on the UI-Thread , and if it's busy i could still come across the problem iv'e described – eran otzap Aug 23 '14 at 11:49
  • I don't see what you mean, no rendering happens on the UI thread, updating the bitmap continues while the UI thread is blocked. It's just not synced with the display until the UI thread unblocks, thats the nature of the UI thread being blocked and not possible to avoid. Unless you spin up a 2nd UI thread just to display your control (which is possible but has a lot of limitations) and carefully avoid blocking that one – Zarat Aug 23 '14 at 17:26
  • and that's exactly what i'm saying , and that's exactly what iv'e done , using something called a HostVisual i created a second dispatcher which is dedicated to the Image my writablebitmap is the Source of. you see in your answer the rendering bottleneck could still exist. – eran otzap Aug 23 '14 at 17:28
  • Well, if that already works for you and you can live with the limitations, that's fine. I wouldn't go back to my proposed solution in that case; the main advantage of my solution is that it's (usually) simpler to implement and has less limitations. – Zarat Aug 23 '14 at 17:33
  • BTW there should be no bottlenecks in my solution, unless the rendering step itself is a bottleneck (which I assumed it was not since you were talking about realtime rendering). `CompositionTarget_Rendering` should be no bottleneck (but you need to manage dirty areas when the image is large). – Zarat Aug 23 '14 at 17:35
  • there is another rendering op. on a different control at some point, when that occurs it seems that the Dispatcher is switching between operations , and you can clearly see the plotter not stopping but not rendering smoothly. – eran otzap Aug 23 '14 at 17:59