Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
51 changes: 16 additions & 35 deletions .github/workflows/build-tlx.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,17 @@ on:

jobs:
build-tlx:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
rid: win-x64
- os: macos-14
rid: osx-arm64
- os: ubuntu-latest
rid: linux-x64
runs-on: ${{ matrix.os }}
permissions:
contents: read
actions: read
Expand Down Expand Up @@ -48,42 +58,13 @@ jobs:
with:
dotnet-version: 8.0.x

- name: Get short SHA
uses: benjlevesque/short-sha@v3.0
id: short-sha
with:
length: 7

- name: Build & pack .tlx
shell: pwsh
run: |
# 单一事实来源:直接调用维护的打包脚本(与本地 dev 打包同一份逻辑),杜绝 CI 内联
# 逻辑漂移。脚本内部会:构建主插件 + MLRuntime,装配 mlruntime/ 子进程(去重
# onnxruntime),剪除输出根部冗余原生库/调试符号,产物落 tools/tlx/<id>-<version>.tlx。
pwsh DiffSingerForTuneLab/tools/pack-tlx.ps1 -Configuration Release

- name: Rename artifact with short SHA
- name: Pack RID-specific TLX
shell: pwsh
run: |
$ErrorActionPreference = 'Stop'
$tlx = Get-ChildItem DiffSingerForTuneLab/tools/tlx/*.tlx | Select-Object -First 1
if (-not $tlx) { Write-Error "未找到打包产物 .tlx" }
$dest = Join-Path $tlx.Directory.FullName "DiffSingerForTuneLab-${{ steps.short-sha.outputs.sha }}.tlx"
Move-Item $tlx.FullName $dest -Force
Write-Host ("产物: {0} ({1:N2} MB)" -f (Split-Path $dest -Leaf), ((Get-Item $dest).Length/1MB))
run: pwsh DiffSingerForTuneLab/tools/pack-tlx.ps1 -Configuration Release -RuntimeIdentifier ${{ matrix.rid }}

- name: Upload .tlx artifact
- name: Upload TLX artifact
uses: actions/upload-artifact@v4
with:
name: DiffSingerForTuneLab-${{ steps.short-sha.outputs.sha }}
path: DiffSingerForTuneLab/tools/tlx/DiffSingerForTuneLab-*.tlx
name: DiffSingerForTuneLab-${{ matrix.rid }}-${{ github.sha }}
path: DiffSingerForTuneLab/tools/tlx/*-${{ matrix.rid }}.tlx
retention-days: 14

- name: Upload build log on failure
if: failure()
uses: actions/upload-artifact@v4
with:
name: build-log-${{ steps.short-sha.outputs.sha }}
path: |
DiffSingerForTuneLab/**/bin/**/*.log
retention-days: 7
53 changes: 21 additions & 32 deletions .github/workflows/pr-test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,17 @@ on:

jobs:
build-and-test:
runs-on: windows-latest
strategy:
fail-fast: false
matrix:
include:
- os: windows-latest
rid: win-x64
- os: macos-14
rid: osx-arm64
- os: ubuntu-latest
rid: linux-x64
runs-on: ${{ matrix.os }}
permissions:
contents: read

Expand All @@ -39,45 +49,24 @@ jobs:
with:
dotnet-version: 8.0.x

- name: Restore NuGet packages
- name: Restore projects
shell: pwsh
run: |
Set-Location DiffSingerForTuneLab
# 主插件、测试项目、冒烟工具是三个独立项目(测试/工具不被主 csproj 引用,
# 裸 dotnet restore 只覆盖主插件),后续 build 步骤用 --no-restore,故此处逐个 restore。
dotnet restore
dotnet restore tests/DiffSinger.Tests/DiffSinger.Tests.csproj
dotnet restore tools/DiffSingerSmokeTest/DiffSingerSmokeTest.csproj
dotnet restore DiffSingerForTuneLab/DiffSingerForTuneLab.csproj -r ${{ matrix.rid }}
dotnet restore DiffSingerForTuneLab/MLRuntime/MLRuntime.csproj -r ${{ matrix.rid }}
dotnet restore DiffSingerForTuneLab/tests/DiffSinger.Tests/DiffSinger.Tests.csproj
dotnet restore DiffSingerForTuneLab/tools/DiffSingerSmokeTest/DiffSingerSmokeTest.csproj -r ${{ matrix.rid }}

