Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 12 additions & 3 deletions osu.Android/AndroidNativeBridgeManager.cs
Original file line number Diff line number Diff line change
Expand Up @@ -54,16 +54,15 @@ public void StartOboeBridge(Scheduler scheduler, Action<double> onLatencyMeasure

onStarted?.Invoke(bridge.SampleRate);

scheduler.AddDelayed(() =>
scheduler.Add(new ScheduledDelegate(() =>
{
if (oboeBridge is not OboeAudioBridge b) return;

double latency = b.GetOutputLatencyMs();
Debug.WriteLine($"[osu!] Oboe measured latency after warm-up: {latency:F1}ms");

if (latency > 0)
onLatencyMeasured(latency);
}, 2000);
}, 2000, 5000));
}
else
{
Expand All @@ -87,6 +86,16 @@ public void StopOboeBridge()

[MethodImpl(MethodImplOptions.NoInlining)]
public static bool SetThreadAffinity(int coreMask) => OboeAudioBridge.nSetThreadAffinity(coreMask) != 0;

[MethodImpl(MethodImplOptions.NoInlining)]
public bool IsOboeActive() => (oboeBridge as OboeAudioBridge)?.IsActive ?? false;

[MethodImpl(MethodImplOptions.NoInlining)]
public string GetOboeStatus()
{
if (oboeBridge is not OboeAudioBridge bridge) return string.Empty;
return $"{(bridge.IsAAudio ? "AAudio" : "OpenSLES")} [{(bridge.IsMMap ? "MMAP" : "Legacy")}]";
}
public double GetMeasuredAudioLatencyMs()
{
return (oboeBridge as OboeAudioBridge)?.GetOutputLatencyMs() ?? -1;
Expand Down
4 changes: 0 additions & 4 deletions osu.Android/Native/OboeAudioBridge.cs
Original file line number Diff line number Diff line change
Expand Up @@ -338,9 +338,5 @@ public void Dispose()
[DllImport(lib_name)]
private static extern void nOboeSetProvider(IntPtr ptr, IntPtr provider);
[DllImport(lib_name)] internal static extern byte nSetThreadAffinity(int coreMask);
[DllImport(lib_name)] internal static extern IntPtr nADPFCreateSession(long targetDurationNanos);
[DllImport(lib_name)] internal static extern void nADPFReportActualDuration(IntPtr sessionPtr, long actualDurationNanos);
[DllImport(lib_name)] internal static extern void nADPFUpdateTargetDuration(IntPtr sessionPtr, long targetDurationNanos);
[DllImport(lib_name)] internal static extern void nADPFCloseSession(IntPtr sessionPtr);
}
}
2 changes: 1 addition & 1 deletion osu.Android/Native/VulkanProbe.cs
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ static VulkanProbe()
public bool SupportsGlobalPriority => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsGlobalPriority(nativePtr) != 0;
public bool SupportsMemoryBudget => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsMemoryBudget(nativePtr) != 0;

public bool IsRecommended => IsAvailable && MeetsVulkan13 && SupportsDynamicRendering && SupportsSynchronization2;
public bool IsRecommended => IsAvailable && MeetsVulkan13 && SupportsDynamicRendering && SupportsSynchronization2 && SupportsGraphicsPipelineLibrary && SupportsShaderObject;

