CPU - поток данных памяти GPU [закрыт]

16

Я начинающий графический программист, и мне недавно стало интересно - как данные модели (сетки и материалы) передаются из приложения (память процессора) на видеокарту (память GPU?)? Скажем, у меня есть статическая модель (например, здание), которую я загружаю и настраиваю один раз и не меняю на протяжении всей жизни приложения.

  • Его данные отправляются в память GPU только один раз и остаются там навсегда?
  • Когда модель фактически отображается в каждом кадре, должны ли процессоры GPU каждый раз получать свои данные из памяти GPU? Что я имею в виду - если бы у меня было 2 модели, представленные по несколько раз в каждой, - было бы важно, если бы я сначала отображал первую модель несколько раз, а затем - несколько раз, или если я отображал первую только один раз, вторую - только один раз и продолжал чередовать это так? В этом смысле я мог бы назвать этот вопрос «внутренним потоком данных графического процессора».
  • Очевидно, что графические карты имеют ограниченный объем ОЗУ - когда он не может содержать все данные модели, необходимые для рендеринга 1 кадра, я думаю, что он продолжает извлекать (частично) из ОЗУ ЦП каждый кадр, верно?

Я знаю, что в Интернете есть много книг и прочего по этому поводу, но, может быть, у вас есть несколько общих рекомендаций о том, как управлять этим потоком данных (когда отправлять что и сколько, когда и как отображать)?

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

Edit2: после прочтения поста Raxvan я хотел бы выделить несколько действий:

  • создание буфера с инициализацией (по его словам, я могу хранить данные как в ЦП, так и в графическом процессоре)
  • обновление данных в буфере (что, как я считаю, является простым, когда данные хранятся в оперативной памяти ЦП и требует извлечения из графического процессора в оперативную память ЦП (и затем обратно), когда они хранятся в оперативной памяти ГП)
  • связывание буфера как активного (это просто способ сообщить API, что я хочу, чтобы этот буфер отображался при следующем вызове отрисовки, и он ничего не делает сам по себе ?)
  • Вызов API-интерфейса (здесь я хотел бы услышать от вас, что на самом деле там происходит)
NPS
источник
Я ни в коем случае не эксперт, но если вы используете современный (то есть не немедленный) OpenGL с VAO и VBO, то данные отправляются в GPU и сохраняются в VRAM всякий раз, когда вы используете одну из команд семейства glBuffer. Затем, каждый раз, когда вы рисуете, соответствующие вершины выбираются из VRAM и отображаются. Если это движущаяся модель, вы склонны хранить ее статически и использовать матрицы для перемещения из пространства модели в пространство мира / камеры. Что касается последнего пункта, я понятия не имею, что произойдет, если у вас заканчивается ОЗУ. Я предполагаю, что если у вас заканчивается VRAM, то данные просто не отправляются, возможно, с кодом ошибки.
Полярный
@ Полярный - не совсем так. GL на самом деле не указывает, в какой памяти хранится буферный объект, и даже может свободно перемещать его во время выполнения в зависимости от модели использования. GL4.4 несколько решает эту проблему, но отмечает, что в итоге лучшее, что он может предоставить, - это «одна из этих глупых подсказок»; см. opengl.org/registry/specs/ARB/buffer_storage.txt и особенно вопросы 2 и 9.
Maximus Minimus
1
@JimmyShelter Ах, спасибо - было бы неплохо, если бы у нас было меньше "этих глупых подсказок" и более конкретная спецификация.
Полярный
@Polar - раздражает то, что ARB_buffer_storage можно было бы избежать , добавив еще одну подсказку, но дизайнеры упустили эту возможность. Ну что ж, возможно, 4,5 наконец-то получит это право.
Максимус Минимус
2
Пожалуйста, не редактируйте свои вопросы, чтобы «отвечать» на ответы. Вместо этого отправьте новый вопрос.

Ответы:

12

Его данные отправляются в память GPU только один раз и остаются там навсегда?

Обычно да, но драйвер может делать то, что является «оптимальным», данные могут храниться в памяти RAM или RAM или могут быть просто кэшированы здесь - статья, объясняющая, что на самом деле происходит с потоком VBO .

