A common pattern is to allocate every buffer as device-local and every staging buffer as host-visible, which is correct on desktop and leaves performance on the table on mobile. Mobile GPUs often share physical memory with the CPU, and a host-visible buffer that is also device-local avoids a copy at no cost.
The rule we use: for data written once and read many times, prefer a device-local heap; for data written every frame, prefer a host-visible heap that is also device-local when the device offers one. Check memoryProperties rather than assuming.
The second-order effect matters more than the allocation: a buffer that is not device-local forces a copy per frame, and the copy shows up as bandwidth contention with the render pass rather than as an obvious stall. That is why the frame looks fine alone and stutters with the UI overlay on top.
Read next: Profiling one frame: where 16.6 ms goes.