57

Здесь вы можете увидеть скриншот небольшой программы на C++ под названием Triangle.exe с вращающимся треугольником на основе OpenGL API.

По общему признанию очень простой пример, но я думаю, что это применимо к другим операциям с видеокартами.

Мне было просто любопытно, и я хотел узнать весь процесс от двойного щелчка по Triangle.exe под Windows XP, пока не смогу увидеть вращающийся треугольник на мониторе. Что происходит, как взаимодействуют CPU (который сначала обрабатывает .exe) и GPU (который, наконец, выводит треугольник на экран)?

Я полагаю, что для отображения этого вращающегося треугольника в первую очередь используется следующее аппаратное / программное обеспечение:

аппаратные средства

  • жесткий диск
  • Системная память (RAM)
  • ЦПУ
  • Видеопамять
  • GPU
  • ЖК дисплей

Программного обеспечения

  • Операционная система
  • DirectX/OpenGL API
  • Nvidia Driver

Может кто-нибудь объяснить процесс, может быть, с какой-то блок-схем для иллюстрации?

Это не должно быть сложное объяснение, охватывающее каждый отдельный шаг (предположим, что оно выходит за рамки), но объяснение, за которым может следовать специалист по ИТ.

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

7 ответов7

53

Я решил написать немного о программном аспекте и о том, как компоненты взаимодействуют друг с другом. Может быть, это поможет пролить свет на некоторые области.

Презентация

Что нужно, чтобы нарисовать на экране то единственное изображение, которое вы разместили в своем вопросе?

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

Но, прежде чем мы сможем что-то нарисовать, мы должны сначала запустить некоторые строительные леса. Мы увидим почему позже:

// Clear The Screen And The Depth Buffer
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); 

// Reset The Current Modelview Matrix
glMatrixMode(GL_MODELVIEW); 
glLoadIdentity();

// Drawing Using Triangles
glBegin(GL_TRIANGLES);

  // Red
  glColor3f(1.0f,0.0f,0.0f);
  // Top Of Triangle (Front)
  glVertex3f( 0.0f, 1.0f, 0.0f);

  // Green
  glColor3f(0.0f,1.0f,0.0f);
  // Left Of Triangle (Front)
  glVertex3f(-1.0f,-1.0f, 1.0f);

  // Blue
  glColor3f(0.0f,0.0f,1.0f);
  // Right Of Triangle (Front)
  glVertex3f( 1.0f,-1.0f, 1.0f);

// Done Drawing
glEnd();

Так, что это сделало?

Когда вы пишете программу, которая хочет использовать видеокарту, вы обычно выбираете какой-то интерфейс для драйвера. Некоторые хорошо известные интерфейсы для драйвера:

  • OpenGL
  • Direct3D
  • CUDA

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

Этот интерфейс должен дать вам определенные инструменты. Эти инструменты принимают форму API, который вы можете вызвать из своей программы.

Этот API - это то, что мы видим в примере выше. Давайте внимательнее посмотрим.

Леса

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

Но мы не будем на это смотреть. Мы просто взглянем на то, что вам придется делать в каждом кадре. Подобно:

Очистка экрана

Графический конвейер не собирается очищать экран для каждого кадра. Вы должны будете сказать это. Зачем? Вот почему:

Если вы не очистите экран, вы будете просто рисовать над ним каждый кадр. Вот почему мы вызываем glClear с набором GL_COLOR_BUFFER_BIT . Другой бит (GL_DEPTH_BUFFER_BIT) говорит OpenGL очистить буфер глубины . Этот буфер используется для определения того, какие пиксели находятся впереди (или позади) других пикселей.

преобразование


Источник изображения

Преобразование - это та часть, где мы берем все входные координаты (вершины нашего треугольника) и применяем нашу матрицу ModelView. Это матрица, которая объясняет, как наша модель (вершины) вращается, масштабируется и переводится (перемещается).

Далее мы применяем нашу матрицу проекции. Это перемещает все координаты так, чтобы они правильно смотрели на нашу камеру.

Теперь мы снова преобразуем нашу матрицу Viewport. Мы делаем это, чтобы масштабировать нашу модель до размеров нашего монитора. Теперь у нас есть набор вершин, которые готовы к визуализации!

Мы вернемся к преобразованию чуть позже.

Рисование

Чтобы нарисовать треугольник, мы можем просто сказать OpenGL начать новый список треугольников , вызвав glBegin с константой GL_TRIANGLES .
Есть и другие формы, которые вы можете нарисовать. Как треугольная полоса или треугольный веер . Это в первую очередь оптимизация, поскольку они требуют меньше связи между процессором и графическим процессором, чтобы нарисовать одинаковое количество треугольников.

