image

Sony PlayStation



История PlayStation началась в 1988 году, когда Nintendo и Sony приступили к совместной работе над дополнительным устройством чтения CD-ROM для консоли SNES. По условиям договора Sony могла независимо разрабатывать игры для этой платформы и сохраняла контроль над форматом «Super Disc» — две необычные уступки со стороны Nintendo.

Проект развивался до выставки CES ’91, на которой Sony объявила о совместном работе над «Play Station». На следующий день в рамках той же выставки Nintendo, к удивлению Sony, объявила, что вместо этого заключила партнёрское соглашение с Philips (на гораздо более выгодных условиях). Преданная и публично униженная Sony попыталась обратиться к совету директоров Sega, сразу же отклонившему эту идею. В интервью 2013 года генеральный директор SEGA Том Калинске вспоминал о решении совета.

«Это глупая идея, Sony не знает, как разрабатывать оборудование. Они не знают и как писать ПО. Зачем нам с ними связываться?» — совет директоров SEGA

И они не ошибались, у Sony действительно было мало опыта в работе с играми. Она почти и не проявляла к ним интереса, за исключением инициативы одного человека — Кена Кутараги. С того самого момента, как он увидел, как его дочь играет в Nintendo Famicom, Кен убеждал Sony, что нужно выйти на этот рынок. Несмотря на рекомендации вице-президентов Sony, он даже разработал для Nintendo аудиочип (SPC700), использованный в SNES.

Большинство руководителей Sony посчитало это рискованной ставкой, но Кутараги обрёл поддержку в лице генерального руководителя Sony Норио Ога. В июне 1992 года Кену позволили начать с нуля создание игровой системы. Для успокоения совета директоров «отца PlayStation», как его стали называть позже, перевели в финансово независимую от родительской компании Sony Music, и он приступил к работе над тем, что со временем превратится в «PlayStation» (уже без пробела в названии).

Изначально разработчики не могли решить, должна ли архитектура делать упор на спрайтовую 2D-графику или полигональную 3D-графику. Однако успех выпущенной в октябре 1993 года компанией Sega на японских аркадных автоматах игры Virtua Fighter уничтожил все сомнения: PSX пойдёт по пути 3D.

Кульминация проекта наступила два года спустя, когда 3 декабря 1994 года была создана Sony Computer Entertainment и консоль выпустили в Японии. Она обрела мгновенный успех и продалась за первый день тиражом 100 тысяч устройств, 2 миллиона устройств спустя шесть месяцев, и 102 миллиона устройств на протяжении всего срока жизни.


Sony PSX (PS1, PlayStation). Фото: Википедия

Архитектура



Сердцем машины является 32-битный процессор RISC R3000A производства компании MIPS с частотой 33,8688 МГц[3] в сочетании с 2 МБ DRAM. Примечательно, что этот чип также имеет Motion Decoder (MDEC), обеспечивающий воспроизведение видео с разрешением 320x240 при частоте 30 fps. Рядом со MDEC мы видим сопроцессор Geometry Transform Engine (GTE), используемый для выполнения быстрых математических операций с фиксированной запятой над векторами и матрицами. Система в целом неспособна работать с числами с плавающей запятой.

GPU представляет собой «чёрный ящик», управляемый центральным процессором при помощи «команд». Он имеет 1 МБ VRAM, недоступный для ЦП. Во многом его работа напоминает работу замечательного Immediate mode OpenGL: он отрисовывает текстурированные полигоны, которые можно «затенять» при помощи цветов вершин. Графический конвейер фиксированный и его нельзя программировать. Z-буфера (буфера глубин) нет.

Звуковой чипа Sound Processing Unit (SPU), как и GPU, является «чёрным ящиком». Он может обрабатывать 24 канала, имеет 512 КБ SRAM для хранения аудио (в формате ADPCM) и способен микшировать аудиодорожки CD-ROM со своими звуковыми каналами. Аббревиатура SRAM означает не «Static RAM», а «Sound RAM».

Самым смелым решением инженеров Sony стал выбор носителя данных. Они выбрали не картриджи, а модуль CD-ROM двойной скорости, что снизило стоимость игр и значительно повысило доступный объём (примерно до 650 МБ). Недостатками стали низкая скорость передачи (300 КБ/с) и чудовищно большое время установки головки (300 мс).

