Войти
ПрограммированиеФорумОбщее

Применение многопоточности в играх. (Комментарии к статье) (6 стр)

Страницы: 15 6 7 810 Следующая »
#75
13:36, 14 ноя. 2003

Boris Batkin
я и не говорил, что он всегда CPU-bound, но бывают моменты, когда это так.
а Backbufferов достаточно двух.
prVovik
>fighter125
>>а почему тогда именно Flip занимает больше всего времени?
>Из-за синхронизации с обратным ходом луча.
а если синхронизация отключена?


#76
14:13, 14 ноя. 2003

Мда, ну вы тут и это, вот...  Amicus Plato (C) Сервантес, и все такое.

fighter
Насчет Present() и прочей ботвы ты не совсем (и/или совсем не) прав.
В Direct3D бОльшая часть команд изменяет внутреннее состояние Direct3D, выставляя dirty-флаги.
При выполнении рендерящих команд (DP, DIP и т.п.) аккумулированные изменения высвапливаются на GPU путем добавления команд в push buffer
(мы говорим исключительно о реализации этого г на nVIDIA).
При этом в push buffer помимо реальных команд вставляются заборы (fences) на ресурсы.
При выполнении Present() в push buffer записываются заборы и команда flip, и все продолжается.
Прохождение через заборы на GPU инициирует прерывания на CPU, что позволяет CPU выполнять побочные функции, в частности:
1. контроль GPU-блокированности ресурсов (что юзает GPU)
2. уход от переполнения push buffer (CPU уйдет в блок, пока GPU не отработает достаточное количество заборов, чтобы освободить буфер)

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

#77
14:18, 14 ноя. 2003

aruslan
Наконец-то, блин. Я все думал, когда ты придешь...

#78
14:22, 14 ноя. 2003

Семен
Зачем обгонять GPU?
Не обгонять (это бесмысленно a priori), а грамотно использовать процессорное время.
При достаточной сложности сцены тебе гарантированы CPU-stallы из-за GPU.
В это время ты вполне можешь посчитать игру на CPU больше и лучше (если хочешь и если применимо).
Собственно, в этом и есть смысл максимального откладывания Present - чтобы, пока тормозит GPU, CPU посчитал.
Очевидным расширением этого принципа является избежание CPU-stall из-за внешней аппаратуры вообще.
В случае с GPU, откладывание Present решает только одну проблему - CPU-stall на время Present.
Но в реальной жизни у тебя может не быть достаточного количества ресурсов, чтобы не обращать внимания на другие проблемы.
Скажем, достаточно одной блокировки буферов, чтобы время Present ушло на блокировку, а сам Present стал бы бесплатен.

#79
15:53, 14 ноя. 2003

prVovik
>Интереснее узнать, часто ли случается ли в реале переполнение того самого
>push-buffer'а, или это может произойти только теоретически?
Ну, по очевидным причинам, если вообще что-то рендерится, то переполнение push buffer происходит всегда :)
Частота переполнений зависит от многих факторов.
У нас переполнения случались регулярно (много данных шло в push buffer GPU).
Во многом, из-за спровоцированного художниками количества различных шейдеров и динамического освещения.
Пока была память - лечилось увеличением количества и размера push buffer.
Рано или поздно приходится искать другие пути.
Нелепые блокировки CPU, как я уже писал, встречаются чаще всего не из-за переполнений, а из-за всего остального.
Переполнения are inevitable.

>Проблема с задержкой в Present() не стоит и выеденного яйца. Ее можно решить и
>не прибегая к многопоточности, например, как предложил Семен.
Устраивает тебя производительность или нет?  Устраивает тебя удобство или нет?
Можешь ли ты реализовать параллельную загрузку ресурсов? Или их динамическую подгрузку?

Вообще, каждый решает те проблемы, которые у него встают.
Есть простая лакмусовая бумажка:

Пусть у нас есть сложная сцена.  Время ее рендеринга (кумулятивное, со всеми CPU-stall) - 16 ms.
Пусть у нас есть сложные игровые процессы. Скажем, нам необходимо 12 ms, чтобы их посчитать.
Чтобы не мучаться, отключим vsync.
Во всех схемах ниже рассматриваются два кадра, начиная с рендера первого и заканчивая обсчетом третьего.