После этого мы можем предоставить список наборов из 3 вершин, которые должны составлять каждый треугольник. Каждый треугольник использует 3 координаты (как в 3D-пространстве). Кроме того, я также предоставляю цвет для каждой вершины, вызывая glColor3f перед вызовом glVertex3f .

Тень между 3 вершинами (3 угла треугольника) рассчитывается OpenGL автоматически. Он будет интерполировать цвет по всей поверхности многоугольника.

взаимодействие

Теперь, когда вы нажимаете на окно. Приложение должно только захватить сообщение окна, которое сигнализирует о щелчке. Затем вы можете запустить любое действие в вашей программе, которое вы хотите.

Это становится намного сложнее, когда вы хотите начать взаимодействовать с 3D-сценой.

Сначала вы должны четко знать, в каком пикселе пользователь щелкнул окно. Затем, принимая во внимание вашу перспективу , вы можете рассчитать направление луча от точки щелчка мыши до вашей сцены. Затем вы можете рассчитать, пересекается ли какой-либо объект в вашей сцене с этим лучом. Теперь вы знаете, нажал ли пользователь на объект.

Итак, как вы делаете это вращаться?

преобразование

Мне известны два типа преобразований, которые обычно применяются:

  • Матричное преобразование
  • Костная трансформация

Разница в том, что кости влияют на отдельные вершины. Матрицы всегда влияют на все нарисованные вершины одинаково. Давайте посмотрим на пример.

пример

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

Если я хочу повернуть его сейчас, я мог бы сам сделать математику (на процессоре) и просто вызвать glVertex3f с другими координатами (которые вращаются). Или я мог бы позволить GPU делать всю работу, вызывая glRotatef перед рисованием:

// Rotate The Triangle On The Y axis
glRotatef(amount,0.0f,1.0f,0.0f);               

amount , конечно, просто фиксированная стоимость. Если вы хотите анимировать , вам придется отслеживать amount и увеличивать его каждый кадр.

Итак, подождите, что случилось со всеми матричными разговорами ранее?

В этом простом примере нам не нужно заботиться о матрицах. Мы просто называем glRotatef и он позаботится обо всем этом для нас.

glRotate производит вращение angle градусов вокруг вектора xyz. Текущая матрица (см. GlMatrixMode) умножается на матрицу вращения с продуктом, заменяющим текущую матрицу, как если бы glMultMatrix вызывался со следующей матрицей в качестве аргумента:

x 2 ⁡ 1 - c + cx ⁢ y z 1 - c - z ⁢ sx ⁢ z ⁡ 1 - c + y ⁢ s 0 y ⁢ x ⁡ 1 - c + z ⁢ sy 2 ⁡ 1 - c + cy ⁢ z ⁡ 1 - c - x ⁢ s 0 x ⁢ z ⁡ 1 - c - y ⁢ sy ⁢ z ⁡ 1 - c + x ⁢ sz 2 ⁡ 1 - c + c 0 0 0 0 1

Ну, спасибо за это!

Заключение

Становится очевидным, что с OpenGL много говорят. Но это ничего не говорит нам . Где связь?

Единственное, что OpenGL говорит нам в этом примере, это когда это будет сделано. Каждая операция займет определенное количество времени. Некоторые операции занимают невероятно много времени, другие - невероятно быстро.

Отправка вершины в GPU будет настолько быстрой, что я даже не знаю, как это выразить. Отправка тысяч вершин из ЦП в ГП, каждый кадр, скорее всего, не проблема.

Очистка экрана может занять миллисекунду или даже хуже (имейте в виду, что у вас обычно есть только около 16 миллисекунд времени для прорисовки каждого кадра), в зависимости от размера вашего окна просмотра. Чтобы очистить его, OpenGL должен нарисовать каждый пиксель в цвет, который вы хотите очистить, это может быть миллионы пикселей.

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

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

Затем мы можем "визуализировать" результат нашего вычисления (в виде новых цветов) в новую текстуру.

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

Мы действительно лишь слегка коснулись всей темы. Программирование трехмерной графики - адское чудовище.


Источник изображения

37

Трудно понять, что именно ты не понимаешь.

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

Информация состоит из программного обеспечения, необходимого для работы графического процессора. Это программное обеспечение поставляется вместе с драйвером, а затем драйвер обрабатывает разделение ответственности между процессором и графическим процессором (выполняя части своего кода на обоих устройствах).

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

