Движок DirectX11 на C++ и интерфейс на C#

У меня есть движок DirectX11, написанный на C++, оболочка на C++ с CLR и интерфейс на C#.

1) мне интересно, где узкое место в этой структуре, и мне интересно, есть ли более эффективный способ позволить мне разместить рендеринг DirectX11 в элементе управления WinForms.

2) есть ли способ визуализации в потоке, отличном от владельца элемента управления WinForms? Сомневаюсь, но решил спросить.

3) есть ли способ визуализировать несколько кадров, не проходя через слой обертки на каждом кадре, но держать приложение отзывчивым?

Я сравнил эту настройку с SlimDX и на самом деле получаю немного медленнее FPS, когда просто очищаю экран и не делаю никаких других вызовов API. SlimDX ~ 3000 кадров в секунду, мой двигатель ~ 2000 кадров в секунду. Это не большое дело, но мне интересно, откуда эта разница в 33%, поскольку она, вероятно, будет иметь значение позже при сравнении 20 fps до 30.

Я пройду через текущую настройку и опишу столько, сколько смогу. Я уверен, что по пути люди будут просить больше информации, и я буду обновлять по мере необходимости.

мой WinForms GraphicsPanel Управления ниже. Он передает системные сообщения на слой оболочки.

public class GraphicsPanel : Control
{
    EngineWrapper Engine;

    public GraphicsPanel()
    {
        this.SetStyle(ControlStyles.Selectable, true);
        this.SetStyle(ControlStyles.UserMouse, true);
        this.SetStyle(ControlStyles.UserPaint, true);
        this.TabStop = true;
    }
    public void SetEngine(EngineWrapper Engine)
    {
        this.Engine = Engine;

        Application.Idle += OnApplicationIdle;
    }

    ~GraphicsPanel()
    {
        System.Windows.Forms.Application.Idle -= OnApplicationIdle;
    }
    void PassMessage(Message m)
    {
        Engine.ProcessWindowMessage(m.Msg, m.WParam, m.LParam);
    }
    protected override void WndProc(ref Message m)
    {
        base.WndProc(ref m);
        PassMessage(m);
    }
    private void OnApplicationIdle(object sender, EventArgs e)
    {
        while (AppStillIdle)
            if (Engine != null)
                Engine.ProcessWindowMessage(0, IntPtr.Zero, IntPtr.Zero);
    }
    public bool AppStillIdle
    {
        get
        {
            NativeMethods.PeekMsg msg;
            return !NativeMethods.PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
        }
    }
    internal class NativeMethods
    {
        private NativeMethods() { }

        [StructLayout(LayoutKind.Sequential)]
        public struct PeekMsg
        {
            public IntPtr hWnd;
            public Message msg;
            public IntPtr wParam;
            public IntPtr lParam;
            public uint time;
            public System.Drawing.Point p;
        }

        [System.Security.SuppressUnmanagedCodeSecurity]
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out PeekMsg msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);
    }
}

внутри Двигатель Фантик у меня есть эта функция для передачи сообщения из элемента управления WinForms на родной C++ слой.

void EngineWrapper::ProcessWindowMessage(int msg, System::IntPtr wParam, System::IntPtr lParam)
{
    m_Engine->ProcessWindowMessage(msg, (void*)wParam, (void*)lParam);
}

наконец,Собственный Движок C++ обрабатывает сообщения как такового:

void Input::ProcessWindowMessage(int msg, void* wParam, void* lParam)
{
    if (msg == 0 || msg == WM_PAINT)
    {
        DrawFrame();
    }
    else if (msg == WM_SIZING || msg == WM_SIZE)
    {
        DoResize();
        DrawFrame();
    }
    else if (msg >= WM_MOUSEFIRST && msg <= WM_MOUSEWHEEL)
    {
        ProcessMouseMessage(msg, wParam, lParam);
    }
}

1 ответов


1) Мне интересно, где узкое место находится в этой структуре, и мне интересно, есть ли более эффективный способ позволить мне разместить рендеринг DirectX11 в элементе управления WinForms.

игнорируя разницу из-за SlimDX, реализуемого в C++/CLI (что должно быть незначительным), единственное различие, которое я вижу в вашем коде по сравнению с реализация SlimDX это то, что вы беспокоите свой двигатель с сообщением обработка:

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
    PassMessage(m);
} 

Я бы предпочел упростить и сохранить сообщение проблемы от вашего двигателя. Обработайте сообщение в своем WndProc переопределить и вызвать любые операции, которые вам нужны на Engine (напр. Resize, MouseMove, MouseClick, или другие виды обработки ввода) и вызов DrawFrame непосредственно в режиме ожидания:

private void OnApplicationIdle(object sender, EventArgs e)
{
    while (AppStillIdle)
        if (Engine != null)
            Engine.DrawFrame();
}

Я бы не ожидал, что это будет учитывать разницу в производительности ~33%, но, возможно, стоит посмотреть на это.

2) есть ли способ для отображения в потоке, отличном от владельца элемента управления WinForms? Сомневаюсь, но решил спросить.

Да, вы можете использовать экран поверхности. Однако затем возникает проблема, как обновить содержимое окна. Например, вы можете использовать средство просмотра изображений и установить изображение, полученное с поверхности вне экрана, но это даст худшую производительность.

3) Есть ли способ отобразить несколько кадров, не проходя через слой обертки на каждом кадр, но держать приложение отзывчивым?

учитывая подход, который вы используете, кадры отображаются по требованию из обработчика событий простоя приложения, поэтому нет. Вы можете сделать это, используя экранную визуализацию, как описано в предыдущем пункте.