Deep DIP
Наверное, я уже надоел вам со своим nouveau, но в очередной раз не могу молчать..
Речь в посте пойдет про сокровенные неонки графической продукции от NVidia.
Предыстория ( для тех кто не читал предыдущие серии ) очень простая. Как-то я проходил мимо магазина М-Видео, там на витрине стояли черные консольки от Сони. Я нащупал кредитку в кармане и решил “надо брать”. Притащил домой, поставил линукс. А потом случайно обнаружил, что RSX под линуксом отлично живет и умеет 3D. Вопреки досужему мнению что, якобы, какой-то там гипервизор не пущает и не позволяет. Графическая железка очень похожа на NV40, а специальные люди с http://nouveau.freedesktop.org/wiki/ эту железку давно уже расхачили. Но не до конца, и место для подвига остается.
Что умеет графическая 3D железка? Она умеет рисовать треугольники. Был такой интерфейс OpenGL для трехмерной графики ( отдельные люди утверждают, что он и сейчас жив ), он тоже умел рисовать треугольники. Там было много способов сделать это, но самый старый и надежный способ – это сказать сначала glBegin(GL_TRIANGLES); потом 3 раза сказать glVertex(); потом сказать glEnd();
Так вот, в низкоуровневом интерфейсе к графической железке есть тот самый glBegin()/glEnd() блок. Такие специальные токены в командном буфере. Командный буфер – просто кусок памяти, куда складываются разные токены, а железка их асинхронно хавает. По DMA. Сказали мы glVertex4f(….), в блок положился специальный токен и данные. Получается хардверный OpenGL, дело доходит до смешного. Константы, которые задают тип примитива, разные GL_TRIANGLES, GL_QUADS, GL_LINES, GL_TRIANGLE_STRIP, GL_QUAD_STRIP – прямо и кладутся в командный буфер. Смешное продолжается тем, что у карточек от NVidia есть ограничение на число вершин внутри glBegin()/glEnd() блока, и это даже ограничение не OpenGL, а IrisGL.
Следующий исторически способ нарисовать треугольник в OpenGL – это сначала сказать gl*Pointer() на массивы вершинных данных, а потом много раз внутри glBegin()/glEnd() блока сказать glArrayElement. И такой способ поддерживается железкой. Есть шестнадцать что ли вершинных атрибутов, для каждого можно указать тип, число компонент, разницу в байтах между последовательными значениями атрибута, указатель на нулевой элемент.
С указателем на нулевой элемент случилась маленькая закавыка. Этот самый указатель – в абстрактном адресном пространстве видеокарточки, память нужно специально мапить. В железе есть два DMA канала, по которым оно умеет считывать вершинные данные. Для этих двух каналов нужно указать DMA объекты, которые определяют права доступа к памяти – начальный и конечный адрес куска памяти, права чтения и записи. Так вот, каждый указатель на нулевой элемент – это смещение в адресном пространстве GPU да еще битик, который говорит, использовать ли права доступа первого или второго канала вершинных данных. Почему каналов два? Cовершенно понятно - потому что есть системная и видеопамять. Зачем права доступа? Тоже совершенно понятно – чтобы в многопоточном приложении я случаем не прихватил кусок чужих данных. Какое-нить там стойкое крипто, которое считается CUDA, гы. Да и ловить выходы индексов за пределы массива тоже очень удобно. Прерыванием ( и специальные люди из Нуво расхачили даже это ).
У меня на RSX тоже есть два куска. Первый кусок - вся видеопамять целиком, на которую добрые программисты Сони дали доступ чтения/записи ( и за это я почти люблю тех безымянных героев ). Второй кусок – 20 мегабайт xdr памяти, заботливо отмапленные на графическую железку.
Ну так вот, в результате мы умеем рисовать индексированную геометрию индексами, которые складываются прямо в glBegin/glEnd блок, в командный буфер. А индексные буфера не умеем использовать. Точнее, люди из Нуво не умели.
Теперь самое время рассказать про техпроцесс расхачки. Есть специальная дампилка, которая умеет считывать командный буфер карточки после вызовов OpenGL драйвера. По просьбе специального человека мне дали возможность туда закоммитить тест с индексным буфером. Другие специальные люди запускают дампилку и посылают дампы, которые другой специальный человек выкладывает в специальном репозитарии http://people.freedesktop.org/~kmeyer/renouveau_dumps/ .
И вуаля – индексный буфер! Чтобы нарисовать индексированную геометрию надо сначала установить указатель на индексный буфер в регистр с номером 0x181c. Что надо делать дальше – вы не поверите. Внутри glBegin()/glEnd() пары надо говорить “нарисуй n примитивов начиная с такого-то смещения”. Причем число n не больше 255, это то самое прекрасное ограничение на число примитивов времен IrisGL. В результате вызовы DIP разбиваются на кусочки не больше 255 индексов, в таком виде и складываются в командный буфер. Внутри glBegin()/glEnd() пары. Технология ниппель. Через тернии к звездам.
На этом небольшом примере превосходно видно ( другой отличный пример – отжиг Семена про комбайнеры http://sim0nsays.livejournal.com/18587.html?nc=43) как развивается железо и интерфейсы. И как внутри современной железки притаились архитектурные решения 10, 15, 20 летней давности. Притаились, прикрылись слоями абстракции, археологическими наслоениями и множественными воркэраундами.