受影响版本:5.4.3 及之后的版本。
其实 Epic 的 PR 里已经有人发现这个问题了。
- Added a hotfix for Vulkan draw markers and drastically improved performance on low-end Android devices in dev builds by Adlerkampf · Pull Request #12541 · EpicGames/UnrealEngine
- Update VulkanLayers.cpp : Fix Android Vulkan Performance by residentour · Pull Request #12361 · EpicGames/UnrealEngine
任意合并一个都可以修复这个问题,不清楚为什么 Epic 都拒绝合并。
问题的原因
从 5.4.3 开始,UE 默认在 DEBUG/Development 包里启用了 Draw Marker 功能:
#define VULKAN_SHOULD_ENABLE_DRAW_MARKERS (UE_BUILD_DEVELOPMENT || UE_BUILD_DEBUG)
bool bVulkanEnableDrawMarkers = VULKAN_ENABLE_DRAW_MARKERS;
这个功能本质上是依托 VK_EXT_debug_utils 扩展实现的,它会给所有的 Vulkan 对象添加调试信息,方便排查问题。
5.4.3 Hot Fix
UE 很快也意识到这个提交会造成巨大的性能问题,于是在 5.4.3 紧急发了一个 hotfix 试图修复它。但这个补丁只能算是半成品:虽然在没有显式传入 -forcevulkandrawmarkers 参数时不再打标记了,但它之前仍然会加载 VK_EXT_debug_utils 扩展,这依然可能引入其他性能问题。
部分机器上的性能劣化问题
我当前的测试工程版本是 5.5.4。在理论算力相近的芯片上,某台测试机的性能会明显慢于另外一个机器。
通过 stat unit 之类的简单测试可以发现,瓶颈不在 GPU,而在 RHI Thread。于是我又用simpleperf抓了一份热力图,分析后发现:绝大多数调用最终竟然都进入了 validation layer(紫色部分为搜索validation关键词的堆栈)

加载 validation layer 的原因
部分系统的图形驱动不提供 VK_EXT_debug_utils 扩展,但前面提到过,UE 会在 Dev 包里尝试启用它。
在Android的打包脚本里,默认的Dev 包里还会附带一个 VK_LAYER_KHRONOS_validation 的 layer。
接着 UE 会发现,第一个提供这个扩展的 layer 正好就是验证层,于是就把验证层悄悄启动了。

为什么MacOS模拟器不会有这个问题
验证后发现,MoltenVK 原生支持 VK_EXT_debug_utils,不需要回退到验证层 layer 上。
代码分析(Codex整理)
源码链路:
Engine/Source/Runtime/VulkanRHI/Private/Android/VulkanAndroidPlatform.h:11#define VULKAN_SHOULD_ENABLE_DRAW_MARKERS (UE_BUILD_DEVELOPMENT || UE_BUILD_DEBUG)
Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp:416- 会因为
draw markers的设置而开启bForceDebugUtils
- 会因为
Engine/Source/Runtime/VulkanRHI/Private/VulkanLayers.cpp:443- 如果
VK_EXT_debug_utils不是由裸驱动直接支持,而是由某个layer提供,就会把这个layer加进去
- 如果
- 这台设备上的
VK_EXT_debug_utils正好是由VK_LAYER_KHRONOS_validation提供的,所以它被加载了
结论:这次问题是 Android Development 包里的 VULKAN_SHOULD_ENABLE_DRAW_MARKERS 导致的,不是 r.Vulkan.EnableValidation,也不是命令行里的 validation 参数。要根治的话,要么让 Android Development 不再默认开启 VULKAN_SHOULD_ENABLE_DRAW_MARKERS,要么修改 ActivateDebuggingExtension(VK_EXT_debug_utils) 这段逻辑,避免为了 debug utils 把 Khronos validation layer 拉起来。