Software или opengl что лучше
Сравнение OpenGL и Direct3D
Очень часто встречаются различные заблуждения по поводу этих двух API.
Я попытался изложить в этой статье основные факты, которые следует знать как разработчикам, так и конечным пользователям.
Так как тема очень холиварная, я старался придерживаться максимально нейтрального тона.
Взгляд с высоты птичьего полёта
Оба API предоставляют доступ к функциям аппаратного ускорения 3D-графики.
Direct3D — проприетарная разработка Microsoft, созданная специально для Windows. В настоящее время используется так же и на Microsoft Xbox. На других платформах недоступен (если не брать в учёт эмуляцию API, предоставляемую Wine, а также виртуализацию).
OpenGL — открытый стандарт, разрабатываемый некоммерческой организацией Khronos Group при участии сообщества. Все крупные производители GPU (nVidia, AMD, Intel), так или иначе, влияли на OpenGL. В отличие от Direct3D, доступен на очень большом количестве платформ. В частности, OpenGL является основным API для взаимодействия с GPU в Linux и Mac OS.
«Внешние» технические различия API
Direct3D основан на технологии COM. COM — это, по сути, стандарт бинарного представления компонентов. Как известно, классы на чистом C++ не могут быть использованы из других языков программирования, так как они не имеют стандартизованного бинарного представления. В частности, каждый компилятор использует свой собственный метод декорирования имён. COM же позволяет работать с объектно-ориентированной концепцией из любого языка, его поддерживающего. COM — это тоже Windows-specific технология (использует такие специфичные для Windows вещи, как реестр).
В приложении на Direct3D используются указатели на интерфейсы объектов. Работа с объектом осуществляется путём вызова методов его интерфейса. Например, интерфейс так называемого device-а (device в Direct3D — это контекст выполнения для конкретного окна), имеет название (примеры для Direct3D 9) IDirect3DDevice9, для объекта текстуры — IDirect3DTexture9, и т.д. Создание объектов происходит как вызовы методов интерфейса IDirect3DDevice9, например, для текстуры это будет IDirect3DDevice9::CreateTexture.
В Direct3D 10 произошло значительное количество изменений. Direct3D 10 не является обратно совместимым с Direct3D 9. Т.е. чтобы перенести программу на новый API потребуется переписать весь код, относящийся к рендерингу. Подробнее о Direct3D 10 ниже.
OpenGL использует обычные функции языка C. Для них существует стандартизированный ABI, а это значит, что OpenGL может быть использован из любого языка, который поддерживает вызов функций native библиотек (т.е., практически говоря, из любого вообще).
В OpenGL используется так называемая машина состояний (конечный автомат). Результат вызовов функций OpenGL зависит от внутреннего состояния, и может изменять его. В OpenGL, чтобы получить доступ к конкретному объекту (например, текстуре), нужно сначала выбрать его в качестве текущего функцией glBindTexture, а затем уже можно влиять на объект, например, задание содержимого текстуры осуществляется вызовом glTexImage2D.
Аналогом концепции device-а в Direct3D здесь является контекст. Контекст OpenGL привязан к конкретному окну, так же, как и device в Direct3D.
Общим для двух API является то, что обе не предоставляют чего либо за пределами работы с графикой. А именно, нет функций ни для создания окна, ни для работы с вводом с клавиатуры/мыши, ни для работы со звуком (здесь я не затрагиваю другие части DirectX, такие как DirectInput и DirectSound). Т.е. они не являются библиотеками высокого уровня.
В самой упрощённой форме можно сказать так: OpenGL и Direct3D позволяют рисовать треугольники. И всё. Суть в том, что треугольники можно рисовать очень по-разному (текстуры, освещение, преобразования, и т.д.).
Самое важное различие
Имя ему — расширения (extensions).
Direct3D по сути фиксирован в пределах одной мажорной версии. Какие-либо изменения/дополнения происходят только при выпуске следующей версии.
В OpenGL реально доступное API определяется производителем GPU. Реализация OpenGL позволяет определять расширения к основной спецификации. Приложение может получить список поддерживаемых расширений во время выполнения, и проверить на доступность те, которые оно желает использовать.
На самом деле практически весь функционал OpenGL — это расширения. Развитие OpenGL идёт так: появляется новая фишка, производитель реализовывает её в своём драйвере и документирует доступное расширение. Приложения могут использовать новые функции прямо сейчас, не дожидаясь включения в официальную спецификацию. Если это расширение специфично для конкретного производителя, то в названии оно несёт его имя (например, вот так: GL_NV_point_sprite, где NV — значит nVidia). Если расширение реализовано многими вендорами, то в названии используется EXT (например, GL_EXT_draw_range_elements).
Со временем, если расширение широко используется, оно стандартизируется в ARB, и после этого содержит в имени ARB (например, GL_ARB_vertex_buffer_object). Такое расширение имеет официальный статус.
Самые важные расширения со временем становятся частью основной спецификации. Каждая новая версия OpenGL — это по сути старая версия+несколько новых интегрированных расширений. При этом новые функции продолжают быть доступными как расширения. Т.е. на самом деле с точки зрения программы может быть вообще всё равно, какая версия OpenGL. Главное — какие доступны расширения. Версия OpenGL — это просто способ указать, какой набор расширений гарантированно поддерживается.
Что нового в Direct3D 10/11 и OpenGL 3.x
Microsoft сделали радикальную переработку API в Direct3D 10. Сейчас оно имеет более унифицированный и современный вид. Были выброшены некоторые устаревшие вещи, такие как fixed function rendering (без использования шейдеров). Ещё был выполнен переход к новой модели работы драйвера. В частности, реализация Direct3D теперь может иметь не только kernel-space часть, а и user-space. Это позволяет экономить время на переключения user-space/kernel-space. Однако, из-за новой модели драйвера, Direct3D 10 и выше недоступен на Windows XP. Учитывая всё ещё большую популярность Windows XP, это довольно грустно.
Реализация OpenGL изначально была разделена на user-space и kernel-space части, так что там такой проблемы и не было. Ещё различие в том, что до сих пор не вносилось изменений в OpenGL API, которые не были бы обратно совместимы. Каждое нововведение — это расширение.
Функционал, появившийся в Direct3D 10, например, геометрические шейдеры, доступен в OpenGL на любой платформе через расширение, или, начиная с OpenGL 3.2, как часть основной спецификации. Стоит особо подчеркнуть, это важно, функционал Direct3D 10/11 доступен в OpenGL на любой платформе, в том числе и Windows XP. Таким образом у многих сложилось впечатление, что Direct3D 10 не доступен на Windows XP исключительно по политическим причинам, а не из-за каких-то реальных технических проблем. Впрочем, я не могу судить здесь, сохраняя нейтральный тон, о том, были ли действительно такие проблемы при введении новой модели видео-драйверов.
Теперь о нововведениях в OpenGL 3.x. Начиная с OpenGL 3.0 появилась так называемая deprecation model. Часть старой функциональности, относящаяся к fixed function rendering, а также к рендерингу, основанному на glBegin/glEnd, и многие другие устаревшие и неактуальные вещи, были объявлены как deprecated, и были впоследствии удалены из основной спецификации OpenGL 3.1. Это позволяет сохранять основную спецификацию в актуальной и современной форме.
Казалось бы, это должно сломать совместимость со старыми программами. Ведь раньше, когда программа создавала контекст OpenGL, она просто получала контекст версии максимально доступной. Это было ОК, т.к. новые версии всегда являлись надстройками над предыдущими. Чтобы избежать нарушений совместимости, программы, которые хотят получить контект OpenGL 3.x должны использовать новый метод создания контекста, который позволяет указать, какую именно версию OpenGL нужно получить.
Но, как следует из того, что уже было написано раньше про расширения, и это важно, функционал OpenGL 3.x можно получить через расширения, не создавая контекст новым методом. Т.е. OpenGL 1.1 + расширения = OpenGL 3.2! Таким образом, сохраняется полная обратная совместимость. Например, геометрические шейдеры — это расширение GL_ARB_geometry_shader4.
Распространённые заблуждения
OpenGL отстаёт от Direct3D, и вообще, судя по таким вялым изменениям в спецификации, наверное уже совсем мёртв.
Собственно, причина такого заблуждения — это незнание о расширениях. Вообще говоря, OpenGL может и часто опережает (!) Direct3D в плане инноваций, т.к. производитель может добавить расширение к OpenGL, не дожидаясь никого, в то время как в Direct3D изменения может внести только Microsoft.
OpenGL — это для программ профессиональной графики, а Direct3D — это для игр.
Это заблуждение имеет историческую причину. OpenGL исходно разрабатывался как библиотека 3D графики, которая МОЖЕТ, но НЕ ОБЯЗАНА ускоряться аппаратно. Это также объясняет наличие некоторых функций, например рендеринг стерео-изображений, которые не нужны играм. Direct3D же разрабатывался гораздо позже, сразу с расчётом на ускорение на GPU. В момент появления многих пакетов профессиональной работы с графикой Direct3D просто не было.
Интересные нюансы
Microsoft поставляет вместе с Windows драйверы без поддержки OpenGL. OpenGL будет рендерить без ускорения, или эмулироваться через DirectX. Так что, если нужна поддержка OpenGL под Windows, нужно ставить драйвер с сайта производителя. Причины для такого неприятия OpenGL, скорее всего, опять чисто политические.
Так что же делать, как жить?
Примечание: А вот эта часть носит весьма субъективный характер.
Если Вы — разработчик, и решаете, какое API использовать, то задумайтесь над следующим:
За OpenGL — массовая кроссплатформенность, в частности, доступность всех новых функций и на Windows XP, где Direct3D 10/11 нет, и никогда не будет.
Против OpenGL — драйвера в Windows из коробки не имеют поддержки OpenGL, так что ставить их нужно с сайта производителя.
Если Вы — новичок в разработке 3D-приложений, и желаете освоить эту область, то я бы рекомендовал сделать так: сначала разобраться с Direct3D (причина тому проста — Microsoft предоставляет очень качественный SDK), а затем разобраться с OpenGL (это будет очень просто после Direct3D). К сожалению, такой вещи, как SDK, для OpenGL нет. Поэтому осваивать с нуля будет сложнее.
Direct3D vs OpenGL: история противостояния
По сей день в Интернете можно встретить споры о том, какой же графический API лучше: Direct3D или OpenGL? Несмотря на свой религиозный характер, такие словесные баталии приносят полезный результат в виде неплохих исторических обзоров развития аппаратно-ускоренной графики.
Целью данного поста является перевод одного из таких экскурсов в историю, написанного Джейсоном МакКессоном (Jason L. McKesson) в ответ на вопрос «Почему разработчики игр предпочитают Windows». Этот текст вряд ли отвечает на поставленный вопрос, но историю развития и противостояния двух самых популярных графических API он описывает очень красочно и довольно подробно, поэтому в переводе я сохранил авторскую разметку. Текст написан в середине 2011 года и охватывает промежуток времени, начинающийся незадолго до появления Direct3D и до момента написания. Автор оригинального текста является опытным разработчиком игр, активным участником StackOverflow и создателем обширного учебника о современном программировании 3D-графики. Итак, предоставим слово Джейсону.
Предисловие
Прежде чем мы начнём, я хотел бы сказать, что знаю больше об OpenGL, чем о Direct3D. В своей жизни я не написал ни строчки кода на D3D, но писал руководства по OpenGL. Но то, о чём я хочу рассказать — вопрос не предубеждения, а истории.
Рождение конфликта
Однажды, где-то в начале 90-х, Microsoft огляделись вокруг. Они увидели, что SNES и Sega Genesis — это очень здорово, на них можно поиграть во множество экшн-игр и всего такого. И они увидели ДОС. Разработчики писали досовские игры как консольные: близко к железу. Тем не менее, в отличие от консолей, где разработчик знал, какое железо будет у пользователя, дос-разработчики были вынуждены писать под множество конфигураций. А это намного сложнее, чем кажется.
Но у Microsoft была бо́льшая проблема: Windows. Видите ли, Windows хотела полностью владеть железом в отличие от ДОС, которая позволяла разработчикам делать всё что угодно. Владение железом необходимо для взаимодействия между приложениями. Но такое взаимодействие — это в точности то, что ненавидят разработчики игр, потому что оно потребляет драгоценные ресурсы, которые они могли бы использовать для всяких крутых вещей.
Для продвижения разработки игр на Windows, Microsoft нуждалась в однородном API, который был бы низкоуровневым, работал бы на Windows без потерь производительности, и был бы совместим с различным железом. Единый API для графики, звука и устройств ввода.
Так родился DirectX.
3D ускорители появились несколько месяцев спустя. И Microsoft вляпались в неприятности. Дело в том, что DirectDraw, графический компонент DirectX, работал только с 2D графикой: выделял графическую память и делал быстрые битовые операции между различными секторами памяти.
Поэтому Microsoft купили немного стороннего ПО и превратили его в Direct3D версии 3. Его ругали абсолютно все. И было за что: чтение кода на D3D v3 выглядело как расшифровка письменности исчезнувшей древней цивилизации.
Старик Джон Кармак в Id Software посмотрел на это безобразие, сказал «Да пошло оно. », и решил писать с использованием другого API: OpenGL.
Однако другая сторона этой запутанной истории заключалась в том, что Microsoft совместно с SGI работали над реализацией OpenGL для Windows. Идея была в том, чтобы привлечь разработчиков типичных GL-приложений для рабочих станций: САПР, систем моделирования и тому подобных вещей. Игры были последним, о чём они думали. В основном это касалось Windows NT, но Microsoft решили добавить OpenGL и в Windows 95.
Чтобы сманить разработчиков софта для рабочих станций на Windows, Microsoft решили подкупить их доступом к новомодным 3D-ускорителям. Они реализовали протокол для устанавливаемых клиентских драйверов: графическая карта могла заменить программный OpenGL от Microsoft своей аппаратной реализацией. Код автоматически использовал аппаратный OpenGL, если таковой был доступен.
Однако, в те времена потребительские видеокарты не имели поддержки OpenGL. Это не остановило Кармака от портирования Quake на OpenGL на рабочей станции SGI. В readme-файле GLQuake можно прочитать следующее:
Теоретически, glquake запустится на любой реализации OpenGL, которая поддерживает расширение texture objects. Но пока вы не запустите её на очень мощном железе, которое ускоряет всё, что нужно, работать она будет непростительно медленно. Если игре потребуется работать через какие-либо программные эмуляции, её производительность скорее всего не превысит один кадр в секунду.
В настоящее время (март 1997), единственной полностью совместимой с opengl железкой, способной потянуть glquake на приемлемом уровне, является ОЧЕНЬ дорогая видеокарта intergraph realizm. 3dlabs существенно увеличили её производительность, но с имеющимися драйверами она всё ещё недостаточно подходит для игры. Некоторые из драйверов от 3dlabs для плат glint и permedia ещё и крэшат NT при выходе из полноэкранного режима, поэтому я не рекомендую запускать glquake на железе от 3dlabs.
3dfx предоставляет opengl32.dll, которая реализует все нужное для glquake, но это не полная реализация opengl. Другие opengl-приложения скорее всего с ней не заработают, поэтому рассматривайте её в основном как «драйвер для glquake».
Это было рождением miniGL драйверов. В конечном итоге они эволюционировали в полноценные реализации OpenGL, как только железо стало достаточно мощным для аппаратной поддержки этой функциональности. nVidia стала первой, кто предложил полную реализацию OpenGL. Другие поставщики всё ещё медлили, что стало одной из причин перехода разработчиков на Direct3D, поддерживаемый более широким спектром оборудования. В конце концов остались только nVidia и ATI (которая сейчас AMD), и у обеих имелись хорошие реализации OpenGL.
Рассвет OpenGL
Итак, участники определены: Direct3D против OpenGL. Это поистине удивительная история, учитывая то, как плох был D3D v3.
Совет по архитектуре OpenGL (Architectural Review Board, ARB) — это организация, ответственная за поддержание и развитие OpenGL. Они выпускают множество расширений, содержат репозиторий с расширениями, и создают новые версии API. ARB — это комитет, состоящий из большого количества игроков индустрии компьютерной графики и некоторых производителей ОС. Apple и Microsoft в разное время тоже были членами ARB.
3Dfx выходит на сцену со своей Voodoo2. Это первая видеокарта, позволяющая делать мультитекстурирование, которое не было ранее предусмотрено в OpenGL. В то время как 3Dfx была решительно против OpenGL, nVidia, следующий производитель чипа с мультитекстурированием (TNT1), были без ума от OpenGL. Тогда ARB выпустил расширение GL_ARB_multitexture, которое предоставляло доступ к множественным текстурам.
Тем временем появляется Direct3D v5. Теперь D3D действительно стал API, а не какой-то несуразицей. В чём же проблема? В отсутствии мультитекстурирования.
Но это не доставляло таких неудобств, какие могло бы доставить, потому что почти никто не использовал множественное текстурирование. Мультитекстурирование почти не вредит производительности, и во многих случаях разница незаметна на фоне многопроходности. И конечно, разработчики игр очень любят чтобы их игры уверенно работали на старом железе, которое не имело поддержку множественных текстур, поэтому многие игры были выпущены без неё.
D3D вздохнул с облегчением.
Время шло, и nVidia выкатили GeForce 256 (не путать с самым первым GeForce GT-250), прекратив борьбу на рынке графических плат на следующие два года. Главным конкурентным преимуществом этой платы была возможность делать преобразования вершин и освещение (transformation & lighting, T&L) аппаратно. Но это ещё не всё: nVidia любила OpenGL настолько, что их T&L-движок фактически и был OpenGL. Почти буквально! Как я понимаю, некоторые из их регистров получали на вход напрямую численные значения переменных типа GLenum.
Выходит Direct3D v6. Наконец то подоспело множественное текстурирование… но без аппаратного T&L. В OpenGL всегда был T&L-конвейер, хотя до GeForce 256 он был реализован программно. Поэтому для nVidia оказалось довольно просто переделать программную реализацию в аппаратное решение. В D3D аппаратное T&L появилось только к седьмой версии.
Заря эпохи шейдеров, OpenGL во мгле
Затем появилась GeForce 3. В то же самое время произошло много интересных вещей.
В Microsoft решили, что они больше не собираются опаздывать. Поэтому вместо того, чтобы смотреть, что сделает nVidia, и копировать их разработки уже пост-фактум, Microsoft приняли изумительное решение: пойти и поговорить. И они полюбили друг друга, и у них появилась совместная маленькая консоль.
Шумный развод произошёл позже, но это совсем другая история.
Для рынка PC это значило, что GeForce 3 вышла одновременно с D3D v8, и нетрудно видеть, как GeForce 3 повлияла на шейдеры D3D v8. Пиксельные шейдеры Shader Model 1.0 были очень сильно заточены под оборудование nVidia. Не было предпринято ни единой попытки сделать хоть что нибудь для абстрагирования от железа nVidia. Shader Model 1.0 стала тем, для чего предназначена GeForce 3.
Когда ATI ворвалась в гонку производительности видеокарт со своей Radeon 8500, появилась одна проблема. Пиксельный конвейер Radeon 8500 оказался более мощным, чем у nVidia. Поэтому Microsoft выпустила Shader Model 1.1, которая в основном была тем, для чего предназначена 8500.
Это звучит как поражение D3D, но успех и провал — понятия относительные. На самом деле эпический провал поджидал OpenGL.
В nVidia очень любили OpenGL, поэтому после выхода GeForce 3 они выпустили целую пачку расширений для OpenGL. Проприетарных расширений, которые работали только на nVidia. Естественно, когда появилась плата 8500, она не могла использовать ни одно из них.
Итак, на D3D 8 вы могли хотя бы запустить шейдеры SM 1.0. Конечно, чтобы использовать всю крутость 8500, приходилось писать новые шейдеры, но код хотя бы работал.
Чтобы получить любые шейдеры на Radeon 8500 в OpenGL, ATI пришлось разработать несколько расширений для OpenGL. Проприетарных расширений, которые работали только на ATI. В итоге, чтобы разработчики могли заявить, что они прикрутили к своему движку шейдеры, им приходилось писать отдельный код для nVidia и отдельный код для ATI.
Вы возможно спросите: «А где же был комитет ARB, который должен поддерживать OpenGL на плаву?». А были они там, где в конечном итоге оказываются многие комитеты: они сидели и тупили.
Обратите внимание, выше я упомянул ARB_multitexture, потому что это расширение глубоко замешано во всей ситуации. Стороннему наблюдателю казалось, что ARB вообще хочет избежать идеи шейдеров. Они решили, что если они вбухают достаточно конфигурируемости в фиксированный конвейер, то он сравняется по своим возможностям с программируемым шейдерным конвейером.
ARB выпускали расширения одно за другим. Каждое расширение со словами «texture_env» в названии было попыткой залатать этот устаревающий дизайн. Посмотрите на список расширений: таких расширений было выпущено восемь штук, и многие из них были переведены в основную функциональность OpenGL.
В то время Microsoft входила в ARB, и покинула его только к выпуску D3D 9, поэтому возможно, Microsoft саботировали OpenGL некоторым образом. Лично я сомневаюсь в этой теории по двум причинам. Во-первых, они должны были бы заручиться поддержкой других членов Комитета, потому как каждый участник имеет всего один голос. Во-вторых, что более важно, Комитету не нужна была помощью Microsoft чтобы всё запороть, свидетельства чему мы увидим далее.
В итоге ARB, скорее всего под давлением ATI и nVidia (обе являются активными участниками), наконец очнулся и ввёл в стандарт ассемблерные шейдеры.
Хотите ещё более дурацкую историю?
Аппаратное T&L. Это то, что в OpenGL было изначально. Чтобы получить максимально возможную производительность аппаратного T&L, нужно хранить данные вершин на GPU. Все-таки, GPU — это основной потребитель вершинных данных.
В D3D v7 Microsoft ввела концепцию вершинных буферов, которые выделяют куски памяти в GPU и размещают там данные вершин.
Хотите знать когда в OpenGL появилась эквивалентная функциональность? Да, nVidia, как самый большой любитель OpenGL, выпустила своё расширение для хранения массивов вершин на GPU ещё во времена выхода GeForce 256. Но когда же ARB представил такую функциональность?
Два года спустя. Это было после того, как она утвердили вершинные и фрагментные (пиксельные в терминах D3D) шейдеры. Столько времени ушло у ARB чтобы разработать кросс-платформенное решение для хранения вершинных данных в памяти GPU. И это то, что необходимо, чтобы аппаратное T&L достигло максимальной производительности.
Итак, OpenGL был сломан в течение какого-то времени. Отсутствовали кросс-платформенные шейдеры и аппаратно-независимое хранение вершин в GPU, в то время как пользователи D3D наслаждались и тем и другим. Могло ли стать ещё хуже?
Можно сказать, что могло. Встречайте: 3D Labs.
Вы спросите: кто же они? Они — мёртвая компания, которую я считаю истинным убийцей OpenGL. Конечно, общая несостоятельность Комитета сделала OpenGL уязвимым, в то время как он должен был рвать D3D в клочья. Но по-моему, 3D Labs — возможно единственная причина текущего положения OpenGL на рынке. Что они для этого сделали?
Они разработали язык шейдеров для OpenGL.
Что в итоге и случилось.
В стремлении оказаться на плаву в мире, которому были не нужны их продукты, 3D Labs заявились на конференцию Game Developer Conference с презентацией того, что они назвали «OpenGL 2.0». Это был OpenGL API, переписанный с нуля. И это имело смысл, ибо в те времена в API OpenGL было полно хлама (который, впрочем, остаётся там и по сей день). Посмотрите хотя бы на то, насколько эзотерически сделаны загрузка и биндинг текстур.
Частью их предложения был язык шейдеров. Да, именно он. Тем не менее, в отличие от имеющихся кросс-платформенных расширений, их язык шейдеров был «высокоуровневым» (C — это высокий уровень для языка шейдеров).
В это же время в Microsoft работали над своим собственным языком шейдеров. Который они, включив всё своё коллективное воображение, назвали… Высокоуровневым Языком Шейдеров (HLSL). Но их подход к языку был фундаментально иным.
Наибольшей проблемой языка от 3D Labs было то, что он был встраиваемым. Microsoft полностью самостоятельно определяла свой язык. Они выпустили компилятор, который генерировал ассемблерный код для шейдеров SM 2.0 (или выше), который, в свою очередь, можно было скармливать D3D. Во времена D3D v9, HLSL никогда не касался D3D напрямую. Он был хорошей, но необязательной абстракцией. У разработчика всегда была возможность взять выхлоп компилятора и подправить его для максимальной производительности.
В языке от 3D Labs ничего этого не было. Вы отдаёте драйверу C-подобный язык, и он создаёт шейдер. На этом всё. Никакого ассемблерного шейдера, ничего, что можно скормить чему-то ещё. Только объект OpenGL, представляющий шейдер.
Для пользователей OpenGL это означало, что они становились подвержены капризам разработчиков OpenGL, которые только научились компилировать ассемблероподобные языки. В компиляторах новорождённого языка шейдеров OpenGL (GLSL) свирепствовали баги. Что ещё хуже, если вам удавалось заставить шейдер корректно компилироваться на различных платформах (что уже само по себе было большим достижением), то он всё ещё был подвержен оптимизаторам тех времён, которые были не так уж оптимальны, как могли бы быть.
Это было большим, но не единственным недостатком GLSL. Далеко не единственным.
В D3D, как и в старых ассемблерных языках OpenGL, можно было смешивать и всячески комбинировать вершинные и фрагментные шейдеры. Можно было использовать любой вершинный шейдер с любым совместимым фрагментным шейдером, если они взаимодействовали через одинаковый интерфейс. Более того, допускалась даже некоторая несовместимость: например, вершинный шейдер мог подать на выход значение, которое не использовалось фрагментным шейдером.
В GLSL ничего такого не было. Вершинный и фрагментный шейдер сплавлялись воедино, образовывая нечто, названное компанией 3D Labs «программным объектом». Поэтому, для совместного использования нескольких вершинных и фрагментных шейдеров в различных комбинациях, приходилось создавать несколько программных объектов. Это стало причиной второй по величине проблемы.
3D Labs думали, что они самые умные. Они взяли C/C++ за основу для модели компиляции GLSL. Это когда вы берёте один c-файл и и компилируете его в объектный файл, а затем берёте несколько объектных файлов и компонуете их в программу. Именно так компилируется GLSL: сначала вы компилируйте вершинный или фрагментный шейдер в шейдерный объект, затем помещаете эти объекты в программный объект и компонуете их воедино чтобы наконец сформировать программу.
В теории это позволяло появиться таким крутым вещам, как «библиотечные» шейдеры, которые содержат код, вызываемый основным шейдером. На практике это приводило к тому, что шейдеры компилировались дважды: один раз на стадии компиляции и второй раз на стадии компоновки. В частности, этим славился компилятор от nVidia. Он не генерировал какой-либо промежуточный объектный код; он компилировал вначале, выбрасывал полученный результат и компилировал заново на стадии компоновки.
Таким образом, чтобы приделать вершинный шейдер к двум разным фрагментным шейдерам, приходилось компилировать намного больше, чем в D3D. Особенно с учётом того, что вся компиляция производится оффлайново, а не перед непосредственным исполнением программы.
У GLSL были и другие проблемы. Возможно, было бы неправильным сваливать всю вину на 3D Labs, ибо в конечном итоге ARB утвердили и включили в OpenGL язык шейдеров (но ничего больше из предложений 3DLabs). Однако, изначальная идея всё равно была за 3D Labs.
И теперь самое печальное: 3D Labs были правы (в основном). GLSL не векторный язык, каким в то время был HLSL. Так случилось потому, что железо 3D Labs было скалярным (как современное железо от nVidia), и они были полностью правы в выборе направления, которому позднее последовали многие производители оборудования.
Они были правы и с выбором модели компиляции для «высокоуровневого» языка. Даже D3D в итоге к этому пришёл.
Проблема в том, что 3D Labs были правы в неправильное время. И в попытках попасть в будущее преждевременно, в попытках быть готовыми к будущему, они отставили в сторону настоящее. Это выглядит как T&L-функциональность в OpenGL, которая была в нём всегда. За исключением того, что T&L-конвейер OpenGL был полезным и до появления аппаратного T&L, а GLSL был обузой до того как остальной мир догнал его.
GLSL — это хороший язык сейчас. Но что было в то время? Он был ужасен. И OpenGL пострадал от этого.
На подходе к апофеозу
Возможно вы слышали эту историю. Во времена OpenGL 2.1, у OpenGL были большие проблемы. Он тащил за собой огромный груз совместимости. API больше не был простым в использовании. Одну вещь можно было сделать пятью разными способами и непонятно какой из них быстрее. Можно было «изучить» OpenGL по простым руководствам, но при этом вы не изучали тот OpenGL, который даёт настоящую графическую мощь и производительность.
ARB решили предпринять ещё одну попытку изобрести OpenGL. Это было как «OpenGL 2.0» от 3D Labs, но лучше, потому что за этой попыткой стоял ARB. Они назвали это «Longs Peak».
Что такого плохого в том, чтобы потратить немного времени на улучшение API? Плохо то, что Microsoft оказалась в довольно шатком положении. Это было время перехода на Vista.
В Vista Microsoft решили внести давно напрашивающиеся изменения в графические драйверы. Они заставили драйверы обращаться к ОС за виртуализацией графической памяти и многим другим.
Можно долго спорить о достоинствах такого подхода, и о том, был ли он вообще возможен, но факт остаётся фактом: Microsoft сделала D3D 10 только для ОС Vista и выше. Даже на поддерживающем D3D железе было невозможно запустить D3D приложение без Висты.
Вы возможно помните, что Виста… скажем так, работала не очень хорошо. Итак, у нас была неторопливая ОС, новый API, который работал только на этой ОС, и новое поколение железа, которое нуждалось в этом API и ОС чтобы делать нечто большее, чем просто превосходить предыдущее поколение в производительности.
Тем не менее, разработчики могли использовать функциональность уровня D3D 10 через OpenGL. То есть могли бы, если бы ARB не был занят работой над Long Peaks.
ARB потратили добрые полтора-два года, работая над улучшением API. Ко времени выхода OpenGL 3.0 переход на Висту закончился, Windows 7 была на подходе, и разработчиков игр больше не заботила функциональность уровня D3D 10. В конце концов, оборудование для D3D 10 прекрасно работало с приложениями на D3D 9. С увеличением темпов портирования с ПК на консоли (или с переходом ПК-разработчиков на консольный рынок), разработчикам всё меньше была нужна функциональность D3D 10.
Если бы разработчики получили доступ к этой функциональности даже на Windows XP, развитие OpenGL могло бы получить живительный заряд бодрости. Но ARB упустили эту возможность. А хотите знать что самое ужасное?
ARB не смогли изобрести API с нуля несмотря на трату двух драгоценных лет на попытки сделать это. Поэтому они вернули статус-кво, добавив лишь механизм для объявления функциональности устаревшей.
В итоге ARB не только упустили ключевые возможности, но и не выполнили ту работу, которая привела их к этому упущению. Это был epic fail по всем направлениям.
Такова история о противостоянии OpenGL и Direct3D. История упущенных возможностей, величайшей глупости, умышленного безрассудства и банальных нелепостей.