Какова стоимость смены государства?

25

Предполагается, что у программистов есть достаточно хорошее представление о стоимости определенных операций: например, о стоимости инструкций по ЦП, о потере кэша L1, L2 или L3, о стоимости LHS.

Когда дело доходит до графики, я понимаю, что понятия не имею, что это такое. Я имею в виду, что если мы упорядочим их по стоимости, изменения состояния будут примерно такими:

  1. Изменение шейдера.
  2. Активное изменение буфера вершин.
  3. Активная смена текстур.
  4. Активная смена шейдерной программы.
  5. Активная смена кадрового буфера.

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

Жюльен Геро
источник

Ответы:

26

Большинство данных, которые я видел, относительно относительной стоимости различных изменений состояния, взяты из выступления Кэса Эверитта и Джона Макдональда о снижении накладных расходов OpenGL API с января 2014 года. Их выступление включало в себя этот слайд (в 31:55):

Относительная стоимость изменений состояния

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

Натан Рид
источник
2
Я выбираю этот ответ, так как он дает порядки величин, что является наиболее близким к тому, что я спросил, хотя упомянутый источник не дает большого объяснения.
Жюльен Геро,
27

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

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

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

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

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

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

Большая часть затрат на изменение состояния ресурсов шейдеров - это сторона процессора. Всякий раз, когда вы устанавливаете новый постоянный буфер, драйвер, скорее всего, копирует содержимое этого постоянного буфера в поток команд для графического процессора. Если вы установили одну форму, драйвер, скорее всего, превратит ее в большой постоянный буфер за вашей спиной, поэтому он должен найти смещение этой формы в постоянном буфере, скопировать значение в, а затем отметить постоянный буфер. как грязный, так что он может быть скопирован в поток команд перед следующим вызовом отрисовки. Если вы связываете новую текстуру или буфер вершин, драйвер, вероятно, копирует управляющую структуру для этого ресурса. Кроме того, если вы используете дискретный графический процессор в многозадачной ОС, драйвер должен отслеживать каждый используемый вами ресурс и когда вы начинаете его использовать, чтобы ядро ​​' Диспетчер памяти графического процессора может гарантировать, что память для этого ресурса будет постоянно находиться в VRAM графического процессора, когда происходит рисование. Принцип:изменения состояния заставляют драйвер перемешивать память, генерируя минимальный поток команд для графического процессора.

Когда вы меняете текущий шейдер, вы, вероятно, вызываете промах кеша GPU (у них тоже есть кеш инструкций!). В принципе, работа процессора должна быть ограничена помещением новой команды в поток команд, говорящей «используйте шейдер». В действительности, однако, существует целый беспорядок компиляции шейдеров. Драйверы GPU очень часто лениво компилируют шейдеры, даже если вы создали шейдер заранее. Более уместно в этой теме, однако, некоторые состояния изначально не поддерживаются аппаратным обеспечением GPU и вместо этого компилируются в шейдерную программу. Одним из популярных примеров являются форматы вершин: они могут быть скомпилированы в вершинный шейдер вместо того, чтобы быть отдельным состоянием на чипе. Так что, если вы используете форматы вершин, которые вы не использовали ранее с определенным вершинным шейдером, теперь вы, возможно, платите кучу затрат на процессор, чтобы исправить шейдер и скопировать шейдерную программу в графический процессор. Кроме того, компилятор драйвера и шейдера может сговорить делать все что угодно, чтобы оптимизировать выполнение программы шейдера. Это может означать оптимизацию структуры памяти ваших униформ и структур управления ресурсами, чтобы они были аккуратно упакованы в смежные регистры памяти или шейдеров. Поэтому, когда вы меняете шейдеры, это может заставить драйвер посмотреть на все, что вы уже связали с конвейером, перепаковать его в совершенно другой формат для нового шейдера и затем скопировать это в поток команд. Принцип: Это может означать оптимизацию структуры памяти ваших униформ и структур управления ресурсами, чтобы они были аккуратно упакованы в смежные регистры памяти или шейдеров. Поэтому, когда вы меняете шейдеры, это может заставить драйвер посмотреть на все, что вы уже связали с конвейером, перепаковать его в совершенно другой формат для нового шейдера и затем скопировать это в поток команд. Принцип: Это может означать оптимизацию структуры памяти ваших униформ и структур управления ресурсами, чтобы они были аккуратно упакованы в смежные регистры памяти или шейдеров. Поэтому, когда вы меняете шейдеры, это может заставить драйвер посмотреть на все, что вы уже связали с конвейером, перепаковать его в совершенно другой формат для нового шейдера и затем скопировать это в поток команд. Принцип:смена шейдеров может вызвать много перестановок памяти процессора.

Изменения кадрового буфера, вероятно, наиболее зависят от реализации, но, как правило, довольно дорогие для графического процессора. Ваш графический процессор может быть не в состоянии одновременно обрабатывать несколько вызовов отрисовки для разных целей рендеринга, поэтому может потребоваться остановить конвейер между этими двумя вызовами отрисовки. Может потребоваться очистить кэш, чтобы можно было прочитать цель рендеринга позже. Возможно, потребуется решить работу, которую он отложил во время розыгрыша. (Очень часто накапливается отдельная структура данных вместе с буферами глубины, целевыми объектами рендеринга MSAA и т. Д. Это может потребоваться завершить при переключении с этого целевого объекта рендеринга. Если вы используете графический процессор на основе тайлов Как и во многих мобильных графических процессорах, при переключении из буфера кадра может потребоваться сброс большого объема фактической работы с затенением.) Принцип:изменение целей рендеринга на GPU обходится дорого.

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

Джон Калсбек
источник