Ландшафтный дизайн, часть два.

Продолжение первой части http://blog.gamedeff.com/?p=157

Скоро сказка сказывается,  QA аппрув значительно более медленная процедура.

Напомню краткое содержание предыдущей части.  Был обычный террейн с геоморфингом, умеренно тормозил и доставлял умеренный набор проблем. Проблемы типичные – много DIP вызовов, тяжелый шейдер, много вершинных стримов. Отчасти проблемы росли из-за того, что один и тот же код работал в игре и редакторе. То, что предназначено для редактирования, не работает быстро. То, что работает быстро, не предназначено для редактирования.

Возникло желание сделать вторую версию, оптимизированную. Специальный билдер будет эту версию строить по ночам, будет счастье.  В принципе, процесс оказался быстрый, можно и по кнопочке “Save” в редакторе сохранять. Реюзать код не хотелось – было принято волевое решение старый террейн оставить как есть, а новый писать по-новой ( вплоть до шейдеров ), выделяя рефакторингом общие куски по мере необходимости.  Апгемахт.

Старый террейн хранится патчами по 256×256 метров, heightmap и карта сплаттинга, на этапе загрузки из нее изготавливались текстурки сплаттинга, 32×32 пикселя ( 32×32  метра ). Решил, что по крайней мере на уровне загрузки гранулярность останется той же самой.  Т.е. вся информация сохраняется для куска 256×256 метров.

А вот гранулярность рендеринга решил почикать. Была гранулярность 32×32 метра, стало 8×8 метров. Текстурки сплаттинга в быстром террейне тоже имеют размер 8×8 и запакованы в атлас.  В каждом фрагментe 8×8 также хранится протокол binary triangle разбиения. В нем прямоугольные равнобедренные треугольники итеративно делятся точкой на середине гипотенузы, можно взглянуть например на http://www.gamasutra.com/features/20000403/turner_01.htm . Разбиение не доходит до самого “низа”, а останавливается на некотором уровне ошибки, так что плоские куски террейна кодируются минимумом информации. На каждую вершину максимального разбиения надо хранить float и еще несколько служебных байтиков, кодирование получается весьма экономным относительно карты высот.

Это самое бинарное дерево построить – в общем-то несложно. Если бы не два “но”. Первое “но” заключается в том, что надо точно стыковать соседние патчи, а алгоритм не очень локальный. Впрочем, он почти локальный – любые изменения в фрагментике (8×8) задевают только соседей. В результате рабочий алгоритм  загружает патч с необходимой “каймой” и работает внутри этого большего множества. Второй момент забавный, связан с тем, что у меня два слоя террейна:

Острова

Необходимо согласовывать разбиение на тех фрагментиках из двух слоев, которые пересекаются по высоте. И десогласовывать там, где велика разница высот. Нудно.

Геоморфинг я решил выкинуть. Вместо него - перестройка индексного буфера ( он полностью динамичный, меняется каждый фрейм ). Строится view-dependent по протоколу бинарного разбиения треугольников. Вершинный буфер тоже можно делать полностью динамическим, и я бы обязательно делал его динамическим на своих любимых SPU. Но на PC решил все же не изголяться. Для каждого куска карты ( те самые 256×256 метров ) заранее строится вершинный буфер. В память вроде влезаю, расстояние видимости небольшое ( примерно 600 метров ). Вершинный формат тонкий, что ли  16 байт.

В рантайме я набираю эти самые кусочки 8×8, видимые в кадре, сортирую их по корзинкам-материалам и рисую каждую корзинку за один присест.

Закодал я всю эту красоту ( вместе с шейдерами, загрузчиком и дампилкой ) за неделю. А потом стал приводить в чувство.  Отвалилась вода, потому что она намертво слиплась со старым террейном.  Пришлось делать новую воду. Она хорошая, тоже оптимизированная, там в специальной текстурке хранится глубина, чтобы рейтрейсом по ней определять прозрачность. Не уверен, что художники видели сей эффект.

Отвалилась трава, потому что она тоже росла только на старом террейне. Случайно кидалась точка, определялась высота, доминирующий тип террейна – рос кустик травки. Тормозило оно и требовало в явном виде информацию о сплаттинге и хейтмап. Этой информации в новом террейне рантайм у меня не было… Я радостно заборол тем, что расставлял кустики травки при билде оптимизованного террейна. И с помощью битовой магии сохранял эту информацию. Травка стала строиться быстрее, подлагивать перестала.

Потом я починил коллизии ( они тоже требовали карту высот! ). Починил весьма забавно – с помощью софтовой растеризации перегоняю свои треугольники в карту высот, но лишь в окрестности пользователя.

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

Потом я чинил много-много багов, чертыхаясь на то, что полез кодить, старый дурень, как 20 летний пацан.

Вроде, террейн не потерял в качестве. Местами получил overall прирост производительности в полтора раза. Был fps в игре 60, стал 90 ( на 7600 GT ). Ну или был 6, стал 10 ( это на 5200 FX ).  Профит.

