Vulkan 设计指南

Vulkan 与早期图形 API 不同,因为它不会对应用程序执行某些优化,例如管道重用。相反,使用 Vulkan 的应用程序必须自行实现此类优化。如果它们不这样做,它们的表现可能比运行 OpenGL ES 的应用程序更差。

当应用程序自行实现这些优化时,它们有可能比驱动程序更成功地实现这些优化,因为它们可以访问给定用例的更具体信息。因此,熟练地优化使用 Vulkan 的应用程序可以获得比使用 OpenGL ES 时更好的性能。

此页面介绍了您的 Android 应用程序可以实现的几种优化,以从 Vulkan 获得性能提升。

硬件加速

大多数设备 通过硬件加速支持 Vulkan 1.1,而一小部分设备通过软件模拟支持它。应用程序可以使用vkGetPhysicalDeviceProperties 检测基于软件的 Vulkan 设备,并检查返回结构的deviceType 字段。SwiftShader 和其他基于 CPU 的实现具有值VK_PHYSICAL_DEVICE_TYPE_CPU。应用程序可以通过检查相同结构的vendorIDdeviceID 字段以获取 SwiftShader 特定的值,从而专门检查 SwiftShader。

性能关键型应用程序应避免使用软件模拟的 Vulkan 实现,而是回退到 OpenGL ES。

在渲染过程中应用显示旋转

当应用程序的面向上方方向与设备显示器的方向不匹配时,合成器会旋转应用程序的交换链图像以使其匹配。它在显示图像时执行此旋转,这会导致比不旋转它们时更多的功耗——有时会显着增加。

相反,在生成交换链图像时旋转它们会导致很少甚至没有额外的功耗。VkSurfaceCapabilitiesKHR::currentTransform 字段指示合成器应用于窗口的旋转。应用程序在渲染过程中应用该旋转后,使用VkSwapchainCreateInfoKHR::preTransform 字段报告旋转已完成。

最小化每帧的渲染传递

在大多数移动 GPU 架构上,开始和结束渲染传递是一个昂贵的操作。您的应用程序可以通过将渲染操作组织成尽可能少的渲染传递来提高性能。

不同的附件加载和附件存储操作提供不同的性能级别。例如,如果您不需要保留附件的内容,则可以使用更快的VK_ATTACHMENT_LOAD_OP_CLEARVK_ATTACHMENT_LOAD_OP_DONT_CARE 而不是VK_ATTACHMENT_LOAD_OP_LOAD。同样,如果您不需要将附件的最终值写入内存以供以后使用,则可以使用VK_ATTACHMENT_STORE_OP_DONT_CARE 来获得比VK_ATTACHMENT_STORE_OP_STORE 更好的性能。

此外,在大多数渲染传递中,您的应用程序不需要加载或存储深度/模板附件。在这种情况下,您可以避免为附件分配物理内存,方法是在创建附件图像时使用VK_IMAGE_USAGE_TRANSIENT_ATTACHMENT_BIT 标志。此位提供了与 OpenGL ES 中的glFramebufferDiscard 相同的好处。

选择合适的内存类型

在分配设备内存时,应用必须选择一种内存类型。内存类型决定了应用如何使用内存,并描述了内存的缓存和一致性属性。不同的设备有不同的可用内存类型;不同的内存类型表现出不同的性能特征。

应用可以使用一个简单的算法来为给定用途选择最佳内存类型。此算法选择VkPhysicalDeviceMemoryProperties::memoryTypes数组中满足两个条件的第一个内存类型:该内存类型必须允许用于缓冲区或图像,并且必须具有应用所需的最小属性。

移动系统通常没有为 CPU 和 GPU 分离的物理内存堆。在这些系统上,VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT 的重要性不如在具有独立 GPU 及其自身专用内存的系统上。应用不应假设此属性是必需的。

按频率对描述符集进行分组

如果您的资源绑定以不同的频率更改,请对每个管道使用多个描述符集,而不是为每次绘制重新绑定所有资源。例如,您可以为每个场景绑定设置一组描述符,为每个材质绑定设置另一组描述符,以及为每个网格实例绑定设置第三组描述符。

对于最高频率的变化(例如每次绘制调用执行的变化)使用立即常量。