Секреты и трюки старых консолей и компьютеров
GameDev.ru / Сообщества / old_tricks / Статьи / Графическая архитектура Playstation 2

Графическая архитектура Playstation 2

Автор:

Playstation 2

Главный чип PS2, в котором находится центральный процессор и несколько сопроцессоров, называется Emotion Engine (EE).
Растеризатор полигонов находится в другом чипе - Graphic Synthesizer (GS) и одна из основных задач EE - накормить GS списком команд для отрисовки через 256-байтную FIFO-очередь команд (GIF).
Основные компоненты EE: центральный процессор (ЦП), векторные процессоры VPU0 и VPU1, декодер MPEG2, 10-канальный DMA-контроллер, системная шина на которой сидят 32 Мб основной RAM и прочие таймеры и порты ввода-вывода.

Центральный процессор
ЦП - 64-битный MIPS R5900 (MIPS III + ограниченная поддержка MIPS IV) на 299 МГц, кроме того добавлены кастомные целочисленные 128-битные SIMD-команды, причём для последних были увеличены до 128 бит (в норме 64-битные) регистры общего назначения MIPS III, что было "видно" только этим командам.
Имеет 16 Кб кеша инструкций, 8 Кб кеша данных и 16 Кб сверхбыстрой "scratchpad RAM". Последняя фактически была реализована по той же технологии, что и кеш, но прямо отображалась в адреса RAM, что даже позволяло использовать её в передачах DMA, так что она крайне рекомендовалась, как буфер для интенсивной обработки данных процессором.
ЦП так же имеет урезанный сопроцессор с плавающей точкой, которого лишили 64-битной точности (double), поэтому обрабатывать он может только 32-битные float-ы, а кроме этого он не поддерживает NaN и +/-Inf, так что его даже нельзя считать IEEE 754-совместимым.
В качестве еще одного сопроцессора к ЦП подключен блок векторых вычислений (VU) от VPU0, так что в норме VPU0 отключен, а его векторным блоком пользуется ЦП исполняя свой поток команд. Однако VPU0 может быть включен как самостоятельный процессор и тогда ЦП должен избегать пользоваться его векторными регистрами и командами.

VPU0 и VPU1
Каждый из VPU обладает 16-битным целочисленным ядром (шестнадцать 16-битных регистров общего назначения) для выполнения команд в собственном выделенном для себя банке памяти и векторным модулем (VU) осуществляющим операции над банком из 32-ух регистров хранящих четвёрки 32-битных float-ов (x,y,z,w).
Через DMA они могут получать/отдавать данные другим компонентам системы. Система команд у них LIW (long instruction word) повышенной параллельности - в 8 байтах кодируется две 4-байтных полукоманды, одна всегда работает с векторным модулем, другая же или с ним или со скалярными вещами.
VPU0 имеет всего 4Кб памяти программ и 4Кб памяти данных (пламенный привет RSP из N64!), VPU1 же имеет по 16Кб того и другого.
VPU1 всегда работает как самостоятельный процессор, а вот VPU0 может переходить в режим подчинения ЦП как сопроцессор - при этом некий усеченный набор команд ему даёт уже ЦП. Но даже в таком режиме ЦП может дать VPU0 команду CALL и тогда его ядро "проснётся", чтобы выполнить подпрограмму в собственном адресном пространстве и снова "уснуть". Кроме того ЦП может давать VPU0 команды LOAD/STORE для доступа к его памяти данных, так что 2x4Кб продолжают оставаться крайне полезными даже тут - VPU0 выступает не просто как сопроцессор, но сопроцессор с программами и данными, которые можно обновлять на лету.
VPU1 имеет дополнительно скалярный блок работы с 32-битными float-ами с экспоненциальными и тригонометрическими функциями для более гибкой обработки данных.
А главное - VPU1 напрямую подсоединён к FIFO-буферу GIF через который данные отправляются в видеочип GS. У VPU1 просто есть машинная команда засылающая 128-битный блок из его памяти данных в GIF.
В принципе данные в GIF может засылать и ЦП - уже посредством DMA-контроллера.
Вообще VPU подключены к системной шине через VIF (Vpu InterFace) - контроллеры достаточно сложные, принимающие-отдающие данные организованные в блоки команд/пакетов. VIF принимает, например, такие команды как "залить данные", "залить код", "запустить код", "дождаться выполнения кода", а так же способен распаковывать поступающие в VPU данные на лету - например распаковывать цвет в формате 1:5:5:5 в четверку float-ов, чтобы VU смог легко считать их потом из внутренней памяти VPU одной векторной командой.
Крайне интересной особенностью PS2 является то, что и VPU1 и ЦП+VPU0 могут засылать команды в GIF/GS одновременно! GIF заранее устроен как бы двухканальным на ввод - с одного (приоритетного) канала он принимает команды от VPU1, а с другого - то что присылается ему по шине через DMA (процессором).

