C++中的DirectX11引擎和C#中的接口

Rus*_*han 6 .net c# c++ directx-11 winforms

我有用C++编写的DirectX11 Engine,带有CLR的C++包装器和C#中的接口.

1)我很好奇这个结构中的瓶颈在哪里,我想知道是否有更有效的方法让我在WinForms控件中托管DirectX11渲染.

2)有没有办法在WinForms控件的所有者以外的线程上呈现?我怀疑它,但我想问.

3)有没有办法渲染多个帧而不通过每个帧上的包装层但保持应用程序响应?

我已经将这个设置与SlimDX进行了比较,实际上只是清除屏幕并且没有进行任何其他API调用时,FPS稍微变慢了.SlimDX~3000 FPS,我的引擎~2000 FPS.这不是什么大问题,但我想知道这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);
    }
}
Run Code Online (Sandbox Code Playgroud)

Engine Wrapper中,我有这个函数将消息从WinForms控件传递到本机C++层.

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

最后,Native C++ Engine按如下方式处理消息:

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);
    }
}
Run Code Online (Sandbox Code Playgroud)

jno*_*ovo 4

1)我很好奇这个结构的瓶颈在哪里,我想知道是否有更有效的方法让我在 WinForms 控件中托管 DirectX11 渲染。

忽略由于 SlimDX 在 C++/CLI 中实现而导致的差异(应该可以忽略不计),与SlimDX 实现相比,我在代码中看到的唯一区别是您在消息处理方面困扰了引擎:

protected override void WndProc(ref Message m)
{
    base.WndProc(ref m);
    PassMessage(m);
} 
Run Code Online (Sandbox Code Playgroud)

我宁愿简化并使消息问题远离您的引擎。在覆盖中处理消息WndProc并调用您需要的任何操作Engine(例如ResizeMouseMoveMouseClick,或其他类型的输入处理)并DrawFrame在空闲时直接调用:

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

我不认为这会导致约 33% 的性能差异,但可能值得研究一下。

2)有没有办法在WinForms控件所有者之外的线程上渲染?我对此表示怀疑,但想我会问一下。

是的,您可以使用屏幕外表面。然而,问题就变成了如何更新窗口内容。例如,您可以使用图片查看器并设置从屏幕外表面获取的图像,但这会产生更差的性能。

3)有没有一种方法可以渲染多个帧,而不需要通过每个帧上的包装层,但保持应用程序响应?

鉴于您使用的方法,帧是根据应用程序空闲事件处理程序按需渲染的,所以不会。您可以使用上一点所述的离屏渲染来实现此目的。