diff --git a/webrender/res/cs_blur.fs.glsl b/webrender/res/cs_blur.fs.glsl index 64d6c368d6..19dcb15ca4 100644 --- a/webrender/res/cs_blur.fs.glsl +++ b/webrender/res/cs_blur.fs.glsl @@ -36,5 +36,5 @@ void main(void) { // Unpremultiply the alpha. color.rgb /= color.a; - oFragColor = color; + oFragColor = dither(color); } diff --git a/webrender/res/cs_box_shadow.fs.glsl b/webrender/res/cs_box_shadow.fs.glsl index 3243dc6d62..6fad1f1634 100644 --- a/webrender/res/cs_box_shadow.fs.glsl +++ b/webrender/res/cs_box_shadow.fs.glsl @@ -144,5 +144,5 @@ void main(void) { float value = color(pos, p0Rect, p1Rect, radii, sigma); value = max(value, 0.0); - oFragColor = vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value); + oFragColor = dither(vec4(1.0, 1.0, 1.0, vInverted == 1.0 ? 1.0 - value : value)); } diff --git a/webrender/res/prim_shared.glsl b/webrender/res/prim_shared.glsl index ff13f4efcd..2efa79868c 100644 --- a/webrender/res/prim_shared.glsl +++ b/webrender/res/prim_shared.glsl @@ -758,4 +758,13 @@ float do_clip() { return vClipMaskUvBounds.xy == vClipMaskUvBounds.zw ? 1.0: all(inside) ? textureLod(sCacheA8, vClipMaskUv, 0.0).r : 0.0; } + +vec4 dither(vec4 color) { + const int matrix_mask = 7; + + ivec2 pos = ivec2(gl_FragCoord.xy) & ivec2(matrix_mask); + float noise_factor = 4.0 / 255.0; + float noise = texelFetch(sDither, pos, 0).r * noise_factor; + return color + vec4(noise, noise, noise, 0); +} #endif //WR_FRAGMENT_SHADER diff --git a/webrender/res/ps_angle_gradient.fs.glsl b/webrender/res/ps_angle_gradient.fs.glsl index 8cc7f1dac7..7b6b773c28 100644 --- a/webrender/res/ps_angle_gradient.fs.glsl +++ b/webrender/res/ps_angle_gradient.fs.glsl @@ -11,11 +11,10 @@ void main(void) { // gradient color entries (texture width / 2). float x = mix(clamp(vOffset, 0.0, 1.0), fract(vOffset), vGradientRepeat) * 0.5 * texture_size.x; - // Start at the center of first color in the nearest 2-color entry, then offset with the - // fractional remainder to interpolate between the colors. Rely on texture clamping when - // outside of valid range. x = 2.0 * floor(x) + 0.5 + fract(x); - // Normalize the texture coordates so we can use texture() for bilinear filtering. - oFragColor = texture(sGradients, vec2(x, vGradientIndex) / texture_size); + // Use linear filtering to mix in the low bits (vGradientIndex + 1) with the high + // bits (vGradientIndex) + float y = vGradientIndex * 2.0 + 0.5 + 1.0 / 256.0; + oFragColor = dither(texture(sGradients, vec2(x, y) / texture_size)); } diff --git a/webrender/res/ps_angle_gradient.vs.glsl b/webrender/res/ps_angle_gradient.vs.glsl index 0d47aaa56f..366042a7ad 100644 --- a/webrender/res/ps_angle_gradient.vs.glsl +++ b/webrender/res/ps_angle_gradient.vs.glsl @@ -27,7 +27,7 @@ void main(void) { vOffset = dot(vi.local_pos - start_point, dir) / dot(dir, dir); // V coordinate of gradient row in lookup texture. - vGradientIndex = float(prim.sub_index) + 0.5; + vGradientIndex = float(prim.sub_index); // Whether to repeat the gradient instead of clamping. vGradientRepeat = float(int(gradient.extend_mode.x) == EXTEND_MODE_REPEAT); diff --git a/webrender/res/ps_box_shadow.fs.glsl b/webrender/res/ps_box_shadow.fs.glsl index ef9e3c1cc8..aefbe00095 100644 --- a/webrender/res/ps_box_shadow.fs.glsl +++ b/webrender/res/ps_box_shadow.fs.glsl @@ -16,5 +16,5 @@ void main(void) { uv = mix(vCacheUvRectCoords.xy, vCacheUvRectCoords.zw, uv); // Modulate the box shadow by the color. - oFragColor = vColor * texture(sCacheRGBA8, vec3(uv, vUv.z)); + oFragColor = dither(vColor * texture(sCacheRGBA8, vec3(uv, vUv.z))); } diff --git a/webrender/res/ps_gradient.fs.glsl b/webrender/res/ps_gradient.fs.glsl index d2c705af49..4165f89b2c 100644 --- a/webrender/res/ps_gradient.fs.glsl +++ b/webrender/res/ps_gradient.fs.glsl @@ -12,5 +12,5 @@ void main(void) { #endif alpha = min(alpha, do_clip()); - oFragColor = vColor * vec4(1.0, 1.0, 1.0, alpha); + oFragColor = dither(vColor * vec4(1.0, 1.0, 1.0, alpha)); } diff --git a/webrender/res/ps_radial_gradient.fs.glsl b/webrender/res/ps_radial_gradient.fs.glsl index 1acd60df91..aaaa01ea3d 100644 --- a/webrender/res/ps_radial_gradient.fs.glsl +++ b/webrender/res/ps_radial_gradient.fs.glsl @@ -50,11 +50,10 @@ void main(void) { // gradient color entries (texture width / 2). x = mix(clamp(x, 0.0, 1.0), fract(x), vGradientRepeat) * 0.5 * texture_size.x; - // Start at the center of first color in the nearest 2-color entry, then offset with the - // fractional remainder to interpolate between the colors. Rely on texture clamping when - // outside of valid range. x = 2.0 * floor(x) + 0.5 + fract(x); - // Normalize the texture coordates so we can use texture() for bilinear filtering. - oFragColor = texture(sGradients, vec2(x, vGradientIndex) / texture_size); + // Use linear filtering to mix in the low bits (vGradientIndex + 1) with the high + // bits (vGradientIndex) + float y = vGradientIndex * 2.0 + 0.5 + 1.0 / 256.0; + oFragColor = dither(texture(sGradients, vec2(x, y) / texture_size)); } diff --git a/webrender/res/ps_radial_gradient.vs.glsl b/webrender/res/ps_radial_gradient.vs.glsl index 86d951bf7b..6f929a95ac 100644 --- a/webrender/res/ps_radial_gradient.vs.glsl +++ b/webrender/res/ps_radial_gradient.vs.glsl @@ -27,7 +27,7 @@ void main(void) { vEndRadius = gradient.start_end_radius_extend_mode.y; // V coordinate of gradient row in lookup texture. - vGradientIndex = float(prim.sub_index) + 0.5; + vGradientIndex = float(prim.sub_index); // Whether to repeat the gradient instead of clamping. vGradientRepeat = float(int(gradient.start_end_radius_extend_mode.z) == EXTEND_MODE_REPEAT); diff --git a/webrender/res/shared.glsl b/webrender/res/shared.glsl index f250a709c6..f0784f6c74 100644 --- a/webrender/res/shared.glsl +++ b/webrender/res/shared.glsl @@ -36,6 +36,7 @@ uniform sampler2D sColor0; uniform sampler2D sColor1; uniform sampler2D sColor2; +uniform sampler2D sDither; uniform sampler2D sMask; //====================================================================================== diff --git a/webrender/src/device.rs b/webrender/src/device.rs index bac7baf1f8..f34b31f119 100644 --- a/webrender/src/device.rs +++ b/webrender/src/device.rs @@ -1547,6 +1547,10 @@ impl Device { if u_color_2 != -1 { self.gl.uniform_1i(u_color_2, TextureSampler::Color2 as i32); } + let u_noise = self.gl.get_uniform_location(program.id, "sDither"); + if u_noise != -1 { + self.gl.uniform_1i(u_noise, TextureSampler::Dither as i32); + } let u_mask = self.gl.get_uniform_location(program.id, "sMask"); if u_mask != -1 { self.gl.uniform_1i(u_mask, TextureSampler::Mask as i32); diff --git a/webrender/src/gpu_store.rs b/webrender/src/gpu_store.rs index 68ba89c466..9297d98baa 100644 --- a/webrender/src/gpu_store.rs +++ b/webrender/src/gpu_store.rs @@ -45,6 +45,10 @@ pub trait GpuStoreLayout { fn items_per_row() -> usize { Self::texture_width::() / Self::texels_per_item::() } + + fn rows_per_item() -> usize { + Self::texels_per_item::() / Self::texture_width::() + } } /// A CPU-side buffer storing content to be uploaded to the GPU. @@ -81,8 +85,10 @@ impl GpuStore { // Extend the data array to be a multiple of the row size. // This ensures memory safety when the array is passed to // OpenGL to upload to the GPU. - while items.len() % items_per_row != 0 { - items.push(T::default()); + if items_per_row != 0 { + while items_per_row != 0 && items.len() % items_per_row != 0 { + items.push(T::default()); + } } items diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 9cd215e6d6..933d052561 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -170,6 +170,7 @@ impl GLContextWrapper { } const COLOR_FLOAT_TO_FIXED: f32 = 255.0; +const COLOR_FLOAT_TO_FIXED_WIDE: f32 = 65535.0; pub const ANGLE_FLOAT_TO_FIXED: f32 = 65535.0; pub const ORTHO_NEAR_PLANE: f32 = -1000000.0; @@ -198,6 +199,7 @@ pub enum TextureSampler { Geometry, ResourceRects, Gradients, + Dither, } impl TextureSampler { @@ -313,12 +315,20 @@ pub struct PackedTexel { } impl PackedTexel { - pub fn from_color(color: &ColorF) -> PackedTexel { + pub fn high_bytes(color: &ColorF) -> PackedTexel { + Self::extract_bytes(color, COLOR_FLOAT_TO_FIXED) + } + + pub fn low_bytes(color: &ColorF) -> PackedTexel { + Self::extract_bytes(color, COLOR_FLOAT_TO_FIXED_WIDE) + } + + fn extract_bytes(color: &ColorF, multiplier: f32) -> PackedTexel { PackedTexel { - b: (0.5 + color.b * COLOR_FLOAT_TO_FIXED).floor() as u8, - g: (0.5 + color.g * COLOR_FLOAT_TO_FIXED).floor() as u8, - r: (0.5 + color.r * COLOR_FLOAT_TO_FIXED).floor() as u8, - a: (0.5 + color.a * COLOR_FLOAT_TO_FIXED).floor() as u8, + b: ((0.5 + color.b * multiplier).floor() as u32 & 0xff) as u8, + g: ((0.5 + color.g * multiplier).floor() as u32 & 0xff) as u8, + r: ((0.5 + color.r * multiplier).floor() as u32 & 0xff) as u8, + a: ((0.5 + color.a * multiplier).floor() as u32 & 0xff) as u8, } } } diff --git a/webrender/src/prim_store.rs b/webrender/src/prim_store.rs index e955a63fa4..380b56ee43 100644 --- a/webrender/src/prim_store.rs +++ b/webrender/src/prim_store.rs @@ -280,13 +280,15 @@ pub struct GradientDataEntry { // color for the following entry, despite them being adjacent. Colors are stored within in BGRA8 // format for texture upload. pub struct GradientData { - pub colors: [GradientDataEntry; GRADIENT_DATA_RESOLUTION], + pub colors_high: [GradientDataEntry; GRADIENT_DATA_RESOLUTION], + pub colors_low: [GradientDataEntry; GRADIENT_DATA_RESOLUTION], } impl Default for GradientData { fn default() -> GradientData { GradientData { - colors: unsafe { mem::uninitialized() } + colors_high: unsafe { mem::uninitialized() }, + colors_low: unsafe { mem::uninitialized() } } } } @@ -294,7 +296,8 @@ impl Default for GradientData { impl Clone for GradientData { fn clone(&self) -> GradientData { GradientData { - colors: self.colors, + colors_high: self.colors_high, + colors_low: self.colors_low, } } } @@ -314,18 +317,24 @@ impl GradientData { let step_a = (end_color.a - start_color.a) * inv_steps; let mut cur_color = *start_color; - let mut cur_packed_color = PackedTexel::from_color(&cur_color); + let mut cur_color_high = PackedTexel::high_bytes(&cur_color); + let mut cur_color_low = PackedTexel::low_bytes(&cur_color); // Walk the ramp writing start and end colors for each entry. - for entry in &mut self.colors[start_idx..end_idx] { - entry.start_color = cur_packed_color; + for index in start_idx..end_idx { + let high_byte_entry = &mut self.colors_high[index]; + let low_byte_entry = &mut self.colors_low[index]; + high_byte_entry.start_color = cur_color_high; + low_byte_entry.start_color = cur_color_low; cur_color.r += step_r; cur_color.g += step_g; cur_color.b += step_b; cur_color.a += step_a; - cur_packed_color = PackedTexel::from_color(&cur_color); - entry.end_color = cur_packed_color; + cur_color_high = PackedTexel::high_bytes(&cur_color); + cur_color_low = PackedTexel::low_bytes(&cur_color); + high_byte_entry.end_color = cur_color_high; + low_byte_entry.end_color = cur_color_low; } end_idx diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index e9b172360f..50903ee167 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -154,15 +154,22 @@ impl GpuDataTexture { } let items_per_row = L::items_per_row::(); + let rows_per_item = L::rows_per_item::(); // Extend the data array to be a multiple of the row size. // This ensures memory safety when the array is passed to // OpenGL to upload to the GPU. - while data.len() % items_per_row != 0 { - data.push(T::default()); + if items_per_row != 0 { + while data.len() % items_per_row != 0 { + data.push(T::default()); + } } - let height = data.len() / items_per_row; + let height = if items_per_row != 0 { + data.len() / items_per_row + } else { + data.len() * rows_per_item + }; device.init_texture(self.id, L::texture_width::() as u32, @@ -201,7 +208,7 @@ impl GpuStoreLayout for GradientDataTextureLayout { } fn texture_width() -> usize { - mem::size_of::() / Self::texel_size() + mem::size_of::() / Self::texel_size() / 2 } fn texture_filter() -> TextureFilter { @@ -513,6 +520,8 @@ pub struct Renderer { /// when no target is yet provided as a cache texture input. dummy_cache_texture_id: TextureId, + dither_matrix_texture_id: TextureId, + /// Optional trait object that allows the client /// application to provide external buffers for image data. external_image_handler: Option>, @@ -749,6 +758,18 @@ impl Renderer { 0xff, 0xff, 0xff, 0xff, ]; + + let dither_matrix: [u8; 64] = [ + 00, 48, 12, 60, 03, 51, 15, 63, + 32, 16, 44, 28, 35, 19, 47, 31, + 08, 56, 04, 52, 11, 59, 07, 55, + 40, 24, 36, 20, 43, 27, 39, 23, + 02, 50, 14, 62, 01, 49, 13, 61, + 34, 18, 46, 30, 33, 17, 45, 29, + 10, 58, 06, 54, 09, 57, 05, 53, + 42, 26, 38, 22, 41, 25, 37, 21 + ]; + // TODO: Ensure that the white texture can never get evicted when the cache supports LRU eviction! let white_image_id = texture_cache.new_item_id(); texture_cache.insert(white_image_id, @@ -773,6 +794,15 @@ impl Renderer { RenderTargetMode::LayerRenderTarget(1), None); + let dither_matrix_texture_id = device.create_texture_ids(1, TextureTarget::Default)[0]; + device.init_texture(dither_matrix_texture_id, + 8, + 8, + ImageFormat::A8, + TextureFilter::Nearest, + RenderTargetMode::None, + Some(&dither_matrix)); + let debug_renderer = DebugRenderer::new(&mut device); let gpu_data_textures = [ @@ -914,6 +944,7 @@ impl Renderer { main_thread_dispatcher: main_thread_dispatcher, cache_texture_id_map: Vec::new(), dummy_cache_texture_id: dummy_cache_texture_id, + dither_matrix_texture_id: dither_matrix_texture_id, external_image_handler: None, external_images: HashMap::with_hasher(Default::default()), vr_compositor_handler: vr_compositor, @@ -1254,6 +1285,9 @@ impl Renderer { self.device.bind_texture(TextureSampler::color(i), texture_id); } + // TODO: this probably isn't the best place for this. + self.device.bind_texture(TextureSampler::Dither, self.dither_matrix_texture_id); + self.device.update_vao_instances(vao, data, VertexUsageHint::Stream); self.device.draw_indexed_triangles_instanced_u16(6, data.len() as i32); self.profile_counters.vertices.add(6 * data.len()); diff --git a/wrench/benchmarks/aligned-gradient.yaml b/wrench/benchmarks/aligned-gradient.yaml new file mode 100644 index 0000000000..b0e2f0a645 --- /dev/null +++ b/wrench/benchmarks/aligned-gradient.yaml @@ -0,0 +1,62 @@ +root: + items: + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 0, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false diff --git a/wrench/benchmarks/benchmarks.list b/wrench/benchmarks/benchmarks.list index 0d30567037..c7508d1521 100644 --- a/wrench/benchmarks/benchmarks.list +++ b/wrench/benchmarks/benchmarks.list @@ -1,3 +1,5 @@ +aligned-gradient.yaml +unaligned-gradient.yaml simple-batching.yaml large-clip-rect.yaml transforms-simple.yaml diff --git a/wrench/benchmarks/radial-gradient.yaml b/wrench/benchmarks/radial-gradient.yaml new file mode 100644 index 0000000000..375485ed68 --- /dev/null +++ b/wrench/benchmarks/radial-gradient.yaml @@ -0,0 +1,59 @@ +--- +root: + items: + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false + - type: radial-gradient + bounds: [ 0, 0, 1980, 1080] + start-center: [ 990, 540 ] + start-radius: 5 + end-center: [ 990, 540 ] + end-radius: 8000 + stops: [ 0.0, black, 1.0, white ] + repeat: false diff --git a/wrench/benchmarks/unaligned-gradient.yaml b/wrench/benchmarks/unaligned-gradient.yaml new file mode 100644 index 0000000000..ea7738202d --- /dev/null +++ b/wrench/benchmarks/unaligned-gradient.yaml @@ -0,0 +1,62 @@ +root: + items: + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false + - type: gradient + bounds: [ 0, 0, 1980, 1080] + start: [ 0, -2000 ] + end: [ 1, 4000 ] + stops: [ 0.0, red, 1.0, green ] + repeat: false