Коли обчислювальний шейдер більш ефективний, ніж піксельний шейдер для фільтрації зображень?


37

Операції фільтрації зображень, такі як розмиття, SSAO, цвітіння тощо, зазвичай виконуються за допомогою піксельних шейдерів і операцій "збирання", коли кожне виклик шейдера пікселів видає ряд текстурних даних для доступу до сусідніх значень пікселів та обчислює значення одного пікселя результат. Цей підхід має теоретичну неефективність у тому, що робиться багато зайвих випробовувань: сусідні виклики шейдерів повторно витягнуть багато однакових текселів.

Ще один спосіб зробити це за допомогою обчислювальних шейдерів. Вони мають потенційну перевагу в можливості спільного використання невеликої кількості пам'яті в групі викликів шейдерів. Наприклад, ви можете отримати кожен виклик одного текселя і зберегти його у спільній пам'яті, а потім обчислити результати звідти. Це може бути, а може і не бути швидшим.

Питання полягає в тому, за яких обставин (якщо взагалі є) метод обчислювальної шейдера насправді швидший, ніж метод піксельного шейдера? Чи залежить це від розміру ядра, якої операції фільтрації це тощо? Очевидно, що відповідь буде відрізнятися від однієї моделі GPU до іншої, але мені цікаво почути, чи є загальні тенденції.


Я думаю, що відповідь "завжди", якщо обчислювальний шейдер виконано правильно. Цього не банально досягти. Обчислювальний шейдер також краще відповідає, ніж піксельний шейдер, концептуально для алгоритмів обробки зображень. Однак піксельний шейдер забезпечує менший простір, з яким можна писати погано працюючі фільтри.
bernie

@bernie Чи можете ви уточнити, що потрібно для того, щоб обчислювальна шейдер була зроблена належним чином? Може написати відповідь? Завжди добре отримати більше поглядів на цю тему. :)
Натан Рід

2
А тепер подивіться, що ви змусили мене зробити! :)
bernie

На додаток до обміну роботою в потоках, можливість використовувати обчислювачі асинхронізу - одна з головних причин використовувати обчислювальні шейдери.
JarkkoL

Відповіді:


23

Архітектурна перевага обчислювальних шейдерів для обробки зображень полягає в тому, що вони пропускають крок ROP . Дуже ймовірно, що записи з піксельних шейдерів проходять через все звичайне обладнання для змішування, навіть якщо ви не використовуєте його. Як правило, обчислювальні шейдери проходять інший (а часто і більш прямий) шлях до пам’яті, тож ви можете уникнути вузького місця, яке у вас інакше було б. Я чув про великі перемоги у виконанні, які пояснюються цим.

Недоліком архітектури обчислювальних шейдерів є те, що графічний процесор більше не знає, які робочі елементи відходять до яких пікселів. Якщо ви використовуєте конвеєр відтінення пікселів, у графічного процесора є можливість упакувати роботу в основу / фронт хвилі, що записує в область цілі візуалізації, яка є суміжною в пам'яті (яка може бути плиткою Z-порядку або щось подібне для продуктивності причини). Якщо ви використовуєте обчислювальний конвеєр, GPU може більше не працювати в оптимальних партіях, що призводить до більшого використання пропускної здатності.

Можливо, вам вдасться знову перетворити змінену упаковку основи / хвильового фронту в перевагу, якщо ви знаєте, що ваша конкретна операція має підструктуру, яку ви можете використовувати, упакуючи пов'язані роботи в ту саму групу потоків. Як ви вже говорили, теоретично ви могли б зробити перерву апаратного забезпечення вибірки, відібравши одне значення на смугу руху і поставивши результат у групову спільну пам'ять для доступу до інших смуг без вибірки. Від того, чи буде це виграш, залежить те, наскільки дорога ваша пам'ять для спільної роботи в групах: якщо вона дешевша, ніж кеш текстури найнижчого рівня, то це може бути виграш, але гарантії цього немає. Графічні процесори вже досить добре справляються з високо локальними фактурними текстурами (за необхідності).

