[英]C#: WinForms/WPF Render Loop
I want to make a render loop to render on a WPF Window or a WinForm. 我想制作一个渲染循环以在WPF窗口或WinForm上渲染。 Therefore I want to use SharpGL ( https://sharpgl.codeplex.com/ ). 因此,我想使用SharpGL( https://sharpgl.codeplex.com/ )。 To make my loop I made a thread: 为了创建循环,我创建了一个线程:
public void Run()
{
IsRunning = true;
this.Initialize();
while (IsRunning)
{
Render(/* arguments here */);
// pausing and stuff
}
Dispose();
}
In Render I want to send the Draw Calls to the GPU. 在渲染中,我想将绘图调用发送到GPU。 So far there is no problem. 到目前为止,没有问题。 But Winforms and WPF need their own thread and loop. 但是Winforms和WPF需要它们自己的线程和循环。 So I can't just create a Window and draw onto like in Java with LWJGL ( https://www.lwjgl.org/ ), which I used before. 因此,我不能像以前使用LWJGL( https://www.lwjgl.org/ )那样在Java中创建一个Window并进行绘制。 I have to start another thread, that runs the Form in an Application (I cut out error handling to make it short): 我必须启动另一个线程,该线程在应用程序中运行Form(我删节了错误处理,以简化它):
[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);
}
When my Renderer wants to access the OpenGL-Control on my Form and use it, an error occurs, as WinForms (and WPF) don't want their Controls to be manipulated by other Threads. 当我的渲染器想要访问窗体上的OpenGL控件并使用它时,会发生错误,因为WinForms(和WPF)不希望它们的控件被其他线程操纵。 So maybe an Invoke is an option, but this would delay my drawcalls and become a bottleneck. 因此,也许可以选择“调用”,但这会延迟我的通话时间并成为瓶颈。 A timer isn't an option, too, as it isn't accurate and unflexible... And I simply don't like it. 计时器也不是一种选择,因为它不准确也不灵活……我根本不喜欢它。 And doing everything inside the Window Code may be possible, but I want an application being independent of its Display, so that it can be changed. 并且可以在Window Code中执行所有操作,但是我希望应用程序独立于其Display,以便可以对其进行更改。 It should be an application having a Display not a Display running an application. 它应该是具有显示器的应用程序,而不是运行应用程序的显示器。 In LWJGL I just had the possibility to create and initialize a Display and then simply use it. 在LWJGL中,我可以创建并初始化一个Display,然后简单地使用它。 The only thing to consider was updating it and everything went fine. 唯一需要考虑的是对其进行更新,一切都很好。 So I just want to create a Window in my render thread and draw onto. 因此,我只想在渲染线程中创建一个Window并进行绘制。 If I do it this way, the window just gets unusable and greyish as it needs this .Net-Loop. 如果我这样做,则窗口将变得不可用且呈灰色,因为它需要此.Net-Loop。 Is there any possibility to realize that or does anybody know another way to create Windows? 是否有可能意识到这一点,或者有人知道另一种创建Windows的方法吗? Can I handle the window loop manually? 我可以手动处理窗口循环吗?
Any idea is welcome. 任何想法都欢迎。
If there would be a way to do this with a WPF Window it would be awesome. 如果有一种使用WPF窗口执行此操作的方法,那就太好了。 Then I could have an OpenGL Control and all WPF-Stuff to make an Editor! 然后,我可以使用OpenGL控件和所有WPF-Stuff来创建编辑器!
The solution was to call 解决的办法是打电话
Application.DoEvents();
in my Render Loop manually, as someone told me. 有人告诉我,在我的“渲染循环”中手动进行。 So I didn't had to use Application.Run
and were able to use my Form in the thread of the loop: 因此,我不必使用Application.Run
,就可以在循环线程中使用Form:
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();
}
Based on my question: How to make a render loop in WPF? 基于我的问题: 如何在WPF中进行渲染循环?
WPF render loop WPF渲染循环
The best way to do this is to use the per-frame callbacks provided by the static 最好的方法是使用静态提供的每帧回调
CompositionTarget.Rendering event.
WinForms render loop WinForms渲染循环
Below is the code of a WinForms render loop class that I made based on this blog post : 以下是我根据此博客文章制作的WinForms渲染循环类的代码:
Just use: 只需使用:
WinFormsAppIdleHandler.Instance.ApplicationLoopDoWork += Instance_ApplicationLoopDoWork;
WinFormsAppIdleHandler.Instance.Enabled = true;
Class: 类:
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
}
}
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.