Практика використання інструментів Vulkan API для підвищення продуктивності програмного забезпечення

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

Рубрика Программирование, компьютеры и кибернетика
Вид статья
Язык украинский
Дата добавления 28.03.2024
Размер файла 105,1 K

Отправить свою хорошую работу в базу знаний просто. Используйте форму, расположенную ниже

Студенты, аспиранты, молодые ученые, использующие базу знаний в своей учебе и работе, будут вам очень благодарны.

Размещено на http://www.allbest.ru/

Національний технічний університет "Київський політехнічний інститут імені Ігоря Сікорського",

ПРАКТИКА ВИКОРИСТАННЯ ІНСТРУМЕНТІВ VULKAN API ДЛЯ ПІДВИЩЕННЯ ПРОДУКТИВНОСТІ ПРОГРАМНОГО ЗАБЕЗПЕЧЕННЯ

Новіков Олександр Олегович здобувач вищої освіти

факультету прикладної математики

Науковий керівник: Коляда Костянтин Вячеславович

канд.техн.наук, ст. викладач

Анотація

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

Ключові слова: Vulkan, графічний процесор, комп'ютерна графіка, GPU, рендеринг

Вступ

Vulkan - це сучасний низькорівневий API для роботи з графічним процесором. Він прийшов на заміну OpenGL і має велику кількість відмінностей в порівнянні із своїм попередником. Vulkan надає інструменти для того щоб контролювати пам'ять графічного процесору, синхронізувати команди, які виконуються на графічному процесорі, тощо. Це дає інженерам можливість підлаштовувати графічний процесор для виконання заданої задачі. Але при цьому така свобода може призвести до неоптимального використання інструментів, що були надані Vulkan. Нами описані практики, тестування яких проводилось на мобільному пристрої, і які можуть допомогти збільшити продуктивність програмного забезпечення.

Кешування графічного конвеєру

Vulkan надає можливість зберегти схему графічного конвеєру для подальшого її використання в пришвидшенні створення аналогічного графічного конвеєру [1]. Що можна використати для пришвидшення роботи програмного забезпечення.

Для створення графічного конвеєру в Vulkan, для початку, необхідно скомпілювати шейдери (VkShaderModule). Це має великий вплив на час побудови кадру, якщо говорити про створення зображення в реальному часі. Використання кешування надає можливість пришвидшити створення графічного конвеєру, що в свою чергу зменшить час необхідний для побудови кадру.

Vulkan має VkPipelineCache-об'єкт, який може бути використаний при створені графічного конвеєру за допомогою, наприклад, vkCreateGraphicsPipelines. Цей об'єкт є хешованим контейнером в якому зберігається необхідна інформація про графічний конвеєр.

Vulkan також надає можливість отримати бінарну інформацію VkPipelineCache-об'єкту та зберегти її у файл на диску. Це дає можливість використати цю інформацію при наступних роботах програмного забезпечення.

Тестування на графічному процесорі Mali G76 показують, що використання кешування зменшує час створення pipeline приблизно у два рази. Без використання збереженого кешованого pipeline, час на створення pipeline становить 50.4 мс. А при використанні кешування - 24.7 мс.

Менеджмент VkDescriptorSet та буферів

Для побудови програмного забезпечення, яке використовує Vulkan API, необхідно реалізовувати систему для менеджменту VkDescriptorPool та VkDescriptorSet. VkDescriptorSet використовується для передачі інформації на GPU.

Для відтворення динамічних об'єктів необхідно передавати інформацію про них на GPU. Для цього необхідно створювати буфер, записувати в нього цю інформацію та налаштовувати VkDescriptorSet, який буде вказувати на цей буфер. Матеріали також потребують у використанні VkDescriptorSet для вказування на текстуру, яку вони використовують. При комплексних програмах, кількість VkDescriptorSet є значною. При цьому VkDescriptoSet повинні змінюватися, в залежності від текстур, об'єктів на сцені, тощо.

