diff --git a/Code/Flowframes.csproj b/Code/Flowframes.csproj
index 4e1d2c9..95dc338 100644
--- a/Code/Flowframes.csproj
+++ b/Code/Flowframes.csproj
@@ -467,6 +467,7 @@
+
Form1.cs
Designer
diff --git a/Code/Form1.cs b/Code/Form1.cs
index daf2372..4137e92 100644
--- a/Code/Form1.cs
+++ b/Code/Form1.cs
@@ -18,6 +18,7 @@ using System.Threading.Tasks;
using System.Linq;
using System.Runtime.InteropServices;
using Flowframes.Media;
+using Flowframes.Utilities;
#pragma warning disable IDE1006
diff --git a/Code/Media/AvProcess.cs b/Code/Media/AvProcess.cs
index 99632c3..5da2ba3 100644
--- a/Code/Media/AvProcess.cs
+++ b/Code/Media/AvProcess.cs
@@ -129,7 +129,7 @@ namespace Flowframes
Logger.Log($"ffprobe -v {settings.LogLevel} {settings.Args}", true, false, "ffmpeg");
if (!asyncOutput)
- return await Task.Run(() => GetProcOutput(ffprobe));
+ return await Task.Run(() => OsUtils.GetProcStdOut(ffprobe));
if (!show)
{
@@ -153,15 +153,6 @@ namespace Flowframes
return processOutput;
}
- private static string GetProcOutput (Process proc)
- {
- proc.Start();
- proc.PriorityClass = ProcessPriorityClass.BelowNormal;
- string output = proc.StandardOutput.ReadToEnd();
- proc.WaitForExit();
- return output;
- }
-
public static string GetFfprobeOutput(string args)
{
Process ffprobe = OsUtils.NewProcess(true);
diff --git a/Code/Os/AiProcess.cs b/Code/Os/AiProcess.cs
index 9014e10..814d5b8 100644
--- a/Code/Os/AiProcess.cs
+++ b/Code/Os/AiProcess.cs
@@ -13,6 +13,7 @@ using ImageMagick;
using Paths = Flowframes.IO.Paths;
using Flowframes.Media;
using System.Drawing;
+using Flowframes.Utilities;
namespace Flowframes.Os
{
@@ -286,7 +287,7 @@ namespace Flowframes.Os
//await RunRifeNcnnMulti(framesPath, outPath, factor, mdl);
await RunRifeNcnnProcess(framesPath, factor, outPath, mdl);
- await DeleteNcnnDupes(outPath, factor);
+ await NcnnUtils.DeleteNcnnDupes(outPath, factor);
}
catch (Exception e)
{
@@ -311,7 +312,7 @@ namespace Flowframes.Os
string ttaStr = Config.GetBool(Config.Key.rifeNcnnUseTta, false) ? "-x" : "";
rifeNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnn.PkgDir).Wrap()} & rife-ncnn-vulkan.exe " +
- $" -v -i {inPath.Wrap()} -o {outPath.Wrap()} {frames} -m {mdl.ToLower()} {ttaStr} {uhdStr} -g {Config.Get(Config.Key.ncnnGpus)} -f {GetNcnnPattern()} -j {GetNcnnThreads()}";
+ $" -v -i {inPath.Wrap()} -o {outPath.Wrap()} {frames} -m {mdl.ToLower()} {ttaStr} {uhdStr} -g {Config.Get(Config.Key.ncnnGpus)} -f {NcnnUtils.GetNcnnPattern()} -j {NcnnUtils.GetNcnnThreads()}";
Logger.Log("cmd.exe " + rifeNcnn.StartInfo.Arguments, true);
@@ -357,7 +358,31 @@ namespace Flowframes.Os
IoUtils.CreateDir(outPath);
Process rifeNcnnVs = OsUtils.NewProcess(!OsUtils.ShowHiddenCmd());
- Logger.Log($"Note: RIFE-NCNN-VS is experimental and may not work as expected with specific Flowframes features, such as FPS limiting and image sequence exporting.");
+ Logger.Log($"Note: RIFE-NCNN-VS is experimental and may not work as expected with certain Flowframes features, such as image sequence exporting.");
+
+ string avDir = Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
+ string rtArgs = $"-window_title \"Flowframes Realtime Interpolation ({Interpolate.currentSettings.inFps.GetString()} FPS x{factor} = {Interpolate.currentSettings.outFps.GetString()} FPS - {mdl})\" -autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} ";
+
+ string pipedTargetArgs = rt ? $"{Path.Combine(avDir, "ffplay").Wrap()} {rtArgs} - " : $"{Path.Combine(avDir, "ffmpeg").Wrap()} -y -i pipe: {await Export.GetPipedFfmpegCmd()}";
+
+ string pkgDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
+ int gpuId = Config.Get(Config.Key.ncnnGpus).Split(',')[0].GetInt();
+
+ VapourSynthUtils.VsSettings vsSettings = new VapourSynthUtils.VsSettings()
+ {
+ InterpSettings = Interpolate.currentSettings,
+ ModelDir = mdl,
+ Factor = factor,
+ Res = res,
+ Uhd = InterpolateUtils.UseUhd(res),
+ GpuId = gpuId,
+ GpuThreads = await NcnnUtils.GetRifeNcnnGpuThreads(res, gpuId, Implementations.rifeNcnnVs),
+ SceneDetectSensitivity = Config.GetBool(Config.Key.scnDetect) ? Config.GetFloat(Config.Key.scnDetectValue) * 0.7f : 0f,
+ Loop = Config.GetBool(Config.Key.enableLoop),
+ MatchDuration = Config.GetBool(Config.Key.fixOutputDuration),
+ Dedupe = Config.GetInt(Config.Key.dedupMode) != 0,
+ Realtime = rt
+ };
if (rt)
{
@@ -367,32 +392,9 @@ namespace Flowframes.Os
else
{
SetProgressCheck(Interpolate.currentMediaFile.FrameCount, factor, Implementations.rifeNcnnVs.LogFilename);
- AiStarted(rifeNcnnVs, 1500, inPath);
+ AiStarted(rifeNcnnVs, 1000, inPath);
}
- string avDir = Path.Combine(Paths.GetPkgPath(), Paths.audioVideoDir);
- string rtArgs = $"-window_title \"Flowframes Realtime Interpolation ({Interpolate.currentSettings.inFps.GetString()} FPS x{factor} = {Interpolate.currentSettings.outFps.GetString()} FPS - {mdl})\" -autoexit -seek_interval {VapourSynthUtils.GetSeekSeconds(Program.mainForm.currInDuration)} ";
-
- string pipedTargetArgs = rt ? $"{Path.Combine(avDir, "ffplay").Wrap()} {rtArgs} - " : $"{Path.Combine(avDir, "ffmpeg").Wrap()} -y -i pipe: {await Export.GetPipedFfmpegCmd()}";
-
- string pkgDir = Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnnVs.PkgDir);
-
- VapourSynthUtils.VsSettings vsSettings = new VapourSynthUtils.VsSettings()
- {
- InterpSettings = Interpolate.currentSettings,
- ModelDir = mdl,
- Factor = factor,
- Res = res,
- Uhd = InterpolateUtils.UseUhd(res),
- GpuId = Config.Get(Config.Key.ncnnGpus).Split(',')[0].GetInt(),
- GpuThreads = GetRifeNcnnVsGpuThreads(res),
- SceneDetectSensitivity = Config.GetBool(Config.Key.scnDetect) ? Config.GetFloat(Config.Key.scnDetectValue) * 0.7f : 0f,
- Loop = Config.GetBool(Config.Key.enableLoop),
- MatchDuration = Config.GetBool(Config.Key.fixOutputDuration),
- Dedupe = Config.GetInt(Config.Key.dedupMode) != 0,
- Realtime = rt
- };
-
rifeNcnnVs.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {pkgDir.Wrap()} & vspipe {VapourSynthUtils.CreateScript(vsSettings).Wrap()} -c y4m - | {pipedTargetArgs}";
Logger.Log("cmd.exe " + rifeNcnnVs.StartInfo.Arguments, true);
@@ -422,7 +424,7 @@ namespace Flowframes.Os
try
{
await RunDainNcnnProcess(framesPath, outPath, factor, mdl, tilesize);
- await DeleteNcnnDupes(outPath, factor);
+ await NcnnUtils.DeleteNcnnDupes(outPath, factor);
}
catch (Exception e)
{
@@ -443,7 +445,7 @@ namespace Flowframes.Os
int targetFrames = ((IoUtils.GetAmountOfFiles(lastInPath, false, "*.*") * factor).RoundToInt());
string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -n {targetFrames} -m {mdl.ToLower()}" +
- $" -t {GetNcnnTilesize(tilesize)} -g {Config.Get(Config.Key.ncnnGpus)} -f {GetNcnnPattern()} -j 2:1:2";
+ $" -t {NcnnUtils.GetNcnnTilesize(tilesize)} -g {Config.Get(Config.Key.ncnnGpus)} -f {NcnnUtils.GetNcnnPattern()} -j 2:1:2";
dain.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args}";
Logger.Log("Running DAIN...", false);
@@ -538,7 +540,7 @@ namespace Flowframes.Os
Logger.Log($"Running IFRNet (NCNN){(await InterpolateUtils.UseUhd() ? " (UHD Mode)" : "")}...", false);
await RunIfrnetNcnnProcess(framesPath, factor, outPath, mdl);
- await DeleteNcnnDupes(outPath, factor);
+ await NcnnUtils.DeleteNcnnDupes(outPath, factor);
}
catch (Exception e)
{
@@ -561,7 +563,7 @@ namespace Flowframes.Os
string ttaStr = ""; // Config.GetBool(Config.Key.rifeNcnnUseTta, false) ? "-x" : "";
ifrnetNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} cd /D {Path.Combine(Paths.GetPkgPath(), Implementations.ifrnetNcnn.PkgDir).Wrap()} & ifrnet-ncnn-vulkan.exe " +
- $" -v -i {inPath.Wrap()} -o {outPath.Wrap()} -m {mdl} {ttaStr} {uhdStr} -g {Config.Get(Config.Key.ncnnGpus)} -f {GetNcnnPattern()} -j {GetNcnnThreads()}";
+ $" -v -i {inPath.Wrap()} -o {outPath.Wrap()} -m {mdl} {ttaStr} {uhdStr} -g {Config.Get(Config.Key.ncnnGpus)} -f {NcnnUtils.GetNcnnPattern()} -j {NcnnUtils.GetNcnnThreads()}";
Logger.Log("cmd.exe " + ifrnetNcnn.StartInfo.Arguments, true);
@@ -700,72 +702,6 @@ namespace Flowframes.Os
InterpolationProgress.UpdateLastFrameFromInterpOutput(line);
}
- static int GetRifeNcnnVsGpuThreads (Size res)
- {
- int threads = 3;
- if(res.Width * res.Height > 2560 * 1440) threads = 2;
- if(res.Width * res.Height > 3840 * 2160) threads = 1;
- return threads;
- }
-
- static string GetNcnnPattern()
- {
- return $"%0{Padding.interpFrames}d{Interpolate.currentSettings.interpExt}";
- }
-
- static string GetNcnnTilesize(int tilesize)
- {
- int gpusAmount = Config.Get(Config.Key.ncnnGpus).Split(',').Length;
- string tilesizeStr = $"{tilesize}";
-
- for (int i = 1; i < gpusAmount; i++)
- tilesizeStr += $",{tilesize}";
-
- return tilesizeStr;
- }
-
- static string GetNcnnThreads(bool forceSingleThread = false)
- {
- int gpusAmount = Config.Get(Config.Key.ncnnGpus).Split(',').Length;
- int procThreads = Config.GetInt(Config.Key.ncnnThreads);
- string progThreadsStr = $"{procThreads}";
-
- for (int i = 1; i < gpusAmount; i++)
- progThreadsStr += $",{procThreads}";
-
- return $"{(forceSingleThread ? 1 : (Interpolate.currentlyUsingAutoEnc ? 2 : 4))}:{progThreadsStr}:4"; // Read threads: 1 for singlethreaded, 2 for autoenc, 4 if order is irrelevant
- }
-
- static async Task DeleteNcnnDupes(string dir, float factor)
- {
- int dupeCount = InterpolateUtils.GetRoundedInterpFramesPerInputFrame(factor);
- var files = IoUtils.GetFileInfosSorted(dir, false).Reverse().Take(dupeCount).ToList();
- Logger.Log($"DeleteNcnnDupes: Calculated dupe count from factor; deleting last {dupeCount} interp frames of {IoUtils.GetAmountOfFiles(dir, false)} ({string.Join(", ", files.Select(x => x.Name))})", true);
-
- int attempts = 4;
-
- while (attempts > 0)
- {
- try
- {
- files.ForEach(x => x.Delete());
- break;
- }
- catch (Exception ex)
- {
- attempts--;
-
- if (attempts < 1)
- {
- Logger.Log($"DeleteNcnnDupes Error: {ex.Message}", true);
- break;
- }
- else
- {
- await Task.Delay(500);
- }
- }
- }
- }
+
}
}
diff --git a/Code/Os/OsUtils.cs b/Code/Os/OsUtils.cs
index d703e85..6d7f7bd 100644
--- a/Code/Os/OsUtils.cs
+++ b/Code/Os/OsUtils.cs
@@ -13,11 +13,24 @@ using Microsoft.VisualBasic.Devices;
using Flowframes.MiscUtils;
using System.Linq;
using Tulpep.NotificationWindow;
+using System.Threading;
namespace Flowframes.Os
{
class OsUtils
{
+ public static string GetProcStdOut(Process proc, bool includeStdErr = false, ProcessPriorityClass priority = ProcessPriorityClass.BelowNormal)
+ {
+ if (includeStdErr)
+ proc.StartInfo.Arguments += " 2>&1";
+
+ proc.Start();
+ proc.PriorityClass = priority;
+ string output = proc.StandardOutput.ReadToEnd();
+ proc.WaitForExit();
+ return output;
+ }
+
public static bool IsUserAdministrator()
{
//bool value to hold our return value
diff --git a/Code/Ui/MainUiFunctions.cs b/Code/Ui/MainUiFunctions.cs
index 8030284..d06bbe8 100644
--- a/Code/Ui/MainUiFunctions.cs
+++ b/Code/Ui/MainUiFunctions.cs
@@ -97,6 +97,7 @@ namespace Flowframes.Ui
{
if (Interpolate.currentSettings == null || !Interpolate.currentSettings.stepByStep) return;
string tmpFolder = InterpolateUtils.GetTempFolderLoc(inpath, outpath);
+
if (Directory.Exists(tmpFolder))
{
int scnFrmAmount = IoUtils.GetAmountOfFiles(Path.Combine(tmpFolder, Paths.scenesDir), false, "*" + Interpolate.currentSettings.interpExt); // TODO: Make this work if the frames extension was changed
@@ -108,6 +109,7 @@ namespace Flowframes.Ui
string msg = $"A temporary folder for this video already exists. It contains {scnFrames}, {srcFrames}, {interpFrames}.";
DialogResult dialogResult = UiUtils.ShowMessageBox($"{msg}\n\nClick \"Yes\" to use the existing files or \"No\" to delete them.", "Use files from existing temp folder?", MessageBoxButtons.YesNo);
+
if (dialogResult == DialogResult.No)
{
IoUtils.TryDeleteIfExists(tmpFolder);
diff --git a/Code/Utilities/NcnnUtils.cs b/Code/Utilities/NcnnUtils.cs
new file mode 100644
index 0000000..2c76510
--- /dev/null
+++ b/Code/Utilities/NcnnUtils.cs
@@ -0,0 +1,120 @@
+using Flowframes.Data;
+using Flowframes.IO;
+using Flowframes.Main;
+using Flowframes.Os;
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+using System.Drawing;
+using System.IO;
+using System.Linq;
+using System.Text;
+using System.Threading.Tasks;
+
+namespace Flowframes.Utilities
+{
+ class NcnnUtils
+ {
+ /// Get amount of GPU Compute Queues (VK) for each GPU
+ public static async Task> GetNcnnGpuComputeQueueCounts ()
+ {
+ Dictionary queueCounts = new Dictionary(); // int gpuId, int queueCount
+
+ Process rifeNcnn = OsUtils.NewProcess(true);
+ rifeNcnn.StartInfo.Arguments = $"{OsUtils.GetCmdArg()} {Path.Combine(Paths.GetPkgPath(), Implementations.rifeNcnn.PkgDir, "rife-ncnn-vulkan.exe")} -i C: -o C:";
+
+ string output = await Task.Run(() => OsUtils.GetProcStdOut(rifeNcnn, true));
+ var queueLines = output.SplitIntoLines().Where(x => x.MatchesWildcard(@"*queueC=*queue*"));
+
+ foreach (var line in queueLines)
+ {
+ int gpuId = line.Split(' ')[0].GetInt();
+ int queueCount = line.Split("queue")[1].Split('[')[1].Split(']')[0].GetInt();
+ Logger.Log($"NCNN: Found GPU {gpuId} with compute queue count {queueCount}", true);
+ queueCounts[gpuId] = queueCount;
+ }
+
+ return queueCounts;
+ }
+
+ public static async Task GetRifeNcnnGpuThreads(Size res, int gpuId, AI ai)
+ {
+ int threads = 3;
+ if (res.Width * res.Height > 2560 * 1440) threads = 2;
+ if (res.Width * res.Height > 3840 * 2160) threads = 1;
+
+ if (threads != 1)
+ {
+ int maxThreads = (await GetNcnnGpuComputeQueueCounts())[gpuId];
+ threads = threads.Clamp(1, maxThreads); // To avoid exceeding the max queue count
+ Logger.Log($"Using {threads}/{maxThreads} GPU threads.", true, false, ai.LogFilename);
+ }
+ else
+ {
+ Logger.Log($"Using {threads} GPU thread.", true, false, ai.LogFilename);
+ }
+
+ return threads;
+ }
+
+ public static string GetNcnnPattern()
+ {
+ return $"%0{Padding.interpFrames}d{Interpolate.currentSettings.interpExt}";
+ }
+
+ public static string GetNcnnTilesize(int tilesize)
+ {
+ int gpusAmount = Config.Get(Config.Key.ncnnGpus).Split(',').Length;
+ string tilesizeStr = $"{tilesize}";
+
+ for (int i = 1; i < gpusAmount; i++)
+ tilesizeStr += $",{tilesize}";
+
+ return tilesizeStr;
+ }
+
+ public static string GetNcnnThreads(bool forceSingleThread = false)
+ {
+ int gpusAmount = Config.Get(Config.Key.ncnnGpus).Split(',').Length;
+ int procThreads = Config.GetInt(Config.Key.ncnnThreads);
+ string progThreadsStr = $"{procThreads}";
+
+ for (int i = 1; i < gpusAmount; i++)
+ progThreadsStr += $",{procThreads}";
+
+ return $"{(forceSingleThread ? 1 : (Interpolate.currentlyUsingAutoEnc ? 2 : 4))}:{progThreadsStr}:4"; // Read threads: 1 for singlethreaded, 2 for autoenc, 4 if order is irrelevant
+ }
+
+ public static async Task DeleteNcnnDupes(string dir, float factor)
+ {
+ int dupeCount = InterpolateUtils.GetRoundedInterpFramesPerInputFrame(factor);
+ var files = IoUtils.GetFileInfosSorted(dir, false).Reverse().Take(dupeCount).ToList();
+ Logger.Log($"DeleteNcnnDupes: Calculated dupe count from factor; deleting last {dupeCount} interp frames of {IoUtils.GetAmountOfFiles(dir, false)} ({string.Join(", ", files.Select(x => x.Name))})", true);
+
+ int attempts = 4;
+
+ while (attempts > 0)
+ {
+ try
+ {
+ files.ForEach(x => x.Delete());
+ break;
+ }
+ catch (Exception ex)
+ {
+ attempts--;
+
+ if (attempts < 1)
+ {
+ Logger.Log($"DeleteNcnnDupes Error: {ex.Message}", true);
+ break;
+ }
+ else
+ {
+ await Task.Delay(500);
+ }
+ }
+ }
+ }
+ }
+}