DirectX11 Engine in C++ and Interface in C#

.net, c#, c++, directx-11, winforms

Solution

1) I am curious about where the bottleneck is in this structure, and I'm wondering if there is a more efficient way to let me host the DirectX11 rendering in a WinForms Control.

Disregarding the difference due to SlimDX being implemented in C++/CLI (which should be negligible), the only difference I see in your code compared to the SlimDX implementation is that you are bothering your engine with message processing:

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

I'd rather simplify and keep message concerns away from your engine. Handle the message in your `WndProc` override and invoke whatever operations you need on `Engine` (eg. `Resize`, `MouseMove`, `MouseClick`, or other sorts of input processing) and call `DrawFrame` directly whenever idle:

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

I wouldn't expect this to account for a ~33% performance difference but it may be worth looking into it.

2) Is there a way to render on a thread other than the owner of the WinForms control? I doubt it but figured I'd ask.

Yes, you can use off-screen surfaces. However, then the issue becomes how to update your window content. For instance, you can use a picture viewer and set the image obtained from the off-screen surface but this would yield a worse performance.

3) Is there a way to render multiple frames without going through the wrapper layer on each frame but keep the application responsive?

Given the approach you are using, frames are rendered on-demand from your application idle event handler, so no. You can do so using an off-screen rendered as explained in the previous point.

Problem

I have my DirectX11 Engine written in C++, a wrapper in C++ with CLR, and an interface in C#. 1) I am curious about where the bottleneck is in this structure, and I'm wondering if there is a more efficient way to let me host the DirectX11 rendering in a WinForms Control. 2) Is there a way to render on a thread other than the owner of the WinForms control? I doubt it but figured I'd ask. 3) Is there a way to render multiple frames without going through the wrapper layer on each frame but keep the application responsive? I have compared this setup to SlimDX and am actually getting slightly slower FPS when simply clearing the screen and not doing any other API calls. SlimDX ~ 3000 FPS, My Engine ~ 2000 FPS. This isn't a big deal but I am wondering where this 33% difference is coming from as it will probably make a difference later when comparing 20 fps to 30. I will walk through the current setup and describe as much as I can. I am sure along the way people will ask for more info and I'll update as needed. My WinForms GraphicsPanel Control is below. It passes the system messages to the wrapper layer. ``` 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); } } ``` Within the Engine Wrapper I have this function to pass the message from the WinForms control to the native C++ layer. ``` void EngineWrapper::ProcessWindowMessage(int msg, System::IntPtr wParam, System::IntPtr lParam) { m_Engine->ProcessWindowMessage(msg, (void*)wParam, (void*)lParam); } ``` Finally, the Native C++ Engine processes the messages as such: ``` 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); } } ```

Original source