using System; using System.Diagnostics; using System.Windows.Forms; using Blacklight.Core; namespace Glass.Testbed { public partial class Testbed : Form { public World world; // Windows runs its own message loop while a window is being dragged or resized, so Application.Idle // stops firing and the next tick would otherwise hand the whole drag to Update at once. private static readonly TimeSpan LongestTick = TimeSpan.FromMilliseconds(100); private readonly Stopwatch clock = Stopwatch.StartNew(); private TimeSpan? lastTick = null; public Testbed() { InitializeComponent(); // The control fills three roles here: it owns the surface, draws the hardware cursor, and polls the mouse. world = new World( viewport.DrawableSurface, viewport.InputRouter, viewport.Camera, cursorSurface: viewport, pointerSource: viewport.Input, verticalSync: GraphicsAdapterManager.Singleton.CurrentModule.VerticalSync); } /// /// Whether the pump has nothing waiting, so the loop can run another frame rather than yield to it. /// private static bool IsApplicationIdle() { return !NativeMethods.PeekMessage( out NativeMethods.Message _, windowHandle: IntPtr.Zero, // Messages from any window on this thread. messageFilterMin: 0, messageFilterMax: 0, // Every message. removeMessage: 0 // Leave them on the queue for the pump. ); } private void Tick() { TimeSpan now = clock.Elapsed; TimeSpan elapsed = now - (lastTick ?? now); lastTick = now; world.Tick(elapsed < LongestTick ? elapsed : LongestTick); } // The loop runs flat out and lets the vsync inside the frame's submit pace it, so the pump only gets a turn // once something is actually waiting on it. // See https://docs.microsoft.com/en-us/archive/blogs/tmiller/my-last-post-on-render-loops-hopefully private void ApplicationIdle(object sender, EventArgs e) { while (IsApplicationIdle()) { Tick(); } } protected override void OnLoad(EventArgs e) { base.OnLoad(e); Application.Idle += ApplicationIdle; } protected override void OnFormClosing(FormClosingEventArgs e) { // Ahead of the unbind, so a late idle can't tick a world that has let go of its window. Application.Idle -= ApplicationIdle; world.Unbind(); base.OnFormClosing(e); } } }