Skip to content

Commit ed78918

Browse files
authored
Merge pull request #140 from winnerspiros/fix/android-vulkan-adreno-performance-flicker-13806510936200067795
Resolve Vulkan performance gap and flickering on Adreno 7xx GPUs
2 parents 446f045 + 821e066 commit ed78918

7 files changed

Lines changed: 79 additions & 13 deletions

File tree

osu.Android/AndroidNativeBridgeManager.cs

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,13 @@ public void StartVulkanProbe()
134134

135135
public bool IsVulkanAvailable() => (vulkanProbe as VulkanProbe)?.IsAvailable ?? (vulkanProbe != null);
136136

137+
[MethodImpl(MethodImplOptions.NoInlining)]
138+
public string GetVulkanStatus()
139+
{
140+
if (vulkanProbe is not VulkanProbe probe) return string.Empty;
141+
return $"{(probe.SupportsMailboxPresentMode ? "MAILBOX" : "FIFO")}{(probe.DisablePresentId ? " [NoID]" : "")}{(probe.DisablePresentWait ? " [NoWait]" : "")}{(probe.DisableGraphicsPipelineLibrary ? " [NoGPL]" : "")}";
142+
}
143+
137144
public void StopVulkanProbe()
138145
{
139146
(vulkanProbe as VulkanProbe)?.Dispose();

osu.Android/Native/VulkanProbe.cs

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,6 +67,11 @@ static VulkanProbe()
6767
public bool SupportsShaderObject => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsShaderObject(nativePtr) != 0;
6868
public bool SupportsGlobalPriority => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsGlobalPriority(nativePtr) != 0;
6969
public bool SupportsMemoryBudget => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsMemoryBudget(nativePtr) != 0;
70+
public bool SupportsSurfaceMaintenance1 => !disposed && nativePtr != IntPtr.Zero && nVulkanSupportsSurfaceMaintenance1(nativePtr) != 0;
71+
72+
public bool DisablePresentId => !disposed && nativePtr != IntPtr.Zero && nVulkanDisablePresentId(nativePtr) != 0;
73+
public bool DisablePresentWait => !disposed && nativePtr != IntPtr.Zero && nVulkanDisablePresentWait(nativePtr) != 0;
74+
public bool DisableGraphicsPipelineLibrary => !disposed && nativePtr != IntPtr.Zero && nVulkanDisableGraphicsPipelineLibrary(nativePtr) != 0;
7075

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

@@ -99,5 +104,9 @@ public void Dispose()
99104
[DllImport(lib_name)] private static extern byte nVulkanSupportsShaderObject(IntPtr ptr);
100105
[DllImport(lib_name)] private static extern byte nVulkanSupportsGlobalPriority(IntPtr ptr);
101106
[DllImport(lib_name)] private static extern byte nVulkanSupportsMemoryBudget(IntPtr ptr);
107+
[DllImport(lib_name)] private static extern byte nVulkanSupportsSurfaceMaintenance1(IntPtr ptr);
108+
[DllImport(lib_name)] private static extern byte nVulkanDisablePresentId(IntPtr ptr);
109+
[DllImport(lib_name)] private static extern byte nVulkanDisablePresentWait(IntPtr ptr);
110+
[DllImport(lib_name)] private static extern byte nVulkanDisableGraphicsPipelineLibrary(IntPtr ptr);
102111
}
103112
}

osu.Android/Native/vulkan_bridge.cpp

Lines changed: 25 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -2,25 +2,32 @@
22
// See the LICENCE file in the repository root for full licence text.
33

44
#include "vulkan_bridge.h"
5+
#include <android/log.h>
56
#include <vector>
67
#include <cstring>
7-
#include <android/log.h>
8+
#include <new>
89

9-
#define LOG_TAG "osu!native-vulkan"
10+
#define LOG_TAG "osu_native"
1011
#define LOGI(...) __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__)
12+
#define LOGE(...) __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__)
1113