Идеальный мир:
Положим, что у нас с отсылкой на рендер серьезных проблем нет, она практически не блокирует CPU и занимает 4 ms.

1. Схема "старый добрый Дональд Дак":
  отрендерили 1 (4 ms) + flip-ждем-с 1 (12 ms) + посчитали 2 (12 ms) + отрендерили 2 (4 ms) + flip-ждем-с 2 (12 ms) + посчитали 3 (12 ms) = 2x28 ms = 35.7 fps
2. Схема "старый мудрый дядя Сэм":
  отрендерили 1 (4 ms) + посчитали 2 (12 ms) + flip-ждем-с 1 (0 ms) + отрендерили 2 (4 ms) + посчитали 3 (12 ms) + flip-ждем-с 2 (0 ms) = 2x16 ms = 60 fps
3. Лешина схема: 2x16 ms = 60 fps
  поток игры:    concurrent stall (4 ms) + посчитали 2 (12 ms) + concurrent stall (4 ms) + посчитали 3 (12 ms)
  поток рендера: отрендерили 1 (4 ms) + flip-ждем-с 1 (12 ms) + отрендерили 2 (4 ms) + flip-ждем-с 2 (12 ms)

Как я уже писал, схема N 2 в идеальном мире предпочтительна.

#80
20:10, 14 ноя. 2003

Семен
> > Если CPU-bound вызван внутренними причинами (что, как правило, редкость), то да, тогда распараллеливание ничего не даст в принципе.
> Боюсь, не такая уж. В свете тепершних DIP cost и всего остального.... Мне всегда казалось, что CPU-limited - нормальное поведение для игрушек.

Я говорил про реальные (incidental) внутренние причины.
Стоимость DIP - это отдельная тема, круто освещенная в куче документов и в массе выступлений на конференциях, но вызывающая у меня ощущение
какой-то виртуальной реальности.
Я, к сожалению, не имею уже на руках точных цифр, но помню, что добивался пиковой производительности с чудовищным количеством
DrawPrimitives и за-pushbuffer-нутых DrawIndexedPrimitives.
Практика показала, что (да-да, я читал все эти статьи и присутствовал на выступлениях товарищей из nVIDIA) дороже всего оказывается установка шейдеров и констант.  Вот с с большим количеством этих вещей мне ни разу не удалось достичь даже близко пиковой производительности.
При этом я изучал этот феномен с PIXом в руках, и ничего хорошего не обнаружил.


Реальный мир:
С отсылкой на рендер более или менее борода.
Реальный мир вносит некоторые коррективы в сам процесс.
Скажем, передача данных на GPU. Если ваши структуры данных позволяют сразу перейти к DIPам, вам повезло.
Чаще всего, потребуется установка шейдеров (копирование VS в PB на Xbox), установка констант (то же самое).
В худшем случае потребуются round-robin или аналогичные блокировки буферов (только не говорите, что вы их перенесли на этап обсчета игры).
[продолжение следует - надо бежать]

#81
20:41, 14 ноя. 2003

aruslan - Класс! Мастер класс... :)))


Но я всеравно не унимаюсь :). Решать проблему блокировок CPU при пересылке данных из-за переполнения push-buffer'а и прочего с помощью разделения по потокам рендера и физики конечно можно, но сдается мне, что только ТЕОРИТИЧЕСКИ, поскольку на практике, в реальной сложной задаче мы столкнемся с серьезными трудностями. А именно...

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

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

#82
21:47, 14 ноя. 2003

Gnom

> Откуда такая уверенность, что Present() асинхронный?

документацию читал. код писал много и часто.

#83
21:55, 14 ноя. 2003

aruslan

> Ну, по очевидным причинам, если вообще что-то рендерится,
> то переполнение push buffer происходит всегда :)

т.е. игра таки местами GPU bound? ;))))

> Пока была память - лечилось увеличением количества и размера push buffer.
> Рано или поздно приходится искать другие пути.

там кроме размера ещё и кол-во можно настраивать.
типичный throughput \ stall tradeoff.

#84
17:30, 16 ноя. 2003

Boris Batkin
> > Ну, по очевидным причинам, если вообще что-то рендерится,
> > то переполнение push buffer происходит всегда :)
> т.е. игра таки местами GPU bound? ;))))
:)) Либо игра CPU bound, либо будут переполнения, не так ли?

