Я начинающий графический программист, и мне недавно стало интересно - как данные модели (сетки и материалы) передаются из приложения (память процессора) на видеокарту (память GPU?)? Скажем, у меня есть статическая модель (например, здание), которую я загружаю и настраиваю один раз и не меняю на протяжении всей жизни приложения.
- Его данные отправляются в память GPU только один раз и остаются там навсегда?
- Когда модель фактически отображается в каждом кадре, должны ли процессоры GPU каждый раз получать свои данные из памяти GPU? Что я имею в виду - если бы у меня было 2 модели, представленные по несколько раз в каждой, - было бы важно, если бы я сначала отображал первую модель несколько раз, а затем - несколько раз, или если я отображал первую только один раз, вторую - только один раз и продолжал чередовать это так? В этом смысле я мог бы назвать этот вопрос «внутренним потоком данных графического процессора».
- Очевидно, что графические карты имеют ограниченный объем ОЗУ - когда он не может содержать все данные модели, необходимые для рендеринга 1 кадра, я думаю, что он продолжает извлекать (частично) из ОЗУ ЦП каждый кадр, верно?
Я знаю, что в Интернете есть много книг и прочего по этому поводу, но, может быть, у вас есть несколько общих рекомендаций о том, как управлять этим потоком данных (когда отправлять что и сколько, когда и как отображать)?
Изменить: я забыл сделать одно различие: есть отправка данных в графический процессор, и есть установка / привязка буферов как текущих . Вызывает ли последний поток данных?
Edit2: после прочтения поста Raxvan я хотел бы выделить несколько действий:
- создание буфера с инициализацией (по его словам, я могу хранить данные как в ЦП, так и в графическом процессоре)
- обновление данных в буфере (что, как я считаю, является простым, когда данные хранятся в оперативной памяти ЦП и требует извлечения из графического процессора в оперативную память ЦП (и затем обратно), когда они хранятся в оперативной памяти ГП)
- связывание буфера как активного (это просто способ сообщить API, что я хочу, чтобы этот буфер отображался при следующем вызове отрисовки, и он ничего не делает сам по себе ?)
- Вызов API-интерфейса (здесь я хотел бы услышать от вас, что на самом деле там происходит)
Ответы:
Обычно да, но драйвер может делать то, что является «оптимальным», данные могут храниться в памяти RAM или RAM или могут быть просто кэшированы здесь - статья, объясняющая, что на самом деле происходит с потоком VBO .
Например, если он был помечен как динамический буфер openGL (например, VBO), он, скорее всего, будет храниться в оперативной памяти. GPU использует прямой доступ к памяти (DMA) для непосредственного доступа к оперативной памяти без вмешательства ЦП, это контролируется контроллером DMA в графической карте и графическом драйвере и выполняется в режиме ядра.
Как и центральным процессорам, графическим процессорам разрешается переупорядочивать инструкции и операции доступа к памяти (читай: выполнение не по порядку ), поэтому, скорее всего, графический процессор будет обрабатывать сценарий, который вы упомянули, обращаясь к памяти, находящейся в его кеше (к которой обычно обращаются недавно ), но иногда это не может сделать это.
Вы не хотите, чтобы это случилось. Но независимо от того, что это произойдет, GPU начнет перемещать память между RAM и VRAM (за это отвечает командный процессор на GPU), что сделает рендеринг намного медленнее, что приведет к зависанию GPU, потому что ему придется ждать данных быть скопированным из / в V / RAM.
Графические процессоры содержат буфер команд , и все команды API передаются в этот буфер, обратите внимание, что это может происходить одновременно с копированием данных в графический процессор. Команда кольцевой буфер очереди связи между CPU и GPU , любая команда , которая должна быть выполнена потребности , которые будут представлены в очередь , чтобы он мог быть execulated ГПУ. Так же, как любая операция, связывающая новые буферы, должна быть отправлена в gpu, чтобы он мог получить доступ к некоторой области памяти.
Это одна из причин, по которой glBegin / glEnd устарела: отправка новых команд требует синхронизации очереди (с использованием ограждений / барьеров памяти).
Что касается других ваших пунктов:
Вы можете выделить буфер без инициализации и сохранить его для дальнейшего использования. Или вы можете выделить для него буфер и копировать данные одновременно (речь идет об уровне API).
Вы можете использовать glMapBuffer для обновления памяти на стороне графического процессора. то, будет ли память скопирована из / в ОЗУ, на самом деле не является стандартной и будет сильно различаться в зависимости от поставщика, типа графического процессора и драйвера.
Мой второй пункт в главном вопросе охватывает это.
Думайте о связывании как об использовании
this
указателя в любом объектно-ориентированном языке, хотя не строго то же самое, любые последующие вызовы API будут относиться к этому буферу связывания.источник
В общем, границы и участие процессора против графического процессора являются специфическими для платформы, но большинство из них следуют этой модели: у процессора есть некоторый оперативный памяти, также GPU, и вы можете перемещать память вокруг (в некоторых случаях оперативная память используется совместно, но для ради простоты давайте придерживаться отдельных баранов).
Первый пункт : данные, которые вы инициализируете, вы можете выбрать для сохранения в памяти ЦП или ОЗУ графического процессора, что является преимуществом для обеих сторон. Когда вы что-то визуализируете, GPU должен выполнять тяжелую работу, поэтому очевидно, что данные, которые уже находятся в памяти GPU, будут обеспечивать лучшую производительность. для процессора он должен сначала отправить данные в графический процессор (который может сохранить их некоторое время), а затем выполнить рендеринг.
Второй момент : в рендеринге есть много хитростей, но основной способ сделать это с полигонами. На кадре gpu будет рендерить объекты, сделанные из полигонов, один за другим, и после завершения этого GPU отправит изображение на дисплей. Нет такого понятия, как объекты, есть только многоугольники, и способ их соединения создаст изображение. работа GPU состоит в том, чтобы спроектировать эти полигоны от 3d до 2d и применить эффект (если требуется). Полигоны идут только по пути CPU-> GPU-> SCREEN или GPU-> SCREEN напрямую (если полигоны уже находятся в области памяти GPU)
Третий момент : например, при рендеринге анимации лучше держать данные близко к процессору, потому что там он выполняет тяжелую работу, поэтому было бы не оптимальным хранить данные в графическом процессоре, перемещать их в ЦП и возвращать каждый кадр. Есть много других примеров, подобных этому, чтобы подсчитать, но в целом все данные останутся близкими к тому, кто делает вычисления. Обычно вы хотите перенести как можно больше данных на оперативную память графического процессора для повышения производительности.
Фактическая отправка данных в gpu осуществляется с помощью используемого вами API (directx / opengl или другого), а концепция связывания и тому подобное - это просто абстракция, так что API понимает, что вы хотите сделать.
Редактировать для редактирования:
buffer creation with initialisation
Это похоже на разницу между inta = 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
После того, как все привязки и настройки буферизуются, это место, где резина встречается с дорогой. Вызов для извлечения возьмет все данные (или идентификаторы, которые указывают на данные), которые вы указали, отправит их в графический процессор (при необходимости) и скажет графическому процессору начать обрабатывать числа. Это не совсем верно, что на всех платформах есть много различий, но для простоты ничья скажет графическому процессору ... рисовать.источник
Самый правильный ответ - это зависит от того, как вы его запрограммируете, но об этом стоит беспокоиться. Несмотря на то, что графические процессоры стали невероятно быстрыми, пропускная способность к ОЗУ и от нее не является и станет вашим самым неприятным узким местом.
Надеюсь, да. Для скорости рендеринга вы хотите, чтобы на графическом процессоре было как можно больше данных, вместо того, чтобы повторно отправлять их каждый кадр. VBO служат именно этой цели. Существуют как статические, так и динамические VBO, первый из которых лучше всего подходит для статических моделей, а второй лучше всего подходит для моделей, вершины которых меняются в каждом кадре (скажем, система частиц). Тем не менее, даже когда речь идет о динамических VBO, вы не хотите пересылать все вершины в каждом кадре; только те, которые меняются.
В случае вашего построения, данные вершин просто сидят там, и единственное, что меняется, это ваши матрицы (модель / мир, проекция и вид).
В случае системы частиц я сделал динамический VBO достаточно большим, чтобы хранить максимальное количество частиц, которое когда-либо будет существовать для этой системы. Каждый кадр я посылаю данные для частиц, испускающих этот кадр, вместе с парой униформ, и это все. Когда я рисую, я могу указать начальную и конечную точку в этом VBO, поэтому мне не нужно удалять данные частиц. Я могу просто сказать, не рисуй их.
Отправка нескольких вызовов на ничью вместо одного - намного больший предел. Проверьте рендеринг экземпляров; это могло бы вам очень помочь и сделать ответ на этот вопрос бесполезным. У меня были некоторые проблемы с драйверами, с которыми я еще не работал, но если вы можете заставить его работать, то проблема решена.
Вы не хотите исчерпать ОЗУ GPU. Если вы это сделаете, то измените вещи, чтобы вы этого не делали. В очень гипотетическом сценарии, который у вас закончится, он, вероятно, каким-то образом рухнет, но я никогда не видел, чтобы это произошло, поэтому, честно говоря, не знаю.
Нет какого-либо значительного потока данных, нет. Это требует определенных затрат, но это верно для каждой строки кода, которую вы пишете. Выяснить, сколько это стоит вам, опять же, для чего нужно профилирование.
Ответ Раксвана звучит хорошо, но не совсем точно. В OpenGL создание буфера не резервирует места. Если вы хотите зарезервировать пространство без передачи каких-либо данных, вы можете вызвать glBufferData и просто передать null. (См. Раздел примечаний здесь .)
Я предполагаю, что вы имеете в виду glBufferData или другие подобные функции, верно? Здесь происходит реальная передача данных. (Если вы не передадите ноль, как я только что сказал в последнем абзаце.)
Да, но это может сделать немного больше, чем это. Например, если вы связываете VAO (объект массива вершин), а затем связываете VBO, этот VBO становится связанным с VAO. Позже, если вы снова свяжете этот VAO и вызовете glDrawArrays, он будет знать, что рисовать VBO.
Обратите внимание, что хотя во многих руководствах вы создадите VAO для каждого VBO, мне сказали, что это не их предназначение. Предположительно, вы должны создать один VAO и использовать его с каждым VBO, имеющим такие же атрибуты. Я еще не пробовал, поэтому не могу точно сказать, лучше это или хуже.
То, что здесь происходит, довольно просто (с нашей точки зрения). Скажем, вы связываете VAO, затем вызываете glDrawArrays. Вы указываете начальную точку и счетчик, и он запускает ваш вершинный шейдер для каждой вершины в этом диапазоне, который, в свою очередь, передает свои выходные данные по линии. Однако весь этот процесс - еще одно эссе.
источник