Подтвердить что ты не робот

Как OpenGL работает на самом низком уровне?

Я понимаю, как писать программы OpenGL/DirectX, и я знаю математику и концептуальные материалы позади нее, но мне любопытно, как взаимодействие графического процессора с процессором работает на низком уровне.

Скажем, у меня есть программа OpenGL, написанная на C, которая отображает треугольник и поворачивает камеру на 45 градусов. Когда я скомпилирую эту программу, она будет превращена в серию ioctl-вызовов, а драйвер gpu затем отправит соответствующие команды в gpu, где вся логика вращения треугольника и установки соответствующих пикселей в соответствующем цвете подключена в? Или программа будет скомпилирована в "программу gpu", которая загружается на gpu и вычисляет вращение и т.д.? Или что-то совсем другое?

Edit: Несколько дней спустя я нашел эту серию статей, которая в основном отвечает на вопрос: http://fgiesen.wordpress.com/2011/07/01/a-trip-through-the-graphics-pipeline-2011-part-1/

4b9b3361

Ответ 1

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

Возможно, возник вопрос: как работает драйвер OpenGL на самом низком уровне. Теперь это снова невозможно ответить в целом, поскольку драйвер тесно связан с каким-то аппаратным обеспечением, что может снова сделать что-то, но разработчик разработал его.

Итак, вопрос должен был быть: "Как он выглядит в среднем за кулисами OpenGL и графической системы?". Давайте посмотрим на это снизу вверх:

  • На самом низком уровне есть какое-то графическое устройство. В настоящее время это графические процессоры, которые предоставляют набор регистров, контролирующих их работу (которые точно регистрируются на устройстве) имеют некоторую программную память для шейдеров, объемную память для входных данных (вершин, текстур и т.д.) И канал ввода-вывода для остальных системы, в которой он получает/отправляет данные и командные потоки.

  • Графический драйвер отслеживает состояние GPU и все прикладные программы ресурсов, которые используют графический процессор. Также он отвечает за преобразование или любую другую обработку данных, отправленных приложениями (преобразовать текстуры в пиксельный формат, поддерживаемый графическим процессором, компилировать шейдеры в машинный код графического процессора). Кроме того, он предоставляет некоторые абстрактные, зависимые от драйверов интерфейсы для прикладных программ.

  • Затем есть драйверная библиотека/драйвер OpenGL, зависящая от драйвера. В Windows это получает загруженный прокси через opengl32.dll, в системах Unix он находится в двух местах:

    • Модуль X11 GLX и драйвер GLX, зависящий от драйвера.
    • и /usr/lib/libGL.so могут содержать некоторые зависящие от драйвера файлы для прямого рендеринга

    В MacOS X это будет "OpenGL Framework".

    Именно эта часть переводит вызовы OpenGL, как вы это делаете, в вызовы к конкретным функциям драйвера в части драйвера, описанной в (2).

  • Наконец, фактическая библиотека API OpenGL, opengl32.dll в Windows и Unix/usr/lib/libGL.so; это в основном просто передает команды на реализацию OpenGL.

То, как происходит фактическое общение, не может быть обобщено:

В Unix соединение 3 ↔ 4 может происходить либо через сокеты (да, оно может и происходит через сеть, если вы хотите), либо через общую память. В Windows интерфейсная библиотека и клиент драйвера загружаются в адресное пространство процесса, так что не так много сообщений, а просто вызовы функций и передача переменных/указателей. В MacOS X это похоже на Windows, только отсутствие разделения между интерфейсом OpenGL и клиентом драйвера (причина, по которой MacOS X настолько не спешит с новыми версиями OpenGL, всегда требует полного обновления операционной системы для доставки нового рамки).

Связь betwen 3 ↔ 2 может проходить через ioctl, чтение/запись или путем сопоставления некоторой памяти в пространстве адресов процесса и конфигурирования MMU для запуска некоторого кода драйвера всякий раз, когда происходят изменения в этой памяти. Это очень похоже на любую операционную систему, так как вам всегда нужно пересекать границу ядра/пользовательского интерфейса: в конечном счете вы проходите через какой-либо системный вызов.

Связь между системой и графическим процессором происходит через перифиальную шину и методы доступа, которые она определяет, поэтому PCI, AGP, PCI-E и т.д., которые работают через порт-ввод-вывод, память Mapped I/O, DMA, IRQ.

Ответ 2

Когда я скомпилирую эту программу, она будет превращена в серию ioctl-вызовов, а драйвер gpu затем отправит соответствующие команды в gpu, где вся логика поворота треугольника и установка соответствующих пикселей в соответствующем цвет подключен? Или программа будет скомпилирована в "программу gpu", которая загружается на gpu и вычисляет вращение и т.д.?

Ты не за горами. Ваша программа вызывает установочный клиентский драйвер (который на самом деле не является драйвером, это общая библиотека пользователей). Это будет использовать ioctl или аналогичный механизм для передачи данных в драйвер ядра.

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

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

Ответ 3

Ваша программа не скомпилирована для какого-либо конкретного графического процессора; он просто динамически связан с библиотекой, которая будет реализовывать OpenGL. Фактическая реализация может включать отправку команд OpenGL на GPU, запуск резервных копий программного обеспечения, компиляцию шейдеров и отправку их на GPU или даже использование резервных копий шейдеров для команд OpenGL. Графический ландшафт довольно сложный. К счастью, связывание изолирует вас от большинства сложностей драйверов, предоставляя разработчикам драйверов возможность использовать любые методы, которые они сочтут нужным.

Ответ 4

Компиляторы/компоновщики C/С++ выполняют одну вещь: они преобразуют текстовые файлы в ряд кодов операций, определенных машиной, которые запускаются на процессоре. OpenGL и Direct3D - это только API C/С++; они не могут магически преобразовать ваш компилятор/компоновщик C/С++ в компилятор/компоновщик для GPU.

Каждая строка кода C/С++, которую вы пишете, будет выполнена на CPU. Вызовы в OpenGL/Direct3D будут вызывать в C/С++ библиотеки, статические или динамические, в зависимости от ситуации.

Единственное место, когда "gpu program" вступает в игру, - это если ваш код явно создает шейдеры. То есть, если вы вызываете вызовы API в OpenGL/D3D, которые вызывают компиляцию и привязку шейдеров. Для этого вы (во время выполнения, а не время компиляции C/С++) генерируете или загружаете строки, которые представляют шейдеры в каком-то шейдерном языке. Затем вы вставляете их через шейдерный компилятор и возвращаете объект в этот API, который представляет этот шейдер. Затем вы применяете один или несколько шейдеров к определенной команде рендеринга. Каждый из этих шагов выполняется явно в направлении вашего кода на C/С++, который, как указано выше, запускается на CPU.

Многие языки шейдеров используют синтаксис типа C/С++. Но это не делает их эквивалентными C/С++.