- name: Build plugin (Release)
- name: Build plugin and MLRuntime
shell: pwsh
run: |
Set-Location DiffSingerForTuneLab
dotnet build --configuration Release --no-restore -v minimal

- name: Build unit tests
shell: pwsh
run: |
Set-Location DiffSingerForTuneLab
dotnet build tests/DiffSinger.Tests/DiffSinger.Tests.csproj --configuration Release --no-restore -v minimal
dotnet build DiffSingerForTuneLab/DiffSingerForTuneLab.csproj -c Release -r ${{ matrix.rid }} --no-self-contained --no-restore -v minimal
dotnet build DiffSingerForTuneLab/MLRuntime/MLRuntime.csproj -c Release -r ${{ matrix.rid }} --no-self-contained --no-restore -v minimal

- name: Run unit tests
shell: pwsh
run: |
Set-Location DiffSingerForTuneLab
dotnet test tests/DiffSinger.Tests/DiffSinger.Tests.csproj --configuration Release --no-build --logger:"console;verbosity=normal"
run: dotnet test DiffSingerForTuneLab/tests/DiffSinger.Tests/DiffSinger.Tests.csproj -c Release --no-restore --logger:"console;verbosity=normal"

- name: Build smoke test tool
shell: pwsh
run: |
Set-Location DiffSingerForTuneLab
dotnet build tools/DiffSingerSmokeTest/DiffSingerSmokeTest.csproj --configuration Release --no-restore -v minimal

- name: Upload test results
if: always()
uses: actions/upload-artifact@v4
with:
name: test-results-${{ github.sha }}
path: |
DiffSingerForTuneLab/tests/**/TestResult*.xml
retention-days: 7
run: dotnet build DiffSingerForTuneLab/tools/DiffSingerSmokeTest/DiffSingerSmokeTest.csproj -c Release -r ${{ matrix.rid }} --no-self-contained --no-restore -v minimal
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -17,3 +17,4 @@ obj/
# 打包产物
*.tlx
/build/
/artifacts/
32 changes: 16 additions & 16 deletions DiffSingerForTuneLab.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,14 @@
<!-- 插件作为动态加载的程序集:令 NuGet 私有依赖随输出复制进包目录(宿主 ALC 从包目录加载,
不会从本机 NuGet 缓存解析),并生成适配动态加载的 deps.json。Private=false 的 SDK 引用不受影响、仍不复制。 -->
<EnableDynamicLoading>true</EnableDynamicLoading>
<!-- RID 决定 ONNX Runtime 包:Windows 保留 DirectML,macOS/Linux 使用跨平台 CPU 包。
本地未显式传 -r 时按构建宿主给出已支持的默认 RID;正式 TLX 始终由 pack-tlx.ps1 显式传入。 -->
<DiffSingerRuntimeIdentifier Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('Windows'))">win-x64</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('OSX'))">osx-arm64</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('Linux'))">linux-x64</DiffSingerRuntimeIdentifier>
<UseDirectML Condition="'$(DiffSingerRuntimeIdentifier)' == 'win-x64'">True</UseDirectML>
<UseDirectML Condition="'$(UseDirectML)' == ''">False</UseDirectML>
</PropertyGroup>

<!-- tools/ 是 dev-only 独立项目(冒烟测试)、MLRuntime/ 是随包分发的独立 exe(自有 csproj/入口);
Expand Down Expand Up @@ -60,9 +68,8 @@
<Content Include="packaging\README.zh-CN.md" Link="README.zh-CN.md" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>