13

Мне было просто любопытно, и я хотел узнать весь процесс от двойного щелчка по Triangle.exe под Windows XP, пока не смогу увидеть вращающийся треугольник на мониторе. Что происходит, как взаимодействуют CPU (который сначала обрабатывает .exe) и GPU (который, наконец, выводит треугольник на экран)?

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

Каков интерфейс между процессором и графическим процессором?

Используя драйвер, центральный процессор может обмениваться данными с такими функциями материнской платы, как PCI, с графической картой и отправлять ей команды для выполнения некоторых инструкций графического процессора, доступа / обновления памяти графического процессора, загрузки кода для выполнения на графическом процессоре и многого другого ...

Но вы не можете прямо говорить с аппаратным обеспечением или драйвером из кода; поэтому это должно происходить через такие API, как OpenGL, Direct3D, CUDA, HLSL, Cg. В то время как первый запускает инструкции GPU и / или обновляет память GPU, последний фактически выполняет код на GPU, поскольку они являются языками физики / шейдеров.

Зачем запускать код на GPU, а не на CPU?

Несмотря на то, что процессор хорошо справляется с нашими ежедневными программами для рабочих станций и серверов, о всей этой блестящей графике, которую вы видите в играх наших дней, особо не задумывались. Когда-то были рендеры программного обеспечения, которые добились цели из некоторых 2D и 3D вещей, но они были очень ограничены. Итак, вот где GPU вступил в игру.

Графический процессор оптимизирован для одного из самых важных вычислений в графике, Matrix Manipulation. В то время как процессор должен вычислять каждое умножение в матричной манипуляции один за другим (позже, такие вещи, как 3DNow! и SSE догнал), GPU может делать все эти умножения одновременно! Параллельность.

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

Как эти инструкции / память / код графического процессора показывают графику?

Есть одна недостающая часть, чтобы заставить это все работать, нам нужно что-то, что мы можем написать, что мы можем затем прочитать и отправить на экран. Мы можем сделать это, создав кадровый буфер. Какую бы операцию вы ни делали, вы в конечном итоге обновите пиксели в кадровом буфере; которые помимо местоположения также содержат информацию о цвете и глубине.

Давайте приведем пример, где вы хотели нарисовать спрайт крови (изображение) где-нибудь; во-первых, сама текстура дерева загружается в память графического процессора, что позволяет легко перерисовывать ее по желанию. Затем, чтобы действительно нарисовать спрайт где-нибудь, мы можем перевести спрайт, используя вершины (расположив его в правильном положении), растеризовав его (превратив его из трехмерного объекта в пиксели) и обновив буфер кадров. Чтобы получить лучшее представление, вот схема конвейера OpenGL из Википедии:

Это основной смысл всей графической идеи, больше исследований - домашнее задание для читателя.

7

Для простоты мы можем описать это так. Некоторые адреса памяти зарезервированы (BIOS и / или операционной системой) не для оперативной памяти, а для видеокарты. Любые данные, записанные в эти значения (указатели), отправляются на карту. Таким образом, теоретически любая программа может писать напрямую на видеокарту, просто зная диапазон адресов, и это было именно так, как это было сделано в старые времена. На практике с современными ОС это управляется видеодрайвером и / или графической библиотекой сверху (DirectX, OpenGL и т.д.).

4

Графические процессоры обычно управляются буферами DMA. Таким образом, драйвер компилирует команды, которые он получает от программы пространства пользователя, в поток инструкций (состояние переключения, рисование таким образом, переключение контекстов и т.д.), Которые затем копируются в память устройства. Затем он инструктирует графический процессор выполнить этот командный буфер через регистр PCI или аналогичные методы.

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

На консолях вы даже можете повеселиться, особенно на PS3.

0

Я думаю, что op не уверен, что именно процессор говорит графической карте, и почему команды, связанные с графикой (например, команды opengl или direct3d), не отправляются напрямую в графический процессор.

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

0

Я думаю, что процессор отправляет видео данные в графический процессор через шину, а затем графический процессор отображает их. Так что более быстрый GPU может обрабатывать больше данных из CPU. Таким образом, часть обработки cpuoffload для GPU. Поэтому вы получаете более высокую скорость в играх.

Это что-то вроде ОЗУ, где процессор хранит вещи, поэтому он может быстро загружаться и обрабатываться. Оба делают игры быстрее.

Или звуковая карта или сетевая карта работают по тому же принципу, то есть получают данные и разгружают некоторую работу процессора.

Всё ещё ищете ответ? Посмотрите другие вопросы с метками .