Для вирішення проблеми зміни VkDescriptorSet можна створити по одному або більше VkDecsriptorPool для кадру. Оновлювати їх на початку обчислення кожного кадру та створювати необхідні VkDescriptorSet з цього VkDescriptorPool. Цей підхід буде використовувати виклики vkResetDescriptorPool на початку обчислення кадру, а vkAllocateDescriptorSets та vkUpdateDescriptorSets для заповнення інформацією [2].

Виклики цих функцій можуть перевантажити CPU. Навіть створити ситуацію в якій оновлення VkDescriptorSet буде відбуватися довше ніж саме обчислення кадру. При тестування такого підходу на графічному процесорі Mali G76 час на відмалювання кадру становить 43.2 мс.

Для вирішення цієї проблеми потрібно побудувати систему кешування. Це надає можливість перевикористовувати вже створені VkDescriptorSet. Система кешування може бути різною. Наприклад, хештаблиця, де буфери та картинки, на які взакує VkDescriptorSet, є ключами таблиці [2]. Такий підхід сильно пришвидшує обчислення кадру. При використанні хештаблиці, час за який обчислюється кадр, зменшився то 27 мс. Але оскільки VkDescriptorPool не очищується при кожному кадрі, для великих динамічних сцен необхідно реалізовувати систему, яка буде відслідковувати descriptor set, що довго не використовуються, та видаляти їх.

Альтернативним підходом до кешування VkDescriptorSet є правильний менеджмент буферів. Замість того, щоб створювати буфер для кожного об'єкту, можна створити буфер і записати в нього інформацію про всі схожі між собою об'єкти. Це зменшить кількість VkDescriptorSet і, відповідно, зменшить виклики vkUpdateDescriptorSets та vkAllocateDescriptorSets. Тестування дає такі ж самі результати як і кешування.

Також має сенс об'єднати кешування та менеджмент в буфері. Це може дати певний результат на великих комплексних сценах.

Використання VkSubpass

Унікальним інструментом для Vulkan є VkSubpass. VkSubpass дає можливість поділити VkRenderPass на окремі логічні частини. Використання subpass замість render pass дає можливість GPU виконувати оптимізації [1].

Перевагу викорстання VkSubpass можна побачити на прикладі Deferred Rendering. Ідея якого полягає у поділенні побудови кадру на дві частини: обчислення геометрії та обчислення освітлення. На етапі обчислення геометрії будується так званий G-буфер. В цей буфер може бути записана різна інформація: глибина, нормалі, позиція, тощо. На другом етапі за допомогою інформації з G-буферу, виконується обчислення того як освітлення впливає на об'єкти, що разтошовані на сцені [3].

Описані етапи обчислення кадру можна поділити двома способами. Або створити два окремих VkRenderPass та передавати між ними інформацію, тобто G-буфер. Або створити один VkRenderPass, який складається з двох VkSubpass. Різниця між цими двома підходами видна в кількості інформації, що передається між різними рівнями пам'яті комп'ютерної системи. У першому випадку інформація зчитується зі швидкістю 2905 MiB на секунду. Записується - зі 2289 MiB на секунду. У другому випадку - зі 825 MiB та 788 MiB на секунду. Така різниця є особливо критичною на мобільних пристроях, оскільки передача інформації є енергозатратною операцією.

Pipeline Barriers

В Vulkan команди які виконуються на GPU мають декілька стадій, що частково відображено на рис. 1 [1]. Для синхронізації спільних ресурсів, які використовують ці команди, в Vulkan використовується так звані бар'єри графічного конвеєру. Бар'єри поділяються на три типи в залежності від типу ресурсів з якими вони працюють: VkMemoryBarrier, VkBufferMemoryBarrier та VkImageMemoryBarrier.

Додавання бар'єру при записі команди встановлює залежність між усіма командами записаними до цього та командами після бар'єру.

