我有一个 SharpDX 项目即将完成。它使用 Kinect 进行交互。因此,我的项目对 Kinect 区域和 KinectUserViewer 对象都使用 WPF。到目前为止,SharpDX 在所有方面都表现良好,但是,当它到达大量使用 direct3D 的程序的某个部分时,它开始闪烁。这显然与以下事实有关:D3Dimage(由 MainWindow.xaml 中的 SharpDXElement 使用)可能“从根本上被破坏,不适合在 WPF 中进行高效的 D3D 渲染”[1]。有没有办法让我的 WPF 元素在 direct3D 中保持不闪烁?
闪烁可能表明在 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 在锁定/解锁代码完成之前完成),但对于较重的负载,它通常尚未完成渲染,从而导致至少某些帧出现空白帧。这看起来像是闪烁。
开源的好处是你可以修改代码。要验证这个问题并提供一个简单(如果黑客)的解决方案,请尝试给 GPU 多一点时间:
D3DImage.Lock();
Thread.Sleep(2); // 2ms
D3DImage.AddDirtyRect(...);
D3DImage.Unlock();
如果这减少或消除了闪烁,那就是你的问题了。至少对于 Direct3D 10 或 11 而言,更彻底的解决方案是使用本问题中所述的查询事件。
使用 Windows 窗体的问题是您最终会遇到 WPF 空域问题(WPF 项目缺乏在子窗口上方绘制的能力)。 空域问题可以修复,但工作比编辑 SharpDXElement 复杂得多。
问题不在于锁定机制。通常您使用
Present
来绘制来呈现图像。 Present
将等待所有绘图准备就绪。对于 D3DImage,您没有使用 Present()
方法。您无需进行演示,而是锁定、添加 DirtyRect 并解锁 D3DImage
。
渲染是异步完成的,因此当您解锁时,绘制操作可能尚未准备好。这导致了闪烁效应。有时您会看到半画的项目。一个糟糕的解决方案(我已经测试过)是在解锁之前添加一个小的延迟。它有一点帮助,但这不是一个巧妙的解决方案。太可怕了!
解决方案:
我继续做别的事;我正在尝试 MSAA (抗锯齿),我面临的第一个问题是; MSAA 无法在 dx11/dx9 共享纹理上完成,因此我决定渲染到新纹理 (dx11) 并创建到 dx9 共享纹理的副本。我把头撞在桌子上,因为现在它已经抗锯齿并且没有闪烁了!!
看起来复制操作要等到所有绘图准备好(当然),现在它有助于完成绘图。
因此,创建纹理的副本:
DXDevice11.Device.ImmediateContext.ResolveSubresource(
_dx11RenderTexture, 0, _dx11BackpageTexture, 0, ColorFormat);
(
_dx11BackpageTexture
是共享纹理,_dx11RenderTexture
是MSAA dx11纹理)将等待渲染准备就绪并创建副本。
这就是我摆脱闪烁的方法......
我也遇到了同样的问题,
DeviceCreationFlags.SingleThreaded
帮助了我。
最后我解决了这个问题: https://learn.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();
}
WindowRenderTarget:
this.DoubleBuffered = false;
SetStyle(ControlStyles.AllPaintingInWmPaint | ControlStyles.Opaque, true);