<!-- 把 DirectML.dll 拷进 runtimes/win-x64/native/(与 onnxruntime.dll 同目录)——见上方 PackageReference 注释。
deploy-dev / pack-tlx 均复制 bin 输出整树,故落到此处即随部署与 .tlx 一并分发。 -->
<Target Name="CopyDirectMLNative" AfterTargets="Build;Publish">
<!-- Windows x64 包把 DirectML.dll 放到 onnxruntime.dll 同目录;CPU-only 的 macOS/Linux 构建不执行。 -->
<Target Name="CopyDirectMLNative" AfterTargets="Build;Publish" Condition="'$(UseDirectML)' == 'True'">
<Copy SourceFiles="$(PkgMicrosoft_AI_DirectML)\bin\x64-win\DirectML.dll"
DestinationFolder="$(OutDir)runtimes\win-x64\native"
SkipUnchangedFiles="true" />
Expand All @@ -82,21 +89,14 @@
<EmbeddedResource Include="G2p\Data\g2p-ru.zip" LogicalName="DiffSingerForTuneLab.G2p.Data.g2p-ru.zip" />
<EmbeddedResource Include="G2p\Data\g2p-fil.zip" LogicalName="DiffSingerForTuneLab.G2p.Data.g2p-fil.zip" />
</ItemGroup>
<!-- 私有依赖:随包分发、进本插件专属 ALC。
· YamlDotNet 解析声库的 dsconfig / character / dsdict。
· OnnxRuntime.DirectML 单包内的 onnxruntime.dll 同含 DirectML EP 与 CPU EP;原生库随 EnableDynamicLoading
输出到 runtimes/win-x64/native/,宿主 ALC 经 deps.json(AssemblyDependencyResolver)解析——勿扁平化。 -->
<!-- 私有依赖:随包分发、进本插件专属 ALC。每次构建只选一个 ONNX Runtime 包,避免两个包提供
同名托管程序集/原生库而冲突。Windows x64 保留 DirectML(其中也含 CPU EP);macOS/Linux 用 CPU 包。 -->
<ItemGroup>
<PackageReference Include="YamlDotNet" Version="18.0.0" />
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.23.0" />
<!-- DirectML.dll 必须随包分发:OnnxRuntime.DirectML 单包只带 onnxruntime.dll,其硬依赖的 DirectML.dll
由传递包 Microsoft.AI.DirectML 提供、却躺在 bin/x64-win/(非 runtimes/<rid>/native 布局),SDK 默认
不随输出复制。于是运行时只能吃用户 System32 里那份——旧 Windows(如 Win10 19045)自带的是 2020 年的
DirectML 1.0,喂不动 onnxruntime 的 DML EP 用到的新 API → AppendExecutionProvider_DML 直接失败
(实测某测试者 GTX1660Ti 就栽在此)。故显式引该包、把 DirectML.dll 拷进 runtimes/win-x64/native/(与
onnxruntime.dll 同目录,NativeLibrary 的 altered-search-path 令其优先于 System32 被加载,已实测生效)。
版本须与 onnxruntime 配对:ort 1.23 配 DirectML 1.15.4(与 OpenUtau 同)。 -->
<PackageReference Include="Microsoft.AI.DirectML" Version="1.15.4" GeneratePathProperty="true" />
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.23.0" Condition="'$(UseDirectML)' == 'True'" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.23.0" Condition="'$(UseDirectML)' != 'True'" />
<!-- DirectML.dll 必须随 Windows 包分发;版本须与 ORT 1.23 配对。 -->
<PackageReference Include="Microsoft.AI.DirectML" Version="1.15.4" GeneratePathProperty="true" Condition="'$(UseDirectML)' == 'True'" />
<!-- 张量缓存键哈希:XxHash64(模型文件内容哈希 + 序列化输入)。 -->
<PackageReference Include="System.IO.Hashing" Version="8.0.0" />
</ItemGroup>
Expand Down
10 changes: 5 additions & 5 deletions DiffSingerModels.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,16 +35,16 @@ public sealed class DiffSingerModelCache : IDisposable

public DiffSingerModelCache(string provider, string runtimeMode, ILogger logger)
{
mProvider = provider;
mProvider = RuntimePlatform.NormalizeProvider(provider);
mLogger = logger;
if (Environment.GetEnvironmentVariable("DIFFSINGER_RUNTIME_LOOPBACK") == "1")
{
mRuntimeClient = new RuntimeClient(provider, p => new LoopbackTransport(new RuntimeHost(p)), canRespawn: false);
mRuntimeClient = new RuntimeClient(mProvider, p => new LoopbackTransport(new RuntimeHost(p)), canRespawn: false);
mLogger.Info("DiffSinger:MLRuntime loopback 模式(dev)");
}
else if (runtimeMode != "inprocess") // 默认 subprocess
{
mRuntimeClient = TryCreateSubprocessClient(provider);
mRuntimeClient = TryCreateSubprocessClient(mProvider);
}
}

