Производительность GLSL — возвращаемое значение/тип функции

Я использую бикубическую фильтрацию для сглаживания карты высот, я реализовал ее на GLSL:

Бикубическая интерполяция: (см. функцию interpolate() ниже)

float interpolateBicubic(sampler2D tex, vec2 t) 
{

vec2 offBot =   vec2(0,-1);
vec2 offTop =   vec2(0,1);
vec2 offRight = vec2(1,0);
vec2 offLeft =  vec2(-1,0);

vec2 f = fract(t.xy * 1025);

vec2 bot0 = (floor(t.xy * 1025)+offBot+offLeft)/1025;
vec2 bot1 = (floor(t.xy * 1025)+offBot)/1025;
vec2 bot2 = (floor(t.xy * 1025)+offBot+offRight)/1025;
vec2 bot3 = (floor(t.xy * 1025)+offBot+2*offRight)/1025;

vec2 mbot0 = (floor(t.xy * 1025)+offLeft)/1025;
vec2 mbot1 = (floor(t.xy * 1025))/1025;
vec2 mbot2 = (floor(t.xy * 1025)+offRight)/1025;
vec2 mbot3 = (floor(t.xy * 1025)+2*offRight)/1025;

vec2 mtop0 = (floor(t.xy * 1025)+offTop+offLeft)/1025;
vec2 mtop1 = (floor(t.xy * 1025)+offTop)/1025;
vec2 mtop2 = (floor(t.xy * 1025)+offTop+offRight)/1025;
vec2 mtop3 = (floor(t.xy * 1025)+offTop+2*offRight)/1025;

vec2 top0 = (floor(t.xy * 1025)+2*offTop+offLeft)/1025;
vec2 top1 = (floor(t.xy * 1025)+2*offTop)/1025;
vec2 top2 = (floor(t.xy * 1025)+2*offTop+offRight)/1025;
vec2 top3 = (floor(t.xy * 1025)+2*offTop+2*offRight)/1025;

float h[16];

h[0] = texture(tex,bot0).r;
h[1] = texture(tex,bot1).r;
h[2] = texture(tex,bot2).r;
h[3] = texture(tex,bot3).r;

h[4] = texture(tex,mbot0).r;
h[5] = texture(tex,mbot1).r;
h[6] = texture(tex,mbot2).r;
h[7] = texture(tex,mbot3).r;

h[8] = texture(tex,mtop0).r;
h[9] = texture(tex,mtop1).r;
h[10] = texture(tex,mtop2).r;
h[11] = texture(tex,mtop3).r;

h[12] = texture(tex,top0).r;
h[13] = texture(tex,top1).r;
h[14] = texture(tex,top2).r;
h[15] = texture(tex,top3).r;

float H_ix[4];

H_ix[0] = interpolate(f.x,h[0],h[1],h[2],h[3]);
H_ix[1] = interpolate(f.x,h[4],h[5],h[6],h[7]);
H_ix[2] = interpolate(f.x,h[8],h[9],h[10],h[11]);
H_ix[3] = interpolate(f.x,h[12],h[13],h[14],h[15]);

float H_iy = interpolate(f.y,H_ix[0],H_ix[1],H_ix[2],H_ix[3]);

return H_iy;
}

Это моя версия, размер текстуры (1025) все еще жестко задан. Использование этого в вершинном шейдере и/или в шейдере оценки тесселяции очень сильно влияет на производительность (20-30 кадров в секунду). Но когда я изменяю последнюю строку этой функции на:

return 0;

производительность увеличивается так же, как если бы я использовал билинейную или ближайшую/без фильтрации.

То же самое происходит с: (я имею в виду, что производительность остается хорошей)

return h[...]; //...
return f.x; //...
return H_ix[...]; //...

Функция интерполяции:

float interpolate(float x, float v0, float v1, float v2,float v3)
{
    double c1,c2,c3,c4; //changed to float, see EDITs

    c1 = spline_matrix[0][1]*v1;
    c2 = spline_matrix[1][0]*v0 + spline_matrix[1][2]*v2;
    c3 = spline_matrix[2][0]*v0 + spline_matrix[2][1]*v1 + spline_matrix[2][2]*v2 + spline_matrix[2][3]*v3;
    c4 = spline_matrix[3][0]*v0 + spline_matrix[3][1]*v1 + spline_matrix[3][2]*v2 + spline_matrix[3][3]*v3;

    return(c4*x*x*x + c3*x*x +c2*x + c1);
};

Fps уменьшается только тогда, когда я возвращаю окончательное значение H_iy. Как возвращаемое значение влияет на производительность?

