我使用SharpDX.WPF项目获得WPF功能,与SharpDX附带的Toolkit相比,它似乎是一个易于理解的低开销库(具有相同的问题!)
首先:我使用以下内容修复了最新SharpDX的SharpDX.WPF项目:https://stackoverflow.com/a/19791534/442833
然后我对DXElement.cs进行了以下hacky调整,这个解决方案也完成here:
private Query queryForCompletion;
public void Render()
{
if (Renderer == null || IsInDesignMode)
return;
var test = Renderer as D3D11;
if (queryForCompletion == null)
{
queryForCompletion = new Query(test.Device,
new QueryDescription {Type = QueryType.Event, Flags = QueryFlags.None});
}
Renderer.Render(GetDrawEventArgs());
Surface.Lock();
test.Device.ImmediateContext.End(queryForCompletion);
// wait until drawing completes
Bool completed;
var counter = 0;
while (!(test.Device.ImmediateContext.GetData(queryForCompletion, out completed)
&& completed))
{
Console.WriteLine("Yielding..." + ++counter);
Thread.Yield();
}
//Surface.Invalidate();
Surface.AddDirtyRect(new Int32Rect(0, 0, Surface.PixelWidth, Surface.PixelHeight));
Surface.Unlock();
}
然后我以立方体模式渲染8000个立方体......
...屈服
经常被打印到控制台,但闪烁仍然存在。 我假设WPF足够好,在渲染完成之前使用不同的线程显示图像,但不确定... 当我使用支持SharpDX的WPF支持的Toolkit变种时,也会发生同样的问题。
图片展示了这个问题:
注意:随机地在这些旧图像之间随机切换。我也使用非常古老的硬件,使闪烁更具吸引力(GeForce Quadro FX 1700)
创建了一个repo,其中包含与我用来解决此问题完全相同的源代码: https://github.com/ManIkWeet/FlickeringIssue/
答案 0 :(得分:3)
与D3DImage
锁定相关,请注意D3DImage.TryLock
API具有相当非常规的语义,大多数开发人员都不会期望:
虽然可能更多的是警告设计选择而不是本身,但误解这种行为将导致D3DImage死锁并挂起,因此可能会造成很大责任人们在试图让D3DImage
正常工作时遇到的挫折感。
以下代码是正确的 WPF D3D渲染,我的应用中没有闪烁:
void WPF_D3D_render(IntPtr pSurface)
{
if (TryLock(new Duration(default(TimeSpan))))
{
SetBackBuffer(D3DResourceType.IDirect3DSurface9, pSurface);
AddDirtyRect(new Int32Rect(0, 0, PixelWidth, PixelHeight));
}
Unlock(); // <--- !
}
是的,这个不直观的代码实际上是正确的;情况是D3DImage.TryLock(0)
每次返回失败时都会泄漏一个内部D3D缓冲区锁。你不必接受我的话,这里是来自PresentationCore.dll
v4.0.30319的CLR代码:
private bool LockImpl(Duration timeout)
{
bool flag = false;
if (_lockCount == uint.MaxValue)
throw new InvalidOperationException();
if (_lockCount == 0)
{
if (timeout == Duration.Forever)
flag = _canWriteEvent.WaitOne();
else
flag = _canWriteEvent.WaitOne(timeout.TimeSpan, false);
UnsubscribeFromCommittingBatch();
}
_lockCount++;
return flag;
}
请注意,无论函数是返回成功还是失败,内部_lockCount
字段都会递增。如果你想避免某些死锁,你必须自己调用Unlock()
,如上面的第一个代码示例所示。如果不这样做,那么调试也是令人讨厌的,因为组件在下一次渲染过程之前不会(可能)死锁,到那时相关证据已经很久了。
TryLock似乎没有提及异常行为,但公平地说,文档并未注意到如果呼叫成功,您还必须致电Unlock()
。
答案 1 :(得分:1)
问题不在于锁定机制。通常使用Present
绘制来呈现图像。 Present
将等待所有绘图准备就绪。使用D3DImage,您没有使用Present()
方法。您可以锁定,添加DirtyRect并解锁D3DImage
,而不是呈现。
渲染完成异步,因此当您解锁时,绘制操作可能没有准备好。这会导致闪烁效应。有时您会看到一半画出的物品。一个糟糕的解决方案(我已经测试过)在解锁之前添加了一个小延迟。它有点帮助,但它不是一个简洁的解决方案。太可怕了!
<强>解决方案:强>
我继续其他的事情;我正在使用MSAA (抗锯齿)进行评估,我遇到的第一个问题是;无法在dx11 / dx9共享纹理上完成MSAA,因此我决定渲染到新纹理(dx11)并创建dx9共享纹理的副本。我把头撞在了表格上,因为现在它已经消除了锯齿并轻弹了!在添加脏矩形之前,不要忘记调用Flush()
。
因此,创建纹理的副本:DXDevice11.Device.ImmediateContext.ResolveSubresource(_dx11RenderTexture, 0, _dx11BackpageTexture, 0, ColorFormat);
(_dx11BackpageTexture
是共享纹理)将等待渲染准备就绪并创建副本。
这就是我摆脱闪烁的方式......
答案 2 :(得分:0)
我认为你没有正确锁定。据我所知,MSDN documentation你应该在整个渲染过程中锁定,而不仅仅是在它结束时:
当D3DImage被锁定时,您的应用程序也可以渲染到分配给后台缓冲区的Direct3D表面。
你在网上找到的关于D3DImage / SharpDX的信息有些令人困惑,因为SharpDX的人并不喜欢D3DImage的实现方式(不能责怪他们),所以有关于这个的说法a&#34; bug&#34;在微软方面,当它实际上只是不恰当地使用API时。
是的,渲染期间的锁定存在性能问题,但如果不将WPF移植到DirectX11并实现UWP应用程序中可用的SwapChainPanel之类的东西,则可能无法修复它们。 (WPF本身仍在DirectX9上运行)
如果锁定对您来说是一个性能问题,我有一个想法(但从未测试过)是您可以渲染到屏幕外表面并减少锁定持续时间以将该表面复制到D3DImage。不知道这是否有助于提高性能,但需要尝试。