Как я могу улучшить производительность моей собственной генерации глубинной текстуры OpenGL ES 2.0?

У меня есть приложение для 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?

39
задан Community 23 May 2017 в 12:01
поделиться