D3DImage 和 SharpDX 在慢速硬件上闪烁

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/

Gle*_*den 5

D3DImage锁定相关,请注意D3DImage.TryLockAPI 具有大多数开发人员不会想到的非常规语义:

谨防!即使在TryLock指示失败(即返回false的情况下,
您也必须调用Unlock

尽管可能更多的是令人担忧的设计选择而不是错误本身,但误解这种行为将很容易导致 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()如果调用成功就必须调用。

在此处输入图片说明