Взаимодействие ЦП, VPU и GPU
И вот сказанное только что и отражает заложенную в пайплайн PS2 изначально разработчиками концепцию оптимального рендера.
По их генеральному плану ЦП должен был брать под свой контроль блок VU от VPU0, чтобы расширять свою систему команд блочной обработкой _необычных_ вершинных данных, которые требуют особого подхода и много логических переходов.
Я думаю понятно, что сюда попадает всякая скелетная анимация, развевающиеся на ветру флаги и всякое такое прочее.
Предполагалочь, что VPU1 в это самое время, вооруженный более крупными 2x16 килобайтами внутренней памяти, обрабатывает простые, массивные и блочные данные - например статичные массивы вершин под матрицами - стены лабиринта и так далее.
И первое и второе скармливались в GS _одновременно_. Вы спросите - а как же глобальные стейты между разными моделями? Если между командами отрисовки уровня от VU1 вдруг вклинивается команда отрисовки треугольника модели от ЦП+VU0, то что произойдёт, ведь у лабиринта и модели, очевидно, разные текстуры?
Так вот - GS отслеживал и понимал из какого из двух каналов данных пришла очередная команда и внутри себя хранил два независимых набора параметров рендера (render state)! Один брался когда приходили команды от VPU1, а другой - когда приходили команды от ЦП/VPU0 (из общей шины)!
Вот и весь фокус - переключение между этими двумя render-state-ами было абсолютно дешевым, скорее всего просто коммутация линий каких то внутренних шин данных, поэтому GS действительно мог безболезннено получать одновременно два независимых потока команд на отрисовку.

Разумеется разработчиков никто не заставлял рендерить всегда именно так - программно рендер мог быть настроен как угодно. Можно было вообще не пользоваться VPU и отправлять команды только процессором.
Можно было "отсоединить" VPU0 от процессора и всё-таки загрузить что-нибудь самостоятельное в его 4 Кб памяти инструкций (у Nintendo 64 с его RSP с точно такими же объёмами кода и данных же получалось!).
Можно было парой ЦП+VPU0 подгатавливать в предыдущем кадре "динамическую порцию" вершинных данных, а на следующем кадре просто линковать их с общим потоком данных для VPU1. И так далее...
Но основным запланированным режимом была параллельная подпитка GS из ЦП+VU0 командами "сложной" геометрии, в то время как из VU1 тут же лилась "простая/блочная" геометрия.

В общем как видно T&L в PS2 был навороченным и наскипидаренным синтезом подходов виденных нами ранее и в PS1 и в Nintendo 64.
В эту копилку еще надо добавить "цепочечный режим передачи DMA", который сильно расширяет способность DMA-канала в PS1 заливать в порт растеризатора данные в формате однонаправленного связного списка.
В PS2 аналогичный формат "связного списка" для DMA-передач уже имеет заголовок с типом текущего блока - и это один из 8 (восьми!) вариантов того как располагаются и что означают поля HEADER/DATA/NEXT. Например в одном варианте данные располагаются сразу после HEADER, а указатель NEXT указывает на следующий HEADER. В другом же формате данные лежат по адресу в NEXT, а следующий HEADER располагается сразу после текущего. При этом еще есть связь с подсписками - типы узлов CALL/RET, для создания древовидной структуры. Кроме того два DMA-канала могут быть завязаны в автоматический кольцевой буфер, когда один из них пишет в основную RAM, а другой из неё читает - такое называется MFIFO (Memory FIFO).

