You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
290 lines
11 KiB
290 lines
11 KiB
1 year ago
|
using System.Diagnostics.CodeAnalysis;
|
||
|
using System.Text.Json.Nodes;
|
||
|
using System.Text.RegularExpressions;
|
||
|
using NLog;
|
||
|
using StabilityMatrix.Core.Helper;
|
||
|
using StabilityMatrix.Core.Helper.Cache;
|
||
|
using StabilityMatrix.Core.Models.FileInterfaces;
|
||
|
using StabilityMatrix.Core.Models.Progress;
|
||
|
using StabilityMatrix.Core.Processes;
|
||
|
using StabilityMatrix.Core.Python;
|
||
|
using StabilityMatrix.Core.Services;
|
||
|
|
||
|
namespace StabilityMatrix.Core.Models.Packages;
|
||
|
|
||
|
public class StableDiffusionUx : BaseGitPackage
|
||
|
{
|
||
|
private static readonly Logger Logger = LogManager.GetCurrentClassLogger();
|
||
|
|
||
|
public override string Name => "stable-diffusion-webui-ux";
|
||
|
public override string DisplayName { get; set; } = "Stable Diffusion Web UI-UX";
|
||
|
public override string Author => "anapnoe";
|
||
|
public override string LicenseType => "AGPL-3.0";
|
||
|
public override string LicenseUrl =>
|
||
|
"https://github.com/anapnoe/stable-diffusion-webui-ux/blob/master/LICENSE.txt";
|
||
|
public override string Blurb =>
|
||
|
"A pixel perfect design, mobile friendly, customizable interface that adds accessibility, "
|
||
|
+ "ease of use and extended functionallity to the stable diffusion web ui.";
|
||
|
public override string LaunchCommand => "launch.py";
|
||
|
public override Uri PreviewImageUri =>
|
||
|
new(
|
||
|
"https://user-images.githubusercontent.com/124302297/227973574-6003142d-0c7c-41c6-9966-0792a94549e9.png"
|
||
|
);
|
||
|
|
||
|
public override SharedFolderMethod RecommendedSharedFolderMethod => SharedFolderMethod.Symlink;
|
||
|
|
||
|
public StableDiffusionUx(
|
||
|
IGithubApiCache githubApi,
|
||
|
ISettingsManager settingsManager,
|
||
|
IDownloadService downloadService,
|
||
|
IPrerequisiteHelper prerequisiteHelper
|
||
|
)
|
||
|
: base(githubApi, settingsManager, downloadService, prerequisiteHelper) { }
|
||
|
|
||
|
public override Dictionary<SharedFolderType, IReadOnlyList<string>> SharedFolders =>
|
||
|
new()
|
||
|
{
|
||
|
[SharedFolderType.StableDiffusion] = new[] { "models/Stable-diffusion" },
|
||
|
[SharedFolderType.ESRGAN] = new[] { "models/ESRGAN" },
|
||
|
[SharedFolderType.RealESRGAN] = new[] { "models/RealESRGAN" },
|
||
|
[SharedFolderType.SwinIR] = new[] { "models/SwinIR" },
|
||
|
[SharedFolderType.Lora] = new[] { "models/Lora" },
|
||
|
[SharedFolderType.LyCORIS] = new[] { "models/LyCORIS" },
|
||
|
[SharedFolderType.ApproxVAE] = new[] { "models/VAE-approx" },
|
||
|
[SharedFolderType.VAE] = new[] { "models/VAE" },
|
||
|
[SharedFolderType.DeepDanbooru] = new[] { "models/deepbooru" },
|
||
|
[SharedFolderType.Karlo] = new[] { "models/karlo" },
|
||
|
[SharedFolderType.TextualInversion] = new[] { "embeddings" },
|
||
|
[SharedFolderType.Hypernetwork] = new[] { "models/hypernetworks" },
|
||
|
[SharedFolderType.ControlNet] = new[] { "models/ControlNet" },
|
||
|
[SharedFolderType.Codeformer] = new[] { "models/Codeformer" },
|
||
|
[SharedFolderType.LDSR] = new[] { "models/LDSR" },
|
||
|
[SharedFolderType.AfterDetailer] = new[] { "models/adetailer" }
|
||
|
};
|
||
|
|
||
|
public override Dictionary<SharedOutputType, IReadOnlyList<string>>? SharedOutputFolders =>
|
||
|
new()
|
||
|
{
|
||
|
[SharedOutputType.Extras] = new[] { "outputs/extras-images" },
|
||
|
[SharedOutputType.Saved] = new[] { "log/images" },
|
||
|
[SharedOutputType.Img2Img] = new[] { "outputs/img2img-images" },
|
||
|
[SharedOutputType.Text2Img] = new[] { "outputs/text2img-images" },
|
||
|
[SharedOutputType.Img2ImgGrids] = new[] { "outputs/img2img-grids" },
|
||
|
[SharedOutputType.Text2ImgGrids] = new[] { "outputs/text2img-grids" }
|
||
|
};
|
||
|
|
||
|
[SuppressMessage("ReSharper", "ArrangeObjectCreationWhenTypeNotEvident")]
|
||
|
public override List<LaunchOptionDefinition> LaunchOptions =>
|
||
|
new()
|
||
|
{
|
||
|
new()
|
||
|
{
|
||
|
Name = "Host",
|
||
|
Type = LaunchOptionType.String,
|
||
|
DefaultValue = "localhost",
|
||
|
Options = new() { "--server-name" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Port",
|
||
|
Type = LaunchOptionType.String,
|
||
|
DefaultValue = "7860",
|
||
|
Options = new() { "--port" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "VRAM",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = HardwareHelper
|
||
|
.IterGpuInfo()
|
||
|
.Select(gpu => gpu.MemoryLevel)
|
||
|
.Max() switch
|
||
|
{
|
||
|
Level.Low => "--lowvram",
|
||
|
Level.Medium => "--medvram",
|
||
|
_ => null
|
||
|
},
|
||
|
Options = new() { "--lowvram", "--medvram", "--medvram-sdxl" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Xformers",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = HardwareHelper.HasNvidiaGpu(),
|
||
|
Options = new() { "--xformers" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "API",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = true,
|
||
|
Options = new() { "--api" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Auto Launch Web UI",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = false,
|
||
|
Options = new() { "--autolaunch" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Skip Torch CUDA Check",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = !HardwareHelper.HasNvidiaGpu(),
|
||
|
Options = new() { "--skip-torch-cuda-test" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Skip Python Version Check",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = true,
|
||
|
Options = new() { "--skip-python-version-check" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "No Half",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
Description = "Do not switch the model to 16-bit floats",
|
||
|
InitialValue = HardwareHelper.HasAmdGpu(),
|
||
|
Options = new() { "--no-half" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Skip SD Model Download",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
InitialValue = false,
|
||
|
Options = new() { "--no-download-sd-model" }
|
||
|
},
|
||
|
new()
|
||
|
{
|
||
|
Name = "Skip Install",
|
||
|
Type = LaunchOptionType.Bool,
|
||
|
Options = new() { "--skip-install" }
|
||
|
},
|
||
|
LaunchOptionDefinition.Extras
|
||
|
};
|
||
|
|
||
|
public override IEnumerable<SharedFolderMethod> AvailableSharedFolderMethods =>
|
||
|
new[] { SharedFolderMethod.Symlink, SharedFolderMethod.None };
|
||
|
|
||
|
public override IEnumerable<TorchVersion> AvailableTorchVersions =>
|
||
|
new[] { TorchVersion.Cpu, TorchVersion.Cuda, TorchVersion.DirectMl, TorchVersion.Rocm };
|
||
|
|
||
|
public override Task<string> GetLatestVersion() => Task.FromResult("master");
|
||
|
|
||
|
public override bool ShouldIgnoreReleases => true;
|
||
|
|
||
|
public override string OutputFolderName => "outputs";
|
||
|
|
||
|
public override async Task InstallPackage(
|
||
|
string installLocation,
|
||
|
TorchVersion torchVersion,
|
||
|
IProgress<ProgressReport>? progress = null,
|
||
|
Action<ProcessOutput>? onConsoleOutput = null
|
||
|
)
|
||
|
{
|
||
|
await base.InstallPackage(installLocation, torchVersion, progress).ConfigureAwait(false);
|
||
|
|
||
|
progress?.Report(new ProgressReport(-1f, "Setting up venv", isIndeterminate: true));
|
||
|
// Setup venv
|
||
|
await using var venvRunner = new PyVenvRunner(Path.Combine(installLocation, "venv"));
|
||
|
venvRunner.WorkingDirectory = installLocation;
|
||
|
await venvRunner.Setup(true, onConsoleOutput).ConfigureAwait(false);
|
||
|
|
||
|
switch (torchVersion)
|
||
|
{
|
||
|
case TorchVersion.Cpu:
|
||
|
await InstallCpuTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
|
||
|
break;
|
||
|
case TorchVersion.Cuda:
|
||
|
await InstallCudaTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
|
||
|
break;
|
||
|
case TorchVersion.Rocm:
|
||
|
await InstallRocmTorch(venvRunner, progress, onConsoleOutput).ConfigureAwait(false);
|
||
|
break;
|
||
|
case TorchVersion.DirectMl:
|
||
|
await InstallDirectMlTorch(venvRunner, progress, onConsoleOutput)
|
||
|
.ConfigureAwait(false);
|
||
|
break;
|
||
|
default:
|
||
|
throw new ArgumentOutOfRangeException(nameof(torchVersion), torchVersion, null);
|
||
|
}
|
||
|
|
||
|
// Install requirements file
|
||
|
progress?.Report(
|
||
|
new ProgressReport(-1f, "Installing Package Requirements", isIndeterminate: true)
|
||
|
);
|
||
|
Logger.Info("Installing requirements_versions.txt");
|
||
|
|
||
|
var requirements = new FilePath(installLocation, "requirements_versions.txt");
|
||
|
await venvRunner
|
||
|
.PipInstallFromRequirements(requirements, onConsoleOutput, excludes: "torch")
|
||
|
.ConfigureAwait(false);
|
||
|
|
||
|
progress?.Report(
|
||
|
new ProgressReport(1f, "Installing Package Requirements", isIndeterminate: false)
|
||
|
);
|
||
|
|
||
|
progress?.Report(new ProgressReport(-1f, "Updating configuration", isIndeterminate: true));
|
||
|
|
||
|
// // Create and add {"show_progress_type": "TAESD"} to config.json
|
||
|
// // Only add if the file doesn't exist
|
||
|
// var configPath = Path.Combine(installLocation, "config.json");
|
||
|
// if (!File.Exists(configPath))
|
||
|
// {
|
||
|
// var config = new JsonObject { { "show_progress_type", "TAESD" } };
|
||
|
// await File.WriteAllTextAsync(configPath, config.ToString()).ConfigureAwait(false);
|
||
|
// }
|
||
|
|
||
|
progress?.Report(new ProgressReport(1f, "Install complete", isIndeterminate: false));
|
||
|
}
|
||
|
|
||
|
public override async Task RunPackage(
|
||
|
string installedPackagePath,
|
||
|
string command,
|
||
|
string arguments,
|
||
|
Action<ProcessOutput>? onConsoleOutput
|
||
|
)
|
||
|
{
|
||
|
await SetupVenv(installedPackagePath).ConfigureAwait(false);
|
||
|
|
||
|
void HandleConsoleOutput(ProcessOutput s)
|
||
|
{
|
||
|
onConsoleOutput?.Invoke(s);
|
||
|
|
||
|
if (!s.Text.Contains("Running on", StringComparison.OrdinalIgnoreCase))
|
||
|
return;
|
||
|
|
||
|
var regex = new Regex(@"(https?:\/\/)([^:\s]+):(\d+)");
|
||
|
var match = regex.Match(s.Text);
|
||
|
if (!match.Success)
|
||
|
return;
|
||
|
|
||
|
WebUrl = match.Value;
|
||
|
OnStartupComplete(WebUrl);
|
||
|
}
|
||
|
|
||
|
var args = $"\"{Path.Combine(installedPackagePath, command)}\" {arguments}";
|
||
|
|
||
|
VenvRunner.RunDetached(args.TrimEnd(), HandleConsoleOutput, OnExit);
|
||
|
}
|
||
|
|
||
|
private async Task InstallRocmTorch(
|
||
|
PyVenvRunner venvRunner,
|
||
|
IProgress<ProgressReport>? progress = null,
|
||
|
Action<ProcessOutput>? onConsoleOutput = null
|
||
|
)
|
||
|
{
|
||
|
progress?.Report(
|
||
|
new ProgressReport(-1f, "Installing PyTorch for ROCm", isIndeterminate: true)
|
||
|
);
|
||
|
|
||
|
await venvRunner.PipInstall("--upgrade pip wheel", onConsoleOutput).ConfigureAwait(false);
|
||
|
|
||
|
await venvRunner
|
||
|
.PipInstall(PyVenvRunner.TorchPipInstallArgsRocm511, onConsoleOutput)
|
||
|
.ConfigureAwait(false);
|
||
|
}
|
||
|
}
|