Блиттера в консоли нет. Модель программирования машины заключалась в том, чтобы разработчики не касались «голого» железа. Однако присутствует контроллер DMA для передачи данных из CD/DRAM во VRAM/SRAM.

Видеосистема


Несмотря на то, что видеосистема поддерживает 24-битный цвет, очень немногие игры использовали его (исключением являются фоны Heart Of Darkness [4]). На практике можно с чистой совестью сказать, что в большинстве игр использовались 16-битные цвета с 1-битной маской.



Цветовое пространство PSX с глубиной 15 бит на пиксель

Потрясающей особенностью видеосистемы является организация VRAM, которая полностью зависела от разработчика. 1 МБ VRAM рассматривается как массив 1024 x 512 x 16 бит, который можно было свободно использовать. Применение двойной или тройной буферизации выполняется тривиальным образом, потому что область просто достаточно зарезервировать, отрисовать и передать системе вывода. Текстуры тоже находятся во VRAM, рядом с буферами кадра. Для записи в буфер кадра запускаются команды GPU отрисовки текстурированных треугольников/четырёхугольников.

Текстуры могут иметь различные форматы. Существуют два прямых источников 16-битных и 24-битных цветов, а также источники на основе палитры, называемой Color Look Up Table (CLUT). Поддерживаются 8-битные и 4-битные CLUT.

Что касается разрешения, то консоль была ограничена телевизионными стандартами NTSC и PAL. Для рынков США и Японии разработчик мог выбирать разрешение по горизонтали в 256, 320, 384, 512 или 640 пикселей. Разрешение по вертикали составляло или 240 пикселей (при пропуске каждой второй растровой строки), или 480 пикселей при чередовании. Оба вертикальных режима работали с частотой 60 Гц. Единственное различие между NTSC и PAL заключалось в повышенном разрешении по вертикали (256/512 вместо 240/480) и пониженной частоте обновления (50 Гц).

Режим     NTSC (60Гц)      PAL (50Гц)        Примечание
==========================================================

0         256 x 240        256 x 256         Без чередования
1         320 x 240        320 x 256         Без чередования
2         512 x 240        512 x 256         Без чередования
3         640 x 240        640 x 256         Без чередования
4         256 x 480        256 x 512         С чередованием
5         320 x 480        320 x 512         С чередованием
6         512 x 480        512 x 512         С чередованием
7         640 x 480        640 x 512         С чередованием
8         384 x 240        384 x 256         Без чередования
9         384 x 480        384 x 512         С чередованием


Обратите внимание на режимы с горизонтальным разрешением 384 пикселя (8 и 9), которые, судя по своим id, были добавлены позже.

DOOM на PSX


DOOM был портирован на PSX компанией Williams Entertainment с поддержкой со стороны id Software. Команде из пяти человек потребовалось чуть меньше года [5][6] на портирование движка, изменение ресурсов и модификацию игры, чтобы всё работало «всего лишь» на 3,5 МБ ОЗУ.

«Графика была ухудшена: текстуры уменьшены в размерах, спрайты, монстры и оружие тоже. [...] Иногда мы урезали кадры анимации». — Гарри Тизли

Готовый результат был выпущен 16 ноября 1995 года. Он считается лучшим консольным портом игры, а некоторые аспекты даже превосходят PC-версию благодаря цветным вершинам и музыке CD-качества.

«Пока это самый лучший DOOM!» — Джон Ромеро

План изучения



Из-за особенностей разработки структура DOOM основана на ядре, использующей для ввода-вывода шесть подсистем. Большую часть времени я изучал те три части, которые мне показались самыми интересными, а именно файловую систему на основе CD-ROM, звук на основе SPU и видео на основе GPU, потому что они уникальны для архитектуры PSX.

Исходный код DOOM для PSX никогда не публиковали, но оказалось, что это совершенно не проблема. Доступной информации вполне достаточно.

