У меня есть приложение с открытым исходным кодом для 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, но я не могу придумать лучшего способа сделать это.
Мой шейдер глубинных фрагментов для сфер (наиболее распространенный элемент отображения), как представляется, лежит в основе этого узкого места (использование Renderer в инструментах привязано к 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 циклов GPU, в худшем - 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);
}
он занимает 18-35 мс на iPad 1, но только 1,7 - 2,4 мс на iPhone 4. Оценочный счетчик GPU для этого шейдера составляет 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?