Graphic Synthesizer (GS)
GS является растеризатором уже прошедших T&L вершин.
Основные его возможности:
- обработка цвета в 32-битном RGBA
- рисование точек, линий, ломанных линий (line strips), треугольников, лент и полос из треугольников (triangle strips and fans), спрайтов
- заливка по Гуро
- текстуры с перспективной коррекцией и билинейным или трилинейным сглаживанием
- как 16/24/32-битные тестуры с прямым указанием цвета так и 4/8-битные текстуры с указанием цвета через CLUT (Color LookUp Table)
- Z-буфер с тремя возможными глубинами: 16, 24 или 32 бит
- альфаблендинг, антиалиасинг, попиксельный туман, обрезка по прямоугольнику (scissors), stencil
- 2 рендер-стейта (уже должно быть понятно почему)
Видеочип имеет 4 Мб выделенной Video RAM (VRAM) в которой в любом месте располагаются фреймбуфер, Z-буфер, текстуры и CLUT.
Кроме того у него есть банк 64-битных регистров с номерами от 0x00 до 0x62. Часть из них, представляющих render-state при отрисовке примитивов, существуют в двух копиях того самого переключаемого контекста. Некоторые представляют вещи не связанные с render-state-ом, например для инициации передачи битмапов между основной памятью и VRAM (в обоих направлениях) нужно произвести запись в определенные регистры, выставив параметры передачи и инициировав её. И еще одно подмножество регистров олицетворяет текущий рисуемый примитив - есть регистр его координат (фиксированная точка 12:4), текстурные и Z-координаты (следует понимать, что в GS всё это отправляется уже в экранных координатах - screenspace), рассчитанный цвет RGBA и т.п.
Часть регистров называются "привелигированными" и доступны для чтения/записи в адресном пространстве ЦП, это самые глобальные параметры - видеорежим, место в VRAM где лежит отображаемое на экране изображение и его формат и т.п. Но большая часть регистров доступна только через GIF.
VU1 и ЦП отправляют в GS через контроллер GIF команды в специальном формате. В сущности команды эти выполняют операции записи в регистры видеочипа.
В связи с тем, что данные в памяти VU как правило разложены в четверки 32-битных float (x,y,z,w), а VU1 рассматривается как основной поставщик команд, то и формат данных для GIF прогибается под это.
Итак, команды в GS заходят пачками/пакетами, предваряемые 16-байтным (128-битным) заголовком, хранящим количество и назначение последющих 16-байтных блоков.
Форматы этих пакетов столь замысловаты, что я их тут заколебаюсь описывать, поэтому я просто констатирую, что есть разные форматы передачи данных в регистры GPU: как универсальные так и уплотнённые "запакованные", особенно что касается передачи данных в регистры атрибутов и вершин примитива, но в целом суть сводится всегда к записи какого то значения в какой то регистр видеочипа.
Ключевым моментом для отрисовки примитивов является то, что ряд регистров описывает как бы атрибуты текущей вершины примитива, запись в них обновляет информацию о текущей вершине - типе примитива в который она входит, её цвете, текстурных координатах и т.п. и при этом запись в регистры содержащие информацию о сообственно координатах (XYZ) приводит к "проталкиванию" вершины во внутренний буфер примитивов, который GS и отрисовывает как только поступают новые данные заканчивающие описание примитива. Для треугольника нужно 3 точки, для стрипов - 2 затравочных начальных и каждая следующая генерирует новый треугольник и так далее.
Другими словами этот чип растеризации явно что-то знает про glBegin/glVertex/glEnd и чует моё сердце, что его ноги растут из одного, всем известного API. :) Хотя это не факт, но тем не менее.

Заключение

Вот и всё, что можно сказать обзорно-архитектурного про PS2 и её графический пайплайн. Машина получилась на удивление продуманной и замысловатой - количество всяческих форматов и контроллеров эти форматы разбирающие и гоняющие данные в них туда-сюда зашкаливает.
Emotion Engine получился довольно мощным переосмыслением идей T&L, которые мы видели как в PS1 так и в N64, причём со своими фишками и рюшечками, спаренными и неспаренными дополнительными процессорами, выделенными ОЗУ, шинами и кодерами/декодерами форматов.
Я уже не помню когда T&L появился в домашних видеокартах, но однозначно, что имея всё это на рынке годами просто невозможно было не прийти к тому же и в персоналках, благо PS2 побила и держит до сих пор рекород по количеству проданных консолей.

Изображение

22 ноября 2017

#64bit, #6generation

2001—2017 © GameDev.ru — Разработка игр