Например, если он был помечен как динамический буфер openGL (например, VBO), он, скорее всего, будет храниться в оперативной памяти. GPU использует прямой доступ к памяти (DMA) для непосредственного доступа к оперативной памяти без вмешательства ЦП, это контролируется контроллером DMA в графической карте и графическом драйвере и выполняется в режиме ядра.

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

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

Очевидно, что графические карты имеют ограниченный объем ОЗУ - когда он не может содержать все данные модели, необходимые для рендеринга 1 кадра, я думаю, что он продолжает извлекать (частично) из ОЗУ ЦП каждый кадр, верно?

Вы не хотите, чтобы это случилось. Но независимо от того, что это произойдет, GPU начнет перемещать память между RAM и VRAM (за это отвечает командный процессор на GPU), что сделает рендеринг намного медленнее, что приведет к зависанию GPU, потому что ему придется ждать данных быть скопированным из / в V / RAM.

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

Графические процессоры содержат буфер команд , и все команды API передаются в этот буфер, обратите внимание, что это может происходить одновременно с копированием данных в графический процессор. Команда кольцевой буфер очереди связи между CPU и GPU , любая команда , которая должна быть выполнена потребности , которые будут представлены в очередь , чтобы он мог быть execulated ГПУ. Так же, как любая операция, связывающая новые буферы, должна быть отправлена ​​в gpu, чтобы он мог получить доступ к некоторой области памяти.

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

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

Что касается других ваших пунктов:

Создание буфера с инициализацией

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

обновление данных буфера

Вы можете использовать glMapBuffer для обновления памяти на стороне графического процессора. то, будет ли память скопирована из / в ОЗУ, на самом деле не является стандартной и будет сильно различаться в зависимости от поставщика, типа графического процессора и драйвера.

API draw call (здесь я хотел бы услышать от вас, что на самом деле там происходит).

Мой второй пункт в главном вопросе охватывает это.

связывание буфера как активного (это просто способ сообщить API, что я хочу, чтобы этот буфер> отображался при следующем вызове отрисовки, и он ничего не делает сам по себе?)

Думайте о связывании как об использовании thisуказателя в любом объектно-ориентированном языке, хотя не строго то же самое, любые последующие вызовы API будут относиться к этому буферу связывания.

concept3d
источник
3

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

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

Второй момент : в рендеринге есть много хитростей, но основной способ сделать это с полигонами. На кадре gpu будет рендерить объекты, сделанные из полигонов, один за другим, и после завершения этого GPU отправит изображение на дисплей. Нет такого понятия, как объекты, есть только многоугольники, и способ их соединения создаст изображение. работа GPU состоит в том, чтобы спроектировать эти полигоны от 3d до 2d и применить эффект (если требуется). Полигоны идут только по пути CPU-> GPU-> SCREEN или GPU-> SCREEN напрямую (если полигоны уже находятся в области памяти GPU)

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

Фактическая отправка данных в gpu осуществляется с помощью используемого вами API (directx / opengl или другого), а концепция связывания и тому подобное - это просто абстракция, так что API понимает, что вы хотите сделать.

