Skip to content

Move Wisp to .NET 10 - #157

Draft
Views2k wants to merge 3 commits into
mainfrom
build/dotnet-10
Draft

Views2k wants to merge 3 commits into
mainfrom
build/dotnet-10

Conversation

@Views2k

@Views2k Views2k commented Oct 21, 2026 •

Copy link
Copy Markdown
Owner

Moves Wisp from .NET 8 to .NET 10 LTS ahead of the .NET 8 end of support on November 10, 2026. Tracks #59 and follows docs/DOTNET-10-MIGRATION.md. No release version is assigned here.

Versions

Before After
SDK (global.json, rollForward: latestPatch) 8.0.426 10.0.402
Bundled runtime (RuntimeFrameworkVersion, app and update helper) 8.0.31 10.0.13
Target frameworks net8.0, net8.0-windows net10.0, net10.0-windows
C# 12 14

10.0.13 / 10.0.402 is the October 13, 2026 security release, the current .NET 10 patch. 10.0.402 is the latest SDK feature band, as 8.0.426 was for .NET 8. .NET 10 is supported through November 14, 2028.

Windows support is unchanged: the .NET 10 supported-OS list for Windows is identical to .NET 8's, and the documented requirement stays Windows 10 or 11, 64-bit.

Changes

  • Every production, test, tool and UI-review project now targets .NET 10, and the app and update helper bundle the 10.0.13 runtime. x64 packaging, single-file publishing, update-helper trimming and the native renderer ABI are unchanged.
  • Lockfiles regenerated with an audited restore (--force-evaluate, NuGetAuditMode=all, no advisories) and verified with --locked-mode. Only two packages changed:
    • Microsoft.NET.ILLink.Tasks 8.0.31 → 10.0.13 (update-helper trimmer; it follows the runtime).
    • System.Security.Principal.Windows 5.0.0 removed from the five test projects. .NET 10 includes it in the framework, so NuGet now leaves it out.
  • Runtime notices replaced with the exact 10.0.13 package files (Microsoft.NETCore.App.Runtime.win-x64 and Microsoft.WindowsDesktop.App.Runtime.win-x64, both Microsoft author-signed and nuget.org repository-signed). Both license files are byte-identical to 8.0.31; only the runtime third-party notices changed (SHA-256 6D15E10A…8A21). Hashes, packaging references, .gitattributes, THIRD-PARTY-NOTICES.md, build paths and build guides are updated; historical release notes are untouched.
  • Focused fixes for the new toolchain:
    • C# 14 treats field as a keyword inside property accessors, so a lambda parameter in TuneSnapshot.IsComplete is renamed (CS9273).
    • The .NET 10 formatter indents a braced block after an embedded else foreach / if … lock differently and wants a blank line after a using before a file-scoped namespace. The five affected files are reformatted (whitespace only) so the full dotnet format --verify-no-changes check keeps passing.
    • The 10.0.13 notices file ends with a blank line, so it is excluded from whitespace checks like the other hash-verified upstream files.

.NET 9 and 10 compatibility review

Each item checked against the source:

  • Single-file native library search (.NET 10). The Wisp.NativeRenderer.dll P/Invokes use the default search path, which still includes the app directory. libmpv uses LoadLibraryEx with an explicit path; winsqlite3.dll is System32-only on purpose.
  • DllImportSearchPath.AssemblyDirectory no longer falls back (.NET 10). Not used.
  • WPF DynamicResource type checking (.NET 10). Code-set theme resources (AppParticleColor, OrbitGlowOpacity, thicknesses, radii) match their property types.
  • WPF empty Grid definitions (.NET 10). None; the XAML compiles.
  • BinaryFormatter removal (.NET 9). Not used. The clipboard is text-only.
  • System.Text.Json metadata-name conflicts (.NET 10). No polymorphic contracts.
  • Saturating float-to-integer conversions (.NET 9). The only floating uint conversion is clamped first. NaN or out-of-range int conversions now saturate; the full test suite covers the telemetry and rendering math.
  • C# 14 span overload resolution. No Reverse() or Contains calls are affected, and none run in interpreted expression trees. The build is clean with warnings as errors.
  • CET shadow stacks (.NET 9). Enabled by default for the app host. The native renderer, libmpv and LibVLC load in-process; this is covered by the native and clip checks below and the live checks.

