Метод Onloading - перенос части нагрузки на CPU

By Josh Doss

Скачать статью

Скачать CPU Onloading: Leveraging the PC Platform [Eng., PDF 877KB]

Введение

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

Экспериментальный метод корпорации Intel, который мы назвали «Onloading», - это попытка повышения производительности путем использования ресурсов CPU для выполнения задач, которые обычно выполняются графическим процессором. Это может быть особенно важно для платформ, где графика обрабатывается процессором: обычно производительность таких систем ниже, чем у систем с мощными дискретными графическими адаптерами.

Несколько лет назад поставщики дискретных видеоадаптеров начали предлагать разработчикам перемещать нагрузки, обычно выполняемые центральными процессорами, на графические процессоры (рис 1). [Harris06] Несмотря на усовершенствования в области встроенных и дискретных видеоадаптеров наличных ресурсов все равно может оказаться недостаточно: набор применяемых визуальных эффектов будет ограничен возможностями графического процессора. Эффекты пост-обработки в значительной степени влияют на качество изображения и отличаются серьезными требованиями в отношении крупных текстур и заполнения. Мониторы высокого разрешения уже давно стали стандартом; все чаще используется несколько мониторов одновременно. [Valve11]

Рисунок 1. Перемещение нагрузок с CPU на графический процессор.

Что может CPU

Общая производительность CPU растет в геометрической прогрессии. Благодаря увеличению числа ядер и блоков векторной обработки CPU может выполнять некоторые типовые графические задачи. Но прежде всего нужно убедиться в том, что мы не перенесли типичные для CPU нагрузки, такие как расчет физики или искусственного интеллекта (ИИ), на GPU.

Давайте рассмотрим вашу игру, когда начинается этап оптимизации и поиск узких мест в производительности. Если ваша игра прекрасно масштабируется, используя ресурсы нескольких ядер и потоков, и обладает наибольшей скоростью работы на используемой платформы, то метод Onloading может вам и не понадобиться. Но если вы ограничены возможностями GPU, расчет физики и ИИ уже осуществляется центральным процессором, и вы пытаетесь найти возможность еще активнее использовать CPU, то метод Onloading может вам пригодиться.

Помимо перечисленных выше возможностей, при использовании CPU для типовых задач, обычно выполняемых посредством GPU, возникает ряд проблем. Одной из таких проблем является отсутствие аппаратных блоков с фиксированными функциями, например, блоков обработки текстур и растеризаторов. Нагрузки, связанные с большим объемом растеризации и фильтрации текстур не подходят для использования метода Onloading. Кроме того, в настоящее время в Microsoft® Direct3D нет механизма, позволяющего избежать копирования рабочего буфера, что приводит к существенной избыточной нагрузке. Но эту проблему можно обойти, используя двойную буферизацию в нашем цикле отрисовки изображения.

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

Если мы рассмотрим возможность сбалансированного использования всей платформы (рис. 2), то нужно обеспечить наибольшее использование всех доступных потоков и векторных блоков. К счастью, за последние годы параллельное и векторное программирование получили значительное развитие; для этих областей создано множество средств разработки и накоплено множество знаний для всех платформ. Разработчики, знакомые с консольным программированием, должны хорошо разбираться в этих принципах. Корпорация Intel предоставляет ряд инструментов, при помощи которых разработчики могут использовать ресурсы наших процессоров, в том числе средство Intel® VTune™ Amplifier XE.

Рисунок 2. Наиболее полное ресурсов платформы за счет переноса части графической нагрузки на CPU

Метод Onloading - принципы и технологии

В данный момент мы предполагаем 4 основных режима использования метода Onloading и пытаемся определить возможные нагрузки и задачи для каждого из этих режимов.
  • Внутрикадровые расчеты с участием CPU: использование CPU для выполнения части работы, тогда как GPU выполняет оставшуюся часть работы и управляет общим доступом к обрабатываемым данным.
  • Расчеты пространства экрана с помощью CPU: использование CPU для расчета пост-эффектов после того, как кадр был полностью прорисован с помощью GPU.
  • Формирование данных с помощью CPU: предварительное создание тепловых карт, карт теней и других исходных данных, которые затем поступают для обработки на GPU.
  • Полная конвейерная обработка: дублирование конвейера растеризации трехмерного изображения на CPU для асинхронной обработки медленно движущихся или редко обновляемых компонентов игры.
Внутрикадровые расчеты с участием CPU можно назвать одним из наиболее полезных подходов: CPU создает компонент кадра (пользовательский интерфейс, частицы и пр.), а затем GPU использует этот компонент в виде переданного буфера. Для такого подхода требуется буферизация прорисовки, поскольку в данный момент в существующих графических API не существует механизмов, поддерживающих совместную прорисовку в пределах одного кадра. Применяя внутрикадровые расчеты с участием CPU, разработчик может настроить платформу для определенных аппаратных конфигураций с различными CPU и GPU. Хорошим примером этого подхода является перенос расчетов системы частиц на CPU. Можно добиться высокой производительности системы расчета частиц с прямым растеризатором с помощью разделения на блоки и сортировки для создания множества параллельно обрабатываемых задач и с использованием инструкций SIMD для обработки и прорисовки до 8 частиц одновременно.

