Рекомендации по проектированию Vulkan, Рекомендации по проектированию Vulkan

Vulkan отличается от более ранних графических API тем, что драйверы не выполняют определенные оптимизации, такие как повторное использование конвейера, для приложений. Вместо этого приложения, использующие Vulkan, должны сами реализовывать такие оптимизации. В противном случае их производительность может быть хуже, чем у приложений, работающих под управлением OpenGL ES.

Когда приложения сами реализуют эти оптимизации, они могут сделать это более успешно, чем драйвер, поскольку у них есть доступ к более конкретной информации для данного варианта использования. В результате умелая оптимизация приложения, использующего Vulkan, может обеспечить более высокую производительность, чем если бы приложение использовало OpenGL ES.

На этой странице представлено несколько оптимизаций, которые ваше Android-приложение может реализовать для повышения производительности за счет Vulkan.

Аппаратное ускорение

Большинство устройств поддерживают Vulkan 1.1 посредством аппаратного ускорения , тогда как небольшое подмножество поддерживает его посредством программной эмуляции. Приложения могут обнаружить программное устройство Vulkan с помощью vkGetPhysicalDeviceProperties и проверки поля deviceType возвращаемой структуры. SwiftShader и другие реализации на базе ЦП имеют значение VK_PHYSICAL_DEVICE_TYPE_CPU . Приложения могут проверять наличие SwiftShader, проверяя поля vendorID и deviceID той же структуры на наличие значений, специфичных для SwiftShader.

Приложениям, критичным к производительности, следует избегать использования реализаций Vulkan с программной эмуляцией и вместо этого вернуться к OpenGL ES.

Применить поворот дисплея во время рендеринга

Когда направление приложения вверх не соответствует ориентации дисплея устройства, наборщик поворачивает изображения цепочки обмена приложения так, чтобы они совпадали. Он выполняет этот поворот при отображении изображений, что приводит к большему энергопотреблению — иногда значительно большему, — чем если бы он не вращал их.

Напротив, вращение образов цепочки обмена при их создании приводит к небольшому дополнительному потреблению энергии или вообще к нему не приводит. Поле VkSurfaceCapabilitiesKHR::currentTransform указывает поворот, который компоновщик применяет к окну. После того как приложение применит это вращение во время рендеринга, оно использует поле VkSwapchainCreateInfoKHR::preTransform чтобы сообщить о завершении вращения.

Минимизируйте количество проходов рендеринга на кадр

В большинстве архитектур мобильных графических процессоров начало и завершение этапа рендеринга является дорогостоящей операцией. Ваше приложение может повысить производительность, организовав операции рендеринга в минимально возможное количество проходов рендеринга.

Различные операции загрузки и хранения вложений предлагают разные уровни производительности. Например, если вам не нужно сохранять содержимое вложения, вы можете использовать гораздо более быстрый VK_ATTACHMENT_LOAD_OP_CLEAR или VK_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 при создании образа вложения. Этот бит обеспечивает те же преимущества, что и glFramebufferDiscard в OpenGL ES.

Выберите подходящие типы памяти

При выделении памяти устройства приложения должны выбрать тип памяти. Тип памяти определяет, как приложение может использовать память, а также описывает свойства кэширования и согласованности памяти. Разные устройства имеют разные типы памяти; разные типы памяти имеют разные характеристики производительности.

Приложение может использовать простой алгоритм, чтобы выбрать лучший тип памяти для конкретного использования. Этот алгоритм выбирает первый тип памяти в массиве VkPhysicalDeviceMemoryProperties::memoryTypes , который соответствует двум критериям: тип памяти должен быть разрешен для буфера или изображения и должен иметь минимальные свойства, необходимые приложению.

Мобильные системы обычно не имеют отдельных куч физической памяти для процессора и графического процессора. В таких системах VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT не так важен, как в системах с дискретными графическими процессорами с собственной выделенной памятью. Приложение не должно предполагать, что это свойство является обязательным.

Групповой дескриптор задается по частоте

Если у вас есть привязки ресурсов, которые меняются с разной частотой, используйте несколько наборов дескрипторов для каждого конвейера, а не повторно привязывайте все ресурсы для каждого отрисовки. Например, вы можете иметь один набор дескрипторов для привязок к каждой сцене, другой набор для привязок к каждому материалу и третий набор для привязок к каждому экземпляру сетки.

Используйте немедленные константы для наиболее частых изменений, например изменений, выполняемых при каждом вызове отрисовки.