Первым источником является потрясающий PSY-Q SDK, который был «официальным» инструментом, используемым разработчиками под PSX того времени. По нему есть много документации, представленной в виде набора файлов PDF. Такое обилие информации только подтверждает всё хорошее, что я слышал о дружественности PSX к разработчикам. Библиотека (т.е., libcd, libds), разработанная Psygnosis, тоже хорошо задокументирована. Было очень радостно увидеть чёткие объяснения, особенно по сравнению с почти полным отсутствием информации о других консолях (да, я про SNES).

Ещё одним источником информации стало множество доступных сегодня внешних инструментов. ISOBuster позволил мне открыть содержимое CD. PSound помог сканировать архивы LCD. Настоящим золотом стала потрясающая способность эмулятора no$PSX трассировать команды GPU и SPU.

Но, пожалуй, больше всего меня впечатлила огромная любовь к DOOM для PSX со стороны сообщества фанатов. Был выполнен полный реверс-инжиниринг игры. Особенно здесь выделяется PSXDOOM-RE, потому что это кодовая база на C, которую можно при помощи PSY-Q SDK скомпилировать в полноценную игру для PSX. Код имеет высокую надёжность, потому что был получен переписыванием каждой функции машинного кода на C.

Удивительный мир компакт-дисков


Прежде чем приступать к изучению реализации файловой системы, я совершил небольшой экскурс, чтобы лучше понять удивительный мир компакт-дисков.

За 20 лет, с 1980 по 2000 год, было выпущено несколько томов спецификаций, раскрывающих эту тему. Все вместе они называются «Rainbow Books». Первая книга серии, «Red Book», содержит спецификации звукового компакт-диска (audio Compact Disk, CD). «Yellow Book» является дополнением к «Red Book», в ней добавлены спецификации данных CD-ROM и ISO-9600. «Orange Book» добавила спецификации CD-DA, CD-R (Recordable) и CR-WR (Rewritable). «White Book» посвящена Video-CD (VCD). «Green Book» рассказывает о CD-Interactive (CD-I). В «Blue Book» представлены данные по Enhanced-CD (ECD) для поддержки мультимедиа. «Scarlet Book» посвящена Super Audio (SACD), добавляющего звук высокого разрешения. В «Purple Book» приведена спецификация Double Density CD (DDCD), увеличившего ёмкость дисков с 650 МБ до 1,3 ГБ. Наконец, в «Cyan Book» подробно описываются спецификации файловой системы 9660[7].


В «Rainbow Books» содержится всё, что мы знаем о CD.

В качестве абсолютного минимума нам нужно понимать, что CD консоли PSX обычно состоят из секторов, каждый из которых содержит по 2048 байт данных. Секторы сгруппированы в дорожки (которые могут быть данными или звуком). Дорожки сгруппированы в сессии. Информация дорожек с данными может быть упорядочена при помощи стандартной файловой системы ISO-9660, однако игра может иметь и жёстко заданные адреса секторов.

Внутри CD игры DOOM



