Skip to content

Commit 223ce47

Browse files
bump: framework 2026.504.1; wire GameThread.FrameCompleted for ADPF per-frame reporting
Agent-Logs-Url: https://github.com/winnerspiros/osu/sessions/e803b44b-0f6a-4b3c-8e9d-0174b32842a1 Co-authored-by: winnerspiros <1675249+winnerspiros@users.noreply.github.com>
1 parent 5f6b452 commit 223ce47

4 files changed

Lines changed: 61 additions & 4 deletions

File tree

osu.Android.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -99,7 +99,7 @@
9999
</PropertyGroup>
100100

101101
<ItemGroup>
102-
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.503.8" />
102+
<PackageReference Include="ppy.osu.Framework.Android" Version="2026.504.1" />
103103
<!-- `ppy.osu.Framework.NativeLibs` is a transitive dependency of `ppy.osu.Framework`
104104
that ships desktop-only natives (Linux/macOS/Windows) under `runtimes/<rid>/native/`
105105
— including a bare Linux `libbass.so`/`libbass_fx.so`/`libbassmix.so` for linux-arm64.

osu.Android/OsuGameAndroid.cs

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ public partial class OsuGameAndroid : OsuGame
170170
// their work within the display frame deadline (e.g. 8.33 ms at 120 Hz).
171171
// The sessions are created once after LoadComplete (when thread IDs are stable) and closed on
172172
// Dispose. Target duration is updated whenever the active display refresh rate changes.
173+
// Per-frame actual-duration reporting is done via GameThread.FrameCompleted, which fires on
174+
// the respective game thread at the end of every frame (after clock throttle), giving the
175+
// CPU governor a signal to pre-boost the clock for the next frame.
173176
private IntPtr adpfDrawSession;
174177
private IntPtr adpfUpdateSession;
175178

@@ -547,7 +550,13 @@ protected override void LoadComplete()
547550
long targetNs = currentRefreshRate > 0 ? 1_000_000_000L / currentRefreshRate : 8_333_333L;
548551
adpfDrawSession = OboeAudioBridge.nADPFCreateSession(targetNs);
549552
if (adpfDrawSession != IntPtr.Zero)
553+
{
550554
Logger.Log($"[osu!] ADPF session created for Draw thread (target={targetNs / 1_000_000.0:F2}ms)", LoggingTarget.Performance);
555+
// Subscribe per-frame reporting now that the session handle is valid.
556+
// FrameCompleted fires on the Draw thread itself, so reading
557+
// Host.DrawThread.Clock.ElapsedFrameTime is thread-safe.
558+
Host!.DrawThread!.FrameCompleted += onDrawFrameCompleted;
559+
}
551560
}
552561
catch { }
553562
});
@@ -559,7 +568,10 @@ protected override void LoadComplete()
559568
long targetNs = currentRefreshRate > 0 ? 1_000_000_000L / currentRefreshRate : 8_333_333L;
560569
adpfUpdateSession = OboeAudioBridge.nADPFCreateSession(targetNs);
561570
if (adpfUpdateSession != IntPtr.Zero)
571+
{
562572
Logger.Log($"[osu!] ADPF session created for Update thread (target={targetNs / 1_000_000.0:F2}ms)", LoggingTarget.Performance);
573+
Host!.UpdateThread!.FrameCompleted += onUpdateFrameCompleted;
574+
}
563575
}
564576
catch { }
565577
});
@@ -1464,6 +1476,44 @@ private void updateAdpfTargetDuration(long targetNs)
14641476
catch { }
14651477
}
14661478