Acceptance gates

Source commit for all local results: fd7260b (this PR's head). Local runs used SDK 10.0.402 (SHA-512 verified against Microsoft's release metadata, Microsoft-signed dotnet.exe), VS 2022 Build Tools 17.14 x64, Inno Setup 6.7.3 and Python 3.12.10 on a Windows 11 desktop with a hardware GPU, with Forza and Wisp closed.

Nightly workflow on this branch (fd7260b): green. Run 39605293346, dispatched manually on a GitHub Windows runner with SDK 10.0.402 / runtime 10.0.13:

  • locked audited restore and Release build;
  • full dotnet format --verify-no-changes;
  • Test-AppBehavior.ps1 -Full: Core 781/781, Telemetry 96/96, App 5,996/5,996 including WpfStyleRuntimeTests, Update 184/184, Updater 75/75, and the four isolated allocation tests;
  • tooling and packaging fixtures: 157 run, OK (2 skipped).

PR CI (fd7260b): green. "Build and test" ran in 6 min on SDK 10.0.402 / runtime 10.0.13: policy, locked audited restore, changed-file formatting, Release build, and the critical and affected tests (App 982, Core 777, Update 184, Telemetry 96, Updater 75). CodeQL passed.

  • Restore, formatting, tests, UI-review build, compatibility audit. The release gate (installer/Build-Installer.ps1 plus the full suite, native contracts and recorder contracts):
    • repository policy: pass.
    • audited --locked-mode restore (NuGetAuditMode=all): pass, no advisories.
    • full-solution dotnet format --verify-no-changes: pass. With SDK 8.0.426 the same source also passes, so the whitespace changes in this PR are what the 10.0.402 formatter requires.
    • Release build: 0 warnings, 0 errors (warnings are errors).
    • offline compatibility-audit and tool tests: 157 run, OK (2 skipped: optional Capstone not installed).
    • UI-review harness build: pass.
    • Test-AppBehavior.ps1 -Full:
      • Core 781, Telemetry 96, Update 184 and Updater 75: all pass.
      • Clean run (08:10–08:16): started after 30 s with no foreground change. WpfStyleRuntimeTests alone: pass. Then the full suite: Core 781/781, Telemetry 96/96, App 5,996/5,996, Update 184/184, Updater 75/75, and the four isolated allocation tests: all pass.
      • Earlier failures, all from desktop activity: five runs failed in WpfStyleRuntimeTests → HudNativeHostTests.AssertForeignOwnerRestart, which asserts that the desktop foreground window is unchanged:
        • two gate full-suite runs (5,995/5,996 each);
        • two runs of the class alone;
        • one alternating A/B run.
      • In each of them a 100 ms foreground sampler, or the test's own handles, recorded other desktop applications or the Start and Search shells taking the foreground during the test.
      • A/B evidence: the .NET 8 baseline (6921ce0, SDK 8.0.426) failed the same way during that activity. Both builds passed once the foreground settled, and the tests do not move the foreground themselves. This is a desktop-sensitive check on both runtimes, not a .NET 10 change.
  • Settings, profiles, Runs, imports, setup and update validation.
    • The full suite includes the Settings, HudPreset, SetupCompletion, RunStore, update and updater suites.
    • A current 2.7.0 data folder (8 HUD profiles, 76 tire calibrations, completed setup, 8 saved runs) was loaded from a copy by the app's own SettingsService and RunStore under .NET 8 and .NET 10 builds of the same source:
      • settings load "success" on both;
      • the loaded state is identical, and Save writes a file byte-identical to the original on both;
      • all 8 runs load with identical content digests;
      • runs exported on each runtime and imported on the other load identically.
    • Installer runtime validation (ConfiguredInstallerPassesRuntimeVerification, ConfiguredInstallerPassesRealRuntimeValidation) and the update-helper guard smoke pass.
  • Native renderer. Built unchanged with the x64 MSVC toolchain. Build-NativeRenderer.ps1 -RunContractTests on the hardware GPU: contractFailures=0, GPU default, CPU rendering supported, 120 Hz pacing, no resume timeouts. Recorder --self-test (the published binary, hash-matched) and all five recorder contract executables pass.
  • UI-review modes at their supported viewports and DPI, compared with the .NET 8 baseline (the same harness at 6921ce0 on SDK 8.0.426).
    • Ten modes ran on both builds: the default matrix, wizard, dashboard at 96 DPI and at 144 DPI (monitor and resizable), offline Appearance at 144 DPI, lap review, native lifetime, calm shell and scroll.
    • Eight pass on both. Their 142 captures are byte-identical between .NET 8 and .NET 10, with 0 binding diagnostics and 0 text-overflow findings.
    • Native lifetime: 6/6 stages pass.
    • --calm-shell-check (loaded/color-choice-count) and --scroll-check (InvalidOperationException in native-digital/initialize) fail identically on the .NET 8 build of main. These are existing harness issues outside CI, not caused by this change.
    • --dashboard-window-check and the --present modes were not run; they show activating windows.
    • The captures use WPF software rendering, so they cover layout, resources and bindings, not shader output.
  • Clean self-contained installer.
    • Wisp-Setup-2.7.0.exe (local, unsigned, version not bumped; SHA-256 ADB62037…CE0A) passed the installer build's executable version, architecture, identity, native, recorder, decoder and runtime-validation checks.
    • The payload carries the three 10.0.13 notices and no 8.0.31 notices.
    • The single-file Wisp.exe bundles runtime 10.0.13.
  • Installer lifecycle canary (fresh install, setup launch, in-place update, uninstall). CI runs it only from main or a tag, so it runs after merge, before release.
  • In-place update from public 2.7.0 preserving settings and completed setup: needs a live install over 2.7.0 (see live checks).
  • Live Steam and Xbox app checks (below).

