   Bloom/HDR Performance Issues
  Hello fellow game developers!  I am developing a RTS game called Galactic Vice using OpenGL, and it requires Bloom/HDR to be implemented for various reasons (like the Lava effects shown here:).  It looks amazing, but causes a HUGE performance drop!I went ahead an implemented it using the following pseudocode: -- BEGIN CODE -- // Render the 3D scene to a FBO3dScene_FBO[0]->begin();    render3D(renderPass_NORMAL);3dScene_FBO[0]->end();3dScene_FBO[1]->begin();    // Render previous FBO, using a bright shader    // Anything greater in luminance than 0.95 is kept,// Everything else is set to black (0.0.3dScene_FBO[1]->end();3dScene_FBO[2]->begin();    // Render previous FBO, using a vertical blur shader (kernelsize = 8)3dScene_FBO[2]->end();// Reuse the bright-FBO3dScene_FBO[1]->begin();    // Render the previous FBO, using a horizontal blur shader (kernelsize = 8)3dScene_FBO[1]->end();// Render the 3D scene with the new bloom-FBO overlayedglClear(...);// Bind the bloom-FBO and 3D-scene FBO3dScene_FBO[0]->bind(0);3dScene_FBO[1]->bind(1);// Render using a shader that adds the two FBO values// cleanup3dScene_FBO[0]->unBind(0);3dScene_FBO[1]->unBind(1); -- END CODE -- Everything seems to look great!  But, the problem is that the Bloom/HDR causes a 72% drop in the frame-rate (~110 FPS to ~30 FPS).  Adjusting the kernel size of the blur makes little difference...  I am doing the vertical and horizontal blurs separately as I should....  I am really not sure what could be causing this.So, does anyone know what could cause such a drastic drop in performance?  Perhaps some ways I could optimize this?  Thanks beforehand for any help!  Here are my Bright & Blur shaders:Blur Shader: -- BEGIN CODE -- uniform sampler2D texScreen;uniform vec2 size;uniform bool horizontal;uniform int kernel_size;void main() {vec2 pas = 1.0 / size;vec2 uv = gl_TexCoord[0].st;vec4 color = vec4(0.0, 0.0, 0.0, 1.0);if(horizontal) {int j = 0;int sum = 0;for(int i = -kernel_size; i <= kernel_size; i++) {vec4 value = texture2D(texScreen, uv + vec2(pas.x*i, 0.0));int factor = kernel_size+1 - abs((float)i);color += value * factor;sum += factor;}color /= sum;} else {int j = 0;int sum = 0;for(int i = -kernel_size; i <= kernel_size; i++) {vec4 value = texture2D(texScreen, uv + vec2(0.0, pas.y * i));int factor = kernel_size + 1 - abs((float)i);color += value * factor;sum += factor;}color /= sum;}gl_FragColor.rgb = color;gl_FragColor.a = 1.0;} -- END CODE -- Bright shader: -- BEGIN CODE -- uniform sampler2D texScreen;void main() {vec3 color = texture2D(texScreen, gl_TexCoord[0].xy).rgb;if(((color.r * 0.299) + (color.g * 0.587) + (color.r * 0.114)) > 0.75) {gl_FragColor.rgb = color;} else {gl_FragColor.rgb = vec3(0.0, 0.0, 0.0);}gl_FragColor.a = 1.0;} -- END CODE -- 
  I think that performance drop could be caused by if-conditions and loops in your fragment shader. Try using 2 different shaders - one for vertical pass and one for horizontal pass - both WITHOUT any conditions and with unrolled loops.This is how my "vertical pass" blur fragment shader looks like:uniform sampler2D p_ScreenTexture;uniform float p_BlurSize;void main(void){   vec4 sum = vec4(0.0);   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - 4.0 * p_BlurSize)) * 0.05;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - 3.0 * p_BlurSize)) * 0.09;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - 2.0 * p_BlurSize)) * 0.12;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y - p_BlurSize)) * 0.15;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y)) * 0.16;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + p_BlurSize)) * 0.15;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + 2.0 * p_BlurSize)) * 0.12;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + 3.0 * p_BlurSize)) * 0.09;   sum += texture2D(p_ScreenTexture, vec2(gl_TexCoord[0].x, gl_TexCoord[0].y + 4.0 * p_BlurSize)) * 0.05;    gl_FragColor = sum;};  Well that's not really that surprising. You can render your scene in a single pass in 9ms, but then you want to rerender the entire screen space 4 more times. 33ms doesn't seem that unreasonable.Given that these are all screen space passes from what I can tell, if you have a high resolution this is a lot of work to keep reading in and writing out the entire framebuffer so many times, so you'll want to try to cut this out where you can.Couple thoughts:I think using multiple render targets in your first pass would be a lot more efficient than a two pass write-read-write. You have all the information you need in the first pass to calculate the luminance (and it's not an expensive calculation), so why not just dump this to a second texture during the original pass. Also any time you can reduce the resolution of any of these secondary targets is going to be a big win for you. Bloom is by nature a very blurry effect, so maybe you could cut dimensions of your brightness/blur textures in half and do half as many blur samples? Assuming you're running at 1280x1024, if you could drop all the secondary targets to maybe 640x480 you're reducing your memory bandwidth requirements by 75%. When you're really currently streaming in and writing out the entire frame what looks like about 10 times per frame, this is really going to add up. You can play with different values to see what you can achieve and still look good.Nice looking effect btw!   ;  This is kind of counter-intuitive, but when I saw this it reminded me of an old GLSL tip that is on OpenGL.org. Don't know if it might help you at all, but might be worth a try.gl_FragColor.rgb = color;gl_FragColor.a = 1.0;Quote:Assignment with MADAssume that you want to set the output value ALPHA to 1.0. Here is one method : myOutputColor.xyz = myColor.xyz; myOutputColor.w = 1.0; gl_FragColor = myOutputColor;The above code can be 2 or 3 move instructions, depending on the compiler and the GPU's capabilities. Newer GPUs can handle setting different parts of gl_FragColor, but older ones can't, which means they need to use a temporary to build the final color and set it with a 3rd move instruction.You can use a MAD instruction to set all the fields at once: const vec2 constantList = vec2(1.0, 0.0); gl_FragColor = mycolor.xyzw * constantList.xxxy + constantList.yyyx;This does it all with one MAD operation, assuming that the building of the constant is compiled directly into the executable. ;@void main:  I will go ahead and make the blur shaders separate and unwrap the loops to see if that helps.  That's definitely a very viable optimisation,  Thanks!@karwosts:  I tried reducing the framebuffer size(s) to 1/4 of the size.  Essentially, having a framebuffer thats 1/4 of the screensize is ruffly equivalent to a 4x4-pixel blur, only OGL does all the work.  Additionally, you then only have to do a 2x2 blur ontop of that to achieve a ruff estimate of what a 8x8 blur would have been.Theoretically that should increase the performance considerably.  However, it only increased the performance marginally -- by ~15%.  I think there is a bigger bottleneck here :)Although, using a secondary rendering target to replace the bright pass would definately speed things up.  Thanks!@karwosts:  That is a very handy trick!  Thank you!I will go ahead and incorporate your suggestions -- hopefully it works! (said as I cross my fingers :P).  If not I'll have to come back and post some more questions.Thank you again for everyone's help!;  Please keep us posted ! I need to add a bloom to my engine soon and am very interested in your progress.;Quote:Original post by ConoktraI am developing a RTS game called Galactic Vice using OpenGL, and it requires Bloom/HDR to be implemented for various reasons (like the Lava effects shown here:).Not that it in any way invalidates your question, but it is perhaps worth noting that with clever texture blending, that effect could be implemented entirely without bloom/hdr.   ;  Alright, I was able to get better -- but not optimal -- performance.  I rewrote my blur shader into two separate ones, one for the vertical and the other for the horizontal pass.  I also reduced the blur FBO's resolution to half that of the window's resolution.  From there I kept the kernel-size at 4 -- which results in a 8x8 blur.  I also did some pretty standard optimisations, such as reciprocal multiplication rather than division.  This is done by the variable "ratio" which is either 1.0 / ScreenWidth or 1.0 / ScreenHeight.I didn't unwrap the loops because the kernel-size needs to be variable.  IMO, none these changes affected the visual quality of the bloom, which is excellent!  Here are my new blur shaders, suggestions on how I could optimise these further are very welcome!-- BEGIN CODE -- // Vertical blur shaderuniform sampler2D texScreen;uniform float ratio;uniform int kernel_size;void main() {    vec2 tc = gl_TexCoord[0].xy;    vec3 total = vec3(0.0, 0.0, 0.0);    for(int i = -kernel_size; i < kernel_size; ++i) {   total += texture2D(texScreen, vec2(tc.x, tc.y + (i * ratio)));    }    total /= (kernel_size * 2);    gl_FragColor = vec4(total.rgb, 1.0);}-- END CODE -- -- BEGIN CODE -- // Horizontal blur shaderuniform sampler2D texScreen;uniform float ratio;uniform int kernel_size;void main() {    vec2 tc = gl_TexCoord[0].xy;    vec3 total = vec3(0.0, 0.0, 0.0);    for(int i = -kernel_size; i < kernel_size; ++i) {   total += texture2D(texScreen, vec2(tc.x + (i * ratio), tc.y));    }    total /= (kernel_size * 2);    gl_FragColor = vec4(total.rgb, 1.0);}-- END CODE -- These new shaders only drop the performance by about ~20%, instead of the hideous 72% the previous ones caused.  This is still not as optimal as I would like it to be -- but it would do if its the best it gets.  Is there any way, short of unrolling the loops, that I could optimise this further?Thanks beforehand!