Russian Qt Forum

Qt => 2D и 3D графика => Тема начата: Igors от Октябрь 14, 2011, 20:35



Название: GLSL (производительность)
Отправлено: Igors от Октябрь 14, 2011, 20:35
Добрый день

Связался с GLSL. Никогда не любил OpenGL со своей памятью ниже средней, но что поделать если есть заказ. Ну ладно, написал первую тысячу строк на GLSL (язык С++ подобный но совсем не одно и то же). Задача в принципе банальный phong shading, ничего более. Получаю бешеные тормоза. Доходит до смешного - software реализация тянет 15 fps, hardware - менее одного. Где-то я растерял неукротимую мощь hardware/GPU, но вот где - понять сложно. Конечно экспериментирую отключая/подключая различные куски GLSL кода + интенсивно гуглю. Кто сталкивался - подскажите, буду признателен.

Спасибо 


Название: Re: GLSL (производительность)
Отправлено: xop от Октябрь 15, 2011, 17:16
1000 строк на GLSL для простого phong shading?? Можно на это посмотреть? :)

Сразу вопрос - какое железо, какая ОС? Не пересоздается или не перелинковывается ли шейдер каждый кадр? И текст шейдеров действительно желательно показать, так будет проще понять в нем ли самом дело. И сразу еще вопрос - а картинка-то корректная получается, или мусор?


Название: Re: GLSL (производительность)
Отправлено: Igors от Октябрь 16, 2011, 09:46
OSX 10.6.8 (на 10.7 результат тот же). Карта
Цитировать
OpenGL Vendor: ATI Technologies Inc.
OpenGL Renderer: ATI Radeon HD 2600 OpenGL Engine
OpenGL Version: 2.1 ATI-1.6.26
Перелинковки не происходит (простые тесты выполняются прилично), для каждого объекта вызывается glUseProgram(shader) в начале рисования и glUseProgram(0) в конце.

"Методом втыка" нашел что не нравится такое
Цитата: cpp
uniform float tbl[1024]; 
Хотелось сделать аккуратный dropoff, вот и потребовалась табличка. Интересно что даже если я выскакиваю return'ом это не спасает от тормозов, напр
Код
C++ (Qt)
gl_FragColor = vec4(1.0, 0.0, 0.0, 1.0);
return;
float test = tbl[i];  // это все равно считается
 

Большой текст связан с тем что надо поддерживать все photoshop blend'ы (multiply, overlay и.т.п). Посмотреть можно, но проект коммерческий, поэтому я Вам напишу в личку, просьба исходники на public не светить.

Спасибо за понимание


Название: Re: GLSL (производительность)
Отправлено: xop от Октябрь 18, 2011, 11:37
Посмотрел сорцы. Печаль ))

Во-первых, по поводу таблицы. В сорцах что-то не нашел ее - можно кинуть вариант с ней? Дальше, про ту же таблицу - обычно такие вещи делаются не массивом uniform, а одномерной текстуркой. И соответствено чтение через texture1DLod( tbl, pos, 0.0 ). Это и быстрее, а если нужна интерполяция и/или ограничения по координатам - еще быстрее, потому что эти операции есть в текстурных семплерах, и на большинстве железа они "бесплатные". Lod - чтобы точно знать, что данные берем с нулевого мипа. Кроме того, некоторые дрова если не могут что-то выполнить аппаратно (например, из-за превышения количества данных через uniformы) - молча падают в software режим. Отсюда и <1 fps.

Во-вторых - от вида сорцов волосы дыбом встают. У видеокарты похоже тоже :) В первую очередь - от количества ifов. Бранчинг (if, for, while, switch) - это медленно. Статический бранчинг (в зависимости от значний uniform) - терпимо. Динамический бранчинг (в зависимости от значений varying) в пиксельном шейдере - это ОЧЕНЬ медленно, если только не соблюдаются условия (оба!):
1) самих ветвей мало
2) высокая когерентность в пространстве экрана, т.е. если большие области пикселей пройдут по одному и тому же пути в ifе

