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

6
我有一个用C++编写的DirectX11引擎,一个带有CLR的C++包装器和一个用C#编写的接口。
1)我想知道这个结构的瓶颈在哪里,是否有更有效的方法让我在WinForms控件中托管DirectX11渲染。
2)除了WinForms控件的所有者线程之外,是否有其他线程可以进行渲染?我怀疑没有,但还是想问一下。
3)是否有一种方法可以在不在每一帧都通过包装层进行渲染的情况下渲染多个帧,同时保持应用程序的响应性?
我已将此设置与SlimDX进行比较,仅清除屏幕而不进行任何其他API调用时,实际上会获得稍微较慢的FPS。 SlimDX约为3000 FPS,我的引擎约为2000 FPS。虽然这并不是什么大问题,但我想知道这33%的差异来自哪里,因为当将20 fps与30 fps进行比较时,它可能会产生影响。
我将介绍当前的设置并尽可能地描述。我相信在此过程中人们会要求更多的信息,我会根据需要进行更新。
我的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);
    }
}

这个主题有1000页的书籍,我不知道我能否在几段话中写出有用(或正确)的答案。 - BlamKiwi
任何见解或批评都是受欢迎的。 - Russell Trahan
1
《游戏编程完全手册第四版》中有一章非常详细地介绍了如何在C#/WinForms UI编辑器中使用本地的C++/DX11游戏引擎。我认为这会对你很有帮助。 - rashmatash
在比较Windows渲染引擎的经验中,我发现即使在不渲染任何内容时2000和3000之间的差异也不会在“真实世界”场景中转化为20和30的分裂。有时候,这种差异完全消失了。我建议使用两个系统渲染一些几何图形,可能添加一些着色器效果等,然后看看你得到的差异是什么。 - Steve Lillis
@rashmatash 我喜欢你的书选择。8D - BlamKiwi
显示剩余3条评论
1个回答

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

不考虑由于SlimDX是用C++/CLI实现而导致的差异(应该可以忽略不计),与SlimDX实现相比,我只看到你正在打扰你的引擎进行消息处理:

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

我希望简化并将消息处理与您的引擎分开。在您的WndProc重写中处理消息,并在Engine上调用您需要的任何操作(例如ResizeMouseMoveMouseClick或其他输入处理),并在空闲时直接调用DrawFrame

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

我不认为这可以解释 ~33% 的性能差异,但可能值得研究一下。

2) 有没有办法在非WinForms控件的所有者线程上进行渲染?我怀疑这是不可能的,但还是问问吧。

是的,您可以使用离屏表面。但是,接下来的问题是如何更新窗口内容。例如,您可以使用图片查看器并设置从离屏表面获取的图像,但这会导致更差的性能。

3) 是否有办法在每个帧上不经过包装层进行多帧渲染,同时保持应用程序响应性?

根据您使用的方法,帧是从应用程序空闲事件处理程序按需呈现的,因此不行。您可以使用前面解释的离屏渲染来实现。


1
在单线程游戏中,将消息处理作为游戏循环的一部分是很常见的,以确保在另一个帧开始之前始终处理消息。这有助于减少来自Win32和Windows的输入延迟,并检测您的应用程序是否实际崩溃。 - BlamKiwi

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接