У меня есть приложение для iOS с открытым исходным кодом, которое использует специальные шейдеры OpenGL ES 2.0 для отображения трехмерных представлений молекулярных структур. Он делает это с помощью процедурно созданных самозванцев из сфер и цилиндров, нарисованных поверх прямоугольников, вместо тех же форм, построенных с использованием множества вершин. Обратной стороной этого подхода является то, что значения глубины для каждого фрагмента этих объектов-самозванцев необходимо вычислять в фрагментном шейдере, который будет использоваться при перекрытии объектов.
К сожалению, OpenGL ES 2. 0 не позволяет вам писать в gl_FragDepth , поэтому мне нужно было вывести эти значения в текстуру настраиваемой глубины. Я прохожу через свою сцену, используя объект кадрового буфера (FBO), визуализируя только цвет, соответствующий значению глубины, а результаты сохраняются в текстуре. Затем эта текстура загружается во вторую половину моего процесса рендеринга, где создается фактическое изображение экрана. Если фрагмент на этом этапе находится на уровне глубины, сохраненном в текстуре глубины для этой точки на экране, он отображается. Если нет, его бросают. Подробнее о процессе, включая диаграммы, можно найти в моем посте здесь .
Создание этой текстуры глубины является узким местом в моем процессе рендеринга, и я ищу способ сделать это быстрее . Это кажется медленнее, чем должно быть, но я не могу понять почему. Чтобы добиться правильного создания этой текстуры глубины, GL_DEPTH_TEST
отключен, GL_BLEND
включен с помощью glBlendFunc (GL_ONE, GL_ONE)
и glBlendEquation ()
устанавливается на GL_MIN_EXT
. Я знаю, что такой способ вывода сцены - не самый быстрый для тайлового отложенного рендерера, такого как серия PowerVR на устройствах iOS, но я не могу придумать лучшего способа сделать это.
Мой шейдер фрагмента глубины для сфер (наиболее распространенный элемент отображения), похоже, лежит в основе этого узкого места (использование средства визуализации в инструментах привязано к 99%, что указывает на то, что я ограничен обработкой фрагментов). В настоящее время это выглядит следующим образом:
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
const vec3 stepValues = vec3(2.0, 1.0, 0.0);
const float scaleDownFactor = 1.0 / 255.0;
void main()
{
float distanceFromCenter = length(impostorSpaceCoordinate);
if (distanceFromCenter > 1.0)
{
gl_FragColor = vec4(1.0);
}
else
{
float calculatedDepth = sqrt(1.0 - distanceFromCenter * distanceFromCenter);
mediump float currentDepthValue = normalizedDepth - adjustedSphereRadius * calculatedDepth;
// Inlined color encoding for the depth values
float ceiledValue = ceil(currentDepthValue * 765.0);
vec3 intDepthValue = (vec3(ceiledValue) * scaleDownFactor) - stepValues;
gl_FragColor = vec4(intDepthValue, 1.0);
}
}
На iPad 1 это занимает от 35 до 68 мс для визуализации кадра модели заполнения пространства ДНК с использованием сквозного шейдера для отображения (от 18 до 35 мс на iPhone 4). Согласно компилятору PowerVR PVRUniSCo (часть их SDK ), этот шейдер использует в лучшем случае 11 циклов графического процессора, в худшем - 16 циклов. Я знаю, что вам не рекомендуется использовать ветвление в шейдере, но в этом случае это привело к лучшей производительности, чем в противном случае.
Когда я упрощаю его до
precision mediump float;
varying mediump vec2 impostorSpaceCoordinate;
varying mediump float normalizedDepth;
varying mediump float adjustedSphereRadius;
void main()
{
gl_FragColor = vec4(adjustedSphereRadius * normalizedDepth * (impostorSpaceCoordinate + 1.0) / 2.0, normalizedDepth, 1.0);
}
, на iPad 1 это занимает 18-35 мс, но только 1,7 - 2,4 мс на iPhone 4. Расчетное количество циклов графического процессора для этого шейдера составляет 8 циклов. Изменение времени рендеринга на основе количества циклов не кажется линейным.
Наконец, если я просто выведу постоянный цвет:
precision mediump float;
void main()
{
gl_FragColor = vec4(0.5, 0.5, 0.5, 1.0);
}
время рендеринга упадет до 1,1 - 2,3 мс на iPad 1 (1,3 мс на iPhone 4) .
Нелинейное масштабирование времени рендеринга и внезапное изменение между iPad и iPhone 4 для второго шейдера заставляют меня думать, что я кое-что упустил. Полный исходный проект, содержащий эти три варианта шейдера (посмотрите файл SphereDepth.fsh и закомментируйте соответствующие разделы), и тестовую модель можно загрузить из здесь , если вы хотите попробовать это сами.
Если вы дочитали до этого места, мой вопрос: на основе этой информации о профилировании, как я могу улучшить производительность рендеринга моего настраиваемого шейдера глубины на устройствах iOS?