Гранулярность рендер-стейтов и черная прелесть.

Потихоньку пишу на черной прелести. Модельки грузятся, шейдеры компилируются, стейты устанавливаются. С нехитрым интерфейсом a-la OpenGL. Нехитрый интерфейс в стиле OpenGL кажется неудобным и устаревшим. Видать, время поспело, надо думать про высокий уровень. Сначала про обертки над рендером, а потом и про “3D сцену.”

Касательно высокого уровня рендера – очень сооблазнительной кажется концепция state blocks a’la DX10. Сначала краткий overview низкого уровня в RSX ( читай NV40 ), я примерно расскажу, сколько занимает описание каждого графического объекта в командном буфере видеокарточки.

Текстура. Собственно, на уровне командного буфера нет отдельного понятия текстуры. Есть настройки, уникальные для каждой текстурной стадии. Чтобы привязать “текстуру X” к текстурной стадии “Y” – надо положить в командный буфер последовательно: смещение в памяти, формат, настройки врап моды, флажок “enable”, фильтрация, свиззлинг, размер, цвет границы текстуры, мелочевка. Итого десяток dwords. Здесь и далее, под dword понимается значение, которое надо залить во внутренний регистр, реальный размер пакета в командном буфере может быть больше ( до двух раз ).

Пиксельный шейдер – тонюсенькая штучка. Два dwords. Указатель на начало шейдера ( конец помечен специальным стоп-битом ). И число временных регистров.

Вершинный шейдер. Ужасный монстр. В командный буфер помещается целиком, тупо прямыми данными. Каждая команда ( что примерно соответствует одному слоту в VS_3_0 ) занимает 4 dwords. На самом деле команды копируются в некоторый внутренний буфер. Насколько я понимаю, можно залить несколько шейдеров, сколько влезет во внутренний буфер. И переключать уже тонкой командой.

Рендерстейты в принципе достаточно тонкие. Вся инфа про блендинг занимает 3 пакованых dword. Вся инфа про альфа-тест – 2 dword. Полная установка Z и stencil тестов тоже совсем мало места занимает.

Геометрия. Бррр! Есть 16 стримов. Для каждого стрима два dword. Первый содержит пакованые формат, число компонент, страйд. Второй dword содержит смещение от начала памяти до начала данного стрима. Самым странным для меня было то, что каждый вершинный атрибут задается отдельным стримом. Нет отдельного смещения, которое бы я мог подвигать, чтобы установить start vertex или смещение сразу для группы вершинных атрибутов. Возможно, я что-то не понял, но выглядит оно именно так. Итого расходы – 2 dwords на каждый вершинный атрибут. На самом деле я имею одно глобальное смещение, которым могу рулить – это параметры активного DMA объекта для вершинных данных. Но так рулить мне почему-то совсем не хочется.

DIP. Информация занимает достаточно много места в командном буфере. Несколько dwords + dword на каждые 256 примитивов.

Итак, что хочется сделать. Хочется сделать immutable объекты, которые создаются по пользовательской информации. Каждый объект – прекомпилированный кусочек командного буфера. Идентификатором пусть служит uint16, вполне хватит. За счет хеширования не бывает двух одинаковых по содержанию кусков с разными идентификаторами. Впрочем, для какой-то пользовательской информации хочется создавать уникальные куски без шаринга.

Хочется полное состояние игрового рендера описать примерно таким набором идентификаторов:

[code]
struct FullRenderState
{
uint16 textureIDs[8]; // хватит!
uint16 depthStencilStateID;
uint16 blendAlphaStateID;
uint16 geometryID; // полная информация о вершинных буферах и декларации, включая смещение от начала видеопамяти...
uint16 indexID; // индексный буфер
uint16 vertexShaderID;
uint16 pixelShaderID;
uint16 userID; // пользовательский кусок командного буфера, для разной анимированой фигни.
// Вроде анимированного параметра alpha ref. И констант вершинного шейдера. Да и dip сюда можно засунуть.
uint16 unusedButVeryImportant;
};
[/code]

Любой рендер-объект должен содержать информацию, как именно его отрисовывать, пусть это будет массив этих самых FullRenderState’s. Массив пусть будет тупой, без всякой виртуальности в стиле virtual void Render();

Кажется, что данный набор идентификаторов можно сделать общим. Скажем, для PC-рендера и для RSX-рендера. Система сортировки и обработки рендер-запросов может работать со структурками, содержащими uint16 идентификаторы ( в одном случае ей удобнее работать на spu, но это не суть важно ). В PC случае появится еще один уровень индирекции. Как обычно. И, как обычно, основная работа переместится в userID.

Но мне чем-то нравится.

  • Pumba

    Что-то похожее пробовал делать на ПС2. Эфекты компилировались один раз в DMA/VIF команды и тупо лежало в памяти. А когда что то рендерилось то делал dmaref или часть копировал в chain если блок зависит от данных. Соответственно user data либо шла в этот копируемый кусочек или если например кости, то как dmaref в VU память.
    Что имеем в выйгрыше, заменяем код который собирает большой кусок командного буфера, на более легкую версию, когда precompiled кусок тупо копируется/референсится и по офсетам меняем данные пользователя. Как-то так.

  • Balmer

    А вертексные и пиксельные константы куда? Они как бы тоже render state.

  • IronPeter

    Константы вершинного шейдера – они прямые данные в push_buffer. Хранилище констант вершинного шейдера переживает смены шейдера. c0 в одном шейдере – это c0 в другом, к чему мы и привыкли.
    На PC DX9 рендере я сделал специальную сущность ( подсмотрев в DX10 ) – VertexShaderConstantBuffer. Которая задает range констант. Одинаковые буфера шарятся и за счет этого константы не устанавливаются дважды. Вершинные константы на RSX можно сделать так же. Lock от буфера выдаст тебе прямо его внутреннее содержимое. Которое лежит в прекомпилированном шматке push buffer.
    С пиксельными шейдерами сложнее. Там константы зашиты прямо в микрокод. Надо патчить микрокод. Внятных общих интерфейсов предложить не могу :). Пиксельные шейдеры тебе придется размножать в памяти, ежели ты константы хочешь менять. Оно грустно.

  • lordmaze

    he he :)

    mi takhe delali na Wii. Te pri loadinge, dannie loadyatsya v pamyat uzhe v sostoyanni prigodnom dlya rendera, a indexes, draw calls, change states, etc – sobirautsya v command buffers kotorie potom i suutsya c gpu cherez precompiled display list.

    v obschem horoshaya ideya.