我有一个非常接近完成的 SharpDX 项目。它使用 Kinect 进行交互。因此,我的项目将 WPF 用于 Kinect 区域和 KinectUserViewer 对象。到目前为止,SharpDX 在所有方面都表现出色,但是,当它到达程序中大量使用 direct3D 的某个部分时,它开始闪烁。这显然与 D3Dimage(由 MainWindow.xaml 中的 SharpDXElement 使用)可能“从根本上损坏且不适合 WPF 中的高效 D3D 渲染” [1]的事实有关。有没有办法让我的 WPF 元素不被 direct3D 闪烁?
4 回答
闪烁可能表明在 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 而言,更彻底的解决方案是使用此问题中描述的查询事件。
使用 Windows 窗体的问题在于您最终会遇到 WPF 空域问题(WPF 项目无法在子窗口上方绘制)。可以修复空域问题,但这项工作比编辑 SharpDXElement 复杂得多。
问题不在于锁定机制。通常您使用Present
绘图来呈现图像。Present
将等到所有绘图准备好。使用 D3DImage 您没有使用该Present()
方法。您可以锁定、添加 DirtyRect 并解锁D3DImage
.
渲染是异步完成的,因此当您解锁时,绘制操作可能尚未准备好。这导致了闪烁效果。有时你会看到项目被画了一半。一个糟糕的解决方案(我已经测试过)是在解锁之前添加一个小的延迟。它有一点帮助,但它不是一个很好的解决方案。太可怕了!
解决方案:
我继续做别的事情;我正在使用 MSAA (抗锯齿),我面临的第一个问题是;MSAA 无法在 dx11/dx9 共享纹理上完成,因此我决定渲染到新纹理(dx11)并创建到 dx9 共享纹理的副本。我把头撞在桌子上,因为现在它是抗锯齿和无闪烁的!!
看起来 thr 复制动作等到所有绘图准备好(当然)现在它有助于完成绘图。
因此,创建纹理的副本:
DXDevice11.Device.ImmediateContext.ResolveSubresource(
_dx11RenderTexture, 0, _dx11BackpageTexture, 0, ColorFormat);
(_dx11BackpageTexture
是共享纹理并且_dx11RenderTexture
是 MSAA dx11 纹理)将等到渲染准备好并创建一个副本。
这就是我摆脱闪烁的方法......
我有同样的问题,DeviceCreationFlags.SingleThreaded
帮助我。
最后我解决了这个问题: 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();
}