> > Пока была память - лечилось увеличением [b]количества и размера[/b] push buffer.
> > Рано или поздно приходится искать другие пути.
> там кроме размера ещё и кол-во можно настраивать.
> типичный throughput \ stall tradeoff.
Как бы никто и не спорит ;)

Вообще-то вся эта развернувшаяся бодяга меня расстраивает по двум причинам:

1. Леша (LFlip) написал действительно очень классный, красивый и элегантный многопоточный движок.
По словами "написал движок" я подразумеваю то, что программистам был представлен
механизм работы с высокоуровневыми примитивами (секциями, связями, командами), а не просто
обернуты низкоуровневые системные сервисы (потоки, критические секции, eventы).
Разница, очевидно, такая же, как между оберткой над OpenGL/Direct3D и реальной рендерящей подсистемой
игрового движка.
В частности, стало очень тяжело писать код, подверженный deadlock и livelock рискам.  Race conditions, очевидно,
неминуемы, но это такая же идейная платформа, как и языковые exceptions.
При этом Лешин движок вполне продолжает работать в однопоточной версии, и количество реально требуемых потоков ограничено реальной же надобностью в асинхронной работе.
Накладные расходы Леше удалось свести к минимуму (у меня под руками всегда было два движка :)).

2. "Каждый вырывает себе гланды любимым способом".
Балансировка рендера - тяжелая, неблагодарная и не всегда
приносящая дивиденды задача.  Во многих случаях есть способы повысить производительность за счет уровней над рендером или вообще за пределами игры - в контенте.
Скажем, абсолютно дешевая сортировка шейдеров в рендере была уничтожена просто в силу того, что пока рендер аккумулировал шейдеры для последующей сортировки, GPU успевал все отрисовать пусть далеко не на пиковой, но на вполне разумной производительности.
Это же касается классических GPU-stall/CPU-stall/GPU-stall блоков, очень напоминающих резонансный занос, с теми же последствиями.

prVovik
> Необходимо два буфера данных на основе которых рендер строит картинку.
> С первым буфером работает рендер, а со вторым, соответственно, физика.
> После рендеринга каждого кадра поток рендера должен восстановить идентичность
> первого буфера (своего) второму, например, скопировав второй буфер в первый.
> Но дело в том, что размеры этих буферов могут быть столь велики (а так скорее всего и будет),
> что накладные расходы на их копирование превысят всю прибыль от многопоточности.
Во-первых, есть такое слово - page flipping/copying.
Во-вторых, размеры далеко не так велики, как ты себе это представляешь (имеются ввиду актуальные размеры, то есть то, что реально модифицируется игрой).
В-третьих, здесь и вот здесь (собственно, на gamedev.ru есть обсуждение) можно прочесть некоторые идеи о том, как сделать так, чтобы "копирование" или "реплицирование" стало прозрачным и дешевым.

#85
22:56, 16 ноя. 2003

aruslan

> :)) Либо игра CPU bound, либо будут переполнения, не так ли?

;) stall будет, переполнение - совсем не обязательно.

я бы здесь много и плотно думал за вынос части объектов в статические push-buffer-а с патчами.
открывается масса интересных возможностей, и реальная нагрузка на CPU получается реально близкая к 0.
можно даже отдельно "выключать" части, назначая им очень короткие шейдеры, которые заведомо будут "убивать" лишнии треугольники.
tradeoffs очевидные. max-throughput несколько снизится, и данные подготавливать сложнее.
но в такой системе stall будет возникать только тогда, когда CPU убежал вперёд на пару кадров.
и в этом случае его лучше таки подождать.

> 1. Леша (LFlip) написал действительно очень классный, красивый и элегантный многопоточный движок.

совсем верю. и на PC имеет смысл, скорее всего.
хотя-бы потому, что реально контролировать происходящее с GPU не получается.
на XBOX - я бы много раз подумал за другие решения.
опять-же по понятным причинам.

#86
23:08, 16 ноя. 2003

aruslan

