Лекция #20. Меши: оптимизация под vcache, strips, оптимальный вывод в OpenGL, VBO [Лектор - cppguru]
Автор: Арсений Капулкин
Disclaimer: некоторые опечатки поправлены, некоторые реплики передвинуты. полный лог
[15:06] <cppguru> план лекции такой
[15:06] <cppguru> 1. Введение. Зачем нужны TnL-кеши и как их заюзать
[15:07] <cppguru> 2. Детали и инструменты
[15:07] <cppguru> 3. Оптимальный рендер в OpenGL
[15:07] <cppguru> и 4. Резюме
[15:07] <cppguru> пункт 3. добавил специально, ибо вопросы по этой теме на форуме не переводятся )
[15:07] <cppguru> ок, введение
[15:08] <cppguru> У видеокарты есть так называемый post-TnL cache, который запоминает трансформированные вершины
[15:08] <cppguru> чтобы он работал, меши должны быть индексированными
[15:08] <cppguru> он хранит несколько последних вертексов и их индексы
[15:09] <cppguru> соответственно, если дублирующиеся вертексы идут подряд, то трансформируется только первый вертекс
[15:09] <cppguru> а остальные как бы бесплатны )
[15:09] <cppguru> фишка в том, что обычно меши _сильно_ избыточны, вертексы повторяются раза по 3-4
[15:10] <cppguru> поэтому оптимизация для этого кеша даёт ускорение раза в 3-4 (наблюдалось)
[15:10] <cppguru> чем мы и займёмся
[15:11] <cppguru> Во-первых, традиционный способ экономить лишние вертексы это triangle strips
[15:12] <cppguru> но реально он не намного лучше, чем просто оптимизированный triangle list
[15:12] <cppguru> он экономит память, по идее, но несильно
[15:12] <cppguru> (чуть меньше индексов получается)
[15:13] <cppguru> я проводил тесты на куче компов, и получилось, что стрипы всё-таки наилучший способ
[15:13] <cppguru> вот эти тесты - http://www.gamedev.ru/forum/?group=0&topic=18367
[15:14] <cppguru> так или иначе, основной выигрыш даёт именно использование кешей, а не уменьшение количества индексов
[15:14] <cppguru> [15:09:05] <Cote-Duke> Чуть меньше? Иногда в два раза :)
[15:14] <cppguru> ну, обычно меньше чем в два раза, потому что нужно склеивать стрипы дегенератами
[15:14] <cppguru> обычно раза в полтора
[15:15] <cppguru> и индексы вообще весят _гораздо_ меньше, чем вертексы
[15:15] <cppguru> в общем, если мы хотим максимального перфоманса,
[15:15] <cppguru> нужно оптимизировать меши так, чтобы дублирующиеся вертексы шли недалеко друг от друга
[15:16] <cppguru> для этого есть несколько библиотек
[15:16] <cppguru> во-первых, в D3DX есть функции ID3DXMesh::Optimize, ID3DXMesh::OptimizeInplace
[15:16] <cppguru> если используется D3D, то проще всего их вызвать и не мучиться )
[15:17] <cppguru> однако лекция расчитана на понимание принципов, так что продолжим
[15:17] <cppguru> для OpenGL есть несколько альтернатив:
[15:17] <cppguru> NvTriStrip, http://developer.nvidia.com/object/nvtristrip_library.html
[15:17] <cppguru> TriStripper, http://users.pandora.be/tfautre/softdev/tristripper/
[15:17] <cppguru> Stripe, http://www.cs.sunysb.edu/~stripe/
[15:17] <cppguru> я пробовал первые две
[15:18] <cppguru> первая - это библиотека от NVidia, она кривовата и чуть менее удобна
[15:18] <cppguru> вторая - tristripper - является улучшением первой, она работает гораздо быстрей
[15:19] <cppguru> она гарантирует оптимизацию за время O(n*Ln(n))
[15:19] <cppguru> при том, что качество оптимизации ничуть не хуже
[15:20] <cppguru> а NvTriStripper имеет квадратичную сложность, видимо
[15:20] <cppguru> и работает ощутимо дольше
[15:20] <cppguru> [15:12:40] <Innochenti> про алгоритмы в этих функциях расскажешь?
[15:20] <cppguru> NvTriStrip, например, берёт кучу раз случайные вертексы и строит стрипы
[15:21] <cppguru> TriStripper делает что-то существенно более умное, я не разбирался
[15:21] <cppguru> кстати, все эти библиотеки доступны в исходниках, так что можно изучать их и править
[15:21] <cppguru> [15:16:32] <Cote-Duke> NvTriStrip помоему это оффлайн тул. Какая разница как быстро он работает?
[15:21] <cppguru> совершенно верно замечено
[15:22] <cppguru> я привёл оценки только как общую информацию
[15:22] <cppguru> ещё немного о том, что же делают эти тулзы
[15:22] <cppguru> они генерируют много коротких стрипов - вертексов по 10
[15:23] <cppguru> NVidia считает, что они максимально vcache-friendly )
[15:23] <cppguru> это так, если располагать физически соседние стрипы рядом
[15:23] <cppguru> видимо они так и делают, так что всё в порядке
[15:23] <cppguru> но
[15:24] <cppguru> отсюда следует маленькая оговорка - стрипов получается довольно много (читай много DIP в D3D)
[15:24] <cppguru> и даже если мы их склеим дегенератами, получится много дополнительных вертексов
[15:24] <cppguru> отсюда следует то, что я говорил в самом начале про размер мешей - он получается несильно меньше, чем у просто triangle lists
[15:25] <cppguru> triangle list - просто оптимизированный список триугольников, в котором дублирущиеся вертексы идут рядом
[15:25] <cppguru> кстати, все вышеперечисленные тулзы могут генерировать и стрипы, и листы
[15:25] <cppguru> [15:19:15] <NULL_PTR^> как получить размер vcache?
[15:26] <cppguru> размер vcache равен 24 на GeForce3,
[15:26] <cppguru> 16 на младших GeForce
[15:26] <cppguru> я тестил на 6800, эмпирически он там 32
[15:26] <cppguru> на ATI, по слухам 32
[15:27] <cppguru> в общем, можно оптимизировать как 24 или 32 для современных видеокарточек и не заморачиваться
[15:28] <cppguru> [15:23:16] <Innochenti> гуру, чем ближе дублирующихся тринглов, тем меньше индексов надо для их хранения?
[15:28] <cppguru> нет
[15:28] <cppguru> число индексов не изменяется совершенно
[15:28] <cppguru> оптимизация мешей - это только перестановка индексов
[15:28] <cppguru> ну и вертексов тоже, о чем мы скоро поговорим )
[15:28] <Cote-Duke> Каков кеш на ТнТ2? :))
[15:29] <cppguru> 16 афаик )
[15:29] <Cote-Duke> О.. много.
[15:29] <cppguru> кстати, хочу предупредить, что TriStripper и NvTriStrip имеют разные мнения о смысле vcache size
[15:29] <cppguru> их размеры отличаются на 6 почему-то
[15:30] <cppguru> [15:24:45] <NULL_PTR^> говорят лучше недооценевать размер кеша. сильно ли можно потерять в скорости, если переоценить?
[15:30] <cppguru> да
[15:30] <cppguru> лучше недооценить
[15:30] <cppguru> если переоцениваешь, то дублируещиеся вертексы не возьмутся из кеша
[15:30] <cppguru> кеша просто не хватит
[15:30] <Cote-Duke> У меня есть вопрос касаемо ДХ и Гл.
[15:30] <Cote-Duke> В ДХ есть ID3DXMesh.Optimize()
[15:31] <Cote-Duke> Но, он не генерить стрипы, раз.
[15:31] <Cote-Duke> И он не такой фичный.
[15:31] <Cote-Duke> Так-что и для ДХ лучше юзать nvTriStrip или Stripper.
[15:31] <Cote-Duke> ИМХО
[15:32] <cppguru> ну это уже вопрос религии )
[15:32] <cppguru> однако замечу
[15:32] <cppguru> есть функции D3DXConvertMeshSubsetToSingleStrip, D3DXConvertMeshSubsetToStrips
[15:32] <cppguru> по всем вопросам можно смотреть пример из DXSDK - OptimizedMesh.cpp
[15:32] <cppguru> и читать хелп
[15:32] <cppguru> стрипы там полностью поддерживаются
[15:33] <Cote-Duke> Да но их можно юзать и ГЛ-людям (с) Земеделец
[15:33] <cppguru> на GL чуть меньше выбор
[15:33] <cppguru> Давайте обсудим, как можно склеивать стрипы
[15:34] <cppguru> ибо много dip-ов это нехорошо
[15:34] <cppguru> есть такой старый заслуженный приём - degenerate triangles
[15:34] <cppguru> Дегенераты - это четыре вырожденных треугольника (degenerate triangles) между каждыми соседними стрипами
[15:35] <cppguru> допустим, стрип заканчивается на вертексы A, B, а следующий начинается на C, D
[15:35] <cppguru> ... A B, C D ...
[15:35] <cppguru> Добавим между стрипами вертексы B и C:
[15:35] <cppguru> ... A B B C C D...
[15:35] <cppguru> тогда реально получатся такие треугольники:
[15:35] <cppguru> ABB BBC BCC CCD
[15:36] <cppguru> Это как раз дегенераты, они имеют нулевую площадь
[15:36] <cppguru> и они нужны только для того, чтобы получилась одна цепочка - один стрип
[15:36] <cppguru> это используется давно и везде, со времен md2 афаик )
[15:37] <cppguru> под OpenGL есть приятная альтернатива от NV
[15:37] <cppguru> называется primitive restart - рестарты
[15:37] <cppguru> можно просто ввести некий кодовый индекс, который будет означать начало нового стрипа, например 0xFFFF
[15:37] <cppguru> есть одна тонкость, связанная с дегенератами
[15:38] <cppguru> нужно соблюдать чётность длины стрипа, иначе испортится face-ordering
[15:38] <cppguru> поэтому когда предыдущий стрип, перед склейкой, имеет нечётную длину, нужно добавить ещё один дегенерат
[15:38] <cppguru> ... A B B B C C D...
[15:38] <cppguru> вот так, например.
[15:39] <cppguru> Хочу повторить очень важный момент, насчёт индексированных мешей
[15:39] <cppguru> как я уже сказал, вся эта кухня (связанная с vcache)
[15:40] <cppguru> работает только для индексированных мешей
[15:40] <cppguru> поэтому меши _всегда_, _обязательно_, должны быть индексированными
[15:40] <cppguru> все
[15:40] <cppguru> исключение - совсем специфичные случаи, такие как патиклы
2 апреля 2006
Комментарии [1]