Заглянув при помощи ISOBuster внутрь CD-ROM, можно увидеть, что DOOM состоит из одной сессии, содержащей восемь дорожек[8]. Семь из них — это звуковые композиции CD-качества, проигрываемые между миссиями и в финальной сцене. В завершающей дорожке (#7) даже используются оцифрованные голоса демонов. Особенно примечательна дорожка номер 6, потому что кажется, что её взяли прямиком с рейв-пати. Оказывается, это музыка, воспроизводимая на суперсекретной карте 59 «Club DOOM» (секретной карте, доступной только с секретной карты)[9]. Позволю вам самим оценить её безумность. Перед запуском проверьте громкость звука.


Оставшаяся дорожка (номер 1) — это ISO-9660, содержащая движок игры и большинство ресурсов. Изучив множество портов DOOM, я наивно ожидал движок под названием DOOM.EXE, файл ресурсов PSXDOOM.WAD и, возможно, манифест. Я сильно ошибался. На дорожке обнаружилось 287 файлов[10][11], и среди них 60 .WAD, 120 .IMG и бесчисленное количество .LCD.

Данные упорядочены с привязкой к уровням (по пять файлов на каждую карту).

Имя файла                Описание
=======================================================

MAPDIR0/MAP01.WAD        Стандартная геометрия (BSP/Reject/...)
MAPDIR0/MAPTEX01.IMG     Текстуры плоскостей/стен
MAPDIR0/MAPSPR01.IMG     Все спрайты, создаваемые THINGS
MUSIC/MUSLEV1.LCD        Воспроизводимая музыка
SNDMAPS1/MAP01.LCD       Все звуки, издаваемые THINGS

На вопрос, почему всё было устроено по-новому, частично ответил разработчик Crash Bandicoot Энди Гэвин в интервью Ars Technica. [перевод на Хабре]

«Для перемещения головки в любую точку CD требуется примерно 1/3 секунды».

Из-за того, что скорость позиционирования головки была близка к 300 мс (что подтверждается документацией PSY-Q[12]), разработчики из Williams Entertainment никак не могли сохранить чёткую архитектуру движка DOOM, который хранил все ресурсы в одном файле (например, в DOOM.WAD) и загружал их по запросу. На PSX это бы привело к ужасной частоте смены кадров.

Разработчики решили проблему, бросив на это казавшийся бесконечным объём байтов на CD. Все необходимые для уровня ресурсы хранились в пяти файлах, содержащих геометрию карты, текстуры. спрайты, звуковые эффекты и музыку. Это очень затратно, но позволило избавиться от необходимости доступа к CD во время игрового процесса.

Интересный факт: в списке файлов на дорожке данных есть файл под названием PSXDOOM.WAD (4 806 088 байт), но он используется только для загрузки палитр и нескольких изображений меню. Вероятно, более активно он использовался в процессе разработки.

Возьмём для примера первую карту: общий объём загружаемых данных был снижен с 4 МБ до 900 КБ.

Имя файла              Размер (в байтах)  
===================================  	
MAPDIR0/MAP01.WAD            28 196
MAPDIR0/MAPTEX01.IMG         90 744
MAPDIR0/MAPSPR01.IMG        590 344
MUSIC/MUSLEV1.LCD            61 232
SNDMAPS1/MAP01.LCD          143 632
===================================
Всего:                      914,148

Зная, что ресурсы занимают 914 КБ, можно подумать, что оставалось много неиспользуемой DRAM. Но нужно помнить, что в неё также должен был помещаться исполняемый файл объёмом 428 КБ, а также стек, изменяющийся во время выполнения. На самом деле во время выполнения было доступно всего около 1 МБ DRAM.

Интересный факт: исследуя исходный код PSXDOOM-RE, я обнаружил функцию P_LoadBlocks, которая до четырёх раз пытается выполнить чтение с CD[13], после чего сдаётся. Это одна из прелестей работы с легко царапаемыми носителями.

Я не ожидал, что время установки головки CD-ROM так сильно повлияет на архитектуру игры. Некоторые игры, например Crash Bandicoot, создавались с нуля с системой страниц для потоковой передачи данных с CD во время выполнения. В случае DOOM движок не был на это способен. CD не используется во время игры, за исключения одной особой композиции (да, с уровня Club DOOM).

Парни из id Software никогда не были поклонниками компромисса между ёмкостью и скоростью, который предлагали диски CD-ROM.

«Мой главный аргумент в чём-то философский. Мне не нравятся те ожидания, которые люди возлагают на игры с CD. Неужели кто-то думает, что дурацкий диалог в Crash 'n Burn — это ХОРОШЕЕ дополнение игры? Меня от этого тошнит. Люди мечтают, что в играх на CD будет куча оцифрованной озвучки и видео, и 3DO активно это поддерживает. Если мы когда-нибудь будем делать CD-версию DOOM, то покупатели получат игру и часовой фильм „Как создавался DOOM“. Компании тратят сотни тысяч долларов на добавление в игры кучи мультимедиа, и это часто их ухудшает. Мы не хотим быть частью этой толпы.

Я лучше вырежу из игры самое необходимое и засуну её в картридж, чем бессмысленно буду забивать CD. В дизайне игр мне свойственна эстетика минимализма». — Джон Кармак (ATARI EXPLORER ONLINE, 22 января 1994 года)

Интересный факт: события внутри игры DOOM запускались при помощи «растяжек». При пересекании линии с особым свойством вызывалась специальная функция. В «legacy»-версии движка не было свойства, позволяющего проигрывать композиции. Для воспроизведения композиции Club DOOM было создано специальное действие linedef (номер 142)[14]. Удивительно, сколько дополнительных усилий потребовалось для создания этого уровня. Наверно, разработчикам очень нравилось отрываться на рейвах.

Дело о пропавшем Archvile



Проводя исследования для своей книги Game Engine Black Book, я никак не мог полностью разобраться в этой фразе дизайнера Гарри Тизли:

«У archvile было в два раза больше кадров анимации, чем у любого другого монстра, и мы не могли запихнуть его в PSX. Нельзя было потерять ни его атаку, ни эффект воскрешения. Он оказался слишком большим». — Гарри Тизли (дизайнер) в интервью doomworld.com

Было очевидно, что проблемой была не 650-мегабайтная ёмкость CD, но я не понимал, что же являлось ограничивающим фактором — DRAM или VRAM.

Разобравшись с ограничениями CD-ROM, я понял, что проблема заключалась не в хранении спрайта на CD и даже не в передаче его из DRAM во VRAM. Проблема в том, чтобы уместить всё в DRAM.

Интересный факт: ArchVile полностью удалён из исходного кода PSXDOOM-RE. Даже его #DEFINE закомментирован[15].

#define CC_ZOMBIE  "Zombieman"
#define CC_SHOTGUN  "Shotgun Guy"
#define CC_HEAVY  "Heavy Weapon Dude"
...
#define CC_HELL   "Hell Knight"
//#define CC_ARCH "Arch-Vile"
#define CC_SPIDER "The Spider Mastermind"
#define CC_CYBER  "The Cyberdemon"
#define CC_HERO   "Our Hero"

Программирование SPU


Чип SPU понимает только один формат, а именно ADPCM. Он способен микшировать до 24 каналов (в том числе и аудиодорожку CD) и имеет мощные функции манипуляций со звуком.

Для приручения этого зверя DOOM PSX использует библиотеку libWESS (Williams Entertainment Sound System), написанную звукорежиссёром Скоттом Паттерсоном. Библиотека довольно мощна, она способна воссоздать систему MiDI, в которой тяжеловесный банк нот (называемый sound font) управляется занимающей мало пространства музыкальной партитурой. Также она может манипулировать в реальном времени такими атрибутами звука, как громкость, тон, скорость ноты и функции ADSR (Attack, Decay, Sustain и Release). Вся музыка, воспроизводимая при игровом процессе, генерируется с помощью libWESS. За одним исключением — как вы догадались, это «Club DOOM», которая воспроизводится с дорожки CD номер 6.

WESS использует два проприетарных формата файлов. Это файлы .WMD, содержащие партитуры для музыки и звуковые эффекты, и файлы .LCD, которые являются форматом PSX VAG (без заголовка) и содержат сэмплы ADPCM. При запуске DOOM библиотека libWESS загружает все звуковые эффекты (SFX, 89 штук) и музыкальные партитуры (19 штук), хранящиеся в небольшом (55 КБ) файле под названием DOOMSND.WMD. Также она загружает в SRAM «всегда используемые» сэмплы, издаваемые персонажем, дверями и т.д.

MUSIC/DOOMSFX.LCD -> SRAM
MUSIC/DOOMSND.WMD -> DRAM

После загрузки карты libWESS открывает MUSIC/MUSLEV%.LCD, который содержит сэмплы ADPCM, используемые в музыке этой карты, и SNDMAPS%/MAP%%.LCD, содержащий сэмплы ADPCM, необходимые для врагов на этом уровне. Все сэмплы ADPCM загружаются напрямую в SRAM и не занимают места в DRAM.

DOOM на PSX: GPU


В области генерации видео Williams Entertainment нужно было решить две проблемы: малый объём VRAM (к этому мы вернёмся позже) и отсутствие корректного наложения текстур с учётом перспективы.

«Мы с Аароном Силером работали над версиями для Nintendo 64 (эта игра довольно сильно отличалась) и для Playstation. Это были первые версии, которые не писались на „голом“ железе, потому что и Sony, и Nintendo заставляли разработчиков (по крайней мере, сторонних) писать на API и не давали им документацию по регистрам оборудования. Поначалу особенно ограничивала разработчиков культура SGI, но постепенно Nintendo ослабила хватку.

Забавная история о разработке версии для Playstation: мы с Аароном начали с другой архитектуры движка, которая рендерила мир треугольниками, потому что консоль обеспечивала их полное аппаратное ускорение. В случае N64 это сработало отлично, ведь у неё был перспективно корректный рендеринг с субпиксельной точностью (результат влияния SGI), но на Playstation аффинное наложение текстур выполнялось с целочисленными координатами, поэтому большие треугольники стен и пола выглядели УЖАСНО». — Джон Кармак (Game Engine Black Book: DOOM)

Чтобы понять, насколько «ужасно» это выглядело, ниже я показал три стены, отрендеренные с аффинным и перспективно корректным текстурированием.


Перспективно корректное текстурирование


Аффинное (PSX)

Заметьте, что у прямо стоящей стены проблем нет, то при увеличении угла перспективы искажение становится всё более заметным.

С этой же проблемой восприятия столкнулись разработчики Rage Software при портировании DOOM на SEGA Saturn.

«Когда я приступил к проекту, мне нужно было создать демо для одобрения id Software. Я начал с извлечения всех файлов, звуков и текстур из файлов WAD и создал собственные их версии для Saturn, а затем написал предварительную версию рендерера, использующего 3D-оборудование. Мы отправили демо, и через пару дней мне позвонил Джон Кармак и поставил условие, что я ни в коем случае не должен использовать 3D-оборудование для отрисовки экрана. Мне нужно было использовать процессор, как и на PC. К счастью, я люблю трудности, поэтому это оказался очень увлекательный проект: оба SH2 использовались для рендеринга изображения, как на PC, а 68000 применялся для управления двумя процессорами.

Однако это повредило игре, сильно снизив скорость и частоту кадров» — Джим Бэгли (разработчик DOOM для Saturn) в интервью RetroGamer №134.

Возможно, так как у разработчиков на PSX было больше времени, они смогли решить проблему перспективно корректного наложения текстур, превратив рендерер полигонов GPU в рендерер линий.

«Я сказал: забэкапьте всё (тогда ещё не было никаких систем контроля версий!), мы будем делать игру совершенно иначе». Мы воспользовались оборудованием, которое рендерило треугольники, представлявшие столбцы или строки шириной в один пиксель, прямо как в ассемблерном коде на PC, и это замечательно сработало. Оказалось, что более распространённым решением на Playstation являлась тесселяция геометрии по двум осям, но мне очень понравилось, что Doom казался менее «дёрганым», чем большинство игр для Playstation того времени". — Джон Кармак (Game Engine Black Book: DOOM)

Благодаря проекту PSXDOOM-RE[16] мы можем узнать, как это было реализовано. Конвейер рендеринга был полностью переписан и разбит на два этапа. Подробнее об этом будет сказано ниже, но мы и в самом деле видим, что функция рендеринга R_Render_Wall передаёт жёстко прописанные команды отрисовки шириной в 1 пиксель.

void R_Render_Wall(...) {
  .
  int x1 = ... ; // Left end of wall.
  int x2 = ... ; // Right end of wall.
  .
  while(x1 < x2) {
    .
    setRGB0(wallpoly);

    setXY3(wallpoly,
      x1    , ypos1 - 1,
      x1 + 1, ypos2 + 1,
      x1    , ypos2 + 1);		

    setUV3(wallpoly,
         upos, v0,
         upos, v1,
         upos, v1);  
    .
    x1 += 1;
  }
}

Стены рендерятся столбцами шириной в один пиксель. Обратите внимание на API, напоминающий Immediate mode в OpenGL.

Интересный факт: проектировщик оборудования Sony сохранил i-кэш процессора MIPS, но отключил его d-кэш, чтобы превратить его в высокоскоростной scratchpad ёмкостью 1 КБ. В процедурах рендеринга стен/плоскостей активно использовался этот scratchpad.

Управление VRAM GPU


Для изучения того, как выполняется управление VRAM, я выбрал любопытный способ: использовал функцию просмотра VRAM в реальном времени эмулятора no$PSX. Эта функция отображает весь массив пикселей 1024 x 512 x 16 бит (хоть и в искажённом виде). Функция просмотра также показывает весь список команд GPU, передаваемых центральным процессором в каждом кадре.


No$PSX — богом посланный нам эмулятор, позволяющий заглянуть во внутренности GPU.

Внимательное изучение VRAM первого кадра позволяет многое узнать.


Первый кадр игры, отображаемый при помощи No$PSX.

Наиболее очевидны две области размером 256 x 240 x 16 бит в левом верхнем углу, которые являются буферами кадра (следовательно, игра использует двойную буферизацию). Стоит заметить, что 256x240 — это наименьшее разрешение, возможное на PSX.

Под буферами кадра расположен цветастый набор пикселей — палитры CLUT. Заметьте оттенки красного, они означают, что когда экран краснеет при получении игроком урона, используются предварительно вычисленные палитры.

В правом верхнем углу мы видим текстуры, которые странно сжаты по горизонтали и имеют «неправильные» цвета. Так получилось потому, что текстуры используют 8-битные индексы описанных выше CLUT.

Снова поговорим об огне в DOOM на PS1


В 2018 году я изучил, как был реализован эффект огня[17] [перевод на Хабре]. Было здорово снова вернуться к нему при исследовании команд GPU. Заметьте, что no$PSX помечает целевую область каждой команды красным.

Этап 1: пламя обновляется в ОЗУ и загружается во VRAM (команда CpuToVram).


Этап 2: текстура пламени четыре раза отрисовывается вдоль экрана (команда QuadTex). Текстура пламени имеет ширину 32 тексела, но GPU используется для отрисовки её шириной 64 пикселя. Билинейная фильтрация здесь невозможна, выполняется сэмплирование ближайших текселов.


Этап 3: поверх всего отрисовывается плашка «Final DOOM» (команда QuadTex).


Полный кадр, команда за командой


Исследование передаваемых в кадре команд показало, что движок совершенно отличается от версии для PC. В нём обход мира выполнялся с ближнего расстояния вдаль. Сначала рендерятся все стены. На втором проходе заполняется вертикальный пробел между ними (visplane) (в том числе и небо). В третьем (последнем) проходе рендерятся спрайты, начиная с самых дальних. Всё это выполнялось с нулевой перерисовкой пикселей.

На PSX рендеринг выполняется немного более грубо. Всё рендерится за один проход, выполняемый, начиная издалека. Системы visplane, которую на PC использовали для заполнения пустоты между стенами, здесь нет. Благодаря новой концепции под названием «листья» плоскость и стены рендерятся по порядку субсекторов. Такие операции в «настоящем 3D» стали возможными благодаря активному использованию функций проецирования матриц GPE. Спрайты тоже рендерятся одновременно со стенами/плоскостью без проверок перекрытия и усечений, из-за чего возникает небольшой объём перерисовки.

void R_RenderPlayerView(void) {
  R_BSP();         // Phase 1
  R_RenderSKY();   // Phase 2
  for (all subsectors from phase 1) {
    R_RenderAll(subsector)
  }
  R_Render_Hud_Weapons();
}

Давайте рассмотрим каждый этап подробно, начав с неба, которое рендерится первым при помощи CopyRectRaw. no$PSX показывает VRAM на момент завершения кадра, но позволяет возвращаться назад по истории команд. Пиксели неба видны только потому, что у no$PSX возникают проблемы с восприятием этого хака со столбцами шириной в один пиксель (другие эмуляторы, например ePSXe, справляются не лучше[18]), но все эти пиксели будут переписаны. Заметьте, что под текстурами неба собраны в атлас все маркеры ключей к дверям.


Затем выполняется обход BSP в порядке от дальних к ближним. Для каждого субсектора рендерятся все стены/плоскости/спрайты. Если вы знакомы с DOOM BSP, то, вероятно, помните, что компилятор doombsp хранил только сплошные сегменты субсекторов. Чтобы обеспечить рендеринг плоскостей, в версию для PSX была добавлена новая концепция «листьев», в которых хранились сегменты разделения BPS (невидимые). Эти сегменты проецируются в экранное пространство для генерации границ плоскостей. Это гораздо более «чистый» подход, потому что позволяет избавиться от хаков с экранным пространством и багов, например, от известного «следа слизня».


В следующем снимке VRAM плоскости из того же субсектора, что и увиденная нами выше стена, рендерятся как треугольники высотой 1 пиксель. Обратите также внимание на текстуры стен/плоскостей, все они имеют одинаковые размеры. Это свойство упрощает выделение VRAM текстур и позволяет избежать фрагментации.


Мы по-прежнему находимся в том же субсекторе. Теперь спрайты рендерятся как четырёхугольники (Quad). В число этих спрайтов входят враги, снаряды, частично прозрачные стены.


Ради интереса покажем плазменные снаряды.


Мы подходим к концу команд отрисовки. Здесь при помощи смешения прямоугольника рендерится оружие.


Последний этап: рендеринг интерфейса (HUD).


Переполнение VRAM!



Работа с GPU являлась упражнением по выделению пространства во VRAM. В случае буферов кадра, CLUT, статического содержимого (интерфейса) и даже стен/плоскостей задача элементарна, ведь все они имеют одинаковый размер. Но со спрайтами всё немного сложнее.

Из-за того, что спрайты имеют разные размеры, они приводят к фрагментации. Более того, текстуры могут покрывать большие области экрана и повторяться, а спрайты часто уникальны и в каждом кадре может потребоваться непредсказуемо большое их количество. В наихудшем случае кадр требует определённого количества спрайтов, которые невозможно поместить во VRAM. Это называется «переполнением кэша текстур» (Texture Cache Overflow) и такую проблему невозможно решить. Когда такое происходит, игра вылетает и отображает загадочное сообщение об ошибке[19], напоминающее некоторым из нас о зловещем «No more visplanes».


Чем больше спрайтов видно одновременно, тем больше занято VRAM.

Разница между PAL и NTSC


В заключение главы про видео я решил посмотреть, как преобразуется NTSC в PAL. К сожалению, как и во многих других играх, в DOOM PSX не учитывалось увеличение разрешения по вертикали. Играя в DOOM на PS1 в Европе, вы видели сжатое по вертикали изображение с заметными чёрными границами.

После того, как я узнал о принципах работы VRAM, мне сложно обвинять в этом разработчиков. Если присмотреться к схеме VRAM в NTSC, то можно заметить, что увеличение вертикального разрешения буфера кадров нарушает всю структуру размещения данных. Хранить текстуры под ним было бы невозможно. Или пришлось бы перемещать CLUT в другое место. Слишком много затрат при относительно малой выгоде, и даже несмотря на то, что мне при игре мешали чёрные границы, соглашусь, что оно того не стоило.

Благодарности


Благодарю Эрика Васкеса (автора PSXDOOM-RE), Сэмюэла Вильяреала (одного из разрабтчиков PSXDOOM-RE) и Дэна Лесли (профессионального разработчика игр для PlayStation 1) за то, что они великодушно поделились со мной своими знаниями.

Справочные материалы
[ 1] Источник: The Polygons Of Another World
[ 2] Источник: Game Engine Black Book: DOOM
[ 3] Источник: Everything You Have Always Wanted to Know about the Playstation
[ 4] Источник: @gmontoir — 'Heart of Darkness uses MDEC frames as background bitmaps'.
[ 5] Источник: Five years of DOOM, part 1
[ 6] Источник: Five years of DOOM, part 2
[ 7] Источник: The Great Books
[ 8] Источник: DOOM PSX tracks
[ 9] Источник: MAP59: Club Doom (PlayStation Doom)
[10] Источник: PSXDOOM/tree.txt
[11] Источник: PSXDOOM files
[12] Источник: PSY-Q SDK, CD Hardware
[13] Источник: P_LoadBlocks
[14] Источник: Linedef #142 plays Club Doom
[15] Источник: //#define CC_ARCH
[16] Источник: PSXDOOM-RE Source code
[17] Источник: How DOOM fire was done
[18] Источник: DOOM on ePSXe
[19] Источник: I_Error('Texture Cache Overflow');