Windows 10 の ARM64 向けビルドは実質 Surface Pro X 向けですが、Surface Pro X でも x86 エミュレーションのオーバーヘッドを調べると無視できるレベルではなかったので、やっぱりアプリケーションは ARM64 ネイティブで動かしたい気持ちが高まってます。
ちなみに Geekbench 4 で x86 と ARM64 を比較した結果は以下のようになります。Surface Go よりは速いのですが、ARM64 ネイティブと比べると差がかなり大きいです。
C# での ARM64 ネイティブ実行は .NET Core 3.0 のコンソールアプリケーション以外不可能なので、現時点で ARM64 ネイティブのデスクトップ向けアプリを作るためには Win32 か UWP になります。*1
ARM64 ネイティブのアプリが増えるにはライブラリがビルド出来る必要があるので、Vcpkg と CMake の 2 つでクロスコンパイルの方法を試しました。昔に比べるとビルドが簡単になってて驚いてます。
ARM64 ビルドツールをインストール
まずは Visual Studio Installer を使って ARM64 ビルドツールをインストールします。ARM64 で検索するとたくさん出てきますが、バージョンの新しいやつを入れておけば良いです。
Win32 向けと UWP 向けの 2 つが出てくるので、両方インストールしておけばよいです。
Vcpkg を使ってビルド
C++ 向けのパッケージマネージャは C# と同じように、NuGet を使ってプリコンパイル済みのバイナリを拾ってくるのかと思ってましたが、最近はソースからビルドする方が流行っているようです。
マシンパワーが格段に上がった昨今では、環境依存を少なく出来るので確かに便利です。
Vcpkg の使い方は既にいろんな人が書いているのと、README に書いてあるコマンドをいくつか叩くだけでセットアップできるので丸っと省略します。
最初から ARM64 Windows に対応した Triplets が用意されているので、以下のようにインストール時に arm64-windows
を指定するだけでビルドが行われます。
vcpkg install zlib:arm64-windows vcpkg install boost-regex:arm64-windows
たまに先に x86-windows
向けのパッケージをインストールしてから再実行しろというエラーメッセージが出ますが、メッセージに従えば大体は問題なくビルドに成功します。
実際に zlib と boost-regex を ARM64 向けにビルドしてみましたが、問題なく完了しました。
パッケージマネージャらしく vcpkg list
を叩くと、ビルド済みのパッケージの管理が出来ます。
Vcpkg でビルドしたバイナリをそのまま、いい感じに C# のプロジェクトから参照するような MSBuild Task を用意したい気持ちでいっぱいです。
P/Invoke で使いたいシチュエーションも結構あると思うので、もうちょっと調べてみるつもりです。*2
CMake を直接使ってビルド
Vcpkg のベースになっている CMake も最初から ARM64 向けクロスコンパイルに対応しているので、こっちの方法も試してみました。CMake に関してはドキュメントを読むのが特に重要な感じでした。
全てのパッケージが Vcpkg でビルド出来れば楽ですが、そういう都合のいいことは無いので CMake 力を多少なりとも上げておきます。非対応のパッケージは当然ながらあります。
今回は Vcpkg で対応していたのですが、ARM64 ビルドは出来なかった OpenCV で試しました。というか ARM64 に対応させるために CMake の定義にパッチを当てました。
ARM64 向けツールセットは -A ARM64
を指定すると自動的に選択されましたが、CMAKE_SYSTEM_PROCESSOR
は自動設定されないようだったので ARM64 を明示的に追加して対応しました。
今回 ARM64 のクロスコンパイルに成功した CMake のパラメータは以下のような感じでした。
cmake -G "Visual Studio 16 2019" -A ARM64 -DCMAKE_SYSTEM_NAME=Windows -DCMAKE_SYSTEM_VERSION=10.0 \ -DCMAKE_SYSTEM_PROCESSOR=ARM64 -DWITH_OPENCL=OFF -DWITH_FFMPEG=OFF -DWITH_CUDA=OFF \ -DBUILD_EXAMPLES=ON -DBUILD_TESTS=ON C:\opencv4
内部で参照されている CMAKE_CROSSCOMPILING
は CMAKE_SYSTEM_NAME
と CMAKE_SYSTEM_VERSION
を明示的に指定しないと設定されないらしく、結構な時間を溶かしてしまいました。
CMake でのクロスコンパイルで重要なのは CMAKE_SYSTEM_NAME
/ CMAKE_SYSTEM_VERSION
/ CMAKE_SYSTEM_PROCESSOR
のようなので、忘れないようにしっかり残しておきます。Vcpkg は微妙にこの辺りの設定をミスっている気配ありますが…。
作業中にはまったポイントなど
C/C++ ランタイムのインストール
Vcpkg や CMake のデフォルトでは C/C++ ランタイムは動的リンクになるので、ビルドしたバイナリを直接 Surface Pro X にコピーしても動きません。忘れないように ARM64 向けのランタイムを入れておきます。
ひっそりと Visual Studio のページからダウンロード可能になっています。
Visual Studio のインストールディレクトリの中にも C/C++ ランタイムのインストーラがあります。
デバッグ用の C/C++ ランタイムは Visual Studio からリモートデバッグする時に、自動的にインストールがされるようなオプションがありましたが試せていません。
OpenGL ライブラリが存在しない
ARM64 Windows 10 の制約として書いてあるように OpenGL は基本的に使えないと考えてよいです。
使えないだけならまだ良いのですが、ARM64 向け SDK には opengl32.lib
すら存在しないので、依存しているライブラリは軒並みビルド出来なくなっています。
代わりに ANGLE を使えと回答が付いていますが、本当に ANGLE が動くのかも少し謎です。
Vcpkg に opengl が存在しているので、依存するライブラリは opengl (!arm64)
のように CONTROL
ファイルで定義しないとビルド出来ないのがかなり辛いです。
デフォルトで追加されるライブラリが少ない?
これはかなり謎な挙動なのですが、OpenCV の highgui をビルド中にリンクエラーが出たので調べたら、x86 / x64 の場合より ARM64 はデフォルトライブラリが少ないような挙動でした。
user32.lib
や kernel32.lib
はありますが、comdlg32.lib
と advapi32.lib
が無かったのでリンクエラーになっていました。どうしようもないので CMake 側で対応をしました。
アセンブラを使っていると辛い
当然ながらコンパイルには ARM64 向けのアセンブラが必要ですが、地味に VC++ の ARM アセンブラにリグレッションが多かったので困りました。
OpenCV の場合は SIMD の利用にコンパイラ側の機能を使っていたので、今度はコンパイラ依存の問題もあったりでひとまず棚上げにしたりも。リベンジはしたいです。