`

СПЕЦИАЛЬНЫЕ
ПАРТНЕРЫ
ПРОЕКТА

Архив номеров

Как изменилось финансирование ИТ-направления в вашей организации?

Best CIO

Определение наиболее профессиональных ИТ-управленцев, лидеров и экспертов в своих отраслях

Человек года

Кто внес наибольший вклад в развитие украинского ИТ-рынка.

Продукт года

Награды «Продукт года» еженедельника «Компьютерное обозрение» за наиболее выдающиеся ИТ-товары

 

Римейк десятилетия: OpenGL 2

0 
 

О значении OpenGL для мира современной трехмерной графики не стоит даже говорить. Это касается как детских компьютерных игр, так и "игр" для взрослых бородатых мужчин из исследовательских лабораторий крупнейших вузов. Однако время идет, и в настоящий момент стандарт рискует утонуть в невероятном количестве фирменных расширений. И вот компания 3Dlabs предприняла героическую попытку реанимировать OpenGL во всем его блеске и величии. Подготовка второй версии стандарта идет полным ходом. В данном материале рассматривается черновая версия создаваемых спецификаций.
Старость -- не радость

Когда-то OpenGL правил миром компьютерной графики, и даже библиотека первого потребительского ускорителя для ПК, созданного компанией 3Dfx, базировалась на архитектуре OpenGL. С тех пор прошло очень много времени -- целая эпоха с точки зрения 3D-графики. Сменялись поколения чипсетов, процессоров, росли объемы текстурной памяти и сложность рендерируемых сцен. Мы постепенно подошли к той грани, за которой картинка на экране обыкновенного ПК будет практически неотличима от реальности. А OpenGL умудрился неплохо сохраниться для своих лет -- сегодня продвигается всего лишь пятая итерация спецификации. И это не полноценная ревизия, а лишь смена десятых: с 1.0 до 1.4. Цифры ничего не говорят сами по себе, сравним произошедшие изменения с эволюцией главного конкурента -- DirectX, который успел за то же время пройти от первой до девятой версии (не считая невероятного количества "подрелизов").

В итоге процесс создания новых расширений для OpenGL привел к тому, что даже такое формально неприкасаемое понятие, как "база стандарта", стало постепенно размываться, включая в себя функции, ставшие частью неотъемлемых возможностей современных видеокарт. Такие дополнения не являются широкораспространенными (всего их существует около 230) и совместимыми с другими реализациями OpenGL-подсистем. Существуют и определенные инициативы по добавлению в стандарт интерфейса для подключения внешних платформенно-зависимых процедур, именуемых шейдерами (shaders). Беда шейдеров в том, что нет единого стандарта для их реализации, поэтому так трудно достичь программного консенсуса в этой области. С расширениями стандарта дошло до смешного -- порой фирменная документация, описывающая дополнения к OpenGL, содержит раза в два больше страниц, чем собственно описание базовой библиотеки.

Хотя у истоков OpenGL стояла Silicon Graphics, дело ее решила продолжить 3Dlabs -- фирма, так ничего и не добившаяся на рынке массовых трехмерных ускорителей. После неудавшейся попытки выйти на этот наиболее доходный рынок, что привело в конце концов к поглощению компании корпорацией Creative, боссы компании решили попробовать "утвердиться" на ниве стандартизации и в 2001 году анонсировали вторую версию OpenGL. Учитывая значимость стандарта для индустрии, идея была встречена более чем положительно, и ряд других производителей видеокарт тоже не посчитали для себя зазорным присоединиться к его сторонникам. Возможно, положительная реакция со стороны NVidia и ATI как раз и была обусловлена шаткостью рыночных позиций инициатора.

Очередная реинкарнация технологии должна стать той долгожданной незыблемой платформой, на базе которой в дальнейшем будет строиться здание расширений. К базовому набору функций добавляются язык для написания шейдеров и средства программного управления ресурсами. При этом сохранится полная совместимость с предыдущими версиями. Конвейер рендеринга станет гораздо более гибким: количество проходов и порядок операций должны определяться не архитектурой системы, а потребностями конечного пользователя -- прикладной программы. Под ресурсами в первую очередь понимается управление распределением памяти. Для реализации многопроходного текстурирования и других многоэтапных операций вводится поддержка специальных буферных областей ОЗУ (оffscreen rendering). Программисту станут подвластны операции сохранения и удаления отдельных объектов в памяти, что ранее было исключительной привилегией системных механизмов библиотеки. И наконец, теперь у OpenGL появился свой собственный язык программирования.

Римейк десятилетия OpenGL 2
Рис. 1
Стратегия развития выбрана интересная (рис. 1) -- сначала новая функциональность будет добавлена к старому командному набору. По мере того как программисты станут переходить на использование только возможностей второй версии библиотеки, необходимость в поддержке старых функций постепенно сойдет на нет. Едва последнее приложение, требующее для своей работы версию 1.3, исчезнет из поля зрения разработчиков, будет введен новый более лаконичный и элегантный базис -- "Pure" OpenGL 2.0. Лишь на его основе станет возможно последующее гармоничное развитие и дополнение OGL 2 новыми элементами.


Механика чудес

Суть и смысл изменений не в самих трансформациях, а в различиях исходного и конечного состояний. Поэтому сначала вспомним "как это было", когда после логотипа OpenGL шла цифра 1. В основе библиотеки архитектура клиент-сервер: приложение генерирует наборы команд и отсылает их серверу, который их интерпретирует и выполняет. В OpenGL используется "контекстная" графическая модель, приблизительно как в языке оператора DRAW в Бейсике: сперва устанавливаются определенные параметры среды, а затем все последующие команды исполняются исходя из этих параметров. Команды в OpenGL обычно обрабатываются не поодиночке, а группами, образующими так называемый Display List. Получается нечто вроде процессорного кэша, что обеспечивает потенциальную возможность параллелизации генерирования отдельных фрагментов.

Рендеринг трехмерной сцены в OpenGL начинается с формирования массива вершин (vertex), определяющих форму всех отображаемых объектов. Каждой вершине, помимо координат, присваивается нормаль, указывающая на ориентацию поверхности, к которой вершина принадлежит, и значение цвета. На следующем этапе происходит трансформация модели: координаты каждой вершины пересчитываются с помощью программно заданных матриц преобразований (matrix transformation). Это позволяет получить заданную ориентацию моделей согласно текущей точки зрения. Затем с помощью команд семейства glLight, glMaterial и других выбираются параметры освещения. Командой glTexGen готовится текстура, а ее координаты, заданные glTexCoord, тоже подвергаются матричным трансформациям, после чего вершинная модель и текстурная карта совмещаются на этапе сборки примитивов (primitives assembly).

Фактически на этом основные операции по формированию трехмерной среды закончены, наступает вторая и завершающая стадия: определение поля вывода и проекция на плоскость экрана. Изображение обрезается дважды, сначала согласно заданным прикладной программой опциям, а затем по объективным факторам, т. е. в соответствии с заранее определенными размерами трехмерной области вывода. При этом модель может подвергаться некоторому "редизайну": по мере отсечения не поместившихся в "экран" фрагментов генерируются новые вершины на пересечении плоскости обрезки и граней модели.

Конечная проекция на экран задается командами glViewport и glDepthRange. Их назначение ясно из названий. Прежде чем отрисовывать трехмерную среду на экране, ее необходимо растеризовать. Для управления этим процессом существует внушительный арсенал вспомогательных команд, касающихся наложения текстур, растеризации точек, полигонов и тумана. В результате вышеупомянутых операций появляются фрагменты. Последние проходят еще несколько стадий обработки, в частности, их проверяют на видимость в окне отображения (не заслоняют ли их другие окна) -- Pixel Ownership Test, прозрачность -- Alpha Test и попадание в область маски (если она задана) -- Stencil Test. Если фрагмент хотя бы в некоторой степени прозрачен, то к нему применяется операция Blending -- смешение цветов составляющих его пикселов с цветами пикселов фрагментов, расположенных дальше относительно точки зрения.

В OpenGL существует отдельная категория вспомогательных команд, обозначающихся префиксом glu, которые базируются на использовании команд основного набора и представляют собой некое подобие функциональной надстройки, облегчающей выполнение некоторых стандартных процедур (например, матричных трансформаций или обработку ошибок).


Механика чудес 2

Римейк десятилетия OpenGL 2
Рис. 2
Чтобы понять преимущества OGL 2 над OGL 1.x, достаточно бросить беглый взгляд на новый графический "конвейер" и сравнить схему с ее запутанной предшественницей (рис. 2).

Для облегчения манипуляций с наборами связанных данных введены объекты. Хороший пример объекта, позволяющий понять его смысл и содержание: Cube Map Object или кубическая карта, объединяющая шесть отдельных текстур. Помимо Cube Map, предусмотрены: Image, Texture, Vertex Array, Display List, Shader, Program, Frame Buffer и Buffer Objects. Объектная форма представления обеспечивает совершенно новую функциональность. Наиболее впечатляюще выглядит возможность вывести трехмерное изображение в буфер, а затем простым назначением присвоить текстурному объекту полученный результат. Если вас смутило наличие в списке и Shader-, и Program-объектов, то поясним -- это не дежа-вю, Program Object есть способ компоновки нескольких Shader Object в единую структуру.

Управление памятью может быть как полностью "ручное", так и с применением определенных правил, индивидуальных для каждого объекта -- memory management policies. Их всего четыре плюс пять политик использования (usage policies). Политики присваиваются создаваемому объекту и определяют дальнейшую его судьбу. Две из политик управления памятью GL_POLICY_PINNED_ACTIVE и GL_ POLICY_PINNED_ALWAYS запрещают OpenGL самостоятельно удалять объекты. Различие их в том, что опция ALWAYS позволяет специфицировать для хранения наиболее производительный тип памяти вне зависимости от текущей востребованности объекта графическим конвейером. GL_POLICY_INHERITED показывает, что политика должна быть наследована от родительского объекта, а GL_POLICY_DEFAULT оставляет данные на произвол автоматических механизмов управления памятью.

Политики использования сообщают серверу OpenGL, какие именно действия будут производиться над объектом: по умолчанию или любые, только запись, однократное и многократное использование, а также разделение ресурсов. В последнем случае объект может быть востребован всеми устройствами системы: например, попеременно то видеоускорителем, то платой захвата видео. Вдобавок ко всему объекты снабжены приоритетами.

Для оптимизации распределения памяти в стандарте появились массивы вершин с множественными индексами. Таким образом, удается не только хранить статические наборы вершин в памяти сервера, но и повторно использовать одни и те же элементы массивов, прибегая к многократному индексированию. Концепция упомянутого ранее "заэкранного" рендеринга привела к выделению особого вспомогательного объекта -- буфера Aux. Отныне OpenGL будет поддерживать многопроходный рендеринг на базовом уровне. Также некоторые команды семейства glu, используемые для контроля над утилитарными аспектами функционирования библиотеки (таких, как создание контекста), введены в базовый стандарт.

Римейк десятилетия OpenGL 2
Рис. 3
В OGL 2 для написания процедур-шейдеров применяется C-подобный (сконструирован на основе ANSI C) объектный язык OpenGL Shading Language, с легкостью манипулирующий стандартными объектами библиотеки. Да и сами процедуры являются обыкновенными объектами, подчиняющимися общим правилам. Читая описание языка, постепенно понимаешь смысл, вкладываемый в понятие "программируемый процессор", которых определено четыре: Vertex, Fragment, Pack и Unpack (рис. 3). Эти модули представляют собой различные этапы "графического конвейера" -- обработку вершин и полигонов, фрагментов и текстур, упаковку и распаковку массивов данных. В частности, к юрисдикции Vertex Processor относятся операции трансформации координат вершин, преобразования координат текстурных карт и освещение. Fragment Processor занимается пикселами: здесь происходят наложение текстур, рендеринг тумана и тому подобные вещи. Появление отдельных процессоров для распаковки и упаковки данных стало следствием стремительно растущего многообразия форматов представления пиксельных массивов. Предназначение Pack и Unpack Processors состоит в трансляции пиксельных потоков, двигающихся между процессором фрагментов и памятью клиентского приложения.

Однако не все операции обработки фрагментов выполняются соответствующим процессором. Существует ряд так называемых "фиксированных" функций, относящихся к неизменному и непрограммируемому базису библиотеки. В частности, в данную категорию попали Alpha Test, Stencil Test, Perspective Projection и другие операции. Хотя не воспрещается выполнять любые дополнительные процедуры в рамках модуля, следует лишь помнить, что далее графический конвейер все равно применит стандартные правила в установленной последовательности. Мотивация такого решения -- сохранить указанные элементы конвейера в их первозданной "непрограммируемой" чистоте, чтобы облегчить их аппаратную реализацию.

Главное же отличие программируемого процессора от традиционной архитектуры OpenGL заключается в возможности замены любого из перечисленных модулей специализированной подпрограммой-шейдером. Единственное неудобство в том, что замена производится на уровне процессора, а не функций. Стало быть, если возникла потребность в коррекции или расширении функциональности какого-либо этапа графического конвейера -- будь любезен, спроектируй новый процессор целиком. Наиболее же пикантной является деталь, касающаяся OpenGL Shading Language: как написано в документации, следует говорить скорее о четырех родственных диалектах, нежели о единообразном языке программирования. Каждый из диалектов связан с одним из процессоров и, следовательно, с объектами и типами данных, применяемыми конкретным процессором, которые не могут быть использованы при создании подпрограммы для другого процессора.

Одна из проблем, решаемая новой версией стандарта, -- задержки графического конвейера, связанные со строгой очередностью выполнения команд и ожиданием сигнала вертикальной синхронизации дисплея. Многие производители чипсетов начали внедрять свои варианты асинхронных расширений -- например, так поступила NVidia, введя в обиход программистов понятие "fences". В первую очередь таким образом удается ускорить обмен командами и подтверждениями между клиентом и сервером OpenGL 2. Раньше клиент по завершении передачи блока команд генерировал маркер Finish и молчаливо ждал отклика от сервера о завершении процесса обработки. Теперь клиент способен продолжать посылать команды, выполнение которых не требует результатов работы их незавершенных предшественниц. Такие "зависимые" команды будут отосланы, когда появится подтверждение выполнения предыдущего блока.

Помимо упорядочения командных потоков, в разрабатываемом стандарте появились и зачатки параллелизма. Теперь некоторые операции, как то: передача больших объемов данных, свопирование буферов при смене кадров и тому подобные вещи отнесены к разряду фоновых и могут исполняться вне зависимости от других команд. Чтобы приложение "не попало впросак" и не попыталось обратиться к области памяти, являющейся субъектом еще не законченного фонового процесса, был определен специальный "синхронизационный параметр" -- тип данных GLsync. С задержкой, обусловленной ожиданием вертикальной синхронизации и разрешения сменить кадр, авторы стандарта расправились с помощью триггеров: клиент выдает в общем потоке команду "свопировать буферы во время T", команда сохраняется в DisplayList сервера OpenGL 2 и в момент времени T исполняется "вне очереди".

Состоянием GLsync можно управлять с помощью команды Fence(), которая говорит серверу о необходимости установить некоторые параметры синхроструктуры после завершения всех операций, отосланных библиотеке до нее. Это позволяет приложению контролировать выполнение фоновых действий.


Так сказал... Джон Кармак

Не будем оригинальными, заключительное суждение о ценности работы, проделанной специалистами 3Dlabs, предоставим сделать величайшему практику OpenGL современности -- Джону Кармаку (John Carmack) из id Software. Но сначала позвольте все-таки сказать несколько слов "от автора".

OpenGL как таковому, кажется, ничто не угрожает. Позиции стандарта в академической и прикладной сферах, на первый взгляд, непоколебимы. Однако уже сейчас компьютерной графике тесно в отведенных первой версией рамках. О количестве фирменных расширений мы уже говорили, а теперь вспомним о таких проектах, как параллельный OpenGL для распределения единого кадра между несколькими независимыми серверами. Учитывая закрытость первоначальной реализации, втиснуть такие вещи в прокрустово ложе спецификаций становится все сложнее, даже учитывая консервативность университетских проектов относительно визуальных красот. Что уж говорить об играх и новомодных графических ускорителях, практически исчерпавших все запасы извлечения дополнительных кадров в секунду. Того и гляди разразится очередная архитектурная революция, и об OpenGL придется забыть. Итак, на наш взгляд, 3Dlabs фактически дарит вторую жизнь этому стандарту. Теперь слово Кармаку.

"Первое впечатление было положительным, поэтому я решил двигаться вперед и переписать движок Doom (речь идет о Doom III. -- Прим. авт.) так, чтобы позволить карте выполнять весь рендеринг за один проход. Наиболее очевидной альтернативой было использование расширений NVidia -- NV_vertex_program и NV_register_combiners -- в комбинации с семью текстурными модулями вместо четырех доступных в GF3/GF4. Вместо этого я решил попробовать новые возможности прототипа OpenGL 2.0.

Кодирование шло очень гладко до тех пор, пока я не столкнулся с ограничениями текущего прототипа компилятора, прежде чем удалось запрограммировать всю необходимую функциональность. Впрочем, мне понравилось. Я действительно собираюсь исследовать эту программную модель после того, как компилятор немного доработают. Пока язык для написания шейдеров является наиболее критичным аспектом, и он может быть выделен как расширение к текущей версии OpenGL, однако есть множество более тонких, но, тем не менее, важных вопросов, которые решены с появлением полного варианта OpenGL 2.0.

Сейчас я настроен поддерживать движок OpenGL 2.0 для Doom на протяжении всего пути развития спецификаций. Во всяком случае, я считаю, что до этого момента я слишком долго бездействовал, не пытаясь продвигать эту идею так, как я мог это делать. Сейчас настал переломный момент, когда необходимо закрепить успех, ведь то, что мы создали, может остаться с нами еще лет на десять.

Конечно, в теории GL2-драйвер не даст никакого превосходства над существующими движками, оптимизированными для карт, способных обрабатывать от 7 и более текстур, но в будущем исследования почти наверняка уйдут от практики низкоуровневого кодирования. И если сейчас появятся новые поставщики (скажем, оживет Rendition) с картой следующего поколения, я бы настойчиво рекомендовал им внедрить GL2 вместо фирменных расширений".
0 
 

Напечатать Отправить другу

Читайте также

 
 
IDC
Реклама

  •  Home  •  Рынок  •  ИТ-директор  •  CloudComputing  •  Hard  •  Soft  •  Сети  •  Безопасность  •  Наука  •  IoT