1479+
/// <summary>
1480+
/// Called by <see cref="osu.Framework.Threading.GameThread.FrameCompleted"/> on the Draw thread.
1481+
/// Reports the actual frame duration to the ADPF session so the CPU governor can adjust clock
1482+
/// frequency for the next frame. <see cref="osu.Framework.Threading.GameThread.Clock"/>
1483+
/// <c>ElapsedFrameTime</c> is in milliseconds; we convert to nanoseconds for the ADPF API.
1484+
/// In <see cref="FrameSync.ActualUnlimited"/> mode there is no throttle sleep, so
1485+
/// ElapsedFrameTime accurately reflects actual GPU+CPU work time.
1486+
/// </summary>
1487+
private void onDrawFrameCompleted()
1488+
{
1489+
if (adpfDrawSession == IntPtr.Zero) return;
1490+
1491+
try
1492+
{
1493+
double elapsedMs = Host?.DrawThread?.Clock.ElapsedFrameTime ?? 0;
1494+
if (elapsedMs > 0)
1495+
OboeAudioBridge.nADPFReportActualDuration(adpfDrawSession, (long)(elapsedMs * 1_000_000.0));
1496+
}
1497+
catch { }
1498+
}
1499+
1500+
/// <summary>
1501+
/// Called by <see cref="osu.Framework.Threading.GameThread.FrameCompleted"/> on the Update thread.
1502+
/// See <see cref="onDrawFrameCompleted"/> for rationale.
1503+
/// </summary>
1504+
private void onUpdateFrameCompleted()
1505+
{
1506+
if (adpfUpdateSession == IntPtr.Zero) return;
1507+
1508+
try
1509+
{
1510+
double elapsedMs = Host?.UpdateThread?.Clock.ElapsedFrameTime ?? 0;
1511+
if (elapsedMs > 0)
1512+
OboeAudioBridge.nADPFReportActualDuration(adpfUpdateSession, (long)(elapsedMs * 1_000_000.0));
1513+
}
1514+
catch { }
1515+
}
1516+
14671517
private global::Android.Views.Display? getActiveDisplay()
14681518
{
14691519
if (gameActivity.IsFinishing || gameActivity.IsDestroyed)
@@ -2441,8 +2491,15 @@ protected override void Dispose(bool isDisposing)
24412491
dexPerformanceSession = null;
24422492

24432493
// Close ADPF hint sessions for game threads.
2494+
// Unsubscribe FrameCompleted FIRST so the callbacks don't fire with a stale
2495+
// (already-closed) session handle during the final frames of teardown.
24442496
try
24452497
{
2498+
if (Host?.DrawThread != null)
2499+
Host.DrawThread.FrameCompleted -= onDrawFrameCompleted;
2500+
if (Host?.UpdateThread != null)
2501+
Host.UpdateThread.FrameCompleted -= onUpdateFrameCompleted;
2502+
24462503
if (adpfDrawSession != IntPtr.Zero)
24472504
{
24482505
OboeAudioBridge.nADPFCloseSession(adpfDrawSession);

osu.Game/osu.Game.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -38,10 +38,10 @@
3838
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
3939
</PackageReference>
4040
<PackageReference Include="Realm" Version="20.1.0" />
41-
<PackageReference Include="ppy.osu.Framework" Version="2026.503.8" />
41+
<PackageReference Include="ppy.osu.Framework" Version="2026.504.1" />
4242
<!--
4343
Explicitly pin `ppy.Veldrid.SPIRV` to the winnerspiros fork build that
44-
`ppy.osu.Framework 2026.503.8` was compiled against. This version is the only
44+
`ppy.osu.Framework 2026.504.1` was compiled against. This version is the only
4545
one whose `runtimes/android-arm64/native/libveldrid-spirv.so` is aligned to 16 KB
4646
pages (required by Android 16+). It lives only as a release asset on
4747
<https://github.com/winnerspiros/veldrid-spirv/releases/tag/1.0> and is vendored

osu.iOS.props

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,6 @@
3333
<SuppressTrimAnalysisWarnings>true</SuppressTrimAnalysisWarnings>
3434
</PropertyGroup>
3535
<ItemGroup>
36-
<PackageReference Include="ppy.osu.Framework.iOS" Version="2026.503.8" />
36+
<PackageReference Include="ppy.osu.Framework.iOS" Version="2026.504.1" />
3737
</ItemGroup>
3838
</Project>

0 commit comments

Comments
 (0)