From 8824a17f32f23d782f1a54af4f6f21296e95330c Mon Sep 17 00:00:00 2001 From: N00MKRAD <61149547+n00mkrad@users.noreply.github.com> Date: Thu, 26 Sep 2024 18:51:07 +0200 Subject: [PATCH] Optimize interpolation progress display, use rolling avg. for fps --- CodeLegacy/Flowframes.csproj | 1 + CodeLegacy/Os/AiProcess.cs | 4 +- CodeLegacy/Ui/InterpolationProgress.cs | 84 ++++++++++++-------------- CodeLegacy/Utilities/RollingAverage.cs | 69 +++++++++++++++++++++ 4 files changed, 112 insertions(+), 46 deletions(-) create mode 100644 CodeLegacy/Utilities/RollingAverage.cs diff --git a/CodeLegacy/Flowframes.csproj b/CodeLegacy/Flowframes.csproj index 9e14d44..4d4baf6 100644 --- a/CodeLegacy/Flowframes.csproj +++ b/CodeLegacy/Flowframes.csproj @@ -517,6 +517,7 @@ + Form1.cs Designer diff --git a/CodeLegacy/Os/AiProcess.cs b/CodeLegacy/Os/AiProcess.cs index d7ddf22..38926b6 100644 --- a/CodeLegacy/Os/AiProcess.cs +++ b/CodeLegacy/Os/AiProcess.cs @@ -104,7 +104,7 @@ namespace Flowframes.Os if (!Interpolate.currentSettings.ai.Piped) InterpolationProgress.UpdateInterpProgress(interpFramesCount, InterpolationProgress.targetFrames); - string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}. Peak Output FPS: {InterpolationProgress.peakFpsOut.ToString("0.00")}"; + string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}. Output FPS: {InterpolationProgress.LastFps.ToString("0.0")}"; if (Interpolate.currentlyUsingAutoEnc && AutoEncode.HasWorkToDo()) { @@ -113,7 +113,7 @@ namespace Flowframes.Os } if (Interpolate.currentSettings.outSettings.Format != Enums.Output.Format.Realtime) - Logger.Log(logStr); + Logger.Log(logStr, replaceLastLine: Logger.LastUiLine.Contains("FPS")); processTime.Stop(); diff --git a/CodeLegacy/Ui/InterpolationProgress.cs b/CodeLegacy/Ui/InterpolationProgress.cs index 1d6b8f6..d09876b 100644 --- a/CodeLegacy/Ui/InterpolationProgress.cs +++ b/CodeLegacy/Ui/InterpolationProgress.cs @@ -33,7 +33,9 @@ namespace Flowframes.Ui progCheckRunning = true; deletedFramesCount = 0; lastFrame = 0; - peakFpsOut = 0f; + LastFps = 0f; + _framesAtTime = null; + _fpsRollAvg.Reset(); Program.mainForm.SetProgress(0); } @@ -125,9 +127,9 @@ namespace Flowframes.Ui } public static int interpolatedInputFramesCount; - public static float peakFpsOut; - - public static int previewUpdateRateMs = 200; + public static float LastFps; + private static Tuple _framesAtTime = null; + private static Utilities.RollingAverage _fpsRollAvg = new Utilities.RollingAverage(10); public static void UpdateInterpProgress(int frames, int target, string latestFramePath = "") { @@ -136,43 +138,48 @@ namespace Flowframes.Ui //ResumeUtils.Save(); target = (target / Interpolate.InterpProgressMultiplier).RoundToInt(); frames = frames.Clamp(0, target); - int percent = (int)Math.Round(((float)frames / target) * 100f); + + if (_framesAtTime == null) + { + _framesAtTime = new Tuple(frames, DateTime.Now); + } + + if (frames > _framesAtTime.Item1 && frames > 0) + { + float fpsCurrent = (frames - _framesAtTime.Item1) / (float)(DateTime.Now - _framesAtTime.Item2).TotalSeconds; + _fpsRollAvg.AddDataPoint(fpsCurrent); + _framesAtTime = new Tuple(frames, DateTime.Now); + } + + int percent = (((float)frames / target) * 100f).RoundToInt(); Program.mainForm.SetProgress(percent); - float generousTime = ((AiProcess.processTime.ElapsedMilliseconds - AiProcess.lastStartupTimeMs) / 1000f); - float fps = ((float)frames / generousTime).Clamp(0, 9999); - string fpsIn = (fps / currentFactor).ToString("0.00"); - string fpsOut = fps.ToString("0.00"); + float fps = _fpsRollAvg.CurrentSize > 2 ? (float)_fpsRollAvg.Average : 0f; + string fpsIn = (fps / currentFactor).ToString("0.0"); + string fpsOut = fps.ToString("0.0"); + LastFps = fps; - if (fps > peakFpsOut) - peakFpsOut = fps; + float eta = fps == 0f ? 0f : (target - frames) * (1f / fps); // ETA = Remaining frames * seconds per frame. Set to 0 if FPS is 0 to avoid div. by zero + string etaStr = eta > 3f ? $" - ETA: {FormatUtils.Time(TimeSpan.FromSeconds(eta), false)}" : ""; + string timeStr = AiProcess.processTime.ElapsedMilliseconds > 0 ? $" - Time: {FormatUtils.Time(AiProcess.processTime.Elapsed)}" : ""; - float secondsPerFrame = generousTime / (float)frames; - int framesLeft = target - frames; - float eta = framesLeft * secondsPerFrame; - string etaStr = FormatUtils.Time(new TimeSpan(0, 0, eta.RoundToInt()), false); + bool replaceLine = Logger.LastUiLine.MatchesWildcard("Interpolated*/* Frames *"); - bool replaceLine = Regex.Split(Logger.textbox.Text, "\r\n|\r|\n").Last().Contains("Average Speed: "); - - string logStr = $"Interpolated {frames}/{target} Frames ({percent}%) - Average Speed: {fpsIn} FPS In / {fpsOut} FPS Out - "; - logStr += $"Time: {FormatUtils.Time(AiProcess.processTime.Elapsed)} - ETA: {etaStr}"; + string logStr = $"Interpolated {frames}/{target} Frames ({percent}%) - Speed: {fpsIn} FPS In / {fpsOut} FPS Out{timeStr}{etaStr}"; if (AutoEncode.busy) logStr += " - Encoding..."; Logger.Log(logStr, false, replaceLine); - try - { - if (latestFramePath.IsNotEmpty() && frames > currentFactor) - { - if (bigPreviewForm == null && (preview == null || !preview.Visible) /* ||Program.mainForm.WindowState != FormWindowState.Minimized */ /* || !Program.mainForm.IsInFocus()*/) return; // Skip if the preview is not visible or the form is not in focus - if (timeSinceLastPreviewUpdate.IsRunning && timeSinceLastPreviewUpdate.ElapsedMilliseconds < previewUpdateRateMs) return; - Image img = IoUtils.GetImage(latestFramePath, false, false); - SetPreviewImg(img); - } - } - catch (Exception e) - { - //Logger.Log("Error updating preview: " + e.Message, true); - } + // try + // { + // if (latestFramePath.IsNotEmpty() && frames > currentFactor) + // { + // if (bigPreviewForm == null && (preview == null || !preview.Visible) /* ||Program.mainForm.WindowState != FormWindowState.Minimized */ /* || !Program.mainForm.IsInFocus()*/) return; // Skip if the preview is not visible or the form is not in focus + // } + // } + // catch (Exception e) + // { + // //Logger.Log("Error updating preview: " + e.Message, true); + // } } public static async Task DeleteInterpolatedInputFrames() @@ -193,16 +200,5 @@ namespace Flowframes.Ui } public static Stopwatch timeSinceLastPreviewUpdate = new Stopwatch(); - - public static void SetPreviewImg(Image img) - { - if (img == null || preview == null) - return; - - timeSinceLastPreviewUpdate.Restart(); - - preview.Image = img; - bigPreviewForm?.SetImage(img); - } } } diff --git a/CodeLegacy/Utilities/RollingAverage.cs b/CodeLegacy/Utilities/RollingAverage.cs new file mode 100644 index 0000000..b223525 --- /dev/null +++ b/CodeLegacy/Utilities/RollingAverage.cs @@ -0,0 +1,69 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Flowframes.Utilities +{ + public class RollingAverage where T : struct + { + public int CurrentSize { get => _values.Count; } + public double Average { get => GetAverage(); } + + private Queue _values; + public Queue Queue { get => _values; } + private int _size; + + public RollingAverage(int size) + { + _values = new Queue(size); + _size = size; + } + + public void AddDataPoint(T dataPoint) + { + if (_values.Count >= _size) + { + _values.Dequeue(); + } + + _values.Enqueue(dataPoint); + } + + public double GetAverage() + { + // Convert the values to double before averaging, this is necessary because Average() does not work directly on generic types + if(_values == null || _values.Count == 0) + { + return 0; + } + + return _values.Select(val => Convert.ToDouble(val)).Average(); + } + + public double GetAverage(int lastXSamples) + { + if (lastXSamples <= 0) + { + lastXSamples = 1; + } + else if (lastXSamples > _values.Count) + { + lastXSamples = _values.Count; + } + + // Take the last X samples and calculate the average + return _values.Skip(Math.Max(0, _values.Count - lastXSamples)).Select(val => Convert.ToDouble(val)).Average(); + } + + public double GetAverage(float percentile) + { + int lastXSamples = (int)Math.Ceiling(_size * percentile); + return GetAverage(lastXSamples); + } + + public void Reset() + { + _values.Clear(); + } + } +}