Man*_*eet 4 wpf multithreading flicker sharpdx d3dimage
我将 SharpDX.WPF 项目用于 WPF 功能,与 SharpDX 附带的工具包(具有相同的问题!)相比,它似乎是一个易于理解的低开销库
首先:我使用以下方法修复了最新 SharpDX 的 SharpDX.WPF 项目: https
然后我对 DXElement.cs 进行了以下 hacky 调整,这里也完成了一个解决方案:
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();
}
Run Code Online (Sandbox Code Playgroud)
然后我以立方体图案渲染 8000 个立方体......
屈服...
经常打印到控制台,但闪烁仍然存在。我假设 WPF 足够好,可以在渲染完成之前使用不同的线程显示图像,但不确定......当我将 WPF 支持的 Toolkit 变体与 SharpDX 一起使用时,也会发生同样的问题。
说明问题的图片:
注意:它随机地在这些旧图像之间随机切换。我还使用了非常旧的硬件,这使得闪烁更加明显(GeForce Quadro FX 1700)
制作了一个 repo,其中包含与我用来解决此问题的源代码完全相同的源代码:https : //github.com/ManIkWeet/FlickeringIssue/
与D3DImage锁定相关,请注意D3DImage.TryLockAPI 具有大多数开发人员不会想到的非常规语义:
尽管可能更多的是令人担忧的设计选择而不是错误本身,但误解这种行为将很容易导致 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(); // <--- !
}
Run Code Online (Sandbox Code Playgroud)
是的,这个不直观的代码实际上是正确的;它的情况是,该D3DImage.TryLock(0) 泄漏一个内部D3D缓冲器锁定每次返回失败时间。你不必相信我的话,这是PresentationCore.dllv4.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;
}
Run Code Online (Sandbox Code Playgroud)
请注意,_lockCount无论函数返回成功还是失败,内部字段都会递增。Unlock()如果您想避免某些死锁,您必须调用自己,如上面的第一个代码示例所示。如果不这样做,调试也会很麻烦,因为组件在下一次渲染之前不会(可能)死锁,届时相关证据早已消失。
MSDN 上似乎没有提到这种不寻常的行为,但公平地说,该文档也没有指出Unlock()如果调用成功就必须调用。
| 归档时间: |
|
| 查看次数: |
2519 次 |
| 最近记录: |