|
25 | 25 | </PropertyGroup> |
26 | 26 |
|
27 | 27 | <!-- Release-only optimisations. |
28 | | - Trimming and AOT were previously enabled here for size/startup, but caused a |
29 | | - reproducible "splash → black screen → crash, no logs" failure on real devices. |
30 | | - Root cause: PublishTrimmed + TrimMode=partial + AndroidEnableProfiledAot, with |
31 | | - SuppressTrimAnalysisWarnings=true masking the build-time signal. Only Microsoft.CSharp |
32 | | - and ppy.Veldrid.SPIRV were rooted — osu.Game, the rulesets, Realm, Newtonsoft.Json, |
33 | | - AutoMapper, Sentry, OsuTK and other reflection-heavy assemblies were not. A trimmed |
34 | | - method/type or an un-AOT'd profiled-AOT call site throws TypeLoadException / |
35 | | - MissingMethodException early in OsuGame construction, before the file logger is open; |
36 | | - the process dies on the SDLThread / .NET ThreadPool thread with only a tombstone, so |
37 | | - the user gets no osu.log and Logcat shows a bare native crash. |
38 | | - Upstream ppy/osu's osu.Android.props enables NEITHER trimming NOR AOT — we now match |
39 | | - that baseline. The remaining release-only knobs (assembly compression, no PDBs) are |
40 | | - safe and unrelated to the startup crash. To re-enable trimming/AOT in the future, |
41 | | - reintroduce them one at a time, root every reflection-using assembly explicitly, and |
42 | | - run with SuppressTrimAnalysisWarnings=false to surface the gaps before shipping. --> |
| 28 | + History: |
| 29 | + Trimming + AOT were once enabled together (PublishTrimmed + TrimMode=partial + |
| 30 | + AndroidEnableProfiledAot, with SuppressTrimAnalysisWarnings=true masking the |
| 31 | + build-time signal). That combination caused a reproducible "splash → black screen |
| 32 | + → crash, no logs" failure: only Microsoft.CSharp and ppy.Veldrid.SPIRV were rooted, |
| 33 | + while osu.Game, the rulesets, Realm, Newtonsoft.Json, AutoMapper, Sentry, OsuTK |
| 34 | + and other reflection-heavy assemblies were not. A trimmed method/type or an |
| 35 | + un-AOT'd profiled-AOT call site threw TypeLoadException / MissingMethodException |
| 36 | + early in OsuGame construction, before the file logger was open; the process died |
| 37 | + on SDLThread / .NET ThreadPool with only a tombstone, so the user got no osu.log |
| 38 | + and Logcat showed a bare native crash. |
| 39 | +
|
| 40 | + Current policy: |
| 41 | + * AndroidEnableProfiledAot=true — re-enabled. The crash root cause was the |
| 42 | + trimming side of the combination (untrimmed-but-AOT'd is safe — every method |
| 43 | + the profile lists exists in the assembly because nothing was stripped; methods |
| 44 | + outside the profile fall back to the JIT and are also still present). This |
| 45 | + gives a measurable startup time reduction and cuts steady-state CPU on hot |
| 46 | + managed paths (the JIT no longer needs to tier them up at runtime, and Mono's |
| 47 | + profiled AOT covers framework/game hot loops including draw-thread Schedulers, |
| 48 | + bindable propagation, and the texture upload path). |
| 49 | + * PublishTrimmed stays OFF — re-enabling it requires explicitly rooting every |
| 50 | + reflection-using assembly (osu.Game, rulesets, Realm, Newtonsoft.Json, |
| 51 | + AutoMapper, Sentry, OsuTK, Microsoft.CSharp, ppy.Veldrid.SPIRV …) and running |
| 52 | + with SuppressTrimAnalysisWarnings=false to surface gaps before shipping. That |
| 53 | + is a separate, larger PR. |
| 54 | + * AndroidEnableMarshalMethods stays OFF (see PropertyGroup above) — silent |
| 55 | + SIGSEGV with current interop on .NET 10 Android. |
| 56 | + * Server GC is enabled for Release: workstation GC's STW pauses on the Update/ |
| 57 | + Draw threads are a primary source of >1ms frame spikes that defeat our 1ms |
| 58 | + frame-time goal. Server GC parallelises mark/sweep across cores and uses |
| 59 | + per-core LOH/SOH allocation contexts, dramatically lowering pause time at |
| 60 | + the cost of ~20-30% extra resident memory. Mono Android has supported this |
| 61 | + since .NET 8; ppy/osu desktop already uses it. Concurrent GC is the .NET |
| 62 | + default but we set it explicitly so Server GC runs in the background mode |
| 63 | + rather than blocking foreground. |
| 64 | +
|
| 65 | + To re-enable trimming in the future, reintroduce it on its own, root every |
| 66 | + reflection-using assembly explicitly, and run with SuppressTrimAnalysisWarnings=false |
| 67 | + to surface the gaps before shipping. --> |
43 | 68 | <PropertyGroup Condition="'$(Configuration)' == 'Release'"> |
44 | 69 | <!-- Compress managed assemblies inside the APK (LZ4). Android extracts them on first run |
45 | 70 | but the download/APK size is significantly smaller. --> |
|
48 | 73 | Stack traces still work via embedded metadata. --> |
49 | 74 | <DebugType>none</DebugType> |
50 | 75 | <DebugSymbols>false</DebugSymbols> |
| 76 | + <!-- Profiled AOT: pre-compile the hot methods listed in the bundled .NET / Android |
| 77 | + profiles. Methods outside the profile remain JIT-compiled at runtime. NO trimming — |
| 78 | + every method in every assembly stays present, so a profile-miss falls back to JIT |
| 79 | + instead of throwing MissingMethodException like the previous trim+AOT combo did. --> |
| 80 | + <AndroidEnableProfiledAot>true</AndroidEnableProfiledAot> |
| 81 | + <!-- Concurrent Server GC. Server GC uses per-core allocation contexts and parallel |
| 82 | + collections to reduce STW pause time, which is the largest source of >1ms frame |
| 83 | + spikes on the Update/Draw threads. Memory usage rises ~20-30% in exchange. --> |
| 84 | + <ServerGarbageCollection>true</ServerGarbageCollection> |
| 85 | + <ConcurrentGarbageCollection>true</ConcurrentGarbageCollection> |
51 | 86 | </PropertyGroup> |
52 | 87 |
|
53 | 88 | <ItemGroup> |
|
0 commit comments