【问题标题】:Need SharpDXElement alternative. Workaround to sharpDX WPF flickering需要 SharpDXElement 替代品。解决sharpDX WPF闪烁的方法
【发布时间】:2015-01-12 01:16:09
【问题描述】:

我有一个 SharpDX 项目即将完成。它使用 Kinect 进行交互。因此,我的项目将 WPF 用于 Kinect 区域和 KinectUserViewer 对象。到目前为止,SharpDX 在所有方面都表现出色,但是,当它到达程序中大量使用 direct3D 的某个部分时,它开始闪烁。这显然与 D3Dimage(由 MainWindow.xaml 中的 SharpDXElement 使用)可能“从根本上损坏且不适合 WPF 中的高效 D3D 渲染”[1] 的事实有关。有没有办法让我的 WPF 元素不被 direct3D 闪烁?

【问题讨论】:

  • 你能显示一些相关的代码吗?在回答这样的问题时,看看你在哪里会非常有帮助。
  • 它有超过 15000 行代码。很难找到相关的代码。它使用 toolkit.game 类进行绘制和更新循环。我主要使用 spritebatch,但其中一部分同时使用 xna 和 direct3D,包括有问题的部分。有没有你认为特别有用的部分?
  • 说,我查看了您的链接,当我查看您引用的上下文时,他们还提到了使用 Windows 窗体的有限解决方法。如果我理解解决方法,那基本上将在 D3DImage 替换区域中托管一个 Windows 窗体 - 我认为 only 该区域将无法使用 XAML 元素。这对你有用吗?还是我错过了解决方法的性质?
  • 按照我的解释,这使得我无法将 KinectUserViewer 显示在 sharpDX 内容之上。我可能错了。
  • 是的,我玩了一个示例,发现 WPF 对象无法放置在表单顶部。 KinectUserViewer 是一个位于屏幕顶部和中心的小窗口。它通过sharpDX在它的左边和右边有时间和天气。我想如果有一种方法可以在sharpDX中显示用户视图,我可以使用该修复程序,但我不知道该怎么做。

标签: c# wpf xaml sharpdx d3dimage


【解决方案1】:

闪烁可能表明在 D3DImage 尝试将帧复制到其前端缓冲区之前,GPU 尚未完成渲染帧。这在 WPF 中很容易发生,因为 WPF 不使用向交换链呈现帧的标准机制。相反,大多数代码使用如下内容:

// rendering code
Device.Flush(); // or equivalent, depending on Direct3D version used
D3DImage.Lock();
D3DImage.AddDirtyRect(...);
D3DImage.Unlock();

这至少是SharpDXElement.InvalidateRender 后面的模式 - 我在该文件中看不到Device.Flush(),但我怀疑它在调用代码中。

问题是Device.Flush() 不是同步的。它适用于轻度 GPU 负载 - GPU 在锁定/解锁代码完成之前完成 - 但对于较重的负载,它通常还没有完成渲染,导致至少某些帧出现空白帧。这显示为闪烁。

开源的好处是您可以修改代码。要验证这个问题并提供一个简单(如果是 hackish)的解决方案,请尝试给 GPU 更多时间:

D3DImage.Lock();
Thread.Sleep(2); // 2ms
D3DImage.AddDirtyRect(...);
D3DImage.Unlock();

如果这样可以减少或消除您的闪烁,这就是您的问题。至少对于 Direct3D 10 或 11 而言,更彻底的解决方案是 use query events as described in this question。

使用 Windows 窗体的问题在于您最终会遇到 WPF 空域问题(WPF 项目无法在子窗口上方绘制)。 Airspace issues can be fixed,但这项工作比编辑 SharpDXElement 复杂得多。

【讨论】:

    【解决方案2】:

    问题不在于锁定机制。通常您使用Present 来绘制以呈现图像。 Present 将等到所有绘图准备好。使用 D3DImage 您没有使用 Present() 方法。您可以锁定、添加 DirtyRect 并解锁 D3DImage,而不是 Presenting。

    渲染是异步完成的,所以当您解锁时,绘制动作可能还没有准备好。这导致了闪烁效果。有时你会看到项目被画了一半。 (我已经测试过) 一个糟糕的解决方案是在解锁之前添加一个小延迟。它有一点帮助,但它不是一个很好的解决方案。太可怕了!

    解决方案:

    我继续做其他事情;我正在使用 MSAA (抗锯齿),我遇到的第一个问题是; MSAA 无法在 dx11/dx9 共享纹理上完成,所以我决定渲染到一个新纹理 (dx11) 并创建一个副本到 dx9 共享纹理。我把头撞在桌子上,因为现在它是抗锯齿且无闪烁的!!

    看起来 thr 复制操作会等到所有绘图都准备好(当然)现在它有助于完成绘图。

    所以,创建纹理的副本:

    DXDevice11.Device.ImmediateContext.ResolveSubresource(
        _dx11RenderTexture, 0, _dx11BackpageTexture, 0, ColorFormat);
    

    (_dx11BackpageTexture 是共享纹理,_dx11RenderTexture 是 MSAA dx11 纹理) 将等待渲染准备好并创建副本。

    这就是我摆脱闪烁的方法......

    【讨论】:

    • 辉煌。打算试试这个。多年来一直受到 DX11/DX9 闪烁的困扰,并尝试了许多不同的 hacks/workarounds ...
    【解决方案3】:

    我遇到了同样的问题,DeviceCreationFlags.SingleThreaded 帮助了我。

    【讨论】:

      【解决方案4】:

      最后我解决了这个问题: https://docs.microsoft.com/zh-cn/windows/win32/api/d3d11/nn-d3d11-id3d11query

      public void Render()
      {
          ThrowIfDisposed();
          if (RenderTarget == null)
          {
              throw new InvalidOperationException("Resize has not been called.");
          }
          D3D10.Query query = new D3D10.Query(device, new D3D10.QueryDescription()
          {
              Flags = D3D10.QueryFlags.None,
              Type = D3D10.QueryType.Event
          });
          query.Begin();
          OnRender();//do your render
          device.Flush();//d3d10 device
          query.End();
          while (!query.IsDataAvailable)
          {
              System.Threading.Thread.Yield();
          }
          query.Dispose();
          OnUpdated();
      }
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 2019-09-13
        • 1970-01-01
        • 1970-01-01
        • 2014-04-20
        • 1970-01-01
        • 2014-04-05
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多