1

我想制作一个渲染循环以在 WPF 窗口或 WinForm 上渲染。因此我想使用 SharpGL ( https://sharpgl.codeplex.com/ )。为了制作我的循环,我做了一个线程:

    public void Run()
    {
        IsRunning = true;
        this.Initialize();

        while (IsRunning)
        {
            Render(/* arguments here */);
            // pausing and stuff
        }

        Dispose();
    }

在渲染中,我想将绘制调用发送到 GPU。到目前为止没有问题。但是 Winforms 和 WPF 需要自己的线程和循环。所以我不能像在 Java 中一样使用我之前使用过的 LWJGL ( https://www.lwjgl.org/ ) 创建一个窗口并在上面绘图。我必须启动另一个线程,在应用程序中运行表单(我删除了错误处理以使其简短):

    [STAThread]
    private void HostWinFormsApplication()
    {
        Application.EnableVisualStyles();
        Application.SetCompatibleTextRenderingDefault(true);

        // get Display
        Display = Engine.MainModule.Resolve<IDisplay>();
        Display.Initialize();
        Display.Closing += (s, e) => Stop();

        Application.Run(Display as Form);
    }

当我的渲染器想要访问我的窗体上的 OpenGL-Control 并使用它时,会发生错误,因为 WinForms(和 WPF)不希望它们的控件被其他线程操作。所以也许调用是一种选择,但这会延迟我的绘制调用并成为瓶颈。计时器也不是一种选择,因为它不准确且不灵活......而且我只是不喜欢它。并且在窗口代码中执行所有操作都是可能的,但我希望应用程序独立于其显示,以便可以更改它。它应该是具有显示而不是显示运行应用程序的应用程序。在 LWJGL 中,我可以创建和初始化一个 Display,然后简单地使用它。唯一需要考虑的是更新它,一切都很好。所以我只想在我的渲染线程中创建一个窗口并绘制。如果我这样做,窗口就会变得无法使用并且变灰,因为它需要这个.Net-Loop。有没有可能意识到这一点,或者有人知道创建 Windows 的另一种方法吗?我可以手动处理窗口循环吗?

欢迎任何想法。

如果有一种方法可以使用 WPF 窗口来做到这一点,那就太棒了。然后我可以有一个 OpenGL 控件和所有 WPF-Stuff 来制作一个编辑器!

4

2 回答 2

1

解决方案是致电

Application.DoEvents();

正如有人告诉我的那样,手动在我的渲染循环中。所以我不必使用Application.Run并且能够在循环的线程中使用我的表单:

public void Run()
{
    Application.EnableVisualStyles();
    Application.SetCompatibleTextRenderingDefault(true);

    IsRunning = true;
    this.Initialize();

    MyForm = new CoolRenderForm();
    MyForm.Show();

    while (IsRunning)
    {
        Render(/* arguments here */);
        Application.DoEvents();
        // refresh form
        // pausing and stuff
    }

    Dispose();
}
于 2015-11-21T12:14:31.163 回答
1

基于我的问题:如何在 WPF 中进行渲染循环?

WPF 渲染循环

最好的方法是使用静态提供的每帧回调

CompositionTarget.Rendering event.

WinForms 渲染循环

下面是我根据这篇博文制作的 WinForms 渲染循环类的代码:

只需使用:

WinFormsAppIdleHandler.Instance.ApplicationLoopDoWork += Instance_ApplicationLoopDoWork;
WinFormsAppIdleHandler.Instance.Enabled = true;

班级:

using System;
using System.Runtime.InteropServices;
using System.Threading;
using System.Windows.Forms;

namespace Utilities.UI
{
    /// <summary>
    /// WinFormsAppIdleHandler implements a WinForms Render Loop (max FPS possible).
    /// Reference: http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
    /// </summary>
    public sealed class WinFormsAppIdleHandler
    {
        private readonly object _completedEventLock = new object();
        private event EventHandler _applicationLoopDoWork;

        //PRIVATE Constructor
        private WinFormsAppIdleHandler()
        {
            Enabled = false;
            SleepTime = 5; //You can play/test with this value
            Application.Idle += Application_Idle;
        }

        /// <summary>
        /// Singleton from:
        /// http://csharpindepth.com/Articles/General/Singleton.aspx
        /// </summary>
        private static readonly Lazy<WinFormsAppIdleHandler> lazy = new Lazy<WinFormsAppIdleHandler>(() => new WinFormsAppIdleHandler());
        public static WinFormsAppIdleHandler Instance { get { return lazy.Value; } }

        /// <summary>
        /// Gets or sets if must fire ApplicationLoopDoWork event.
        /// </summary>
        public bool Enabled { get; set; }

        /// <summary>
        /// Gets or sets the minimum time betwen ApplicationLoopDoWork fires.
        /// </summary>
        public int SleepTime { get; set; }

        /// <summary>
        /// Fires while the UI is free to work. Sleeps for "SleepTime" ms.
        /// </summary>
        public event EventHandler ApplicationLoopDoWork
        {
            //Reason of using locks:
            //http://stackoverflow.com/questions/1037811/c-thread-safe-events
            add
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork += value;
            }

            remove
            {
                lock (_completedEventLock)
                    _applicationLoopDoWork -= value;
            }
        }

        /// <summary>
        /// FINALMENTE! Imagem ao vivo sem travar! Muito bom!
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void Application_Idle(object sender, EventArgs e)
        {
            //Try to update interface
            while (Enabled && IsAppStillIdle())
            {
                OnApplicationIdleDoWork(EventArgs.Empty);
                //Give a break to the processor... :)
                //8 ms -> 125 Hz
                //10 ms -> 100 Hz
                Thread.Sleep(SleepTime);
            }
        }

        private void OnApplicationIdleDoWork(EventArgs e)
        {
            var handler = _applicationLoopDoWork;
            if (handler != null)
            {
                handler(this, e);
            }
        }

        /// <summary>
        /// Gets if the app still idle.
        /// </summary>
        /// <returns></returns>
        private static bool IsAppStillIdle()
        {
            bool stillIdle = false;
            try
            {
                Message msg;
                stillIdle = !PeekMessage(out msg, IntPtr.Zero, 0, 0, 0);
            }
            catch (Exception e)
            {
                //Should never get here... I hope...
                MessageBox.Show("IsAppStillIdle() Exception. Message: " + e.Message);
            }
            return stillIdle;
        }

        #region  Unmanaged Get PeekMessage
        // http://blogs.msdn.com/b/tmiller/archive/2005/05/05/415008.aspx
        [System.Security.SuppressUnmanagedCodeSecurity] // We won't use this maliciously
        [DllImport("User32.dll", CharSet = CharSet.Auto)]
        public static extern bool PeekMessage(out Message msg, IntPtr hWnd, uint messageFilterMin, uint messageFilterMax, uint flags);

        #endregion
    }
}
于 2019-09-24T12:19:08.463 回答