Russian Qt Forum
Ноябрь 24, 2024, 02:04 *
Добро пожаловать, Гость. Пожалуйста, войдите или зарегистрируйтесь.
Вам не пришло письмо с кодом активации?

Войти
 
  Начало   Форум  WIKI (Вики)FAQ Помощь Поиск Войти Регистрация  

Страниц: [1]   Вниз
  Печать  
Автор Тема: GLSL (производительность)  (Прочитано 6388 раз)
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« : Октябрь 14, 2011, 20:35 »

Добрый день

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

Спасибо 
Записан
xop
Гость
« Ответ #1 : Октябрь 15, 2011, 17:16 »

1000 строк на GLSL для простого phong shading?? Можно на это посмотреть? Улыбающийся

Сразу вопрос - какое железо, какая ОС? Не пересоздается или не перелинковывается ли шейдер каждый кадр? И текст шейдеров действительно желательно показать, так будет проще понять в нем ли самом дело. И сразу еще вопрос - а картинка-то корректная получается, или мусор?
« Последнее редактирование: Октябрь 15, 2011, 17:23 от xop » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #2 : Октябрь 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 не светить.

Спасибо за понимание
Записан
xop
Гость
« Ответ #3 : Октябрь 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.

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

Сообщений: 11445


Просмотр профиля
« Ответ #4 : Октябрь 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 (почему не знаю). Правда линкуются с этой картой шейдеры заметно дольше - неск секунд
Записан
xop
Гость
« Ответ #5 : Октябрь 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ы. Ну, просто для одного какого-нибудь конкретного простого случая. Если упиралось в производительность именно шейдера (а скорее всего в него упиралось, особенно если на весь экран выводить) - скорее всего вы удивитесь Улыбающийся

А вообще - пока производительность устраивает - оптимизировать действительно не имеет смысла.
« Последнее редактирование: Октябрь 18, 2011, 15:10 от xop » Записан
Igors
Джедай : наставник для всех
*******
Offline Offline

Сообщений: 11445


Просмотр профиля
« Ответ #6 : Октябрь 18, 2011, 16:43 »

Вы кажется немного не поняли Улыбающийся Гасит fps не то, что бранч сработает или нет, а сам факт его наличия.
Точно! (не могу пока привыкнуть к таким финтам). Да. действительно, blend отгрызает 30-40% (не смертельно, но ощутимо)
Записан
xop
Гость
« Ответ #7 : Октябрь 18, 2011, 17:27 »

Посмотрите еще сколько отгрызает чтение 8 текстур, вместо одной. Подозреваю, что еще больше, чем вариации бленда.

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

Сообщений: 11445


Просмотр профиля
« Ответ #8 : Октябрь 20, 2011, 18:11 »

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

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

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


Страница сгенерирована за 0.078 секунд. Запросов: 23.