/// <summary> /// Updates the specified context. /// </summary> /// <param name="uv">The Ultraviolet context to update.</param> /// <param name="time">Time elapsed since the last update.</param> /// <returns><see langword="true"/> if the host should continue processing; otherwise, <see langword="false"/>.</returns> private Boolean UpdateContext(UltravioletContext uv, UltravioletTime time) { using (UltravioletProfiler.Section(UltravioletProfilerSections.Update)) { uv.Update(time); } return(!uv.Disposed); }
/// <summary> /// Advances the application state by one tick. /// </summary> public void RunOneTick() { var uv = host.Ultraviolet; if (!host.IsActive && InactiveSleepTime.TotalMilliseconds > 0) { Thread.Sleep(InactiveSleepTime); } var syncContext = SynchronizationContext.Current as UltravioletSynchronizationContext; if (syncContext != null) { syncContext.ProcessWorkItems(); } if (IsFixedTimeStep) { if (tickTimer.Elapsed.TotalMilliseconds >= targetElapsedTime.TotalMilliseconds) { tickTimer.Restart(); tickElapsed -= targetElapsedTime.TotalMilliseconds * (int)(tickElapsed / targetElapsedTime.TotalMilliseconds); uv.HandleFrameStart(); const Double CatchUpThreshold = 1.05; if (frameElapsed > 0 && frameElapsed > targetElapsedTime.TotalMilliseconds * CatchUpThreshold) { const Int32 RunningSlowlyFrameCount = 5; runningSlowlyFrames = RunningSlowlyFrameCount; isRunningSlowly = true; const Int32 CatchUpFrameLimit = 10; var catchUpUpdates = Math.Min(CatchUpFrameLimit, (int)(frameElapsed / targetElapsedTime.TotalMilliseconds)); for (int i = 0; i < catchUpUpdates; i++) { timeTrackerUpdate.Increment(targetElapsedTime, isRunningSlowly); timeTrackerDraw.Increment(targetElapsedTime, isRunningSlowly); if (!UpdateContext(uv, timeTrackerUpdate.Time)) { return; } } } else { if (isRunningSlowly) { runningSlowlyFrames--; if (runningSlowlyFrames == 0) { isRunningSlowly = false; } } } frameTimer.Restart(); var uvTimeUpdate = timeTrackerUpdate.Increment(targetElapsedTime, isRunningSlowly); if (!UpdateContext(uv, uvTimeUpdate)) { return; } if (!host.IsSuspended) { var uvTimeDraw = timeTrackerDraw.Increment(targetElapsedTime, isRunningSlowly); using (UltravioletProfiler.Section(UltravioletProfilerSections.Draw)) { uv.Draw(uvTimeDraw); } } uv.HandleFrameEnd(); frameElapsed = frameTimer.Elapsed.TotalMilliseconds; } else { // NOTE: The precision of Sleep() can generally be assumed to be something like 10-15ms, so // we only call it if we know we're going to be waiting a long time and there's no point // in sucking up CPU cycles spinning our loop. var frameDelay = (int)(targetElapsedTime.TotalMilliseconds - tickTimer.Elapsed.TotalMilliseconds); if (frameDelay >= 10) { Thread.Sleep(frameDelay); } } } else { var time = tickTimer.Elapsed.TotalMilliseconds; tickTimer.Restart(); uv.HandleFrameStart(); var uvTimeDelta = TimeSpan.FromTicks((long)(time * TimeSpan.TicksPerMillisecond)); var uvTimeUpdate = timeTrackerUpdate.Increment(uvTimeDelta, false); if (!UpdateContext(uv, uvTimeUpdate)) { return; } if (!host.IsSuspended) { var uvTimeDraw = uvTimeUpdate; using (UltravioletProfiler.Section(UltravioletProfilerSections.Draw)) { uv.Draw(uvTimeDraw); } } uv.HandleFrameEnd(); } }
/// <summary> /// Releases resources associated with the object. /// </summary> public void Dispose() { UltravioletProfiler.EndSection(name); }
/// <summary> /// Called when a frame is completed. /// </summary> public void HandleFrameEnd() { OnFrameEnd(); UltravioletProfiler.EndSection(UltravioletProfilerSections.Frame); }
/// <summary> /// Called when a new frame is started. /// </summary> public void HandleFrameStart() { UltravioletProfiler.BeginSection(UltravioletProfilerSections.Frame); OnFrameStart(); }
/// <inheritdoc/> public void RunOneTick() { var uv = host.Ultraviolet; if (SynchronizationContext.Current is UltravioletSynchronizationContext syncContext) { syncContext.ProcessWorkItems(); } UpdateSystemTimerResolution(); if (InactiveSleepTime.Ticks > 0 && !host.IsActive) { Thread.Sleep(InactiveSleepTime); } var elapsedTicks = tickTimer.Elapsed.Ticks; tickTimer.Restart(); accumulatedElapsedTime += elapsedTicks; if (accumulatedElapsedTime > MaxElapsedTime.Ticks) { accumulatedElapsedTime = MaxElapsedTime.Ticks; } var gameTicksToRun = 0; var timeDeltaDraw = default(TimeSpan); var timeDeltaUpdate = default(TimeSpan); if (IsFixedTimeStep) { gameTicksToRun = (Int32)(accumulatedElapsedTime / TargetElapsedTime.Ticks); if (gameTicksToRun > 0) { lagFrames += (gameTicksToRun == 1) ? -1 : Math.Max(0, gameTicksToRun - 1); if (lagFrames == 0) { runningSlowly = false; } if (lagFrames > 5) { runningSlowly = true; } timeDeltaUpdate = TargetElapsedTime; timeDeltaDraw = TimeSpan.FromTicks(gameTicksToRun * TargetElapsedTime.Ticks); accumulatedElapsedTime -= gameTicksToRun * TargetElapsedTime.Ticks; } else { var frameDelay = (Int32)(TargetElapsedTime.TotalMilliseconds - tickTimer.Elapsed.TotalMilliseconds); if (frameDelay >= 1 + systemTimerPeriod) { Thread.Sleep(frameDelay - 1); } return; } } else { gameTicksToRun = 1; if (forceElapsedTimeToZero) { timeDeltaUpdate = TimeSpan.Zero; forceElapsedTimeToZero = false; } else { timeDeltaUpdate = TimeSpan.FromTicks(elapsedTicks); timeDeltaDraw = timeDeltaUpdate; } accumulatedElapsedTime = 0; runningSlowly = false; } if (gameTicksToRun == 0) { return; } uv.HandleFrameStart(); for (var i = 0; i < gameTicksToRun; i++) { var updateTime = timeTrackerUpdate.Increment(timeDeltaUpdate, runningSlowly); if (!UpdateContext(uv, updateTime)) { return; } } if (!host.IsSuspended) { var drawTime = timeTrackerDraw.Increment(timeDeltaDraw, runningSlowly); using (UltravioletProfiler.Section(UltravioletProfilerSections.Draw)) { uv.Draw(drawTime); } } uv.HandleFrameEnd(); }