четверг, 13 ноября 2008 г.

Lod'ы шейдеров в Direct3D10 и отсутствие FFP

Привел от рисовку объектов при Direct3D10 рендере в порядок. Теперь они не исчезают при большом расстоянии от камеры.
В движке есть понятие Lod для шейдеров. С каким-то шагом можно задавать более сложные шейдеры для объектов. Это дает прирост производительность на старом железе(а может и на новом). К примеру возьмём дерево, которое анимируется на ветру вершинным шейдером, в шейдере есть тригонометрическая функция для нее генерируется достаточно много инструкций, очевидно дерево можно не анимировать с очень большого расстояния, в этом случае включится простой шейдер, просто трансформирующий вершину. Другой пример - нет смысла делать эффект бампа и освещения на большом расстоняии - можно ограничится диффузионным освещением или вообще без него. Конечно переключение возможно будет заметно. Аналогичная ситуация с анимацией травы. Можно сделать лоды для скелетной анимации. Но там более сложные параметры.
В случае OpenGL/Direct3D9, если не будет найден подходящий лод, то будет выставлены NULL шейдеры, т.к. для них есть фиксированный конвейер то можно вызвать IDirect3DDevice9::SetTransform(world) и glLoadMatrixf(worldViewMatrix). Для Direct3D10 в этом случае не будет ничего рисоваться. Я решил эту проблему таким образом. Для каждого формата вершин создал список шейдеров по умолчанию. В случае выставления NULL шейдера, будет браться шейдер по умолчанию, если есть вершинный буфер.
Это простое и удобное решение. Не нужно дополнительных шейдеров, тем более шейдера по умолчанию простые и короткие, поэтому их код хранится прямо в реализации класса рендера движка. Для шейдеров с альфа тестом, есть дополнительный список шейдеров по умолчанию для каждого формата, само собой с параметром - текущий AlphaRef, тут имеется ввиду значение, передаваемое в IDirect3DDevice9::SetRenderState(D3DRS_ALPHAREF, AlphaRef) в Direct3D9 и в glAlphaFunc(func, AlphaRef) в OpenGL. В Direct3D10 убран альфа тест из FFP, поэтому нужен такой параметр по которому делается discard.
Таким образом объекты перестали исчезать.

понедельник, 10 ноября 2008 г.

Добавлен встроенный профайл

Добавил в движок встроенный профайл. По очень простой схеме,
некий класс содержащий данные о текущей строке имени файла и имени функции. Этот класс имеет конструктор с такими параметрами. С помощью макроса
#define PROFILE Profile::ProfileData data(__FILE__, __LINE__, __FUNCTION__);
можно в начале любой функции добавлять отсчёт времени. В конструкторе запоминается стартовое время работы. В деструкторе формируется строка в виде "File, line, function, time"
и рассчитывается время работы функции, данные добавляются с список. Все банально и просто. Нужно ещё решить как-то проблему мигания значение цифр, что происходит из-за разброса значений времени.

четверг, 6 ноября 2008 г.

Объединение одинаковых пересекающихся объектов на сцене.

При сильно загруженной сцене бывает очень много вызовов от рисовок геометрии, всем известными под названием DIP (IDirect3DDevice9::DrawIndexedPrimitive для Direct3D9, glDrawElements для OpenGL и ID3D10Device::DrawIndexed для Direct3D10). Большое количество таких вызовов может нанести серьезный удар по быстродействию рендера, особенно это более актуально для Direct3D 9, именно в в момент вызова идет проверка на валидность пиксельного и вершинного шейдера, правильная линовка данных передаваемых из вершинного в пиксельный, проверка вершинных деклараций на соответствие того что использует вершинный шейдер и что находится в вершинном буфере, так-же индексы вершин и остальные параметры. В OpenGL все намного проще, после выполнения каждой функции может возникнуть ошибка. Таким образов время как-бы равномерно распределяется между всеми вызовами функция перед вызовом от рисовки.
Ну в Direct3D 10 DIP cost значительно снижен.
Так-же частые вызовы может серьёзно нагрузить CPU по вызовам функций. Обычно до 1000 DIP на кадр ещё не все так плохо.
Итак при загрузки сцены я сделал некую простейшую оптимизацию, ищем объекты с одинаковы мешами и если они пересекаются своими ограничивающими боксами, собираем их в 1 мешь с учтя матрицу трансформации. Что это дает?
1) уменьшение нагрузки на CPU при обходе Scene Graph и при сортировке объектов;
2) уменьшение DIP cost при отриcовке в Depth текстуру для теней;
3) уменьшение DIP cost при финальной от рисовке объектов;
Минусы в том что будет небольшой перерасход памяти. Но если к примеру у меня на сцене дерево ели имеет 350 полигонов и их несколько сотен, то объедение по 3-4 в 1 мешь будут значительно сокращать DIP cost. Пока тесты особых положительных результатов не дали но число DIP и число объектов в кадре значительно сокращены в грубом приближении на 30% в среднем. Повышение FPS вроде не более 10%. Буду тестировать на более слабом железе. На будущее конечно нужно подумать об использовании Instansing'а. Железо сейчас c такой функциональностью уже достаточно доступно и инстансинг есть уже в 3 API в том числе и поддержка у ATI для OpenGL.