Обзор графической архитектуры 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 могут работать параллельно ЦП и заточены под обработку больших массивов векторных данных.
Каждый из VPU обладает 16–битным целочисленным ядром (шестнадцать 16–битных регистров общего назначения) для выполнения команд в собственном выделенном для себя банке памяти и векторным модулем (VU) осуществляющим операции над банком из 32–ух регистров хранящих четвёрки 32–битных вещественных (float) — как бы математические векторы (x,y,z,w).
Через DMA они могут получать/отдавать данные другим компонентам системы. Система команд у них LIW (long instruction word) повышенной параллельности — в 8 байтах кодируется две 4–байтных полукоманды, одна всегда работает с векторным модулем, другая же или с ним или со скалярными/целочисленными операциями.
VPU0 имеет всего 4Кб памяти программ и 4Кб памяти данных (пламенный привет RSP из Nintendo 64!), VPU1 же имеет по 16Кб того и другого.
VPU1 всегда работает как самостоятельный процессор, а вот VPU0 может переходить в режим подчинения ЦП как сопроцессор — при этом некий усеченный набор команд ему даёт уже ЦП поставляя их из своего потока выполнения. Но даже в таком режиме ЦП может дать VPU0 команду CALL (вызов подпрограммы) и тогда его ядро «проснётся», чтобы выполнить эту подпрограмму в собственном адресном пространстве и снова «уснуть». Кроме того ЦП может давать VPU0 команды LOAD/STORE для доступа к его памяти данных, так что 2x4Кб продолжают оставаться крайне полезными даже тут — VPU0 выступает не просто как сопроцессор, но сопроцессор с программами и данными, которые можно обновлять на лету.
VPU1 имеет дополнительно скалярный блок работы с 32–битными вещественными с экспоненциальными и тригонометрическими функциями для более гибкой обработки данных.
А главное — VPU1 напрямую подсоединён к FIFO–буферу GIF через который данные отправляются в видеочип GS. У VPU1 есть машинная команда засылающая 128–битный блок из его памяти данных в GIF.
В принципе данные в GIF может засылать и ЦП, но уже посредством DMA–контроллера.
Вообще VPU подключены к системной шине через VIF (Vpu InterFace) — контроллеры достаточно сложные, принимающие–отдающие данные организованные в блоки команд/пакетов. VIF принимает, например, такие команды как «залить данные» (в VPU), «залить код», «запустить код», «дождаться выполнения кода», а так же способен распаковывать поступающие в VPU данные на лету — например распаковывать цвет в целочисленном формате 1:5:5:5 в четверку вещественных чисел, чтобы 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 точки, для «лент (strips)» — 2 затравочных начальных и каждая следующая генерирует новый треугольник и так далее.
Другими словами этот чип растеризации явно создан под влиянием OpenGL и с большой степенью совместимости с его интерфейсом команд glBegin/glVertex/glEnd (хотя и SDK самой Sony для PS2, насколько мне известно, OpenGL не использовало).

Заключение

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



4 комментария

avatar
Всё, больше обзоров на консольные 32/64 бита у меня нет и вряд ли будут новые — этими двумя «золотыми» поколениями я лично свой интерес удовлетворил.
В следующих статьях вернусь к 16 битам — обзор на Sega Mega Drive и SNES, плюс некоторые трюки на первой и больная мозоль многих холиваров — сравнение того и другого «кто мощнее слон или кит». :)
avatar
Что забавно, PS2 уже считается ретро-платформой. Из поколения до 2000-х ещё интересен Dreamcast, любительская разработка для него относительно доступна, там выходит немало крутых homebrew-проектов (или даже можно назвать их инди).

К слову, на PS2 тоже применялась куча весьма интересных трюков. Например, в MGS (игра на последней картинке) есть блестящие, отражаюшие в себе всю обстановку полы. Как они сделаны: коридор скопирован и перевёрнут по линии пола, с пониженной детализацией, также перевёрнуты и отрисованы ниже линии пола модели персонажей, а сам пол сделан полупрозрачной текстурой, она затеняет нижнюю копию, и создаётся эффект отражения. И там много подобных маленьких трюков, в результате игра выглядит весьма круто для тех лет (версия на PS2 лучшая, во всех портах и римейках много чего урезано или испорчено) и при этом идёт на 60 FPS.
avatar
если уж брать приставки, то мне как амижнику интересно почитать про amiga cd32 и/или cdtv.
avatar
Логика моих изысканий была в том, что бралось из консолей что постарше и что было в широком ходу. 3DO как исключение просто потому что хотелось понять где и как впервые вылезла 3D-графика в играх. Но про амиги есть ровно одна статья, которую сейчас выложу.
Только зарегистрированные и авторизованные пользователи могут оставлять комментарии.