суббота, 17 января 2009 г.

Передача констант в шейдеры.

Всем известно что передавать константы лучше все сразу, а не по отдельности, либо минимизировать эти вызовы.
Достичь это можно несколькими путями и по разному для каждого API. Опишу отдельно для Direct3D9, Direct3D10, OpenGL.
Для Direct3D9 лучше Не использовать ID3DXEffect для шейдеров, не использовать ID3DXConstantTable для установки констант. Они не несомненно удобны но не очень быстрые в работе. Почему сейчас расскажу.
Возьмем любой метод ID3DXEffect/ID3DXConstantTable для установки констант. Все они имеют параметр D3DXHANDLE - это либо строка либо указатель возвращаемый
другими методами. Напрашивается вопрос как значение этого параметра внутри распознается. Внутри это может быть либо свой указатель либо строка с именем константы.
Что конкретно определяется проверкой установленного старшего бита параметра, если установлен то HANDLE, если нет то строка. Таким образом, мы имеем некие потери при установки константы. При чем внутри таких вызовов будут еще и проверки. Если это строка то будет поиск по строке, пусть даже через Hash. В случае с указателем чуть быстрее. Итого, представив много шейдера на сложной сцене, с большим числом констант, которые еще что хуже всего передаются по имени через строку.
В данных интерфейсах есть средства для засылки констант группой, за справкой отсылаю к DirectX SDK. Но нет средств что-бы заслать все константы для всего шейдера разом, что по идее должно быть идеально. Что бы слать все, лучше использовать напрямую интерфейсы IDirect3DPixelShader9/IDirect3DVeretxShader9 и использовать методы:
IDirect3DDevice9::SetPixelShaderConstant*/IDirect3DDevice9::SetVertexShaderConstant*
при таком подходе можно заслать единообразно все константы определенного типа за 1 вызов начиная со стартового регистра. Причем ID3DXConstantTable/ID3DXEffect являются wrapper'ами над этими методами.
Для Direct3D10 все намного проще. Есть константные буферы которые и предназначены для передачи группы констант. Кроме того данная функциональность есть в ID3D10Effect интерфейсе.
К сожалению в OpenGL с этим немного похуже ситуация. В расширениях GL_ARB_fragment_program/GL_ARB_vertex_program есть функция glProgramLocalParameter4fvARB, но она не позволяет слать больше 4 float(1 регистр).
Можно использовать для матриц константы GL_MATRIX0_ARB - GL_MATRIX31_ARB
и передавать через glMatrixMode это чуть улучшает ситуацию. Недавно я использовал новое расшерение GL_EXT_gpu_program_parameters оно позовляет засылать константы пачками. Причем это прямой аналог передачи констант для Direct3D9. Радует поддержка расширения на ATI.
Для GLSL передавать все константы для шейдера за 1 вызов не получится. Потому что нет у порядочности хранения uniform переменных. И функции для передачи параметров могут передавать либо матрицу либо vector4 и т.д. Можно передать матрицы 1 вызовом но их нужно хранить в массиве. Но с появлением расширения GL_EXT_bindable_uniform ситуация улучшилась но оно поддерживается пока только nVidia картами.