Как повысить FPS и преодолеть пропускную способность памяти за счет случайного доступа к текстурам?

В моей программе виртуальной реальности я сильно ограничен пропускной способностью памяти:

#version 320 es
precision lowp float;

const int n_pool = 30;

layout(local_size_x = 8, local_size_y = 16, local_size_z = 1) in;
layout(rgba8, binding = 0) writeonly uniform lowp image2D image;
layout(rgba8, binding = 1) readonly uniform lowp image2DArray pool;

uniform mat3 RT[n_pool]; // <- this is a rotation-translation matrix

void main() {
    uint u = gl_GlobalInvocationID.y;
    uint v = gl_GlobalInvocationID.x;
    vec4 Ir = imageLoad(pool, ivec3(u,v,29));
    float cost = 1.0/0.0;

    for (int j = 0; j < 16; j++) {
        float C = 0.0;
            for (int i = 0; i < n_pool; i++) {
                vec3 w = RT[i]*vec3(u,v,j);
                C += length(imageLoad(pool, ivec3(w[0],w[1],i)) - Ir);
            }
        }
        cost = C < cost ? C : cost;
    }
    imageStore(image, ivec2(u,v), vec4(cost, cost, cost, 1.0));
}

Вы можете видеть, что у меня есть много случайных обращений к TEXTURE_2D_ARRAY (ширина = 320, высота = 240, слои = 30). Однако доступ не такой уж случайный, потому что он будет в непосредственной близости от u,v.

Вот мои мысли:

  • другой формат текстуры вместо rgba-floats (может быть, rgba-unsigned byte?).
  • общая память слишком мала, чтобы хранить даже одно изображение в градациях серого.
  • изменение порядка циклов. Как ни странно, этот порядок быстрее, хотя другой должен иметь лучшее поведение кэширования.
  • изменение размеров рабочих групп, чтобы они лучше соответствовали текстурам.
  • с использованием сжатых изображений (маловероятный сценарий, обеспечивающий повышение производительности). Теоретически, однако, это должно помочь с пропускной способностью.

о чем ты думаешь?


person Armen Avetisyan    schedule 27.09.2016    source источник
comment
На самом деле вы выполняете поиск текстур 30 * 16. Мне интересно, какова цель чтения таких тяжелых текстур на устройстве OpenGLES. Если вы не предоставите больше информации о назначении кода, оптимизация будет немного сложной.   -  person codetiger    schedule 27.09.2016
comment
Вы можете грубо оптимизировать карту глубины (здесь 16 дискретных значений глубины) сцены. Выполняя некоторое матричное преобразование из одного кадра в другой, вы можете сопоставить пиксели из последующих кадров (при условии, что ваше значение глубины правильное). Смотрите отредактированную версию.   -  person Armen Avetisyan    schedule 27.09.2016
comment
Слова «грубая сила» и «оптимизация» редко встречаются в одном предложении. Какой у вас формат текстур RGBA F32?   -  person solidpixel    schedule 27.09.2016
comment
... также, на каком графическом процессоре вы работаете?   -  person solidpixel    schedule 27.09.2016
comment
назовите это исчерпывающим поиском или поиском грубой силы... одно и то же, но другое имя   -  person Armen Avetisyan    schedule 28.09.2016


Ответы (2)


Есть ли у вас фактические данные, которые показывают, что проблема заключается в пропускной способности текстур, или это просто предположение?

Я вижу ряд проблем, которые означают, что на самом деле это не может быть вашей проблемой. Например:

vec3 w = RT[i]*vec3(u,v,j);

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

Если вы используете ввод текстур fp16 или fp32 RGBA, то более узкие 8-битные форматы unorm всегда будут быстрее (fp32 особенно дорог).

Для следующих:

cost = C < cost ? C : cost;

... вероятно, более надежным с точки зрения генерации кода является использование встроенной функции min().

person solidpixel    schedule 27.09.2016
comment
На большинстве процессоров (и, я думаю, на графических процессорах) A = B < C ? E : F — это одна инструкция и, следовательно, очень быстрая. В остальном я думаю, что вы были правы, посмотрите мое третье обновление. Предварительно вычислив некоторые матрицы перед циклом, я мог наблюдать безумный прирост производительности. - person Armen Avetisyan; 28.09.2016
comment
Да, большинство компиляторов преобразовали бы это в одну инструкцию, но ESSL имеет встроенную библиотеку функций, которая практически гарантированно работает быстро (и делает код более читабельным), так почему бы не использовать ее... - person solidpixel; 28.09.2016
comment
Я сделал. (только для удобства чтения). Похоже оптимизация под GPU в OpenGL это много проб и ошибок... проверенные шаблоны не всегда применимы - person Armen Avetisyan; 28.09.2016

ОБНОВЛЕНИЕ 1

  • переход от обычного пиксельного конвейера к вычислительному шейдеру привел к 3-кратному ускорению

ОБНОВЛЕНИЕ 2

  • использование сжатых форматов увеличивает FPS на 5%

ОБНОВЛЕНИЕ 3

  • В этой упрощенной версии я не показал, что действительно создал много временных векторов на лету (как во внешнем, так и во внутреннем цикле). Удалив создание mat3/vec4/vec3 в цикле, мы получили ускорение в 2 раза. Меня очень удивляет, что создание векторов в циклах так дорого обходится.

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

person Armen Avetisyan    schedule 28.09.2016
comment
Создание векторов дорого в контексте этого цикла, потому что цикл очень короткий. Доступ к текстуре, скорее всего, осуществляется за один цикл, поэтому даже один дополнительный цикл накладных расходов (например, перемещение i в правильное место векторного регистра) вполне может удвоить длину пути кода. - person solidpixel; 28.09.2016