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

58

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

введите описание изображения здесь

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

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

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

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

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

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

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

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

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

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

JohnnyFromBF
источник
Ваша дилемма была бы закончена, если бы вы могли считать GPU расширением CPU!
KawaiKx

Ответы:

55

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

Презентация

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

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

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

// 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-сценой.

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

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

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

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

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

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

пример

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

Если я хочу , чтобы повернуть его сейчас, я мог либо сделать математику сам (на CPU) и просто звонить 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 работает аналогично этому аспекту, но у меня никогда не было возможности работать с ним.

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

введите описание изображения здесь
Источник изображения

Der Hochstapler
источник
38

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

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

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

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

Дэвид Шварц
источник
1
Спасибо за ваше объяснение. По сути, я не понял, как набор команд ЦП взаимодействует с набором команд графического процессора, но, очевидно, именно этот драйвер выполняет эту часть. Вот что я имел в виду со слоями абстракции.
JohnnyFromBF
2
Не задействован набор команд процессора. Драйвер и среда выполнения компилируют ваши CUDA, OpenGL, Direct3D и т. Д. В собственные программы / ядра GPU, которые затем также загружаются в память устройства. Буфер команд тогда относится к тем же ресурсам, что и любой другой.
Аксель Гнейтинг
2
Я не уверен, какие программы вы имеете в виду (которые работают на GPU и включены в драйверы). GPU - это в основном аппаратное обеспечение с фиксированными функциями, и единственные программы, которые он будет запускать, это шейдеры, которые предоставляются приложением, а не драйвером. Драйвер только компилирует эти программы, а затем загружает их в память графического процессора.
Бен Ричардс
1
@ sidran32: Например, в архитектуре nVidia Kepler ядра, потоки и события создаются программным обеспечением, работающим на графическом процессоре, а не (обычно) процессором. Программное обеспечение на стороне GPU также управляет RDMA. Все это программное обеспечение загружается драйвером в память GPU и запускается как «мини-ОС» на GPU, которая обрабатывает сторону GPU взаимодействующей пары CPU / GPU.
Дэвид Шварц
@DavidSchwartz Я забыл про задачи вычислений на GPU. Тем не менее, они все равно ведут себя как шейдеры в реализации. Я бы не назвал это «мини-ОС», так как она не обладает той же функциональностью, которая обычно ассоциируется с ОС. Это все еще очень специализированное программное обеспечение, поскольку графический процессор не спроектирован как процессор (по уважительной причине).
Бен Ричардс
13

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

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

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

Используя драйвер , ЦПУ может обмениваться данными с такими функциями материнской платы, как 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 и т. Д.).

AZ.
источник
1
-1 он спрашивает, как вызов API DirectX от ЦПУ может взаимодействовать с графическим процессором, и ваш ответ «управляется драйвером и / или DirectX» ? Это также не объясняет, как можно запускать пользовательский код (ala CUDA).
BlueRaja - Дэнни Пфлугхофт
3
пожалуйста, научитесь читать. Я сказал, записав конкретные адреса памяти, которые зарезервированы для графического процессора вместо оперативной памяти. И это объясняет, как вы можете запустить все. Диапазон памяти зарегистрирован для карты. Все, что вы пишете в этом диапазоне, поступает в графический процессор, выполняющий обработку вершин, CUDA, что угодно.
А-Я.
5

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

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

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

Аксель Гнейтинг
источник
0

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

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

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

user583641
источник
Это дублирует другой ответ и не добавляет новый контент. Пожалуйста, не публикуйте ответ, если у вас нет чего-то нового.
DavidPostill
0

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

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

Дейв
источник