From 44f18d930ef15491f4a12d0022583eaba532ce32 Mon Sep 17 00:00:00 2001 From: WarmUpTill <19472752+WarmUpTill@users.noreply.github.com> Date: Mon, 13 Jul 2026 21:19:02 +0200 Subject: [PATCH] draft windows arm support --- .github/actions/run-tests/action.yaml | 2 +- .github/scripts/Build-Deps-Windows.ps1 | 14 ++-- .github/scripts/Build-Windows.ps1 | 2 +- .github/scripts/Package-Windows.ps1 | 15 ++-- .github/workflows/build-project.yaml | 60 ++++++++++++++ CMakePresets.json | 43 ++++++++++ buildspec.json | 12 ++- cmake/windows/buildspec.cmake | 102 ++++++++++++++++++++++++ lib/pch.hpp | 8 ++ lib/win/advanced-scene-switcher-win.cpp | 1 + plugins/openvr/CMakeLists.txt | 10 ++- plugins/speech/CMakeLists.txt | 10 +++ 12 files changed, 259 insertions(+), 20 deletions(-) diff --git a/.github/actions/run-tests/action.yaml b/.github/actions/run-tests/action.yaml index f265e4c6..99eeb710 100644 --- a/.github/actions/run-tests/action.yaml +++ b/.github/actions/run-tests/action.yaml @@ -38,4 +38,4 @@ runs: if: ${{ runner.os == 'Windows' }} shell: pwsh run: | - ${{ inputs.workingDirectory }}/build_x64/tests/${{ inputs.config }}/advanced-scene-switcher-tests.exe + ${{ inputs.workingDirectory }}/build_${{ inputs.target }}/tests/${{ inputs.config }}/advanced-scene-switcher-tests.exe diff --git a/.github/scripts/Build-Deps-Windows.ps1 b/.github/scripts/Build-Deps-Windows.ps1 index 460fd9e7..a568a16c 100644 --- a/.github/scripts/Build-Deps-Windows.ps1 +++ b/.github/scripts/Build-Deps-Windows.ps1 @@ -2,7 +2,7 @@ param( [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] [string] $Configuration = 'RelWithDebInfo', - [ValidateSet('x86', 'x64')] + [ValidateSet('x86', 'x64', 'arm64')] [string] $Target, [ValidateSet('Visual Studio 17 2022', 'Visual Studio 16 2019')] [string] $CMakeGenerator, @@ -211,13 +211,14 @@ function Build { $msbuildExe = vswhere -latest -requires Microsoft.Component.MSBuild -find MSBuild\**\Bin\MSBuild.exe | select-object -first 1 if ($msbuildExe) { + $LibusbPlatform = if ($Target -eq 'arm64') { 'arm64' } else { 'x64' } $env:CL="/wd5287" - Invoke-External $msbuildExe "${LibusbPath}/msvc/libusb.sln" /property:Configuration=Release /property:Platform=x64 + Invoke-External $msbuildExe "${LibusbPath}/msvc/libusb.sln" /property:Configuration=Release /property:Platform=$LibusbPlatform Remove-Item Env:CL - $libusbBuildResultDirectory = "${LibusbPath}/build/v143/x64/Release" + $libusbBuildResultDirectory = "${LibusbPath}/build/v143/${LibusbPlatform}/Release" if (-not (Test-Path -Path $libusbBuildResultDirectory)) { - $libusbBuildResultDirectory = "${LibusbPath}/x64/Release/dll" + $libusbBuildResultDirectory = "${LibusbPath}/${LibusbPlatform}/Release/dll" } Copy-Item -Path "${libusbBuildResultDirectory}/*" -Destination ${ADVSSDepPath} -Recurse -Force } else { @@ -247,9 +248,10 @@ function Build { if ($opensslDir) { Write-Host "Detected OpenSSL at: $opensslDir" + $OpenSSLArch = if ($Target -eq 'arm64') { 'arm64' } else { 'x64' } $MqttCmakeArgs += "-DOPENSSL_ROOT_DIR=$opensslDir" - $MqttCmakeArgs += "-DOPENSSL_CRYPTO_LIBRARY=$opensslDir\lib\VC\x64\MD\libcrypto.lib" - $MqttCmakeArgs += "-DOPENSSL_SSL_LIBRARY=$opensslDir\lib\VC\x64\MD\libssl.lib" + $MqttCmakeArgs += "-DOPENSSL_CRYPTO_LIBRARY=$opensslDir\lib\VC\$OpenSSLArch\MD\libcrypto.lib" + $MqttCmakeArgs += "-DOPENSSL_SSL_LIBRARY=$opensslDir\lib\VC\$OpenSSLArch\MD\libssl.lib" } else { Write-Warning "OpenSSL not found - maybe cmake will find it ..." } diff --git a/.github/scripts/Build-Windows.ps1 b/.github/scripts/Build-Windows.ps1 index 2958507c..51a5f28f 100644 --- a/.github/scripts/Build-Windows.ps1 +++ b/.github/scripts/Build-Windows.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] param( - [ValidateSet('x64')] + [ValidateSet('x64', 'arm64')] [string] $Target = 'x64', [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] [string] $Configuration = 'RelWithDebInfo', diff --git a/.github/scripts/Package-Windows.ps1 b/.github/scripts/Package-Windows.ps1 index d82c8d65..d745ca6e 100644 --- a/.github/scripts/Package-Windows.ps1 +++ b/.github/scripts/Package-Windows.ps1 @@ -1,6 +1,6 @@ [CmdletBinding()] param( - [ValidateSet('x64')] + [ValidateSet('x64', 'arm64')] [string] $Target = 'x64', [ValidateSet('Debug', 'RelWithDebInfo', 'Release', 'MinSizeRel')] [string] $Configuration = 'RelWithDebInfo', @@ -69,7 +69,8 @@ function Package { Remove-Item @RemoveArgs $ReleasePath = "${ProjectRoot}/release/${Configuration}" - $NewBinPath = "${ReleasePath}/${ProductName}/bin/64bit" + $BinDirName = if ($Target -eq 'arm64') { 'arm64' } else { '64bit' } + $NewBinPath = "${ReleasePath}/${ProductName}/bin/${BinDirName}" $NewDataPath = "${ReleasePath}/${ProductName}/data" $CIWindowsDir = "${ProjectRoot}/build-aux/CI/windows" @@ -80,7 +81,7 @@ function Package { New-Item -ItemType Directory -Force -Path $RecStaging | Out-Null Copy-Item -Path "${CIWindowsDir}/README.txt" -Destination "${RecStaging}/README.txt" if ( Test-Path -Path $NewBinPath ) { - $RecBinPath = "${RecStaging}/${ProductName}/bin/64bit" + $RecBinPath = "${RecStaging}/${ProductName}/bin/${BinDirName}" New-Item -ItemType Directory -Force -Path $RecBinPath | Out-Null Copy-Item -Path "${NewBinPath}/*" -Destination $RecBinPath -Recurse -Force } @@ -102,7 +103,7 @@ function Package { New-Item -ItemType Directory -Force -Path $PortableStaging | Out-Null Copy-Item -Path "${CIWindowsDir}/README-portable.txt" -Destination "${PortableStaging}/README.txt" if ( Test-Path -Path $NewBinPath ) { - $PortableBinPath = "${PortableStaging}/obs-plugins/64bit" + $PortableBinPath = "${PortableStaging}/obs-plugins/${BinDirName}" New-Item -ItemType Directory -Force -Path $PortableBinPath | Out-Null Copy-Item -Path "${NewBinPath}/*" -Destination $PortableBinPath -Recurse -Force } @@ -136,10 +137,10 @@ function Package { Copy-Item -Path "${Configuration}/*" -Destination $PkgRec -Recurse -Force # Legacy layout (for OBS installation directory) - if ( Test-Path "${Configuration}/${ProductName}/bin/64bit" ) { - $PkgLegBin = "Package/portable/obs-plugins/64bit" + if ( Test-Path "${Configuration}/${ProductName}/bin/${BinDirName}" ) { + $PkgLegBin = "Package/portable/obs-plugins/${BinDirName}" New-Item -ItemType Directory -Force -Path $PkgLegBin | Out-Null - Copy-Item -Path "${Configuration}/${ProductName}/bin/64bit/*" -Destination $PkgLegBin -Recurse -Force + Copy-Item -Path "${Configuration}/${ProductName}/bin/${BinDirName}/*" -Destination $PkgLegBin -Recurse -Force } if ( Test-Path "${Configuration}/${ProductName}/data" ) { $PkgLegData = "Package/portable/data/obs-plugins/${ProductName}" diff --git a/.github/workflows/build-project.yaml b/.github/workflows/build-project.yaml index 184d872c..a1ed656c 100644 --- a/.github/workflows/build-project.yaml +++ b/.github/workflows/build-project.yaml @@ -415,3 +415,63 @@ jobs: with: name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-windows-x64-${{ needs.check-event.outputs.commitHash }} path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-windows-x64*.* + + windows-arm64-build: + name: Build for Windows ARM64 🪟 + runs-on: windows-11-arm + needs: check-event + defaults: + run: + shell: pwsh + steps: + - uses: actions/checkout@v6 + with: + submodules: recursive + fetch-depth: 0 + + - name: Set Up Environment 🔧 + id: setup + run: | + # Set Up Environment 🔧 + if ( $Env:RUNNER_DEBUG -ne $null ) { + Set-PSDebug -Trace 1 + } + + $BuildSpec = Get-Content -Path buildspec.json -Raw | ConvertFrom-Json + $ProductName = $BuildSpec.name + $GitOutput = git describe --tags + + "pluginName=${ProductName}" >> $env:GITHUB_OUTPUT + "pluginVersion=${GitOutput}" >> $env:GITHUB_OUTPUT + + - name: Build Dependencies 🏗️ + uses: ./.github/actions/build-dependencies + with: + workingDirectory: ${{ github.workspace }}/plugin + target: arm64 + config: ${{ needs.check-event.outputs.config }} + + - name: Build Plugin 🧱 + uses: ./.github/actions/build-plugin + with: + target: arm64 + config: ${{ needs.check-event.outputs.config }} + + - name: Run tests + uses: ./.github/actions/run-tests + with: + target: arm64 + config: ${{ needs.check-event.outputs.config }} + + - name: Package Plugin 📀 + uses: ./.github/actions/package-plugin + with: + target: arm64 + config: ${{ needs.check-event.outputs.config }} + package: ${{ fromJSON(needs.check-event.outputs.package) }} + + - name: Upload Artifacts 📡 + uses: actions/upload-artifact@v7 + with: + name: ${{ steps.setup.outputs.pluginName }}-${{ steps.setup.outputs.pluginVersion }}-windows-arm64-${{ needs.check-event.outputs.commitHash }} + path: ${{ github.workspace }}/release/${{ steps.setup.outputs.pluginName }}-*-windows-arm64*.* diff --git a/CMakePresets.json b/CMakePresets.json index 547449a9..4e823a36 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -74,6 +74,35 @@ "ADVSS_ENABLE_TESTS": true } }, + { + "name": "windows-arm64", + "displayName": "Windows ARM64", + "description": "Build for Windows ARM64", + "inherits": ["template"], + "binaryDir": "${sourceDir}/build_arm64", + "condition": { + "type": "equals", + "lhs": "${hostSystemName}", + "rhs": "Windows" + }, + "generator": "Visual Studio 17 2022", + "architecture": "ARM64", + "warnings": {"dev": true, "deprecated": true}, + "cacheVariables": { + "QT_VERSION": "6", + "CMAKE_SYSTEM_VERSION": "10.0.18363.657" + } + }, + { + "name": "windows-ci-arm64", + "inherits": ["windows-arm64"], + "displayName": "Windows ARM64 CI build", + "description": "Build for Windows ARM64 on CI", + "cacheVariables": { + "CMAKE_COMPILE_WARNING_AS_ERROR": true, + "ADVSS_ENABLE_TESTS": true + } + }, { "name": "linux-x86_64", "displayName": "Linux x86_64", @@ -162,6 +191,20 @@ "description": "Windows CI build for x64 (RelWithDebInfo configuration)", "configuration": "RelWithDebInfo" }, + { + "name": "windows-arm64", + "configurePreset": "windows-arm64", + "displayName": "Windows ARM64", + "description": "Windows build for ARM64", + "configuration": "RelWithDebInfo" + }, + { + "name": "windows-ci-arm64", + "configurePreset": "windows-ci-arm64", + "displayName": "Windows ARM64 CI", + "description": "Windows CI build for ARM64 (RelWithDebInfo configuration)", + "configuration": "RelWithDebInfo" + }, { "name": "linux-x86_64", "configurePreset": "linux-x86_64", diff --git a/buildspec.json b/buildspec.json index eacb8ebf..b72e482e 100644 --- a/buildspec.json +++ b/buildspec.json @@ -6,7 +6,8 @@ "label": "OBS sources", "hashes": { "macos": "39751f067bacc13d44b116c5138491b5f1391f91516d3d590d874edd21292291", - "windows-x64": "2c8427c10b55ac6d68008df2e9a3e82f4647aaad18f105e30d4713c2de678ccf" + "windows-x64": "2c8427c10b55ac6d68008df2e9a3e82f4647aaad18f105e30d4713c2de678ccf", + "windows-ARM64": "2c8427c10b55ac6d68008df2e9a3e82f4647aaad18f105e30d4713c2de678ccf" } }, "prebuilt": { @@ -15,7 +16,8 @@ "label": "Pre-Built obs-deps", "hashes": { "macos": "495687e63383d1a287684b6e2e9bfe246bb8f156fe265926afb1a325af1edd2a", - "windows-x64": "c8c642c1070dc31ce9a0f1e4cef5bb992f4bff4882255788b5da12129e85caa7" + "windows-x64": "c8c642c1070dc31ce9a0f1e4cef5bb992f4bff4882255788b5da12129e85caa7", + "windows-ARM64": "f581cc61e8f734a8b12d485fc8662a408ca59d222814e4b37bce115bd442fb04" } }, "qt6": { @@ -24,10 +26,12 @@ "label": "Pre-Built Qt6", "hashes": { "macos": "d3f5f04b6ea486e032530bdf0187cbda9a54e0a49621a4c8ba984c5023998867", - "windows-x64": "0e76bf0555dd5382838850b748d3dcfab44a1e1058441309ab54e1a65b156d0a" + "windows-x64": "0e76bf0555dd5382838850b748d3dcfab44a1e1058441309ab54e1a65b156d0a", + "windows-ARM64": "7aab240504931f32ea8e8d208a912a0d6ddbd78c16858f2e559c7c9b29ab9326" }, "debugSymbols": { - "windows-x64": "11b7be92cf66a273299b8f3515c07a5cfb61614b59a4e67f7fc5ecba5e2bdf21" + "windows-x64": "11b7be92cf66a273299b8f3515c07a5cfb61614b59a4e67f7fc5ecba5e2bdf21", + "windows-ARM64": "884ae08291b5c87d83a7b0004ea8db356530c1f4ff9bdc814b5d599a8d28a152" } } }, diff --git a/cmake/windows/buildspec.cmake b/cmake/windows/buildspec.cmake index 1b5a2f41..98544a3a 100644 --- a/cmake/windows/buildspec.cmake +++ b/cmake/windows/buildspec.cmake @@ -23,6 +23,108 @@ function(_check_dependencies_windows) set(dependencies_list prebuilt qt6 obs-studio) _check_dependencies() + + if(arch STREQUAL "ARM64") + _setup_qt6_host_windows() + endif() +endfunction() + +# _setup_qt6_host_windows: Fetch native x64 Qt6 as QT_HOST_PATH for ARM64 build +# tools +function(_setup_qt6_host_windows) + set(dependencies_dir "${CMAKE_CURRENT_SOURCE_DIR}/.deps") + + if(NOT buildspec) + file(READ "${CMAKE_CURRENT_SOURCE_DIR}/buildspec.json" buildspec) + endif() + + string(JSON data GET ${buildspec} dependencies qt6) + string(JSON version GET ${data} version) + string(JSON hash GET ${data} hashes windows-x64) + string(JSON base_url GET ${data} baseUrl) + string(JSON label GET ${data} label) + string( + JSON + revision + ERROR_VARIABLE + error + GET + ${data} + revision + windows-x64) + + set(file "windows-deps-qt6-VERSION-x64-REVISION.zip") + set(destination "obs-deps-qt6-VERSION-x64") + string(REPLACE "VERSION" "${version}" file "${file}") + string(REPLACE "VERSION" "${version}" destination "${destination}") + if(revision) + string(REPLACE "_REVISION" "_v${revision}" file "${file}") + string(REPLACE "-REVISION" "-v${revision}" file "${file}") + else() + string(REPLACE "_REVISION" "" file "${file}") + string(REPLACE "-REVISION" "" file "${file}") + endif() + + set(url "${base_url}/${version}/${file}") + + message(STATUS "Setting up ${label} host (x64) for ARM64 cross-compilation") + + if(NOT EXISTS + "${dependencies_dir}/${destination}/lib/cmake/Qt6/Qt6Config.cmake") + if(NOT EXISTS "${dependencies_dir}/${file}") + message(STATUS "Downloading ${url}") + + set(MAX_DOWNLOAD_RETRIES 3) + set(RETRY_DELAY 60) # seconds + set(download_success FALSE) + + foreach(i RANGE 1 ${MAX_DOWNLOAD_RETRIES}) + message(STATUS "Attempt ${i}/${MAX_DOWNLOAD_RETRIES} for ${url}") + + file( + DOWNLOAD "${url}" "${dependencies_dir}/${file}" + STATUS download_status + EXPECTED_HASH SHA256=${hash}) + + list(GET download_status 0 error_code) + list(GET download_status 1 error_message) + + if(error_code EQUAL 0) + message(STATUS "Downloading ${url} - success on attempt ${i}") + set(download_success TRUE) + break() + else() + message(WARNING "Download failed (attempt ${i}): ${error_message}") + file(REMOVE "${dependencies_dir}/${file}") + + if(NOT i EQUAL MAX_DOWNLOAD_RETRIES) + message(STATUS "Retrying in ${RETRY_DELAY} seconds...") + execute_process(COMMAND ${CMAKE_COMMAND} -E sleep ${RETRY_DELAY}) + endif() + endif() + endforeach() + + if(NOT download_success) + message( + FATAL_ERROR + "Unable to download ${url} after ${MAX_DOWNLOAD_RETRIES} attempts") + endif() + message(STATUS "Downloading ${url} - done") + endif() + + file(MAKE_DIRECTORY "${dependencies_dir}/${destination}") + file(ARCHIVE_EXTRACT INPUT "${dependencies_dir}/${file}" DESTINATION + "${dependencies_dir}/${destination}") + endif() + + message(STATUS "Using ${dependencies_dir}/${destination} as QT_HOST_PATH") + set(QT_HOST_PATH + "${dependencies_dir}/${destination}" + CACHE PATH "Path to a native Qt6 installation used to supply host tools" + FORCE) + + message( + STATUS "Setting up ${label} host (x64) for ARM64 cross-compilation - done") endfunction() _check_dependencies_windows() diff --git a/lib/pch.hpp b/lib/pch.hpp index 57ba0a2a..c19ab166 100644 --- a/lib/pch.hpp +++ b/lib/pch.hpp @@ -38,3 +38,11 @@ #include #include #include + +// ARM64 libobs pulls in windows.h, whose macros collide with our identifiers +#ifdef _WIN32 +#undef DELETE +#undef DispatchMessage +#undef SendMessage +#undef GetObject +#endif diff --git a/lib/win/advanced-scene-switcher-win.cpp b/lib/win/advanced-scene-switcher-win.cpp index eb5ea85f..ec0e4126 100644 --- a/lib/win/advanced-scene-switcher-win.cpp +++ b/lib/win/advanced-scene-switcher-win.cpp @@ -2,6 +2,7 @@ #include "plugin-state-helpers.hpp" #include +#include #include #include #include diff --git a/plugins/openvr/CMakeLists.txt b/plugins/openvr/CMakeLists.txt index bd64014d..6e66954c 100644 --- a/plugins/openvr/CMakeLists.txt +++ b/plugins/openvr/CMakeLists.txt @@ -9,6 +9,12 @@ if(NOT WIN32) return() endif(NOT WIN32) +if(CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|arm64|aarch64") + message(WARNING "OpenVR does not provide Windows ARM64 binaries!\n" + "OpenVR condition will be disabled!") + return() +endif() + # --- End of section --- add_library(${PROJECT_NAME} MODULE) @@ -28,7 +34,9 @@ endif() if(EXISTS "${OpenVR_DIR}/CMakeLists.txt") set(SIZEOF_VOIDP ${CMAKE_SIZEOF_VOID_P}) - if((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8)) + if(WIN32 AND CMAKE_SYSTEM_PROCESSOR MATCHES "ARM64|arm64|aarch64") + set(PROCESSOR_ARCH "arm64") + elseif((NOT APPLE) AND (CMAKE_SIZEOF_VOID_P EQUAL 8)) set(PROCESSOR_ARCH "64") else() set(PROCESSOR_ARCH "32") diff --git a/plugins/speech/CMakeLists.txt b/plugins/speech/CMakeLists.txt index e6a87b4a..b606d751 100644 --- a/plugins/speech/CMakeLists.txt +++ b/plugins/speech/CMakeLists.txt @@ -12,6 +12,16 @@ if(NOT EXISTS "${WHISPER_DIR}/CMakeLists.txt") return() endif() +# whisper.cpp's ggml CPU backend requires clang on Windows ARM64 and fails with +# MSVC +if(OS_WINDOWS AND CMAKE_GENERATOR_PLATFORM STREQUAL "ARM64") + message( + WARNING + "Speech plugin is not supported on Windows ARM64 (requires clang)!\n" + "Speech condition will be disabled!") + return() +endif() + set(WHISPER_BUILD_TESTS OFF CACHE BOOL "" FORCE)