Bug 1705433 - Pack more varyings in vectors to work around Adreno 3xx bug. r=kvark
[gecko.git] / gfx / wr / webrender / res / brush_image.glsl
blob83d5f5c4a94f662c77868d8211b112a2da904499
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/. */
5 #define VECS_PER_SPECIFIC_BRUSH 3
7 #include shared,prim_shared,brush
9 // Interpolated UV coordinates to sample.
10 varying vec2 v_uv;
12 #ifdef WR_FEATURE_ALPHA_PASS
13 flat varying vec4 v_color;
14 flat varying vec2 v_mask_swizzle;
15 flat varying vec2 v_tile_repeat;
16 #endif
18 // Normalized bounds of the source image in the texture.
19 flat varying vec4 v_uv_bounds;
20 // Normalized bounds of the source image in the texture, adjusted to avoid
21 // sampling artifacts.
22 flat varying vec4 v_uv_sample_bounds;
24 #if defined(PLATFORM_ANDROID) && !defined(SWGL)
25 // On Adreno 3xx devices we have observed that a flat scalar varying used to calculate
26 // fragment shader output can result in the entire output being "flat". Here, for example,
27 // v_perspective being flat results in the UV coordinate calculated for every fragment
28 // being equal to the UV coordinate for the provoking vertex, which results in the entire
29 // triangle being rendered a solid color.
30 // Packing the varying in a vec2 works around this. See bug 1630356.
31 flat varying vec2 v_perspective_vec;
32 #define v_perspective v_perspective_vec.x
33 #else
34 // Flag to allow perspective interpolation of UV.
35 flat varying float v_perspective;
36 #endif
38 #ifdef WR_VERTEX_SHADER
40 // Must match the AlphaType enum.
41 #define BLEND_MODE_ALPHA            0
42 #define BLEND_MODE_PREMUL_ALPHA     1
44 struct ImageBrushData {
45     vec4 color;
46     vec4 background_color;
47     vec2 stretch_size;
50 ImageBrushData fetch_image_data(int address) {
51     vec4[3] raw_data = fetch_from_gpu_cache_3(address);
52     ImageBrushData data = ImageBrushData(
53         raw_data[0],
54         raw_data[1],
55         raw_data[2].xy
56     );
57     return data;
60 void brush_vs(
61     VertexInfo vi,
62     int prim_address,
63     RectWithSize prim_rect,
64     RectWithSize segment_rect,
65     ivec4 prim_user_data,
66     int specific_resource_address,
67     mat4 transform,
68     PictureTask pic_task,
69     int brush_flags,
70     vec4 segment_data
71 ) {
72     ImageBrushData image_data = fetch_image_data(prim_address);
74     // If this is in WR_FEATURE_TEXTURE_RECT mode, the rect and size use
75     // non-normalized texture coordinates.
76 #ifdef WR_FEATURE_TEXTURE_RECT
77     vec2 texture_size = vec2(1, 1);
78 #else
79     vec2 texture_size = vec2(TEX_SIZE(sColor0));
80 #endif
82     ImageSource res = fetch_image_source(specific_resource_address);
83     vec2 uv0 = res.uv_rect.p0;
84     vec2 uv1 = res.uv_rect.p1;
86     RectWithSize local_rect = prim_rect;
87     vec2 stretch_size = image_data.stretch_size;
88     if (stretch_size.x < 0.0) {
89         stretch_size = local_rect.size;
90     }
92     // If this segment should interpolate relative to the
93     // segment, modify the parameters for that.
94     if ((brush_flags & BRUSH_FLAG_SEGMENT_RELATIVE) != 0) {
95         local_rect = segment_rect;
96         stretch_size = local_rect.size;
98         if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
99             // If the extra data is a texel rect, modify the UVs.
100             vec2 uv_size = res.uv_rect.p1 - res.uv_rect.p0;
101             uv0 = res.uv_rect.p0 + segment_data.xy * uv_size;
102             uv1 = res.uv_rect.p0 + segment_data.zw * uv_size;
103         }
105         #ifdef WR_FEATURE_REPETITION
106             // TODO(bug 1609893): Move this logic to the CPU as well as other sources of
107             // branchiness in this shader.
108             if ((brush_flags & BRUSH_FLAG_TEXEL_RECT) != 0) {
109                 // Value of the stretch size with repetition. We have to compute it for
110                 // both axis even if we only repeat on one axis because the value for
111                 // each axis depends on what the repeated value would have been for the
112                 // other axis.
113                 vec2 repeated_stretch_size = stretch_size;
114                 // Size of the uv rect of the segment we are considering when computing
115                 // the repetitions. For the fill area it is a tad more complicated as we
116                 // have to use the uv size of the top-middle segment to drive horizontal
117                 // repetitions, and the size of the left-middle segment to drive vertical
118                 // repetitions. So we track the reference sizes for both axis separately
119                 // even though in the common case (the border segments) they are the same.
120                 vec2 horizontal_uv_size = uv1 - uv0;
121                 vec2 vertical_uv_size = uv1 - uv0;
122                 // We use top and left sizes by default and fall back to bottom and right
123                 // when a size is empty.
124                 if ((brush_flags & BRUSH_FLAG_SEGMENT_NINEPATCH_MIDDLE) != 0) {
125                     repeated_stretch_size = segment_rect.p0 - prim_rect.p0;
127                     float epsilon = 0.001;
129                     // Adjust the the referecne uv size to compute vertical repetitions for
130                     // the fill area.
131                     vertical_uv_size.x = uv0.x - res.uv_rect.p0.x;
132                     if (vertical_uv_size.x < epsilon || repeated_stretch_size.x < epsilon) {
133                         vertical_uv_size.x = res.uv_rect.p1.x - uv1.x;
134                         repeated_stretch_size.x = prim_rect.p0.x + prim_rect.size.x
135                             - segment_rect.p0.x - segment_rect.size.x;
136                     }
138                     // Adjust the the referecne uv size to compute horizontal repetitions
139                     // for the fill area.
140                     horizontal_uv_size.y = uv0.y - res.uv_rect.p0.y;
141                     if (horizontal_uv_size.y < epsilon || repeated_stretch_size.y < epsilon) {
142                         horizontal_uv_size.y = res.uv_rect.p1.y - uv1.y;
143                         repeated_stretch_size.y = prim_rect.p0.y + prim_rect.size.y
144                             - segment_rect.p0.y - segment_rect.size.y;
145                     }
146                 }
148                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
149                     float uv_ratio = horizontal_uv_size.x / horizontal_uv_size.y;
150                     stretch_size.x = repeated_stretch_size.y * uv_ratio;
151                 }
152                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
153                     float uv_ratio = vertical_uv_size.y / vertical_uv_size.x;
154                     stretch_size.y = repeated_stretch_size.x * uv_ratio;
155                 }
157             } else {
158                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X) != 0) {
159                     stretch_size.x = segment_data.z - segment_data.x;
160                 }
161                 if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y) != 0) {
162                     stretch_size.y = segment_data.w - segment_data.y;
163                 }
164             }
165             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_X_ROUND) != 0) {
166                 float nx = max(1.0, round(segment_rect.size.x / stretch_size.x));
167                 stretch_size.x = segment_rect.size.x / nx;
168             }
169             if ((brush_flags & BRUSH_FLAG_SEGMENT_REPEAT_Y_ROUND) != 0) {
170                 float ny = max(1.0, round(segment_rect.size.y / stretch_size.y));
171                 stretch_size.y = segment_rect.size.y / ny;
172             }
173         #endif
174     }
176     float perspective_interpolate = (brush_flags & BRUSH_FLAG_PERSPECTIVE_INTERPOLATION) != 0 ? 1.0 : 0.0;
177     v_perspective = perspective_interpolate;
179     // Handle case where the UV coords are inverted (e.g. from an
180     // external image).
181     vec2 min_uv = min(uv0, uv1);
182     vec2 max_uv = max(uv0, uv1);
184     v_uv_sample_bounds = vec4(
185         min_uv + vec2(0.5),
186         max_uv - vec2(0.5)
187     ) / texture_size.xyxy;
189     vec2 f = (vi.local_pos - local_rect.p0) / local_rect.size;
191 #ifdef WR_FEATURE_ALPHA_PASS
192     int color_mode = prim_user_data.x & 0xffff;
193     int blend_mode = prim_user_data.x >> 16;
194     int raster_space = prim_user_data.y;
196     if (color_mode == COLOR_MODE_FROM_PASS) {
197         color_mode = uMode;
198     }
200     // Derive the texture coordinates for this image, based on
201     // whether the source image is a local-space or screen-space
202     // image.
203     if (raster_space == RASTER_SCREEN) {
204         // Since the screen space UVs specify an arbitrary quad, do
205         // a bilinear interpolation to get the correct UV for this
206         // local position.
207         f = get_image_quad_uv(specific_resource_address, f);
208     }
209 #endif
211     // Offset and scale v_uv here to avoid doing it in the fragment shader.
212     vec2 repeat = local_rect.size / stretch_size;
213     v_uv = mix(uv0, uv1, f) - min_uv;
214     v_uv /= texture_size;
215     v_uv *= repeat.xy;
216     if (perspective_interpolate == 0.0) {
217         v_uv *= vi.world_pos.w;
218     }
220 #ifdef WR_FEATURE_TEXTURE_RECT
221     v_uv_bounds = vec4(0.0, 0.0, vec2(textureSize(sColor0)));
222 #else
223     v_uv_bounds = vec4(min_uv, max_uv) / texture_size.xyxy;
224 #endif
226 #ifdef WR_FEATURE_REPETITION
227     // Normalize UV to 0..1 scale only if using repetition. Otherwise, leave
228     // UVs unnormalized since we won't compute a modulus without repetition
229     // enabled.
230     v_uv /= (v_uv_bounds.zw - v_uv_bounds.xy);
231 #endif
233 #ifdef WR_FEATURE_ALPHA_PASS
234     v_tile_repeat = repeat.xy;
236     float opacity = float(prim_user_data.z) / 65535.0;
237     switch (blend_mode) {
238         case BLEND_MODE_ALPHA:
239             image_data.color.a *= opacity;
240             break;
241         case BLEND_MODE_PREMUL_ALPHA:
242         default:
243             image_data.color *= opacity;
244             break;
245     }
247     switch (color_mode) {
248         case COLOR_MODE_ALPHA:
249         case COLOR_MODE_BITMAP:
250             #ifdef SWGL_BLEND
251                 swgl_blendDropShadow(image_data.color);
252                 v_mask_swizzle = vec2(1.0, 0.0);
253                 v_color = vec4(1.0);
254             #else
255                 v_mask_swizzle = vec2(0.0, 1.0);
256                 v_color = image_data.color;
257             #endif
258             break;
259         case COLOR_MODE_SUBPX_BG_PASS2:
260         case COLOR_MODE_IMAGE:
261             v_mask_swizzle = vec2(1.0, 0.0);
262             v_color = image_data.color;
263             break;
264         case COLOR_MODE_SUBPX_CONST_COLOR:
265         case COLOR_MODE_SUBPX_BG_PASS0:
266         case COLOR_MODE_COLOR_BITMAP:
267             v_mask_swizzle = vec2(1.0, 0.0);
268             v_color = vec4(image_data.color.a);
269             break;
270         case COLOR_MODE_SUBPX_BG_PASS1:
271             v_mask_swizzle = vec2(-1.0, 1.0);
272             v_color = vec4(image_data.color.a) * image_data.background_color;
273             break;
274         case COLOR_MODE_SUBPX_DUAL_SOURCE:
275             v_mask_swizzle = vec2(image_data.color.a, 0.0);
276             v_color = image_data.color;
277             break;
278         case COLOR_MODE_MULTIPLY_DUAL_SOURCE:
279             v_mask_swizzle = vec2(-image_data.color.a, image_data.color.a);
280             v_color = image_data.color;
281             break;
282         default:
283             v_mask_swizzle = vec2(0.0);
284             v_color = vec4(1.0);
285     }
286 #endif
288 #endif
290 #ifdef WR_FRAGMENT_SHADER
292 vec2 compute_repeated_uvs(float perspective_divisor) {
293 #ifdef WR_FEATURE_REPETITION
294     vec2 uv_size = v_uv_bounds.zw - v_uv_bounds.xy;
296     #ifdef WR_FEATURE_ALPHA_PASS
297     // This prevents the uv on the top and left parts of the primitive that was inflated
298     // for anti-aliasing purposes from going beyound the range covered by the regular
299     // (non-inflated) primitive.
300     vec2 local_uv = max(v_uv * perspective_divisor, vec2(0.0));
302     // Handle horizontal and vertical repetitions.
303     vec2 repeated_uv = fract(local_uv) * uv_size + v_uv_bounds.xy;
305     // This takes care of the bottom and right inflated parts.
306     // We do it after the modulo because the latter wraps around the values exactly on
307     // the right and bottom edges, which we do not want.
308     if (local_uv.x >= v_tile_repeat.x) {
309         repeated_uv.x = v_uv_bounds.z;
310     }
311     if (local_uv.y >= v_tile_repeat.y) {
312         repeated_uv.y = v_uv_bounds.w;
313     }
314     #else
315     vec2 repeated_uv = fract(v_uv * perspective_divisor) * uv_size + v_uv_bounds.xy;
316     #endif
318     return repeated_uv;
319 #else
320     return v_uv * perspective_divisor + v_uv_bounds.xy;
321 #endif
324 Fragment brush_fs() {
325     float perspective_divisor = mix(gl_FragCoord.w, 1.0, v_perspective);
326     vec2 repeated_uv = compute_repeated_uvs(perspective_divisor);
328     // Clamp the uvs to avoid sampling artifacts.
329     vec2 uv = clamp(repeated_uv, v_uv_sample_bounds.xy, v_uv_sample_bounds.zw);
331     vec4 texel = TEX_SAMPLE(sColor0, uv);
333     Fragment frag;
335 #ifdef WR_FEATURE_ALPHA_PASS
336     #ifdef WR_FEATURE_ANTIALIASING
337         float alpha = antialias_brush();
338     #else
339         float alpha = 1.0;
340     #endif
341     #ifndef WR_FEATURE_DUAL_SOURCE_BLENDING
342         texel.rgb = texel.rgb * v_mask_swizzle.x + texel.aaa * v_mask_swizzle.y;
343     #endif
345     vec4 alpha_mask = texel * alpha;
346     frag.color = v_color * alpha_mask;
348     #ifdef WR_FEATURE_DUAL_SOURCE_BLENDING
349         frag.blend = alpha_mask * v_mask_swizzle.x + alpha_mask.aaaa * v_mask_swizzle.y;
350     #endif
351 #else
352     frag.color = texel;
353 #endif
355     return frag;
358 #if defined(SWGL_DRAW_SPAN) && (!defined(WR_FEATURE_ALPHA_PASS) || !defined(WR_FEATURE_DUAL_SOURCE_BLENDING))
359 void swgl_drawSpanRGBA8() {
360     if (!swgl_isTextureRGBA8(sColor0)) {
361         return;
362     }
364     #ifdef WR_FEATURE_ALPHA_PASS
365         if (v_mask_swizzle != vec2(1.0, 0.0)) {
366             return;
367         }
368     #endif
370     float perspective_divisor = mix(swgl_forceScalar(gl_FragCoord.w), 1.0, v_perspective);
372     #ifdef WR_FEATURE_REPETITION
373         // Get the UVs before any repetition, scaling, or offsetting has occurred...
374         vec2 uv = v_uv * perspective_divisor;
375     #else
376         vec2 uv = compute_repeated_uvs(perspective_divisor);
377     #endif
379     #ifdef WR_FEATURE_ALPHA_PASS
380     if (v_color != vec4(1.0)) {
381         #ifdef WR_FEATURE_REPETITION
382             swgl_commitTextureRepeatColorRGBA8(sColor0, uv, v_tile_repeat, v_uv_bounds, v_uv_sample_bounds, v_color);
383         #else
384             swgl_commitTextureColorRGBA8(sColor0, uv, v_uv_sample_bounds, v_color);
385         #endif
386         return;
387     }
388     // No color scaling required, so just fall through to a normal textured span...
389     #endif
391     #ifdef WR_FEATURE_REPETITION
392         #ifdef WR_FEATURE_ALPHA_PASS
393             swgl_commitTextureRepeatRGBA8(sColor0, uv, v_tile_repeat, v_uv_bounds, v_uv_sample_bounds);
394         #else
395             swgl_commitTextureRepeatRGBA8(sColor0, uv, vec2(0.0), v_uv_bounds, v_uv_sample_bounds);
396         #endif
397     #else
398         swgl_commitTextureRGBA8(sColor0, uv, v_uv_sample_bounds);
399     #endif
401 #endif
403 #endif