cs_blur.glsl (7670B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ 4 5 #define WR_FEATURE_TEXTURE_2D 6 7 #include shared,prim_shared 8 9 varying highp vec2 vUv; 10 flat varying highp vec4 vUvRect; 11 flat varying highp vec4 vUvClampRect; 12 flat varying mediump vec2 vOffsetScale; 13 // The number of pixels on each end that we apply the blur filter over. 14 // Packed in to vector to work around bug 1630356. 15 flat varying mediump ivec2 vSupport; 16 flat varying mediump vec2 vGaussCoefficients; 17 18 #define EDGE_MODE_DUPLICATE 0 19 #define EDGE_MODE_MIRROR 1 20 21 #ifdef WR_VERTEX_SHADER 22 // Applies a separable gaussian blur in one direction, as specified 23 // by the dir field in the blur command. 24 25 #define DIR_HORIZONTAL 0 26 #define DIR_VERTICAL 1 27 28 PER_INSTANCE in int aBlurRenderTaskAddress; 29 PER_INSTANCE in int aBlurSourceTaskAddress; 30 PER_INSTANCE in int aBlurDirection; 31 PER_INSTANCE in int aBlurEdgeMode; 32 PER_INSTANCE in vec3 aBlurParams; 33 34 struct BlurTask { 35 RectWithEndpoint task_rect; 36 float blur_radius; 37 vec2 blur_region; 38 }; 39 40 BlurTask fetch_blur_task(int address) { 41 RectWithEndpoint task_rect = fetch_render_task_rect(address); 42 43 BlurTask task = BlurTask( 44 task_rect, 45 aBlurParams.x, 46 aBlurParams.yz 47 ); 48 49 return task; 50 } 51 52 void calculate_gauss_coefficients(float sigma) { 53 // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) 54 vGaussCoefficients = vec2(1.0 / (sqrt(2.0 * 3.14159265) * sigma), 55 exp(-0.5 / (sigma * sigma))); 56 57 // Pre-calculate the coefficient total in the vertex shader so that 58 // we can avoid having to do it per-fragment and also avoid division 59 // by zero in the degenerate case. 60 vec3 gauss_coefficient = vec3(vGaussCoefficients, 61 vGaussCoefficients.y * vGaussCoefficients.y); 62 float gauss_coefficient_total = gauss_coefficient.x; 63 for (int i = 1; i <= vSupport.x; i += 2) { 64 gauss_coefficient.xy *= gauss_coefficient.yz; 65 float gauss_coefficient_subtotal = gauss_coefficient.x; 66 gauss_coefficient.xy *= gauss_coefficient.yz; 67 gauss_coefficient_subtotal += gauss_coefficient.x; 68 gauss_coefficient_total += 2.0 * gauss_coefficient_subtotal; 69 } 70 71 // Scale initial coefficient by total to avoid passing the total separately 72 // to the fragment shader. 73 vGaussCoefficients.x /= gauss_coefficient_total; 74 } 75 76 void main(void) { 77 BlurTask blur_task = fetch_blur_task(aBlurRenderTaskAddress); 78 RectWithEndpoint src_rect = fetch_render_task_rect(aBlurSourceTaskAddress); 79 80 RectWithEndpoint target_rect = blur_task.task_rect; 81 82 vec2 texture_size = vec2(TEX_SIZE(sColor0).xy); 83 84 // Ensure that the support is an even number of pixels to simplify the 85 // fragment shader logic. 86 // 87 // TODO(pcwalton): Actually make use of this fact and use the texture 88 // hardware for linear filtering. 89 vSupport.x = int(ceil(1.5 * blur_task.blur_radius)) * 2; 90 vSupport.y = aBlurEdgeMode; 91 92 if (vSupport.x > 0) { 93 calculate_gauss_coefficients(blur_task.blur_radius); 94 } else { 95 // The gauss function gets NaNs when blur radius is zero. 96 vGaussCoefficients = vec2(1.0, 1.0); 97 } 98 99 switch (aBlurDirection) { 100 case DIR_HORIZONTAL: 101 vOffsetScale = vec2( 102 1.0 / (src_rect.p1.x - src_rect.p0.x), 103 0.0 104 ); 105 break; 106 case DIR_VERTICAL: 107 vOffsetScale = vec2( 108 0.0, 109 1.0 / (src_rect.p1.y - src_rect.p0.y) 110 ); 111 break; 112 default: 113 vOffsetScale = vec2(0.0); 114 } 115 116 vUvRect = vec4(src_rect.p0, src_rect.p1); 117 vUvRect /= texture_size.xyxy; 118 119 vUvClampRect = vec4(src_rect.p0 + vec2(0.5), 120 src_rect.p0 + blur_task.blur_region - vec2(0.5)); 121 vUvClampRect /= texture_size.xyxy; 122 123 vec2 pos = mix(target_rect.p0, target_rect.p1, aPosition.xy); 124 125 vUv = aPosition.xy; 126 127 gl_Position = uTransform * vec4(pos, 0.0, 1.0); 128 } 129 #endif 130 131 #ifdef WR_FRAGMENT_SHADER 132 133 #if defined WR_FEATURE_COLOR_TARGET 134 #define SAMPLE_TYPE vec4 135 #define SAMPLE_TEXTURE(uv) texture(sColor0, uv) 136 #else 137 #define SAMPLE_TYPE float 138 #define SAMPLE_TEXTURE(uv) texture(sColor0, uv).r 139 #endif 140 141 // TODO(gw): Write a fast path blur that handles smaller blur radii 142 // with a offset / weight uniform table and a constant 143 // loop iteration count! 144 145 // GLSL implementation of GL_MIRRORED_REPEAT 146 vec2 mirrored_repeat(vec2 uv) { 147 vec2 abs_uv = abs(uv); 148 vec2 uv_int = floor(abs_uv); 149 vec2 uv_frac = abs_uv - uv_int; 150 vec2 f = mod(uv_int, vec2(2.0)); 151 152 return mix(uv_frac, vec2(1.0) - uv_frac, f); 153 } 154 155 void main(void) { 156 vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv); 157 SAMPLE_TYPE original_color = SAMPLE_TEXTURE(uv); 158 159 // Incremental Gaussian Coefficent Calculation (See GPU Gems 3 pp. 877 - 889) 160 vec3 gauss_coefficient = vec3(vGaussCoefficients, 161 vGaussCoefficients.y * vGaussCoefficients.y); 162 163 SAMPLE_TYPE avg_color = original_color * gauss_coefficient.x; 164 165 // Evaluate two adjacent texels at a time. We can do this because, if c0 166 // and c1 are colors of adjacent texels and k0 and k1 are arbitrary 167 // factors, this formula: 168 // 169 // k0 * c0 + k1 * c1 (Equation 1) 170 // 171 // is equivalent to: 172 // 173 // k1 174 // (k0 + k1) * lerp(c0, c1, -------) 175 // k0 + k1 176 // 177 // A texture lookup of adjacent texels evaluates this formula: 178 // 179 // lerp(c0, c1, t) 180 // 181 // for some t. So we can let `t = k1/(k0 + k1)` and effectively evaluate 182 // Equation 1 with a single texture lookup. 183 // 184 // Clamp loop condition variable to a statically known value to workaround 185 // driver bug on Adreno 3xx. vSupport should not exceed 300 anyway, due to 186 // the max blur radius being 100. See bug 1720841 for details. 187 int support = min(vSupport.x, 300); 188 for (int i = 1; i <= support; i += 2) { 189 gauss_coefficient.xy *= gauss_coefficient.yz; 190 191 float gauss_coefficient_subtotal = gauss_coefficient.x; 192 gauss_coefficient.xy *= gauss_coefficient.yz; 193 gauss_coefficient_subtotal += gauss_coefficient.x; 194 float gauss_ratio = gauss_coefficient.x / gauss_coefficient_subtotal; 195 196 vec2 offset = vOffsetScale * (float(i) + gauss_ratio); 197 198 vec2 uv0 = vUv - offset; 199 vec2 uv1 = vUv + offset; 200 201 if (vSupport.y == EDGE_MODE_MIRROR) { 202 uv0 = mirrored_repeat(uv0); 203 uv1 = mirrored_repeat(uv1); 204 } 205 206 uv0 = mix(vUvRect.xy, vUvRect.zw, uv0); 207 uv1 = mix(vUvRect.xy, vUvRect.zw, uv1); 208 209 uv0 = max(uv0, vUvClampRect.xy); 210 uv1 = min(uv1, vUvClampRect.zw); 211 212 avg_color += (SAMPLE_TEXTURE(uv0) + SAMPLE_TEXTURE(uv1)) * 213 gauss_coefficient_subtotal; 214 } 215 216 oFragColor = vec4(avg_color); 217 } 218 219 #ifdef SWGL_DRAW_SPAN 220 #ifdef WR_FEATURE_COLOR_TARGET 221 void swgl_drawSpanRGBA8() { 222 vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv); 223 swgl_commitGaussianBlurRGBA8(sColor0, uv, vUvClampRect, vOffsetScale.x != 0.0, 224 vSupport.x, vGaussCoefficients); 225 } 226 #else 227 void swgl_drawSpanR8() { 228 vec2 uv = mix(vUvRect.xy, vUvRect.zw, vUv); 229 swgl_commitGaussianBlurR8(sColor0, uv, vUvClampRect, vOffsetScale.x != 0.0, 230 vSupport.x, vGaussCoefficients); 231 } 232 #endif 233 #endif 234 235 #endif