ollix
2007-02-11, 19:23:07
Hi,
ich habe vor einiger Zeit mal einen kleinen 3x3 und 5x5 Gauß-Weichzeichner mit dynamischem Kernel implementiert, der eine Textur an einer bestimmten Stelle abtastet und einen weichgezeichneten Texel zurückliefert. Jetzt dachte ich mir, guck mal ob das schneller geht und habe mal einige Instruktionen zusammengefaßt in der Hoffnung, da die GPU ja gerne auf Vektoren rechnen bzw. um die SIMD Einheiten zu nutzen, das das schneller geht.
vec4 gaussian_blur_3x3_opt(sampler2D tex, vec2 UV, float amount)
{
float sigma = mix( 0.1, 2.0, amount);
vec2 ka = gaussian_1D( sigma, 0, 1 );
vec3 weights = ka.xxy * ka.xyy;
weights /= dot(weights, vec3( 1, 4, 4));
float kernel[9];
kernel[4] = weights.x;
kernel[1] = kernel[3] = kernel[5] = kernel[7] = weights.y;
kernel[0] = kernel[2] = kernel[6] = kernel[8] = weights.z;
vec2 offsets[9];
offsets[0] = vec2(-GAUSS_RADIUS, -GAUSS_RADIUS); offsets[1] = vec2(0, -GAUSS_RADIUS); offsets[2] = vec2(GAUSS_RADIUS, -GAUSS_RADIUS);
offsets[3] = vec2(-GAUSS_RADIUS, 0); offsets[4] = vec2(0, 0); offsets[5] = vec2(GAUSS_RADIUS, 0);
offsets[6] = vec2(-GAUSS_RADIUS, GAUSS_RADIUS); offsets[7] = vec2(0, GAUSS_RADIUS); offsets[8] = vec2(GAUSS_RADIUS, GAUSS_RADIUS);
vec4 Tblur = vec4(0);
for (int i = 0; i < 9; ++i)
{
vec2 UV_0 = UV + offsets[i];
Tblur += kernel[i] * texture2D(tex, UV_0);
}
return Tblur;
}
vec2 gaussian_1D( float sigma, int x1, int x2 )
{
float c = 1.0 / (TWOPI_SQRT * sigma);
vec2 order = -0.5 * pow( vec2(x1,x2), vec2(2) );
return c * exp( order );
}
Die Berechnung erfolgte ursprünglich durch:
float ka0 = gaussian_1D( sigma, 0 );
float ka1 = gaussian_1D( sigma, 1 );
float sumfrac = 1.0 / (4.0*(ka1*ka1) + 4.0*(ka1*ka0) + (ka0*ka0));
float kernel[9];
kernel[0] = kernel[2] = kernel[6] = kernel[8] = (ka1*ka1)*sumfrac;
kernel[1] = kernel[3] = kernel[5] = kernel[7] = (ka1*ka0)*sumfrac;
kernel[4] = (ka0*ka0)*sumfrac;
Es wurde eigentlich nur die Berechnung eines Gaußwertes durch gaussian_1D() von einem für zwei (sowie drei und vier) Elemente implementiert und die Skalierung der Gewichte auf 1 bzw. das Aufsummieren über eine dotproduct Operation mit der entsprechenden Wertigkeit. Ansonsten ist alles gleich. Ich dachte vielleicht ist es dadurch ja ein bissle schneller, aber im Gegenteil. Die ursprüngliche Variante läuft in einem Test mit ~270fps, die optimierte Variante mit ~249fps auf meiner NV40. Der Unterschied ist nicht groß, aber woran liegt das bzw. wie könnte man das besser machen?
danke
ich habe vor einiger Zeit mal einen kleinen 3x3 und 5x5 Gauß-Weichzeichner mit dynamischem Kernel implementiert, der eine Textur an einer bestimmten Stelle abtastet und einen weichgezeichneten Texel zurückliefert. Jetzt dachte ich mir, guck mal ob das schneller geht und habe mal einige Instruktionen zusammengefaßt in der Hoffnung, da die GPU ja gerne auf Vektoren rechnen bzw. um die SIMD Einheiten zu nutzen, das das schneller geht.
vec4 gaussian_blur_3x3_opt(sampler2D tex, vec2 UV, float amount)
{
float sigma = mix( 0.1, 2.0, amount);
vec2 ka = gaussian_1D( sigma, 0, 1 );
vec3 weights = ka.xxy * ka.xyy;
weights /= dot(weights, vec3( 1, 4, 4));
float kernel[9];
kernel[4] = weights.x;
kernel[1] = kernel[3] = kernel[5] = kernel[7] = weights.y;
kernel[0] = kernel[2] = kernel[6] = kernel[8] = weights.z;
vec2 offsets[9];
offsets[0] = vec2(-GAUSS_RADIUS, -GAUSS_RADIUS); offsets[1] = vec2(0, -GAUSS_RADIUS); offsets[2] = vec2(GAUSS_RADIUS, -GAUSS_RADIUS);
offsets[3] = vec2(-GAUSS_RADIUS, 0); offsets[4] = vec2(0, 0); offsets[5] = vec2(GAUSS_RADIUS, 0);
offsets[6] = vec2(-GAUSS_RADIUS, GAUSS_RADIUS); offsets[7] = vec2(0, GAUSS_RADIUS); offsets[8] = vec2(GAUSS_RADIUS, GAUSS_RADIUS);
vec4 Tblur = vec4(0);
for (int i = 0; i < 9; ++i)
{
vec2 UV_0 = UV + offsets[i];
Tblur += kernel[i] * texture2D(tex, UV_0);
}
return Tblur;
}
vec2 gaussian_1D( float sigma, int x1, int x2 )
{
float c = 1.0 / (TWOPI_SQRT * sigma);
vec2 order = -0.5 * pow( vec2(x1,x2), vec2(2) );
return c * exp( order );
}
Die Berechnung erfolgte ursprünglich durch:
float ka0 = gaussian_1D( sigma, 0 );
float ka1 = gaussian_1D( sigma, 1 );
float sumfrac = 1.0 / (4.0*(ka1*ka1) + 4.0*(ka1*ka0) + (ka0*ka0));
float kernel[9];
kernel[0] = kernel[2] = kernel[6] = kernel[8] = (ka1*ka1)*sumfrac;
kernel[1] = kernel[3] = kernel[5] = kernel[7] = (ka1*ka0)*sumfrac;
kernel[4] = (ka0*ka0)*sumfrac;
Es wurde eigentlich nur die Berechnung eines Gaußwertes durch gaussian_1D() von einem für zwei (sowie drei und vier) Elemente implementiert und die Skalierung der Gewichte auf 1 bzw. das Aufsummieren über eine dotproduct Operation mit der entsprechenden Wertigkeit. Ansonsten ist alles gleich. Ich dachte vielleicht ist es dadurch ja ein bissle schneller, aber im Gegenteil. Die ursprüngliche Variante läuft in einem Test mit ~270fps, die optimierte Variante mit ~249fps auf meiner NV40. Der Unterschied ist nicht groß, aber woran liegt das bzw. wie könnte man das besser machen?
danke