private void DrawFrame() { if (SlowDownDrawCalls && (UpdateTime.FrameCount & 1) == 1) // skip the draw call about one frame over two. { return; } try { // Initialized if (!profilingDraw.IsInitialized) { profilingDraw = Profiler.Begin(GameProfilingKeys.GameDrawFPS); } // Update profiling data profilingDraw.CheckIfEnabled(); if (!isExiting && GameSystems.IsFirstUpdateDone && !Window.IsMinimized) { drawTime.Update(totalDrawTime, lastFrameElapsedGameTime, singleFrameUpdateTime, drawRunningSlowly, true); if (drawTime.FramePerSecondUpdated) { // TODO: store some GameTime information as attributes in the Profiling using profilingDraw.SetAttribute(..) profilingDraw.Mark("Frame = {0}, Update = {1:0.000}ms, Draw = {2:0.000}ms, FPS = {3:0.00}", drawTime.FrameCount, updateTime.TimePerFrame.TotalMilliseconds, drawTime.TimePerFrame.TotalMilliseconds, drawTime.FramePerSecond); } using (Profiler.Begin(GameProfilingKeys.GameDraw)) { Draw(drawTime); } } } finally { lastFrameElapsedGameTime = TimeSpan.Zero; } }
/// <summary> /// Updates the game's clock and calls Update and Draw. /// </summary> public void Tick() { try { // If this instance is existing, then don't make any further update/draw if (isExiting) { CheckEndRun(); return; } // If this instance is not active, sleep for an inactive sleep time if (!IsActive) { Utilities.Sleep(InactiveSleepTime); return; } // Update the timer if (updateTime.FrameCount < 2) //-> delay timer reset after first draw to avoid important gap in game time space { timer.Reset(); } timer.Tick(); // Update the playTimer timer playTimer.Tick(); // Measure updateTimer updateTimer.Reset(); var elapsedAdjustedTime = timer.ElapsedTimeWithPause; if (forceElapsedTimeToZero) { elapsedAdjustedTime = TimeSpan.Zero; forceElapsedTimeToZero = false; } if (elapsedAdjustedTime > maximumElapsedTime) { elapsedAdjustedTime = maximumElapsedTime; } bool suppressNextDraw = true; int updateCount = 1; var singleFrameElapsedTime = elapsedAdjustedTime; var drawLag = 0L; if (IsFixedTimeStep) { // If the rounded TargetElapsedTime is equivalent to current ElapsedAdjustedTime // then make ElapsedAdjustedTime = TargetElapsedTime. We take the same internal rules as XNA if (Math.Abs(elapsedAdjustedTime.Ticks - TargetElapsedTime.Ticks) < (TargetElapsedTime.Ticks >> 6)) { elapsedAdjustedTime = TargetElapsedTime; } // Update the accumulated time accumulatedElapsedGameTime += elapsedAdjustedTime; // Calculate the number of update to issue updateCount = (int)(accumulatedElapsedGameTime.Ticks / TargetElapsedTime.Ticks); if (IsDrawDesynchronized) { drawLag = accumulatedElapsedGameTime.Ticks % TargetElapsedTime.Ticks; suppressNextDraw = false; } else if (updateCount == 0) { // If there is no need for update, then exit return; } // Calculate a moving average on updateCount lastUpdateCount[nextLastUpdateCountIndex] = updateCount; float updateCountMean = 0; for (int i = 0; i < lastUpdateCount.Length; i++) { updateCountMean += lastUpdateCount[i]; } updateCountMean /= lastUpdateCount.Length; nextLastUpdateCountIndex = (nextLastUpdateCountIndex + 1) % lastUpdateCount.Length; // Test when we are running slowly drawRunningSlowly = updateCountMean > updateCountAverageSlowLimit; // We are going to call Update updateCount times, so we can substract this from accumulated elapsed game time accumulatedElapsedGameTime = new TimeSpan(accumulatedElapsedGameTime.Ticks - (updateCount * TargetElapsedTime.Ticks)); singleFrameElapsedTime = TargetElapsedTime; } else { Array.Clear(lastUpdateCount, 0, lastUpdateCount.Length); nextLastUpdateCountIndex = 0; drawRunningSlowly = false; } bool beginDrawSuccessful = false; try { beginDrawSuccessful = BeginDraw(); // Reset the time of the next frame for (lastFrameElapsedGameTime = TimeSpan.Zero; updateCount > 0 && !isExiting; updateCount--) { updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true); try { UpdateAndProfile(updateTime); if (EarlyExit) { return; } // If there is no exception, then we can draw the frame suppressNextDraw &= suppressDraw; suppressDraw = false; } finally { lastFrameElapsedGameTime += singleFrameElapsedTime; totalUpdateTime += singleFrameElapsedTime; } } // End measuring update time updateTimer.Tick(); singleFrameUpdateTime += updateTimer.ElapsedTime; // Update game time just before calling draw //updateTime.Update(totalUpdateTime, singleFrameElapsedTime, singleFrameUpdateTime, drawRunningSlowly, true); if (!suppressNextDraw) { totalDrawTime = TimeSpan.FromTicks(totalUpdateTime.Ticks + drawLag); DrawInterpolationFactor = drawLag / (float)TargetElapsedTime.Ticks; DrawFrame(); } singleFrameUpdateTime = TimeSpan.Zero; } finally { if (beginDrawSuccessful) { using (Profiler.Begin(GameProfilingKeys.GameEndDraw)) { EndDraw(true); } } CheckEndRun(); } } catch (Exception ex) { Log.Error("Unexpected exception", ex); throw; } }