1214
VulkanProbe::VulkanProbe() {
13-
if (createInstance()) {
14-
available_ = queryDevice();
15+
if (!createInstance()) {
16+
LOGE("Failed to create Vulkan instance for probing");
17+
return;
1518
}
1619

17-
if (!available_) {
20+
if (!queryDevice()) {
21+
LOGE("Failed to query Vulkan physical device info");
1822
cleanup();
23+
return;
1924
}
2025

26+
available_ = true;
27+
2128
if (available_) {
2229
LOGI("Vulkan available: %s (Vendor: 0x%x, API %u.%u.%u, driver %u, VRAM %u MB, "
23-
"queues %u, mailbox=%d, vk1.3=%d, sync2=%d, presentWait=%d, gpl=%d, shaderObj=%d, priority=%d)",
30+
"qCount %u, mailbox %d, vk1.3 %d, sync2 %d, pWait %d, gpl %d, sObj %d, gPrio %d)",
2431
deviceInfo_.deviceName.c_str(),
2532
deviceInfo_.vendorId,
2633
VK_VERSION_MAJOR(deviceInfo_.apiVersion),
@@ -155,14 +162,16 @@ void VulkanProbe::queryModernExtensions(VkPhysicalDevice device) {
155162
if (strcmp(ext.extensionName, VK_EXT_SHADER_OBJECT_EXTENSION_NAME) == 0) deviceInfo_.supportsShaderObject = true;
156163
if (strcmp(ext.extensionName, VK_EXT_GLOBAL_PRIORITY_EXTENSION_NAME) == 0 || strcmp(ext.extensionName, VK_KHR_GLOBAL_PRIORITY_EXTENSION_NAME) == 0) deviceInfo_.supportsGlobalPriority = true;
157164
if (strcmp(ext.extensionName, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME) == 0) deviceInfo_.supportsMemoryBudget = true;
165+
if (strcmp(ext.extensionName, "VK_EXT_surface_maintenance1") == 0) deviceInfo_.supportsSurfaceMaintenance1 = true;
158166
}
159167

160-
// Adreno 740 (S23 Ultra) known flickering issues with present_id/wait extensions.
161-
// Adreno vendor ID is 0x5143 (Qualcomm).
162-
if (deviceInfo_.vendorId == 0x5143 && (deviceInfo_.deviceName.find("740") != std::string::npos || deviceInfo_.deviceName.find("Adreno") != std::string::npos)) {
163-
LOGI("Adreno GPU detected: disabling present_id and present_wait to prevent flickering/glitching");
164-
deviceInfo_.supportsPresentId = false;
165-
deviceInfo_.supportsPresentWait = false;
168+
if (deviceInfo_.vendorId == 0x5143 && (deviceInfo_.deviceName.find("740") != std::string::npos ||
169+
deviceInfo_.deviceName.find("750") != std::string::npos ||
170+
deviceInfo_.deviceName.find("Adreno") != std::string::npos)) {
171+
LOGI("Adreno 7xx GPU detected: applying aggressive performance and flickering overrides");
172+
deviceInfo_.disablePresentId = true;
173+
deviceInfo_.disablePresentWait = true;
174+
deviceInfo_.disableGraphicsPipelineLibrary = true;
166175
}
167176
}
168177

@@ -208,4 +217,8 @@ OSU_EXPORT byte nVulkanSupportsGraphicsPipelineLibrary(intptr_t ptr) { return (p
208217
OSU_EXPORT byte nVulkanSupportsShaderObject(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().supportsShaderObject) ? 1 : 0; }
209218
OSU_EXPORT byte nVulkanSupportsGlobalPriority(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().supportsGlobalPriority) ? 1 : 0; }
210219
OSU_EXPORT byte nVulkanSupportsMemoryBudget(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().supportsMemoryBudget) ? 1 : 0; }
220+
OSU_EXPORT byte nVulkanSupportsSurfaceMaintenance1(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().supportsSurfaceMaintenance1) ? 1 : 0; }
221+
OSU_EXPORT byte nVulkanDisablePresentId(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().disablePresentId) ? 1 : 0; }
222+
OSU_EXPORT byte nVulkanDisablePresentWait(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().disablePresentWait) ? 1 : 0; }
223+
OSU_EXPORT byte nVulkanDisableGraphicsPipelineLibrary(intptr_t ptr) { return (ptr && reinterpret_cast<VulkanProbe*>(ptr)->getDeviceInfo().disableGraphicsPipelineLibrary) ? 1 : 0; }
211224
}