public void Dispose()
{
Expand Down
34 changes: 1 addition & 33 deletions osu.Android/Native/oboe_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -172,7 +172,6 @@ void OboeBridge::setProvider(OboeAudioProvider provider) {
oboe::DataCallbackResult OboeBridge::onAudioReady(
oboe::AudioStream* stream, void* audioData, int32_t numFrames) {


OboeAudioProvider provider = provider_.load(std::memory_order_acquire);

if (provider) {
Expand All @@ -190,7 +189,6 @@ oboe::DataCallbackResult OboeBridge::onAudioReady(
memset(audioData, 0, byteCount);
}


uint32_t count = callbackCount_.fetch_add(1, std::memory_order_relaxed);

if ((count & 127) == 0) {
Expand Down Expand Up @@ -386,34 +384,4 @@ OSU_EXPORT byte nSetThreadAffinity(int coreMask) {
}
return (sched_setaffinity(0, sizeof(cpu_set_t), &cpuset) == 0) ? 1 : 0;
}
}

#include <android/performance_hint.h>

extern "C" {
OSU_EXPORT intptr_t nADPFCreateSession(int64_t targetDurationNanos) {
auto manager = APerformanceHint_getManager();
if (!manager) return 0;

int32_t thread_id = gettid();
return reinterpret_cast<intptr_t>(APerformanceHint_createSession(manager, &thread_id, 1, targetDurationNanos));
}

OSU_EXPORT void nADPFReportActualDuration(intptr_t sessionPtr, int64_t actualDurationNanos) {
if (sessionPtr) {
APerformanceHint_reportActualWorkDuration(reinterpret_cast<APerformanceHintSession*>(sessionPtr), actualDurationNanos);
}
}

OSU_EXPORT void nADPFUpdateTargetDuration(intptr_t sessionPtr, int64_t targetDurationNanos) {
if (sessionPtr) {
APerformanceHint_updateTargetWorkDuration(reinterpret_cast<APerformanceHintSession*>(sessionPtr), targetDurationNanos);
}
}

OSU_EXPORT void nADPFCloseSession(intptr_t sessionPtr) {
if (sessionPtr) {
APerformanceHint_closeSession(reinterpret_cast<APerformanceHintSession*>(sessionPtr));
}
}
}
}
3 changes: 1 addition & 2 deletions osu.Android/Native/vulkan_bridge.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -130,13 +130,12 @@ void VulkanProbe::queryQueueFamilies(VkPhysicalDevice device) {
}

void VulkanProbe::queryMailboxSupport(VkPhysicalDevice device) {
// mailbox detection via display_timing hint
uint32_t count = 0;
vkEnumerateDeviceExtensionProperties(device, nullptr, &count, nullptr);
std::vector<VkExtensionProperties> exts(count);
vkEnumerateDeviceExtensionProperties(device, nullptr, &count, exts.data());
for (const auto& ext : exts) {
if (strcmp(ext.extensionName, "VK_GOOGLE_display_timing") == 0) {
if (strcmp(ext.extensionName, VK_KHR_SWAPCHAIN_EXTENSION_NAME) == 0) {
deviceInfo_.supportsMailboxPresentMode = true;
break;
}
Expand Down
45 changes: 9 additions & 36 deletions osu.Android/OsuGameAndroid.cs
Original file line number Diff line number Diff line change
Expand Up @@ -86,8 +86,6 @@ public partial class OsuGameAndroid : OsuGame
private OboeAudioRedirector? audioRedirector;
private Delegate? activeMixersHandler;
private object? activeMixersList;
private IntPtr updateAdpfSession;
private IntPtr renderAdpfSession;

/// <summary>
/// Boxed reference to the native bridge manager.
Expand Down Expand Up @@ -211,24 +209,22 @@ protected override void LoadComplete()
try
{
// Target 1ms (1,000,000ns) for 1000 FPS target.
updateAdpfSession = OboeAudioBridge.nADPFCreateSession(1000000);


Scheduler.Add(() =>
{
Host.DrawThread.Scheduler.Add(() =>
{
try
{
renderAdpfSession = OboeAudioBridge.nADPFCreateSession(1000000);

if (renderAdpfSession != IntPtr.Zero)

Debug.WriteLine("[osu!] ADPF Performance Hint Session created for Render thread");
}
catch { }
});
});

if (updateAdpfSession != IntPtr.Zero)
Debug.WriteLine("[osu!] ADPF Performance Hint Session created for Update thread");
}
catch { }
Expand Down Expand Up @@ -267,9 +263,6 @@ protected override void LoadComplete()
{
startOboeBridge(latency =>
{
if (Math.Abs(audioOffset.Value) >= 0.01)
return;

double suggested = Math.Clamp(-latency, audioOffset.MinValue, audioOffset.MaxValue);
audioOffset.Value = suggested;
Debug.WriteLine($"[osu!] Audio offset auto-suggested: {suggested:F1}ms (hardware latency={latency:F1}ms)");
Expand Down Expand Up @@ -404,6 +397,12 @@ private void selectHighestRefreshRate()

public override bool IsVulkanSupported => (nativeBridges as AndroidNativeBridgeManager)?.IsVulkanAvailable() ?? false;

public override bool IsOboeActive => (nativeBridges as AndroidNativeBridgeManager)?.IsOboeActive() ?? false;

public override string OboeStatus => (nativeBridges as AndroidNativeBridgeManager)?.GetOboeStatus() ?? string.Empty;

public override double OboeLatency => (nativeBridges as AndroidNativeBridgeManager)?.GetMeasuredAudioLatencyMs() ?? -1;

private void onActiveMixersChanged(object? sender, NotifyCollectionChangedEventArgs args) => Schedule(() => { if (lowLatencyAudio.Value) audioRedirector?.RefreshMixers(0); });

public double GetMeasuredAudioLatencyMs() => getMeasuredAudioLatencyFromBridge();
Expand Down Expand Up @@ -543,37 +542,11 @@ protected override void Dispose(bool isDisposing)

if (nativeBridges != null)
disposeNativeBridges();

if (updateAdpfSession != IntPtr.Zero)
{
OboeAudioBridge.nADPFCloseSession(updateAdpfSession);
updateAdpfSession = IntPtr.Zero;
}

if (renderAdpfSession != IntPtr.Zero)
{
OboeAudioBridge.nADPFCloseSession(renderAdpfSession);
renderAdpfSession = IntPtr.Zero;
}
}
}

[MethodImpl(MethodImplOptions.AggressiveOptimization)]
protected override void UpdateAfterChildren()
{
if (updateAdpfSession == IntPtr.Zero)
{
base.UpdateAfterChildren();
return;
}

long startTime = Stopwatch.GetTimestamp();
base.UpdateAfterChildren();
long elapsedTicks = Stopwatch.GetTimestamp() - startTime;
long elapsedNanos = (elapsedTicks * 1000000000) / Stopwatch.Frequency;

OboeAudioBridge.nADPFReportActualDuration(updateAdpfSession, elapsedNanos);
}
protected override void UpdateAfterChildren() => base.UpdateAfterChildren();

}

Expand Down
15 changes: 15 additions & 0 deletions osu.Game.OsuGameBase.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
--- a/osu.Game/OsuGameBase.cs
+++ b/osu.Game/OsuGameBase.cs
@@ -122,6 +122,12 @@

public virtual bool IsVulkanSupported => false;

+ public virtual bool IsOboeActive => false;
+
+ public virtual string OboeStatus => string.Empty;
+
+ public virtual double OboeLatency => -1;
+
public virtual string Version
{
get
18 changes: 17 additions & 1 deletion osu.Game/Graphics/UserInterface/FPSCounter.cs
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,12 @@ public partial class FPSCounter : VisibilityContainer, IHasCustomTooltip
[Resolved]
private OsuColour colours { get; set; } = null!;

[Resolved]
protected OsuGame Game { get; private set; } = null!;

[Resolved]
private GameHost host { get; set; } = null!;

public FPSCounter()
{
AutoSizeAxes = Axes.Both;
Expand Down Expand Up @@ -232,7 +238,17 @@ private void requestDisplay()
private void updateFpsDisplay()
{
counterDrawFPS.Colour = getColour(displayedFpsCount / aimDrawFPS);
counterDrawFPS.Text = $"{displayedFpsCount:#,0} fps";
string status = $"{displayedFpsCount:#,0} fps";

if (Game is OsuGame osu)
{
status += $" | {host.ResolvedRenderer.ToString()}";

if (osu.IsOboeActive)
status += $" | Oboe: {osu.OboeStatus} ({osu.OboeLatency:F1}ms)";
}

counterDrawFPS.Text = status;
}

private void updateFrameTimeDisplay()
Expand Down
6 changes: 6 additions & 0 deletions osu.Game/OsuGameBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@ public virtual EndpointConfiguration CreateEndpoints() =>

public virtual bool IsVulkanSupported => false;

public virtual bool IsOboeActive => false;

public virtual string OboeStatus => string.Empty;

public virtual double OboeLatency => -1;

public virtual string Version
{
get
Expand Down
Loading
Loading