EDIT Я только что понял, что использовал double в функции interpolate() для объявления c1, c2... и т. д. Я изменил его на float, и теперь производительность остается хорошей с правильным возвращаемым значением. Поэтому вопрос немного меняется:

Как переменная точности double влияет на производительность оборудования и почему другая функция интерполяции не вызвала эту потерю производительности, а только последнюю, поскольку массив H_ix[] тоже был float, как и H_iy?


person David Szalai    schedule 18.11.2013    source источник
comment
Если у вас есть две переменные, a и b, и вы возвращаете только a, b не нужно вычислять, и все вместе оптимизируется. На графических процессорах меньше поддержки двойной точности (карты, ориентированные на вычисления, имеют больше, но все же меньше, чем число с плавающей запятой), поэтому по сравнению с производительностью с плавающей запятой это довольно плохо, не говоря уже о том, что двойная точность удваивает обрабатываемые данные, и, кроме того, больше нужно сделать округление. Наткнулся на это на днях, похоже, это связано... /GPUGems2/gpugems2_chapter20.html   -  person jozxyqk    schedule 18.11.2013
comment
отличная ссылка, спасибо! Что вы имели в виду под примерами «а» и «б»? Я заменил «возврат H_iy» на «возврат интерполяции (...)», который вычисляет H_iy, если я правильно понял.   -  person David Szalai    schedule 18.11.2013
comment
Кроме того, почему вы написали это в комментарии, если это можно было бы написать как ответ? ^^   -  person David Szalai    schedule 18.11.2013
comment
мой плохой, я был ленив.   -  person jozxyqk    schedule 18.11.2013


Ответы (2)


Одна вещь, которую вы можете сделать, чтобы ускорить это, это использовать texelFetch() вместо floor()/texture(), чтобы аппаратное обеспечение не тратило время на фильтрацию. Хотя аппаратная фильтрация работает довольно быстро, отчасти поэтому я дал ссылку на статью gpu gems. . Также теперь есть функция textureSize(), которая сохраняет передачу значений в себя.

GLSL имеет очень агрессивный оптимизатор, который выбрасывает все, что возможно. Итак, скажем, вы потратили целую вечность на вычисление действительно дорогого значения освещения, но в конце просто скажите colour = vec4(1), все ваши вычисления будут проигнорированы, и они будут выполняться очень быстро. Это может занять некоторое время, когда вы пытаетесь сравнить вещи. Я считаю, что это проблема, которую вы видите при возврате разных значений. Представьте, что каждая переменная имеет дерево зависимостей, и если какая-либо переменная не используется в выводе, включая юниформы и атрибуты, и даже на этапах шейдера, GLSL полностью игнорирует ее. Одно место, где я видел, что компиляторы GLSL терпят неудачу, — это копирование входных/выходных аргументов функции, когда в этом нет необходимости.

Что касается двойной точности, аналогичный вопрос здесь: https://superuser.com/questions/386456/why-does-a-geforce-card-perform-4x-slower-in-double-precision-than-a-tesla-card. В общем, графика должна быть быстрой и почти всегда использует одинарную точность. Для вычислительных приложений более общего назначения, например, для научных симуляций, двойники, конечно, дают более высокую точность. Вы, вероятно, найдете гораздо больше об этом в связи с CUDA.

person jozxyqk    schedule 18.11.2013

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

uniform sampler2D texture;
uniform sampler2D mask;
uniform vec2 texOffset;
varying vec4 vertColor;
varying vec4 vertTexCoord;
void main() {
  vec4 p0 = texture2D(texture, vertTexCoord.st).rgba;
  vec2 d  = texOffset * 0.125;
  vec4 p1 = texture2D(texture, vertTexCoord.st+vec2( d.x, d.y)).rgba;
  vec4 p2 = texture2D(texture, vertTexCoord.st+vec2(-d.x, d.y)).rgba;
  vec4 p3 = texture2D(texture, vertTexCoord.st+vec2( d.x,-d.y)).rgba;
  vec4 p4 = texture2D(texture, vertTexCoord.st+vec2(-d.x,-d.y)).rgba;
  gl_FragColor = (  2.0*p0   + p1 + p2 + p3 + p4)/6.0;
 }

и это результат

  • первое изображение - стандартная интерполяция Hradware
  • второе изображение - бикубическая интерполяция с использованием кода выше
  • та же бикубическая интерполяция, но с дискретизированным цветом, чтобы увидеть контуры

Первое изображение

person Prokop Hapala    schedule 25.01.2014
comment
Немного старый пост, но спасибо, что поделились им! Сейчас я больше в криптологии, но я попробую. Выглядит неплохо. - person David Szalai; 26.01.2014