Нужна альтернатива SharpDXElement. Обходной путь для мерцания sharpDX WPF

У меня есть проект SharpDX, который очень близок к завершению. Он использует Kinect для взаимодействия. Из-за этого мой проект использует WPF как для региона Kinect, так и для объекта KinectUserViewer. До сих пор SharpDX отлично работал для всех, однако, когда он попадает в определенную часть программы, которая интенсивно использует Direct3D, он начинает мерцать. По-видимому, это связано с тем фактом, что D3Dimage (используемый SharpDXElement в MainWindow.xaml) может быть "принципиально сломан и непригоден для эффективного рендеринга D3D в WPF" [1]. Есть ли способ сохранить мои элементы WPF и не мерцать с Direct3D?

4 ответа

Вероятно, мерцание указывает на то, что графический процессор не завершил рендеринг кадра до того, как D3DImage попытается скопировать его в свой передний буфер. Это может легко произойти в 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 немного больше времени:

D3DImage.Lock();
Thread.Sleep(2); // 2ms
D3DImage.AddDirtyRect(...);
D3DImage.Unlock();

Если это уменьшает или устраняет ваше мерцание, это ваша проблема. Более полное решение, по крайней мере для Direct3D 10 или 11, состоит в использовании событий запроса, как описано в этом вопросе.

Проблема с использованием Windows Forms заключается в том, что вы сталкиваетесь с проблемами воздушного пространства 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://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();
}
Другие вопросы по тегам