Якщо у вас є проміжні етапи в операції, де ви хочете поділитися результатами, можливо, буде більш доцільним використовувати групову спільну пам’ять (оскільки ви не можете повернутися до обладнання для вибору текстур, не фактично записавши проміжний результат у пам'ять). На жаль, ви також не можете залежати від результатів будь-якої іншої групи ниток, тому другий етап повинен обмежуватися лише тим, що доступно в одній плитці. Я думаю, що тут є канонічний приклад обчислення середньої освітленості екрана для автоматичної експозиції. Я також міг би уявити комбінування перегляду текстури з деякою іншою операцією (оскільки поглинання, на відміну від пониження та зменшення розмиття, не залежить від будь-яких значень поза певною плиткою).


Я серйозно сумніваюся, що ROP додає будь-які продуктивні витрати, якщо змішування вимкнено.
GroverManheim

@GroverManheim Залежить від архітектури! Крок злиття / виходу ROP також повинен відповідати гарантіям впорядкування, навіть якщо змішування вимкнено. У трикутнику на весь екран не існує жодної небезпеки щодо замовлення, але обладнання може не знати цього. Можливі спеціальні швидкі шляхи в апаратному забезпеченні, але точно знаючи, що ви маєте право на них ...
Джон Calsbeek

10

Джон вже написав чудову відповідь, тому вважайте цю відповідь продовженням своєї.

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

Після того, як ви обмотаєте голову навколо того, як працюють обчислювальні шейдери, вони також мають набагато більше сенсу у багатьох випадках. Використання піксельних шейдерів для фільтрації зображення вимагає налаштування рамбуфера, надсилання вершин, використання декількох ступенів шейдерів тощо. Чому це потрібно для фільтрації зображення? На мою думку, звикання показувати повноекранні квадроцикли для обробки зображень - це, безумовно, єдина "вагома" причина. Я переконаний, що новачок в області обчислювальної графіки знайшов би обчислювальні шейдери набагато більш природними для обробки зображень, ніж надання текстурам.

Ваше запитання стосується зокрема фільтрації зображень, тому я не буду надто детально розглядати інші теми. У деяких наших тестах просто встановлення зворотного зв’язку трансформації або переключення об'єктів framebuffer для надання текстури може призвести до витрат на продуктивність приблизно в 0,2 мс. Майте на увазі, що це виключає будь-яку візуалізацію! В одному випадку ми зберегли такий самий алгоритм, який було портовано для обчислення шейдерів, і помітно збільшили продуктивність.

При використанні обчислювальних шейдерів, більше кремнію на графічному процесорі можна використовувати для виконання фактичної роботи. Усі ці додаткові кроки необхідні при використанні маршруту піксельних шейдерів:

  • Збірка вершин (зчитування атрибутів вершин, дільниць вершин, перетворення типів, розширення їх на vec4 тощо)
  • Вершинний шейдер потрібно планувати незалежно від того, наскільки він мінімальний
  • Растерізатор повинен обчислити список пікселів, щоб відтіняти та інтерполювати вершинні виходи (можливо, лише текстурні координати для обробки зображень)
  • Усі різні стани (випробування на глибину, альфа-тест, ножиці, змішування) повинні бути встановлені та керовані

Ви можете стверджувати, що розумні драйвери можуть заперечувати всі згадані раніше переваги у роботі. Ви б мали рацію. Такий драйвер може визначити, що ви рендеруєте повноекранний квадроцикл без тестування глибини тощо, і налаштувати "швидкий шлях", який пропускає всю марну роботу, виконану для підтримки піксельних шейдерів. Я не був би здивований, якщо деякі драйвери роблять це для прискорення пропусків після обробки в деяких іграх AAA для їх конкретних графічних процесорів. Ви, звичайно, можете забути про будь-яке подібне лікування, якщо ви не працюєте в грі AAA.

Що, однак, водій не може зробити, це знайти кращі паралельності можливостей, пропонованих обчислювальним шейдером. Візьмемо класичний приклад гауссового фільтра. За допомогою обчислювальних шейдерів ви можете зробити щось подібне (розділяючи фільтр чи ні):

  1. Для кожної робочої групи розділіть вибірку вихідного зображення на розмір робочої групи і збережіть результати в групі спільної пам'яті.
  2. Обчисліть вихід фільтра, використовуючи результати вибірки, що зберігаються в спільній пам'яті.
  3. Запишіть до тексту тексту виводу

Крок 1 є ключовим тут. У версії піксельних шейдерів вихідне зображення відбирається на вибірку кілька разів на піксель. У версії обчислювального шейдера кожен текстовий текст-джерело читається лише один раз всередині робочої групи. Для зчитування текстур зазвичай використовується кеш на основі плитки, але цей кеш все ще набагато повільніше, ніж спільна пам'ять.

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

Однак є улов. Обчислювальні шейдери вимагають явних бар'єрів пам'яті для синхронізації їх виводу. Існує також менше гарантій захисту від помилкового доступу до пам'яті. Для програмістів з хорошими знаннями паралельного програмування обчислювальні шейдери пропонують набагато більшу гнучкість. Однак ця гнучкість означає, що також легше обробляти обчислювальні шейдери, як звичайний код C ++ і писати повільний або неправильний код.

Список літератури


Удосконалений паралелізм вибірки, який ви описуєте, є інтригуючим - у мене є флюїдний сим, який уже реалізований за допомогою обчислювальних шейдерів з безліччю екземплярів декількох зразків на піксель. Використання групової пам'яті для одноразового відбору з бар'єром пам'яті, як ви описуєте, здається чудовим, але я затримався на одному біті - як мені отримати доступ до сусідніх пікселів, коли вони потраплять до іншої робочої групи? наприклад, якщо у мене є імітаційний домен 64x64, розподілений по диспетчеру (2,2,1) numthreads (16,16,1), як би піксель з id.xy == [15,15] отримав сусідні пікселі ?
Тоссрок

У цьому випадку я бачу два основні варіанти. 1) збільшити розмір групи понад 64 і записати результати лише для 64x64 пікселів. 2) спочатку зразок 64 + nX64 + n розділено якось у вашій робочій групі 64x64, а потім використовуйте цю більшу "вхідну" сітку для обчислень. Найкраще рішення залежить від ваших конкретних умов, і я пропоную вам написати ще одне питання для отримання додаткової інформації, оскільки коментарі для цього погано підходять.
Берні

3

Я натрапив на цей блог: Обчислити шейдерні оптимізації для AMD

З огляду на те, які трюки можна виконати в обчислювальному шейдері (які характерні лише для обчислення шейдерів), мені було цікаво, якщо паралельне зменшення на шейдері обчислювальної техніки було швидше, ніж на піксельних шейдерах. Я написав електронною поштою автору Вольфу Енгелю, щоб запитати, чи спробував він піксельний шейдер. Він відповів, що так і назад, коли писав повідомлення в блозі, версія обчислювальної шейдера була значно швидшою, ніж версія піксельної шейдера. Він також додав, що сьогодні різниці ще більше. Тож мабуть, є випадки, коли використання обчислювального шейдера може мати велику перевагу.

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.