2020-11-23 16:51:05 +01:00
using Flowframes.IO ;
using System ;
using System.Collections.Generic ;
using System.Diagnostics ;
using System.IO ;
using System.Linq ;
using System.Runtime.InteropServices ;
using System.Text ;
using System.Threading.Tasks ;
using Flowframes.OS ;
using Flowframes.UI ;
using Flowframes.Main ;
2020-12-04 16:53:39 +01:00
using Flowframes.Data ;
2020-12-27 22:52:14 +01:00
using Flowframes.MiscUtils ;
2020-11-23 16:51:05 +01:00
namespace Flowframes
{
class AiProcess
{
2020-11-24 12:28:47 +01:00
public static bool hasShownError ;
2020-11-23 16:51:05 +01:00
public static Process currentAiProcess ;
public static Stopwatch processTime = new Stopwatch ( ) ;
2020-11-26 00:07:45 +01:00
public static Stopwatch processTimeMulti = new Stopwatch ( ) ;
2020-11-23 16:51:05 +01:00
2020-11-25 14:04:31 +01:00
public static int lastStartupTimeMs = 1000 ;
2020-12-20 21:25:34 +01:00
static string lastInPath ;
2020-11-25 14:04:31 +01:00
2020-12-02 00:44:43 +01:00
public static Dictionary < string , string > filenameMap = new Dictionary < string , string > ( ) ; // TODO: Store on disk instead for crashes?
2020-12-20 21:25:34 +01:00
static void AiStarted ( Process proc , int startupTimeMs , int factor , string inPath = "" )
2020-11-23 16:51:05 +01:00
{
2020-11-25 14:04:31 +01:00
lastStartupTimeMs = startupTimeMs ;
2020-11-24 12:28:47 +01:00
processTime . Restart ( ) ;
currentAiProcess = proc ;
2020-12-20 21:25:34 +01:00
lastInPath = string . IsNullOrWhiteSpace ( inPath ) ? Interpolate . current . framesFolder : inPath ;
int frames = IOUtils . GetAmountOfFiles ( lastInPath , false , "*.png" ) ;
InterpolateUtils . currentFactor = factor ;
InterpolateUtils . targetFrames = ( frames * factor ) - ( factor - 1 ) ;
2020-11-24 12:28:47 +01:00
hasShownError = false ;
}
2020-11-23 16:51:05 +01:00
2021-01-06 21:44:09 +01:00
static async Task AiFinished ( string aiName )
2020-11-30 20:32:33 +01:00
{
Program . mainForm . SetProgress ( 100 ) ;
2020-12-20 21:25:34 +01:00
InterpolateUtils . UpdateInterpProgress ( IOUtils . GetAmountOfFiles ( Interpolate . current . interpFolder , false , "*.png" ) , InterpolateUtils . targetFrames ) ;
2020-11-30 20:32:33 +01:00
string logStr = $"Done running {aiName} - Interpolation took {FormatUtils.Time(processTime.Elapsed)}" ;
2020-12-01 16:54:12 +01:00
if ( Interpolate . currentlyUsingAutoEnc & & AutoEncode . HasWorkToDo ( ) )
2020-11-30 20:32:33 +01:00
logStr + = " - Waiting for encoding to finish..." ;
Logger . Log ( logStr ) ;
processTime . Stop ( ) ;
2021-01-06 23:33:00 +01:00
while ( Interpolate . currentlyUsingAutoEnc & & Program . busy )
2021-01-06 21:44:09 +01:00
{
2021-01-07 11:02:43 +01:00
if ( AvProcess . lastProcess ! = null & & ! AvProcess . lastProcess . HasExited & & AvProcess . lastTask = = AvProcess . TaskType . Encode )
2021-01-06 22:51:04 +01:00
{
string lastLine = AvProcess . lastOutputFfmpeg . SplitIntoLines ( ) . Last ( ) ;
2021-01-06 23:33:00 +01:00
Logger . Log ( lastLine . Trim ( ) . TrimWhitespaces ( ) , false , Logger . GetLastLine ( ) . Contains ( "frame" ) ) ;
2021-01-06 22:51:04 +01:00
}
2021-01-06 21:44:09 +01:00
await Task . Delay ( 1000 ) ;
}
2020-11-30 20:32:33 +01:00
}
2020-11-24 12:28:47 +01:00
public static async Task RunDainNcnn ( string framesPath , string outPath , int targetFrames , int tilesize )
{
2020-12-10 16:10:52 +01:00
string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -n {targetFrames} -t {GetNcnnTilesize(tilesize)} -g {Config.Get(" ncnnGpus ")}" ;
2020-11-23 16:51:05 +01:00
string dainDir = Path . Combine ( Paths . GetPkgPath ( ) , Path . GetFileNameWithoutExtension ( Packages . dainNcnn . fileName ) ) ;
Process dain = OSUtils . NewProcess ( ! OSUtils . ShowHiddenCmd ( ) ) ;
2020-12-20 21:25:34 +01:00
AiStarted ( dain , 1500 , Interpolate . current . interpFactor ) ;
dain . StartInfo . Arguments = $"{OSUtils.GetCmdArg()} cd /D {dainDir.Wrap()} & dain-ncnn-vulkan.exe {args} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}" ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "Running DAIN..." , false ) ;
Logger . Log ( "cmd.exe " + dain . StartInfo . Arguments , true ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
2020-12-27 22:52:14 +01:00
dain . OutputDataReceived + = ( sender , outLine ) = > { LogOutput ( "[O] " + outLine . Data , "dain-ncnn-log" ) ; } ;
dain . ErrorDataReceived + = ( sender , outLine ) = > { LogOutput ( "[E] " + outLine . Data , "dain-ncnn-log" ) ; } ;
2020-11-23 16:51:05 +01:00
}
dain . Start ( ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
dain . BeginOutputReadLine ( ) ;
dain . BeginErrorReadLine ( ) ;
}
while ( ! dain . HasExited )
await Task . Delay ( 1 ) ;
2020-11-25 17:27:15 +01:00
if ( Interpolate . canceled ) return ;
2020-12-04 16:53:39 +01:00
if ( ! Interpolate . currentlyUsingAutoEnc )
2020-12-20 21:25:34 +01:00
IOUtils . ZeroPadDir ( outPath , InterpolateUtils . GetOutExt ( ) , Padding . interpFrames ) ;
2020-12-04 16:53:39 +01:00
2020-11-30 20:32:33 +01:00
AiFinished ( "DAIN" ) ;
2020-11-23 16:51:05 +01:00
}
public static async Task RunCainNcnnMulti ( string framesPath , string outPath , int tilesize , int times )
{
2020-11-26 00:07:45 +01:00
processTimeMulti . Restart ( ) ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "Running CAIN..." , false ) ;
2020-12-06 18:49:53 +01:00
string args = $" -v -i {framesPath.Wrap()} -o {outPath.Wrap()} -g {Config.Get(" ncnnGpus ")}" ;
2020-11-23 16:51:05 +01:00
await RunCainPartial ( args ) ;
if ( times = = 4 | | times = = 8 ) // #2
{
2020-11-25 17:27:15 +01:00
if ( Interpolate . canceled ) return ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "Re-Running CAIN for 4x interpolation..." , false ) ;
string run1ResultsPath = outPath + "-run1" ;
IOUtils . TryDeleteIfExists ( run1ResultsPath ) ;
Directory . Move ( outPath , run1ResultsPath ) ;
Directory . CreateDirectory ( outPath ) ;
2020-12-06 18:49:53 +01:00
args = $" -v -i {run1ResultsPath.Wrap()} -o {outPath.Wrap()} -g {Config.Get(" ncnnGpus ")}" ;
2020-11-23 16:51:05 +01:00
await RunCainPartial ( args ) ;
2020-11-26 20:52:11 +01:00
IOUtils . TryDeleteIfExists ( run1ResultsPath ) ;
2020-11-23 16:51:05 +01:00
}
if ( times = = 8 ) // #3
{
2020-11-25 17:27:15 +01:00
if ( Interpolate . canceled ) return ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "Re-Running CAIN for 8x interpolation..." , false ) ;
string run2ResultsPath = outPath + "-run2" ;
IOUtils . TryDeleteIfExists ( run2ResultsPath ) ;
Directory . Move ( outPath , run2ResultsPath ) ;
Directory . CreateDirectory ( outPath ) ;
2020-12-06 18:49:53 +01:00
args = $" -v -i {run2ResultsPath.Wrap()} -o {outPath.Wrap()} -g {Config.Get(" ncnnGpus ")}" ;
2020-11-23 16:51:05 +01:00
await RunCainPartial ( args ) ;
2020-11-26 20:52:11 +01:00
IOUtils . TryDeleteIfExists ( run2ResultsPath ) ;
2020-11-23 16:51:05 +01:00
}
2020-11-25 17:27:15 +01:00
if ( Interpolate . canceled ) return ;
2020-12-04 16:53:39 +01:00
if ( ! Interpolate . currentlyUsingAutoEnc )
2020-12-20 21:25:34 +01:00
IOUtils . ZeroPadDir ( outPath , InterpolateUtils . GetOutExt ( ) , Padding . interpFrames ) ;
2020-11-25 17:27:15 +01:00
2020-11-30 20:32:33 +01:00
AiFinished ( "CAIN" ) ;
2020-11-23 16:51:05 +01:00
}
static async Task RunCainPartial ( string args )
{
string cainDir = Path . Combine ( Paths . GetPkgPath ( ) , Path . GetFileNameWithoutExtension ( Packages . cainNcnn . fileName ) ) ;
string cainExe = "cain-ncnn-vulkan.exe" ;
Process cain = OSUtils . NewProcess ( ! OSUtils . ShowHiddenCmd ( ) ) ;
2020-12-20 21:25:34 +01:00
AiStarted ( cain , 1500 , 2 ) ;
cain . StartInfo . Arguments = $"{OSUtils.GetCmdArg()} cd /D {cainDir.Wrap()} & {cainExe} {args} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}" ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "cmd.exe " + cain . StartInfo . Arguments , true ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
2020-12-27 22:52:14 +01:00
cain . OutputDataReceived + = ( sender , outLine ) = > { LogOutput ( "[O] " + outLine . Data , "cain-ncnn-log" ) ; } ;
cain . ErrorDataReceived + = ( sender , outLine ) = > { LogOutput ( "[E] " + outLine . Data , "cain-ncnn-log" ) ; } ;
2020-11-23 16:51:05 +01:00
}
cain . Start ( ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
cain . BeginOutputReadLine ( ) ;
cain . BeginErrorReadLine ( ) ;
}
while ( ! cain . HasExited ) await Task . Delay ( 1 ) ;
}
public static async Task RunRifeCuda ( string framesPath , int interpFactor )
{
string rifeDir = Path . Combine ( Paths . GetPkgPath ( ) , Path . GetFileNameWithoutExtension ( Packages . rifeCuda . fileName ) ) ;
2020-12-17 23:26:25 +01:00
string script = "inference_video.py" ;
2021-01-05 17:23:37 +01:00
string uhdStr = InterpolateUtils . UseUHD ( ) ? "--UHD" : "" ;
2020-12-20 21:25:34 +01:00
string args = $" --img {framesPath.Wrap()} --exp {(int)Math.Log(interpFactor, 2)} {uhdStr} --imgformat {InterpolateUtils.GetOutExt()} --output {Paths.interpDir}" ;
2020-12-03 14:53:18 +01:00
2020-12-12 20:25:03 +01:00
if ( ! File . Exists ( Path . Combine ( rifeDir , script ) ) )
{
Interpolate . Cancel ( "RIFE script not found! Make sure you didn't modify any files." ) ;
return ;
}
2020-11-23 16:51:05 +01:00
Process rifePy = OSUtils . NewProcess ( ! OSUtils . ShowHiddenCmd ( ) ) ;
2020-12-20 21:25:34 +01:00
AiStarted ( rifePy , 3500 , Interpolate . current . interpFactor ) ;
2020-11-26 00:07:45 +01:00
rifePy . StartInfo . Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeCuda).Wrap()} & " +
2021-01-04 14:27:34 +01:00
$"set CUDA_VISIBLE_DEVICES={Config.Get(" torchGpus ")} & {Python.GetPyCmd()} {script} {args}" ;
2021-01-05 17:23:37 +01:00
Logger . Log ( $"Running RIFE {(InterpolateUtils.UseUHD() ? " ( UHD Mode ) " : " ")} ({script})..." . TrimWhitespaces ( ) , false ) ;
2020-11-23 16:51:05 +01:00
Logger . Log ( "cmd.exe " + rifePy . StartInfo . Arguments , true ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
2020-12-27 22:52:14 +01:00
rifePy . OutputDataReceived + = ( sender , outLine ) = > { LogOutput ( "[O] " + outLine . Data , "rife-cuda-log" ) ; } ;
rifePy . ErrorDataReceived + = ( sender , outLine ) = > { LogOutput ( "[E] " + outLine . Data , "rife-cuda-log" ) ; } ;
2020-11-23 16:51:05 +01:00
}
rifePy . Start ( ) ;
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
rifePy . BeginOutputReadLine ( ) ;
rifePy . BeginErrorReadLine ( ) ;
}
2020-11-26 00:07:45 +01:00
while ( ! rifePy . HasExited ) await Task . Delay ( 1 ) ;
2020-11-30 20:32:33 +01:00
AiFinished ( "RIFE" ) ;
2020-11-25 14:04:31 +01:00
}
2020-11-26 00:07:45 +01:00
public static async Task RunRifeNcnnMulti ( string framesPath , string outPath , int tilesize , int times )
{
processTimeMulti . Restart ( ) ;
2021-01-05 23:13:29 +01:00
Logger . Log ( $"Running RIFE{(InterpolateUtils.UseUHD() ? " ( UHD Mode ) " : " ")}..." , false ) ;
2020-11-26 00:07:45 +01:00
2020-12-04 16:53:39 +01:00
bool useAutoEnc = Interpolate . currentlyUsingAutoEnc ;
if ( times > 2 )
AutoEncode . paused = true ; // Disable autoenc until the last iteration
2020-12-20 21:25:34 +01:00
await RunRifePartial ( framesPath , outPath ) ;
2020-11-26 00:07:45 +01:00
if ( times = = 4 | | times = = 8 ) // #2
{
if ( Interpolate . canceled ) return ;
Logger . Log ( "Re-Running RIFE for 4x interpolation..." , false ) ;
string run1ResultsPath = outPath + "-run1" ;
IOUtils . TryDeleteIfExists ( run1ResultsPath ) ;
Directory . Move ( outPath , run1ResultsPath ) ;
Directory . CreateDirectory ( outPath ) ;
2020-12-04 16:53:39 +01:00
if ( useAutoEnc & & times = = 4 )
AutoEncode . paused = false ;
2020-12-20 21:25:34 +01:00
await RunRifePartial ( run1ResultsPath , outPath ) ;
2020-11-26 20:52:11 +01:00
IOUtils . TryDeleteIfExists ( run1ResultsPath ) ;
2020-11-26 00:07:45 +01:00
}
if ( times = = 8 ) // #3
{
if ( Interpolate . canceled ) return ;
Logger . Log ( "Re-Running RIFE for 8x interpolation..." , false ) ;
string run2ResultsPath = outPath + "-run2" ;
IOUtils . TryDeleteIfExists ( run2ResultsPath ) ;
Directory . Move ( outPath , run2ResultsPath ) ;
Directory . CreateDirectory ( outPath ) ;
2020-12-04 16:53:39 +01:00
if ( useAutoEnc & & times = = 8 )
AutoEncode . paused = false ;
2020-12-20 21:25:34 +01:00
await RunRifePartial ( run2ResultsPath , outPath ) ;
2020-11-26 20:52:11 +01:00
IOUtils . TryDeleteIfExists ( run2ResultsPath ) ;
2020-11-26 00:07:45 +01:00
}
if ( Interpolate . canceled ) return ;
2020-12-04 16:53:39 +01:00
if ( ! Interpolate . currentlyUsingAutoEnc )
2020-12-20 21:25:34 +01:00
IOUtils . ZeroPadDir ( outPath , InterpolateUtils . GetOutExt ( ) , Padding . interpFrames ) ;
2020-11-26 00:07:45 +01:00
2020-11-30 20:32:33 +01:00
AiFinished ( "RIFE" ) ;
2020-11-26 00:07:45 +01:00
}
2020-12-20 21:25:34 +01:00
static async Task RunRifePartial ( string inPath , string outPath )
2020-11-26 00:07:45 +01:00
{
Process rifeNcnn = OSUtils . NewProcess ( ! OSUtils . ShowHiddenCmd ( ) ) ;
2020-12-20 21:25:34 +01:00
AiStarted ( rifeNcnn , 1500 , 2 , inPath ) ;
2021-01-05 17:23:37 +01:00
string uhdStr = InterpolateUtils . UseUHD ( ) ? "-u" : "" ;
2020-12-20 21:25:34 +01:00
rifeNcnn . StartInfo . Arguments = $"{OSUtils.GetCmdArg()} cd /D {PkgUtils.GetPkgFolder(Packages.rifeNcnn).Wrap()} & rife-ncnn-vulkan.exe " +
2021-01-05 17:23:37 +01:00
$" -v -i {inPath.Wrap()} -o {outPath.Wrap()} -m rife1.7 {uhdStr} -g {Config.Get(" ncnnGpus ")} -f {InterpolateUtils.GetOutExt()} -j {GetNcnnThreads()}" ;
2020-11-26 00:07:45 +01:00
Logger . Log ( "cmd.exe " + rifeNcnn . StartInfo . Arguments , true ) ;
2021-01-05 17:23:37 +01:00
2020-11-26 00:07:45 +01:00
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
2020-12-27 22:52:14 +01:00
rifeNcnn . OutputDataReceived + = ( sender , outLine ) = > { LogOutput ( "[O] " + outLine . Data , "rife-ncnn-log" ) ; } ;
rifeNcnn . ErrorDataReceived + = ( sender , outLine ) = > { LogOutput ( "[E] " + outLine . Data , "rife-ncnn-log" ) ; } ;
2020-11-26 00:07:45 +01:00
}
2021-01-05 17:23:37 +01:00
2020-11-26 00:07:45 +01:00
rifeNcnn . Start ( ) ;
2021-01-05 17:23:37 +01:00
2020-11-26 00:07:45 +01:00
if ( ! OSUtils . ShowHiddenCmd ( ) )
{
rifeNcnn . BeginOutputReadLine ( ) ;
rifeNcnn . BeginErrorReadLine ( ) ;
}
2021-01-05 17:23:37 +01:00
2020-11-26 00:07:45 +01:00
while ( ! rifeNcnn . HasExited ) await Task . Delay ( 1 ) ;
}
2020-11-23 16:51:05 +01:00
static void LogOutput ( string line , string logFilename )
{
2020-12-09 14:32:43 +01:00
if ( string . IsNullOrWhiteSpace ( line ) | | line . Length < 6 )
2020-11-23 16:51:05 +01:00
return ;
Logger . LogToFile ( line , false , logFilename ) ;
2020-11-25 17:27:15 +01:00
if ( line . Contains ( "ff:nocuda-cpu" ) )
Logger . Log ( "WARNING: CUDA-capable GPU device is not available, running on CPU instead!" ) ;
2020-11-24 12:28:47 +01:00
if ( ! hasShownError & & line . ToLower ( ) . Contains ( "out of memory" ) )
{
hasShownError = true ;
InterpolateUtils . ShowMessage ( $"Your GPU ran out of VRAM! Please try a video with a lower resolution or use the Max Video Size option in the settings.\n\n{line}" , "Error" ) ;
}
if ( ! hasShownError & & line . ToLower ( ) . Contains ( "modulenotfounderror" ) )
{
hasShownError = true ;
2020-12-07 22:10:58 +01:00
InterpolateUtils . ShowMessage ( $"A python module is missing.\nCheck {logFilename} for details.\n\n{line}\n\nIf you don't want to install it yourself, use the Python package from the Package Installer." , "Error" ) ;
2021-01-04 14:27:34 +01:00
if ( ! Python . HasEmbeddedPyFolder ( ) )
2020-12-10 17:59:11 +01:00
Process . Start ( "https://github.com/n00mkrad/flowframes/blob/main/PythonDependencies.md" ) ;
2020-11-24 12:28:47 +01:00
}
2020-11-23 16:51:05 +01:00
2020-11-24 12:28:47 +01:00
if ( ! hasShownError & & line . ToLower ( ) . Contains ( "no longer supports this gpu" ) )
{
hasShownError = true ;
2020-11-26 20:17:18 +01:00
InterpolateUtils . ShowMessage ( $"Your GPU seems to be outdated and is not supported!\n\n{line}" , "Error" ) ;
2020-11-24 12:28:47 +01:00
}
2020-12-07 22:10:58 +01:00
if ( ! hasShownError & & ( line . Contains ( "RuntimeError" ) | | line . Contains ( "ImportError" ) | | line . Contains ( "OSError" ) ) )
2020-11-24 12:28:47 +01:00
{
hasShownError = true ;
2020-12-07 22:10:58 +01:00
InterpolateUtils . ShowMessage ( $"A python error occured during interpolation!\nCheck {logFilename} for details.\n\n{line}" , "Error" ) ;
2020-11-24 12:28:47 +01:00
}
2020-11-26 20:17:18 +01:00
2020-12-10 16:46:30 +01:00
if ( ! hasShownError & & ( line . Contains ( "vkQueueSubmit failed" ) | | line . Contains ( "vkAllocateMemory failed" ) ) )
2020-11-26 20:17:18 +01:00
{
hasShownError = true ;
InterpolateUtils . ShowMessage ( $"A Vulkan error occured during interpolation!\n\n{line}" , "Error" ) ;
}
2020-12-10 16:46:30 +01:00
if ( ! hasShownError & & line . Contains ( "invalid gpu device" ) )
{
hasShownError = true ;
InterpolateUtils . ShowMessage ( $"A Vulkan error occured during interpolation!\n\n{line}\n\nAre your GPU IDs set correctly?" , "Error" ) ;
}
2020-11-26 20:17:18 +01:00
if ( hasShownError )
Interpolate . Cancel ( ) ;
2020-11-23 16:51:05 +01:00
}
2020-12-10 16:10:52 +01:00
static string GetNcnnTilesize ( int tilesize )
{
int gpusAmount = Config . Get ( "ncnnGpus" ) . Split ( ',' ) . Length ;
string tilesizeStr = $"{tilesize}" ;
for ( int i = 1 ; i < gpusAmount ; i + + )
tilesizeStr + = $",{tilesize}" ;
return tilesizeStr ;
}
static string GetNcnnThreads ( )
{
int gpusAmount = Config . Get ( "ncnnGpus" ) . Split ( ',' ) . Length ;
int procThreads = Config . GetInt ( "ncnnThreads" ) ;
string progThreadsStr = $"{procThreads}" ;
for ( int i = 1 ; i < gpusAmount ; i + + )
progThreadsStr + = $",{procThreads}" ;
return $"4:{progThreadsStr}:4" ; ;
}
2020-11-23 16:51:05 +01:00
}
}