380 likes | 604 Views
Разработка системы шейдерных материалов. Андрей Аксенов shodan NOSPAM @ NOSPAM shodan.ru КРИ ’2005. Обозначения. шейдер конкретная GPU микропрограмма , загружаемая в память GPU константа передающаяся в шейдер параметр любое свойство материала ( в основном доступное дизайнеру).
E N D
Разработка системы шейдерных материалов Андрей Аксенов shodanNOSPAM@NOSPAMshodan.ru КРИ’2005
Обозначения • шейдер • конкретная GPU микропрограмма, загружаемая в память GPU • константа • передающаяся в шейдер • параметр • любоесвойство материала (в основном доступное дизайнеру)
Обозначения (2) • текстура • один из видов параметра • прешейдер • формула вычисления константыиз параметровматериала и сцены • пин • переменная времени компиляции, влияющая на выбор конкретного шейдера
Обозначения (3) • привязка (биндинг) • процесс назнчения текстур в текстурные стейджи, заливки шейдерных констант, итп • материал • набор шейдеров и параметров (общая статическая таблица для всех объектов класса и агрегированные значения параметров в объектах)
2002-й год… Это была тестовая карта!
Начальный проект шейдинга • проходы • shadow map • diffuse / specular (основной) • EM / EMBM (опционально) • пины (разбиения шейдеров) • static / skinned ( 1/2/3/4 кости ) • NV / ATI ( наличие SM hack ) • EM / EMBM
Эволюцияшейдинга • дополнительные пины • тип тумана ( range / height ) • коррекция блендинга для тумана • итд итп • небольшой комбинаторный взрыв • ~150 шейдеров • 3..5 факторов для выбора шейдера • 8 разных мест для ручной регистрации каждого шейдера
Эволюцияшейдинга (2) • единый общий класс материала • 5 текстур • ~15 параметров • сложности управления • неочевидные факторы и сложная функцияпривязки материала (пример: (pBump!=NULL) ) • трудоемкость дальнейшего разбиения шейдеров
Расширение единого класса? • означает добавление параметров • как минимум 8семантик текстур • как минимум 20-30 параметров • одновременно не используются • означает еще большую сложность • функция привязки более 1000 строк • еще больше заполняемых вручную таблиц выбора шейдеров…
Нужна система материалов! • система материалов? • некоторый общий (для нескольких проектов) код, которыймаксимально автоматизирует создание новых видов шейдерных материалов и их внедрение в проекты
Требования к системе • простота создания новых материалов • как минимум, регистрация нового материала не более, чем в 1-2 местах • как максимум, загрузка на лету • простота создания новых пинов • иначе добавлять к существующим материалам функциональность слишком трудоемко
Требования к системе (2) • простота создания новых shading path и проходов внутри shading path • shading path отличается от пина тем, что влияет на число проходов и привязку каждого прохода • простота создания прешейдеров • в хардкоде “прешейдер” задавался в функции привязки, отдельно от материала - неудобно
Требования к системе (3) • менеджмент упакованных и композитных текстур • для облегчения работы дизайнеров и минимизации человеческих ошибок • автоматическая упаковка в DXT • автоматическое смешивание каналов исходных текстур (не используемых напрямую) в одну, используемую шейдером ( bump.rgb + gloss.a )
Почему не D3DXEffect? • проблема черного ящика • а точнее, имеющегося качества и скоростиподдержки… • проблемы кодогенерации FXC • недостаточно хорошая оптимизация • недостаточно гибкая привязка констант • полное отсутствие контроля за прешейдерами
Почему не D3DXEffect? (2) • проблема сортировки по текстурам и шейдерам • как сделать эффективно? • проблема сопряжения со специфичными типами данных • контроллеры анимации • композитные текстуры • решаемо посредством оберток, но нет гарантий эффективности
Архитектура системы • class FXMaterial_c • базовый абстрактный интерфейс • реализация общей функциональности • конкретные реализацииматериалов • class FXMaterial_Diffuse_c : public FXMaterial_c • агрегирует конкретные параметры • реализует функции привязки
Архитектура системы (2) • препроцессор спецификаций FXPP (FX Preprocessor) • генерирует весь C++ код реализации материала ( FXMaterial_Diffuse_c ) • в том числе, заранее компилирует все возможные уникальные HW шейдера, необходимые данному виду материала • использует спецификацию материала
Блок спецификации material { // пины compilevar USE_BONES; compilevar USE_SHADOW; compilevar USE_SHADOW4SAMPLE; compilevar USE_FOG_TYPE; compilevar USE_OMNIGROUPS; // параметры материала texmap texDiffuse { uiname="Diffuse map"; } uivar color3 cLuminosity; uivar color3 cDiffuse; uivar float fShadowTransp; uivar texgen2 tgDiffuse; // ...
Блок спецификации (2) path default { pass 0 { stage 0 = scene.ShadowMap; stage 3 = texDiffuse; float4 vs.g_cAmbiLumi = mat.cLuminosity + mat.cDiffuse*scene.cSunAmbient; float4 vs.g_cTotalDiffuse = mat.cDiffuse*scene.cSunDiffuse; float4 vs.g_cMatDiffuse = mat.cDiffuse; float4 vs.g_tgDiffuse0 = mat.tgDiffuse.GetXform(0); float4 vs.g_tgDiffuse1 = mat.tgDiffuse.GetXform(1); float4 ps.g_cShadowTransp = { 0, 0, 1-scene.fShadowTransp*mat.fShadowTransp/2.0f, scene.fShadowTransp*mat.fShadowTransp/2.0f }; float4 ps.g_cAlpha = { 0, 0, 0, frag.iAlpha/255.0f }; } }
Задание шейдеров • по имени (жесткая схема именования) • либо ассемблер, либо HLSL • общий код через # include • возможность копирования реализации // HLSL LitVertex_t vs_default_pass0 ( SkinVertex_t IN ) { ... } // asm VertexShader vs_default_pass0 = asm { ... } // implementation copy VertexShader vs_r9700_pass0 = vs_default_pass0;
Препроцессинг FX • разбор спецификации • компиляция всех возможных шейдеров • варьируем значения пинов • привязываем константы: • uniform float4 g_cDiffuse : register ( c13 ); • #define VS_g_cDiffuse c13 • отсев дублирующихся шейдеров • генерирование C++ кода материалов • генерирование общего C++ кода
C++ код материала • бинарные данные шейдеров • таблица шейдеров, ее инициализация • дескриптор параметров • функция привязки шейдеров • выборка по таблице в зависимости от shading path, номера прохода и значений пинов • функции привязки констант • в зависимости от shading path, прохода, пинов • разделение на per-material / per-instance
Общий C++ код • списки (enum) ID материалов • константы ( FX_PINS_TOTAL итп ) • функция создания материала по ID • декларации классов • инициализация • запросы к дескрипторам материалов по ID • union FXInstanceFlags_u содержащийпины в виде битовых полей
Функциональность FXMaterial_c • общие параметры ( альфареф, флаги,… ) • бинарный и текстовый сериализаторы • управление текстурами, в том числе упакованными и композитными • управление значениями параметров • привязка рендерстейтов • поддерживающие функции ( GetFlags, operator =, ит.п. )
Дескриптор параметров для каждого параметра заданы: • тип ( eg. COLOR ) • UI имя ( eg. “Diffuse color” ) • техническое имя ( eg. “cDiffuse” ) • смещение в базовом классе • флаг доступности дизайнеру через UI • диапазон возможных значений ( для UI )
Как решены проблемы D3DX? • проблемы поддержки нету • проблемы оптимизации – обходим, используя ассемблерные шейдеры • проблемы привязки – решаем, привязывая константы из FXPP • проблему прешейдеров – решаем, генерируя C++ код привязки констант • который в итоге компилируется в инлайновые SSE инструкции
Как решены проблемы D3DX?(2) • cортировка – по следующему 64-битному ключу: struct { DWORD m_uMesh : LOG2<MAX_MESHES>::Value; DWORD m_uPins : FX_PINS_TOTAL; DWORD m_uMaterial : LOG2<FXMAT_TOTAL>::Value; DWORD m_uTexture : LOG2<MAX_TEXTURES>::Value; }; __int64 m_uValue; • порядок полей определяет приоритеты сортировки
Специальные типы параметров на примере композитных текстур: • введен подтип у типа параметра FXPARAM_TEXTURE • введено два новых ключевых слова compositemap, submap в спецификацию • доработана функция SetMap • написаны функции обновления и сохранения Итого: 1-2 рабочих дняна сложный (!) тип.
Известные проблемы • отсутствие (пока) разных типов инстанса • для разных моделей нужная разная per-instance информация • примеры – fading, instancing • отсутствие (пока) удобных деклараций для сложных массивов констант • примеры – skinning, упаковка групп источников света • дублирование кода между некоторыми видами материалов ( Diffuse, DiffuseEnv )
Итоги • добавление новых (фактически, Иных) материалов –сводится к созданию FX-файла в директории с материалами • конечно, неплохо бы еще и в dependencies прописать… • добавление новых пинов – сводится к добавлению одной строчки в FXPP • например, добавление нового типа тумана или портирование всех шейдеров на ps_1_4
Итоги (2) • разработка окупается • на разработку ушло МНОГО времени • на добавление в хардкод и отладку одних только автоматически добавленных в новой системы разбиений по пинам –ушло бы времясравнимого порядка • стоит ли писать свою систему? • вопрос неоднозначный! • если не устраивает хардкод (много шейдеров) • если не устраивает D3DX
Занимательная нумерология • 45-50 дней по трекеру, планировалось 15 • 11 видов материалов, 89 килобайт FX • всего компилируется 2842 варианта HW шейдеров, из них 532 уникальны • компиляция длится 75 секунд • генерируется 75809 строк C++,3298 KB • за вычетом собственнобинарных шейдеров, остается 5239 строк, или 328 KB • FXPP это 2268 строккода, или 61 KB, и самое страшное…
Занимательная нумерология (2) • FXPP написан на Perl. # store original line numbers $l = 0; $fx =~ s/^/++$l.":"/gems; # remove /*...*/ comments $fx =~ s/\/\*.*?\*\///gms; # remove // comments $fx =~ s/^((([^\/"\n])|("[^"]*"))*)\/\/.*$/\1/gm; # ... # build material types list $i = 0; print OUT "enum FXMaterial_e\n{\n", join ( ",\n", map { uc "\tFXMAT_$_ = " . $i++ } @g_fx_names ), ",\n\tFXMAT_TOTAL\n};\n\n";