Недавно пришел менеджер, принес ноутбук от Интела. Выяснилось, что мы должны идти на интегрированной графике. Я взял, запустил игру… И обнаружил, что новый террейн тормозит общий фреймрейт раза в 4. Находясь в состоянии шока, понял ( или вспомнил ), что софтверная обработка вершин ( которая работает в драйверах на железе от Интела ) целиком обрабатывает range, указанный в параметрах DIP. И такое случается на каждый DIP. Динамический же индексный буфер читал из статического вершинного в scattered манере, абсолютно непредсказуемым образом. Я стал материться – в принципе, проблема решалась полностью динамическим буфером вершин… Однако я написал чит, который локализует материалы в вершинном буфере, совершается больше DIP вызовов, но с более предсказуемыми границами. Активизируется чит на софтверной обработке вершин.

Попутно исправил все места в рендере, где границы DIP вызова указывались неточно ( неточно сверху ). Словил перезагрузки, BSOD и просто тривиальные мигания треугольников.

 Недавно тестер с каким-то там Radeon 2600 HD заявил, что у него новый террейн мигает. Я хотел было написать works for me. Но потом поглядел в флаги lock индексного буфера. Там был NOOVERWRITE…

 Вот такие пирожки с котятками.

  • http://zeux.livejournal.com/ Zeux

    > В нем прямоугольные равносторонние треугольники итеративно делятся точкой на середине гипотенузы
    Равнобедренные?

    За пост спасибо, забавно.

  • IronPeter

    Ага. Поправил.

  • tav

    Good work!

    Но непонятно только, почему прирост на 7600 GT и 5200 FX почти одинаков, т.к. оптимизации коснулись как я понял в основном DIP-ов/геометрии, т.е. DIP-ы и динамический индекс буфер – от GPU зависеть ничего не должно, тут шина/CPU больше влияет гораздо, да и кол-во тр-ков как бы тоже не должно на перфоманс карточки сильно влиять (если их не 3 ляма на кадр как в кризисе конечно).

  • Sergei_am

    >>помощью софтовой растеризации перегоняю свои треугольники в карту высот, но лишь в окрестности пользователя.
    А вдали, монстрики как бегают? Как сервер скажет?

    И сколько памяти въиграл, из-за картъ въсот – или она есть, просто не юзается для рендеринга?

  • IronPeter

    Сервер реплицирует объекты примерно в радиусе 100 метров, я гружу с запасом.

    Карта высот есть в примерно 4 кусках 256×256, окружающих нашу point of view. Выиграл пару-тройку сотен метров в игровом паке. И немного рантайм-памяти, ага.

    Вне этого участка монстрики бегают по точным серверным позициям, ага.

  • my.name

    спасиба

  • kas

    > На каждую вершину максимального разбиения надо хранить float
    флот это высота? не шорт потому что и так децл?

  • IronPeter

    >флот это высота? не шорт потому что и так децл?

    Я бы не сказал, что уж совсем децл. Вершин в сетке максимальной детализации в среднем всего в 3-5 раз меньше, чем точек в хейтмапе – при таком уровне сжатия разница на глаз незаметна, при большей компрессии лезут артефакты. Еще два байта на нормаль хранятся. Еще бы байтик, чтобы хранить “цену” вертекса ( т.е. расстояние, на котором его edge split активировать ).

    Не знаю, наверное можно и short для высоты. У нас range от -500 до +500 метров где-то, точности в пару сантиметров хватит.

  • kas

    > Не знаю, наверное можно и short для высоты. У нас range от -500 до +500 метров где-то, точности в пару сантиметров хватит.
    ну можно же пер пач хранить базовую, или прям внутри патча мегоперепады такие? ну в целом забавно, да. почти сподвигло какойто из свох терейнов оживить и запробывать всякое =)

  • IronPeter

    >Но непонятно только, почему прирост на 7600 GT и 5200 FX почти одинаков

    Я таки не написал внятно, почему все это затеял. Мотивации простые. У шейдера террейна много пинов. Гранулярность была 32×32 метра. Т.е. любое минорное пятнышко текстуры создает лишний слой в карте сплаттинга. Любой минорный бугорок форсирует более сложный шейдер, который текстурирует в зависимости от нормали. Пересечение двух слоев террейна на куске 32×32 метра форсирует шейдер с texkill. Если гранулярность рендер-куска меньше, то материал много проще в среднем.

    Треугольников стало меньше ( до этого момента картинка в wire frame была почти полностью забита линиями, не смотря на лоды ), меньше пикселей на краях треугольников.

  • IronPeter

    >ну можно же пер пач хранить базовую, или прям внутри патча мегоперепады такие.

    Мегоперепады. Т.е. Дизайнеры зачем-то делают вертикальные стенки в небо. И провалы вниз. Причем по непонятным мне причинам выбирают максимальный range. Дашь 10 километров – сделают 10 километров.

  • http://101gr.com/ GLoom

    > Причем по непонятным мне причинам выбирают максимальный range.

    Может тулзы определяют? То есть вдруг там кнопка есть выровнять по потолку-полу и они ей пользуются?