Для прикладу роботи бар'єрів знову візьмемо Deferred Rendering з двома Render Pass. В першому VkRenderPass записується інформація в G-буфер. У другому інформація зчитується з G-буферу для обчислення кінцевого вигляду кадру. Це є критичною точкою, тому необхідно реалізувати синхронізацію.

Рис. 1 Стадії графічного конвеєру

Простим рішенням буде дуже строгий бар'єр - ALL_GRAPHICS_BIT або ALL_COMMANDS_BIT. Але це призведе до того, що обчислювальні ресурси GPU буду простоювати.

Інформація з G-буферу використовуються у фрагментному шейдері. Тому кращим бар'єром у цьому випадку буде той, що очікує на етапі фрагментного шейдеру, поки буде виконано запис в G-буфер. Такий бар'єр буде мати вигляд COLOR_ATTACHMENT_OUTPUT_BIT - FRAGMENT_SHADER_BIT. Цей бар'єр надає можливість паралельно обчислювати фрагментний шейдер з першого проходу та вершиний шейдер з другого проходу.

При бар'єрі BOTTOM_OF_PIPE_BIT - TOP_OF_PIPE_BIT час на відтворення кадру становить 31.6 мс. Такий бар'єр блокує можливість виконувати фрагментний та вершинный шейдер одночасно.

Другий випадок, коли бар'єр COLOR_ATTACHMENT_OUTPUT_BIT - VERTEX_SHADER_BIT. Це вважається стандартним бар'єром, який дає змогу впевнитися, що ресурси з першого проходу готові до використання у другому. Тут час становить 31.8 мс. Тобто час не змінився з попереднього прикладу, оскільки в даному випадку обчислювання не відбуваються у вершиному шейдері другого проходу. Оскільки у випадку Deferred Rendering обчислення відбуваються в фрагментному шейдері, то оптимальним бар'єром є COLOR_ATTACHMENT_OUTPUT_BIT - FRAGMENT_SHADER_BIT. В цьому випадку час обчислення кадру станови 27.3 мс.

процесор програмний забезпечення графічний

Синхронізація роботи центрального та графічного процесорів

Простим методом синхронізації CPU та GPU є використання vkQueueWaitIdle або vkDeviceWaitIdle. Ці команди очікують поки пристрій або черга на пристрої закінчать виконання всіх команд. Хоча цей метод є надійним, але він призводить до появи етапів коли GPU простоює. Припиняється паралельне виконання вершиного та фрагментного етапів графічного конвеєру для різних кадрів. Це призводить до збільшення часу необхідного для обчислення кадру.

Альтернативою є використання VkFence об'єктів. Вони створенні якраз для повідомлення CPU, що GPU готовий до отримання наступних команд. Цей підхід позбавляє проблеми простоювання GPU, оскільки CPU знає коли необхідно відправляти команди, необхідні для обчислення наступного кадру.

Тестування було проведено на двох прикладах. В першому використовується vkDeviceWaitIdle перед початком кожного нового кадру. Це змушує GPU закінчити повність усю роботу перед початком наступного кадру. Що призводить до простоювання ресурсів GPU. При такому підході час, затрачений на обчислення кадру, становить 70.2 мс. У другому випадку використовується Fence, а час становить 56 мс.

Список використаних джерел

1. Vulkan Specification. Вилучено з https://registry.khronos.org/vulkan/specsZ1.3extensions/html/vkspec.html

2. Arm Developer. Best Practices. Вилучено з https://developer.arm.com/documentation/101897/0301 /Optimizing-applicationlogic/Vulkan-GPU-pipelining

3. Eric Haines, Naty Hoffman, Tomas Moller (2019). Real-Time Rendering Third Edition

Размещено на Allbest.ru


Подобные документы

Работы в архивах красиво оформлены согласно требованиям ВУЗов и содержат рисунки, диаграммы, формулы и т.д.
PPT, PPTX и PDF-файлы представлены только в архивах.
Рекомендуем скачать работу.