Використовує екземпляр для всього?


16

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

Редагувати. Скажімо, ми створюємо гру FPS. Більшість об’єктів мають лише один екземпляр: ніж, пістолет, кулемет, будівля та радіовежа. Але є також деякі об'єкти з декількома екземплярами: дерева (наприклад, 3 види дерев із сотнями екземплярів), трава і так далі ... Що я маю на увазі: замість того, щоб одноосібні об’єкти надавали "традиційний" спосіб та дерева і траву, використовуючи інстанціювання, ми рендеруємо їх усі за допомогою інстанції. Таким чином, наша радіовежа має лише один екземпляр (інформацію про який ми зберігаємо в буфері даних екземпляра), і ми робимо цю вежу, використовуючи якийсь DrawInstanced()виклик, кількість примірників дорівнює 1. Те саме з усіма іншими об'єктами (звичайно, дерева і трава мають кілька примірників).

Отже, моє запитання: чи погана ідея малювати один екземпляр об'єкта за допомогою інстанції? Чи має інстанції занадто великі накладні витрати (для пам'яті та продуктивності) чи він якимось чином небажаний для надання об'єктів з одним примірником?

Відповіді:


19

Під D3D9 з XPDM ви майже напевно хочете, щоб це було можливо, коли це можливо. Накладні витрати на дзвінки просто такі великі, що має сенс. У цьому сценарії точка перетину може бути низькою, як 2 або 3 екземпляри.

Якщо у вас є лише один екземпляр даної сітки, на поверхні може здатися спокусливим намалювати її неінсталяційно. Однак подивіться, що пов'язано:

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

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

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

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


Причини, які ви навели, - це ті самі, що змусили мене задати це питання в першу чергу. Дякую.
НПС

12

(У моїй системі не перевіряли її ніде) У GL, інстанція однієї сітки (малюнок із рахунком = 1) має деякі неприємні накладні витрати, але я не знаю, звідки вона береться. Я настійно пропоную не робити цього.

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

Пізніше, коли оптимізувати візуалізацію, просто переключившись з примірника об'єктів count = 1, щоб подати їх звичайним чином, врятував мене близько 3,5 мілісекунд на кадр, який коштує часу на i7 3770k (і GTX 770). Перемикання сітки з декількома екземплярами на просто їх виконання традиційним способом врятувало мене ще 0,5 мс. Загалом додаток перейшов від ~ 120 FPS до приблизно 230 FPS.

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


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

2

Ви можете бути впевнені, що малювати один об'єкт, який встановлюється, дорожче, ніж звичайно малювати один об'єкт. Для екземпляра GPU готується до великої кількості об'єктів, і ця підготовка буде відрізнятися, ніж для одного об’єкта. Однак, наскільки великий цей розрив у ефективності, можна виявити лише експериментуючи, і це дуже залежить від вашої фактичної настройки рендерінгу. Єдиний спосіб точно знати - це протестувати його самостійно. Бенчмаркінг одного дзвінка розіграшу важкий. Ось декілька ідей щодо того, як можна діяти.


2

Минуло 4 роки ... і я вважаю, що можна сказати, що цілком чудово подавати " вбудовані " дзвінки з 1. Як ви вже помітили, нові API DX12 та Vk мають кількість екземплярів, яка може становити від 0 до NUM_INSTANCES . Також зауважте, що немає DrawIndexed (...) .

EDIT

Як застереження, вищезгадане, мабуть, добре з цим сучасним API, можливо, для використання чогось такого старого, як Gl <3.3 або, можливо, DX11 потребує певного профілювання, як згадували інші користувачі.

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