osu.Android/Native/vulkan_bridge.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,12 @@ class VulkanProbe {
3636
bool supportsShaderObject = false;
3737
bool supportsGlobalPriority = false;
3838
bool supportsMemoryBudget = false;
39+
bool supportsSurfaceMaintenance1 = false;
40+
41+
// Quirks / Blacklist flags
42+
bool disablePresentId = false;
43+
bool disablePresentWait = false;
44+
bool disableGraphicsPipelineLibrary = false;
3945
};
4046

4147
VulkanProbe();

osu.Android/OsuGameAndroid.cs

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -178,7 +178,7 @@ private void load()
178178
[MethodImpl(MethodImplOptions.AggressiveOptimization)]
179179
protected override void LoadComplete()
180180
{
181-
// Pin the current thread (Update thread) to high-performance cores.
181+
// Pin the current thread (Update thread) to high-performance cores.
182182
// On S23 Ultra, cores 3-7 are high-performance. Mask = 0xF8 (11111000 in binary)
183183
try
184184
{
@@ -396,6 +396,10 @@ private void selectHighestRefreshRate()
396396

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

399+
public override string VulkanStatus => (nativeBridges as AndroidNativeBridgeManager)?.GetVulkanStatus() ?? string.Empty;
400+
401+
402+
399403
public override bool IsOboeActive => (nativeBridges as AndroidNativeBridgeManager)?.IsOboeActive() ?? false;
400404

401405
public override string OboeStatus => (nativeBridges as AndroidNativeBridgeManager)?.GetOboeStatus() ?? string.Empty;
@@ -506,6 +510,28 @@ private void updateOrientation()
506510

507511
public override void SetHost(GameHost host)
508512
{
513+
// Apply Vulkan environment overrides before the graphics device is initialized.
514+
if (nativeBridges is AndroidNativeBridgeManager mgr && mgr.IsVulkanAvailable())
515+
{
516+
try
517+
{
518+
string status = mgr.GetVulkanStatus();
519+
if (status.Contains("MAILBOX"))
520+
System.Environment.SetEnvironmentVariable("VULKAN_PRESENT_MODE", "MAILBOX");
521+
522+
var disabledExtensions = new System.Collections.Generic.List<string>();
523+
if (status.Contains("NoID")) disabledExtensions.Add("VK_KHR_present_id");
524+
if (status.Contains("NoWait")) disabledExtensions.Add("VK_KHR_present_wait");
525+
if (status.Contains("NoGPL")) disabledExtensions.Add("VK_EXT_graphics_pipeline_library");
526+
527+
if (disabledExtensions.Count > 0)
528+
System.Environment.SetEnvironmentVariable("VULKAN_DISABLE_EXTENSIONS", string.Join(",", disabledExtensions));
529+
530+
Debug.WriteLine($"[osu!] Vulkan overrides applied: MODE={System.Environment.GetEnvironmentVariable("VULKAN_PRESENT_MODE")}, DISABLE={System.Environment.GetEnvironmentVariable("VULKAN_DISABLE_EXTENSIONS")}");
531+
}
532+
catch (Exception e) { Debug.WriteLine($"[osu!] Failed to set Vulkan overrides: {e.Message}"); }
533+
}
534+
509535
base.SetHost(host);
510536

511537
if (host.Window != null)

osu.Game/Graphics/UserInterface/FPSCounter.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -244,6 +244,9 @@ private void updateFpsDisplay()
244244
{
245245
status += $" | {host?.ResolvedRenderer.ToString()}";
246246

247+
if (!string.IsNullOrEmpty(Game.VulkanStatus))
248+
status += $" | {Game.VulkanStatus}";
249+
247250
if (Game.IsOboeActive)
248251
status += $" | Oboe: {Game.OboeStatus} ({Game.OboeLatency:F1}ms)";
249252
}

osu.Game/OsuGameBase.cs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -128,6 +128,8 @@ public virtual EndpointConfiguration CreateEndpoints() =>
128128

129129
public virtual double OboeLatency => -1;
130130

131+
public virtual string VulkanStatus => string.Empty;
132+
131133
public virtual string Version
132134
{
133135
get

0 commit comments

Comments
 (0)