Расчет пространства экрана с помощью CPU также представляет определенный интерес: можно с легкостью применять конвейер глубиной n-1 кадров; эффекты пост-обработки и другие полноэкранные эффекты создают высокую нагрузку на GPU. Общий подход такой: отрисовка трехмерной сцены в буфер отрисовки, который затем считывается центральным процессором. Плоскость разделяется на небольшие легко обрабатываемые блоки для максимального использования параллельных вычислений. Для асинхронной обработки данных используется распределение задач. Для наиболее полного использования возможностей CPU важно обрабатывать все данные в векторном виде, чтобы применять архитектуру SIMD, выполняя несколько операций одной инструкцией. CPU дает возможность дальнейшей оптимизации благодаря своей гибкости и большому кэшу: можно выполнять такие задачи как построение карты оттенков путем накопления.

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

Полная конвейерная обработка с участием CPU является наиболее простым подходом к методу Onloading и подразумевает использование CPU и программного растеризатора для отрисовки асинхронных компонентов трехмерной сцены. На приведенном ниже изображении «Onloaded Shadows» применяется данная технология метода Onloading с помощью растеризатора Microsoft® WARP 10 на одном ядре (рис. 3). Из-за неудовлетворительного соответствия графических API, таких как Microsoft® Direct3D 10, архитектуре CPU такой подход является наименее эффективным. При всякой попытке применения этой методики следует тщательно анализировать производительность, чтобы убедиться, что использование CPU дает здесь хоть сколько-нибудь ощутимое преимущество.

Рисунок 3. Демонстрация полной конвейерной обработки с участием CPU

Пост-обработка изображений с высоким динамическим диапазоном. Метод Onloading в действии

В качестве примера обработки пространства экрана с участием CPU можно привести пост-обработку изображений с высоким динамическим диапазоном. Нам удалось добиться в этой реализации показателя в 2 мс на кадр на современном мощном CPU с операцией асинхронного копирования, ограничивающей минимальную длительность расчета одного кадра в 3,5 мс. Снижение задержек достигается конвейерным распределением работы между CPU и GPU таким образом, что GPU занимается отрисовкой следующего кадра, тогда как CPU просчитывает эффекты пост-обработки (рис. 4).

Рисунок 4. Конвейерные вычисления

Ранее мы обсудили важность распределения задач и применения векторных компонентов кода для наибольшей производительности вычислений на CPU. Применение этих принципов к эффектам в пространстве экране осуществляется достаточно просто. Для того чтобы иметь возможность оценить скорость выполнения нагрузки на обоих устройствах (CPU и GPU), используем буфер отрисовки float16. Этот буфер отрисовки копируется на CPU, где осуществляется обработка и фильтрация. Распределение задач дает нам возможность с легкостью разделить задачу фильтрации целого экрана на множество задач меньшего объема путем разделения буфера на блоки (рис. 5). После этого каждый блок можно обрабатывать независимо, а каждый этап алгоритма можно задать в виде зависимости. Дополнительные сведения о распределении задач см. в статье Многозадачная анимационная модель.

Рисунок 5. Разделение на блоки

Можно добиться повышения производительности, используя возможности SIMD (одиночный поток команд, множество потоков данных) центрального процессора. Принцип SIMD дает возможность обработать несколько фрагментов данных одной инструкцией. Возможности применения данного алгоритма можно показать на примере уменьшения разрешения. Берем блок из 16 пикселей (4x4) и начинаем покомпонентное уменьшение разрешения путем векторного сложения (рис. 6). Данные SIMD обрабатываются «по вертикали» [Tonteri10], поэтому нужно взять набор этих четырех вертикальных (частичных) сумм и выполнить транспозицию для наибольшего использования SIMD. Одно дополнительное суммирование после транспозиции дает там в итоге 4 суммы 16-пиксельных блоков (изначально было 64 пикселя). Чтобы завершить уменьшение разрешения, достаточно умножить на 1/16 среднего значения.

Рисунок 6. Вычисление частичной суммы по вертикали для каждого блока пикселей 4х4

Аналогичный поход также можно использовать для вычисления освещенности с помощью SIMD по 4 (SSE) или 8 (Intel® AVX) средним значениям. Гибкость CPU предоставляет преимущество при вычислении освещенности. Для классической технологии пиксельных шейдеров требуется прорисовка полноэкранного прямоугольника поверх буфера отрисовки, для которого просчитывается освещенность. После этого осуществляется уменьшение разрешения; для всей обработки может потребоваться несколько проходов. С помощью CPU можно просто вычислить освещенность и путем сложения получить ее среднее значение для каждого блока.

Рисунок 7. Транспозиция результатов частичной суммы и вычисление итоговой суммы

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

Время экспериментов

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

Для получения подробной информации о возможностях оптимизации компилятора обратитесь к нашему Уведомлению об оптимизации.