Leftover runtime files from older layouts

The installer overwrites files and has no [InstallDelete], so an install that once held a loose (non-single-file) layout keeps those files after an update. Private diagnostic builds use that layout; public releases have been single-file since 1.0.1.

A throwaway single-file WPF executable named Wisp.exe was run in a folder seeded with 255 loose files from such an install. These included .NET 8 wpfgfx_cor3.dll, PresentationNative_cor3.dll, Wisp.dll, Wisp.deps.json and Wisp.runtimeconfig.json. Under .NET 10 (and .NET 8):

  • it ran on its bundled runtime;
  • it loaded only its own extracted WPF native libraries;
  • it loaded no managed assembly from disk;
  • it rendered identically to a clean folder.

No installer change is made here.

Live checks still needed (Steam and Xbox app, separately)

  1. Install a private build of this commit over the current 2.7.0 install. Confirm that settings, HUD profiles and placement, units, colors, enabled gauges, tire calibration and saved Runs are intact, and that setup is not shown again.
  2. Native GPU rendering in game: analogue and digital tach needle smoothness versus 2.7.0 at game fps with normal settings, gauges, overlay placement, and focus and alt-tab transitions.
  3. CPU rendering mode and the WPF fallback in game.
  4. Clip recording and playback (the recorder, the libmpv and LibVLC decoders, and export).
  5. The automatic update check and an update-helper apply.

Views2k and others added 3 commits October 21, 2026 07:08
Move every project to net10.0 or net10.0-windows, select SDK 10.0.402 in
global.json, and pin the self-contained app and update helper to runtime
10.0.13. Regenerate the lockfiles with an audited restore: the trimmer
package follows the runtime, and the framework now supplies
System.Security.Principal.Windows to the test projects.

Replace the bundled runtime notices with the 10.0.13 package files and
update their hashes, packaging references, build paths and build guides.
Rename a lambda parameter that C# 14 reads as the field keyword, and
apply the .NET 10 formatter's whitespace changes so the full format check
keeps passing.
The published notices file ends with a blank line and is hash-verified,
so treat it like the other upstream license files.
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.

1 participant