From a189332b8306dc1f47af142f4ac8a99432331c5a Mon Sep 17 00:00:00 2001 From: kvark Date: Wed, 1 Feb 2017 14:18:53 -0500 Subject: [PATCH 1/3] Removed image borders --- webrender/res/ps_image.fs.glsl | 9 ++- webrender/res/ps_image.glsl | 1 + webrender/res/ps_image.vs.glsl | 1 + webrender/res/ps_yuv_image.fs.glsl | 19 ++++-- webrender/res/ps_yuv_image.glsl | 2 + webrender/res/ps_yuv_image.vs.glsl | 3 + webrender/src/texture_cache.rs | 98 +++++++++--------------------- 7 files changed, 57 insertions(+), 76 deletions(-) diff --git a/webrender/res/ps_image.fs.glsl b/webrender/res/ps_image.fs.glsl index 6d19273220..f0fe715683 100644 --- a/webrender/res/ps_image.fs.glsl +++ b/webrender/res/ps_image.fs.glsl @@ -24,8 +24,13 @@ void main(void) { // account the spacing in between tiles. We only paint if our fragment does // not fall into that spacing. vec2 position_in_tile = mod(relative_pos_in_rect, vStretchSize + vTileSpacing); - vec2 st = vTextureOffset + ((position_in_tile / vStretchSize) * vTextureSize); + // We clamp the texture coordinates to the half-pixel offset from the borders + // in order to avoid sampling outside of the texture area. + vec2 st = vTextureOffset + clamp( + (position_in_tile / vStretchSize) * vTextureSize, + vHalfTexel, vTextureSize - vHalfTexel); + alpha = alpha * float(all(bvec2(step(position_in_tile, vStretchSize)))); - oFragColor = vec4(1.0, 1.0, 1.0, alpha) * texture(sColor0, st); + oFragColor = vec4(1.0, 1.0, 1.0, alpha) * textureLod(sColor0, st, 0.0); } diff --git a/webrender/res/ps_image.glsl b/webrender/res/ps_image.glsl index 0ca0ac5721..0a85b61430 100644 --- a/webrender/res/ps_image.glsl +++ b/webrender/res/ps_image.glsl @@ -5,6 +5,7 @@ flat varying vec2 vTextureOffset; // Offset of this image into the texture atlas. flat varying vec2 vTextureSize; // Size of the image in the texture atlas. flat varying vec2 vTileSpacing; // Amount of space between tiled instances of this image. +flat varying vec2 vHalfTexel; // Normalized length of the half of a texel. #ifdef WR_FEATURE_TRANSFORM varying vec3 vLocalPos; diff --git a/webrender/res/ps_image.vs.glsl b/webrender/res/ps_image.vs.glsl index bac236cb22..6ee39ccae7 100644 --- a/webrender/res/ps_image.vs.glsl +++ b/webrender/res/ps_image.vs.glsl @@ -36,4 +36,5 @@ void main(void) { vTextureOffset = st0; vTileSpacing = image.stretch_size_and_tile_spacing.zw; vStretchSize = image.stretch_size_and_tile_spacing.xy; + vHalfTexel = vec2(0.5) / texture_size; } diff --git a/webrender/res/ps_yuv_image.fs.glsl b/webrender/res/ps_yuv_image.fs.glsl index 133e23952f..30e03a7e72 100644 --- a/webrender/res/ps_yuv_image.fs.glsl +++ b/webrender/res/ps_yuv_image.fs.glsl @@ -19,13 +19,20 @@ void main(void) { alpha = min(alpha, do_clip()); - vec2 st_y = vTextureOffsetY + relative_pos_in_rect / vStretchSize * vTextureSizeY; - vec2 st_u = vTextureOffsetU + relative_pos_in_rect / vStretchSize * vTextureSizeUv; - vec2 st_v = vTextureOffsetV + relative_pos_in_rect / vStretchSize * vTextureSizeUv; + // We clamp the texture coordinates to the half-pixel offset from the borders + // in order to avoid sampling outside of the texture area. + vec2 st_y = vTextureOffsetY + clamp( + relative_pos_in_rect / vStretchSize * vTextureSizeY, + vHalfTexelY, vTextureSizeY - vHalfTexelY); + vec2 uv_offset = clamp( + relative_pos_in_rect / vStretchSize * vTextureSizeUv, + vHalfTexelUv, vTextureSizeUv - vHalfTexelUv); + vec2 st_u = vTextureOffsetU + uv_offset; + vec2 st_v = vTextureOffsetV + uv_offset; - float y = texture(sColor0, st_y).r; - float u = texture(sColor1, st_u).r; - float v = texture(sColor2, st_v).r; + float y = textureLod(sColor0, st_y, 0.0).r; + float u = textureLod(sColor1, st_u, 0.0).r; + float v = textureLod(sColor2, st_v, 0.0).r; // See the vertex shader for an explanation of where the constants come from. vec3 rgb = vYuvColorMatrix * vec3(y - 0.06275, u - 0.50196, v - 0.50196); diff --git a/webrender/res/ps_yuv_image.glsl b/webrender/res/ps_yuv_image.glsl index 65ff75e3a2..e8012fcc3e 100644 --- a/webrender/res/ps_yuv_image.glsl +++ b/webrender/res/ps_yuv_image.glsl @@ -8,6 +8,8 @@ flat varying vec2 vTextureOffsetV; // Offset of the v plane into the texture atl flat varying vec2 vTextureSizeY; // Size of the y plane in the texture atlas. flat varying vec2 vTextureSizeUv; // Size of the u and v planes in the texture atlas. flat varying vec2 vStretchSize; +flat varying vec2 vHalfTexelY; // Normalized length of the half of a Y texel. +flat varying vec2 vHalfTexelUv; // Normalized length of the half of u and v texels. flat varying mat3 vYuvColorMatrix; diff --git a/webrender/res/ps_yuv_image.vs.glsl b/webrender/res/ps_yuv_image.vs.glsl index 382e8bbbae..34e4e18d9a 100644 --- a/webrender/res/ps_yuv_image.vs.glsl +++ b/webrender/res/ps_yuv_image.vs.glsl @@ -45,6 +45,9 @@ void main(void) { vStretchSize = image.size; + vHalfTexelY = vec2(0.5) / y_texture_size; + vHalfTexelUv = vec2(0.5) / uv_texture_size; + // The constants added to the Y, U and V components are applied in the fragment shader. if (image.color_space == YUV_REC601) { // From Rec601: diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index da08fb0575..c7c0ed58c1 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -53,26 +53,6 @@ const COALESCING_TIMEOUT_CHECKING_INTERVAL: usize = 256; pub type TextureCacheItemId = FreeListItemId; -#[inline] -fn copy_pixels(src: &[u8], - target: &mut Vec, - x: u32, - y: u32, - count: u32, - width: u32, - stride: Option, - bpp: u32) { - let row_length = match stride { - Some(value) => value / bpp, - None => width, - }; - - let pixel_index = (y * row_length + x) * bpp; - for byte in src.iter().skip(pixel_index as usize).take((count * bpp) as usize) { - target.push(*byte); - } -} - /// A texture allocator using the guillotine algorithm with the rectangle merge improvement. See /// sections 2.2 and 2.2.5 in "A Thousand Ways to Pack the Bin - A Practical Approach to Two- /// Dimensional Rectangle Bin Packing": @@ -447,36 +427,31 @@ pub struct TextureCacheItem { // The size of the entire texture (not just the allocated rectangle) pub texture_size: DeviceUintSize, - // The size of the actual allocated rectangle, - // and the requested size. The allocated size - // is the same as the requested in most cases, - // unless the item has a border added for - // bilinear filtering / texture bleeding purposes. + // The size of the allocated rectangle. pub allocated_rect: DeviceUintRect, - pub requested_rect: DeviceUintRect, } // Structure squat the width/height fields to maintain the free list information :) impl FreeListItem for TextureCacheItem { fn next_free_id(&self) -> Option { - if self.requested_rect.size.width == 0 { - debug_assert!(self.requested_rect.size.height == 0); + if self.allocated_rect.size.width == 0 { + debug_assert_eq!(self.allocated_rect.size.height, 0); None } else { - debug_assert!(self.requested_rect.size.width == 1); - Some(FreeListItemId::new(self.requested_rect.size.height)) + debug_assert_eq!(self.allocated_rect.size.width, 1); + Some(FreeListItemId::new(self.allocated_rect.size.height)) } } fn set_next_free_id(&mut self, id: Option) { match id { Some(id) => { - self.requested_rect.size.width = 1; - self.requested_rect.size.height = id.value(); + self.allocated_rect.size.width = 1; + self.allocated_rect.size.height = id.value(); } None => { - self.requested_rect.size.width = 0; - self.requested_rect.size.height = 0; + self.allocated_rect.size.width = 0; + self.allocated_rect.size.height = 0; } } } @@ -484,25 +459,23 @@ impl FreeListItem for TextureCacheItem { impl TextureCacheItem { fn new(texture_id: CacheTextureId, - allocated_rect: DeviceUintRect, - requested_rect: DeviceUintRect, + rect: DeviceUintRect, texture_size: &DeviceUintSize) -> TextureCacheItem { TextureCacheItem { texture_id: texture_id, texture_size: *texture_size, pixel_rect: RectUv { - top_left: DeviceIntPoint::new(requested_rect.origin.x as i32, - requested_rect.origin.y as i32), - top_right: DeviceIntPoint::new((requested_rect.origin.x + requested_rect.size.width) as i32, - requested_rect.origin.y as i32), - bottom_left: DeviceIntPoint::new(requested_rect.origin.x as i32, - (requested_rect.origin.y + requested_rect.size.height) as i32), - bottom_right: DeviceIntPoint::new((requested_rect.origin.x + requested_rect.size.width) as i32, - (requested_rect.origin.y + requested_rect.size.height) as i32) + top_left: DeviceIntPoint::new(rect.origin.x as i32, + rect.origin.y as i32), + top_right: DeviceIntPoint::new((rect.origin.x + rect.size.width) as i32, + rect.origin.y as i32), + bottom_left: DeviceIntPoint::new(rect.origin.x as i32, + (rect.origin.y + rect.size.height) as i32), + bottom_right: DeviceIntPoint::new((rect.origin.x + rect.size.width) as i32, + (rect.origin.y + rect.size.height) as i32) }, - allocated_rect: allocated_rect, - requested_rect: requested_rect, + allocated_rect: rect, } } } @@ -641,7 +614,6 @@ impl TextureCache { bottom_right: DeviceIntPoint::zero(), }, allocated_rect: DeviceUintRect::zero(), - requested_rect: DeviceUintRect::zero(), texture_size: DeviceUintSize::zero(), texture_id: CacheTextureId(0), }; @@ -668,7 +640,6 @@ impl TextureCache { let cache_item = TextureCacheItem::new( texture_id, DeviceUintRect::new(DeviceUintPoint::zero(), requested_size), - DeviceUintRect::new(DeviceUintPoint::zero(), requested_size), &requested_size); *self.items.get_mut(image_id) = cache_item; @@ -686,31 +657,22 @@ impl TextureCache { ImageFormat::Invalid | ImageFormat::RGBAF32 => unreachable!(), }; - let border_size = 1; - let allocation_size = DeviceUintSize::new(requested_width + border_size * 2, - requested_height + border_size * 2); - // TODO(gw): Handle this sensibly (support failing to render items that can't fit?) - assert!(allocation_size.width < max_texture_size()); - assert!(allocation_size.height < max_texture_size()); + assert!(requested_size.width < max_texture_size()); + assert!(requested_size.height < max_texture_size()); // Loop until an allocation succeeds, growing or adding new // texture pages as required. loop { let location = page_list.last_mut().and_then(|last_page| { - last_page.allocate(&allocation_size) + last_page.allocate(&requested_size) }); if let Some(location) = location { let page = page_list.last_mut().unwrap(); - let allocated_rect = DeviceUintRect::new(location, allocation_size); - let requested_rect = DeviceUintRect::new( - DeviceUintPoint::new(location.x + border_size, location.y + border_size), - requested_size); - + let requested_rect = DeviceUintRect::new(location, requested_size); let cache_item = TextureCacheItem::new(page.texture_id, - allocated_rect, requested_rect, &page.texture_size); *self.items.get_mut(image_id) = cache_item; @@ -777,8 +739,8 @@ impl TextureCache { let existing_item = self.items.get(image_id); // TODO(gw): Handle updates to size/format! - debug_assert!(existing_item.requested_rect.size.width == descriptor.width); - debug_assert!(existing_item.requested_rect.size.height == descriptor.height); + debug_assert_eq!(existing_item.allocated_rect.size.width, descriptor.width); + debug_assert_eq!(existing_item.allocated_rect.size.height, descriptor.height); let op = match data { ImageData::ExternalHandle(..) | ImageData::ExternalBuffer(..)=> { @@ -786,8 +748,8 @@ impl TextureCache { } ImageData::Raw(bytes) => { TextureUpdateOp::Update { - page_pos_x: existing_item.requested_rect.origin.x, - page_pos_y: existing_item.requested_rect.origin.y, + page_pos_x: existing_item.allocated_rect.origin.x, + page_pos_y: existing_item.allocated_rect.origin.y, width: descriptor.width, height: descriptor.height, data: bytes, @@ -848,13 +810,13 @@ impl TextureCache { // image's borders TextureCache::insert_image_border(&bytes, result.item.allocated_rect, - result.item.requested_rect, + result.item.allocated_rect, stride, bpp, &mut op); // image itself - op(result.item.requested_rect.origin.x, result.item.requested_rect.origin.y, - result.item.requested_rect.size.width, result.item.requested_rect.size.height, + op(result.item.allocated_rect.origin.x, result.item.requested_rect.origin.y, + result.item.allocated_rect.size.width, result.item.requested_rect.size.height, bytes, stride); } ImageData::ExternalBuffer(id) => { From 13bb88686c2e2968c2feff61ed228fb7332ef383 Mon Sep 17 00:00:00 2001 From: kvark Date: Wed, 1 Feb 2017 21:47:05 -0500 Subject: [PATCH 2/3] Borders optimized out for the text run shader --- webrender/res/ps_text_run.fs.glsl | 5 +++-- webrender/res/ps_text_run.glsl | 1 + webrender/res/ps_text_run.vs.glsl | 1 + 3 files changed, 5 insertions(+), 2 deletions(-) diff --git a/webrender/res/ps_text_run.fs.glsl b/webrender/res/ps_text_run.fs.glsl index e7049741bf..8381b30270 100644 --- a/webrender/res/ps_text_run.fs.glsl +++ b/webrender/res/ps_text_run.fs.glsl @@ -3,11 +3,12 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ void main(void) { + vec2 tc = clamp(vUv, vUvBorder.xy, vUvBorder.zw); #ifdef WR_FEATURE_SUBPIXEL_AA //note: the blend mode is not compatible with clipping - oFragColor = texture(sColor0, vUv); + oFragColor = texture(sColor0, tc); #else - float alpha = texture(sColor0, vUv).a; + float alpha = texture(sColor0, tc).a; #ifdef WR_FEATURE_TRANSFORM float a = 0.0; init_transform_fs(vLocalPos, vLocalRect, a); diff --git a/webrender/res/ps_text_run.glsl b/webrender/res/ps_text_run.glsl index d58ff7d14b..c98f6b9507 100644 --- a/webrender/res/ps_text_run.glsl +++ b/webrender/res/ps_text_run.glsl @@ -4,6 +4,7 @@ flat varying vec4 vColor; varying vec2 vUv; +flat varying vec4 vUvBorder; #ifdef WR_FEATURE_TRANSFORM varying vec3 vLocalPos; diff --git a/webrender/res/ps_text_run.vs.glsl b/webrender/res/ps_text_run.vs.glsl index 31bbb1c338..9826daa6ae 100644 --- a/webrender/res/ps_text_run.vs.glsl +++ b/webrender/res/ps_text_run.vs.glsl @@ -37,4 +37,5 @@ void main(void) { vColor = text.color; vUv = mix(st0, st1, f); + vUvBorder = (res.uv_rect + vec4(0.5, 0.5, -0.5, -0.5)) / texture_size.xyxy; } From 5b41357fcb3fea43b3a2f065645d99efe617d186 Mon Sep 17 00:00:00 2001 From: kvark Date: Thu, 2 Feb 2017 18:41:09 -0500 Subject: [PATCH 3/3] Merged the border code with jerry's external textures logic --- webrender/src/internal_types.rs | 4 +- webrender/src/renderer.rs | 21 +++------- webrender/src/texture_cache.rs | 74 ++++++--------------------------- 3 files changed, 18 insertions(+), 81 deletions(-) diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 0902590cd9..158632f843 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -388,10 +388,8 @@ pub enum TextureUpdateOp { stride: Option, }, UpdateForExternalBuffer { - allocated_rect: DeviceUintRect, - requested_rect: DeviceUintRect, + rect: DeviceUintRect, id: ExternalImageId, - bpp: u32, stride: Option, }, Grow { diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index 38a9f73169..a801c80555 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -1080,7 +1080,7 @@ impl Renderer { width, height, stride, data.as_slice()); } - TextureUpdateOp::UpdateForExternalBuffer { allocated_rect, requested_rect, id, bpp, stride } => { + TextureUpdateOp::UpdateForExternalBuffer { rect, id, stride } => { let handler = self.external_image_handler .as_mut() .expect("Found external image, but no handler set!"); @@ -1089,23 +1089,12 @@ impl Renderer { match handler.lock(id).source { ExternalImageSource::RawData(data) => { - // image itself device.update_texture(cached_id, - requested_rect.origin.x, - requested_rect.origin.y, - requested_rect.size.width, - requested_rect.size.height, + rect.origin.x, + rect.origin.y, + rect.size.width, + rect.size.height, stride, data); - // image's borders - let op = |x , y , w , h , src: Arc> , stride| { - device.update_texture(cached_id, x, y, w, h, stride, src.as_slice()); - }; - TextureCache::insert_image_border(data, - allocated_rect, - requested_rect, - stride, - bpp, - op); } _ => panic!("No external buffer found"), }; diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index c7c0ed58c1..4c3567e443 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -13,7 +13,6 @@ use std::collections::hash_map::Entry; use std::hash::BuildHasherDefault; use std::mem; use std::slice::Iter; -use std::sync::Arc; use time; use util; use webrender_traits::{ImageData, ImageFormat, DevicePixel, DeviceIntPoint}; @@ -567,37 +566,6 @@ impl TextureCache { } } - // The fn is a closure(x: u32, y: u32, w: u32, h: u32, src: Arc>, stride: Option). - pub fn insert_image_border(src: &[u8], - allocated_rect: DeviceUintRect, - requested_rect: DeviceUintRect, - stride: Option, - bpp: u32, - mut op: F) where F: FnMut(u32, u32, u32, u32, Arc>, Option){ - let mut top_row_data = Vec::new(); - let mut bottom_row_data = Vec::new(); - let mut left_column_data = Vec::new(); - let mut right_column_data = Vec::new(); - - copy_pixels(&src, &mut top_row_data, 0, 0, 1, requested_rect.size.width, stride, bpp); - copy_pixels(&src, &mut top_row_data, 0, 0, requested_rect.size.width, requested_rect.size.width, stride, bpp); - copy_pixels(&src, &mut top_row_data, requested_rect.size.width - 1, 0, 1, requested_rect.size.width, stride, bpp); - - copy_pixels(&src, &mut bottom_row_data, 0, requested_rect.size.height - 1, 1, requested_rect.size.width, stride, bpp); - copy_pixels(&src, &mut bottom_row_data, 0, requested_rect.size.height - 1, requested_rect.size.width, requested_rect.size.width, stride, bpp); - copy_pixels(&src, &mut bottom_row_data, requested_rect.size.width - 1, requested_rect.size.height - 1, 1, requested_rect.size.width, stride, bpp); - - for y in 0..requested_rect.size.height { - copy_pixels(&src, &mut left_column_data, 0, y, 1, requested_rect.size.width, stride, bpp); - copy_pixels(&src, &mut right_column_data, requested_rect.size.width - 1, y, 1, requested_rect.size.width, stride, bpp); - } - - op(allocated_rect.origin.x, allocated_rect.origin.y, allocated_rect.size.width, 1, Arc::new(top_row_data), None); - op(allocated_rect.origin.x, allocated_rect.origin.y + requested_rect.size.height + 1, allocated_rect.size.width, 1, Arc::new(bottom_row_data), None); - op(allocated_rect.origin.x, requested_rect.origin.y, 1, requested_rect.size.height, Arc::new(left_column_data), None); - op(allocated_rect.origin.x + requested_rect.size.width + 1, requested_rect.origin.y, 1, requested_rect.size.height, Arc::new(right_column_data), None); - } - pub fn pending_updates(&mut self) -> TextureUpdateList { mem::replace(&mut self.pending_updates, TextureUpdateList::new()) } @@ -782,8 +750,6 @@ impl TextureCache { format, filter); - let bpp = format.bytes_per_pixel().unwrap(); - match result.kind { AllocationKind::TexturePage => { match data { @@ -791,42 +757,26 @@ impl TextureCache { panic!("External handle should not go through texture_cache."); } ImageData::Raw(bytes) => { - let mut op = |x , y , w , h , src , stride| { - let update_op = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update { - page_pos_x: x, - page_pos_y: y, - width: w, - height: h, - data: src, - stride: stride, - }, - }; - - self.pending_updates.push(update_op); + let update_op = TextureUpdate { + id: result.item.texture_id, + op: TextureUpdateOp::Update { + page_pos_x: result.item.allocated_rect.origin.x, + page_pos_y: result.item.allocated_rect.origin.y, + width: result.item.allocated_rect.size.width, + height: result.item.allocated_rect.size.height, + data: bytes, + stride: stride, + }, }; - // image's borders - TextureCache::insert_image_border(&bytes, - result.item.allocated_rect, - result.item.allocated_rect, - stride, - bpp, - &mut op); - // image itself - op(result.item.allocated_rect.origin.x, result.item.requested_rect.origin.y, - result.item.allocated_rect.size.width, result.item.requested_rect.size.height, - bytes, stride); + self.pending_updates.push(update_op); } ImageData::ExternalBuffer(id) => { let update_op = TextureUpdate { id: result.item.texture_id, op: TextureUpdateOp::UpdateForExternalBuffer { - allocated_rect: result.item.allocated_rect, - requested_rect: result.item.requested_rect, + rect: result.item.allocated_rect, id: id, - bpp: bpp, stride: stride, }, };