Наиболее типичные способы уменьшать количество ifов:
1) конструкции вида "if( какой-то uniform флажок ) то выполнить это" можно заменить на "#ifdef КАКОЙ_ТО_ДЕФАЙН выполнить это #endif" и компилировать различные версии шейдера для разных конфигураций флажков. Можно заранее, можно на лету и держать кэш шейдеров. Может звучит дико на первый взгляд, но это обычная практика.
2) конструкции вида "for( int i = 0; i < какой-то uniform int; ++i )" можно заменить на "for( int i = 0; i < КАКОЙ_ТО_ДЕФАЙН; ++i )" и опять же компилировать различные версии шейдера
3) конструкции типа if( x < 0 ) x = 0, и вообще, если надо какое-то значение ограничить - пользоваться не ifами, а функциями min, max и clamp. В данном случае например x = max( x, 0 )
4) конструкции типа if( x > threshold ) y += "что-то посчитать, но несложное" и тому подобное можно делать через step: y += step( threshold, x ) * "что-то посчитать". Если рассчет относительно простой, то быстрее будет его выполнить и умножить на ноль, чем бранчиться
5) конструкции типа if( x > threshold ) y = "один" else y = "два" и тому подобное можно делать через step и mix: y = mix( "один", "два", step( threshold, x ) ). Если рассчеты один и два несложные, то проще выполнить их оба и выбрать один из двух.

Еще момент. Если мы работает с векторными данными (позиция, нормаль, цвет) - крайне желательно делать это в явном виде через вектора. И не забывать, что min/max/clamp/step/mix работают и с векторами. Пример оптимизации маленькой функции в вашем коде. Было:
Код:
float SomeBlend( float r, float r1 )
{
    if (r1 <= 0.5)
        r = (r * r1) * 2.0;
    else
        r = r + (1.0 - r) * (r1 + r1 - 1.0);
    return r;
}
...
dst.r = SomeBlend(src.r, dst.r);
dst.g = SomeBlend(src.g, dst.g);
dst.b = SomeBlend(src.b, dst.b);
Стало:
Код:
vec3 SomeBlend( vec3 r, vec3 r1 )
{
    vec3 a = 2.0 * r * r1;
    vec3 b = r + (vec3(1.0) - r) * (2.0 * r1 - vec3(1.0) ); // а можно и вообще b = mix( 2.0 * r1 - vec3(1.0), vec3(1.0), r );
    return mix( a, b, step( vec3(0.5), r1 ) );
}
...
dst = SomeBlend(src, dst);
Надеюсь не обидитесь за вытаскивание сюда этого маленького фрагмента, просто очень показательный пример, может кому еще пригодится посмотреть.

Еще момент - если нужно делать линейную интерполяцию - не нужно изобретать велосипед и писать (1.0 - t) * a + t * b,и a + t*(b-a) тоже не надо - используйте стандартную функцию mix.

В общем, там еще много странных решений я увидел, чуть позже отпишу по ним, но для начала думаю и этого хватит с головой.


Название: Re: GLSL (производительность)
Отправлено: Igors от Октябрь 18, 2011, 14:42
Дальше, про ту же таблицу - обычно такие вещи делаются не массивом uniform, а одномерной текстуркой. И соответствено чтение через texture1DLod( tbl, pos, 0.0 ). Это и быстрее, а если нужна интерполяция и/или ограничения по координатам - еще быстрее, потому что эти операции есть в текстурных семплерах,
Здорово! (и интерполяция как раз нужна). А есть ли возможность сделать это не занимая "слот" текстуры? (т.е. по-прежнему имея 8 Texture2D). Спасибо

Стало:
Код:
vec3 SomeBlend( vec3 r, vec3 r1 )
{
    vec3 a = 2.0 * r * r1;
    vec3 b = r + (vec3(1.0) - r) * (2.0 * r1 - vec3(1.0) ); // а можно и вообще b = mix( 2.0 * r1 - vec3(1.0), vec3(1.0), r );
    return mix( a, b, step( vec3(0.5), r1 ) );
}
В данном случае результат не mix(a. b..), но к чему стремиться - понял

