Summary
Since moving our shipped Android title from UE 5.7.4 to 5.8 (seen on 5.8.3 and 5.8.4), our top crash is a null pointer dereference (SIGSEGV, fault address 0x10) inside MediaTek’s Vulkan driver, vulkan.mtk.so, on the RHI thread at end of frame, called from FVulkanCommandBufferPool::FreeUnusedCmdBuffers in the RHIEndFrame_RenderThread lambda (full callstack below).
It only happens on MediaTek SoCs, with both Imagination PowerVR and Arm Mali GPUs, and across several vulkan.mtk.so driver builds. Phones on Qualcomm, Exynos and Google Tensor chips are not affected.
Likely cause: engine commit 2a295e977f27 (“Removed BlockUntilGPUIdle and SubmitCommandsAndFlushGPU … Removed FRHICommandList::FlushResources and RHIFlushResources … Moved Vulkan Command Buffer cleanup”), which first shipped in 5.8.0:
- In 5.7.4, RHIEndFrame_RenderThread called Context.FreeUnusedCmdBuffers(bTrimMemory = false), which frees command buffers that have been idle for a while. vkTrimCommandPool only ran from RHIFlushResources (bTrimMemory = true), which is rare.
- In 5.8.x, RHIFlushResources is gone and RHIEndFrame_RenderThread passes bTrimMemory = true. So FVulkanCommandBufferPool::FreeUnusedCmdBuffers now calls vkTrimCommandPool on the immediate context’s pool every frame and returns early. The idle command buffer cleanup no longer runs at all.
MediaTek’s driver crashes inside that per-frame trim. A UE 5.6 report with the same signature on a Dimensity 930 (null dereference at 0x10 in vulkan.mtk.so on the RHI thread, on level switch, when the trim used to run) suggests the driver bug predates 5.8, and 5.8 makes it far more likely by trimming every frame: Unreal engine 5.6 crashing on andriod mediatek processor vulkan
What Type of Bug are you experiencing?
Platform Mobile
Steps to Reproduce
- Package any project for Android with Vulkan enabled (we use ASTC; Development and Shipping are both affected) on UE 5.8.3 or 5.8.4.
- Run on a device with a MediaTek SoC. Devices in our crash reports include:
- Samsung Galaxy A03s (SM-A037F) and A04e (SM-A042F): Helio P35, PowerVR GE8320
- Samsung Galaxy Tab A7 Lite (SM-T220 / SM-T227U): Helio P22T, PowerVR GE8320
- Motorola Moto G Power 5G (2023), G54, G55, G56: Dimensity 930 / 7020 / 7025 / 7060, PowerVR BXM-8-256
- Xiaomi Redmi Note 14 5G (24094RAD4G): Dimensity 6100+, Mali-G57
- plus TCL, ZTE, BLU, Itel and other low-cost MediaTek devices
- Play normally. The crash is intermittent, at end of frame on the RHI thread.
Expected Result
No crash. Command pools are trimmed occasionally (as in 5.7), not on every frame.
Observed Result
Intermittent SIGSEGV (null pointer dereference, fault address 0x10) in vulkan.mtk.so, called from FVulkanCommandBufferPool::FreeUnusedCmdBuffers during FVulkanDynamicRHI::RHIEndFrame_RenderThread on the RHI thread. It has been our most common Android crash since our 5.8 release.
Affects Versions
5.8
Platform(s)
Android
For crash reports, include your callstack
null pointer dereference: SIGSEGV 0x0000000000000010
#00 pc 0x6b770 vulkan.mtk.so
#01 pc 0x95efa3c libUnreal.so (FVulkanCommandBufferPool::FreeUnusedCmdBuffers(FVulkanQueue*, bool))
#02 pc 0x95efa3c libUnreal.so (FVulkanCommandBufferPool::FreeUnusedCmdBuffers(FVulkanQueue*, bool))
#03 pc 0x9672128 libUnreal.so (FVulkanDynamicRHI::RHIEndFrame_RenderThread(FRHICommandListImmediate&)::$_0::operator()(FVulkanContextArray const&) const)
#04 pc 0x967223c libUnreal.so (TRHILambdaCommandMultiPipe<FRHICommandListBase, FVulkanDynamicRHI::RHIEndFrame_RenderThread(FRHICommandListImmediate&)::$_0>::ExecuteAndDestruct(FRHICommandListBase&))
#05 pc 0x2c54ffc libUnreal.so (FRHICommandListExecutor::FTranslateState::Translate_ExecuteCommandList(FRHICommandListBase*))
#06 pc 0x2c53fe0 libUnreal.so (FRHICommandListExecutor::FTaskPipe::Execute(FRHICommandListExecutor::FTaskPipe::FTask*, TRefCountPtr const&) const)
#07 pc 0x2c5d8b4 libUnreal.so (TGraphTask<TFunctionGraphTaskImpl<void (ENamedThreads::Type, TRefCountPtr const&), (ESubsequentsMode::Type)0> >::ExecuteTask())
#08 pc 0x2599e00 libUnreal.so (UE::Tasks::Private::FTaskBase::TryExecuteTask())
#09 pc 0x25b4ca0 libUnreal.so (FNamedTaskThread::ProcessTasksNamedThread(int, bool))
#10 pc 0x25b454c libUnreal.so (FNamedTaskThread::ProcessTasksUntilQuit(int))
#11 pc 0x2bc6eb8 libUnreal.so (FRHIThread::Run())
#12 pc 0x2630108 libUnreal.so (FRunnableThreadPThread::Run())
#13 pc 0x259b8e8 libUnreal.so (FRunnableThreadPThread::_ThreadProc(void*))
#14 pc 0x102d40 libc.so
#15 pc 0x96b50 libc.so
(Game thread at the time: FEngineLoop::Tick → FFrameEndSync::Sync → FRenderCommandFence::Wait. The same signature also reproduces on a Dimensity 6100+ / Mali-G57 device with a different vulkan.mtk.so build.)
Additional Notes
- Suggested fix: go back to bTrimMemory = false in RHIEndFrame_RenderThread, and trim command pools only occasionally (e.g. on memory pressure or level transitions), or skip the trim on MediaTek drivers.
- Our workaround for now: a device profile matching rule on SRC_Hardware (regex [1][Tt][0-9]) that sets r.Android.DisableVulkanSupport=1, so MediaTek devices run OpenGL ES.
- Falling back to OpenGL ES exposed two separate engine issues, which we can report separately if useful:
- BinkMedia on OpenGL ES: FBinkMediaTextureResource::UpdateDeferredResource calls GetNativeResource() on the render thread, which hits the “Potential use of GL context from incorrect thread” fatal in Development builds. Also, FBinkTextures::Draw begins its render pass without setting a viewport, so on GL the video draws into the previous (backbuffer-sized) viewport and only fills part of the texture.
- A skeletal mesh LOD built with “Use High Precision Tangent Basis” creates a tangents buffer SRV whose format maps to GL_RGBA16, which OpenGL ES rejects in glTexBufferRange (GL_INVALID_ENUM). BindGLTexBufferRange doesn’t check the error, so it later trips an unrelated GL_CHECK (seen as “Assertion failed: !Err” in OpenGLTexture.cpp:972 after glCompressedTexSubImage2D) in Development builds.
Mm ↩︎