【问题标题】:WPF - Rendering "Real Time" Best PracticeWPF - 渲染“实时”最佳实践
【发布时间】:2014-07-14 06:51:45
【问题描述】:

实施说明:

在每个 CompositionTarget.Rendering 事件中,我使用 Writablebitmap 绘制 4 条相邻线, (这是“实时”折线图的绘图仪)。

问题:

这很好用,直到 UI 线程似乎很忙,然后下一个 CompositionTarget.Rendering 事件需要更长的时间才能触发。

问题:

是否有某种机制可以保持恒定的渲染间隔,优先于任何其他 UI 操作?

可能的灵魂:

我正在考虑创建一个 HostVisual 以及如何为其分配 DispatcherTimer ,
这种方法行得通吗?

想到的任何其他方法?

提前致谢。

【问题讨论】:

  • 跟踪实际时间然后在最后一个渲染间隔内绘制尽可能多的相邻线不是更合适吗?例如。 4 行,60 fps,但如果帧速率下降到 30,则为 8 行。
  • 我正在这样做,但问题是在线程繁忙后一次渲染 8 行看起来不像是实时绘图,它看起来像是卡了一会儿然后绘制所有的东西,例如:下一个间隔的时间是相对恒定的间隔的 4 倍,那么您将绘制 16 条线,这些线不会在人眼中显示为“实时”。
  • 如果您仍然被卡住,那么您可以分享您所拥有的工作样本。我在渲染方面有一些经验。
  • @pushpraj Iv'e 实际上解决了这个问题,但我想看看你做了什么,我会与你分享我所做的实际上是使用 HostVisual。

标签: c# wpf multithreading rendering


【解决方案1】:

如果您已经在渲染到 WritableBitmap,为什么不在 ThreadPool 上分离一个异步任务呢? WritableBitmap 支持来自不同线程的更新,例如查看this questionthe class documentation remarks 上的信息。或者,如果线程池不是您的东西,则只需运行您自己的线程。这样您就可以控制时间,除了同步图像之外,不需要依赖 UI 线程来做任何其他事情。

这是我刚刚编写的一些粗略的示例代码,应该可以让您了解我的意思:

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);
    }
}

以及对应的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>

【讨论】:

  • 这是一个很好的方法,我可能会尝试,但是渲染本身仍然在 UI-Thread 上完成,如果它很忙,我仍然会遇到我所描述的问题
  • 我不明白你的意思,UI 线程上没有渲染,当 UI 线程被阻塞时更新位图继续。在 UI 线程解除阻塞之前,它不会与显示同步,这就是 UI 线程被阻塞且无法避免的性质。除非您启动第二个 UI 线程只是为了显示您的控件(这是可能的,但有很多限制)并小心避免阻塞那个
  • 这正是我要说的,这正是我所做的,我使用称为 HostVisual 的东西创建了第二个调度程序,该调度程序专用于我的 writablebitmap 是其来源的图像。您在答案中看到渲染瓶颈可能仍然存在。
  • 好吧,如果这对你有用并且你可以忍受这些限制,那很好。在这种情况下,我不会回到我提出的解决方案;我的解决方案的主要优点是它(通常)更容易实现并且限制更少。
  • 顺便说一句,我的解决方案中应该没有瓶颈,除非渲染步骤本身是瓶颈(我认为不是因为您在谈论实时渲染)。 CompositionTarget_Rendering 应该没有瓶颈(但是图片很大的时候需要管理脏区)。
猜你喜欢
  • 1970-01-01
  • 2020-10-23
  • 1970-01-01
  • 2013-01-08
  • 1970-01-01
  • 1970-01-01
  • 2011-08-06
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多