Во-вторых - от вида сорцов волосы дыбом встают. У видеокарты похоже тоже :)
Про Ваши волосы не скажу, но у 2 карт на которых проверял - ничего не встает  :) Сегодня перенес на Вындоуз, там карта заметно лучше (NVidia GeForce 8600 GI/PGI/SSE, OpenGL 3.1.0) - в обоих случаях включение разнообразных blend'ов никак не гасит fps (почему не знаю). Правда линкуются с этой картой шейдеры заметно дольше - неск секунд


Название: Re: GLSL (производительность)
Отправлено: xop от Октябрь 18, 2011, 15:07
А есть ли возможность сделать это не занимая "слот" текстуры?
Нет, слот будет занят. Но - этих слотов в современных видеокартах 16 минимум. В той же 8600 их кажется вообще 32. А текстурные координаты вам отдельно передавать не надо.

В данном случае результат не mix(a. b..), но к чему стремиться - понял
Именно mix( a, b, step( 0.5, r1 ) ). Step вернет 0, если r1 < 0.5 и 1, если r1 > 0.5 - причем поканально. А mix соответственно вернет a или b - опять же поканально. Что полностью соответствует предыдущему куску кода.

включение разнообразных blend'ов никак не гасит fps
Вы кажется немного не поняли :) Гасит fps не то, что бранч сработает или нет, а сам факт его наличия. Попробуйте для эксперимента вообще из кода шейдера выпилить все бленды. И еще цикл там по текстурам попробуйте развернуть и тоже убрать ifы. Ну, просто для одного какого-нибудь конкретного простого случая. Если упиралось в производительность именно шейдера (а скорее всего в него упиралось, особенно если на весь экран выводить) - скорее всего вы удивитесь :)

А вообще - пока производительность устраивает - оптимизировать действительно не имеет смысла.


Название: Re: GLSL (производительность)
Отправлено: Igors от Октябрь 18, 2011, 16:43
Вы кажется немного не поняли :) Гасит fps не то, что бранч сработает или нет, а сам факт его наличия.
Точно! (не могу пока привыкнуть к таким финтам). Да. действительно, blend отгрызает 30-40% (не смертельно, но ощутимо)


Название: Re: GLSL (производительность)
Отправлено: xop от Октябрь 18, 2011, 17:27
Посмотрите еще сколько отгрызает чтение 8 текстур, вместо одной. Подозреваю, что еще больше, чем вариации бленда.

Кстати, есть еще один прием, может пригодится :) Если не хочется иметь ограничение на количество текстур на объекте - можно выводить в несколько проходов. Т.е. рисуем объект несколько раз с аппаратным блендом, накладывая разные текстуры. Можно по одной текстуре на проход (это проще всего, но медленнее, чем все разом в шейдере смешать), можно группировать текстуры (это быстрее, но больше комбинаций шейдеров).


Название: Re: GLSL (производительность)
Отправлено: Igors от Октябрь 20, 2011, 18:11
Ну при такой "кровавой" отладке сильно не разгонишься  :) Сбить все бленды с аппаратными - куча возни, дело еще осложняется тем что текстуры могут шейдиться или нет.

Нет предвычислений, любые объекты могут быть добавлены в сцену и в любом кол-ве. Поэтому сразу же "поднимать планку" с оптимизацией непрактично - пользователь может воспринять это как должное, потом запросто можно нажить грыжу с "вариациями шейдеров". Пока скорость хотя бы 50% от Gourand - пусть работает.

Главный критерий оценки (помимо конечно скорости) - соответствие OpenGL preview чистовому рендеру. Сейчас самое уязвимое место lighting (особенно Area Light - то самое окно которое светит). Сначала я хочу вычистить все остальное - но это не так просто. Вчера получил на NVIDIA карте - шейдер "не выгружается". Запустил, ага, надо подправить - но изменения игнорируются. Все проходит (компиляция, линковка, если есть ошибки - компилятор сообщает) но вот работает предыдущий вариант  :) Разбираюсь..