Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

WPF - Rendering "Real Time" Best Practice

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 .

like image 712
eran otzap Avatar asked Oct 20 '22 07:10

eran otzap


1 Answers

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>
like image 93
Zarat Avatar answered Oct 23 '22 10:10

Zarat