Add native ARM64 (Windows on Arm) support#666
Conversation
Add ARM64 platform configurations to the ConsoleApplication.sln stack (PresentData, PresentMon, CommonUtilities) so PresentMon.exe builds and runs natively on Windows on Arm. Verified on a Snapdragon device: the DxgKrnl/DWM ETW providers materialize under Qualcomm's WDDM driver, present-mode classification and per-frame GPU timing populate correctly, and CSV output matches x64. - CommonUtilities: add Debug|ARM64 and Release|ARM64 (mirroring x64), which import vcpkg.props so the target triplet resolves to arm64-windows-static. - PresentData, PresentMon: regenerate ARM64 configurations to mirror x64 exactly, repairing stale ARM64 leftovers that were missing LanguageStandard=stdcpplatest and, for PresentMon, the mc.exe DD-events resource-generation custom build step. - Remove dead 32-bit ARM configurations; Windows on Arm runs x86/x64 under emulation and modern toolsets no longer target ARM32. - ConsoleApplication.sln: drop 32-bit ARM, map CommonUtilities ARM64 to its real ARM64 configuration instead of x64. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extends the ARM64 port beyond the console app across the PresentMon.sln build graph: - Add Debug|ARM64 / Release|ARM64 configurations to the service, SDK/API, interprocess, middleware, provider, versioning and tooling projects, mirroring their existing x64 configs. Strip the dead 32-bit ARM configs and restore LanguageStandard=stdcpplatest on ARM64 in the test project. - Pin vcpkg to the v143 toolset for arm64-windows-static via an overlay triplet, so ARM64 dependencies (e.g. CLI11) are compiled with v143 and link against the same runtime the projects use, instead of the newer default toolset that ships natively on VS 2026. - Drop the hardcoded PreferredToolArchitecture=x64 from the ARM64 configs so an ARM64 build host uses the native arm64 compiler rather than the emulated x64 cross tools. - Wire ARM64 solution configurations into PresentMon.sln. CEF-linked projects (Core, CefNano, KernelProcess and their tests) stay x64-only and are skipped for ARM64 pending later phases. Builds clean for ARM64 (Debug + Release) on a Snapdragon/WoA device: PresentMonService, PresentMonAPI2 and loader, the ETW provider, SampleClient, ETLTrimmer, the etw_list/pm_convert_csv tools and the tests. ETLTrimmer additionally requires the v143 ATL component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extend the ARM64 port to the CEF-based Capture UI and the top-level launcher, completing the desktop application stack. - Core, CefNano (PresentMonUI.exe) and KernelProcess (PresentMon.exe) get native ARM64 project configurations; PresentMon.sln wires them as buildable ARM64 targets. - CEF batch pipeline made architecture-aware via a -Platform switch (default x64 unchanged). ARM64 uses a parallel cef-lock.arm64.json; the x64 cef-lock.json is untouched. The wrapper build now auto-detects the Visual Studio generator and builds via `cmake --build` (handling the new .slnx solution format), pinning toolset v143. - Shaders.vcxitems gains ARM64 FXCompile conditions, fixing the pixel shader miscompiling as vs_2_0 under ARM64. Full-solution ARM64 builds clean for Debug and Release (LTCG+CFG); PresentMonUI.exe links the ARM64 CEF runtime and runs natively. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Enable ARM64 for the native CppUnitTestFramework test DLLs: - UnitTests (PresentMonUnitTests) and PresentMonAPI2Tests get ARM64 project configurations; PresentMon.sln wires them as buildable ARM64 targets. EtlLoggerTests.cpp remains excluded on ARM64, mirroring x64. Both build clean for ARM64. PresentMonUnitTests runs 287/288 on-device; the single failure is a pre-existing, architecture-exposed metric attribution discrepancy in shared code (Intel XeFG generated-frame msCPUBusy), tracked separately. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Completes the Windows-on-Arm port with an architecture-aware WiX installer and the build-tooling fixes needed to produce it. Installer / packaging: - PresentMon.wxs: derive the MSI Package platform from $(sys.BUILDARCH); make the bundled console app arch-aware. - PMInstaller.wixproj: add a real ARM64 platform (-arch arm64) and make the CEF installer validation architecture-aware. - PMInstallerExtension: console-app filename helper takes an arch argument. - PresentMon.sln: wire the installer projects (PMInstaller, PMInstallerLib, PMInstallerExtension) to build for ARM64. - PMInstallerLib.wixproj: resolve WixTargetsPath robustly so the installer builds under the VS-hosted ARM64 MSBuild. Build tooling (arch-neutral robustness; no-op on VS 2022): - triplets/x64-windows-static.cmake + x86-windows-static.cmake: pin the vcpkg toolset to v143 to match the app toolset (fixes the VS 2026 link skew). - cef-lock.psm1: generate the CEF WiX fragments with a locale-independent (OrdinalIgnoreCase) sort and compare them independent of line endings, so the installer builds reproducibly on any host. - bootstrap.ps1: add -Platform x64|arm64 to restore the matching CEF payload. Docs: - BUILDING.md: VS 2022/2026 toolset notes and a "Building for ARM64" section. Validated end-to-end on a Snapdragon device: the produced arm64 MSI (Template=Arm64;1033) installs and runs with live frame metrics. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Build in Release and run the unit tests on both native toolchains:
- x64 on windows-2022 (VS 2022 + WiX 3.14): full solution build,
which also packages the x64 MSI; the MSI is uploaded as an artifact.
- ARM64 on windows-11-arm (VS 2022, no WiX): build the app, service,
API and unit tests directly (installer skipped).
A throwaway self-signed certificate is created per run to satisfy the
KernelProcess Release SignTool post-build. The one known ARM64-only
metrics unit-test failure is excluded on the ARM64 leg (tracked follow-up).
Layered caching keeps runs fast: the installed vcpkg dependency trees
(vcpkg_installed, so the install is skipped wholesale on a hit — robust
even where vcpkg's binary-cache ABI hash isn't reproducible on arm64),
vcpkg source downloads, the staged CEF payload (skips its download +
wrapper build), aux test data, and the npm download cache. On a warm
cache the build also runs in parallel (msbuild /m); a cold run falls back
to serial to avoid a concurrent vcpkg-install download race.
Documented in BUILDING.md (Continuous Integration).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
vcpkg.props pinned VcpkgHostTriplet to x64-windows-static unconditionally, so on a Windows-on-Arm build host the host dependencies resolved as x64 and their tooling ran under emulation. Make the pin host-aware: arm64-windows-static when the machine running MSBuild is ARM64, x64-windows-static otherwise. The x64 fallback (rather than pinning arm64 for ARM64 targets) is what keeps cross-compilation working: an x64 host cannot run arm64 host tools, while an arm64 host can run x64 ones under emulation. The host is detected via RuntimeInformation.OSArchitecture, which reports Arm64 from the native arm64 MSBuild (the CI leg uses msbuild-architecture: arm64; VS on ARM64 is native); an x64 MSBuild running emulated cannot see through the emulation and lands on the still-working x64 fallback. Drop ETLTrimmer's per-config Vcpkg property groups: they restated exactly what the imported vcpkg.props already sets (see the Debug configs, which have no such group), and the hardcoded host triplet would now defeat the host-aware selection. Add vcpkg.props to the CI vcpkg_installed cache key: the vcpkg install stamp file is named after the host triplet, so a cached tree from before this change would re-run the install (and, with the arm64 ABI drift, rebuild all deps) on every warm run. The key bump takes one cold run per leg and re-caches with the new stamp names. Local checkouts similarly re-run the vcpkg install once after this change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
| @@ -0,0 +1,274 @@ | |||
| name: CI | |||
There was a problem hiding this comment.
I thought adding CI would be a nice addition so that you confidently merge changes in the future while being sure that they work both on x64 and arm64. Feel free to leave this out if you rather not have CI pipelines (they take quite a while, especially when there's no cache yet). Here's a successful run in my fork: https://github.com/dennisameling/PresentMon/actions/runs/29485626232
| <PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'"> | ||
| <VcpkgUseStatic>true</VcpkgUseStatic> | ||
| <VcpkgHostTriplet>x64-windows-static</VcpkgHostTriplet> | ||
| </PropertyGroup> |
There was a problem hiding this comment.
This is intentional; see the vcpkg.props file in the repo root which will be picked up automatically here.
| function Get-CefLockPath { | ||
| return Join-Path (Get-AppCefRoot) 'cef-lock.json' | ||
| param([string]$Platform = 'x64') | ||
| $name = if ($Platform -ieq 'arm64') { 'cef-lock.arm64.json' } else { 'cef-lock.json' } |
There was a problem hiding this comment.
We could probably merge this into a single lockfile for both archs to make things easier; let me know what you prefer.
| All projects pin the **v143** platform toolset. Visual Studio 2022 ships v143 by | ||
| default and needs nothing extra. Visual Studio 2026 on ARM64 ships only the newer | ||
| v145 toolset, so install the v143 build tools alongside it from the Visual Studio | ||
| Installer (*Individual components*): | ||
|
|
||
| - *MSVC v143 - VS 2022 C++ ARM64/ARM64EC build tools* **and** *... x64/x86 build tools* | ||
| - *C++ ATL for v143 build tools* for both *ARM64/ARM64EC* and *x64/x86* (required by ETLTrimmer) |
There was a problem hiding this comment.
I think #634 would make it easier moving forward to switch between MSVC / Visual Studio versions without having to update a lot of build files across the board.
| | `windows-2022` | VS 2022 + WiX 3.14 | Full `PresentMon.sln` (x64) and **packages the MSI**, uploaded as the `PresentMon-x64-msi` artifact | | ||
| | `windows-11-arm` | VS 2022 (no WiX) | App, service, API and unit tests (the WiX installer projects are skipped) | |
There was a problem hiding this comment.
WiX v3 is no longer supported. More recent versions have better ARM64 support. Happy to file a follow-up PR to upgrade to the latest version if that's OK for you. https://www.firegiant.com/blog/2025/2/6/wix-v3-and-wix-v4-are-no-longer-in-community-support/
Closes #667
Summary
Adds native ARM64 (Windows on Arm) support to PresentMon, which previously shipped x64 only. Every component now builds and runs natively on ARM64 (validated on a Snapdragon/Adreno device and in CI). Here's a successful CI run: https://github.com/dennisameling/PresentMon/actions/runs/29485626232
This is a stacked series of 7 commits, one per area — reviewing commit-by-commit is recommended.
I still have a backup branch which has
FlashInjectorincluded: https://github.com/dennisameling/PresentMon/tree/backup/arm64-support-pre-rebaseThe app works end to end on ARM64:
I also tested the overlay in a x64 game (The Sims 4) on an ARM64 device and even that worked nicely:
Building the ARM64 MSI also works locally and installs correctly. These are the files that it installs:
What's included
Console app & core analysis (
28dfbe15)ConsoleApplication.sln.Service, SDK/API, provider & tooling (
01a35e54)PresentMon.slnwiring.triplets/arm64-windows-static.cmakepins the vcpkg toolset to v143 to match the app ABI.Capture UI (CEF) & kernel process (
e734227b)-Platformon pull/upgrade/validate; per-arch lockcef-lock.arm64.json).Test projects (
ff101438)Installer, packaging & docs (
7c288d4c)WixTargetsPath; locale-independent CEF WiX-fragment generation;bootstrap.ps1 -Platform; a "Building for ARM64" section inBUILDING.md.Continuous Integration (
aabe8396)main): x64 onwindows-2022(VS 2022 + WiX → full solution, and packages the MSI as an artifact) and ARM64 onwindows-11-arm(VS 2022, no WiX → app/service/API + tests).msbuild /mon a warm cache; documented inBUILDING.md→ Continuous Integration.Native vcpkg host dependencies on ARM64 hosts (
503fc8ec)vcpkg.propspinnedVcpkgHostTriplettox64-windows-staticunconditionally, so ARM64 build hosts resolved host dependencies as x64 (emulated tooling). The pin is now host-aware:arm64-windows-staticon a Windows-on-Arm host,x64-windows-staticotherwise — the x64 fallback keeps cross-compilation from x64 hosts working (an x64 host can't run arm64 host tools; the reverse works under emulation).vcpkg.propsand would defeat the host-aware pin).vcpkg_installedCI cache key now includesvcpkg.props(the install stamp is named after the host triplet, so pre-change caches would re-run the install every warm run).Toolchain notes
BUILDING.md).Validation
On a Snapdragon device:
Release|ARM64solution + installer build.Template=Arm64;1033) installs to Program Files and runs.In CI (GitHub Actions):
windows-2022) and ARM64 (windows-11-arm) Release builds and unit tests both green; x64 additionally builds and uploads the MSI.Known follow-ups (not blockers)
...IntelXefg_GeneratedRowsRemain...) yields NaN vs 0 for a frame-gen row on ARM64 — a latent metrics-attribution issue surfaced by ARM64, not introduced here. It is skipped on the ARM64 CI leg pending a fix.PROCESS_DPI_UNAWAREupstream (arch-neutral).windows-2025,windows-11-vs2026-arm) is deferred to a follow-up: those images ship v145, and side-loading the pinned v143 toolset viavs_installerneeds more work.