Есть много хорошей литературы о движке Quake: книги, бесчисленные статьи в Интернете, блоги и вики-страницы. Среди них всех моими любимыми остаются «Graphics Programming Black Book» Майкла Абраша, опубликованная в 1997 году, и «Rocket Jump: Quake and the Golden Age of First-Person Shooters» Дэвида Л. Крэддока (2018 год).
К сожалению, можно найти очень мало информации о разработанном примерно в 1996 году оборудовании, позволившем улучшить 3D-рендеринг и, в частности, графику революционной игры id Software. Внутри архитектуры и конструкции этих кусков кремния заключена история технологической дуэли между Rendition V1000 и 3dfx Interactive Voodoo.
После выпуска vQuake в начале декабря 1996 года казалось, что преимуществом завладела Rendition. V1000 была быстрой картой, способной запускать Quake с аппаратным ускорением, обеспечивающим, по заявлению разработчика, скорость заполнения 25 мегапикселей/с[1]. Прямо перед Рождеством Rendition захватила рынок, позволив игрокам запускать игру с высоким разрешением, частотой кадров и в 16-битном цвете[2]. Но, как показала история, изъян в конструкции Vérité 1000 оказался смертельным для инновационной компании.
Правильно подобранное время и убойные приложения
Идея специализированного оборудования для ускорения графики появилась не внезапно. Ещё в 1954 году у авиакомпании United Airlines были симуляторы полёта для тренировок пилотов. Крупнейший игрок в этой области, Silicon Graphics, Inc. (SGI), появился ещё в 1982 году и предлагал в то время мощные рабочие станции, такие как Indy, O2 и Indigo². Однако цены на эти машины не позволяли приобретать их обычным потребителям (SGI Infinite Reality 1993 года могла продаваться за 100 тысяч долларов, что эквивалентно 177 262 долларам 2019 года). Причиной ситуации, возникшей в конце 90-х, стало сочетание трёх факторов.
Во-первых, значительно снизилась цена на ОЗУ. Даже несмотря на то, что в 1995 году появился огромный дефицит ОЗУ (в основном потому, что для ОС Microsoft Windows 95 рекомендовалось 8 МБ памяти), за год цена ОЗУ упала почти на 90%. Это открыло перспективы для карт с потрясающе огромными буферами кадров (640×480 с 16-битным RGB-цветом), способными хранить текстуры локально.
Во-вторых, повысилась производительность ОЗУ. FastPage RAM была шагом вперёд по сравнению с DRAM, но после выпуска EDO RAM задержки снизились на 30%, а время доступа к ОЗУ составило 50 нс[3].
Третьим и последним куском головоломки стали «убойные приложения» (killer apps). У PC появились мощные ЦП, например, Intel Pentium с частотой 166 МГц, которые разработчики использовали для создания высококачественных 3D-игр. В 1996 году все говорили о двух играх: Tomb Raider компании Core Design и Quake от id Software.
Rendition и V1000
Rendition Inc была основана в 1993. Два года спустя, в 1995 году, компания объявила о создании архитектуры V1000, которая быстро была лицензирована четырьмя OEM. Первыми на рынке появились Creative Labs 3D Blaster PCI, Sierra Screamin’ 3D, Canopus Total 3D и Intergraph Reactor, а вскоре за ними в дело вступила компания MiRO.
Intergraph Reactor. Изображение с vgamuseum.ru.
Creative Labs 3D Blaster. Изображение клуба «Retro Graphics Cards».
Заметьте, что первый чип V1000-E был позже заменён на V1000L-P с меньшим энергопотреблением и на 20% быстрее[4].
MiroCrystal VRX. Изображение с vgamuseum.info.
Canopus Total3D. Изображение с vgamuseum.ru.
Название карт менялось, но используемые в них чипы были одинаковыми. Единственным параметром, по которому производители должны были балансировать цену и производительность, оставалось качество установленной на карте ОЗУ.
- VGA-порт для подключения к ЭЛТ-монитору.
- Ramdac, обычно от Bt, но иногда и чип AT&T.
- Ядро карты — чип V1000-E, V1000-P или v1000-L.
- Восемь 512 кибибайтных чипов DRAM/EDO (в сумме 4 мебибайта) для хранения буферов кадров и текстур.
- 64 кибибайта EEPROM, содержащие BIOS.
V1000 имела два неотъемлемых свойства, которые важно отметить, потому что в 3dfx Voodoo (которую я рассмотрю позже) использовался радикально иной подход.
Во-первых, карта должна была стать заменой тому, что уже установлено у покупателя. Чип поддерживал отрисовку и 2D, и 3D в VGA, а благодаря переключателям контекста имел впечатляющий режим «3D в окне». Поэтому у карты был единственный выходной VGA-порт.
Вторая особенность — это архитектура «big iron», основанная на одном ЦП Mips, получающем доступ ко всем 4 мебибайтам памяти. 64-битная шина данных между ними не имела никаких особых свойств. Такая стандартизованная конструкция позволяла с лёгкостью программирования карту с помощью загружаемого при запуске микрокода (это превратило карту в первый GPU для PC, задолго до того, как Nvidia придумала это определение.)
Программирование V1000
SDK[5] поставлялся с набором файлов заголовков для взаимодействия с языком C (RRedline в Windows и Speedy3D в DOS). Отрисовка текстурированного треугольника напоминала то, что обеспечивает сегодня Vulkan с ручным управлением VRAM. API, способный рендерить текстурированные треугольники с учётом ракурса, также поддерживал альфа-тесты, альфа-смешение и туман.
#include #include #include WinMain(HINSTANCE instance, HINSTANCE prevInstance, LPSTR cmdLine, int cmdShow){ int WIDTH=640, HEIGHT = 480; HWND hWndMain = ... ; // Setup Verite board and resolution/refresh rate v_handle verite; VL_OpenVerite(hWndMain, &verite); V_SetDisplayType(verite, V_FULLSCREEN_APP); V_SetDisplayMode(verite, WIDTH, HEIGHT, 16, 75); // Copy texture to VRAM bmp_info bmp = loadBMP("data\rlogo.bmp"); v_memory memObj = V_AllocLockedMem(verite, bmp.linebytes*bmp.height); memcpy(V_GetMemoryObjectAddress(memObj), bmp.addr, bmp.linebytes*bmp.height); v_surface *display, *texture; VL_CreateSurface(verite, &display, V_SURFACE_PRIMARY, 2, V_PIXFMT_565, WIDTH, HEIGHT); VL_CreateSurface(verite, &texture, 0, 1, V_PIXFMT_565, bmp.width, bmp.height); v_cmdbuffer cmdbuffer = V_CreateCmdBuffer(verite, 0, 0); VL_LoadBuffer(&cmdbuffer, texture, 0, bmp.linebytes, bmp.width, bmp.height, memObj, 0); VL_InstallDstBuffer(&cmdbuffer, display); VL_InstallTextureMap(&cmdbuffer, texture); VL_SetSrcFunc(&cmdbuffer, V_SRCFUNC_REPLACE) // Clear screen to black VL_FillBuffer(&cmdbuffer, display, 1, 0, 0, display->width, display->height,0); // Populate cmd with triangle coo and textCoo v_kaxyzuvq vertex[3] = ... ; VL_Triangle(&cmdbuffer, V_FIFO_KAXYZUVQ, &vertex[0], &vertex[1], &vertex[2]); V_IssueCmdBuffer(verite, cmdbuffer); VL_SwapDisplaySurface(&cmdbuffer, display); }
RRedline обеспечивал загрузку в Vérité 128 кибибайт микрокода и транслировал вызовы C в вызовы ассемблерных функций V1000.
Интересный факт: название API «RRedline» обыгрывало словосочетание «Rendition Ready» и скорее всего было выбрано коллегиально. Однако название Speedy3D было идеей Уолта Донована.
По сути, v1000 всего лишь был медленным ЦП (25 МГц), имевшим однотактовое умножение 32*32 (занимавшую солидную часть чипа!), однотактовую инструкцию вычисления аппроксимированного обратного значения (то есть двухтактовое аппроксимированное целочисленное деление), и обычный набор RISC-инструкций. О, и ещё инструкцию «билинейной загрузки», которая считывала блок линейной памяти размером 2×2 и выполняла билинейную фильтрацию на основе дробных значений u и v, переданных в инструкцию. В карте был крошечный кеш, кажется, всего 4 пикселя. Поэтому если появлялся идеально совпадающий блок 2×2, мы получали уменьшение нагрузки на пропускную способность памяти.
Аппаратная поддержка Z-буферов отсутствовала. Поэтому ПО, выполнявшееся в v1000, должно было считывать Z, выполнять сравнение, а затем решать, выполнять запись или нет.
— Уолт Донован (архитектор алгоритмов)
Для отправки текстур и микрокода в карту драйвер использовал DMA, чтобы передавать данные по PCI без вмешательства ЦП. На практике, у многих материнских плат шинное управление не было реализовано правильно, поэтому играм приходилось возвращаться к режиму PCI FIFO, что отрицательно сказывалось на производительности[6]. Внутри карты все операции выполнялись в 32-битных целых числах с фиксированной запятой.
Разработчики решили, что Rendition будет полностью программируемым, но не использовали никакого умного конвейера или быстрой синхронизации. Поэтому если для записи пикселя нужно было 25 инструкций, то мы получим всего 1 мегапиксель/с. Если использовать оборудование с фиксированным функционалом, то можно создать конвейер, эквивалентный этим 25 инструкциям, и добиться 25 мегапикселей/с. Сотрудники 3dfx пришли из SGI, поэтому они выбрали подход, оказавшийся правильным решением — создать в оборудовании движок обработки треугольников с фиксированным функционалом и подмножеством функций OpenGL для управления. Разработчики V1000 имели совсем другой опыт, они не знали OpenGL, а поэтому решили, что правильнее будет создать ЦП.
— Уолт Донован (архитектор алгоритмов)
Кроме всего этого набора функций карта также имела инновационную систему сглаживания, обладавшей забавным побочным эффектом.
Использованный в vQuake алгоритм сглаживания (антиалиасинга) был запатентован (номер патента 6005580). Об этом алгоритме ходила забавная шутка. Он работал только с треугольниками, но не интервалами. В Quake использовалась концепция «идеальной z-буферизации», при которой графика делилась на интервалы и визуально сортировалась с помощью BSP/PVS (двоичного разбиения пространства/набора потенциально видимых элементов). Поэтому движок создавал набор интервалов, которые идеально покрывали экран без наложений и пропущенных пикселей, и для отрисовки требовалась единственная операция записи (без z-буферизации!) в память дисплея. Однако изначальными данными для этих интервалов были треугольники. Алгоритм антиалиасинга искал рёбра силуэтов и сглаживал их. (Подробнее об этой идее см. сайт humus.name, запись Geometric Post-Process antialiasing примерно от марта 2011 год — автор изобрёл эту технологию заново!) Но так как сглаживание выполнялось после рендеринга экрана (все интервалы уже были отрисованы), алгоритм понятия не имел, видимо ребро или нет. Он отрисовывал его в любом случае. (Если бы использовался z-буфер, то перерисовывались бы только видимые рёбра!) На практике это не оказалось большой проблемой, потому что BSP обычно очень хорошо отсекал невидимые треугольники.
Но не у моделей персонажей! Поэтому vquake позволял игроку видеть людей, прячущихся за дверьми и стенами, создавая небольшое и подвижное искажение в текстурах!
— Уолт Донован (архитектор алгоритмов)
vQuake
В момент выпуска карт они поддерживали несколько хороших игр. Да, Descent II, Grand Prix Legends, IndyCar Racing II, Myst, Nascar Racing, EF2000 и Tomb Raider были неплохими играми, но истинным бриллиантом в короне, самой требовательной и продвигающей продажи была Quake. Игра id Software получила собственный порт под Vérité с названием vQuake, выпущенный 2 декабря 1996 года. Он был написан Уолтом Донованом и Стефаном Поделлом из Vérité при сотрудничестве с Майклом Абрашем из id Software.
Работа была довольно кропотливой, но порт заработал. Pentium 166Mhz, способный рендерить Quake в разрешении 320×200 с частотой 26 кадров в секунду, мог перепрыгнуть на 640×480 с билинейной фильтрацией и по-прежнему рендериться с частотой 22 кадра в секунду[8]. На практике игроки выбирали разрешение 512×384, которое и красиво выглядело, и позволяло обеспечивать на P166 32 кадра в секунду. В течение короткого промежутка времени vQuake бесспорно оставался наилучшим способом игры в Quake.
Программный рендеринг
Vérité V1000
Большое спасибо пользователю @swaaye с форума vogons.org за скриншоты с V1000 и Fruit Of the Dojo за его высококачественный и простой в хакинге порт Quake на MacOSX[9].
Программный рендеринг
Vérité V1000
Изъян Z-буфера
Чего не хватало V1000 (и косвенно его преемнику V2200), так это аппаратного ускорения z-буфера. Как только разработчик включал тест глубины, скорость заполнения падала до 12,5 мегапикселей/с и частота кадров уменьшалась вдвое. Как позже объяснил Стефан Поделл[10], vQuake (и все другие игры) портировались на V1000 таким образом, чтобы минимизировать чтение z-буфера.
Разработчики выяснили, что единственным способом обеспечения нужной скорости был перенос основной части работы в ЦП. В случае vQuake это означало, что карта будет использоваться как сверхбыстрый рендерер горизонтальных интервалов, который всегда выполняет запись в z-буфер, но считывание и сравнение z производит только при рендеринге врагов. И хотя разработчикам удалось создать хорошие продукты, последствия такого выбора архитектуры аукались ещё долго.
3dfx и падение Rendition
id Software выпустила GLQuake 22 января 1997 года. Он был реализован на основе miniGL (подмножества стандарта OpenGL 1.0, в котором, среди прочего, отсутствовали GL_LIGHT и GL_FOG). Этот двоичный файл открыл двери всем картам для PC с аппаратным ускорением. В этом отношении особо выделялись карты Voodoo компании 3dfx Interactive, их потрясающие показатели (41fps в разрешении 512×384 с 16-битным цветом на P166[11]) де-факто стали эталоном для 3D-акселераторов. Скорость заполнения V1000 в 25 мегапикселей/с, которая когда-то выгодно отличалась от программного рендеринга Pentium, теперь казалась посредственной на фоне 50 мегапикселей/с карты Voodoo, на которую даже не влияли z-тесты.
Ответом Rendition стала более мощная V2x00, которая парадоксальным образом усугубила ситуацию. Рекламировалось, что благодаря аппаратному z-буферу V2x00 была в два раза быстрее, однако ей не удалось улучшить даже частоту кадров в vQuake. Эта аномалия подорвала доверие покупателей и плохо отразилась на разработчике vQuake Стефане Поделле, который чувствовал, что ему нужно объяснить, почему производительность vQuake ограничивалась ЦП, а не GPU[12].
… моя репутация оказалась подпорченной тем, что VQuake и VHexen2 не стали работать быстрее на V2x00, поэтому я должен объяснить, почему так произошло.
[…]
Уолт и Майкл решили, что поскольку Verite 1000 не очень хорошо проявляла себя в пикселях с Z-буферизацией, то если позволить Pentium заниматься этой сортировкой интервалов, это сможет уменьшить количество пикселей, которые нужно отрисовывать Verite. Более того, мы смогли бы отключить в Verite функцию сравнения по Z.
[…]
… каким бы ни был чип Verite, ЦП доставалось очень много работы.
— Стефан Поделл
Более того, присутствовали значительные проблемы аппаратной архитектуры, которые изначально приводили к выходу из строя[13] V2x00. Для устранения проблемы потребовалось несколько месяцев, и даже после этого плата всё равно работала на частоте 50 МГц, в то время как NVidia NV3 и Voodoo2 уже достигли 100 МГц.
Третье поколение, основанное на V3300, могло изменить курс истории, но вышло слишком поздно. Проект был отменён в 1998 году, после того, как компанию Rendition купила Micron Technology.
Работая в Rendition, мы совершили много ошибок. Можно было выпустить v1000 на несколько месяцев раньше (и в течение этих месяцев не иметь конкурентов), если бы мы разрабатывали схему сами, а не передали в фаб. К тому же вызывал вопросы контроль качества чипа. Один парень в нашей компании потратил несколько месяцев на реализацию декомпрессии mpeg на языке ассемблера V1000, но так и не смог заставить её работать из-за непредсказуемых багов чипа.
vQuake работал хорошо только потому, что v1000 выполнял не так много работы. «Отрендерить этот список интервалов», «сгладить это ребро» — вот почти и всё, что он делал. Мы с Майком Абрашем потратили слишком много времени на обеспечение совместимости Quake с V1000, поэтому для дальней перспективы такая модель не подходила.
— Уолт Донован (архитектор алгоритмов)
После развала Rendition компания 3dfx удвоила усилия по продвижению Voodoo2, выдающиеся характеристики которой позволили смести всех конкурентов. Король 3D-графики на PC какое-то время правил на рынке. Затем игра продолжилась, на сцене появились новые конкуренты, и среди них были канадская ATI и почти неизвестная в то время компания под названием Nvidia.
Справочные материалы
[1] Источник: VGA Museum, V1000 Texel Fillrate (MTexel/s) reported as 25
[2] Источник: John Carmack .plan Aug 22, 1996 «at 512*384 it is almost twice as fast»
[3] Источник: 3dfx VOODOO1 Reference Rev. 1.0
[4] Источник: Review of the V1000
[5] Источник: Rendition Verite V1000 SDK
[6] Источник: The immaturity of the PCI bus […] caused DMA bugs to surface
[7] Источник: RRedline Programming Guide
[8] Источник: Benchmarks to compare the Rendition Vérité V1000-E and V1000L-P
[9] Источник: MacOSX X Quake port source code on github.com
[10] Источник: Stephan Podell BSS post
[11] Источник: Comparison of Frame-rates in GLQuake Using Voodoo1
[12] Источник: Stephan Podell BSS post
[13] Источник: wikipedia.com, Downfall section
Источник