Редактировать для редактирования:

  • buffer creation with initialisationЭто похоже на разницу между int a = new int[10]и тем, a[0] = 0,a[1] = 1.... etc когда вы создаете буфер, вы освобождаете место для данных, а когда вы инициализируете данные, вы помещаете туда нужный материал.

  • buffer data updateесли он находится на процессоре ЦП, то у вас есть vertex * verticesвозможность поиграть с ним, если его там нет, вам придется переместить его из графического процессора vertex * vertices = map(buffer_id);(карта - мифологическая функция, которая должна перемещать данные из графического процессора в оперативную память процессора, она также имеет свою противоположность buffer_id = create_buffer(vertices);

  • binding the buffer as activeэто просто концепция, которую они называют bindingрендерингом, это сложный процесс, и это похоже на вызов функции с 10000 параметрами. Связывание - это просто термин, который они использовали для определения, куда и куда направляется буфер. За этим термином нет настоящей магии, он не конвертирует, не перемещает и не перераспределяет буферы, а просто сообщает драйверу, что при следующем вызове отрисовки использовать этот буфер.

  • API draw callПосле того, как все привязки и настройки буферизуются, это место, где резина встречается с дорогой. Вызов для извлечения возьмет все данные (или идентификаторы, которые указывают на данные), которые вы указали, отправит их в графический процессор (при необходимости) и скажет графическому процессору начать обрабатывать числа. Это не совсем верно, что на всех платформах есть много различий, но для простоты ничья скажет графическому процессору ... рисовать.

Raxvan
источник
2

Самый правильный ответ - это зависит от того, как вы его запрограммируете, но об этом стоит беспокоиться. Несмотря на то, что графические процессоры стали невероятно быстрыми, пропускная способность к ОЗУ и от нее не является и станет вашим самым неприятным узким местом.

Его данные отправляются в память GPU только один раз и остаются там навсегда?

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

В случае вашего построения, данные вершин просто сидят там, и единственное, что меняется, это ваши матрицы (модель / мир, проекция и вид).

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

Когда модель фактически отображается в каждом кадре, должны ли процессоры GPU каждый раз получать свои данные из памяти GPU? Что я имею в виду - если бы у меня было 2 модели, представленные по несколько раз в каждой, - было бы важно, если бы я сначала отображал первую модель несколько раз, а затем - несколько раз, или если я отображал первую только один раз, вторую - только один раз и продолжал чередовать это так?

Отправка нескольких вызовов на ничью вместо одного - намного больший предел. Проверьте рендеринг экземпляров; это могло бы вам очень помочь и сделать ответ на этот вопрос бесполезным. У меня были некоторые проблемы с драйверами, с которыми я еще не работал, но если вы можете заставить его работать, то проблема решена.

Очевидно, что графические карты имеют ограниченный объем ОЗУ - когда он не может содержать все данные модели, необходимые для рендеринга 1 кадра, я думаю, что он продолжает извлекать (частично) из ОЗУ ЦП каждый кадр, верно?

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

Я забыл сделать одно различие: отправка данных в графический процессор и установка / привязка буферов как текущих. Вызывает ли последний поток данных?

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

создание буфера с инициализацией

Ответ Раксвана звучит хорошо, но не совсем точно. В OpenGL создание буфера не резервирует места. Если вы хотите зарезервировать пространство без передачи каких-либо данных, вы можете вызвать glBufferData и просто передать null. (См. Раздел примечаний здесь .)

обновление данных буфера

Я предполагаю, что вы имеете в виду glBufferData или другие подобные функции, верно? Здесь происходит реальная передача данных. (Если вы не передадите ноль, как я только что сказал в последнем абзаце.)

связывание буфера как активного (это просто способ сообщить API, что я хочу, чтобы этот буфер отображался при следующем вызове отрисовки, и он ничего не делает сам по себе?)

Да, но это может сделать немного больше, чем это. Например, если вы связываете VAO (объект массива вершин), а затем связываете VBO, этот VBO становится связанным с VAO. Позже, если вы снова свяжете этот VAO и вызовете glDrawArrays, он будет знать, что рисовать VBO.

Обратите внимание, что хотя во многих руководствах вы создадите VAO для каждого VBO, мне сказали, что это не их предназначение. Предположительно, вы должны создать один VAO и использовать его с каждым VBO, имеющим такие же атрибуты. Я еще не пробовал, поэтому не могу точно сказать, лучше это или хуже.

Вызов API

То, что здесь происходит, довольно просто (с нашей точки зрения). Скажем, вы связываете VAO, затем вызываете glDrawArrays. Вы указываете начальную точку и счетчик, и он запускает ваш вершинный шейдер для каждой вершины в этом диапазоне, который, в свою очередь, передает свои выходные данные по линии. Однако весь этот процесс - еще одно эссе.

Ледяной вызов
источник
«тогда проблема решена» Да, инстансинг очень помог бы, но без него мне все равно пришлось бы делать вызов для каждого объекта. Одинаковое количество в обоих случаях. Так что мне интересно, если порядок имеет значение.
NPS
@NPS - Это имеет значение некоторые . Если они заказаны, так что вам не нужно постоянно переключать свои привязки, да, это, вероятно, будет на порядок меньше. Но если вам придется изо всех сил сортировать их, это, вероятно, будет гораздо дороже. Слишком много переменных зависит от вашей реализации, чтобы сказать гораздо больше.
Ледяной неповиновение