,

Vulkan отличается от более ранних графических API тем, что драйверы не выполняют определенные оптимизации, такие как повторное использование конвейера, для приложений. Вместо этого приложения, использующие Vulkan, должны сами реализовывать такие оптимизации. В противном случае их производительность может быть хуже, чем у приложений, работающих под управлением OpenGL ES.

Когда приложения сами реализуют эти оптимизации, они могут сделать это более успешно, чем драйвер, поскольку у них есть доступ к более конкретной информации для данного варианта использования. В результате умелая оптимизация приложения, использующего Vulkan, может обеспечить более высокую производительность, чем если бы приложение использовало OpenGL ES.

На этой странице представлено несколько оптимизаций, которые ваше Android-приложение может реализовать для повышения производительности за счет Vulkan.

Аппаратное ускорение

Большинство устройств поддерживают Vulkan 1.1 посредством аппаратного ускорения , тогда как небольшое подмножество поддерживает его посредством программной эмуляции. Приложения могут обнаружить программное устройство Vulkan с помощью vkGetPhysicalDeviceProperties и проверки поля deviceType возвращаемой структуры. SwiftShader и другие реализации на базе ЦП имеют значение VK_PHYSICAL_DEVICE_TYPE_CPU . Приложения могут проверять наличие SwiftShader, проверяя поля vendorID и deviceID той же структуры на наличие значений, специфичных для SwiftShader.

Приложениям, критичным к производительности, следует избегать использования реализаций Vulkan с программной эмуляцией и вместо этого вернуться к OpenGL ES.

Применить поворот дисплея во время рендеринга

Когда направление приложения вверх не соответствует ориентации дисплея устройства, наборщик поворачивает изображения цепочки обмена приложения так, чтобы они совпадали. Он выполняет этот поворот при отображении изображений, что приводит к большему энергопотреблению — иногда значительно большему, — чем если бы он не вращал их.

Напротив, вращение образов цепочки обмена при их создании приводит к небольшому дополнительному потреблению энергии или вообще к нему не приводит. Поле VkSurfaceCapabilitiesKHR::currentTransform указывает поворот, который компоновщик применяет к окну. После того как приложение применит это вращение во время рендеринга, оно использует поле VkSwapchainCreateInfoKHR::preTransform чтобы сообщить о завершении вращения.

Минимизируйте количество проходов рендеринга на кадр

В большинстве архитектур мобильных графических процессоров начало и завершение этапа рендеринга является дорогостоящей операцией. Ваше приложение может повысить производительность, организовав операции рендеринга в минимально возможное количество проходов рендеринга.

Различные операции загрузки и хранения вложений предлагают разные уровни производительности. Например, если вам не нужно сохранять содержимое вложения, вы можете использовать гораздо более быстрый VK_ATTACHMENT_LOAD_OP_CLEAR или VK_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 при создании образа вложения. Этот бит обеспечивает те же преимущества, что и glFramebufferDiscard в OpenGL ES.

Выберите подходящие типы памяти

При выделении памяти устройства приложения должны выбрать тип памяти. Тип памяти определяет, как приложение может использовать память, а также описывает свойства кэширования и согласованности памяти. Разные устройства имеют разные типы памяти; разные типы памяти имеют разные характеристики производительности.

Приложение может использовать простой алгоритм, чтобы выбрать лучший тип памяти для конкретного использования. Этот алгоритм выбирает первый тип памяти в массиве VkPhysicalDeviceMemoryProperties::memoryTypes , который соответствует двум критериям: тип памяти должен быть разрешен для буфера или изображения и должен иметь минимальные свойства, необходимые приложению.

Мобильные системы обычно не имеют отдельных куч физической памяти для процессора и графического процессора. В таких системах VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT не так важен, как в системах с дискретными графическими процессорами с собственной выделенной памятью. Приложение не должно предполагать, что это свойство является обязательным.

Групповой дескриптор задается по частоте

Если у вас есть привязки ресурсов, которые меняются с разной частотой, используйте несколько наборов дескрипторов для каждого конвейера, а не повторно привязывайте все ресурсы для каждого отрисовки. Например, вы можете иметь один набор дескрипторов для привязок к каждой сцене, другой набор для привязок к каждому материалу и третий набор для привязок к каждому экземпляру сетки.

Используйте немедленные константы для наиболее частых изменений, например изменений, выполняемых при каждом вызове отрисовки.