diff --git a/webrender/src/internal_types.rs b/webrender/src/internal_types.rs index 2d05b7c1dc..13fb2691d5 100644 --- a/webrender/src/internal_types.rs +++ b/webrender/src/internal_types.rs @@ -21,6 +21,8 @@ use tiling; use webrender_traits::{Epoch, ColorF, PipelineId, DeviceIntSize}; use webrender_traits::{ImageFormat, MixBlendMode, NativeFontHandle}; use webrender_traits::{ExternalImageId, ScrollLayerId, WebGLCommand}; +use webrender_traits::{ImageData}; +use webrender_traits::{DeviceUintRect}; // An ID for a texture that is owned by the // texture cache module. This can include atlases @@ -342,10 +344,37 @@ pub enum RenderTargetMode { } pub enum TextureUpdateOp { - Create(u32, u32, ImageFormat, TextureFilter, RenderTargetMode, Option>>), - Update(u32, u32, u32, u32, Arc>, Option), - Grow(u32, u32, ImageFormat, TextureFilter, RenderTargetMode), - Free + Create { + width: u32, + height: u32, + format: ImageFormat, + filter: TextureFilter, + mode: RenderTargetMode, + data: Option, + }, + Update { + page_pos_x: u32, // the texture page position which we want to upload + page_pos_y: u32, + width: u32, + height: u32, + data: Arc>, + stride: Option, + }, + UpdateForExternalBuffer { + allocated_rect: DeviceUintRect, + requested_rect: DeviceUintRect, + id: ExternalImageId, + bpp: u32, + stride: Option, + }, + Grow { + width: u32, + height: u32, + format: ImageFormat, + filter: TextureFilter, + mode: RenderTargetMode, + }, + Free, } pub type ExternalImageUpdateList = Vec; diff --git a/webrender/src/renderer.rs b/webrender/src/renderer.rs index a671c0f1a8..3a401b5e07 100644 --- a/webrender/src/renderer.rs +++ b/webrender/src/renderer.rs @@ -38,7 +38,7 @@ use tiling::{BlurCommand, CacheClipInstance, PrimitiveInstance, RenderTarget, Re use time::precise_time_ns; use util::TransformedRectKind; use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier, RenderDispatcher}; -use webrender_traits::{ExternalImageId, ImageFormat, RenderApiSender, RendererKind}; +use webrender_traits::{ExternalImageId, ImageData, ImageFormat, RenderApiSender, RendererKind}; use webrender_traits::{DeviceIntRect, DevicePoint, DeviceIntPoint, DeviceIntSize, DeviceUintSize}; use webrender_traits::ImageDescriptor; use webrender_traits::channel; @@ -571,7 +571,7 @@ impl Renderer { is_opaque: false, }, TextureFilter::Linear, - Arc::new(white_pixels)); + ImageData::Raw(Arc::new(white_pixels))); let dummy_mask_image_id = texture_cache.new_item_id(); texture_cache.insert(dummy_mask_image_id, @@ -583,7 +583,7 @@ impl Renderer { is_opaque: false, }, TextureFilter::Linear, - Arc::new(mask_pixels)); + ImageData::Raw(Arc::new(mask_pixels))); let debug_renderer = DebugRenderer::new(&mut device); @@ -903,45 +903,107 @@ impl Renderer { for update_list in pending_texture_updates.drain(..) { for update in update_list.updates { match update.op { - TextureUpdateOp::Create(width, height, format, filter, mode, maybe_bytes) => { + TextureUpdateOp::Create { width, height, format, filter, mode, data } => { let CacheTextureId(cache_texture_index) = update.id; if self.cache_texture_id_map.len() == cache_texture_index { // Create a new native texture, as requested by the texture cache. let texture_id = self.device - .create_texture_ids(1, TextureTarget::Default)[0]; + .create_texture_ids(1, TextureTarget::Default)[0]; self.cache_texture_id_map.push(texture_id); } let texture_id = self.cache_texture_id_map[cache_texture_index]; - let maybe_slice = maybe_bytes.as_ref().map(|bytes|{ bytes.as_slice() }); - self.device.init_texture(texture_id, - width, - height, - format, - filter, - mode, - maybe_slice); + if let Some(image) = data { + match image { + ImageData::Raw(raw) => { + self.device.init_texture(texture_id, + width, + height, + format, + filter, + mode, + Some(raw.as_slice())); + } + ImageData::ExternalBuffer(id) => { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + + match handler.lock(id).source { + ExternalImageSource::RawData(raw) => { + self.device.init_texture(texture_id, + width, + height, + format, + filter, + mode, + Some(raw)); + } + _ => panic!("No external buffer found"), + }; + handler.unlock(id); + } + _ => { + panic!("No suitable image buffer for TextureUpdateOp::Create."); + } + } + } else { + self.device.init_texture(texture_id, + width, + height, + format, + filter, + mode, + None); + } } - TextureUpdateOp::Grow(new_width, - new_height, - format, - filter, - mode) => { + TextureUpdateOp::Grow { width, height, format, filter, mode } => { let texture_id = self.cache_texture_id_map[update.id.0]; self.device.resize_texture(texture_id, - new_width, - new_height, + width, + height, format, filter, mode); } - TextureUpdateOp::Update(x, y, width, height, bytes, stride) => { + TextureUpdateOp::Update { page_pos_x, page_pos_y, width, height, data, stride } => { let texture_id = self.cache_texture_id_map[update.id.0]; self.device.update_texture(texture_id, - x, - y, + page_pos_x, + page_pos_y, width, height, stride, - bytes.as_slice()); + data.as_slice()); + } + TextureUpdateOp::UpdateForExternalBuffer { allocated_rect, requested_rect, id, bpp, stride } => { + let handler = self.external_image_handler + .as_mut() + .expect("Found external image, but no handler set!"); + let device = &mut self.device; + let cached_id = self.cache_texture_id_map[update.id.0]; + + 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, + 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"), + }; + handler.unlock(id); } TextureUpdateOp::Free => { let texture_id = self.cache_texture_id_map[update.id.0]; @@ -1341,6 +1403,7 @@ impl Renderer { let texture_id = match image.source { ExternalImageSource::NativeTexture(texture_id) => TextureId::new(texture_id), + _ => panic!("No native texture found."), }; self.external_images.insert(external_id, texture_id); @@ -1507,10 +1570,9 @@ impl Renderer { } } -pub enum ExternalImageSource { - // TODO(gw): Work out the API for raw buffers. - //RawData(*const u8, usize), - NativeTexture(u32), // Is a gl::GLuint texture handle +pub enum ExternalImageSource<'a> { + RawData(&'a [u8]), // raw buffers. + NativeTexture(u32), // Is a gl::GLuint texture handle } /// The data that an external client should provide about @@ -1522,12 +1584,12 @@ pub enum ExternalImageSource { /// the returned timestamp for a given image, the renderer /// will know to re-upload the image data to the GPU. /// Note that the UV coords are supplied in texel-space! -pub struct ExternalImage { +pub struct ExternalImage<'a> { pub u0: f32, pub v0: f32, pub u1: f32, pub v1: f32, - pub source: ExternalImageSource, + pub source: ExternalImageSource<'a>, } /// The interfaces that an application can implement to support providing diff --git a/webrender/src/resource_cache.rs b/webrender/src/resource_cache.rs index 21fdf964a8..9295c337e9 100644 --- a/webrender/src/resource_cache.rs +++ b/webrender/src/resource_cache.rs @@ -262,7 +262,7 @@ impl ResourceCache { Some(image) => { // This image should not be an external image. match image.data { - ImageData::External(id) => { + ImageData::ExternalHandle(id) => { panic!("Update an external image with buffer, id={} image_key={:?}", id.0, image_key); }, _ => {}, @@ -291,7 +291,7 @@ impl ResourceCache { // If the key is associated to an external image, pass the external id to renderer for cleanup. if let Some(image) = value { match image.data { - ImageData::External(id) => { + ImageData::ExternalHandle(id) => { self.pending_external_image_update_list.push(id); }, _ => {}, @@ -451,8 +451,9 @@ impl ResourceCache { let image_template = &self.image_templates[&image_key]; let external_id = match image_template.data { - ImageData::External(id) => Some(id), - ImageData::Raw(..) => None, + ImageData::ExternalHandle(id) => Some(id), + // raw and externalBuffer are all use resource_cache. + ImageData::Raw(..) | ImageData::ExternalBuffer(..) => None, }; ImageProperties { @@ -520,7 +521,7 @@ impl ResourceCache { is_opaque: false, }, TextureFilter::Linear, - Arc::new(glyph.bytes)); + ImageData::Raw(Arc::new(glyph.bytes))); Some(image_id) } else { None @@ -539,19 +540,21 @@ impl ResourceCache { for request in self.pending_image_requests.drain(..) { let cached_images = &mut self.cached_images; let image_template = &self.image_templates[&request.key]; + let image_data = image_template.data.clone(); match image_template.data { - ImageData::External(..) => {} - ImageData::Raw(ref bytes) => { + ImageData::ExternalHandle(..) => { + // external handle doesn't need to update the texture_cache. + } + ImageData::Raw(..) | ImageData::ExternalBuffer(..) => { match cached_images.entry(request.clone(), self.current_frame_id) { Occupied(entry) => { let image_id = entry.get().texture_cache_id; if entry.get().epoch != image_template.epoch { - // TODO: Can we avoid the clone of the bytes here? self.texture_cache.update(image_id, image_template.descriptor, - bytes.clone()); + image_data); // Update the cached epoch *entry.into_mut() = CachedImageInfo { @@ -568,11 +571,10 @@ impl ResourceCache { ImageRendering::Auto | ImageRendering::CrispEdges => TextureFilter::Linear, }; - // TODO: Can we avoid the clone of the bytes here? self.texture_cache.insert(image_id, image_template.descriptor, filter, - bytes.clone()); + image_data); entry.insert(CachedImageInfo { texture_cache_id: image_id, diff --git a/webrender/src/texture_cache.rs b/webrender/src/texture_cache.rs index 0a40151591..da08fb0575 100644 --- a/webrender/src/texture_cache.rs +++ b/webrender/src/texture_cache.rs @@ -16,7 +16,7 @@ use std::slice::Iter; use std::sync::Arc; use time; use util; -use webrender_traits::{ImageFormat, DevicePixel, DeviceIntPoint}; +use webrender_traits::{ImageData, ImageFormat, DevicePixel, DeviceIntPoint}; use webrender_traits::{DeviceUintRect, DeviceUintSize, DeviceUintPoint}; use webrender_traits::ImageDescriptor; @@ -594,6 +594,37 @@ 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()) } @@ -742,19 +773,28 @@ impl TextureCache { pub fn update(&mut self, image_id: TextureCacheItemId, descriptor: ImageDescriptor, - bytes: Arc>) { + data: ImageData) { 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); - let op = TextureUpdateOp::Update(existing_item.requested_rect.origin.x, - existing_item.requested_rect.origin.y, - descriptor.width, - descriptor.height, - bytes, - descriptor.stride); + let op = match data { + ImageData::ExternalHandle(..) | ImageData::ExternalBuffer(..)=> { + panic!("Doesn't support Update() for external image."); + } + ImageData::Raw(bytes) => { + TextureUpdateOp::Update { + page_pos_x: existing_item.requested_rect.origin.x, + page_pos_y: existing_item.requested_rect.origin.y, + width: descriptor.width, + height: descriptor.height, + data: bytes, + stride: descriptor.stride, + } + } + }; let update_op = TextureUpdate { id: existing_item.texture_id, @@ -768,7 +808,7 @@ impl TextureCache { image_id: TextureCacheItemId, descriptor: ImageDescriptor, filter: TextureFilter, - bytes: Arc>) { + data: ImageData) { let width = descriptor.width; let height = descriptor.height; let format = descriptor.format; @@ -780,99 +820,82 @@ impl TextureCache { format, filter); - let op = match result.kind { - AllocationKind::TexturePage => { - let bpp = format.bytes_per_pixel().unwrap(); - - let mut top_row_bytes = Vec::new(); - let mut bottom_row_bytes = Vec::new(); - let mut left_column_bytes = Vec::new(); - let mut right_column_bytes = Vec::new(); + let bpp = format.bytes_per_pixel().unwrap(); - copy_pixels(&bytes, &mut top_row_bytes, 0, 0, 1, width, stride, bpp); - copy_pixels(&bytes, &mut top_row_bytes, 0, 0, width, width, stride, bpp); - copy_pixels(&bytes, &mut top_row_bytes, width-1, 0, 1, width, stride, bpp); - - copy_pixels(&bytes, &mut bottom_row_bytes, 0, height-1, 1, width, stride, bpp); - copy_pixels(&bytes, &mut bottom_row_bytes, 0, height-1, width, width, stride, bpp); - copy_pixels(&bytes, &mut bottom_row_bytes, width-1, height-1, 1, width, stride, bpp); - - for y in 0..height { - copy_pixels(&bytes, &mut left_column_bytes, 0, y, 1, width, stride, bpp); - copy_pixels(&bytes, &mut right_column_bytes, width-1, y, 1, width, stride, bpp); + match result.kind { + AllocationKind::TexturePage => { + match data { + ImageData::ExternalHandle(..) => { + 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); + }; + + // image's borders + TextureCache::insert_image_border(&bytes, + result.item.allocated_rect, + result.item.requested_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, + bytes, stride); + } + 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, + id: id, + bpp: bpp, + stride: stride, + }, + }; + + self.pending_updates.push(update_op); + } } - - let border_update_op_top = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update(result.item.allocated_rect.origin.x, - result.item.allocated_rect.origin.y, - result.item.allocated_rect.size.width, - 1, - Arc::new(top_row_bytes), - None) - }; - - let border_update_op_bottom = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update( - result.item.allocated_rect.origin.x, - result.item.allocated_rect.origin.y + - result.item.requested_rect.size.height + 1, - result.item.allocated_rect.size.width, - 1, - Arc::new(bottom_row_bytes), - None) - }; - - let border_update_op_left = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update( - result.item.allocated_rect.origin.x, - result.item.requested_rect.origin.y, - 1, - result.item.requested_rect.size.height, - Arc::new(left_column_bytes), - None) - }; - - let border_update_op_right = TextureUpdate { - id: result.item.texture_id, - op: TextureUpdateOp::Update(result.item.allocated_rect.origin.x + result.item.requested_rect.size.width + 1, - result.item.requested_rect.origin.y, - 1, - result.item.requested_rect.size.height, - Arc::new(right_column_bytes), - None) - }; - - self.pending_updates.push(border_update_op_top); - self.pending_updates.push(border_update_op_bottom); - self.pending_updates.push(border_update_op_left); - self.pending_updates.push(border_update_op_right); - - TextureUpdateOp::Update(result.item.requested_rect.origin.x, - result.item.requested_rect.origin.y, - width, - height, - bytes, - stride) } AllocationKind::Standalone => { - TextureUpdateOp::Create(width, - height, - format, - filter, - RenderTargetMode::None, - Some(bytes)) + match data { + ImageData::ExternalHandle(..) => { + panic!("External handle should not go through texture_cache."); + } + _ => { + let update_op = TextureUpdate { + id: result.item.texture_id, + op: TextureUpdateOp::Create { + width: width, + height: height, + format: format, + filter: filter, + mode: RenderTargetMode::None, + data: Some(data), + }, + }; + + self.pending_updates.push(update_op); + } + } } - }; - - let update_op = TextureUpdate { - id: result.item.texture_id, - op: op, - }; - - self.pending_updates.push(update_op); + } } pub fn get(&self, id: TextureCacheItemId) -> &TextureCacheItem { @@ -902,18 +925,27 @@ impl TextureCache { fn texture_create_op(texture_size: DeviceUintSize, format: ImageFormat, mode: RenderTargetMode) -> TextureUpdateOp { - TextureUpdateOp::Create(texture_size.width, texture_size.height, format, TextureFilter::Linear, mode, None) + TextureUpdateOp::Create { + width: texture_size.width, + height: texture_size.height, + format: format, + filter: TextureFilter::Linear, + mode: mode, + data: None, + } } fn texture_grow_op(texture_size: DeviceUintSize, format: ImageFormat, mode: RenderTargetMode) -> TextureUpdateOp { - TextureUpdateOp::Grow(texture_size.width, - texture_size.height, - format, - TextureFilter::Linear, - mode) + TextureUpdateOp::Grow { + width: texture_size.width, + height: texture_size.height, + format: format, + filter: TextureFilter::Linear, + mode: mode, + } } trait FitsInside { diff --git a/webrender_traits/src/types.rs b/webrender_traits/src/types.rs index e23a9b4378..a5d44e2b74 100644 --- a/webrender_traits/src/types.rs +++ b/webrender_traits/src/types.rs @@ -512,7 +512,8 @@ pub struct ExternalImageId(pub u64); #[derive(Clone, Serialize, Deserialize)] pub enum ImageData { Raw(Arc>), - External(ExternalImageId), + ExternalHandle(ExternalImageId), + ExternalBuffer(ExternalImageId), } impl ImageData { diff --git a/wrench/src/json_frame_writer.rs b/wrench/src/json_frame_writer.rs index 4f8397e5b9..882a9c7c58 100644 --- a/wrench/src/json_frame_writer.rs +++ b/wrench/src/json_frame_writer.rs @@ -214,7 +214,8 @@ impl webrender::ApiRecordingReceiver for JsonFrameWriter { ); let bytes = match data { &ImageData::Raw(ref v) => { (**v).clone() } - &ImageData::External(_) => { return; } + &ImageData::ExternalHandle(_) => { return; } + &ImageData::ExternalBuffer(_) => { return; } }; self.images.insert(*key, CachedImage { width: descriptor.width, diff --git a/wrench/src/yaml_frame_writer.rs b/wrench/src/yaml_frame_writer.rs index 0364a2e078..84c09993d3 100644 --- a/wrench/src/yaml_frame_writer.rs +++ b/wrench/src/yaml_frame_writer.rs @@ -673,7 +673,8 @@ impl webrender::ApiRecordingReceiver for YamlFrameWriterReceiver { ); let bytes = match data { &ImageData::Raw(ref v) => { (**v).clone() } - &ImageData::External(_) => { return; } + &ImageData::ExternalHandle(_) => { return; } + &ImageData::ExternalBuffer(_) => { return; } }; self.frame_writer.images.insert(*key, CachedImage { width: descriptor.width,