From 47da1da414276055102f25177a4dc42a020300f6 Mon Sep 17 00:00:00 2001 From: cm4ker Date: Sat, 30 Aug 2025 22:56:28 +0600 Subject: [PATCH 1/3] Windows Arm Support --- Directory.Build.props | 12 ++--- Directory.Build.targets | 25 ++++++++-- pkg/TorchAudio/TorchAudio.nupkgproj | 2 +- pkg/TorchVision/TorchVision.nupkgproj | 2 +- .../libtorch-cpu-win-arm64.nupkgproj | 16 ++++++ pkg/libtorch-cpu/libtorch-cpu.nupkgproj | 1 + pkg/pack.proj | 6 ++- src/Examples.Utils/Examples.Utils.csproj | 2 +- src/Native/build.cmd | 25 ++++------ src/Native/gen-buildsys-win.bat | 1 + src/Redist/libtorch-cpu/libtorch-cpu.proj | 15 +++++- ...arm64-shared-with-deps-2.7.1%2Bcpu.zip.sha | 1 + ...shared-with-deps-debug-2.7.1%2Bcpu.zip.sha | 1 + src/TorchSharp/Torch.cs | 50 +++++++++++++++++-- 14 files changed, 123 insertions(+), 36 deletions(-) create mode 100644 pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj create mode 100644 src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.7.1%2Bcpu.zip.sha create mode 100644 src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.7.1%2Bcpu.zip.sha diff --git a/Directory.Build.props b/Directory.Build.props index ff94123a3..8ba341083 100644 --- a/Directory.Build.props +++ b/Directory.Build.props @@ -52,14 +52,13 @@ mac linux + $(TargetOS)-$(TargetArchitecture) - win-x64 + win-arm64 + win-x64 linux-x64 osx-arm64 - - win-x64 - linux-x64 osx-$(TargetArchitecture) @@ -95,8 +94,8 @@ - true - false + true + false false @@ -141,6 +140,7 @@ cpu cu$(CudaVersionNoDot) libtorch-win-shared-with-deps$(LibTorchDebug) + libtorch-win-arm64-shared-with-deps$(LibTorchDebug) libtorch-cxx11-abi-shared-with-deps libtorch-macos-x86_64 libtorch-macos-arm64 diff --git a/Directory.Build.targets b/Directory.Build.targets index 70c79f166..dc72e290c 100644 --- a/Directory.Build.targets +++ b/Directory.Build.targets @@ -1,7 +1,12 @@ - + + + + + + - + @@ -20,6 +25,20 @@ + + + + + + + + + + + + + + @@ -116,5 +135,5 @@ - + \ No newline at end of file diff --git a/pkg/TorchAudio/TorchAudio.nupkgproj b/pkg/TorchAudio/TorchAudio.nupkgproj index 7ac14ee82..1ca16a71b 100644 --- a/pkg/TorchAudio/TorchAudio.nupkgproj +++ b/pkg/TorchAudio/TorchAudio.nupkgproj @@ -8,7 +8,7 @@ - + diff --git a/pkg/TorchVision/TorchVision.nupkgproj b/pkg/TorchVision/TorchVision.nupkgproj index f389e9b84..813fd1959 100644 --- a/pkg/TorchVision/TorchVision.nupkgproj +++ b/pkg/TorchVision/TorchVision.nupkgproj @@ -8,7 +8,7 @@ - + diff --git a/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj b/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj new file mode 100644 index 000000000..766c20d0d --- /dev/null +++ b/pkg/libtorch-cpu-win-arm64/libtorch-cpu-win-arm64.nupkgproj @@ -0,0 +1,16 @@ + + + + netstandard2.0 + + + + + + + + + + + + \ No newline at end of file diff --git a/pkg/libtorch-cpu/libtorch-cpu.nupkgproj b/pkg/libtorch-cpu/libtorch-cpu.nupkgproj index 97c3ffe67..97296c6c8 100644 --- a/pkg/libtorch-cpu/libtorch-cpu.nupkgproj +++ b/pkg/libtorch-cpu/libtorch-cpu.nupkgproj @@ -8,6 +8,7 @@ + diff --git a/pkg/pack.proj b/pkg/pack.proj index 3c9db2f98..d5f0f86f3 100644 --- a/pkg/pack.proj +++ b/pkg/pack.proj @@ -22,8 +22,10 @@ - + + diff --git a/src/Examples.Utils/Examples.Utils.csproj b/src/Examples.Utils/Examples.Utils.csproj index a542b181d..620d5b487 100644 --- a/src/Examples.Utils/Examples.Utils.csproj +++ b/src/Examples.Utils/Examples.Utils.csproj @@ -17,7 +17,7 @@ - + diff --git a/src/Native/build.cmd b/src/Native/build.cmd index c805b2608..ee5d19838 100644 --- a/src/Native/build.cmd +++ b/src/Native/build.cmd @@ -23,6 +23,7 @@ if /i [%1] == [Debug] ( set CMAKE_BUILD_TYPE=Debug&&shift&goto Arg_Loop) if /i [%1] == [x86] ( set __BuildArch=x86&&set __VCBuildArch=x86&&shift&goto Arg_Loop) if /i [%1] == [x64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) if /i [%1] == [amd64] ( set __BuildArch=x64&&set __VCBuildArch=x86_amd64&&shift&goto Arg_Loop) +if /i [%1] == [arm64] ( set __BuildArch=arm64&&set __VCBuildArch=x86_arm64&&shift&goto Arg_Loop) if /i [%1] == [--libtorchpath] ( set LIBTORCH_PATH=%2&&shift&goto Arg_Loop) @@ -64,40 +65,32 @@ exit /b 1 :: Setup vars for VS2022 set __PlatformToolset=v143 set __VSVersion=17 2022 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build +call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2019 :: Setup vars for VS2019 set __PlatformToolset=v142 set __VSVersion=16 2019 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build +call "%VS160COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2017 :: Setup vars for VS2017 set __PlatformToolset=v141 set __VSVersion=15 2017 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build +call "%VS150COMNTOOLS%..\..\VC\Auxiliary\Build\vcvarsall.bat" %__VCBuildArch% goto :SetupDirs :VS2015 :: Setup vars for VS2015build set __PlatformToolset=v140 set __VSVersion=14 2015 -if NOT "%__BuildArch%" == "arm64" ( - :: Set the environment for the native build - call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %__VCBuildArch% -) +:: Set the environment for the native build +call "%VS140COMNTOOLS%..\..\VC\vcvarsall.bat" %__VCBuildArch% :SetupDirs :: Setup to cmake the native components diff --git a/src/Native/gen-buildsys-win.bat b/src/Native/gen-buildsys-win.bat index b3870c171..e8ec2fd39 100644 --- a/src/Native/gen-buildsys-win.bat +++ b/src/Native/gen-buildsys-win.bat @@ -30,6 +30,7 @@ popd :: Set the target architecture to a format cmake understands. if /i "%3" == "x64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A x64) if /i "%3" == "x86" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A Win32) +if /i "%3" == "arm64" (set __ExtraCmakeParams=%__ExtraCmakeParams% -A ARM64) echo "%CMakePath%" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DLIBTORCH_PATH=%LIBTORCH_PATH%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% -B. -H%1 "%CMakePath%" "-DCMAKE_BUILD_TYPE=%CMAKE_BUILD_TYPE%" "-DCMAKE_INSTALL_PREFIX=%__CMakeBinDir%" "-DLIBTORCH_PATH=%LIBTORCH_PATH%" -G "Visual Studio %__VSString%" %__ExtraCmakeParams% -B. -H%1 diff --git a/src/Redist/libtorch-cpu/libtorch-cpu.proj b/src/Redist/libtorch-cpu/libtorch-cpu.proj index 9c538dbe8..16705c006 100644 --- a/src/Redist/libtorch-cpu/libtorch-cpu.proj +++ b/src/Redist/libtorch-cpu/libtorch-cpu.proj @@ -30,7 +30,7 @@ $(MainPackageFolder)\.copied.SkipTests.$(SkipTests).IncludeLibTorchCpuPackages.$(IncludeLibTorchCpuPackages) - + @@ -41,6 +41,19 @@ + + + + + + + + + + + + + diff --git a/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.7.1%2Bcpu.zip.sha b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.7.1%2Bcpu.zip.sha new file mode 100644 index 000000000..2a96235c2 --- /dev/null +++ b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-2.7.1%2Bcpu.zip.sha @@ -0,0 +1 @@ +F9DD47C792C900601F08265DDC0186036E01503ADA7895F0C7C90F8AF64FBC4B diff --git a/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.7.1%2Bcpu.zip.sha b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.7.1%2Bcpu.zip.sha new file mode 100644 index 000000000..f35825d9a --- /dev/null +++ b/src/Redist/libtorch-cpu/libtorch-win-arm64-shared-with-deps-debug-2.7.1%2Bcpu.zip.sha @@ -0,0 +1 @@ +DFDD2BECA32B1B3D4894EFA801DC60570849F0ED47A318734BA798A5C4D6513B diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index c59196bfe..de68b2a91 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -36,13 +36,13 @@ public static partial class torch RuntimeInformation.OSArchitecture == Architecture.Arm64; static string nativeRid => - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? $"win-x64" : + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ($"win-{(RuntimeInformation.OSArchitecture == Architecture.Arm64 ? "arm64" : "x64")}") : RuntimeInformation.IsOSPlatform(OSPlatform.Linux) ? $"linux-x64" : isAppleSilicon ? "osx-arm64" : "any"; static string nativeGlob => - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @".*\.dll" : + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @".*\.dll(\.dll)?" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? @".*\.dylib\.*" : // must match // lib.so @@ -127,7 +127,7 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr if (useCudaBackend) { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (isWindows) { + if (isWindows && RuntimeInformation.OSArchitecture != Architecture.Arm64) { trace.AppendLine($" Try loading Windows cuda native components"); // Preloading these DLLs on windows seems to iron out problems where one native DLL // requests a load of another through dynamic linking techniques. @@ -151,9 +151,49 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr ok = TryLoadNativeLibraryByName("torch_cuda", typeof(torch).Assembly, trace); ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + + // On Windows, also attempt direct absolute-path loads from the assembly directory + if (!ok && isWindows) { + var asmDir = Path.GetDirectoryName(typeof(torch).Assembly.Location)!; + var torchCudaPath = Path.Combine(asmDir, "torch_cuda.dll"); + var libTorchSharpPath = Path.Combine(asmDir, "LibTorchSharp.dll"); + trace.AppendLine($" Attempting absolute-path load of native components from {asmDir}..."); + ok = TryLoadNativeLibraryFromFile(torchCudaPath, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(libTorchSharpPath, trace); + } } else { - ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); - ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + if (isWindows) { + // Preload dependency chain explicitly on Windows: torch_global_deps -> c10 -> uv -> torch -> torch_cpu -> LibTorchSharp + trace.AppendLine($" Try loading Windows CPU native dependency chain: torch_global_deps -> c10 -> uv -> torch -> torch_cpu -> LibTorchSharp"); + ok = TryLoadNativeLibraryByName("torch_global_deps", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("c10", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("uv", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("torch", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + } else { + ok = TryLoadNativeLibraryByName("torch_cpu", typeof(torch).Assembly, trace); + if (ok) ok = TryLoadNativeLibraryByName("LibTorchSharp", typeof(torch).Assembly, trace); + } + + // On Windows, also attempt direct absolute-path loads from the assembly directory + if (!ok && isWindows) { + var asmDir = Path.GetDirectoryName(typeof(torch).Assembly.Location)!; + var torchGlobalDepsPath = Path.Combine(asmDir, "torch_global_deps.dll"); + var c10Path = Path.Combine(asmDir, "c10.dll"); + var uvPath = Path.Combine(asmDir, "uv.dll"); + var torchPath = Path.Combine(asmDir, "torch.dll"); + var torchCpuPath = Path.Combine(asmDir, "torch_cpu.dll"); + var libTorchSharpPath = Path.Combine(asmDir, "LibTorchSharp.dll"); + trace.AppendLine($" Attempting absolute-path load of native components from {asmDir}..."); + ok = TryLoadNativeLibraryFromFile(torchGlobalDepsPath, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(c10Path, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(uvPath, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(torchPath, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(torchCpuPath, trace); + if (ok) ok = TryLoadNativeLibraryFromFile(libTorchSharpPath, trace); + } } trace.AppendLine($" Result from regular native load of LibTorchSharp is {ok}"); From de6d26f5824ed460b1cb9457123b6dbab97981ba Mon Sep 17 00:00:00 2001 From: cm4ker Date: Sun, 31 Aug 2025 02:47:26 +0600 Subject: [PATCH 2/3] Update src/TorchSharp/Torch.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index de68b2a91..61b43a763 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -42,7 +42,7 @@ public static partial class torch "any"; static string nativeGlob => - RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @".*\.dll(\.dll)?" : + RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? @".*\.dll$" : RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? @".*\.dylib\.*" : // must match // lib.so From 68b3eb7d1b78155e7a07a062c222e4399395b658 Mon Sep 17 00:00:00 2001 From: cm4ker Date: Sun, 31 Aug 2025 02:48:03 +0600 Subject: [PATCH 3/3] Update src/TorchSharp/Torch.cs Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> --- src/TorchSharp/Torch.cs | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/TorchSharp/Torch.cs b/src/TorchSharp/Torch.cs index 61b43a763..cfb4770d8 100644 --- a/src/TorchSharp/Torch.cs +++ b/src/TorchSharp/Torch.cs @@ -127,7 +127,8 @@ private static void LoadNativeBackend(bool useCudaBackend, out StringBuilder? tr if (useCudaBackend) { var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); - if (isWindows && RuntimeInformation.OSArchitecture != Architecture.Arm64) { + // CUDA is not supported on Windows ARM64. Only attempt to load CUDA components on Windows x64. + if (isWindows && RuntimeInformation.OSArchitecture == Architecture.X64) { trace.AppendLine($" Try loading Windows cuda native components"); // Preloading these DLLs on windows seems to iron out problems where one native DLL // requests a load of another through dynamic linking techniques.