WebGL, вимірювання продуктивності та методи оптимізації WebGL -додатків в браузері
Анотація: Проведено аналіз методів вимірювання продуктивності WebGL-додатків. Розглянуто методи оптимізації роботи шейдерів WebGL та відтворення об’єктів.
Бібліографічний опис статті:
Алевтина Кучеренко. WebGL, вимірювання продуктивності та методи оптимізації WebGL -додатків в браузері//Наука онлайн: Міжнародний електронний науковий журнал - 2019. - №12. - https://nauka-online.com/publications/technical-sciences/2019/12/webgl-vimiryuvannya-produktivnosti-ta-metodi-optimizatsiyi-webgl-dodatkiv-v-brauzeri/
Технічні науки
УДК 004.416.2
Кучеренко Алевтина Олександрівна
студентка
Національного технічного університету України
«Київський політехнічний інститут імені Ігоря Сікорського»
WEBGL, ВИМІРЮВАННЯ ПРОДУКТИВНОСТІ ТА МЕТОДИ ОПТИМІЗАЦІЇ WEBGL -ДОДАТКІВ В БРАУЗЕРІ
Анотація. Проведено аналіз методів вимірювання продуктивності WebGL-додатків. Розглянуто методи оптимізації роботи шейдерів WebGL та відтворення об’єктів.
Ключові слова: WebGL, API, JavaScript, буфер, текстура, рейдер, браузер, GPU, CPU.
WebGL – це API, який дає веб-платформі доступ до ресурсів графічної карти, тобто він дозволяє малювати апаратно-прискорену графіку, в тому числі тривимірну, в браузері, та за допомогою JavaScript.
WebGL-додаток спирається на три основні види ресурсів:
- Буфери – великі блоки байт, куди ми укладаємо нашу геометрію, яку ми хочемо показувати користувачеві, наші моделі.
- Текстури – картинки, які ми хочемо накладати на наші моделі, щоб вони виглядали гарніше, природніше, були схожі на об’єкти реального світу.
- Шейдери – це невеликі програми, які запускаються прямо на GPU, і розповідає GPU про те, як ми хочемо показувати нашу геометрію, як ми хочемо на неї накладати наші текстури і тому подібне.
І всі ці ресурси живуть, народжуються і вмирають в рамках WebGL-контексту. Через WebGL-контекст ми отримуємо доступ до всіх ресурсів, ми їх створюємо і, крім того, в ньому ще є стан, який розповідає про те, як ми пов’язуємо ці ресурси між собою, як ми пов’язуємо разом буфери, шейдери, текстури, і як ми використовуємо результати обчислення шейдерів. Тобто стан, який впливає на відтворення.
Перед тим як розглядати оптимізацію, спочатку потрібно розглянути вимірювання. Неможливо поліпшити те, що ми не можемо виміряти. І найпростішим інструментом, який дозволяє нам це робити, є лічильник кадрів в секунду.
Він доступний в браузері Google Chrome, але його нескладно зробити самому. Логіка його вимірів досить проста, якщо у нас багато кадрів в секунду, то це добре, мало кадрів – погано. Але треба пам’ятати одну тонкість, що FPS в додатку обмежений частотою оновлення екрану користувача. У більшості користувачів це 60 Гц, тобто 60 кадрів в секунду, і тому неважливо, генеруємо ми наш кадр за 5 мс або за 10 мс. Завжди буде 60 кадрів. До лічильнику кадрів в секунду потрібно ставитися з певною часткою скепсису. І все-таки, якщо у нас мало кадрів в секунду, додаток працює повільно, ми починаємо шукати в ньому вузькі місця. Ми починаємо намагатися його оптимізувати.
Зазвичай в таких випадках розробник починає профілювати додаток, дивитися, в яких функціях JavaScript проводить найбільше часу, розглядає ці функції під мікроскопом і намагається їх оптимізувати.
Однак у випадку з OpenGL-додатками профайлер стикається з проблемою.
Справа в тому, що WebGL API частково асинхронне, тобто всі виклики відтворення асинхронні, деякі інші виклики теж асинхронні. Що це означає, наприклад, з викликами відтворення? Що коли, виклик повернув управління в наш JS, то відтворення до цього моменту не закінчилось, воно закінчиться колись пізніше. Більш того, він може початися навіть пізніше, ніж нам повернулося управління. Відповідно, в профайлері такі виклики не будуть показані, та й, якщо відтворення відбувається повільно, ми цього не побачимо.
Але не все так погано, деякі виклики все-таки синхронні, а деякі виклики, не тільки синхронні, вони ще шкідливим чином синхронізують весь контекст. Тобто вони не тільки виконують роботу, яку ми на них поклали синхронно, вони ще й, блокують наш додаток при цьому, чекають кінця виконання всіх завдань, які ми поклали на контекст до цього, тобто всіх відтворень, всіх якихось змін станів і тому подібне. Якраз такі проблеми профайлер допоможе нам побачити.
А з відтвореннями нам може допомогти інший інструмент, який називається EXT_disjoint_timer_query.
Це розширення для WebGL, воно ще експериментальне, тобто воно не є стабільним, та знаходиться у стані активної розробки. Воно дозволяє вимірювати час виконання наших команд на GPU. При цьому воно це робить асинхронно. Тобто воно не синхронізує наш контекст. Весь код, який ми напишемо для вимірювання продуктивності додатка, можна буде додати прямо в кінцеву версію нашого коду, прямо користувачам. І, наприклад, збирати статистику. Або, що ще краще, на підставі цих вимірів та даних, підлаштовувати якість картинки під обладнання користувача, тобто на смартфонах показувати користувачам одну картинку, на планшетах – більш якісну, а на персональних комп’ютерах, показувати найякіснішу картинку з найякіснішими ефектами. Але при цьому, треба написати якийсь код навколо цього – це невеликий мінус.
EXT_disjoint_timer_query дозволяє виміряти час виконання першого або декількох викликів прямо на GPU. Ще воно дозволяє ставити точні тимчасові мірки в конвеєрі. Тобто воно дозволяє, наприклад, виміряти, скільки часу наші команди GL йдуть від нашого JS до GPU, тобто які у нас затримки в конвеєрі.
Ще EXT_disjoint_timer_query – це синхронний API, він дозволяє створювати об’єкти query до драйверу, до реалізації WebGL, через об’єкти query йде з ним робота. Після цього, закінчується побудова кадру і зазвичай на наступний кадр, вам стають доступні результати вимірювання в наносекундах, досить точні при цьому. І ці вимірювання можна використовувати.
Підіб’ємо короткі висновки про вимірювання.
У нас є лічильник кадрів в секунду як якась якісна характеристика додатки, яка дозволяє сказати нам, на скільки оптимально працює. І якщо показник низький, то профайлер JS нам допоможе з пошуком вузьких місць в роботі нашого коду на CPU. EXT_disjoint_timer_query допоможе нам з пошуком вузьких місць в роботі нашого коду на GPU.
Ми розглянули як вимірювати продуктивність, ми розглянули як шукати вузькі місця, тепер треба зрозуміти, що робити з ними далі. Тут ми обговоримо лише деякі оптимізації. Комп’ютерна графіка – це велика область для оптимізації. Ми обговоримо лише деякі загальні підходи.
Перше правило, яке потрібно взяти на озброєння на шляху до швидкого WebGL-додатку – це акуратно працювати з WebGL-станом. В WebGL-контексті є великий шматок стану, який впливає на відворення і працювати з ним потрібно акуратно. Зокрема, не потрібно робити викликів get і read, тобто викликів, які запитують поточний стан контексту або читають дані з відеокарти. Якщо це нам вкрай необхідно. Чому? Тому що ці виклики, мало того, що синхронні, вони ще можуть викликати синхронізацію всього контексту, тим самим загальмувавши наші програми. Зокрема, метод getError, який перевіряє, чи не виникла у нас помилка в роботі з WebGL-контекстом. Його варто викликати тільки під час розробки. Також потрібно мінімізувати перемикання стану, не робити це занадто часто, це може викликати синхронізацію контекстів.
Як це може виглядати в коді?
Зокрема, як вкладені цикли, де в зовнішніх циклах ми перемикаємо дорожчі стани, ми перемикаємо їх рідше, а в більш вкладених циклах ми перемикаємо стани, які перемикати дешевше. Зокрема, підключення буферів або перемикання шейдерних програм – це досить дорогі зміни, вони практично гарантовано викликають синхронізації і, взагалі, проходять досить довго. А перемикання текстур і параметрів шейдерів – швидше, і тому їх можна робити частіше.
Але і в цій чудовій конструкції можна оптимізуватися далі.
Можна робити менше викликів відтворення, тобто кожен виклик відтворення додає якусь роботу на перевірку валідності стану, на відправку цих даних до відеокарту. А зменшити його можна одним лише способом – роблячи цих викликів менше, додаючи трохи менше перенавантажень, при цьому роблячи більше роботи за кожен виклик. Зокрема, можна укласти кілька об’єктів, кілька моделей, які ми хочемо намалювати, в один буфер з даними. А якщо вони використовують текстури, то всі ці текстури укласти в одну велику картинку, яку зазвичай називають текстурованим атласом. Таким чином наведемо кілька об’єктів до того, як для них буде вимагатися однакове стан для відтворення, і ми зможемо намалювати їх за один виклик.
Є певна техніка, називається Instancing, яка дозволяє малювати кілька копій одного і того ж об’єкта з різними параметрами за один виклик. Хорошим прикладом застосування оптимізації Instancing є система частинок.
Взагалі, використовувати розширення – це хороша практика, в них часто закладені дуже корисні функції, без яких складно або неможливо робити якісь речі. Але, треба використовувати їх акуратно, тобто треба завжди залишати запасний шлях в коді, який буде працювати без розширень.
Література
- Мацудо К. WebGL: программирование трехмерной графики./ К. Мацудо, 2019. С. 235- 254.
- Закас Н. Оптимизация производительности / Н. Закас. 2016. С. 145-168.
- Флэнаган Д. JavaScript. Карманный справочник / Д. Флэнаган. 2016. C. 55-63.
Коментарі закрито.
To comment on the article - you need to download the candidate degree and / or doctor of Science