Expand All @@ -53,7 +53,7 @@ public DiffSingerModelCache(string provider, string runtimeMode, ILogger logger)
RuntimeClient? TryCreateSubprocessClient(string provider)
{
var dir = Path.GetDirectoryName(typeof(DiffSingerModelCache).Assembly.Location)!;
var exe = Path.Combine(dir, "mlruntime", "MLRuntime.exe");
var exe = Path.Combine(dir, "mlruntime", RuntimePlatform.RuntimeExecutableName);
Action<string> exeLog = line => mLogger.Info($"[MLRuntime] {line}"); // 子进程 stdout/stderr 转发
Func<string, IRuntimeTransport> factory = p => new PipeTransport(exe, p, exeLog);
try
Expand Down Expand Up @@ -170,7 +170,7 @@ IModelSession LoadSession(string modelPath)
mLogger.Info($"DiffSinger:加载 {fileName} · MLRuntime({mProvider})");
return remote;
}
if (mProvider == "cpu")
if (!RuntimePlatform.IsDirectML(mProvider))
{
// 图优化用 onnxruntime 默认(ORT_ENABLE_ALL):1.20.1 时代的 BASIC 封顶已随 1.23 升级取消,见 RuntimeHost.LoadSession。
var cpu = new InferenceSession(modelPath);
Expand Down
18 changes: 12 additions & 6 deletions DiffSingerVoiceEngine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,8 @@ VoicebankConfig ConfigForRoot(string rootPath)
// 模型缓存按当前执行设备设置懒建;provider 变更则弃旧建新(旧缓存 Dispose 释放原生会话)。
DiffSingerModelCache EnsureModelCache()
{
var provider = mSettings.GetString(KeyExecutionProvider, "directml");
var provider = RuntimePlatform.NormalizeProvider(
mSettings.GetString(KeyExecutionProvider, RuntimePlatform.DefaultProvider));
var runtimeMode = mSettings.GetString(KeyRuntimeMode, "subprocess");
if (mModelCache == null || mProviderInUse != provider || mRuntimeModeInUse != runtimeMode)
{
Expand Down Expand Up @@ -169,11 +170,7 @@ public ObjectConfig GetSettingsConfig(IExtensionSettingsContext context)
},
{
(KeyExecutionProvider, L.Tr("Execution device")),
ComboBoxConfig.Create(new List<ComboBoxItem>
{
new(PropertyValue.Create("directml"), L.Tr("GPU (DirectML)")),
new(PropertyValue.Create("cpu"), L.Tr("CPU")),
})
ComboBoxConfig.Create(ExecutionProviderItems())
},
{
(KeyRuntimeMode, L.Tr("Inference mode")),
Expand All @@ -199,6 +196,15 @@ public ObjectConfig GetSettingsConfig(IExtensionSettingsContext context)
return ObjectConfig.Create(properties);
}

static List<ComboBoxItem> ExecutionProviderItems()
{
var items = new List<ComboBoxItem>();
if (RuntimePlatform.SupportsDirectML)
items.Add(new(PropertyValue.Create(RuntimePlatform.DirectMlProvider), L.Tr("GPU (DirectML)")));
items.Add(new(PropertyValue.Create(RuntimePlatform.CpuProvider), L.Tr("CPU")));
return items;
}

// 目录列表(变长):每行一个路径 TextBox、+ 追加空行。行数按当前已存值算——
// 缺席(从未配置)= 0 行、不 seed(默认目录本就隐式生效,无需列在此)。
static ListConfig DirListConfig(PropertyObject settings, string key)
Expand Down
10 changes: 3 additions & 7 deletions DirectMlNative.cs
Original file line number Diff line number Diff line change
Expand Up @@ -16,15 +16,11 @@ internal static class DirectMlNative
{
public static void Preload(string pluginRootDir)
{
if (!OperatingSystem.IsWindows())
return;
try
{
string rid = RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => "win-x86",
Architecture.Arm64 => "win-arm64",
_ => "win-x64",
};
var dml = Path.Combine(pluginRootDir, "runtimes", rid, "native", "DirectML.dll");
var dml = Path.Combine(pluginRootDir, "runtimes", RuntimePlatform.RuntimeIdentifier, "native", "DirectML.dll");
if (File.Exists(dml))
NativeLibrary.TryLoad(dml, out _);
}
Expand Down
15 changes: 11 additions & 4 deletions MLRuntime/MLRuntime.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -9,18 +9,25 @@
<Nullable>enable</Nullable>
<AssemblyName>MLRuntime</AssemblyName>
<RootNamespace>DiffSingerForTuneLab</RootNamespace>
<!-- 原生库随 EnableDynamicLoading 输出到 runtimes/win-x64/native/(同插件,经 deps.json 解析,勿扁平化)。 -->
<!-- 原生库随 EnableDynamicLoading 输出到 runtimes/<rid>/native/;打包后由 resolver 从父插件目录复用。 -->
<EnableDynamicLoading>true</EnableDynamicLoading>
<Platforms>x64</Platforms>
<DiffSingerRuntimeIdentifier Condition="'$(RuntimeIdentifier)' != ''">$(RuntimeIdentifier)</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('Windows'))">win-x64</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('OSX'))">osx-arm64</DiffSingerRuntimeIdentifier>
<DiffSingerRuntimeIdentifier Condition="'$(DiffSingerRuntimeIdentifier)' == '' and $([MSBuild]::IsOSPlatform('Linux'))">linux-x64</DiffSingerRuntimeIdentifier>
<UseDirectML Condition="'$(DiffSingerRuntimeIdentifier)' == 'win-x64'">True</UseDirectML>
<UseDirectML Condition="'$(UseDirectML)' == ''">False</UseDirectML>
</PropertyGroup>

<ItemGroup>
<!-- 与插件同一 onnxruntime 包(单包内含 DirectML EP 与 CPU EP)。 -->
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.23.0" />
<!-- 与插件同版且同平台包;Windows 含 DirectML/CPU,macOS/Linux 仅 CPU。 -->
<PackageReference Include="Microsoft.ML.OnnxRuntime.DirectML" Version="1.23.0" Condition="'$(UseDirectML)' == 'True'" />
<PackageReference Include="Microsoft.ML.OnnxRuntime" Version="1.23.0" Condition="'$(UseDirectML)' != 'True'" />
</ItemGroup>

<ItemGroup>
<!-- 与插件共享的可移植核(零 TuneLab 依赖):链接编译进本 exe,不新增共享 DLL。 -->
<Compile Include="..\RuntimePlatform.cs" Link="Shared\RuntimePlatform.cs" />
<Compile Include="..\DirectMlNative.cs" Link="Shared\DirectMlNative.cs" />
<Compile Include="..\TensorCodec.cs" Link="Shared\TensorCodec.cs" />
<Compile Include="..\RuntimeProtocol.cs" Link="Shared\RuntimeProtocol.cs" />
Expand Down
11 changes: 3 additions & 8 deletions MLRuntime/OnnxNativeResolver.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,20 +18,15 @@ public static void Register()
if (pluginDir == null)
return;

string rid = RuntimeInformation.ProcessArchitecture switch
{
Architecture.X86 => "win-x86",
Architecture.Arm64 => "win-arm64",
_ => "win-x64",
};
var candidate = Path.Combine(pluginDir, "runtimes", rid, "native", "onnxruntime.dll");
var candidate = Path.Combine(
pluginDir, "runtimes", RuntimePlatform.RuntimeIdentifier, "native", RuntimePlatform.OnnxRuntimeLibraryName);

NativeLibrary.SetDllImportResolver(typeof(Microsoft.ML.OnnxRuntime.SessionOptions).Assembly, (name, _, _) =>
name == "onnxruntime" && File.Exists(candidate) && NativeLibrary.TryLoad(candidate, out var handle)
? handle
: IntPtr.Zero); // 回退默认解析(自带 runtimes/ 仍在时可载)

// 预载随包 DirectML.dll(否则 DML EP delay-load 会走到 System32 的旧版而失败,见 DirectMlNative 说明)
// 仅 Windows 的 DirectML 构建需要预载;CPU-only 的 macOS/Linux 上该方法直接返回
DirectMlNative.Preload(pluginDir);
}
}
Loading
Loading