Skip to content

Add native ARM64 (Windows on Arm) support#666

Open
dennisameling wants to merge 7 commits into
GameTechDev:mainfrom
dennisameling:arm64-support
Open

Add native ARM64 (Windows on Arm) support#666
dennisameling wants to merge 7 commits into
GameTechDev:mainfrom
dennisameling:arm64-support

Conversation

@dennisameling

@dennisameling dennisameling commented Jul 16, 2026

Copy link
Copy Markdown

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.

Note: the branch is rebased onto upstream's flash-injection removal (de4b9c40, #655). An earlier revision of this PR also ported the FlashInjector overlay to ARM64 (N-arch injector model with native-vs-emulated process detection via GetProcessInformation(ProcessMachineTypeInfo)); that commit was dropped along with the feature.

I still have a backup branch which has FlashInjector included: https://github.com/dennisameling/PresentMon/tree/backup/arm64-support-pre-rebase

The app works end to end on ARM64:

Screenshot 2026-07-15 091546

I also tested the overlay in a x64 game (The Sims 4) on an ARM64 device and even that worked nicely:

Screenshot 2026-07-15 091055

Building the ARM64 MSI also works locally and installs correctly. These are the files that it installs:

Screenshot 2026-07-15 091613

What's included

Console app & core analysis (28dfbe15)

  • ARM64 configs for PresentData / PresentMon / CommonUtilities + ConsoleApplication.sln.

Service, SDK/API, provider & tooling (01a35e54)

  • ARM64 across the service, PresentMonAPI2, provider, and ancillary tools + PresentMon.sln wiring.
  • Repo overlay triplet triplets/arm64-windows-static.cmake pins the vcpkg toolset to v143 to match the app ABI.

Capture UI (CEF) & kernel process (e734227b)

  • Native ARM64 CefNano/AppCef + KernelProcess.
  • CEF batch pipeline made architecture-aware (-Platform on pull/upgrade/validate; per-arch lock cef-lock.arm64.json).

Test projects (ff101438)

  • ARM64 configs for UnitTests + PresentMonAPI2Tests.

Installer, packaging & docs (7c288d4c)

  • Architecture-aware WiX installer producing an arm64 MSI that packages the native ARM64 binaries.
  • v143 x64/x86 vcpkg triplet pins; robust WixTargetsPath; locale-independent CEF WiX-fragment generation; bootstrap.ps1 -Platform; a "Building for ARM64" section in BUILDING.md.

Continuous Integration (aabe8396)

  • GitHub Actions builds Release and runs the unit tests on both architectures for every PR (and pushes to main): x64 on windows-2022 (VS 2022 + WiX → full solution, and packages the MSI as an artifact) and ARM64 on windows-11-arm (VS 2022, no WiX → app/service/API + tests).
  • Layered caching (installed vcpkg trees / CEF payload / aux data / npm) with parallel msbuild /m on a warm cache; documented in BUILDING.mdContinuous Integration.

Native vcpkg host dependencies on ARM64 hosts (503fc8ec)

  • vcpkg.props pinned VcpkgHostTriplet to x64-windows-static unconditionally, so ARM64 build hosts resolved host dependencies as x64 (emulated tooling). The pin is now host-aware: arm64-windows-static on a Windows-on-Arm host, x64-windows-static otherwise — the x64 fallback keeps cross-compilation from x64 hosts working (an x64 host can't run arm64 host tools; the reverse works under emulation).
  • Removes ETLTrimmer's redundant per-config Vcpkg property groups (they restated the imported vcpkg.props and would defeat the host-aware pin).
  • The vcpkg_installed CI cache key now includes vcpkg.props (the install stamp is named after the host triplet, so pre-change caches would re-run the install every warm run).

Toolchain notes

  • All projects pin the v143 platform toolset. VS 2022 has it by default; VS 2026 (ARM64) needs the v143 build tools + ATL side-loaded (documented in BUILDING.md).
  • The vcpkg triplet pins are no-ops on VS 2022 (v143 is already the default) and fix the VS 2026 vectorized-STL link skew.
  • 32-bit ARM configs were intentionally not added.

Validation

On a Snapdragon device:

  • Full Release|ARM64 solution + installer build.
  • ETW capture: sane frame pacing, correct DXGI present-mode classification on Adreno WDDM, populated GPU timing.
  • Native ARM64 Capture UI launches and renders.
  • arm64 MSI (Template=Arm64;1033) installs to Program Files and runs.
  • UnitTests: 287/288 pass on-device.

In CI (GitHub Actions):

  • x64 (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)

  • One metrics unit test (...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.
  • HiDPI: the UI is deliberately PROCESS_DPI_UNAWARE upstream (arch-neutral).
  • CI covers the VS 2022 toolchain (x64 + ARM64). Adding VS 2026 legs (windows-2025, windows-11-vs2026-arm) is deferred to a follow-up: those images ship v145, and side-loading the pinned v143 toolset via vs_installer needs more work.

dennisameling and others added 7 commits July 16, 2026 08:27
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>
Comment thread .github/workflows/ci.yml
@@ -0,0 +1,274 @@
name: CI

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Comment on lines -56 to -59
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
<VcpkgUseStatic>true</VcpkgUseStatic>
<VcpkgHostTriplet>x64-windows-static</VcpkgHostTriplet>
</PropertyGroup>

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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' }

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could probably merge this into a single lockfile for both archs to make things easier; let me know what you prefer.

Comment thread BUILDING.md
Comment on lines +58 to +64
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)

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Comment thread BUILDING.md
Comment on lines +118 to +119
| `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) |

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Native ARM64 support

1 participant