public override void Update(GameTime gameTime) { if (dumpTiming.ElapsedMilliseconds > 500) { dumpTiming.Restart(); } else { return; } if (stringBuilderTask != null && !stringBuilderTask.IsCompleted) { return; } stringBuilderTask = new Task(() => { //Advance any profiler that needs it gcProfiler.Tick(); //Copy events from profiler ( this will also clean up the profiler ) //todo do we really need this copy? var eventsCopy = Profiler.GetEvents(); if (eventsCopy == null) { return; } var elapsedTime = eventsCopy.Length > 0 ? eventsCopy[eventsCopy.Length - 1].TimeStamp - eventsCopy[0].TimeStamp : 0; //update strings that need update foreach (var e in eventsCopy) { //gc profiling is a special case if (e.Key == GcProfiling.GcMemoryKey && e.Custom0.HasValue && e.Custom1.HasValue && e.Custom2.HasValue) { gcMemoryStringBuilder.Clear(); gcMemoryStringBuilder.AppendFormat(gcMemoryStringBase, e.Custom0.Value.LongValue, e.Custom2.Value.LongValue, e.Custom1.Value.LongValue); continue; } if (e.Key == GcProfiling.GcCollectionCountKey && e.Custom0.HasValue && e.Custom1.HasValue && e.Custom2.HasValue) { gcCollectionsStringBuilder.Clear(); gcCollectionsStringBuilder.AppendFormat(gcCollectionsStringBase, e.Custom0.Value.IntValue, e.Custom1.Value.IntValue, e.Custom2.Value.IntValue); continue; } if (e.Key == GameProfilingKeys.GameDrawFPS && e.Type == ProfilingMessageType.End) { fpsStatStringBuilder.Clear(); fpsStatStringBuilder.AppendFormat(e.Text, e.Custom0.Value.IntValue, e.Custom1.Value.DoubleValue, e.Custom2.Value.DoubleValue, e.Custom3.Value.FloatValue); continue; } ProfilingResult profilingResult; if (!profilingResultsDictionary.TryGetValue(e.Key, out profilingResult)) { profilingResult.MinTime = long.MaxValue; } if (e.Type == ProfilingMessageType.End) { profilingResult.AccumulatedTime += e.ElapsedTime; if (e.ElapsedTime < profilingResult.MinTime) { profilingResult.MinTime = e.ElapsedTime; } if (e.ElapsedTime > profilingResult.MaxTime) { profilingResult.MaxTime = e.ElapsedTime; } profilingResult.Event = e; } else if (e.Type == ProfilingMessageType.Mark) // counter incremented only Mark! { profilingResult.Count++; } if (e.Custom0.HasValue) { profilingResult.Custom0 = e.Custom0.Value; } if (e.Custom1.HasValue) { profilingResult.Custom1 = e.Custom1.Value; } if (e.Custom2.HasValue) { profilingResult.Custom2 = e.Custom2.Value; } if (e.Custom3.HasValue) { profilingResult.Custom3 = e.Custom3.Value; } if (e.Key == MicroThreadProfilingKeys.ProfilingKey) { scriptsProfilingResultsDictionary[e.Text] = profilingResult; } else { profilingResultsDictionary[e.Key] = profilingResult; } } profilersStringBuilder.Clear(); profilingResults.Clear(); foreach (var profilingResult in profilingResultsDictionary) { if (!profilingResult.Value.Event.HasValue) { continue; } profilingResults.Add(profilingResult.Value); } profilingResultsDictionary.Clear(); if (SortingMode == GameProfilingSorting.ByTime) { profilingResults.Sort((x1, x2) => Math.Sign(x2.AccumulatedTime - x1.AccumulatedTime)); } else { // Can't be null because we skip those events without values // ReSharper disable PossibleInvalidOperationException profilingResults.Sort((x1, x2) => string.Compare(x1.Event.Value.Key.Name, x2.Event.Value.Key.Name, StringComparison.Ordinal)); // ReSharper restore PossibleInvalidOperationException } foreach (var result in profilingResults) { AppendEvent(result, result.Event.Value, elapsedTime); } profilingResults.Clear(); foreach (var profilingResult in scriptsProfilingResultsDictionary) { if (!profilingResult.Value.Event.HasValue) { continue; } profilingResults.Add(profilingResult.Value); } scriptsProfilingResultsDictionary.Clear(); if (SortingMode == GameProfilingSorting.ByTime) { profilingResults.Sort((x1, x2) => Math.Sign(x2.AccumulatedTime - x1.AccumulatedTime)); } else { // Can't be null because we skip those events without values // ReSharper disable PossibleInvalidOperationException profilingResults.Sort((x1, x2) => string.Compare(x1.Event.Value.Key.Name, x2.Event.Value.Key.Name, StringComparison.Ordinal)); // ReSharper restore PossibleInvalidOperationException } foreach (var result in profilingResults) { AppendEvent(result, result.Event.Value, elapsedTime); } lock (stringLock) { gcCollectionsString = gcCollectionsStringBuilder.ToString(); gcMemoryString = gcMemoryStringBuilder.ToString(); profilersString = profilersStringBuilder.ToString(); fpsStatString = fpsStatStringBuilder.ToString(); } }); stringBuilderTask.Start(); }
private void UpdateProfilingStrings() { profilersStringBuilder.Clear(); fpsStatStringBuilder.Clear(); gpuInfoStringBuilder.Clear(); gpuGeneralInfoStringBuilder.Clear(); //Advance any profiler that needs it gcProfiler.Tick(); // calculate elaspsed frames var newDraw = Game.DrawTime.FrameCount; var elapsedFrames = newDraw - lastFrame; lastFrame = newDraw; // Get events from the profiler ( this will also clean up the profiler ) var events = Profiler.GetEvents(FilteringMode == GameProfilingResults.CpuEvents? ProfilingEventType.CpuProfilingEvent : ProfilingEventType.GpuProfilingEvent); if (FilteringMode != GameProfilingResults.Fps) { var containsMarks = false; var tickFrequency = FilteringMode == GameProfilingResults.GpuEvents ? GraphicsDevice.TimestampFrequency : Stopwatch.Frequency; const float kb = 1 << 10; const float mb = 1 << 20; //update strings that need update foreach (var e in events) { //gc profiling is a special case if (e.Key == GcProfiling.GcMemoryKey && e.Custom0.HasValue && e.Custom1.HasValue && e.Custom2.HasValue) { gcMemoryStringBuilder.Clear(); gcMemoryStringBuilder.AppendFormat(gcMemoryStringBase, e.Custom0.Value.LongValue / mb, e.Custom2.Value.LongValue / mb, e.Custom1.Value.LongValue / kb); continue; } if (e.Key == GcProfiling.GcCollectionCountKey && e.Custom0.HasValue && e.Custom1.HasValue && e.Custom2.HasValue) { gcCollectionsStringBuilder.Clear(); gcCollectionsStringBuilder.AppendFormat(gcCollectionsStringBase, e.Custom0.Value.IntValue, e.Custom1.Value.IntValue, e.Custom2.Value.IntValue); continue; } if (e.Key == GameProfilingKeys.GameDrawFPS && e.Type == ProfilingMessageType.End) { continue; } ProfilingResult profilingResult; if (!profilingResultsDictionary.TryGetValue(e.Key, out profilingResult)) { profilingResult.MinTime = long.MaxValue; } if (e.Type == ProfilingMessageType.End) { ++profilingResult.Count; profilingResult.AccumulatedTime += e.ElapsedTime; if (e.ElapsedTime < profilingResult.MinTime) { profilingResult.MinTime = e.ElapsedTime; } if (e.ElapsedTime > profilingResult.MaxTime) { profilingResult.MaxTime = e.ElapsedTime; } profilingResult.Event = e; } else if (e.Type == ProfilingMessageType.Mark) { profilingResult.MarkCount++; containsMarks = true; } if (e.Custom0.HasValue) { profilingResult.Custom0 = e.Custom0.Value; } if (e.Custom1.HasValue) { profilingResult.Custom1 = e.Custom1.Value; } if (e.Custom2.HasValue) { profilingResult.Custom2 = e.Custom2.Value; } if (e.Custom3.HasValue) { profilingResult.Custom3 = e.Custom3.Value; } profilingResultsDictionary[e.Key] = profilingResult; } profilersStringBuilder.Clear(); profilingResults.Clear(); foreach (var profilingResult in profilingResultsDictionary) { if (!profilingResult.Value.Event.HasValue) { continue; } profilingResults.Add(profilingResult.Value); } profilingResultsDictionary.Clear(); if (SortingMode == GameProfilingSorting.ByTime) { profilingResults.Sort((x1, x2) => Math.Sign(x2.AccumulatedTime - x1.AccumulatedTime)); } else { // Can't be null because we skip those events without values // ReSharper disable PossibleInvalidOperationException profilingResults.Sort((x1, x2) => string.Compare(x1.Event.Value.Key.Name, x2.Event.Value.Key.Name, StringComparison.Ordinal)); // ReSharper restore PossibleInvalidOperationException } var availableDisplayHeight = viewportHeight - 2 * TextRowHeight - 3 * TopRowHeight; var elementsPerPage = (int)Math.Floor(availableDisplayHeight / TextRowHeight); numberOfPages = (uint)Math.Ceiling(profilingResults.Count / (float)elementsPerPage); CurrentResultPage = Math.Min(CurrentResultPage, numberOfPages); profilersStringBuilder.AppendFormat("TOTAL | AVG/CALL | MIN/CALL | MAX/CALL | CALLS | "); if (containsMarks) { profilersStringBuilder.AppendFormat("MARKS | "); } profilersStringBuilder.AppendFormat("PROFILING KEY / EXTRA INFO\n"); for (int i = 0; i < Math.Min(profilingResults.Count - (CurrentResultPage - 1) * elementsPerPage, elementsPerPage); i++) { AppendEvent(profilingResults[((int)CurrentResultPage - 1) * elementsPerPage + i], elapsedFrames, tickFrequency, containsMarks); } profilingResults.Clear(); if (numberOfPages > 1) { profilersStringBuilder.AppendFormat("PAGE {0} OF {1}", CurrentResultPage, numberOfPages); } gpuInfoStringBuilder.Clear(); gpuInfoStringBuilder.AppendFormat("Drawn triangles: {0:0.0}k, Draw calls: {1}, Buffer memory: {2:0.00}[MB], Texture memory: {3:0.00}[MB]", trianglesCount / 1000f, drawCallsCount, GraphicsDevice.BuffersMemory / mb, GraphicsDevice.TextureMemory / mb); gpuGeneralInfoStringBuilder.Clear(); gpuGeneralInfoStringBuilder.AppendFormat("Device: {0}, Platform: {1}, Profile: {2}, Resolution: {3}", GraphicsDevice.Adapter.Description, GraphicsDevice.Platform, GraphicsDevice.ShaderProfile, renderTargetSize); } fpsStatStringBuilder.Clear(); fpsStatStringBuilder.AppendFormat("Displaying: {0}, Frame: {1}, Update: {2:0.00}ms, Draw: {3:0.00}ms, FPS: {4:0.00}", FilteringMode, Game.DrawTime.FrameCount, Game.UpdateTime.TimePerFrame.TotalMilliseconds, Game.DrawTime.TimePerFrame.TotalMilliseconds, Game.DrawTime.FramePerSecond); lock (stringLock) { gcCollectionsString = gcCollectionsStringBuilder.ToString(); gcMemoryString = gcMemoryStringBuilder.ToString(); profilersString = profilersStringBuilder.ToString(); fpsStatString = fpsStatStringBuilder.ToString(); gpuInfoString = gpuInfoStringBuilder.ToString(); gpuGeneralInfoString = gpuGeneralInfoStringBuilder.ToString(); } }