From fbd5dbf1d052db1c9dd423970b5339d76398cae3 Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Wed, 7 Oct 2026 17:24:11 +0000 Subject: [PATCH 1/6] Wire IRocmPackageHelper into Forge Neo - Add IRocmPackageHelper to the ForgeNeo primary constructor without forwarding it to ForgeClassic (the base chain does not take it) - Add private HasWindowsRocmSupport() and GetWindowsRocmCompatibility() wrappers delegating to the shared BaseGitPackage ROCm helpers, matching the ComfyUI/Reforge hook pattern - Pass rocmPackageHelper when constructing ForgeNeo in PackageFactory --- .../Helper/Factory/PackageFactory.cs | 3 ++- .../Models/Packages/ForgeNeo.cs | 19 ++++++++++++++++++- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs index c23051fac..5d15d7853 100644 --- a/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs +++ b/StabilityMatrix.Core/Helper/Factory/PackageFactory.cs @@ -260,7 +260,8 @@ public BasePackage GetNewBasePackage(InstalledPackage installedPackage) downloadService, prerequisiteHelper, pyInstallationManager, - pipWheelService + pipWheelService, + rocmPackageHelper ), "framepack" => new FramePack( githubApiCache, diff --git a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs index ee8f4f9f2..5ce0b0f9a 100644 --- a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs +++ b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs @@ -2,8 +2,10 @@ using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.Rocm; using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; +using StabilityMatrix.Core.Services.Rocm; namespace StabilityMatrix.Core.Models.Packages; @@ -14,7 +16,8 @@ public class ForgeNeo( IDownloadService downloadService, IPrerequisiteHelper prerequisiteHelper, IPyInstallationManager pyInstallationManager, - IPipWheelService pipWheelService + IPipWheelService pipWheelService, + IRocmPackageHelper rocmPackageHelper ) : ForgeClassic( githubApi, @@ -25,6 +28,20 @@ IPipWheelService pipWheelService pipWheelService ) { + /// + /// Uses the shared ROCm helper for Windows ROCm eligibility checks so Forge Neo does not maintain + /// its own support matrix. + /// + private bool HasWindowsRocmSupport() + { + return HasWindowsRocmSupport(rocmPackageHelper); + } + + private RocmCompatibilityResult GetWindowsRocmCompatibility() + { + return GetWindowsRocmCompatibility(rocmPackageHelper); + } + public override string Name => "forge-neo"; public override string DisplayName { get; set; } = "Stable Diffusion WebUI Forge - Neo"; public override string MainBranch => "neo"; From 24bfeb3b187444d596347310285e181db1aa501b Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Wed, 7 Oct 2026 17:55:40 +0000 Subject: [PATCH 2/6] Add Forge Neo Windows ROCm profile with pre-release torch support - Add RocmPackageProfile.IncludePrereleaseTorch to let a profile pass --pre to the helper's torch install - Honor IncludePrereleaseTorch in RocmPackageHelper's Windows-native torch install command - Add ForgeNeoWindowsRocmProfile: bitsandbytes + triton-windows post install, TORCH_COMMAND/TORCH_INDEX_URL overrides, and ROCm launch option defaults (pin-shared-memory, cuda-stream, disable-smart-memory, use-pytorch-cross-attention; cuda-malloc disabled) - Enable --pre for Forge Neo temporarily to dodge a broken stable ROCm release while an AMD fix and a defensive SM change are pending --- .../Models/Rocm/ForgeNeoWindowsRocmProfile.cs | 125 ++++++++++++++++++ .../Models/Rocm/RocmPackageProfile.cs | 7 + .../Services/Rocm/RocmPackageHelper.cs | 7 + 3 files changed, 139 insertions(+) create mode 100644 StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs diff --git a/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs b/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs new file mode 100644 index 000000000..ef2a53e5c --- /dev/null +++ b/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs @@ -0,0 +1,125 @@ +using StabilityMatrix.Core.Helper; +using StabilityMatrix.Core.Models.Packages; +using StabilityMatrix.Core.Services.Rocm; + +namespace StabilityMatrix.Core.Models.Rocm; + +/// +/// Shared Windows ROCm profile for Forge Neo. +/// Forge Neo has no native AMD support, so the helper owns the ROCm torch install and the launch +/// environment, while Forge Neo's own launch.py still installs the rest of its dependencies. +/// +public class ForgeNeoWindowsRocmProfile : RocmPackageProfile +{ + private const string DisableSmartMemoryOptionName = "Disable Smart Memory"; + private const string UsePytorchCrossAttentionOptionName = "Use PyTorch Cross Attention"; + + // Recommended launch defaults from the "AMD Forge Neo with ROCm" setup guide. + private static readonly string[] EnableLaunchOptionNames = ["Pin Shared Memory", "CUDA Stream"]; + private static readonly string[] DisableLaunchOptionNames = ["CUDA Malloc"]; + + public ForgeNeoWindowsRocmProfile() + { + InstallConfig = new PipInstallConfig + { + // Forge Neo installs its own requirements via launch.py; the helper only needs to add the + // ROCm-oriented accelerators that upstream would otherwise pull as CUDA-only builds. + PostTorchInstallPipArgs = ["bitsandbytes", "triton-windows"], + UpgradePackages = true, + }; + + // TEMPORARY: a stable ROCm release is currently broken while AMD prepares a fix (and a defensive + // Stability Matrix change is pending). Force pre-release resolution so installs succeed. + IncludePrereleaseTorch = true; + + ExtraEnvironmentFactory = BuildEnvironment; + } + + public static ForgeNeoWindowsRocmProfile Default { get; } = new ForgeNeoWindowsRocmProfile(); + + private static IReadOnlyDictionary BuildEnvironment(RocmRuntimeContext runtimeContext) + { + var indexUrl = WindowsRocmSupport.GetMultiArchPythonPackageIndexUrl(runtimeContext.RuntimeGfxArch); + var deviceExtra = WindowsRocmSupport.TryGetMultiArchDeviceExtra(runtimeContext.RuntimeGfxArch); + + if (string.IsNullOrWhiteSpace(deviceExtra)) + { + return new Dictionary(); + } + + // Persisted so that if Forge Neo re-runs its own torch install step it targets AMD's index + // instead of pulling the NVIDIA CUDA build from PyTorch. + var torchCommand = + $"pip install --pre --index-url {indexUrl} " + + $"\"torch[{deviceExtra}]\" \"torchvision[{deviceExtra}]\" torchaudio \"rocm[devel]\""; + + return new Dictionary + { + ["TORCH_INDEX_URL"] = indexUrl, + ["TORCH_COMMAND"] = torchCommand, + }; + } + + public override void ApplyWindowsRocmLaunchDefaults( + List launchOptions, + IRocmPackageHelper rocmPackageHelper + ) + { + if (!(Compat.IsWindows && rocmPackageHelper.GetCompatibility().IsCompatible)) + { + return; + } + + for (var i = 0; i < launchOptions.Count; i++) + { + var option = launchOptions[i]; + + if (EnableLaunchOptionNames.Contains(option.Name)) + { + launchOptions[i] = option with { InitialValue = true }; + } + else if (DisableLaunchOptionNames.Contains(option.Name)) + { + launchOptions[i] = option with { InitialValue = null }; + } + } + + InsertBoolOptionIfMissing( + launchOptions, + DisableSmartMemoryOptionName, + "--disable-smart-memory", + "Aggressively offload to RAM instead of keeping models in VRAM when possible" + ); + InsertBoolOptionIfMissing( + launchOptions, + UsePytorchCrossAttentionOptionName, + "--use-pytorch-cross-attention", + "Use the PyTorch cross attention (override sageattention/flash_attn/xformers)" + ); + } + + private static void InsertBoolOptionIfMissing( + List launchOptions, + string name, + string option, + string description + ) + { + if (launchOptions.Any(x => x.Name == name)) + { + return; + } + + var definition = new LaunchOptionDefinition + { + Name = name, + Type = LaunchOptionType.Bool, + Description = description, + Options = [option], + InitialValue = true, + }; + + var extrasIndex = launchOptions.FindIndex(x => x.Name == LaunchOptionDefinition.Extras.Name); + launchOptions.Insert(extrasIndex < 0 ? launchOptions.Count : extrasIndex, definition); + } +} diff --git a/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs b/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs index 41e13f8f4..249f9cffc 100644 --- a/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs +++ b/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs @@ -15,6 +15,13 @@ public class RocmPackageProfile /// public PipInstallConfig InstallConfig { get; init; } = new(); + /// + /// When true, the ROCm helper passes --pre when installing torch so pre-release multi-arch + /// builds can be selected. Intended as a temporary workaround for packages that need to avoid a + /// broken stable ROCm release. + /// + public bool IncludePrereleaseTorch { get; init; } + /// /// Optional callback for package-specific environment variables derived from a resolved ROCm context. /// diff --git a/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs b/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs index 1bf15be60..171211ff6 100644 --- a/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs +++ b/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs @@ -273,6 +273,13 @@ public async Task InstallWindowsNativeTorchAsync( torchArgs = torchArgs.AddArg("--force-reinstall"); } + // Temporary escape hatch for profiles that must resolve pre-release builds (e.g. while a stable + // AMD ROCm release is broken). + if (profile.IncludePrereleaseTorch) + { + torchArgs = torchArgs.AddArg("--pre"); + } + if (installedPackage.PipOverrides != null) { torchArgs = torchArgs.WithUserOverrides(installedPackage.PipOverrides); From 8adcbc732bb1d46ccf25369fe51aef2385d8e908 Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Wed, 7 Oct 2026 18:01:35 +0000 Subject: [PATCH 3/6] Add ForgeClassic pre-install hook for ROCm environment prep - Add protected virtual ForgeClassic.PrepareRocmInstallAsync, invoked after venv setup and before the upstream launch.py install args are built - Default implementation is a no-op, so ForgeClassic install behavior is unchanged - Gives Forge Neo a place to pre-install the Windows ROCm torch build before launch.py runs its torch step --- .../Models/Packages/ForgeClassic.cs | 29 +++++++++++++++++++ 1 file changed, 29 insertions(+) diff --git a/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs b/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs index f9365ab75..d089d051e 100644 --- a/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs +++ b/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs @@ -219,6 +219,18 @@ public override async Task InstallPackage( ) .ConfigureAwait(false); + // Allow derived packages (e.g. Forge Neo on Windows ROCm) to prepare the environment before the + // upstream launch.py install runs, such as pre-installing an alternative torch build. + await PrepareRocmInstallAsync( + venvRunner, + installedPackage, + options, + onConsoleOutput, + progress, + cancellationToken + ) + .ConfigureAwait(false); + progress?.Report(new ProgressReport(-1f, "Running install script...", isIndeterminate: true)); // Build args for their launch.py - use --uv for fast installs, --exit to quit after setup @@ -293,6 +305,23 @@ public override async Task InstallPackage( progress?.Report(new ProgressReport(1f, "Install complete", isIndeterminate: false)); } + /// + /// Hook for derived packages to prepare the environment before the upstream Forge install script + /// runs. The default implementation does nothing. Used by Forge Neo to pre-install a Windows ROCm + /// torch build so the upstream launch.py does not install the NVIDIA CUDA build. + /// + protected virtual Task PrepareRocmInstallAsync( + IPyVenvRunner venvRunner, + InstalledPackage installedPackage, + InstallPackageOptions options, + Action? onConsoleOutput, + IProgress? progress, + CancellationToken cancellationToken + ) + { + return Task.CompletedTask; + } + private async Task RunInstallScriptWithPromptHandling( IPyVenvRunner venvRunner, IReadOnlyCollection launchArgs, From 60274081f0571f4249f3c2aed36f45ead7540d77 Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Wed, 7 Oct 2026 18:44:02 +0000 Subject: [PATCH 4/6] Add Windows ROCm install and launch support to Forge Neo - Advertise TorchIndex.Rocm on Windows and select it when the shared ROCm helper reports a compatible machine - Override ForgeClassic.PrepareRocmInstallAsync to apply the ROCm launch environment and pre-install the AMD multi-arch torch build before the upstream launch.py runs, so its is_installed("torch") gate skips the default NVIDIA CUDA torch install - Apply the ROCm environment at launch (GetEnvVars) and surface the ROCm launch notice (GetLaunchNoticeLines) - Apply the Forge Neo ROCm profile launch-option defaults - Keep CUDA as the only torch index off Windows - Drop the now-unused GetWindowsRocmCompatibility wrapper --- .../Models/Packages/ForgeNeo.cs | 100 ++++++++++++++++-- 1 file changed, 94 insertions(+), 6 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs index 5ce0b0f9a..6d67d918d 100644 --- a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs +++ b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs @@ -1,8 +1,12 @@ -using Injectio.Attributes; +using System.Collections.Immutable; +using Injectio.Attributes; using StabilityMatrix.Core.Helper; using StabilityMatrix.Core.Helper.Cache; +using StabilityMatrix.Core.Helper.HardwareInfo; using StabilityMatrix.Core.Models; +using StabilityMatrix.Core.Models.Progress; using StabilityMatrix.Core.Models.Rocm; +using StabilityMatrix.Core.Processes; using StabilityMatrix.Core.Python; using StabilityMatrix.Core.Services; using StabilityMatrix.Core.Services.Rocm; @@ -37,15 +41,33 @@ private bool HasWindowsRocmSupport() return HasWindowsRocmSupport(rocmPackageHelper); } - private RocmCompatibilityResult GetWindowsRocmCompatibility() - { - return GetWindowsRocmCompatibility(rocmPackageHelper); - } - public override string Name => "forge-neo"; public override string DisplayName { get; set; } = "Stable Diffusion WebUI Forge - Neo"; public override string MainBranch => "neo"; public override PackageType PackageType => PackageType.SdInference; + + // Windows-only ROCm comes from the shared helper; CUDA remains the only option everywhere else. + public override IEnumerable AvailableTorchIndices => + Compat.IsWindows ? [TorchIndex.Cuda, TorchIndex.Rocm] : [TorchIndex.Cuda]; + + public override bool IsCompatible => HardwareHelper.HasNvidiaGpu() || HasWindowsRocmSupport(); + + public override TorchIndex GetRecommendedTorchVersion() + { + var preferNvidia = SettingsManager.Settings.PreferredGpu?.IsNvidia ?? HardwareHelper.HasNvidiaGpu(); + if (AvailableTorchIndices.Contains(TorchIndex.Cuda) && preferNvidia) + { + return TorchIndex.Cuda; + } + + if (AvailableTorchIndices.Contains(TorchIndex.Rocm) && HasWindowsRocmSupport()) + { + return TorchIndex.Rocm; + } + + return base.GetRecommendedTorchVersion(); + } + public override List LaunchOptions { get @@ -62,10 +84,76 @@ public override List LaunchOptions Options = ["--bnb"], } ); + + ForgeNeoWindowsRocmProfile.Default.ApplyWindowsRocmLaunchDefaults(options, rocmPackageHelper); + return options; } } + protected override async Task PrepareRocmInstallAsync( + IPyVenvRunner venvRunner, + InstalledPackage installedPackage, + InstallPackageOptions options, + Action? onConsoleOutput, + IProgress? progress, + CancellationToken cancellationToken + ) + { + var torchIndex = options.PythonOptions.TorchIndex ?? GetRecommendedTorchVersion(); + if (!rocmPackageHelper.ShouldApplyWindowsLaunchEnvironment(torchIndex)) + { + return; + } + + var profile = ForgeNeoWindowsRocmProfile.Default; + + progress?.Report( + new ProgressReport(-1f, "Preparing Windows ROCm environment...", isIndeterminate: true) + ); + + // Apply the ROCm launch environment (including the TORCH_COMMAND/TORCH_INDEX_URL override) so both + // the helper-managed torch install and the subsequent upstream launch.py see it. + venvRunner.UpdateEnvironmentVariables(env => + env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(profile)) + ); + + // Install the AMD multi-arch torch build before launch.py runs, so its is_installed("torch") + // gate skips the default NVIDIA CUDA torch install. + await rocmPackageHelper + .InstallWindowsNativeTorchAsync( + venvRunner, + installedPackage, + profile, + progress, + onConsoleOutput, + cancellationToken + ) + .ConfigureAwait(false); + } + + protected override ImmutableDictionary GetEnvVars( + ImmutableDictionary env, + InstalledPackage installedPackage + ) + { + env = base.GetEnvVars(env, installedPackage); + + var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion(); + if (!rocmPackageHelper.ShouldApplyWindowsLaunchEnvironment(selectedTorchIndex)) + { + return env; + } + + return env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(ForgeNeoWindowsRocmProfile.Default)); + } + + protected override IReadOnlyList GetLaunchNoticeLines(InstalledPackage installedPackage) + { + var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion(); + return rocmPackageHelper.GetWindowsLaunchNoticeLines(selectedTorchIndex); + } + public override string Blurb => "Neo mainly serves as an continuation for the \"latest\" version of Forge. Additionally, this fork is focused on optimization and usability, with the main goal of being the lightest WebUI without any bloatwares."; } From ef5d99b13eec7352a0ae477bfe92adbb9ef2dd10 Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Fri, 9 Oct 2026 02:38:30 +0000 Subject: [PATCH 5/6] feat(rocm): add standalone Linux ROCm support to Forge Neo + Cleanup Linux: - Install torch/torchvision on Linux from the upstream PyTorch ROCm index (rocm7.14) with an exclusive --index-url before the upstream launch.py runs, so its is_installed("torch") gate skips the default CUDA build - Advertise TorchIndex.Rocm on Linux and recommend it for AMD GPUs; make ROCm eligibility OS-agnostic via the shared helper's GetCompatibility - Select the Windows or Linux ROCm profile per platform and reuse the helper's BuildLaunchEnvironment for the launch env on both - Fail with a clear message when ROCm is selected on an incompatible machine, and verify the installed torch reports a rocm build Windows / helper: - Drop the temporary forced pre-release workaround now that the upstream fix and defensive SM change are landing: remove IncludePrereleaseTorch from the Forge Neo Windows profile and RocmPackageProfile, remove the helper's --pre branch, and drop --pre from the persisted TORCH_COMMAND Cleanups: - Generalize the ForgeClassic pre-install hook docs/comments and the Forge Neo Windows profile summary to be OS-neutral since ROCm baseline envars are passed on Linux also, and future transition of ROCm helper to OS agnostic. - Drop redundant AvailableTorchIndices.Contains guards and tidy member ordering in ForgeNeo --- .../Models/Packages/ForgeClassic.cs | 6 +- .../Models/Packages/ForgeNeo.cs | 128 +++++++++++++----- .../Models/Rocm/ForgeNeoWindowsRocmProfile.cs | 8 +- .../Models/Rocm/RocmPackageProfile.cs | 7 - .../Services/Rocm/RocmPackageHelper.cs | 7 - 5 files changed, 101 insertions(+), 55 deletions(-) diff --git a/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs b/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs index d089d051e..f106e85e1 100644 --- a/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs +++ b/StabilityMatrix.Core/Models/Packages/ForgeClassic.cs @@ -219,7 +219,7 @@ public override async Task InstallPackage( ) .ConfigureAwait(false); - // Allow derived packages (e.g. Forge Neo on Windows ROCm) to prepare the environment before the + // Allow derived packages (e.g. Forge Neo on ROCm) to prepare the environment before the // upstream launch.py install runs, such as pre-installing an alternative torch build. await PrepareRocmInstallAsync( venvRunner, @@ -307,8 +307,8 @@ await PrepareRocmInstallAsync( /// /// Hook for derived packages to prepare the environment before the upstream Forge install script - /// runs. The default implementation does nothing. Used by Forge Neo to pre-install a Windows ROCm - /// torch build so the upstream launch.py does not install the NVIDIA CUDA build. + /// runs. The default implementation does nothing. Used by Forge Neo to pre-install an alternative + /// ROCm torch build so the upstream launch.py does not install its default (CUDA) torch build. /// protected virtual Task PrepareRocmInstallAsync( IPyVenvRunner venvRunner, diff --git a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs index 6d67d918d..0dc0c29eb 100644 --- a/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs +++ b/StabilityMatrix.Core/Models/Packages/ForgeNeo.cs @@ -32,35 +32,53 @@ IRocmPackageHelper rocmPackageHelper pipWheelService ) { + internal const string LinuxRocmIndexUrl = "https://download.pytorch.org/whl/rocm7.14"; + + // Linux uses the upstream PyTorch ROCm index rather than AMD repo wheels. Kept inline because it + // collapses into the helper once its OS-agnostic path lands. + private static readonly RocmPackageProfile LinuxRocmProfile = new() + { + ExtraEnvironmentFactory = _ => new Dictionary + { + ["TORCH_INDEX_URL"] = LinuxRocmIndexUrl, + ["TORCH_COMMAND"] = $"pip install --index-url {LinuxRocmIndexUrl} torch torchvision", + }, + }; + /// - /// Uses the shared ROCm helper for Windows ROCm eligibility checks so Forge Neo does not maintain - /// its own support matrix. + /// Windows installs use AMD repo wheels; Linux uses the upstream PyTorch ROCm index. /// - private bool HasWindowsRocmSupport() - { - return HasWindowsRocmSupport(rocmPackageHelper); - } + private RocmPackageProfile ActiveRocmProfile => + Compat.IsWindows ? ForgeNeoWindowsRocmProfile.Default : LinuxRocmProfile; public override string Name => "forge-neo"; public override string DisplayName { get; set; } = "Stable Diffusion WebUI Forge - Neo"; public override string MainBranch => "neo"; public override PackageType PackageType => PackageType.SdInference; - // Windows-only ROCm comes from the shared helper; CUDA remains the only option everywhere else. - public override IEnumerable AvailableTorchIndices => - Compat.IsWindows ? [TorchIndex.Cuda, TorchIndex.Rocm] : [TorchIndex.Cuda]; + // CUDA for NVIDIA; ROCm for AMD. + public override IEnumerable AvailableTorchIndices => [TorchIndex.Cuda, TorchIndex.Rocm]; + + public override bool IsCompatible => HardwareHelper.HasNvidiaGpu() || HasRocmSupport(); - public override bool IsCompatible => HardwareHelper.HasNvidiaGpu() || HasWindowsRocmSupport(); + /// + /// Uses the shared ROCm helper for ROCm eligibility checks so Forge Neo does not maintain its own + /// support matrix. The helper resolves compatibility for both Windows and Linux. + /// + private bool HasRocmSupport() + { + return rocmPackageHelper.GetCompatibility().IsCompatible; + } public override TorchIndex GetRecommendedTorchVersion() { var preferNvidia = SettingsManager.Settings.PreferredGpu?.IsNvidia ?? HardwareHelper.HasNvidiaGpu(); - if (AvailableTorchIndices.Contains(TorchIndex.Cuda) && preferNvidia) + if (preferNvidia) { return TorchIndex.Cuda; } - if (AvailableTorchIndices.Contains(TorchIndex.Rocm) && HasWindowsRocmSupport()) + if (HasRocmSupport()) { return TorchIndex.Rocm; } @@ -101,35 +119,80 @@ CancellationToken cancellationToken ) { var torchIndex = options.PythonOptions.TorchIndex ?? GetRecommendedTorchVersion(); - if (!rocmPackageHelper.ShouldApplyWindowsLaunchEnvironment(torchIndex)) + if (torchIndex != TorchIndex.Rocm) { return; } - var profile = ForgeNeoWindowsRocmProfile.Default; + if (!HasRocmSupport()) + { + throw new InvalidOperationException( + rocmPackageHelper.GetCompatibility().FailureReason + ?? "The current machine is not compatible with the selected ROCm torch index." + ); + } - progress?.Report( - new ProgressReport(-1f, "Preparing Windows ROCm environment...", isIndeterminate: true) - ); + var profile = ActiveRocmProfile; + + progress?.Report(new ProgressReport(-1f, "Preparing ROCm environment...", isIndeterminate: true)); // Apply the ROCm launch environment (including the TORCH_COMMAND/TORCH_INDEX_URL override) so both - // the helper-managed torch install and the subsequent upstream launch.py see it. + // the torch install and the subsequent upstream launch.py see it. venvRunner.UpdateEnvironmentVariables(env => env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(profile)) ); - // Install the AMD multi-arch torch build before launch.py runs, so its is_installed("torch") - // gate skips the default NVIDIA CUDA torch install. - await rocmPackageHelper - .InstallWindowsNativeTorchAsync( - venvRunner, - installedPackage, - profile, - progress, - onConsoleOutput, - cancellationToken - ) - .ConfigureAwait(false); + if (Compat.IsWindows) + { + // Install the AMD multi-arch torch build before launch.py runs, so its is_installed("torch") + // gate skips the default NVIDIA CUDA torch install. + await rocmPackageHelper + .InstallWindowsNativeTorchAsync( + venvRunner, + installedPackage, + profile, + progress, + onConsoleOutput, + cancellationToken + ) + .ConfigureAwait(false); + return; + } + + // Linux: install from the upstream PyTorch ROCm index. An exclusive --index-url (rather than + // --extra-index-url) keeps pip from quietly resolving the CPU build from PyPI. + var torchArgs = new PipInstallArgs() + .AddKeyedArgs("--index-url", ["--index-url", LinuxRocmIndexUrl]) + .AddArg("torch") + .AddArg("torchvision"); + + await venvRunner.PipInstall(torchArgs, onConsoleOutput).ConfigureAwait(false); + + await VerifyLinuxRocmTorchInstallAsync(venvRunner, cancellationToken).ConfigureAwait(false); + } + + private static async Task VerifyLinuxRocmTorchInstallAsync( + IPyVenvRunner venvRunner, + CancellationToken cancellationToken + ) + { + cancellationToken.ThrowIfCancellationRequested(); + + var torchInfo = await venvRunner.PipShow("torch").ConfigureAwait(false); + if (torchInfo is null) + { + throw new InvalidOperationException("torch was not installed after Linux ROCm setup."); + } + + if ( + string.IsNullOrWhiteSpace(torchInfo.Version) + || !torchInfo.Version.Contains("rocm", StringComparison.OrdinalIgnoreCase) + ) + { + throw new InvalidOperationException( + $"Installed torch is not a ROCm build (detected version: {torchInfo.Version})." + ); + } } protected override ImmutableDictionary GetEnvVars( @@ -140,17 +203,18 @@ InstalledPackage installedPackage env = base.GetEnvVars(env, installedPackage); var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion(); - if (!rocmPackageHelper.ShouldApplyWindowsLaunchEnvironment(selectedTorchIndex)) + if (selectedTorchIndex != TorchIndex.Rocm || !HasRocmSupport()) { return env; } - return env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(ForgeNeoWindowsRocmProfile.Default)); + return env.SetItems(rocmPackageHelper.BuildLaunchEnvironment(ActiveRocmProfile)); } protected override IReadOnlyList GetLaunchNoticeLines(InstalledPackage installedPackage) { var selectedTorchIndex = installedPackage.PreferredTorchIndex ?? GetRecommendedTorchVersion(); + // The helper returns an empty list on non-Windows platforms. return rocmPackageHelper.GetWindowsLaunchNoticeLines(selectedTorchIndex); } diff --git a/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs b/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs index ef2a53e5c..69d5e56b3 100644 --- a/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs +++ b/StabilityMatrix.Core/Models/Rocm/ForgeNeoWindowsRocmProfile.cs @@ -5,7 +5,7 @@ namespace StabilityMatrix.Core.Models.Rocm; /// -/// Shared Windows ROCm profile for Forge Neo. +/// Windows ROCm profile for Forge Neo. /// Forge Neo has no native AMD support, so the helper owns the ROCm torch install and the launch /// environment, while Forge Neo's own launch.py still installs the rest of its dependencies. /// @@ -28,10 +28,6 @@ public ForgeNeoWindowsRocmProfile() UpgradePackages = true, }; - // TEMPORARY: a stable ROCm release is currently broken while AMD prepares a fix (and a defensive - // Stability Matrix change is pending). Force pre-release resolution so installs succeed. - IncludePrereleaseTorch = true; - ExtraEnvironmentFactory = BuildEnvironment; } @@ -50,7 +46,7 @@ private static IReadOnlyDictionary BuildEnvironment(RocmRuntimeC // Persisted so that if Forge Neo re-runs its own torch install step it targets AMD's index // instead of pulling the NVIDIA CUDA build from PyTorch. var torchCommand = - $"pip install --pre --index-url {indexUrl} " + $"pip install --index-url {indexUrl} " + $"\"torch[{deviceExtra}]\" \"torchvision[{deviceExtra}]\" torchaudio \"rocm[devel]\""; return new Dictionary diff --git a/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs b/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs index 249f9cffc..41e13f8f4 100644 --- a/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs +++ b/StabilityMatrix.Core/Models/Rocm/RocmPackageProfile.cs @@ -15,13 +15,6 @@ public class RocmPackageProfile /// public PipInstallConfig InstallConfig { get; init; } = new(); - /// - /// When true, the ROCm helper passes --pre when installing torch so pre-release multi-arch - /// builds can be selected. Intended as a temporary workaround for packages that need to avoid a - /// broken stable ROCm release. - /// - public bool IncludePrereleaseTorch { get; init; } - /// /// Optional callback for package-specific environment variables derived from a resolved ROCm context. /// diff --git a/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs b/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs index 171211ff6..1bf15be60 100644 --- a/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs +++ b/StabilityMatrix.Core/Services/Rocm/RocmPackageHelper.cs @@ -273,13 +273,6 @@ public async Task InstallWindowsNativeTorchAsync( torchArgs = torchArgs.AddArg("--force-reinstall"); } - // Temporary escape hatch for profiles that must resolve pre-release builds (e.g. while a stable - // AMD ROCm release is broken). - if (profile.IncludePrereleaseTorch) - { - torchArgs = torchArgs.AddArg("--pre"); - } - if (installedPackage.PipOverrides != null) { torchArgs = torchArgs.WithUserOverrides(installedPackage.PipOverrides); From 5e4f1ff20f37492c2662fc43a1dd2cf4bdd99b70 Mon Sep 17 00:00:00 2001 From: NeuralFault Date: Fri, 9 Oct 2026 02:59:03 +0000 Subject: [PATCH 6/6] docs: note Forge Neo AMD ROCm support - List Forge Neo under the Windows Native ROCm and Linux AMD (ROCm) package lists - Add a note that upstream Forge Neo supports NVIDIA CUDA only, so its ROCm support is added by Stability Matrix and is not supported upstream --- docs/advanced/hardware-support.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/advanced/hardware-support.md b/docs/advanced/hardware-support.md index ad74692cb..0a7e69920 100644 --- a/docs/advanced/hardware-support.md +++ b/docs/advanced/hardware-support.md @@ -58,7 +58,7 @@ Architectures in the `gfx110x`, `gfx115x`, and `gfx120x` ranges are treated as " - Torch is installed from AMD's ROCm multi-arch index (`repo.amd.com/rocm/whl-multi-arch/`) as device-specific wheels (`torch[device-gfxNNNN]`). Vega parts (`gfx900` / `gfx906`) pull from the 'TheRock' nightly multi-arch feed instead, since these architecture builds currently are only available there instead of the stable production distribution stream. - On modern architectures it applies a set of ROCm performance and attention environment variables at launch (MIOpen find-mode tuning, AOTriton experimental flash attention, `COMFYUI_ENABLE_MIOPEN`, and an allocator tuning string). AOTriton is excluded on the `gfx1152` / `gfx1153` APU architectures, which it does not yet support. Legacy architectures instead force a math SDP fallback. The full variable list and exactly which ones are auto-applied are documented in [Environment Variables](environment-variables.md#amd-and-rocm-variables). - ComfyUI offers optional extra commands for supported AMD GPUs, including **Install Triton and SageAttention (ROCm)** (Sage Attention 1.x), **Install Flash Attention (ROCm)** (legacy architectures), an **Install ROCm Development SDK** step, and an **Install bitsandbytes (ROCm)** step for Python 3.12 environments. -- **Packages:** ComfyUI, Stable Diffusion WebUI Reforge, InvokeAI, SwarmUI, and Wan2GP are supported by this install path. +- **Packages:** ComfyUI, Stable Diffusion WebUI Reforge, Stable Diffusion WebUI Forge - Neo, InvokeAI, SwarmUI, and Wan2GP are supported by this install path. > [!NOTE] While not managed by Stability Matrix for ROCm installs, SD.Next has a Windows-native ROCm install when the "ROCm" Pytorch index is selected in Advanced Installation Options during initial package install and `--use-rocm` is set in launch options. This install path is internally handled by SD.Next itself and currently only supports RDNA2 dedicated GPUs, RDNA3, RDNA3.5, and RDNA4 GPUs. **Caveats:** @@ -100,7 +100,10 @@ On Linux, AMD GPUs use native ROCm directly, which is the mature AMD path. - **Caveats:** - Native ROCm on Linux depends on a system-level ROCm installation and a compatible kernel/driver stack, which Stability Matrix does not install for you. - The Windows-only ROCm performance environment overrides described above are not auto-applied on Linux, so if you want them you can set them yourself via the [Environment Variables](environment-variables.md) editor. -- **Packages:** ComfyUI, Stable Diffusion WebUI, SD.Next, Stable Diffusion WebUI Forge, InvokeAI, SwarmUI, SDFX, OneTrainer, and Wan2GP list ROCm support. +- **Packages:** ComfyUI, Stable Diffusion WebUI, SD.Next, Stable Diffusion WebUI Forge, Stable Diffusion WebUI Forge - Neo, InvokeAI, SwarmUI, SDFX, OneTrainer, and Wan2GP list ROCm support. + + > [!NOTE] + > Forge Neo's upstream project officially supports NVIDIA CUDA only, so its ROCm support here is added by Stability Matrix and is not supported by the upstream developers. ## Intel (IPEX)