>1. Леша (LFlip) написал действительно очень классный, красивый и элегантный многопоточный движок.
>По словами "написал движок" я подразумеваю то, что программистам был представлен
>механизм работы с высокоуровневыми примитивами (секциями, связями, командами), а не просто
>обернуты низкоуровневые системные сервисы (потоки, критические секции, eventы).
>Разница, очевидно, такая же, как между оберткой над OpenGL/Direct3D и реальной рендерящей подсистемой
>игрового движка.
>В частности, стало очень тяжело писать код, подверженный deadlock и livelock рискам. Race conditions, очевидно,
>неминуемы, но это такая же идейная платформа, как и языковые exceptions.
>При этом Лешин движок вполне продолжает работать в однопоточной версии, и количество реально требуемых потоков >ограничено реальной же надобностью в асинхронной работе.
>Накладные расходы Леше удалось свести к минимуму (у меня под руками всегда было два движка :)).

Ну дак если бы кто-то в этом сомневался, уже давно бы разгорелся спор о целесообразности применения движка в целом. Но ведь нет этого! Возможно, имело бы смысл создать отдельный топик "Рендер отдельным потоком"  дабы не травмировать душу народу.
...


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

Реально, хочется в это верить!
А то ж ведь можно продолбаться с "оптимизацией" и ближе к концу заметить, что все коту под хвост.
В такие моменты можно и в смысле жизни разочароваться ;)

Кстати, а почем нынче свечи... то есть игра? В смысле какую именно прибыль можно получить от введения многопоточности если не учитывать простои в Present, а только простои, связанные с переполнениями и пр.? Знать это необходимо, чтобы определить: стоит ли игра свеч. А ведь свечки то дорогие !!!! Очень даже! Причем в их стоимость входит не только субъективное понятие "сложность реализации", но и вполне ощутимые (и объективные!) накладные расходы.

>В-третьих, здесь и вот здесь (собственно, на gamedev.ru есть обсуждение) можно прочесть некоторые идеи о том, как сделать так, чтобы "копирование" или "реплицирование" стало прозрачным и дешевым.

Generic programming тут не при чем. Мы же ведь обсуждаем не проблему проектирования ПО вообще, а более  "приземленные" вещи, как то: реализация конкретного алгоритма. Тут предполагается отсутствие искривлений верхних конечностей девелопера, а такой гипотетический супермен и на ассемблере напишет не хуже, чем на С++ with templates...

#87
11:20, 17 ноя. 2003

Ок, видимо, разобрались. Спасибо всем за комментарии.
Все же, наверное, не надо было использовать пример с рендером в статье. :)

#88
14:03, 17 ноя. 2003

Boris Batkin
> > :)) Либо игра CPU bound, либо будут переполнения, не так ли?
> ;) stall будет, переполнение - совсем не обязательно.
У нас, похоже, расхождение в терминологии.
Под переполнением я понимаю необходимость CPU-stall до выполнения GPU достаточного количества заборов.

> я бы здесь много и плотно думал за вынос части объектов в статические push-buffer-а с патчами.
> открывается масса интересных возможностей, и реальная нагрузка на CPU получается реально близкая к 0.
Думал и пробовал. Единственная причина из-за которой я не стал год назад этого делать - слишком уж слабый выигрыш.
Реально объем патчей сравним с объемом записи в pushbuffer при обычном рендеринге (матрицы и прочие константы, в первую очередь).
Но YMMV, и для других проектов я бы рассматривал определенно такой вариант.

> tradeoffs очевидные. max-throughput несколько снизится, и данные подготавливать сложнее.
Кстати, не сильно и сложнее.  В нашей системе, во всяком случае, это прошло практически незаметно.

Только это все очень глубокий Xbox specific, а учитывая Xbox Next и OpenGL 2  хотелось бы все-таки быть ближе к mainstream.

> можно даже отдельно "выключать" части, назначая им очень короткие шейдеры, которые заведомо будут "убивать" лишнии треугольники.
Гм.

> совсем верю. и на PC имеет смысл, скорее всего. хотя-бы потому, что реально контролировать происходящее с GPU не получается.
> на XBOX - я бы много раз подумал за другие решения. опять-же по понятным причинам.
Ну, по тем же причинам, на Xbox мне принятое нами решение показалось наиболее естественным.
Впрочем, яйца - они все разные.

#89
14:04, 17 ноя. 2003

Семен
Amen.

Страницы: 15 6 7 810 Следующая »
ПрограммированиеФорумОбщее

Тема в архиве.