RIFE-NCNN-VS: Limit GPU thread count to max available (compute queue count)

This commit is contained in:
n00mkrad
2022-07-25 10:37:44 +02:00
parent e6034a87b4
commit 5bf8245885
7 changed files with 172 additions and 108 deletions

View File

@@ -467,6 +467,7 @@
<Compile Include="Ui\UiUtils.cs" />
<Compile Include="Ui\QuickSettingsTab.cs" />
<Compile Include="Utilities\ColorDataUtils.cs" />
<Compile Include="Utilities\NcnnUtils.cs" />
<EmbeddedResource Include="Form1.resx">
<DependentUpon>Form1.cs</DependentUpon>
<SubType>Designer</SubType>

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}
}
}
}
}

View File

@@ -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

View File

@@ -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);

120
Code/Utilities/NcnnUtils.cs Normal file
View File

@@ -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
{
/// <summary> Get amount of GPU Compute Queues (VK) for each GPU </summary>
public static async Task<Dictionary<int, int>> GetNcnnGpuComputeQueueCounts ()
{
Dictionary<int, int> queueCounts = new Dictionary<int, int>(); // 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<int> 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);
}
}
}
}
}
}