diff --git a/Cargo.toml b/Cargo.toml index eeab2aad4b..b792c2ffc4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -6,13 +6,13 @@ authors = ["Glenn Watson "] [dependencies.freetype] git = "https://github.com/servo/rust-freetype" +[dependencies.webrender_traits] +git = "https://github.com/glennw/webrender_traits" + [dependencies] gleam = "*" -libc = "*" euclid = "0.3" time = "*" -serde = "0.6" -serde_macros = "0.5" fnv="*" scoped_threadpool = "0.1.6" app_units = "0.1" diff --git a/src/aabbtree.rs b/src/aabbtree.rs index bf598c53e3..fe89685f4b 100644 --- a/src/aabbtree.rs +++ b/src/aabbtree.rs @@ -1,10 +1,16 @@ use euclid::{Point2D, Rect, Size2D}; -use internal_types::{CompiledNode, DisplayItemKey}; +use internal_types::{CompiledNode, DrawListId, DrawListItemIndex}; use resource_list::ResourceList; -use std::mem; -use types::NodeIndex; use util; +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct NodeIndex(pub u32); + +pub struct DrawListIndexBuffer { + pub draw_list_id: DrawListId, + pub indices: Vec, +} + pub struct AABBTreeNode { pub split_rect: Rect, pub actual_rect: Rect, @@ -15,7 +21,7 @@ pub struct AABBTreeNode { pub is_visible: bool, - pub src_items: Vec, + pub draw_lists: Vec, pub resource_list: Option, pub compiled_node: Option, @@ -30,37 +36,51 @@ impl AABBTreeNode { children: None, is_visible: false, resource_list: None, - src_items: Vec::new(), + draw_lists: Vec::new(), compiled_node: None, } } #[inline] fn append_item(&mut self, - draw_list_index: usize, - item_index: usize, + draw_list_id: DrawListId, + item_index: DrawListItemIndex, rect: &Rect) { self.actual_rect = self.actual_rect.union(rect); - let key = DisplayItemKey::new(draw_list_index, item_index); - self.src_items.push(key); + + let need_new_list = match self.draw_lists.last_mut() { + Some(draw_list) => { + draw_list.draw_list_id != draw_list_id + } + None => { + true + } + }; + + if need_new_list { + self.draw_lists.push(DrawListIndexBuffer { + draw_list_id: draw_list_id, + indices: Vec::new(), + }); + } + + self.draw_lists.last_mut().unwrap().indices.push(item_index); } - fn take_compiled_data_from(&mut self, other_aabb_tree_node: &mut AABBTreeNode) { - if self.compiled_node.is_none() && self.resource_list.is_none() { - mem::swap(&mut self.compiled_node, &mut other_aabb_tree_node.compiled_node); - mem::swap(&mut self.resource_list, &mut other_aabb_tree_node.resource_list); + fn item_count(&self) -> usize { + let mut count = 0; + for list in &self.draw_lists { + count += list.indices.len(); } + count } } pub struct AABBTree { pub nodes: Vec, pub split_size: f32, -} -pub struct AABBTreeNodeInfo { - pub rect: Rect, - pub is_visible: bool, + work_node_indices: Vec, } impl AABBTree { @@ -68,6 +88,8 @@ impl AABBTree { AABBTree { nodes: Vec::new(), split_size: split_size, + + work_node_indices: Vec::new(), } } @@ -86,13 +108,14 @@ impl AABBTree { } let node = self.node(node_index); - println!("{}n={:?} sr={:?} ar={:?} c={:?} items={}", + println!("{}n={:?} sr={:?} ar={:?} c={:?} lists={} items={}", indent, node_index, node.split_rect, node.actual_rect, node.children, - node.src_items.len()); + node.draw_lists.len(), + node.item_count()); if let Some(child_index) = node.children { let NodeIndex(child_index) = child_index; @@ -113,21 +136,10 @@ impl AABBTree { &mut self.nodes[index as usize] } - pub fn node_info(&self) -> Vec { - let mut info = Vec::new(); - for node in &self.nodes { - info.push(AABBTreeNodeInfo { - rect: node.actual_rect, - is_visible: node.is_visible, - }); - } - info - } - #[inline] - fn find_best_node(&mut self, - node_index: NodeIndex, - rect: &Rect) -> Option { + fn find_best_nodes(&mut self, + node_index: NodeIndex, + rect: &Rect) { self.split_if_needed(node_index); if let Some(child_node_index) = self.node(node_index).children { @@ -136,33 +148,31 @@ impl AABBTree { let right_node_index = NodeIndex(child_node_index + 1); let left_intersect = self.node(left_node_index).split_rect.intersects(rect); - let right_intersect = self.node(right_node_index).split_rect.intersects(rect); + if left_intersect { + self.find_best_nodes(left_node_index, rect); + } - if left_intersect && right_intersect { - Some(node_index) - } else if left_intersect { - self.find_best_node(left_node_index, rect) - } else if right_intersect { - self.find_best_node(right_node_index, rect) - } else { - None + let right_intersect = self.node(right_node_index).split_rect.intersects(rect); + if right_intersect { + self.find_best_nodes(right_node_index, rect); } } else { - Some(node_index) + self.work_node_indices.push(node_index); } } #[inline] pub fn insert(&mut self, rect: &Rect, - draw_list_index: usize, - item_index: usize) -> Option { - let node_index = self.find_best_node(NodeIndex(0), rect); - if let Some(node_index) = node_index { - let node = self.node_mut(node_index); - node.append_item(draw_list_index, item_index, rect); + draw_list_id: DrawListId, + item_index: DrawListItemIndex) { + debug_assert!(self.work_node_indices.len() == 0); + self.find_best_nodes(NodeIndex(0), rect); + for node_index in self.work_node_indices.drain(..) { + let NodeIndex(node_index) = node_index; + let node = &mut self.nodes[node_index as usize]; + node.append_item(draw_list_id, item_index, rect); } - node_index } fn split_if_needed(&mut self, node_index: NodeIndex) { @@ -210,8 +220,9 @@ impl AABBTree { let children = { let node = self.node_mut(node_index); if node.split_rect.intersects(rect) { - if node.src_items.len() > 0 && + if node.draw_lists.len() > 0 && node.actual_rect.intersects(rect) { + debug_assert!(node.children.is_none()); node.is_visible = true; } node.children @@ -236,13 +247,4 @@ impl AABBTree { self.check_node_visibility(NodeIndex(0), &rect); } } - - pub fn take_compiled_data_from(&mut self, other_aabb_tree: &mut AABBTree) { - debug_assert!(self.nodes.len() == other_aabb_tree.nodes.len()); - for (this_node, other_node) in self.nodes.iter_mut().zip(other_aabb_tree.nodes - .iter_mut()) { - this_node.take_compiled_data_from(other_node) - } - } } - diff --git a/src/batch.rs b/src/batch.rs index fbe4ccc387..7819b2ae53 100644 --- a/src/batch.rs +++ b/src/batch.rs @@ -1,121 +1,166 @@ use device::{ProgramId, TextureId, TextureIndex}; -use fnv::FnvHasher; -use internal_types::{BatchId, DisplayItemKey, DrawListIndex}; -use internal_types::{PackedVertex, PackedVertexForTextureCacheUpdate, Primitive}; -use std::collections::HashMap; -use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::collections::hash_state::DefaultState; -use types::BlurDirection; - -const MAX_MATRICES_PER_BATCH: usize = 32; - -pub struct RenderBatch { - pub batch_id: BatchId, - pub sort_key: DisplayItemKey, - pub program_id: ProgramId, - pub color_texture_id: TextureId, - pub mask_texture_id: TextureId, +use internal_types::{BlurDirection, PackedVertex}; +use internal_types::{PackedVertexForTextureCacheUpdate, Primitive}; +use std::sync::atomic::Ordering::SeqCst; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; + +pub const MAX_MATRICES_PER_BATCH: usize = 32; + +static ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; + +#[inline] +pub fn new_id() -> usize { + ID_COUNTER.fetch_add(1, SeqCst) +} + +#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] +pub struct VertexBufferId(pub usize); + +#[derive(Clone, Copy, Debug, Eq, PartialEq)] +pub struct MatrixIndex(pub u8); + +impl VertexBufferId { + fn new() -> VertexBufferId { + VertexBufferId(new_id()) + } +} + +pub struct VertexBuffer { + pub id: VertexBufferId, pub vertices: Vec, pub indices: Vec, - pub matrix_map: HashMap>, } -impl RenderBatch { - pub fn new(batch_id: BatchId, - sort_key: DisplayItemKey, - program_id: ProgramId, - color_texture_id: TextureId, - mask_texture_id: TextureId) -> RenderBatch { - RenderBatch { - sort_key: sort_key, - batch_id: batch_id, - program_id: program_id, - color_texture_id: color_texture_id, - mask_texture_id: mask_texture_id, +impl VertexBuffer { + pub fn new() -> VertexBuffer { + VertexBuffer { + id: VertexBufferId::new(), vertices: Vec::new(), indices: Vec::new(), - matrix_map: HashMap::with_hash_state(Default::default()), + } + } +} + +#[derive(Debug)] +pub struct Batch { + pub color_texture_id: TextureId, + pub mask_texture_id: TextureId, + pub first_vertex: u16, + pub index_count: u16, +} + +impl Batch { + pub fn new(color_texture_id: TextureId, + mask_texture_id: TextureId, + first_vertex: u16) -> Batch { + Batch { + color_texture_id: color_texture_id, + mask_texture_id: mask_texture_id, + first_vertex: first_vertex, + index_count: 0, } } pub fn can_add_to_batch(&self, - color_texture_id: TextureId, - mask_texture_id: TextureId, - key: &DisplayItemKey, - program_id: ProgramId) -> bool { - let matrix_ok = self.matrix_map.len() < MAX_MATRICES_PER_BATCH || - self.matrix_map.contains_key(&key.draw_list_index); - let program_ok = program_id == self.program_id; + color_texture_id: TextureId, + mask_texture_id: TextureId) -> bool { let color_texture_ok = color_texture_id == self.color_texture_id; let mask_texture_ok = mask_texture_id == self.mask_texture_id; - let vertices_ok = self.vertices.len() < 65535; // to ensure we can use u16 index buffers + color_texture_ok && mask_texture_ok + } - let batch_ok = matrix_ok && - program_ok && - color_texture_ok && - mask_texture_ok && - vertices_ok; + pub fn add_draw_item(&mut self, index_count: u16) { + self.index_count += index_count; + } +} - if !batch_ok { - //println!("break batch! matrix={} program={} color={} mask={} vertices={} [{:?} vs {:?}]", - // matrix_ok, program_ok, color_texture_ok, mask_texture_ok, vertices_ok, color_texture_id, self.color_texture_id); +pub struct BatchBuilder<'a> { + vertex_buffer: &'a mut VertexBuffer, + batches: Vec, +} + +impl<'a> BatchBuilder<'a> { + pub fn new(vertex_buffer: &mut VertexBuffer) -> BatchBuilder { + BatchBuilder { + vertex_buffer: vertex_buffer, + batches: Vec::new(), } + } - batch_ok + pub fn finalize(self) -> Vec { + self.batches } pub fn add_draw_item(&mut self, + matrix_index: MatrixIndex, color_texture_id: TextureId, mask_texture_id: TextureId, primitive: Primitive, - vertices: &mut [PackedVertex], - key: &DisplayItemKey) { - debug_assert!(color_texture_id == self.color_texture_id); - debug_assert!(mask_texture_id == self.mask_texture_id); + vertices: &mut [PackedVertex]) { - let next_matrix_index = self.matrix_map.len() as u8; - let matrix_index = match self.matrix_map.entry(key.draw_list_index) { - Vacant(entry) => *entry.insert(next_matrix_index), - Occupied(entry) => *entry.get(), + let need_new_batch = match self.batches.last_mut() { + Some(batch) => { + !batch.can_add_to_batch(color_texture_id, + mask_texture_id) + } + None => { + true + } }; - debug_assert!(self.matrix_map.len() <= MAX_MATRICES_PER_BATCH); - let index_offset = self.vertices.len(); + let index_offset = self.vertex_buffer.vertices.len(); + + if need_new_batch { + self.batches.push(Batch::new(color_texture_id, + mask_texture_id, + self.vertex_buffer.indices.len() as u16)); + } + + let mut index_count = 0; match primitive { Primitive::Rectangles | Primitive::Glyphs => { for i in (0..vertices.len()).step_by(4) { let index_base = (index_offset + i) as u16; - self.indices.push(index_base + 0); - self.indices.push(index_base + 1); - self.indices.push(index_base + 2); - self.indices.push(index_base + 2); - self.indices.push(index_base + 3); - self.indices.push(index_base + 1); + self.vertex_buffer.indices.push(index_base + 0); + self.vertex_buffer.indices.push(index_base + 1); + self.vertex_buffer.indices.push(index_base + 2); + self.vertex_buffer.indices.push(index_base + 2); + self.vertex_buffer.indices.push(index_base + 3); + self.vertex_buffer.indices.push(index_base + 1); + index_count += 6; } } Primitive::Triangles => { for i in (0..vertices.len()).step_by(3) { let index_base = (index_offset + i) as u16; - self.indices.push(index_base + 0); - self.indices.push(index_base + 1); - self.indices.push(index_base + 2); + self.vertex_buffer.indices.push(index_base + 0); + self.vertex_buffer.indices.push(index_base + 1); + self.vertex_buffer.indices.push(index_base + 2); + index_count += 3; } } Primitive::TriangleFan => { for i in (1..vertices.len() - 1) { - self.indices.push(index_offset as u16); // center vertex - self.indices.push((index_offset + i + 0) as u16); - self.indices.push((index_offset + i + 1) as u16); + self.vertex_buffer.indices.push(index_offset as u16); // center vertex + self.vertex_buffer.indices.push((index_offset + i + 0) as u16); + self.vertex_buffer.indices.push((index_offset + i + 1) as u16); + index_count += 3; } } } + self.batches.last_mut().unwrap().add_draw_item(index_count); + + let MatrixIndex(matrix_index) = matrix_index; for vertex in vertices.iter_mut() { vertex.matrix_index = matrix_index; } - self.vertices.push_all(vertices); + self.vertex_buffer.vertices.push_all(vertices); + + // TODO(gw): Handle exceeding u16 index buffer! + debug_assert!(self.vertex_buffer.vertices.len() < 65535); } } @@ -194,4 +239,3 @@ impl RasterBatch { self.vertices.push_all(vertices); } } - diff --git a/src/batch_builder.rs b/src/batch_builder.rs new file mode 100644 index 0000000000..e8b9d3468b --- /dev/null +++ b/src/batch_builder.rs @@ -0,0 +1,1454 @@ +use app_units::Au; +use batch::{BatchBuilder, MatrixIndex}; +use clipper::{self, ClipBuffers, Polygon}; +use device::{TextureId, TextureIndex}; +use euclid::{Rect, Point2D, Size2D}; +use fnv::FnvHasher; +use internal_types::{CombinedClipRegion, RectPosUv}; +use internal_types::{RectUv, Primitive, BorderRadiusRasterOp, RasterItem, ClipRectToRegionResult}; +use internal_types::{GlyphKey, PackedVertex, WorkVertex}; +use internal_types::{PolygonPosColorUv, BorderEdgeDirection}; +use internal_types::{BasicRotationAngle, BoxShadowRasterOp, TiledImageKey}; +use renderer::BLUR_INFLATION_FACTOR; +use resource_cache::ResourceCache; +use std::collections::HashMap; +use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::collections::hash_state::DefaultState; +use texture_cache::{TextureCacheItem}; +use webrender_traits::{ColorF, ImageFormat, BorderStyle, BoxShadowClipMode}; +use webrender_traits::{BorderRadius, BorderSide, FontKey, GlyphInstance, ImageKey}; +use webrender_traits::{BorderDisplayItem, GradientStop, ComplexClipRegion}; + +const BORDER_DASH_SIZE: f32 = 3.0; + +impl<'a> BatchBuilder<'a> { + #[inline] + fn add_textured_rectangle(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + image_info: &TextureCacheItem, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + color: &ColorF) { + self.add_axis_aligned_gradient_with_texture(matrix_index, + rect, + clip, + image_info, + resource_cache, + clip_buffers, + &[*color, *color, *color, *color]) + } + + #[inline] + pub fn add_color_rectangle(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + color: &ColorF) { + self.add_axis_aligned_gradient(matrix_index, + rect, + clip, + resource_cache, + clip_buffers, + &[*color, *color, *color, *color]) + } + + pub fn add_image(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + stretch_size: &Size2D, + image_key: ImageKey, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + color: &ColorF) { + // Should be caught higher up + debug_assert!(stretch_size.width > 0.0 && stretch_size.height > 0.0); + + let image_info = resource_cache.get_image(image_key); + + if rect.size.width == stretch_size.width && rect.size.height == stretch_size.height { + let uv = RectUv { + top_left: Point2D::new(image_info.u0, image_info.v0), + top_right: Point2D::new(image_info.u1, image_info.v0), + bottom_left: Point2D::new(image_info.u0, image_info.v1), + bottom_right: Point2D::new(image_info.u1, image_info.v1), + }; + + self.push_image_rect(color, + image_info, + clip, + matrix_index, + resource_cache, + clip_buffers, + rect, + &uv); + return + } + + let (image_info, tiled_size) = match TiledImageKey::create_if_necessary(image_key, + &rect.size, + stretch_size) { + Some(ref tiled_image_key) => { + (resource_cache.get_tiled_image(tiled_image_key), + Size2D::new(tiled_image_key.tiled_width as f32, + tiled_image_key.tiled_height as f32)) + } + None => (image_info, *stretch_size), + }; + + let uv = RectUv { + top_left: Point2D::new(image_info.u0, image_info.v0), + top_right: Point2D::new(image_info.u1, image_info.v0), + bottom_left: Point2D::new(image_info.u0, image_info.v1), + bottom_right: Point2D::new(image_info.u1, image_info.v1), + }; + + let mut y_offset = 0.0; + while y_offset < rect.size.height { + let mut x_offset = 0.0; + while x_offset < rect.size.width { + let origin = Point2D::new(rect.origin.x + x_offset, rect.origin.y + y_offset); + let tiled_rect = Rect::new(origin, tiled_size.clone()); + + self.push_image_rect(color, + image_info, + clip, + matrix_index, + resource_cache, + clip_buffers, + &tiled_rect, + &uv); + + x_offset = x_offset + tiled_size.width; + } + + y_offset = y_offset + tiled_size.height; + } + } + + fn push_image_rect(&mut self, + color: &ColorF, + image_info: &TextureCacheItem, + clip: &CombinedClipRegion, + matrix_index: MatrixIndex, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + rect: &Rect, + uv: &RectUv) { + clipper::clip_rect_to_combined_region(RectPosUv { + pos: *rect, + uv: *uv + }, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { + let mask = mask_for_clip_region(resource_cache, &clip_region, false); + let colors = [*color, *color, *color, *color]; + let mut vertices = clip_region.make_packed_vertices_for_rect(&colors, + mask, + image_info.texture_index); + + self.add_draw_item(matrix_index, + image_info.texture_id, + mask.texture_id, + Primitive::Rectangles, + &mut vertices); + } + } + + pub fn add_text(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + font_key: FontKey, + size: Au, + blur_radius: Au, + color: &ColorF, + glyphs: &Vec, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + device_pixel_ratio: f32) { + let dummy_mask_image = resource_cache.get_dummy_mask_image(); + + // Logic below to pick the primary render item depends on len > 0! + assert!(glyphs.len() > 0); + + let need_text_clip = !clip.clip_in_rect.contains(&rect.origin) || + !clip.clip_in_rect.contains(&rect.bottom_right()); + + let mut glyph_key = GlyphKey::new(font_key, size, blur_radius, glyphs[0].index); + let blur_offset = blur_radius.to_f32_px() * (BLUR_INFLATION_FACTOR as f32) / 2.0; + + let mut text_batches: HashMap<(TextureId, TextureIndex), Vec, DefaultState> = + HashMap::with_hash_state(Default::default()); + + for glyph in glyphs { + glyph_key.index = glyph.index; + let image_info = resource_cache.get_glyph(&glyph_key); + + if image_info.width > 0 && image_info.height > 0 { + let x = glyph.x + image_info.user_x0 as f32 / device_pixel_ratio - blur_offset; + let y = glyph.y - image_info.user_y0 as f32 / device_pixel_ratio - blur_offset; + let width = image_info.width as f32 / device_pixel_ratio; + let height = image_info.height as f32 / device_pixel_ratio; + + let uv = RectUv { + top_left: Point2D::new(image_info.u0, image_info.v0), + top_right: Point2D::new(image_info.u1, image_info.v0), + bottom_left: Point2D::new(image_info.u0, image_info.v1), + bottom_right: Point2D::new(image_info.u1, image_info.v1), + }; + + let rect = RectPosUv { + pos: Rect::new(Point2D::new(x, y), + Size2D::new(width, height)), + uv: uv, + }; + + let rect_buffer = match text_batches.entry((image_info.texture_id, + image_info.texture_index)) { + Occupied(entry) => { + entry.into_mut() + } + Vacant(entry) => { + entry.insert(Vec::new()) + } + }; + + rect_buffer.push(rect); + } + } + + let mut vertex_buffer = Vec::new(); + for ((texture_id, texture_index), mut rect_buffer) in text_batches { + let rect_buffer = if need_text_clip { + let mut clipped_rects = Vec::new(); + for rect in rect_buffer.drain(..) { + rect.clip_to_rect(&mut clip_buffers.sh_clip_buffers, + &clip.clip_in_rect, + &mut clipped_rects); + } + clipped_rects + } else { + rect_buffer + }; + + vertex_buffer.clear(); + + for rect in rect_buffer { + let x0 = rect.pos.origin.x; + let y0 = rect.pos.origin.y; + let x1 = x0 + rect.pos.size.width; + let y1 = y0 + rect.pos.size.height; + + vertex_buffer.push(PackedVertex::from_components( + x0, y0, + color, + rect.uv.top_left.x, rect.uv.top_left.y, + dummy_mask_image.u0, dummy_mask_image.v0, + texture_index, + dummy_mask_image.texture_index)); + vertex_buffer.push(PackedVertex::from_components( + x1, y0, + color, + rect.uv.top_right.x, rect.uv.top_right.y, + dummy_mask_image.u1, dummy_mask_image.v0, + texture_index, + dummy_mask_image.texture_index)); + vertex_buffer.push(PackedVertex::from_components( + x0, y1, + color, + rect.uv.bottom_left.x, rect.uv.bottom_left.y, + dummy_mask_image.u0, dummy_mask_image.v1, + texture_index, + dummy_mask_image.texture_index)); + vertex_buffer.push(PackedVertex::from_components( + x1, y1, + color, + rect.uv.bottom_right.x, rect.uv.bottom_right.y, + dummy_mask_image.v0, dummy_mask_image.v1, + texture_index, + dummy_mask_image.texture_index)); + } + + self.add_draw_item(matrix_index, + texture_id, + dummy_mask_image.texture_id, + Primitive::Glyphs, + &mut vertex_buffer); + } + } + + // Colors are in the order: top left, top right, bottom right, bottom left. + #[inline] + fn add_axis_aligned_gradient(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + colors: &[ColorF; 4]) { + let white_image = resource_cache.get_dummy_color_image(); + self.add_axis_aligned_gradient_with_texture(matrix_index, + rect, + clip, + white_image, + resource_cache, + clip_buffers, + colors); + } + + // Colors are in the order: top left, top right, bottom right, bottom left. + fn add_axis_aligned_gradient_with_texture(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + image_info: &TextureCacheItem, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + colors: &[ColorF; 4]) { + if rect.size.width == 0.0 || rect.size.height == 0.0 { + return + } + + let uv = RectUv { + top_left: Point2D::new(image_info.u0, image_info.v0), + top_right: Point2D::new(image_info.u1, image_info.v0), + bottom_left: Point2D::new(image_info.u0, image_info.v1), + bottom_right: Point2D::new(image_info.u1, image_info.v1), + }; + + clipper::clip_rect_to_combined_region( + RectPosUv { + pos: *rect, + uv: uv, + }, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { + let mask = mask_for_clip_region(resource_cache, &clip_region, false); + + let mut vertices = clip_region.make_packed_vertices_for_rect(colors, + mask, + image_info.texture_index); + + self.add_draw_item(matrix_index, + image_info.texture_id, + mask.texture_id, + Primitive::Rectangles, + &mut vertices); + } + } + + pub fn add_gradient(&mut self, + matrix_index: MatrixIndex, + clip: &CombinedClipRegion, + start_point: &Point2D, + end_point: &Point2D, + stops: &[GradientStop], + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + let white_image = resource_cache.get_dummy_color_image(); + + debug_assert!(stops.len() >= 2); + + let dir_x = end_point.x - start_point.x; + let dir_y = end_point.y - start_point.y; + let dir_len = (dir_x * dir_x + dir_y * dir_y).sqrt(); + let dir_xn = dir_x / dir_len; + let dir_yn = dir_y / dir_len; + let perp_xn = -dir_yn; + let perp_yn = dir_xn; + + for i in 0..stops.len()-1 { + let stop0 = &stops[i]; + let stop1 = &stops[i+1]; + + if stop0.offset == stop1.offset { + continue; + } + + let color0 = &stop0.color; + let color1 = &stop1.color; + + let start_x = start_point.x + stop0.offset * (end_point.x - start_point.x); + let start_y = start_point.y + stop0.offset * (end_point.y - start_point.y); + + let end_x = start_point.x + stop1.offset * (end_point.x - start_point.x); + let end_y = start_point.y + stop1.offset * (end_point.y - start_point.y); + + let len_scale = 1000.0; // todo: determine this properly!! + + let x0 = start_x - perp_xn * len_scale; + let y0 = start_y - perp_yn * len_scale; + + let x1 = end_x - perp_xn * len_scale; + let y1 = end_y - perp_yn * len_scale; + + let x2 = end_x + perp_xn * len_scale; + let y2 = end_y + perp_yn * len_scale; + + let x3 = start_x + perp_xn * len_scale; + let y3 = start_y + perp_yn * len_scale; + + let gradient_polygon = PolygonPosColorUv { + vertices: vec![ + WorkVertex::new(x0, y0, color0, 0.0, 0.0), + WorkVertex::new(x1, y1, color1, 0.0, 0.0), + WorkVertex::new(x2, y2, color1, 0.0, 0.0), + WorkVertex::new(x3, y3, color0, 0.0, 0.0), + ], + }; + + { // scope for buffers + clipper::clip_rect_to_combined_region(gradient_polygon, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.polygon_pos_color_uv, + clip); + for clip_result in clip_buffers.polygon_pos_color_uv + .clip_rect_to_region_result_output + .drain(..) { + let mask = mask_for_clip_region(resource_cache, &clip_result, false); + + let mut packed_vertices = Vec::new(); + if clip_result.rect_result.vertices.len() >= 3 { + for vert in clip_result.rect_result.vertices.iter() { + packed_vertices.push(clip_result.make_packed_vertex( + &vert.position(), + &vert.uv(), + &vert.color(), + &mask, + white_image.texture_index)); + } + } + + if packed_vertices.len() > 0 { + self.add_draw_item(matrix_index, + white_image.texture_id, + mask.texture_id, + Primitive::TriangleFan, + &mut packed_vertices); + } + } + } + } + } + + pub fn add_box_shadow(&mut self, + matrix_index: MatrixIndex, + box_bounds: &Rect, + clip: &CombinedClipRegion, + box_offset: &Point2D, + color: &ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); + + // Fast path. + if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None { + self.add_color_rectangle(matrix_index, &rect, clip, resource_cache, clip_buffers, color); + return; + } + + // Draw the corners. + self.add_box_shadow_corners(matrix_index, + box_bounds, + box_offset, + color, + blur_radius, + spread_radius, + border_radius, + clip_mode, + clip, + resource_cache, + clip_buffers); + + // Draw the sides. + self.add_box_shadow_sides(matrix_index, + box_bounds, + clip, + box_offset, + color, + blur_radius, + spread_radius, + border_radius, + clip_mode, + resource_cache, + clip_buffers); + + match clip_mode { + BoxShadowClipMode::None => { + // Fill the center area. + self.add_color_rectangle(matrix_index, + box_bounds, + clip, + resource_cache, + clip_buffers, + color); + } + BoxShadowClipMode::Outset => { + // Fill the center area. + let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); + let center_rect = Rect::new(metrics.tl_inner, + Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, + metrics.br_inner.y - metrics.tl_inner.y)); + let mut clip = *clip; + clip.clip_out(&ComplexClipRegion::from_rect(&box_bounds)); + self.add_color_rectangle(matrix_index, + ¢er_rect, + &clip, + resource_cache, + clip_buffers, + color); + } + BoxShadowClipMode::Inset => { + // Fill in the outsides. + self.fill_outside_area_of_inset_box_shadow(matrix_index, + box_bounds, + clip, + box_offset, + color, + blur_radius, + spread_radius, + border_radius, + resource_cache, + clip_buffers); + } + } + } + + fn add_box_shadow_corners(&mut self, + matrix_index: MatrixIndex, + box_bounds: &Rect, + box_offset: &Point2D, + color: &ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + // Draw the corners. + // + // +--+------------------+--+ + // |##| |##| + // +--+------------------+--+ + // | | | | + // | | | | + // | | | | + // +--+------------------+--+ + // |##| |##| + // +--+------------------+--+ + + let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); + let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); + + let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, + box_bounds, + border_radius, + clip_mode); + + self.add_box_shadow_corner(matrix_index, + &metrics.tl_outer, + &metrics.tl_inner, + &color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Upright); + self.add_box_shadow_corner(matrix_index, + &Point2D::new(metrics.tr_inner.x, metrics.tr_outer.y), + &Point2D::new(metrics.tr_outer.x, metrics.tr_inner.y), + &color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise90); + self.add_box_shadow_corner(matrix_index, + &metrics.br_inner, + &metrics.br_outer, + &color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise180); + self.add_box_shadow_corner(matrix_index, + &Point2D::new(metrics.bl_outer.x, metrics.bl_inner.y), + &Point2D::new(metrics.bl_inner.x, metrics.bl_outer.y), + &color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise270); + } + + fn add_box_shadow_sides(&mut self, + matrix_index: MatrixIndex, + box_bounds: &Rect, + clip: &CombinedClipRegion, + box_offset: &Point2D, + color: &ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); + let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); + + let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, + box_bounds, + border_radius, + clip_mode); + + // Draw the sides. + // + // +--+------------------+--+ + // | |##################| | + // +--+------------------+--+ + // |##| |##| + // |##| |##| + // |##| |##| + // +--+------------------+--+ + // | |##################| | + // +--+------------------+--+ + + let horizontal_size = Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, + metrics.edge_size); + let vertical_size = Size2D::new(metrics.edge_size, + metrics.br_inner.y - metrics.tl_inner.y); + let top_rect = Rect::new(metrics.tl_outer + Point2D::new(metrics.edge_size, 0.0), + horizontal_size); + let right_rect = + Rect::new(metrics.tr_outer + Point2D::new(-metrics.edge_size, metrics.edge_size), + vertical_size); + let bottom_rect = + Rect::new(metrics.bl_outer + Point2D::new(metrics.edge_size, -metrics.edge_size), + horizontal_size); + let left_rect = Rect::new(metrics.tl_outer + Point2D::new(0.0, metrics.edge_size), + vertical_size); + + self.add_box_shadow_edge(matrix_index, + &top_rect.origin, + &top_rect.bottom_right(), + color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise270); + self.add_box_shadow_edge(matrix_index, + &right_rect.origin, + &right_rect.bottom_right(), + color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Upright); + self.add_box_shadow_edge(matrix_index, + &bottom_rect.origin, + &bottom_rect.bottom_right(), + color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise90); + self.add_box_shadow_edge(matrix_index, + &left_rect.origin, + &left_rect.bottom_right(), + color, + blur_radius, + border_radius, + clip_mode, + &clip, + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise180); + } + + fn fill_outside_area_of_inset_box_shadow(&mut self, + matrix_index: MatrixIndex, + box_bounds: &Rect, + clip: &CombinedClipRegion, + box_offset: &Point2D, + color: &ColorF, + blur_radius: f32, + spread_radius: f32, + border_radius: f32, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); + let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); + + let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, + box_bounds, + border_radius, + BoxShadowClipMode::Inset); + + // Fill in the outside area of the box. + // + // +------------------------------+ + // A --> |##############################| + // +--+--+------------------+--+--+ + // |##| | | |##| + // |##+--+------------------+--+##| + // |##| | | |##| + // D --> |##| | | |##| <-- B + // |##| | | |##| + // |##+--+------------------+--+##| + // |##| | | |##| + // +--+--+------------------+--+--+ + // C --> |##############################| + // +------------------------------+ + + // A: + self.add_color_rectangle(matrix_index, + &Rect::new(box_bounds.origin, + Size2D::new(box_bounds.size.width, + metrics.tl_outer.y - box_bounds.origin.y)), + &clip, + resource_cache, + clip_buffers, + color); + + // B: + self.add_color_rectangle(matrix_index, + &Rect::new(metrics.tr_outer, + Size2D::new(box_bounds.max_x() - metrics.tr_outer.x, + metrics.br_outer.y - metrics.tr_outer.y)), + &clip, + resource_cache, + clip_buffers, + color); + + // C: + self.add_color_rectangle(matrix_index, + &Rect::new(Point2D::new(box_bounds.origin.x, metrics.bl_outer.y), + Size2D::new(box_bounds.size.width, + box_bounds.max_y() - metrics.br_outer.y)), + &clip, + resource_cache, + clip_buffers, + color); + + // D: + self.add_color_rectangle(matrix_index, + &Rect::new(Point2D::new(box_bounds.origin.x, metrics.tl_outer.y), + Size2D::new(metrics.tl_outer.x - box_bounds.origin.x, + metrics.bl_outer.y - metrics.tl_outer.y)), + &clip, + resource_cache, + clip_buffers, + color); + } + + fn adjust_clip_for_box_shadow_clip_mode<'b>(&mut self, + clip: &CombinedClipRegion<'b>, + box_bounds: &Rect, + border_radius: f32, + clip_mode: BoxShadowClipMode) + -> CombinedClipRegion<'b> { + let mut clip = *clip; + match clip_mode { + BoxShadowClipMode::None => {} + BoxShadowClipMode::Inset => { + clip.clip_in(&ComplexClipRegion { + rect: *box_bounds, + radii: BorderRadius::uniform(border_radius), + }); + } + BoxShadowClipMode::Outset => { + // TODO(pcwalton): Support border radii here too, once we have inverted rounded + // rect clip masks. + clip.clip_out(&ComplexClipRegion::from_rect(box_bounds)); + } + } + clip + } + + #[inline] + fn add_border_edge(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + direction: BorderEdgeDirection, + color: &ColorF, + border_style: BorderStyle, + resource_cache: &ResourceCache, + clip_buffers: &mut clipper::ClipBuffers) { + // TODO: Check for zero width/height borders! + if color.a <= 0.0 { + return + } + + match border_style { + BorderStyle::Dashed => { + let (extent, step) = match direction { + BorderEdgeDirection::Horizontal => { + (rect.size.width, rect.size.height * BORDER_DASH_SIZE) + } + BorderEdgeDirection::Vertical => { + (rect.size.height, rect.size.width * BORDER_DASH_SIZE) + } + }; + let mut origin = 0.0; + while origin < extent { + let dash_rect = match direction { + BorderEdgeDirection::Horizontal => { + Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), + Size2D::new(f32::min(step, extent - origin), + rect.size.height)) + } + BorderEdgeDirection::Vertical => { + Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), + Size2D::new(rect.size.width, + f32::min(step, extent - origin))) + } + }; + + self.add_color_rectangle(matrix_index, + &dash_rect, + clip, + resource_cache, + clip_buffers, + color); + + origin += step + step; + } + } + BorderStyle::Dotted => { + let (extent, step) = match direction { + BorderEdgeDirection::Horizontal => (rect.size.width, rect.size.height), + BorderEdgeDirection::Vertical => (rect.size.height, rect.size.width), + }; + let mut origin = 0.0; + while origin < extent { + let (dot_rect, mask_radius) = match direction { + BorderEdgeDirection::Horizontal => { + (Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), + Size2D::new(f32::min(step, extent - origin), + rect.size.height)), + rect.size.height / 2.0) + } + BorderEdgeDirection::Vertical => { + (Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), + Size2D::new(rect.size.width, + f32::min(step, extent - origin))), + rect.size.width / 2.0) + } + }; + + let raster_op = + BorderRadiusRasterOp::create(&Size2D::new(mask_radius, mask_radius), + &Size2D::new(0.0, 0.0), + false, + ImageFormat::RGBA8).expect( + "Didn't find border radius mask for dashed border!"); + let raster_item = RasterItem::BorderRadius(raster_op); + let color_image = resource_cache.get_raster(&raster_item); + + // Top left: + self.add_textured_rectangle(matrix_index, + &Rect::new(dot_rect.origin, + Size2D::new(dot_rect.size.width / 2.0, + dot_rect.size.height / 2.0)), + clip, + color_image, + resource_cache, + clip_buffers, + color); + + // Top right: + self.add_textured_rectangle(matrix_index, + &Rect::new(dot_rect.top_right(), + Size2D::new(-dot_rect.size.width / 2.0, + dot_rect.size.height / 2.0)), + clip, + color_image, + resource_cache, + clip_buffers, + color); + + // Bottom right: + self.add_textured_rectangle(matrix_index, + &Rect::new(dot_rect.bottom_right(), + Size2D::new(-dot_rect.size.width / 2.0, + -dot_rect.size.height / 2.0)), + clip, + color_image, + resource_cache, + clip_buffers, + color); + + // Bottom left: + self.add_textured_rectangle(matrix_index, + &Rect::new(dot_rect.bottom_left(), + Size2D::new(dot_rect.size.width / 2.0, + -dot_rect.size.height / 2.0)), + clip, + color_image, + resource_cache, + clip_buffers, + color); + + origin += step + step; + } + } + BorderStyle::Double => { + let (outer_rect, inner_rect) = match direction { + BorderEdgeDirection::Horizontal => { + (Rect::new(rect.origin, + Size2D::new(rect.size.width, rect.size.height / 3.0)), + Rect::new(Point2D::new(rect.origin.x, + rect.origin.y + rect.size.height * 2.0 / 3.0), + Size2D::new(rect.size.width, rect.size.height / 3.0))) + } + BorderEdgeDirection::Vertical => { + (Rect::new(rect.origin, + Size2D::new(rect.size.width / 3.0, rect.size.height)), + Rect::new(Point2D::new(rect.origin.x + rect.size.width * 2.0 / 3.0, + rect.origin.y), + Size2D::new(rect.size.width / 3.0, rect.size.height))) + } + }; + self.add_color_rectangle(matrix_index, + &outer_rect, + clip, + resource_cache, + clip_buffers, + color); + self.add_color_rectangle(matrix_index, + &inner_rect, + clip, + resource_cache, + clip_buffers, + color); + } + _ => { + self.add_color_rectangle(matrix_index, + rect, + clip, + resource_cache, + clip_buffers, + color); + } + } + } + + #[inline] + fn add_border_corner(&mut self, + matrix_index: MatrixIndex, + clip: &CombinedClipRegion, + vertices_rect: &Rect, + color0: &ColorF, + color1: &ColorF, + outer_radius: &Size2D, + inner_radius: &Size2D, + resource_cache: &ResourceCache, + clip_buffers: &mut clipper::ClipBuffers, + rotation_angle: BasicRotationAngle) { + if color0.a <= 0.0 && color1.a <= 0.0 { + return + } + + // TODO: Check for zero width/height borders! + let white_image = resource_cache.get_dummy_color_image(); + let mask_image = match BorderRadiusRasterOp::create(outer_radius, + inner_radius, + false, + ImageFormat::A8) { + Some(raster_item) => { + let raster_item = RasterItem::BorderRadius(raster_item); + resource_cache.get_raster(&raster_item) + } + None => { + resource_cache.get_dummy_mask_image() + } + }; + + // FIXME(pcwalton): Either use RGBA8 textures instead of alpha masks here, or implement + // a mask combiner. + let mask_uv = RectUv::from_image_and_rotation_angle(mask_image, rotation_angle); + clipper::clip_rect_to_combined_region( + RectPosUv { + pos: *vertices_rect, + uv: mask_uv, + }, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { + let v0; + let v1; + let muv0; + let muv1; + match rotation_angle { + BasicRotationAngle::Upright => { + v0 = clip_region.rect_result.pos.origin; + muv0 = clip_region.rect_result.uv.top_left; + v1 = clip_region.rect_result.pos.bottom_right(); + muv1 = clip_region.rect_result.uv.bottom_right; + } + BasicRotationAngle::Clockwise90 => { + v0 = clip_region.rect_result.pos.top_right(); + muv0 = clip_region.rect_result.uv.top_right; + v1 = clip_region.rect_result.pos.bottom_left(); + muv1 = clip_region.rect_result.uv.bottom_left; + } + BasicRotationAngle::Clockwise180 => { + v0 = clip_region.rect_result.pos.bottom_right(); + muv0 = clip_region.rect_result.uv.bottom_right; + v1 = clip_region.rect_result.pos.origin; + muv1 = clip_region.rect_result.uv.top_left; + } + BasicRotationAngle::Clockwise270 => { + v0 = clip_region.rect_result.pos.bottom_left(); + muv0 = clip_region.rect_result.uv.bottom_left; + v1 = clip_region.rect_result.pos.top_right(); + muv1 = clip_region.rect_result.uv.top_right; + } + } + + let mut vertices = [ + PackedVertex::from_components(v0.x, v0.y, + color0, + 0.0, 0.0, + muv0.x, muv0.y, + white_image.texture_index, + mask_image.texture_index), + PackedVertex::from_components(v1.x, v1.y, + color0, + 0.0, 0.0, + muv1.x, muv1.y, + white_image.texture_index, mask_image.texture_index), + PackedVertex::from_components(v0.x, v1.y, + color0, + 0.0, 0.0, + muv0.x, muv1.y, + white_image.texture_index, mask_image.texture_index), + PackedVertex::from_components(v0.x, v0.y, + color1, + 0.0, 0.0, + muv0.x, muv0.y, + white_image.texture_index, mask_image.texture_index), + PackedVertex::from_components(v1.x, v0.y, + color1, + 0.0, 0.0, + muv1.x, muv0.y, + white_image.texture_index, mask_image.texture_index), + PackedVertex::from_components(v1.x, v1.y, + color1, + 0.0, 0.0, + muv1.x, muv1.y, + white_image.texture_index, mask_image.texture_index), + ]; + + self.add_draw_item(matrix_index, + white_image.texture_id, + mask_image.texture_id, + Primitive::Triangles, + &mut vertices); + } + } + + fn add_color_image_rectangle(&mut self, + matrix_index: MatrixIndex, + v0: &Point2D, + v1: &Point2D, + clip: &CombinedClipRegion, + color0: &ColorF, + color1: &ColorF, + color_image: &TextureCacheItem, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + rotation_angle: BasicRotationAngle) { + if color0.a <= 0.0 || color1.a <= 0.0 { + return + } + + let vertices_rect = Rect::new(*v0, Size2D::new(v1.x - v0.x, v1.y - v0.y)); + let color_uv = RectUv::from_image_and_rotation_angle(color_image, rotation_angle); + + clipper::clip_rect_to_combined_region(RectPosUv { + pos: vertices_rect, + uv: color_uv, + }, + &mut clip_buffers.sh_clip_buffers, + &mut clip_buffers.rect_pos_uv, + clip); + for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { + let mask = mask_for_clip_region(resource_cache, + &clip_region, + false); + let colors = [*color0, *color0, *color1, *color1]; + let mut vertices = clip_region.make_packed_vertices_for_rect(&colors, + mask, + color_image.texture_index); + + self.add_draw_item(matrix_index, + color_image.texture_id, + mask.texture_id, + Primitive::Rectangles, + &mut vertices); + } + } + + pub fn add_border(&mut self, + matrix_index: MatrixIndex, + rect: &Rect, + clip: &CombinedClipRegion, + info: &BorderDisplayItem, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers) { + // TODO: If any border segment is alpha, place all in alpha pass. + // Is it ever worth batching at a per-segment level? + let radius = &info.radius; + let left = &info.left; + let right = &info.right; + let top = &info.top; + let bottom = &info.bottom; + + let tl_outer = Point2D::new(rect.origin.x, rect.origin.y); + let tl_inner = tl_outer + Point2D::new(radius.top_left.width.max(left.width), + radius.top_left.height.max(top.width)); + + let tr_outer = Point2D::new(rect.origin.x + rect.size.width, rect.origin.y); + let tr_inner = tr_outer + Point2D::new(-radius.top_right.width.max(right.width), + radius.top_right.height.max(top.width)); + + let bl_outer = Point2D::new(rect.origin.x, rect.origin.y + rect.size.height); + let bl_inner = bl_outer + Point2D::new(radius.bottom_left.width.max(left.width), + -radius.bottom_left.height.max(bottom.width)); + + let br_outer = Point2D::new(rect.origin.x + rect.size.width, + rect.origin.y + rect.size.height); + let br_inner = br_outer - Point2D::new(radius.bottom_right.width.max(right.width), + radius.bottom_right.height.max(bottom.width)); + + let left_color = left.border_color(1.0, 2.0/3.0, 0.3, 0.7); + let top_color = top.border_color(1.0, 2.0/3.0, 0.3, 0.7); + let right_color = right.border_color(2.0/3.0, 1.0, 0.7, 0.3); + let bottom_color = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3); + + // Edges + self.add_border_edge(matrix_index, + &Rect::new(Point2D::new(tl_outer.x, tl_inner.y), + Size2D::new(left.width, bl_inner.y - tl_inner.y)), + clip, + BorderEdgeDirection::Vertical, + &left_color, + info.left.style, + resource_cache, + clip_buffers); + + self.add_border_edge(matrix_index, + &Rect::new(Point2D::new(tl_inner.x, tl_outer.y), + Size2D::new(tr_inner.x - tl_inner.x, + tr_outer.y + top.width - tl_outer.y)), + clip, + BorderEdgeDirection::Horizontal, + &top_color, + info.top.style, + resource_cache, + clip_buffers); + + self.add_border_edge(matrix_index, + &Rect::new(Point2D::new(br_outer.x - right.width, tr_inner.y), + Size2D::new(right.width, br_inner.y - tr_inner.y)), + clip, + BorderEdgeDirection::Vertical, + &right_color, + info.right.style, + resource_cache, + clip_buffers); + + self.add_border_edge(matrix_index, + &Rect::new(Point2D::new(bl_inner.x, bl_outer.y - bottom.width), + Size2D::new(br_inner.x - bl_inner.x, + br_outer.y - bl_outer.y + bottom.width)), + clip, + BorderEdgeDirection::Horizontal, + &bottom_color, + info.bottom.style, + resource_cache, + clip_buffers); + + // Corners + self.add_border_corner(matrix_index, + clip, + &Rect::new(tl_outer, + Size2D::new(tl_inner.x - tl_outer.x, + tl_inner.y - tl_outer.y)), + &left_color, + &top_color, + &radius.top_left, + &info.top_left_inner_radius(), + resource_cache, + clip_buffers, + BasicRotationAngle::Upright); + + self.add_border_corner(matrix_index, + clip, + &Rect::new(Point2D::new(tr_inner.x, tr_outer.y), + Size2D::new(tr_outer.x - tr_inner.x, + tr_inner.y - tr_outer.y)), + &right_color, + &top_color, + &radius.top_right, + &info.top_right_inner_radius(), + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise90); + + self.add_border_corner(matrix_index, + clip, + &Rect::new(br_inner, + Size2D::new(br_outer.x - br_inner.x, + br_outer.y - br_inner.y)), + &right_color, + &bottom_color, + &radius.bottom_right, + &info.bottom_right_inner_radius(), + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise180); + + self.add_border_corner(matrix_index, + clip, + &Rect::new(Point2D::new(bl_outer.x, bl_inner.y), + Size2D::new(bl_inner.x - bl_outer.x, + bl_outer.y - bl_inner.y)), + &left_color, + &bottom_color, + &radius.bottom_left, + &info.bottom_left_inner_radius(), + resource_cache, + clip_buffers, + BasicRotationAngle::Clockwise270); + } + + // FIXME(pcwalton): Assumes rectangles are well-formed with origin in TL + fn add_box_shadow_corner(&mut self, + matrix_index: MatrixIndex, + top_left: &Point2D, + bottom_right: &Point2D, + color: &ColorF, + blur_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + rotation_angle: BasicRotationAngle) { + let inverted = match clip_mode { + BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, + BoxShadowClipMode::Inset => true, + }; + + let color_image = match BoxShadowRasterOp::create_corner(blur_radius, + border_radius, + inverted) { + Some(raster_item) => { + let raster_item = RasterItem::BoxShadow(raster_item); + resource_cache.get_raster(&raster_item) + } + None => resource_cache.get_dummy_color_image(), + }; + + self.add_color_image_rectangle(matrix_index, + top_left, + bottom_right, + clip, + color, + color, + &color_image, + resource_cache, + clip_buffers, + rotation_angle) + } + + fn add_box_shadow_edge(&mut self, + matrix_index: MatrixIndex, + top_left: &Point2D, + bottom_right: &Point2D, + color: &ColorF, + blur_radius: f32, + border_radius: f32, + clip_mode: BoxShadowClipMode, + clip: &CombinedClipRegion, + resource_cache: &ResourceCache, + clip_buffers: &mut ClipBuffers, + rotation_angle: BasicRotationAngle) { + let inverted = match clip_mode { + BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, + BoxShadowClipMode::Inset => true, + }; + + let color_image = match BoxShadowRasterOp::create_edge(blur_radius, + border_radius, + inverted) { + Some(raster_item) => { + let raster_item = RasterItem::BoxShadow(raster_item); + resource_cache.get_raster(&raster_item) + } + None => resource_cache.get_dummy_color_image(), + }; + + self.add_color_image_rectangle(matrix_index, + top_left, + bottom_right, + clip, + color, + color, + &color_image, + resource_cache, + clip_buffers, + rotation_angle) + } +} + +trait BorderSideHelpers { + fn border_color(&self, + scale_factor_0: f32, + scale_factor_1: f32, + black_color_0: f32, + black_color_1: f32) -> ColorF; +} + +impl BorderSideHelpers for BorderSide { + fn border_color(&self, + scale_factor_0: f32, + scale_factor_1: f32, + black_color_0: f32, + black_color_1: f32) -> ColorF { + match self.style { + BorderStyle::Inset => { + if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { + self.color.scale_rgb(scale_factor_1) + } else { + ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a) + } + } + BorderStyle::Outset => { + if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { + self.color.scale_rgb(scale_factor_0) + } else { + ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a) + } + } + _ => self.color, + } + } +} + +fn mask_for_border_radius<'a>(resource_cache: &'a ResourceCache, + border_radius: f32, + inverted: bool) + -> &'a TextureCacheItem { + if border_radius == 0.0 { + return resource_cache.get_dummy_mask_image() + } + + let border_radius = Au::from_f32_px(border_radius); + resource_cache.get_raster(&RasterItem::BorderRadius(BorderRadiusRasterOp { + outer_radius_x: border_radius, + outer_radius_y: border_radius, + inner_radius_x: Au(0), + inner_radius_y: Au(0), + inverted: inverted, + image_format: ImageFormat::A8, + })) +} + +fn mask_for_clip_region<'a,P>(resource_cache: &'a ResourceCache, + clip_region: &ClipRectToRegionResult

, + inverted: bool) + -> &'a TextureCacheItem { + match clip_region.mask_result { + None => { + resource_cache.get_dummy_mask_image() + } + Some(ref mask_result) => { + mask_for_border_radius(resource_cache, + mask_result.border_radius, + inverted) + } + } +} + +#[derive(Debug)] +struct BoxShadowMetrics { + edge_size: f32, + tl_outer: Point2D, + tl_inner: Point2D, + tr_outer: Point2D, + tr_inner: Point2D, + bl_outer: Point2D, + bl_inner: Point2D, + br_outer: Point2D, + br_inner: Point2D, +} + +impl BoxShadowMetrics { + fn new(box_bounds: &Rect, border_radius: f32, blur_radius: f32) -> BoxShadowMetrics { + let outside_edge_size = 3.0 * blur_radius; + let inside_edge_size = outside_edge_size.max(border_radius); + let edge_size = outside_edge_size + inside_edge_size; + let inner_rect = box_bounds.inflate(-inside_edge_size, -inside_edge_size); + let outer_rect = box_bounds.inflate(outside_edge_size, outside_edge_size); + + BoxShadowMetrics { + edge_size: edge_size, + tl_outer: outer_rect.origin, + tl_inner: inner_rect.origin, + tr_outer: outer_rect.top_right(), + tr_inner: inner_rect.top_right(), + bl_outer: outer_rect.bottom_left(), + bl_inner: inner_rect.bottom_left(), + br_outer: outer_rect.bottom_right(), + br_inner: inner_rect.bottom_right(), + } + } +} + +fn compute_box_shadow_rect(box_bounds: &Rect, + box_offset: &Point2D, + spread_radius: f32) + -> Rect { + let mut rect = (*box_bounds).clone(); + rect.origin.x += box_offset.x; + rect.origin.y += box_offset.y; + rect.inflate(spread_radius, spread_radius) +} diff --git a/src/clipper.rs b/src/clipper.rs index ed0a6fce1f..7392a08906 100644 --- a/src/clipper.rs +++ b/src/clipper.rs @@ -1,13 +1,23 @@ use euclid::{Point2D, Rect, Size2D}; use internal_types::{ClipRectToRegionMaskResult, ClipRectToRegionResult}; use internal_types::{CombinedClipRegion, PolygonPosColorUv, RectPosUv, RectUv, WorkVertex}; -use render_backend::MAX_RECT; use simd::f32x4; use std::fmt::Debug; use std::mem; -use types::{ColorF, ComplexClipRegion}; +use webrender_traits::{ColorF, ComplexClipRegion}; use util; +pub static MAX_RECT: Rect = Rect { + origin: Point2D { + x: -1000.0, + y: -1000.0, + }, + size: Size2D { + width: 10000.0, + height: 10000.0, + }, +}; + /// Computes whether the point c is inside the clipping edge ab. /// /// NB: Assumes clockwise winding for the clipping polygon that the edge comes from. @@ -516,4 +526,3 @@ impl Polygon for PolygonPosColorUv { self.vertices.iter().any(|vertex| rect.contains(&Point2D::new(vertex.x, vertex.y))) } } - diff --git a/src/device.rs b/src/device.rs index 688d61a9d6..d68669b59b 100644 --- a/src/device.rs +++ b/src/device.rs @@ -9,7 +9,7 @@ use std::fs::File; use std::path::PathBuf; use std::mem; use std::io::Read; -use types::ImageFormat; +use webrender_traits::ImageFormat; #[cfg(not(any(target_os = "android", target_os = "gonk")))] const GL_FORMAT_BGRA: gl::GLuint = gl::BGRA; @@ -1343,9 +1343,12 @@ impl Device { gl::buffer_data(gl::ELEMENT_ARRAY_BUFFER, &indices, usage_hint.to_gl()); } - pub fn draw_triangles_u16(&mut self, index_count: i32) { + pub fn draw_triangles_u16(&mut self, first_vertex: i32, index_count: i32) { debug_assert!(self.inside_frame); - gl::draw_elements(gl::TRIANGLES, index_count, gl::UNSIGNED_SHORT, 0); + gl::draw_elements(gl::TRIANGLES, + index_count, + gl::UNSIGNED_SHORT, + first_vertex as u32 * 2); } pub fn delete_vao(&mut self, vao_id: VAOId) { diff --git a/src/frame.rs b/src/frame.rs new file mode 100644 index 0000000000..aef523ccbe --- /dev/null +++ b/src/frame.rs @@ -0,0 +1,765 @@ +use batch::MAX_MATRICES_PER_BATCH; +use device::{TextureId}; +use euclid::{Rect, Point2D, Size2D, Matrix4}; +use fnv::FnvHasher; +use internal_types::{BlurDirection, LowLevelFilterOp, CompositionOp, DrawListItemIndex}; +use internal_types::{BatchUpdateList, DrawListId, TextureTarget}; +use internal_types::{RendererFrame, DrawListContext, BatchInfo, DrawCall}; +use internal_types::{BatchUpdate, BatchUpdateOp, DrawLayer}; +use internal_types::{DrawCommand, ClearInfo, CompositeInfo}; +use layer::Layer; +use node_compiler::NodeCompiler; +use resource_cache::ResourceCache; +use resource_list::BuildRequiredResources; +use scene::{SceneStackingContext, ScenePipeline, Scene, SceneItem, SpecificSceneItem}; +use scoped_threadpool; +use std::cmp::Ordering; +use std::collections::HashMap; +use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::collections::hash_state::DefaultState; +use std::mem; +use util; +use util::MatrixHelpers; +use webrender_traits::{PipelineId, Epoch, ScrollPolicy, ScrollLayerId, StackingContext}; +use webrender_traits::{FilterOp, ImageFormat, MixBlendMode, StackingLevel}; + +#[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)] +pub struct RenderTargetIndex(pub u32); + +#[derive(Debug)] +pub struct DrawListBatchInfo { + pub scroll_layer_id: ScrollLayerId, + pub draw_lists: Vec, +} + +#[derive(Debug)] +pub enum FrameRenderItem { + Clear(ClearInfo), + Composite(CompositeInfo), + DrawListBatch(DrawListBatchInfo), +} + +#[derive(Debug)] +pub struct FrameRenderTarget { + pub size: Size2D, + pub texture_id: Option, + pub items: Vec, + current_batch: Option, +} + +impl FrameRenderTarget { + pub fn new(size: Size2D, + texture_id: Option) -> FrameRenderTarget { + FrameRenderTarget { + size: size, + items: Vec::new(), + texture_id: texture_id, + current_batch: None, + } + } + + fn finalize(&mut self) { + self.flush(); + } + + fn flush(&mut self) { + if let Some(batch) = self.current_batch.take() { + self.items.push(FrameRenderItem::DrawListBatch(batch)); + } + } + + fn push_clear(&mut self, clear_info: ClearInfo) { + self.flush(); + self.items.push(FrameRenderItem::Clear(clear_info)); + } + + fn push_composite(&mut self, composite_info: CompositeInfo) { + self.flush(); + self.items.push(FrameRenderItem::Composite(composite_info)); + } + + fn push_draw_list(&mut self, + draw_list_id: DrawListId, + scroll_layer_id: ScrollLayerId) { + let need_new_batch = match self.current_batch { + Some(ref batch) => { + batch.scroll_layer_id != scroll_layer_id || + batch.draw_lists.len() == MAX_MATRICES_PER_BATCH + } + None => { + true + } + }; + + if need_new_batch { + self.flush(); + + self.current_batch = Some(DrawListBatchInfo { + scroll_layer_id: scroll_layer_id, + draw_lists: Vec::new(), + }); + } + + self.current_batch.as_mut().unwrap().draw_lists.push(draw_list_id); + } +} + +pub struct Frame { + pub layers: HashMap>, + pub pipeline_epoch_map: HashMap>, + pub render_targets: Vec, + pub render_target_stack: Vec, + pub pending_updates: BatchUpdateList, +} + +enum SceneItemKind<'a> { + StackingContext(&'a SceneStackingContext), + Pipeline(&'a ScenePipeline) +} + +struct FrameItem { + scene_item: SceneItem, + sort_key: u32, +} + +impl<'a> SceneItemKind<'a> { + fn collect_frame_items(&self, scene: &Scene) -> Vec { + let mut result = Vec::new(); + let mut current_sort_key = 0; + + let stacking_context = match *self { + SceneItemKind::StackingContext(stacking_context) => { + &stacking_context.stacking_context + } + SceneItemKind::Pipeline(pipeline) => { + if let Some(background_draw_list) = pipeline.background_draw_list { + result.push(FrameItem { + scene_item: SceneItem { + stacking_level: StackingLevel::BackgroundAndBorders, + specific: SpecificSceneItem::DrawList(background_draw_list), + }, + sort_key: current_sort_key, + }); + current_sort_key += 1; + } + + &scene.stacking_context_map + .get(&pipeline.root_stacking_context_id) + .unwrap() + .stacking_context + } + }; + + for display_list_id in &stacking_context.display_lists { + let display_list = &scene.display_list_map[display_list_id]; + for item in &display_list.items { + result.push(FrameItem { + scene_item: item.clone(), + sort_key: current_sort_key, + }); + current_sort_key += 1; + } + } + + // TODO: Sort by z-index here? + result.sort_by(|a, b| { + let result = a.scene_item.stacking_level.cmp(&b.scene_item.stacking_level); + match result { + Ordering::Equal => { + a.sort_key.cmp(&b.sort_key) + } + _ => { + result + } + } + }); + + result + } +} + +trait StackingContextHelpers { + fn needs_composition_operation_for_mix_blend_mode(&self) -> bool; +} + +impl StackingContextHelpers for StackingContext { + fn needs_composition_operation_for_mix_blend_mode(&self) -> bool { + match self.mix_blend_mode { + MixBlendMode::Normal => false, + MixBlendMode::Multiply | + MixBlendMode::Screen | + MixBlendMode::Overlay | + MixBlendMode::Darken | + MixBlendMode::Lighten | + MixBlendMode::ColorDodge | + MixBlendMode::ColorBurn | + MixBlendMode::HardLight | + MixBlendMode::SoftLight | + MixBlendMode::Difference | + MixBlendMode::Exclusion | + MixBlendMode::Hue | + MixBlendMode::Saturation | + MixBlendMode::Color | + MixBlendMode::Luminosity => true, + } + } +} + +impl Frame { + pub fn new() -> Frame { + Frame { + layers: HashMap::with_hash_state(Default::default()), + pipeline_epoch_map: HashMap::with_hash_state(Default::default()), + render_targets: Vec::new(), + render_target_stack: Vec::new(), + pending_updates: BatchUpdateList::new(), + } + } + + pub fn reset(&mut self, + resource_cache: &mut ResourceCache) -> HashMap> { + self.pipeline_epoch_map.clear(); + + // Free any render targets from last frame. + // TODO: This should really re-use existing targets here... + for render_target in self.render_targets.drain(..) { + if let Some(texture_id) = render_target.texture_id { + resource_cache.free_render_target(texture_id); + } + } + + mem::replace(&mut self.layers, HashMap::with_hash_state(Default::default())) + } + + pub fn pending_updates(&mut self) -> BatchUpdateList { + mem::replace(&mut self.pending_updates, BatchUpdateList::new()) + } + + pub fn scroll(&mut self, delta: &Point2D, viewport_size: &Size2D) { + // TODO: Select other layers for scrolling! + let layer = self.layers.get_mut(&ScrollLayerId(0)); + + if let Some(layer) = layer { + layer.scroll_offset = layer.scroll_offset + *delta; + + layer.scroll_offset.x = layer.scroll_offset.x.min(0.0); + layer.scroll_offset.y = layer.scroll_offset.y.min(0.0); + + layer.scroll_offset.x = layer.scroll_offset.x.max(-layer.scroll_boundaries.width + + viewport_size.width); + layer.scroll_offset.y = layer.scroll_offset.y.max(-layer.scroll_boundaries.height + + viewport_size.height); + } else { + println!("unable to find root scroll layer (may be an empty stacking context)"); + } + } + + pub fn create(&mut self, + scene: &Scene, + viewport_size: Size2D, + resource_cache: &mut ResourceCache) { + if let Some(root_pipeline_id) = scene.root_pipeline_id { + if let Some(root_pipeline) = scene.pipeline_map.get(&root_pipeline_id) { + let mut old_layers = self.reset(resource_cache); + + let root_stacking_context = scene.stacking_context_map + .get(&root_pipeline.root_stacking_context_id) + .unwrap(); + + let root_scroll_layer_id = root_stacking_context.stacking_context + .scroll_layer_id + .expect("root layer must be a scroll layer!"); + + debug_assert!(self.render_target_stack.len() == 0); + self.push_render_target(viewport_size, None); + self.flatten(SceneItemKind::Pipeline(root_pipeline), + &Point2D::zero(), + &Matrix4::identity(), + &Matrix4::identity(), + root_scroll_layer_id, + resource_cache, + &root_stacking_context.stacking_context.overflow, + scene, + &old_layers, + &root_stacking_context.stacking_context.overflow); + self.pop_render_target(); + debug_assert!(self.render_target_stack.len() == 0); + + // TODO(gw): This should be moved elsewhere! + if let Some(root_scroll_layer) = self.layers.get_mut(&root_scroll_layer_id) { + root_scroll_layer.scroll_boundaries = root_stacking_context.stacking_context.overflow.size; + } + + for (_, old_layer) in &mut old_layers { + old_layer.reset(&mut self.pending_updates) + } + } + } + } + + pub fn flatten(&mut self, + item_kind: SceneItemKind, + parent_offset: &Point2D, + parent_transform: &Matrix4, + parent_perspective: &Matrix4, + parent_scroll_layer: ScrollLayerId, + resource_cache: &mut ResourceCache, + clip_rect: &Rect, + scene: &Scene, + old_layers: &HashMap>, + scene_rect: &Rect) { + let _pf = util::ProfileScope::new(" flatten"); + + let stacking_context = match item_kind { + SceneItemKind::StackingContext(stacking_context) => { + &stacking_context.stacking_context + } + SceneItemKind::Pipeline(pipeline) => { + self.pipeline_epoch_map.insert(pipeline.pipeline_id, pipeline.epoch); + + &scene.stacking_context_map + .get(&pipeline.root_stacking_context_id) + .unwrap() + .stacking_context + } + }; + + let (this_scroll_layer, parent_scroll_layer) = match stacking_context.scroll_policy { + ScrollPolicy::Scrollable => { + let scroll_layer = stacking_context.scroll_layer_id.unwrap_or(parent_scroll_layer); + (scroll_layer, scroll_layer) + } + ScrollPolicy::Fixed => { + debug_assert!(stacking_context.scroll_layer_id.is_none()); + (ScrollLayerId::fixed_layer(), parent_scroll_layer) + } + }; + + // TODO: Account for scroll offset with transforms! + + // Build world space transform + let origin = &stacking_context.bounds.origin; + let child_offset = *parent_offset + *origin; + let local_transform = Matrix4::identity().translate(origin.x, origin.y, 0.0) + .mul(&stacking_context.transform); + + let mut final_transform = parent_perspective.mul(&parent_transform) + .mul(&local_transform); + + // Build world space perspective transform + let perspective_transform = Matrix4::identity().translate(origin.x, origin.y, 0.0) + .mul(&stacking_context.perspective) + .translate(-origin.x, -origin.y, 0.0); + + let overflow = stacking_context.overflow.intersection(&clip_rect); + + if let Some(overflow) = overflow { + // When establishing a new 3D context, clear Z. This is only needed if there + // are child stacking contexts, otherwise it is a redundant clear. + if stacking_context.establishes_3d_context && + stacking_context.has_stacking_contexts { + self.push_clear(ClearInfo { + clear_color: false, + clear_z: true, + clear_stencil: true, + }); + } + + let mut composition_operations = vec![]; + if stacking_context.needs_composition_operation_for_mix_blend_mode() { + composition_operations.push(CompositionOp::MixBlend(stacking_context.mix_blend_mode)); + } + for filter in stacking_context.filters.iter() { + match *filter { + FilterOp::Blur(radius) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur( + radius, + BlurDirection::Horizontal))); + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur( + radius, + BlurDirection::Vertical))); + } + FilterOp::Brightness(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Brightness( + amount))); + } + FilterOp::Contrast(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Contrast( + amount))); + } + FilterOp::Grayscale(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Grayscale( + amount))); + } + FilterOp::HueRotate(angle) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::HueRotate( + angle))); + } + FilterOp::Invert(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Invert( + amount))); + } + FilterOp::Opacity(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Opacity( + amount))); + } + FilterOp::Saturate(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Saturate( + amount))); + } + FilterOp::Sepia(amount) => { + composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Sepia( + amount))); + } + } + } + + for composition_operation in composition_operations.iter() { + let size = Size2D::new(stacking_context.overflow.size.width as u32, + stacking_context.overflow.size.height as u32); + let origin = final_transform.transform_point(&stacking_context.bounds.origin); + let origin = Point2D::new(origin.x as u32, origin.y as u32); + + let texture_id = resource_cache.allocate_render_target(TextureTarget::Texture2D, + size.width, + size.height, + 1, + ImageFormat::RGBA8); + + self.push_composite(CompositeInfo { + operation: *composition_operation, + color_texture_id: texture_id, + rect: Rect::new(origin, size), + }); + + self.push_render_target(size, Some(texture_id)); + final_transform = Matrix4::identity(); + } + + let frame_items = item_kind.collect_frame_items(scene); + + for item in frame_items { + match item.scene_item.specific { + SpecificSceneItem::DrawList(draw_list_id) => { + self.push_draw_list(draw_list_id, this_scroll_layer); + + let layer = match self.layers.entry(this_scroll_layer) { + Occupied(entry) => { + entry.into_mut() + } + Vacant(entry) => { + let scroll_offset = match old_layers.get(&this_scroll_layer) { + Some(ref old_layer) => old_layer.scroll_offset, + None => Point2D::zero(), + }; + + entry.insert(Layer::new(scene_rect, &scroll_offset)) + } + }; + + let draw_list = resource_cache.get_draw_list_mut(draw_list_id); + + // Store draw context + draw_list.context = Some(DrawListContext { + origin: child_offset, + overflow: overflow, + final_transform: final_transform, + }); + + for (item_index, item) in draw_list.items.iter().enumerate() { + // Node index may already be Some(..). This can occur when a page has iframes + // and a new root stacking context is received. In this case, the node index + // may already be set for draw lists from other iframe(s) that weren't updated + // as part of this new stacking context. + let item_index = DrawListItemIndex(item_index as u32); + let rect = final_transform.transform_rect(&item.rect); + layer.insert(&rect, draw_list_id, item_index); + } + } + SpecificSceneItem::StackingContext(id) => { + let stacking_context = scene.stacking_context_map + .get(&id) + .unwrap(); + + self.flatten(SceneItemKind::StackingContext(stacking_context), + &child_offset, + &final_transform, + &perspective_transform, + parent_scroll_layer, + resource_cache, + clip_rect, + scene, + old_layers, + scene_rect); + } + SpecificSceneItem::Iframe(iframe_info) => { + let pipeline = scene.pipeline_map + .get(&iframe_info.id); + + if let Some(pipeline) = pipeline { + // TODO: Doesn't handle transforms on iframes yet! + let iframe_transform = Matrix4::identity().translate(iframe_info.offset.x, + iframe_info.offset.y, + 0.0); + + let clip_rect = clip_rect.intersection(&iframe_info.clip); + + if let Some(clip_rect) = clip_rect { + self.flatten(SceneItemKind::Pipeline(pipeline), + &child_offset, + &iframe_transform, + &perspective_transform, + parent_scroll_layer, + resource_cache, + &clip_rect, + scene, + old_layers, + scene_rect); + } + } + } + } + } + + for _ in composition_operations.iter() { + self.pop_render_target(); + } + } + } + + pub fn push_render_target(&mut self, + size: Size2D, + texture_id: Option) { + let rt_index = RenderTargetIndex(self.render_targets.len() as u32); + self.render_target_stack.push(rt_index); + + let render_target = FrameRenderTarget::new(size, texture_id); + self.render_targets.push(render_target); + } + + #[inline] + fn push_clear(&mut self, clear_info: ClearInfo) { + self.current_render_target().push_clear(clear_info); + } + + #[inline] + fn push_composite(&mut self, composite_info: CompositeInfo) { + self.current_render_target().push_composite(composite_info); + } + + #[inline] + fn push_draw_list(&mut self, + draw_list_id: DrawListId, + scroll_layer_id: ScrollLayerId) { + self.current_render_target().push_draw_list(draw_list_id, + scroll_layer_id); + } + + fn current_render_target(&mut self) -> &mut FrameRenderTarget { + let RenderTargetIndex(index) = *self.render_target_stack.last().unwrap(); + &mut self.render_targets[index as usize] + } + + pub fn pop_render_target(&mut self) { + self.current_render_target().finalize(); + self.render_target_stack.pop().unwrap(); + } + + pub fn build(&mut self, + viewport: &Rect, + resource_cache: &mut ResourceCache, + thread_pool: &mut scoped_threadpool::Pool, + device_pixel_ratio: f32) -> RendererFrame { + let origin = Point2D::new(viewport.origin.x as f32, viewport.origin.y as f32); + let size = Size2D::new(viewport.size.width as f32, viewport.size.height as f32); + let viewport_rect = Rect::new(origin, size); + + // Traverse layer trees to calculate visible nodes + for (_, layer) in &mut self.layers { + layer.cull(&viewport_rect); + } + + // Build resource list for newly visible nodes + self.update_resource_lists(resource_cache, thread_pool); + + // Update texture cache and build list of raster jobs. + self.update_texture_cache_and_build_raster_jobs(resource_cache); + + // Rasterize needed glyphs on worker threads + self.raster_glyphs(thread_pool, + resource_cache); + + // Compile nodes that have become visible + self.compile_visible_nodes(thread_pool, + resource_cache, + device_pixel_ratio); + + // Update the batch cache from newly compiled nodes + self.update_batch_cache(); + + // Collect the visible batches into a frame + self.collect_and_sort_visible_batches(resource_cache) + } + + pub fn update_resource_lists(&mut self, + resource_cache: &ResourceCache, + thread_pool: &mut scoped_threadpool::Pool) { + let _pf = util::ProfileScope::new(" update_resource_lists"); + + for (_, layer) in &mut self.layers { + let nodes = &mut layer.aabb_tree.nodes; + + thread_pool.scoped(|scope| { + for node in nodes { + if node.is_visible && node.compiled_node.is_none() { + scope.execute(move || { + node.build_resource_list(resource_cache); + }); + } + } + }); + } + } + + pub fn update_texture_cache_and_build_raster_jobs(&mut self, resource_cache: &mut ResourceCache) { + let _pf = util::ProfileScope::new(" update_texture_cache_and_build_raster_jobs"); + + for (_, layer) in &self.layers { + for node in &layer.aabb_tree.nodes { + if node.is_visible { + let resource_list = node.resource_list.as_ref().unwrap(); + resource_cache.add_resource_list(resource_list); + } + } + } + } + + pub fn raster_glyphs(&mut self, + thread_pool: &mut scoped_threadpool::Pool, + resource_cache: &mut ResourceCache) { + let _pf = util::ProfileScope::new(" raster_glyphs"); + resource_cache.raster_pending_glyphs(thread_pool); + } + + pub fn compile_visible_nodes(&mut self, + thread_pool: &mut scoped_threadpool::Pool, + resource_cache: &ResourceCache, + device_pixel_ratio: f32) { + let _pf = util::ProfileScope::new(" compile_visible_nodes"); + + let layers = &mut self.layers; + let render_targets = &self.render_targets; + + thread_pool.scoped(|scope| { + for (_, layer) in layers { + let nodes = &mut layer.aabb_tree.nodes; + for node in nodes { + if node.is_visible && node.compiled_node.is_none() { + scope.execute(move || { + node.compile(resource_cache, + render_targets, + device_pixel_ratio); + }); + } + } + } + }); + } + + pub fn update_batch_cache(&mut self) { + // Allocate and update VAOs + for (_, layer) in &mut self.layers { + for node in &mut layer.aabb_tree.nodes { + if node.is_visible { + let compiled_node = node.compiled_node.as_mut().unwrap(); + if let Some(vertex_buffer) = compiled_node.vertex_buffer.take() { + debug_assert!(compiled_node.vertex_buffer_id.is_none()); + + self.pending_updates.push(BatchUpdate { + id: vertex_buffer.id, + op: BatchUpdateOp::Create(vertex_buffer.vertices, + vertex_buffer.indices), + }); + + compiled_node.vertex_buffer_id = Some(vertex_buffer.id); + } + } + } + } + } + + pub fn collect_and_sort_visible_batches(&mut self, + resource_cache: &ResourceCache) -> RendererFrame { + let mut frame = RendererFrame::new(self.pipeline_epoch_map.clone()); + + for render_target in &self.render_targets { + let mut commands = Vec::new(); + + for item in &render_target.items { + match item { + &FrameRenderItem::Clear(ref info) => { + commands.push(DrawCommand::Clear(info.clone())); + } + &FrameRenderItem::Composite(ref info) => { + commands.push(DrawCommand::Composite(info.clone())); + } + &FrameRenderItem::DrawListBatch(ref batch_info) => { + let layer = &self.layers[&batch_info.scroll_layer_id]; + let first_draw_list_id = *batch_info.draw_lists.first().unwrap(); + debug_assert!(batch_info.draw_lists.len() < MAX_MATRICES_PER_BATCH); + let mut matrix_palette = vec![Matrix4::identity(); batch_info.draw_lists.len()]; + + // Update batch matrices + for (index, draw_list_id) in batch_info.draw_lists.iter().enumerate() { + let draw_list = resource_cache.get_draw_list(*draw_list_id); + + let transform = draw_list.context.as_ref().unwrap().final_transform; + let transform = transform.translate(layer.scroll_offset.x, + layer.scroll_offset.y, + 0.0); + matrix_palette[index] = transform; + } + + let mut batch_info = BatchInfo::new(matrix_palette); + + // Collect relevant draws from each node in the tree. + for node in &layer.aabb_tree.nodes { + if node.is_visible { + debug_assert!(node.compiled_node.is_some()); + let compiled_node = node.compiled_node.as_ref().unwrap(); + + let batch_list = compiled_node.batch_list.iter().find(|batch_list| { + batch_list.first_draw_list_id == first_draw_list_id + }); + + if let Some(batch_list) = batch_list { + let vertex_buffer_id = compiled_node.vertex_buffer_id.unwrap(); + + for batch in &batch_list.batches { + batch_info.draw_calls.push(DrawCall { + vertex_buffer_id: vertex_buffer_id, + color_texture_id: batch.color_texture_id, + mask_texture_id: batch.mask_texture_id, + first_vertex: batch.first_vertex, + index_count: batch.index_count, + }); + } + } + } + } + + // Finally, add the batch + draw calls + commands.push(DrawCommand::Batch(batch_info)); + } + } + } + + let layer = DrawLayer::new(render_target.texture_id, + render_target.size, + commands); + frame.layers.push(layer); + } + + frame + } +} diff --git a/src/freelist.rs b/src/freelist.rs index 68bdff80aa..3cb4156565 100644 --- a/src/freelist.rs +++ b/src/freelist.rs @@ -1,53 +1,53 @@ -#[derive(Debug, Copy, Clone)] +#[derive(Debug, Copy, Clone, PartialEq, Eq, Hash)] pub struct FreeListItemId(u32); impl FreeListItemId { - #[inline] - pub fn new(value: u32) -> FreeListItemId { - FreeListItemId(value) - } + #[inline] + pub fn new(value: u32) -> FreeListItemId { + FreeListItemId(value) + } - #[inline] - pub fn value(&self) -> u32 { - let FreeListItemId(value) = *self; - value - } + #[inline] + pub fn value(&self) -> u32 { + let FreeListItemId(value) = *self; + value + } } pub trait FreeListItem { - fn next_free_id(&self) -> Option; - fn set_next_free_id(&mut self, id: Option); + fn next_free_id(&self) -> Option; + fn set_next_free_id(&mut self, id: Option); } pub struct FreeList { - items: Vec, - first_free_index: Option, + items: Vec, + first_free_index: Option, } impl FreeList { - pub fn new() -> FreeList { - FreeList { - items: Vec::new(), - first_free_index: None, - } - } + pub fn new() -> FreeList { + FreeList { + items: Vec::new(), + first_free_index: None, + } + } - pub fn insert(&mut self, item: T) -> FreeListItemId { - match self.first_free_index { - Some(free_index) => { - let FreeListItemId(index) = free_index; - let free_item = &mut self.items[index as usize]; - self.first_free_index = free_item.next_free_id(); - *free_item = item; - free_index - } - None => { - let item_id = FreeListItemId(self.items.len() as u32); - self.items.push(item); - item_id - } - } - } + pub fn insert(&mut self, item: T) -> FreeListItemId { + match self.first_free_index { + Some(free_index) => { + let FreeListItemId(index) = free_index; + let free_item = &mut self.items[index as usize]; + self.first_free_index = free_item.next_free_id(); + *free_item = item; + free_index + } + None => { + let item_id = FreeListItemId(self.items.len() as u32); + self.items.push(item); + item_id + } + } + } pub fn get(&self, id: FreeListItemId) -> &T { let FreeListItemId(index) = id; @@ -61,10 +61,10 @@ impl FreeList { // TODO(gw): Actually free items from the texture cache!! #[allow(dead_code)] - pub fn free(&mut self, id: FreeListItemId) { - let FreeListItemId(index) = id; - let item = &mut self.items[index as usize]; - item.set_next_free_id(self.first_free_index); - self.first_free_index = Some(id); - } + pub fn free(&mut self, id: FreeListItemId) { + let FreeListItemId(index) = id; + let item = &mut self.items[index as usize]; + item.set_next_free_id(self.first_free_index); + self.first_free_index = Some(id); + } } diff --git a/src/internal_types.rs b/src/internal_types.rs index 8b1abdf87d..588d49e8e6 100644 --- a/src/internal_types.rs +++ b/src/internal_types.rs @@ -1,19 +1,17 @@ use app_units::Au; -use batch::RenderBatch; -use device::{ProgramId, TextureId, TextureIndex}; +use batch::{VertexBuffer, Batch, VertexBufferId}; +use device::{TextureId, TextureIndex}; use euclid::{Matrix4, Point2D, Rect, Size2D}; use fnv::FnvHasher; +use freelist::{FreeListItem, FreeListItemId}; use gleam::gl; -use platform::font::NativeFontHandle; -use render_api::RenderApi; use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::sync::Arc; -use std::sync::mpsc::Sender; use texture_cache::TextureCacheItem; -use types::{DisplayListID, FontKey, ImageKey, Epoch, ColorF, PipelineId}; -use types::{ImageFormat, StackingContext, DisplayListBuilder, DisplayListMode, CompositionOp}; -use types::{ComplexClipRegion}; +use webrender_traits::{FontKey, ImageKey, Epoch, ColorF, PipelineId}; +use webrender_traits::{ImageFormat}; +use webrender_traits::{ComplexClipRegion, MixBlendMode, NativeFontHandle, DisplayItem}; use util; const UV_FLOAT_TO_FIXED: f32 = 65535.0; @@ -33,22 +31,12 @@ static ZERO_RECT_F32: Rect = Rect { }, }; -#[derive(Clone, Copy, Debug)] -pub struct IdNamespace(pub u32); - -#[derive(Clone, Copy, Debug)] -pub struct ResourceId(pub u32); - pub enum FontTemplate { Raw(Arc>), Native(NativeFontHandle), } -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)] -pub struct BatchId(pub usize); - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct DrawListID(pub usize); +pub type DrawListId = FreeListItemId; #[derive(Debug, PartialEq, Eq)] pub enum TextureSampler { @@ -262,17 +250,12 @@ impl TextureUpdateList { } pub enum BatchUpdateOp { - Create(Vec, - Vec, - ProgramId, - TextureId, - TextureId), - UpdateUniforms(Vec), + Create(Vec, Vec), Destroy, } pub struct BatchUpdate { - pub id: BatchId, + pub id: VertexBufferId, pub op: BatchUpdateOp, } @@ -311,8 +294,32 @@ pub struct CompositeInfo { } #[derive(Clone, Debug)] -pub enum DrawCommandInfo { - Batch(BatchId), +pub struct DrawCall { + pub vertex_buffer_id: VertexBufferId, + pub color_texture_id: TextureId, + pub mask_texture_id: TextureId, + pub first_vertex: u16, + pub index_count: u16, +} + +#[derive(Clone, Debug)] +pub struct BatchInfo { + pub matrix_palette: Vec, + pub draw_calls: Vec, +} + +impl BatchInfo { + pub fn new(matrix_palette: Vec) -> BatchInfo { + BatchInfo { + matrix_palette: matrix_palette, + draw_calls: Vec::new(), + } + } +} + +#[derive(Clone, Debug)] +pub enum DrawCommand { + Batch(BatchInfo), Composite(CompositeInfo), Clear(ClearInfo), } @@ -320,13 +327,6 @@ pub enum DrawCommandInfo { #[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)] pub struct RenderTargetIndex(pub u32); -#[derive(Clone, Debug)] -pub struct DrawCommand { - pub render_target: RenderTargetIndex, - pub sort_key: DisplayItemKey, - pub info: DrawCommandInfo, -} - #[derive(Debug)] pub struct DrawLayer { pub texture_id: Option, @@ -346,55 +346,24 @@ impl DrawLayer { } } -pub struct Frame { +pub struct RendererFrame { pub pipeline_epoch_map: HashMap>, pub layers: Vec, } -impl Frame { - pub fn new(pipeline_epoch_map: HashMap>) -> Frame { - Frame { +impl RendererFrame { + pub fn new(pipeline_epoch_map: HashMap>) -> RendererFrame { + RendererFrame { pipeline_epoch_map: pipeline_epoch_map, layers: Vec::new(), } } - - pub fn add_layer(&mut self, layer: DrawLayer) { - self.layers.push(layer); - } -} - -pub enum ApiMsg { - AddRawFont(FontKey, Vec), - AddNativeFont(FontKey, NativeFontHandle), - AddImage(ImageKey, u32, u32, ImageFormat, Vec), - UpdateImage(ImageKey, u32, u32, ImageFormat, Vec), - AddDisplayList(DisplayListID, PipelineId, Epoch, DisplayListBuilder), - CloneApi(Sender), - SetRootStackingContext(StackingContext, ColorF, Epoch, PipelineId), - SetRootPipeline(PipelineId), - Scroll(Point2D), - TranslatePointToLayerSpace(Point2D, Sender>), } pub enum ResultMsg { UpdateTextureCache(TextureUpdateList), UpdateBatches(BatchUpdateList), - NewFrame(Frame), -} - -pub struct DisplayList { - pub mode: DisplayListMode, - - pub pipeline_id: PipelineId, - pub epoch: Epoch, - - pub background_and_borders_id: Option, - pub block_backgrounds_and_borders_id: Option, - pub floats_id: Option, - pub content_id: Option, - pub positioned_content_id: Option, - pub outlines_id: Option, + NewFrame(RendererFrame), } #[derive(Debug, Clone, Copy)] @@ -512,28 +481,47 @@ pub enum BorderEdgeDirection { Vertical, } -#[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq, Hash)] -pub struct DrawListIndex(pub u32); +#[derive(Debug)] +pub struct DrawListContext { + pub origin: Point2D, + pub overflow: Rect, + pub final_transform: Matrix4, +} -#[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq)] -pub struct DrawListItemIndex(pub u32); +#[derive(Debug)] +pub struct DrawList { + pub items: Vec, + + pub context: Option, -#[derive(Clone, Debug, Eq, PartialEq)] -pub struct DisplayItemKey { - pub draw_list_index: DrawListIndex, - pub item_index: DrawListItemIndex, + // TODO(gw): Structure squat to remove this field. + next_free_id: Option, } -impl DisplayItemKey { - pub fn new(draw_list_index: usize, item_index: usize) -> DisplayItemKey { - DisplayItemKey { - draw_list_index: DrawListIndex(draw_list_index as u32), - item_index: DrawListItemIndex(item_index as u32), +impl DrawList { + pub fn new(items: Vec) -> DrawList { + DrawList { + items: items, + context: None, + next_free_id: None, } } } -#[derive(Debug)] +impl FreeListItem for DrawList { + fn next_free_id(&self) -> Option { + self.next_free_id + } + + fn set_next_free_id(&mut self, id: Option) { + self.next_free_id = id; + } +} + +#[derive(Clone, Copy, Debug, Ord, PartialOrd, PartialEq, Eq)] +pub struct DrawListItemIndex(pub u32); + +#[derive(Debug, Copy, Clone)] pub enum Primitive { Triangles, Rectangles, // 4 vertices per rect @@ -541,22 +529,25 @@ pub enum Primitive { Glyphs, // font glyphs (some platforms may specialize shader) } +pub struct BatchList { + pub batches: Vec, + pub first_draw_list_id: DrawListId, +} + pub struct CompiledNode { - pub batches: Vec, - pub commands: Vec, - pub batch_id_list: Vec, - pub matrix_maps: HashMap>, - DefaultState>, + // TODO(gw): These are mutually exclusive - unify into an enum? + pub vertex_buffer: Option, + pub vertex_buffer_id: Option, + + pub batch_list: Vec, } impl CompiledNode { pub fn new() -> CompiledNode { CompiledNode { - batches: Vec::new(), - commands: Vec::new(), - batch_id_list: Vec::new(), - matrix_maps: HashMap::with_hash_state(Default::default()), + batch_list: Vec::new(), + vertex_buffer: None, + vertex_buffer_id: None, } } } @@ -876,3 +867,27 @@ impl<'a> CombinedClipRegion<'a> { } } +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum LowLevelFilterOp { + Blur(Au, BlurDirection), + Brightness(f32), + Contrast(f32), + Grayscale(f32), + HueRotate(f32), + Invert(f32), + Opacity(f32), + Saturate(f32), + Sepia(f32), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum CompositionOp { + MixBlend(MixBlendMode), + Filter(LowLevelFilterOp), +} + +#[derive(Clone, Copy, Debug, PartialEq)] +pub enum BlurDirection { + Horizontal, + Vertical, +} diff --git a/src/layer.rs b/src/layer.rs index f60803452d..4440c9cf44 100644 --- a/src/layer.rs +++ b/src/layer.rs @@ -1,7 +1,7 @@ -use aabbtree::AABBTree; +use aabbtree::{AABBTree, NodeIndex}; use euclid::{Point2D, Rect, Size2D}; use internal_types::{BatchUpdate, BatchUpdateList, BatchUpdateOp}; -use types::NodeIndex; +use internal_types::{DrawListItemIndex, DrawListId}; pub struct Layer { // TODO: Remove pub from here if possible in the future @@ -25,12 +25,11 @@ impl Layer { pub fn reset(&mut self, pending_updates: &mut BatchUpdateList) { for node in &mut self.aabb_tree.nodes { if let Some(ref mut compiled_node) = node.compiled_node { - for batch_id in compiled_node.batch_id_list.drain(..) { - pending_updates.push(BatchUpdate { - id: batch_id, - op: BatchUpdateOp::Destroy, - }); - } + let vertex_buffer_id = compiled_node.vertex_buffer_id.take().unwrap(); + pending_updates.push(BatchUpdate { + id: vertex_buffer_id, + op: BatchUpdateOp::Destroy, + }); } } } @@ -38,9 +37,9 @@ impl Layer { #[inline] pub fn insert(&mut self, rect: &Rect, - draw_list_index: usize, - item_index: usize) -> Option { - self.aabb_tree.insert(rect, draw_list_index, item_index) + draw_list_id: DrawListId, + item_index: DrawListItemIndex) { + self.aabb_tree.insert(rect, draw_list_id, item_index); } pub fn cull(&mut self, viewport_rect: &Rect) { @@ -48,10 +47,6 @@ impl Layer { self.aabb_tree.cull(&adjusted_viewport); } - pub fn take_compiled_data_from(&mut self, other_layer: &mut Layer) { - self.aabb_tree.take_compiled_data_from(&mut other_layer.aabb_tree) - } - #[allow(dead_code)] pub fn print(&self) { self.aabb_tree.print(NodeIndex(0), 0); diff --git a/src/lib.rs b/src/lib.rs index 522f232d66..9ff728a5e2 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,30 +1,25 @@ -#![feature(step_by, convert, zero_one)] -#![feature(plugin)] -#![feature(custom_derive)] -#![plugin(serde_macros)] #![feature(drain)] #![feature(hashmap_hasher)] #![feature(vec_push_all)] +#![feature(step_by, convert, zero_one)] -extern crate app_units; -extern crate euclid; -extern crate fnv; -extern crate freetype; -extern crate gleam; -extern crate libc; -extern crate time; -extern crate serde; -extern crate scoped_threadpool; -extern crate simd; - -#[cfg(target_os="macos")] -extern crate core_graphics; -#[cfg(target_os="macos")] -extern crate core_text; - -pub mod types; -pub mod renderer; -pub mod render_api; +mod aabbtree; +mod batch; +mod batch_builder; +mod clipper; +mod device; +mod frame; +mod freelist; +mod internal_types; +mod layer; +mod node_compiler; +mod optimizer; +mod render_backend; +mod resource_cache; +mod resource_list; +mod scene; +mod texture_cache; +mod util; mod platform { #[cfg(target_os="macos")] @@ -42,28 +37,24 @@ mod platform { } } -mod aabbtree; -mod batch; -mod clipper; -mod device; -mod freelist; -mod internal_types; -mod layer; -mod optimizer; -mod render_backend; -mod resource_cache; -mod resource_list; -mod stats; -mod texture_cache; -mod util; +pub mod renderer; -pub use types::{FontKey, ImageKey, StackingLevel, DisplayListID, StackingContext, DisplayListBuilder}; -pub use types::{ColorF, ImageFormat, GradientStop, PipelineId, GlyphInstance, RenderNotifier}; -pub use types::{BorderSide, BorderRadius, BorderStyle, Epoch, BoxShadowClipMode, ClipRegion}; -pub use types::{ScrollLayerId, ScrollPolicy, MixBlendMode, ComplexClipRegion, FilterOp}; -pub use render_api::RenderApi; -pub use renderer::Renderer; +#[cfg(target_os="macos")] +extern crate core_graphics; +#[cfg(target_os="macos")] +extern crate core_text; +extern crate app_units; +extern crate euclid; +extern crate fnv; +extern crate gleam; +extern crate freetype; +extern crate scoped_threadpool; +extern crate simd; +extern crate time; +extern crate webrender_traits; + +pub use renderer::Renderer; #[doc(hidden)] pub mod bench { @@ -71,4 +62,3 @@ pub mod bench { pub use clipper::{clip_polygon, ClipBuffers, Polygon}; pub use internal_types::WorkVertex; } - diff --git a/src/node_compiler.rs b/src/node_compiler.rs new file mode 100644 index 0000000000..44edf08943 --- /dev/null +++ b/src/node_compiler.rs @@ -0,0 +1,146 @@ +use aabbtree::AABBTreeNode; +use batch::{BatchBuilder, MatrixIndex, VertexBuffer}; +use clipper::{ClipBuffers}; +use frame::{FrameRenderItem, FrameRenderTarget}; +use internal_types::{DrawListItemIndex, CompiledNode, CombinedClipRegion, BatchList}; +use resource_cache::ResourceCache; +use webrender_traits::{ColorF, SpecificDisplayItem}; + +pub trait NodeCompiler { + fn compile(&mut self, + resource_cache: &ResourceCache, + render_targets: &Vec, + device_pixel_ratio: f32); +} + +impl NodeCompiler for AABBTreeNode { + fn compile(&mut self, + resource_cache: &ResourceCache, + render_targets: &Vec, + device_pixel_ratio: f32) { + let color_white = ColorF::new(1.0, 1.0, 1.0, 1.0); + let mut compiled_node = CompiledNode::new(); + let mut vertex_buffer = VertexBuffer::new(); + + let mut clip_buffers = ClipBuffers::new(); + + for render_target in render_targets { + for item in &render_target.items { + match item { + &FrameRenderItem::Clear(..) | + &FrameRenderItem::Composite(..) => {} + &FrameRenderItem::DrawListBatch(ref batch_info) => { + // TODO: Move this to outer loop when combining with >1 draw list! + let mut builder = BatchBuilder::new(&mut vertex_buffer); + + for (index, draw_list_id) in batch_info.draw_lists.iter().enumerate() { + let draw_list_id = *draw_list_id; + let matrix_index = MatrixIndex(index as u8); + + let draw_list_index_buffer = self.draw_lists.iter().find(|draw_list| { + draw_list.draw_list_id == draw_list_id + }); + + if let Some(draw_list_index_buffer) = draw_list_index_buffer { + let draw_list = resource_cache.get_draw_list(draw_list_id); + + for index in &draw_list_index_buffer.indices { + let DrawListItemIndex(index) = *index; + let display_item = &draw_list.items[index as usize]; + + let context = draw_list.context.as_ref().unwrap(); + let clip_rect = display_item.clip.main.intersection(&context.overflow); + let clip_rect = clip_rect.and_then(|clip_rect| { + let split_rect_local_space = self.split_rect.translate(&-context.origin); + clip_rect.intersection(&split_rect_local_space) + }); + + if let Some(ref clip_rect) = clip_rect { + let mut clip = CombinedClipRegion::from_clip_in_rect_and_stack( + clip_rect, + &display_item.clip.complex[..]); + + match display_item.item { + SpecificDisplayItem::Image(ref info) => { + builder.add_image(matrix_index, + &display_item.rect, + &clip, + &info.stretch_size, + info.image_key, + resource_cache, + &mut clip_buffers, + &color_white); + } + SpecificDisplayItem::Text(ref info) => { + builder.add_text(matrix_index, + &display_item.rect, + &clip, + info.font_key, + info.size, + info.blur_radius, + &info.color, + &info.glyphs, + resource_cache, + &mut clip_buffers, + device_pixel_ratio); + } + SpecificDisplayItem::Rectangle(ref info) => { + builder.add_color_rectangle(matrix_index, + &display_item.rect, + &clip, + resource_cache, + &mut clip_buffers, + &info.color); + } + SpecificDisplayItem::Gradient(ref info) => { + clip.clip_in_rect(&display_item.rect); + builder.add_gradient(matrix_index, + &clip, + &info.start_point, + &info.end_point, + &info.stops, + resource_cache, + &mut clip_buffers); + } + SpecificDisplayItem::BoxShadow(ref info) => { + builder.add_box_shadow(matrix_index, + &info.box_bounds, + &clip, + &info.offset, + &info.color, + info.blur_radius, + info.spread_radius, + info.border_radius, + info.clip_mode, + resource_cache, + &mut clip_buffers); + } + SpecificDisplayItem::Border(ref info) => { + builder.add_border(matrix_index, + &display_item.rect, + &clip, + info, + resource_cache, + &mut clip_buffers); + } + } + } + } + } + } + + let batches = builder.finalize(); + + compiled_node.batch_list.push(BatchList { + batches: batches, + first_draw_list_id: *batch_info.draw_lists.first().unwrap(), + }); + } + } + } + } + + compiled_node.vertex_buffer = Some(vertex_buffer); + self.compiled_node = Some(compiled_node); + } +} diff --git a/src/optimizer.rs b/src/optimizer.rs index 636fad7eaf..1f6b5dfd59 100644 --- a/src/optimizer.rs +++ b/src/optimizer.rs @@ -4,13 +4,17 @@ use euclid::{Point2D, Rect, Size2D}; use std::mem; -use types::{DisplayItem, DisplayListBuilder, DrawList, ImageDisplayItem, SpecificDisplayItem}; +use webrender_traits::{DisplayItem, DisplayListBuilder}; +use webrender_traits::{ImageDisplayItem, SpecificDisplayItem, SpecificDisplayListItem}; pub const IMAGE_TILE_THRESHOLD: f32 = 5000.0; -fn optimize_draw_list(draw_list: &mut DrawList) { - let old_draw_list = mem::replace(draw_list, DrawList::new()); - for item in old_draw_list.items.into_iter() { +// TODO(gw): Store in each display list which lists have images, skip this step in common case. +// Or perhaps just do this in DisplayItem::add_image(). +fn optimize_draw_list(draw_list: &mut Vec) { + let old_draw_list = mem::replace(draw_list, Vec::new()); + + for item in old_draw_list.into_iter() { match item.item { SpecificDisplayItem::Image(ref image) => { // Break up large tiled images into smaller ones so that large background images @@ -35,7 +39,6 @@ fn optimize_draw_list(draw_list: &mut DrawList) { }), rect: Rect::new(Point2D::new(x, y), tile_size), clip: item.clip.clone(), - node_index: item.node_index, }); x += tile_size.width; } @@ -52,11 +55,13 @@ fn optimize_draw_list(draw_list: &mut DrawList) { } pub fn optimize_display_list_builder(display_list_builder: &mut DisplayListBuilder) { - optimize_draw_list(&mut display_list_builder.background_and_borders); - optimize_draw_list(&mut display_list_builder.block_backgrounds_and_borders); - optimize_draw_list(&mut display_list_builder.floats); - optimize_draw_list(&mut display_list_builder.content); - optimize_draw_list(&mut display_list_builder.positioned_content); - optimize_draw_list(&mut display_list_builder.outlines); + for item in &mut display_list_builder.items { + match item.specific { + SpecificDisplayListItem::DrawList(ref mut info) => { + optimize_draw_list(&mut info.items); + } + SpecificDisplayListItem::StackingContext(..) | + SpecificDisplayListItem::Iframe(..) => {} + } + } } - diff --git a/src/platform/linux/font.rs b/src/platform/linux/font.rs index 7745b3e482..49c74fa41c 100644 --- a/src/platform/linux/font.rs +++ b/src/platform/linux/font.rs @@ -1,5 +1,5 @@ use app_units::Au; -use types::FontKey; +use webrender_traits::{FontKey, NativeFontHandle}; use freetype::freetype::{FTErrorMethods, FT_PIXEL_MODE_GRAY}; use freetype::freetype::{FT_Done_FreeType, FT_LOAD_RENDER}; @@ -12,10 +12,6 @@ use std::{mem, ptr, slice}; use std::collections::HashMap; //use util; -/// Native fonts are not used on Linux; all fonts are raw. -#[derive(Clone)] -pub struct NativeFontHandle; - struct Face { face: FT_Face, } diff --git a/src/platform/macos/font.rs b/src/platform/macos/font.rs index acc4a4ff4d..274c7ae055 100644 --- a/src/platform/macos/font.rs +++ b/src/platform/macos/font.rs @@ -8,10 +8,9 @@ use core_graphics::geometry::CGPoint; use core_text::font::CTFont; use core_text::font_descriptor::kCTFontDefaultOrientation; use core_text; -use libc::size_t; use std::collections::HashMap; use std::collections::hash_map::Entry; -use types::FontKey; +use webrender_traits::FontKey; pub type NativeFontHandle = CGFont; @@ -70,15 +69,15 @@ impl FontContext { } pub fn get_glyph(&mut self, - font_key: &FontKey, + font_key: FontKey, size: Au, character: u32, device_pixel_ratio: f32) -> Option { - let ct_font = match self.ct_fonts.entry(((*font_key).clone(), size)) { + let ct_font = match self.ct_fonts.entry(((font_key).clone(), size)) { Entry::Occupied(entry) => (*entry.get()).clone(), Entry::Vacant(entry) => { - let cg_font = match self.cg_fonts.get(font_key) { + let cg_font = match self.cg_fonts.get(&font_key) { None => return Some(RasterizedGlyph::blank()), Some(cg_font) => cg_font, }; @@ -103,10 +102,10 @@ impl FontContext { return Some(RasterizedGlyph::blank()) } - let mut cg_context = CGContext::create_bitmap_context(rasterized_width as size_t, - rasterized_height as size_t, + let mut cg_context = CGContext::create_bitmap_context(rasterized_width as usize, + rasterized_height as usize, 8, - rasterized_width as size_t * 4, + rasterized_width as usize * 4, &CGColorSpace::create_device_rgb(), kCGImageAlphaPremultipliedLast); cg_context.set_allows_font_smoothing(true); diff --git a/src/render_api.rs b/src/render_api.rs deleted file mode 100644 index efeeedd372..0000000000 --- a/src/render_api.rs +++ /dev/null @@ -1,112 +0,0 @@ -use euclid::Point2D; -use internal_types::{ApiMsg, IdNamespace, ResourceId}; -use platform::font::NativeFontHandle; -use std::cell::Cell; -use std::sync::mpsc::{self, Sender}; -use types::{PipelineId, ImageKey, ImageFormat, StackingContext}; -use types::{ColorF, DisplayListID, DisplayListBuilder, Epoch, FontKey}; - -pub struct RenderApi { - pub tx: Sender, - pub id_namespace: IdNamespace, - pub next_id: Cell, -} - -impl RenderApi { - pub fn add_raw_font(&self, bytes: Vec) -> FontKey { - let new_id = self.next_unique_id(); - let key = FontKey::new(new_id.0, new_id.1); - let msg = ApiMsg::AddRawFont(key, bytes); - self.tx.send(msg).unwrap(); - key - } - - pub fn add_native_font(&self, native_font_handle: NativeFontHandle) -> FontKey { - let new_id = self.next_unique_id(); - let key = FontKey::new(new_id.0, new_id.1); - let msg = ApiMsg::AddNativeFont(key, native_font_handle); - self.tx.send(msg).unwrap(); - key - } - - pub fn alloc_image(&self) -> ImageKey { - let new_id = self.next_unique_id(); - ImageKey::new(new_id.0, new_id.1) - } - - pub fn add_image(&self, - width: u32, - height: u32, - format: ImageFormat, - bytes: Vec) -> ImageKey { - let new_id = self.next_unique_id(); - let key = ImageKey::new(new_id.0, new_id.1); - let msg = ApiMsg::AddImage(key, width, height, format, bytes); - self.tx.send(msg).unwrap(); - key - } - - // TODO: Support changing dimensions (and format) during image update? - pub fn update_image(&self, - key: ImageKey, - width: u32, - height: u32, - format: ImageFormat, - bytes: Vec) { - let msg = ApiMsg::UpdateImage(key, width, height, format, bytes); - self.tx.send(msg).unwrap(); - } - - pub fn add_display_list(&self, - display_list: DisplayListBuilder, - pipeline_id: PipelineId, - epoch: Epoch) -> DisplayListID { - debug_assert!(display_list.item_count() > 0, "Avoid adding empty lists!"); - let new_id = self.next_unique_id(); - let id = DisplayListID(new_id.0, new_id.1); - let msg = ApiMsg::AddDisplayList(id, pipeline_id, epoch, display_list); - self.tx.send(msg).unwrap(); - id - } - - pub fn set_root_pipeline(&self, pipeline_id: PipelineId) { - let msg = ApiMsg::SetRootPipeline(pipeline_id); - self.tx.send(msg).unwrap(); - } - - pub fn set_root_stacking_context(&self, - stacking_context: StackingContext, - background_color: ColorF, - epoch: Epoch, - pipeline_id: PipelineId) { - let msg = ApiMsg::SetRootStackingContext(stacking_context, background_color, epoch, pipeline_id); - self.tx.send(msg).unwrap(); - } - - pub fn scroll(&self, delta: Point2D) { - let msg = ApiMsg::Scroll(delta); - self.tx.send(msg).unwrap(); - } - - pub fn translate_point_to_layer_space(&self, point: &Point2D) -> Point2D { - let (tx, rx) = mpsc::channel(); - let msg = ApiMsg::TranslatePointToLayerSpace(*point, tx); - self.tx.send(msg).unwrap(); - rx.recv().unwrap() - } - - pub fn clone_api(&self) -> RenderApi { - let (tx, rx) = mpsc::channel(); - let msg = ApiMsg::CloneApi(tx); - self.tx.send(msg).unwrap(); - rx.recv().unwrap() - } - - fn next_unique_id(&self) -> (u32, u32) { - let IdNamespace(namespace) = self.id_namespace; - let ResourceId(id) = self.next_id.get(); - self.next_id.set(ResourceId(id + 1)); - (namespace, id) - } -} - diff --git a/src/render_backend.rs b/src/render_backend.rs index 63dfe7359b..c68a5a0b31 100644 --- a/src/render_backend.rs +++ b/src/render_backend.rs @@ -1,3156 +1,227 @@ -use aabbtree::{AABBTreeNode, AABBTreeNodeInfo}; -use app_units::Au; -use batch::RenderBatch; -use clipper::{self, ClipBuffers}; -use device::{ProgramId, TextureId}; -use euclid::{Rect, Point2D, Size2D, Matrix4}; -use fnv::FnvHasher; -use internal_types::{ApiMsg, Frame, ResultMsg, DrawLayer, Primitive, ClearInfo}; -use internal_types::{BorderRadiusRasterOp, BoxShadowRasterOp, DrawListID, RasterItem}; -use internal_types::{BatchUpdateList, BatchId, BatchUpdate, BatchUpdateOp, CompiledNode}; -use internal_types::{PackedVertex, WorkVertex, DisplayList, DrawCommand, DrawCommandInfo}; -use internal_types::{ClipRectToRegionResult, DrawListIndex, DrawListItemIndex, DisplayItemKey}; -use internal_types::{CompositeInfo, BorderEdgeDirection, RenderTargetIndex, GlyphKey}; -use internal_types::{FontTemplate, Glyph, PolygonPosColorUv, RectPosUv, TextureTarget}; -use internal_types::{ResourceId, IdNamespace, TiledImageKey, BasicRotationAngle}; -use internal_types::{RectUv, CombinedClipRegion}; -use layer::Layer; -use optimizer; -use render_api::RenderApi; -use renderer::BLUR_INFLATION_FACTOR; +use euclid::{Rect, Size2D}; +use frame::Frame; +use internal_types::{FontTemplate, ResultMsg, DrawLayer}; use resource_cache::ResourceCache; -use resource_list::ResourceList; +use scene::Scene; +use scoped_threadpool; use std::cell::Cell; -use std::collections::HashMap; -use std::collections::hash_map::Entry::{Occupied, Vacant}; -use std::collections::hash_state::DefaultState; -use std::cmp::Ordering; -use std::f32; -use std::mem; -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; -use std::sync::atomic::Ordering::SeqCst; use std::sync::Arc; use std::sync::mpsc::{Sender, Receiver}; -use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId}; -use types::{DisplayListID, Epoch, FontKey, ImageKey, BorderDisplayItem, ScrollPolicy}; -use types::{RectangleDisplayItem, ScrollLayerId, ClearDisplayItem}; -use types::{GradientStop, DisplayListMode, ClipRegion}; -use types::{GlyphInstance, DrawList, ImageFormat, BoxShadowClipMode, DisplayItem}; -use types::{PipelineId, RenderNotifier, StackingContext, SpecificDisplayItem, ColorF}; -use types::{RenderTargetID, MixBlendMode, CompositeDisplayItem, BorderSide, BorderStyle}; -use types::{NodeIndex, CompositionOp, FilterOp, LowLevelFilterOp, BlurDirection}; -use types::{ComplexClipRegion, BorderRadius}; +use texture_cache::{TextureCache, TextureCacheItemId}; use util; -use util::MatrixHelpers; -use scoped_threadpool; - -type DisplayListMap = HashMap>; -type DrawListMap = HashMap>; -type FlatDrawListArray = Vec; -type StackingContextMap = HashMap>; - -static ID_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; - -#[inline] -pub fn new_id() -> usize { - ID_COUNTER.fetch_add(1, SeqCst) -} - -pub static MAX_RECT: Rect = Rect { - origin: Point2D { - x: -1000.0, - y: -1000.0, - }, - size: Size2D { - width: 10000.0, - height: 10000.0, - }, -}; - -const BORDER_DASH_SIZE: f32 = 3.0; +use webrender_traits::{ApiMsg, IdNamespace, ResourceId, RenderApi, RenderNotifier, ScrollLayerId}; -impl BatchId { - fn new() -> BatchId { - BatchId(new_id()) - } -} - -#[derive(Debug)] -struct RenderTarget { - size: Size2D, - draw_list_indices: Vec, - texture_id: Option, -} +/* + Add items from parent AABB nodes into both children. + Only end up with items in leaf nodes. + Need to adjust / change / remove split vs actual rect. -impl RenderTarget { - fn new(size: Size2D, texture_id: Option) -> RenderTarget { - RenderTarget { - size: size, - draw_list_indices: Vec::new(), - texture_id: texture_id, - } - } -} + Compile node: + Clip to node boundaries. + No need to worry about overlap! + Only overlap issues are with layers / aabb trees + *Should* be detectable when there is a draw list with different scroll layer + This will work for fixed too... -struct DisplayItemIterator<'a> { - flat_draw_lists: &'a FlatDrawListArray, - current_key: DisplayItemKey, - last_key: DisplayItemKey, -} + */ -impl<'a> DisplayItemIterator<'a> { - fn new(flat_draw_lists: &'a FlatDrawListArray, - src_items: &Vec) -> DisplayItemIterator<'a> { +pub struct RenderBackend { + api_rx: Receiver, + api_tx: Sender, + result_tx: Sender, - match (src_items.first(), src_items.last()) { - (Some(first), Some(last)) => { - let current_key = first.clone(); - let mut last_key = last.clone(); + viewport: Rect, + device_pixel_ratio: f32, + next_namespace_id: IdNamespace, - let DrawListItemIndex(last_item_index) = last_key.item_index; - last_key.item_index = DrawListItemIndex(last_item_index + 1); + thread_pool: scoped_threadpool::Pool, + resource_cache: ResourceCache, - DisplayItemIterator { - current_key: current_key, - last_key: last_key, - flat_draw_lists: flat_draw_lists, - } - } - (None, None) => { - DisplayItemIterator { - current_key: DisplayItemKey::new(0, 0), - last_key: DisplayItemKey::new(0, 0), - flat_draw_lists: flat_draw_lists - } - } - _ => unreachable!(), - } - } + scene: Scene, + frame: Frame, } -impl<'a> Iterator for DisplayItemIterator<'a> { - type Item = DisplayItemKey; - - fn next(&mut self) -> Option { - if self.current_key == self.last_key { - return None; - } - - let key = self.current_key.clone(); - let DrawListItemIndex(item_index) = key.item_index; - let DrawListIndex(list_index) = key.draw_list_index; - - self.current_key.item_index = DrawListItemIndex(item_index + 1); - - if key.draw_list_index != self.last_key.draw_list_index { - let last_item_index = DrawListItemIndex(self.flat_draw_lists[list_index as usize].draw_list.items.len() as u32); - if self.current_key.item_index == last_item_index { - self.current_key.draw_list_index = DrawListIndex(list_index + 1); - self.current_key.item_index = DrawListItemIndex(0); - } - } - - Some(key) - } -} +impl RenderBackend { + pub fn new(api_rx: Receiver, + api_tx: Sender, + result_tx: Sender, + viewport: Rect, + device_pixel_ratio: f32, + white_image_id: TextureCacheItemId, + dummy_mask_image_id: TextureCacheItemId, + texture_cache: TextureCache) -> RenderBackend { + let mut thread_pool = scoped_threadpool::Pool::new(8); -trait GetDisplayItemHelper { - fn get_item(&self, key: &DisplayItemKey) -> &DisplayItem; - fn get_item_and_draw_context(&self, key: &DisplayItemKey) -> (&DisplayItem, &DrawContext); -} + let resource_cache = ResourceCache::new(&mut thread_pool, + texture_cache, + white_image_id, + dummy_mask_image_id, + device_pixel_ratio); -impl GetDisplayItemHelper for FlatDrawListArray { - fn get_item(&self, key: &DisplayItemKey) -> &DisplayItem { - let DrawListIndex(list_index) = key.draw_list_index; - let DrawListItemIndex(item_index) = key.item_index; - &self[list_index as usize].draw_list.items[item_index as usize] - } + let backend = RenderBackend { + thread_pool: thread_pool, + api_rx: api_rx, + api_tx: api_tx, + result_tx: result_tx, + viewport: viewport, + device_pixel_ratio: device_pixel_ratio, + resource_cache: resource_cache, + scene: Scene::new(), + frame: Frame::new(), + next_namespace_id: IdNamespace(1), + }; - fn get_item_and_draw_context(&self, key: &DisplayItemKey) -> (&DisplayItem, &DrawContext) { - let DrawListIndex(list_index) = key.draw_list_index; - let DrawListItemIndex(item_index) = key.item_index; - let list = &self[list_index as usize]; - (&list.draw_list.items[item_index as usize], &list.draw_context) + backend } -} -trait StackingContextHelpers { - fn needs_composition_operation_for_mix_blend_mode(&self) -> bool; -} + pub fn run(&mut self, notifier: Box) { + let mut notifier = notifier; -impl StackingContextHelpers for StackingContext { - fn needs_composition_operation_for_mix_blend_mode(&self) -> bool { - match self.mix_blend_mode { - MixBlendMode::Normal => false, - MixBlendMode::Multiply | - MixBlendMode::Screen | - MixBlendMode::Overlay | - MixBlendMode::Darken | - MixBlendMode::Lighten | - MixBlendMode::ColorDodge | - MixBlendMode::ColorBurn | - MixBlendMode::HardLight | - MixBlendMode::SoftLight | - MixBlendMode::Difference | - MixBlendMode::Exclusion | - MixBlendMode::Hue | - MixBlendMode::Saturation | - MixBlendMode::Color | - MixBlendMode::Luminosity => true, - } - } -} + loop { + let msg = self.api_rx.recv(); -#[derive(Clone)] -struct DrawContext { - render_target_index: RenderTargetIndex, - overflow: Rect, - device_pixel_ratio: f32, - final_transform: Matrix4, - scroll_layer_id: ScrollLayerId, -} + match msg { + Ok(msg) => { + match msg { + ApiMsg::AddRawFont(id, bytes) => { + self.resource_cache + .add_font_template(id, FontTemplate::Raw(Arc::new(bytes))); + } + ApiMsg::AddNativeFont(id, native_font_handle) => { + self.resource_cache + .add_font_template(id, FontTemplate::Native(native_font_handle)); + } + ApiMsg::AddImage(id, width, height, format, bytes) => { + self.resource_cache.add_image_template(id, + width, + height, + format, + bytes); + } + ApiMsg::UpdateImage(id, width, height, format, bytes) => { + self.resource_cache.update_image_template(id, + width, + height, + format, + bytes); + } + ApiMsg::AddDisplayList(id, + pipeline_id, + epoch, + display_list_builder) => { + self.scene.add_display_list(id, + pipeline_id, + epoch, + display_list_builder, + &mut self.resource_cache); + } + ApiMsg::AddStackingContext(id, + pipeline_id, + epoch, + stacking_context) => { + self.scene.add_stacking_context(id, + pipeline_id, + epoch, + stacking_context); + } + ApiMsg::CloneApi(sender) => { + let new_api = RenderApi { + tx: self.api_tx.clone(), + id_namespace: self.next_namespace_id, + next_id: Cell::new(ResourceId(0)), + }; -struct FlatDrawList { - pub id: Option, - pub draw_context: DrawContext, - pub draw_list: DrawList, -} + let IdNamespace(id_namespace) = self.next_namespace_id; + self.next_namespace_id = IdNamespace(id_namespace + 1); -struct StackingContextDrawLists { - background_and_borders: Vec, - block_background_and_borders: Vec, - floats: Vec, - content: Vec, - positioned_content: Vec, - outlines: Vec, -} + sender.send(new_api).unwrap(); + } + ApiMsg::SetRootStackingContext(stacking_context_id, + background_color, + epoch, + pipeline_id) => { + let _pf = util::ProfileScope::new("SetRootStackingContext"); -impl StackingContextDrawLists { - fn new() -> StackingContextDrawLists { - StackingContextDrawLists { - background_and_borders: Vec::new(), - block_background_and_borders: Vec::new(), - floats: Vec::new(), - content: Vec::new(), - positioned_content: Vec::new(), - outlines: Vec::new(), - } - } + self.scene.set_root_stacking_context(pipeline_id, + epoch, + stacking_context_id, + background_color, + &mut self.resource_cache); - #[inline(always)] - fn push_draw_list_id(id: Option, list: &mut Vec) { - if let Some(id) = id { - list.push(id); - } - } -} + self.build_scene(); + self.render(&mut *notifier); + } + ApiMsg::SetRootPipeline(pipeline_id) => { + let _pf = util::ProfileScope::new("SetRootPipeline"); -trait CollectDrawListsForStackingContext { - fn collect_draw_lists(&self, display_lists: &DisplayListMap) -> StackingContextDrawLists; -} + self.scene.set_root_pipeline_id(pipeline_id); -impl CollectDrawListsForStackingContext for StackingContext { - fn collect_draw_lists(&self, display_lists: &DisplayListMap) -> StackingContextDrawLists { - let mut result = StackingContextDrawLists::new(); + self.build_scene(); + self.render(&mut *notifier); + } + ApiMsg::Scroll(delta) => { + let _pf = util::ProfileScope::new("Scroll"); - for display_list_id in &self.display_lists { - let display_list = &display_lists[display_list_id]; - match display_list.mode { - DisplayListMode::Default => { - StackingContextDrawLists::push_draw_list_id(display_list.background_and_borders_id, - &mut result.background_and_borders); - StackingContextDrawLists::push_draw_list_id(display_list.block_backgrounds_and_borders_id, - &mut result.block_background_and_borders); - StackingContextDrawLists::push_draw_list_id(display_list.floats_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.content_id, - &mut result.content); - StackingContextDrawLists::push_draw_list_id(display_list.positioned_content_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.outlines_id, - &mut result.outlines); - } - DisplayListMode::PseudoFloat => { - StackingContextDrawLists::push_draw_list_id(display_list.background_and_borders_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.block_backgrounds_and_borders_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.floats_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.content_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.positioned_content_id, - &mut result.floats); - StackingContextDrawLists::push_draw_list_id(display_list.outlines_id, - &mut result.floats); + let viewport_size = Size2D::new(self.viewport.size.width as f32, + self.viewport.size.height as f32); + self.frame.scroll(&delta, &viewport_size); + self.render(&mut *notifier); + } + ApiMsg::TranslatePointToLayerSpace(point, tx) => { + // TODO(pcwalton): Select other layers for mouse events. + let point = point / self.device_pixel_ratio; + match self.frame.layers.get_mut(&ScrollLayerId(0)) { + None => tx.send(point).unwrap(), + Some(layer) => tx.send(point - layer.scroll_offset).unwrap(), + } + } + } } - DisplayListMode::PseudoPositionedContent => { - StackingContextDrawLists::push_draw_list_id(display_list.background_and_borders_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.block_backgrounds_and_borders_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.floats_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.content_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.positioned_content_id, - &mut result.positioned_content); - StackingContextDrawLists::push_draw_list_id(display_list.outlines_id, - &mut result.positioned_content); + Err(..) => { + break; } } } - - result - } -} - -struct Scene { - // Internal state - layers: HashMap>, - - // Source data - flat_draw_lists: Vec, - - // Outputs - pipeline_epoch_map: HashMap>, - render_targets: Vec, - render_target_stack: Vec, - pending_updates: BatchUpdateList, - - // Constants - quad_program_id: ProgramId, -} - -impl Scene { - fn new(quad_program_id: ProgramId) -> Scene { - Scene { - layers: HashMap::with_hash_state(Default::default()), - - flat_draw_lists: Vec::new(), - - pipeline_epoch_map: HashMap::with_hash_state(Default::default()), - render_targets: Vec::new(), - render_target_stack: Vec::new(), - pending_updates: BatchUpdateList::new(), - - quad_program_id: quad_program_id, - } } - pub fn pending_updates(&mut self) -> BatchUpdateList { - mem::replace(&mut self.pending_updates, BatchUpdateList::new()) + fn build_scene(&mut self) { + // Flatten the stacking context hierarchy + self.frame.create(&self.scene, + Size2D::new(self.viewport.size.width as u32, + self.viewport.size.height as u32), + &mut self.resource_cache); } - fn reset(&mut self, resource_cache: &mut ResourceCache) { - debug_assert!(self.render_target_stack.len() == 0); - self.pipeline_epoch_map.clear(); + fn render(&mut self, notifier: &mut RenderNotifier) { + let mut frame = self.frame.build(&self.viewport, + &mut self.resource_cache, + &mut self.thread_pool, + self.device_pixel_ratio); - // Free any render targets from last frame. - // TODO: This should really re-use existing targets here... - for render_target in &mut self.render_targets { - if let Some(texture_id) = render_target.texture_id { - resource_cache.free_render_target(texture_id); - } + // Bit of a hack - if there was nothing visible, at least + // add one layer to the frame so that the screen gets + // cleared to the default UA background color. Perhaps + // there is a better way to handle this... + if frame.layers.len() == 0 { + frame.layers.push(DrawLayer { + texture_id: None, + size: Size2D::new(self.viewport.size.width as u32, + self.viewport.size.height as u32), + commands: Vec::new(), + }); } - self.render_targets.clear(); - } - - fn push_render_target(&mut self, - size: Size2D, - texture_id: Option) { - let rt_index = RenderTargetIndex(self.render_targets.len() as u32); - self.render_target_stack.push(rt_index); - - let render_target = RenderTarget::new(size, texture_id); - self.render_targets.push(render_target); - } - - fn current_render_target(&self) -> RenderTargetIndex { - *self.render_target_stack.last().unwrap() - } - - fn pop_render_target(&mut self) { - self.render_target_stack.pop().unwrap(); - } - - fn push_draw_list(&mut self, - id: Option, - draw_list: DrawList, - draw_context: &DrawContext) { - let RenderTargetIndex(current_render_target) = *self.render_target_stack.last().unwrap(); - let render_target = &mut self.render_targets[current_render_target as usize]; - - let draw_list_index = DrawListIndex(self.flat_draw_lists.len() as u32); - render_target.draw_list_indices.push(draw_list_index); - - self.flat_draw_lists.push(FlatDrawList { - id: id, - draw_context: draw_context.clone(), - draw_list: draw_list, - }); - } - - fn add_draw_list(&mut self, - draw_list_id: DrawListID, - draw_context: &DrawContext, - draw_list_map: &mut DrawListMap, - iframes: &mut Vec) { - let draw_list = draw_list_map.remove(&draw_list_id).expect(&format!("unable to remove draw list {:?}", draw_list_id)); + let pending_update = self.resource_cache.pending_updates(); + if pending_update.updates.len() > 0 { + self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap(); + } - // TODO: DrawList should set a flag if iframes are added, to avoid this loop in the common case of no iframes. - for item in &draw_list.items { - match item.item { - SpecificDisplayItem::Iframe(ref info) => { - let iframe_offset = draw_context.final_transform.transform_point(&item.rect.origin); - iframes.push(IframeInfo::new(info.iframe, iframe_offset, item.rect)); - } - _ => {} - } + let pending_update = self.frame.pending_updates(); + if pending_update.updates.len() > 0 { + self.result_tx.send(ResultMsg::UpdateBatches(pending_update)).unwrap(); } - self.push_draw_list(Some(draw_list_id), - draw_list, - draw_context); + self.result_tx.send(ResultMsg::NewFrame(frame)).unwrap(); + notifier.new_frame_ready(); } - - fn flatten_stacking_context(&mut self, - stacking_context_kind: StackingContextKind, - parent_transform: &Matrix4, - parent_perspective: &Matrix4, - display_list_map: &DisplayListMap, - draw_list_map: &mut DrawListMap, - parent_scroll_layer: ScrollLayerId, - stacking_contexts: &StackingContextMap, - device_pixel_ratio: f32, - resource_cache: &mut ResourceCache, - clip_rect: &Rect) { - let _pf = util::ProfileScope::new(" flatten_stacking_context"); - let stacking_context = match stacking_context_kind { - StackingContextKind::Normal(stacking_context) => stacking_context, - StackingContextKind::Root(root) => &root.stacking_context, - }; - - let mut iframes = Vec::new(); - - let (this_scroll_layer, parent_scroll_layer) = match stacking_context.scroll_policy { - ScrollPolicy::Scrollable => { - let scroll_layer = stacking_context.scroll_layer_id.unwrap_or(parent_scroll_layer); - (scroll_layer, scroll_layer) - } - ScrollPolicy::Fixed => { - debug_assert!(stacking_context.scroll_layer_id.is_none()); - (ScrollLayerId::fixed_layer(), parent_scroll_layer) - } - }; - - // TODO: Account for scroll offset with transforms! - - // Build world space transform - let origin = &stacking_context.bounds.origin; - let local_transform = Matrix4::identity().translate(origin.x, origin.y, 0.0) - .mul(&stacking_context.transform); - - let mut final_transform = parent_perspective.mul(&parent_transform) - .mul(&local_transform); - - // Build world space perspective transform - let perspective_transform = Matrix4::identity().translate(origin.x, origin.y, 0.0) - .mul(&stacking_context.perspective) - .translate(-origin.x, -origin.y, 0.0); - - let overflow = stacking_context.overflow.intersection(&clip_rect); - - if let Some(overflow) = overflow { - let mut draw_context = DrawContext { - render_target_index: self.current_render_target(), - overflow: overflow, - device_pixel_ratio: device_pixel_ratio, - final_transform: final_transform, - scroll_layer_id: this_scroll_layer, - }; - - // When establishing a new 3D context, clear Z. This is only needed if there - // are child stacking contexts, otherwise it is a redundant clear. - if stacking_context.establishes_3d_context && stacking_context.children.len() > 0 { - let mut clear_draw_list = DrawList::new(); - let clear_item = ClearDisplayItem { - clear_color: false, - clear_z: true, - clear_stencil: true, - }; - let clip = ClipRegion { - main: stacking_context.overflow, - complex: vec![], - }; - let display_item = DisplayItem { - item: SpecificDisplayItem::Clear(clear_item), - rect: stacking_context.overflow, - clip: clip, - node_index: None, - }; - clear_draw_list.push(display_item); - self.push_draw_list(None, clear_draw_list, &draw_context); - } - - let mut composition_operations = vec![]; - if stacking_context.needs_composition_operation_for_mix_blend_mode() { - composition_operations.push(CompositionOp::MixBlend(stacking_context.mix_blend_mode)); - } - for filter in stacking_context.filters.iter() { - match *filter { - FilterOp::Blur(radius) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur( - radius, - BlurDirection::Horizontal))); - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Blur( - radius, - BlurDirection::Vertical))); - } - FilterOp::Brightness(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Brightness( - amount))); - } - FilterOp::Contrast(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Contrast( - amount))); - } - FilterOp::Grayscale(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Grayscale( - amount))); - } - FilterOp::HueRotate(angle) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::HueRotate( - angle))); - } - FilterOp::Invert(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Invert( - amount))); - } - FilterOp::Opacity(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Opacity( - amount))); - } - FilterOp::Saturate(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Saturate( - amount))); - } - FilterOp::Sepia(amount) => { - composition_operations.push(CompositionOp::Filter(LowLevelFilterOp::Sepia( - amount))); - } - } - } - - for composition_operation in composition_operations.iter() { - let size = Size2D::new(stacking_context.overflow.size.width as u32, - stacking_context.overflow.size.height as u32); - let texture_id = resource_cache.allocate_render_target(TextureTarget::Texture2D, - size.width, - size.height, - 1, - ImageFormat::RGBA8); - let TextureId(render_target_id) = texture_id; - - let mut composite_draw_list = DrawList::new(); - let composite_item = CompositeDisplayItem { - operation: *composition_operation, - texture_id: RenderTargetID(render_target_id), - }; - let clip = ClipRegion { - main: stacking_context.overflow, - complex: vec![], - }; - let composite_item = DisplayItem { - item: SpecificDisplayItem::Composite(composite_item), - rect: stacking_context.overflow, - clip: clip, - node_index: None, - }; - composite_draw_list.push(composite_item); - self.push_draw_list(None, composite_draw_list, &draw_context); - - self.push_render_target(size, Some(texture_id)); - final_transform = Matrix4::identity(); - draw_context.final_transform = final_transform; - draw_context.render_target_index = self.current_render_target(); - } - - match stacking_context_kind { - StackingContextKind::Normal(..) => {} - StackingContextKind::Root(root) => { - self.pipeline_epoch_map.insert(root.pipeline_id, root.epoch); - - if root.background_color.a > 0.0 { - let mut root_draw_list = DrawList::new(); - let rectangle_item = RectangleDisplayItem { - color: root.background_color.clone(), - }; - let clip = ClipRegion { - main: stacking_context.overflow, - complex: vec![], - }; - let root_bg_color_item = DisplayItem { - item: SpecificDisplayItem::Rectangle(rectangle_item), - rect: stacking_context.overflow, - clip: clip, - node_index: None, - }; - root_draw_list.push(root_bg_color_item); - - self.push_draw_list(None, root_draw_list, &draw_context); - } - } - } - - let draw_list_ids = stacking_context.collect_draw_lists(display_list_map); - - for id in &draw_list_ids.background_and_borders { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - // TODO: Sort children (or store in two arrays) to avoid having - // to iterate this list twice. - for child in &stacking_context.children { - if child.z_index >= 0 { - continue; - } - self.flatten_stacking_context(StackingContextKind::Normal(child), - &final_transform, - &perspective_transform, - display_list_map, - draw_list_map, - parent_scroll_layer, - stacking_contexts, - device_pixel_ratio, - resource_cache, - clip_rect); - } - - for id in &draw_list_ids.block_background_and_borders { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - for id in &draw_list_ids.floats { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - for id in &draw_list_ids.content { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - for id in &draw_list_ids.positioned_content { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - for child in &stacking_context.children { - if child.z_index < 0 { - continue; - } - self.flatten_stacking_context(StackingContextKind::Normal(child), - &final_transform, - &perspective_transform, - display_list_map, - draw_list_map, - parent_scroll_layer, - stacking_contexts, - device_pixel_ratio, - resource_cache, - clip_rect); - } - - // TODO: This ordering isn't quite right - it should look - // at the z-index in the iframe root stacking context. - for iframe_info in &iframes { - let iframe = stacking_contexts.get(&iframe_info.id); - if let Some(iframe) = iframe { - // TODO: DOesn't handle transforms on iframes yet! - let iframe_transform = Matrix4::identity().translate(iframe_info.offset.x, - iframe_info.offset.y, - 0.0); - - let clip_rect = clip_rect.intersection(&iframe_info.clip_rect); - - if let Some(clip_rect) = clip_rect { - let clip_rect = clip_rect.translate(&-iframe_info.offset); - self.flatten_stacking_context(StackingContextKind::Root(iframe), - &iframe_transform, - &perspective_transform, - display_list_map, - draw_list_map, - parent_scroll_layer, - stacking_contexts, - device_pixel_ratio, - resource_cache, - &clip_rect); - } - } - } - - for id in &draw_list_ids.outlines { - self.add_draw_list(*id, &draw_context, draw_list_map, &mut iframes); - } - - for _ in composition_operations.iter() { - self.pop_render_target(); - } - } - } - - fn build_layers(&mut self, scene_rect: &Rect, can_reuse_old_batches: bool) { - let _pf = util::ProfileScope::new(" build_layers"); - - let mut old_layers = mem::replace(&mut self.layers, - HashMap::with_hash_state(Default::default())); - - // push all visible draw lists into aabb tree - for (draw_list_index, flat_draw_list) in self.flat_draw_lists.iter_mut().enumerate() { - let scroll_offset = match old_layers.get(&flat_draw_list.draw_context - .scroll_layer_id) { - Some(ref old_layer) => old_layer.scroll_offset, - None => Point2D::zero(), - }; - - let layer = match self.layers.entry(flat_draw_list.draw_context.scroll_layer_id) { - Occupied(entry) => { - entry.into_mut() - } - Vacant(entry) => { - entry.insert(Layer::new(scene_rect, &scroll_offset)) - } - }; - - for (item_index, item) in flat_draw_list.draw_list.items.iter_mut().enumerate() { - // Node index may already be Some(..). This can occur when a page has iframes - // and a new root stacking context is received. In this case, the node index - // may already be set for draw lists from other iframe(s) that weren't updated - // as part of this new stacking context. - let rect = flat_draw_list.draw_context.final_transform.transform_rect(&item.rect); - item.node_index = layer.insert(&rect, draw_list_index, item_index); - } - } - - if can_reuse_old_batches { - for (scroll_layer_id, new_layer) in &mut self.layers { - if let Some(ref mut old_layer) = old_layers.get_mut(&scroll_layer_id) { - new_layer.take_compiled_data_from(old_layer) - } - } - } - - for (_, old_layer) in &mut old_layers { - old_layer.reset(&mut self.pending_updates) - } - } - - fn build_frame(&mut self, - viewport: &Rect, - resource_cache: &mut ResourceCache, - thread_pool: &mut scoped_threadpool::Pool) -> Frame { - let origin = Point2D::new(viewport.origin.x as f32, viewport.origin.y as f32); - let size = Size2D::new(viewport.size.width as f32, viewport.size.height as f32); - let viewport_rect = Rect::new(origin, size); - - // Traverse layer trees to calculate visible nodes - for (_, layer) in &mut self.layers { - layer.cull(&viewport_rect); - } - - // Build resource list for newly visible nodes - self.update_resource_lists(thread_pool); - - // Update texture cache and build list of raster jobs. - self.update_texture_cache_and_build_raster_jobs(resource_cache); - - // Rasterize needed glyphs on worker threads - self.raster_glyphs(thread_pool, - resource_cache); - - // Compile nodes that have become visible - self.compile_visible_nodes(thread_pool, - resource_cache); - - // Update the batch cache from newly compiled nodes - self.update_batch_cache(); - - // Collect the visible batches into a frame - self.collect_and_sort_visible_batches() - } - - fn collect_and_sort_visible_batches(&mut self) -> Frame { - let mut frame = Frame::new(self.pipeline_epoch_map.clone()); - - let mut render_layers = Vec::new(); - - for render_target in &self.render_targets { - render_layers.push(DrawLayer::new(render_target.texture_id, - render_target.size, - Vec::new())); - } - - for (_, layer) in &self.layers { - for node in &layer.aabb_tree.nodes { - if node.is_visible { - debug_assert!(node.compiled_node.is_some()); - let compiled_node = node.compiled_node.as_ref().unwrap(); - - // Update batch matrices - for (batch_id, matrix_map) in &compiled_node.matrix_maps { - // TODO: Could cache these matrices rather than generate for every batch. - let mut matrix_palette = vec![Matrix4::identity(); matrix_map.len()]; - - for (draw_list_index, matrix_index) in matrix_map { - let DrawListIndex(draw_list_index) = *draw_list_index; - let transform = self.flat_draw_lists[draw_list_index as usize].draw_context.final_transform; - let transform = transform.translate(layer.scroll_offset.x, - layer.scroll_offset.y, - 0.0); - let matrix_index = *matrix_index as usize; - matrix_palette[matrix_index] = transform; - } - - self.pending_updates.push(BatchUpdate { - id: *batch_id, - op: BatchUpdateOp::UpdateUniforms(matrix_palette), - }); - } - - for command in &compiled_node.commands { - let RenderTargetIndex(render_target) = command.render_target; - render_layers[render_target as usize].commands.push(command.clone()); - } - } - } - } - - for mut render_layer in render_layers { - if render_layer.commands.len() > 0 { - render_layer.commands.sort_by(|a, b| { - let draw_list_order = a.sort_key.draw_list_index.cmp(&b.sort_key.draw_list_index); - match draw_list_order { - Ordering::Equal => { - a.sort_key.item_index.cmp(&b.sort_key.item_index) - } - order => { - order - } - } - }); - - frame.add_layer(render_layer); - } - } - - frame - } - - fn compile_visible_nodes(&mut self, - thread_pool: &mut scoped_threadpool::Pool, - resource_cache: &ResourceCache) { - let _pf = util::ProfileScope::new(" compile_visible_nodes"); - - // TODO(gw): This is a bit messy with layers - work out a cleaner interface - // for detecting node overlaps... - let mut node_info_map = HashMap::with_hash_state(Default::default()); - for (scroll_layer_id, layer) in &self.layers { - node_info_map.insert(*scroll_layer_id, layer.aabb_tree.node_info()); - } - - let flat_draw_list_array = &self.flat_draw_lists; - let layers = &mut self.layers; - let node_info_map = &node_info_map; - let quad_program_id = self.quad_program_id; - - thread_pool.scoped(|scope| { - for (scroll_layer_id, layer) in layers { - let nodes = &mut layer.aabb_tree.nodes; - for node in nodes { - if node.is_visible && node.compiled_node.is_none() { - scope.execute(move || { - node.compile(flat_draw_list_array, - resource_cache, - node_info_map, - quad_program_id, - *scroll_layer_id); - }); - } - } - } - }); - } - - fn update_batch_cache(&mut self) { - // Allocate and update VAOs - for (_, layer) in &mut self.layers { - for node in &mut layer.aabb_tree.nodes { - if node.is_visible { - let compiled_node = node.compiled_node.as_mut().unwrap(); - for batch in compiled_node.batches.drain(..) { - self.pending_updates.push(BatchUpdate { - id: batch.batch_id, - op: BatchUpdateOp::Create(batch.vertices, - batch.indices, - batch.program_id, - batch.color_texture_id, - batch.mask_texture_id), - }); - compiled_node.batch_id_list.push(batch.batch_id); - compiled_node.matrix_maps.insert(batch.batch_id, batch.matrix_map); - } - } - } - } - } - - fn update_texture_cache_and_build_raster_jobs(&mut self, resource_cache: &mut ResourceCache) { - let _pf = util::ProfileScope::new(" update_texture_cache_and_build_raster_jobs"); - - for (_, layer) in &self.layers { - for node in &layer.aabb_tree.nodes { - if node.is_visible { - let resource_list = node.resource_list.as_ref().unwrap(); - resource_cache.add_resource_list(resource_list); - } - } - } - } - - fn raster_glyphs(&mut self, - thread_pool: &mut scoped_threadpool::Pool, - resource_cache: &mut ResourceCache) { - let _pf = util::ProfileScope::new(" raster_glyphs"); - resource_cache.raster_pending_glyphs(thread_pool); - } - - fn update_resource_lists(&mut self, - thread_pool: &mut scoped_threadpool::Pool) { - let _pf = util::ProfileScope::new(" update_resource_lists"); - - let flat_draw_lists = &self.flat_draw_lists; - - for (_, layer) in &mut self.layers { - let nodes = &mut layer.aabb_tree.nodes; - - thread_pool.scoped(|scope| { - for node in nodes { - if node.is_visible && node.compiled_node.is_none() { - scope.execute(move || { - node.build_resource_list(flat_draw_lists); - }); - } - } - }); - } - } - - fn scroll(&mut self, delta: &Point2D, viewport_size: &Size2D) { - // TODO: Select other layers for scrolling! - let layer = self.layers.get_mut(&ScrollLayerId(0)); - - if let Some(layer) = layer { - layer.scroll_offset = layer.scroll_offset + *delta; - - layer.scroll_offset.x = layer.scroll_offset.x.min(0.0); - layer.scroll_offset.y = layer.scroll_offset.y.min(0.0); - - layer.scroll_offset.x = layer.scroll_offset.x.max(-layer.scroll_boundaries.width + - viewport_size.width); - layer.scroll_offset.y = layer.scroll_offset.y.max(-layer.scroll_boundaries.height + - viewport_size.height); - } else { - println!("unable to find root scroll layer (may be an empty stacking context)"); - } - } - - fn draw_lists_are_identical(&self, old_draw_lists: &Vec) -> bool { - if self.flat_draw_lists.len() != old_draw_lists.len() { - return false - } - for (new_flat_draw_list, old_draw_list) in - self.flat_draw_lists.iter().zip(old_draw_lists.iter()) { - if new_flat_draw_list.draw_list.items.len() != old_draw_list.items.len() { - return false - } - for (new_flat_draw_item, old_flat_draw_item) in - new_flat_draw_list.draw_list - .items - .iter() - .zip(old_draw_list.items.iter()) { - if new_flat_draw_item.item != old_flat_draw_item.item || - new_flat_draw_item.rect != old_flat_draw_item.rect || - new_flat_draw_item.clip != old_flat_draw_item.clip { - return false - } - } - } - true - } -} - -struct DrawCommandBuilder { - quad_program_id: ProgramId, - render_target_index: RenderTargetIndex, - current_batch: Option, - draw_commands: Vec, - batches: Vec, -} - -impl DrawCommandBuilder { - fn new(quad_program_id: ProgramId, render_target_index: RenderTargetIndex) - -> DrawCommandBuilder { - DrawCommandBuilder { - render_target_index: render_target_index, - quad_program_id: quad_program_id, - current_batch: None, - draw_commands: Vec::new(), - batches: Vec::new(), - } - } - - fn flush_current_batch(&mut self) { - // When a clear/composite is encountered - always flush any batches that are pending. - // TODO: It may be possible to be smarter about this in the future and avoid - // flushing the batches in some cases. - if let Some(current_batch) = self.current_batch.take() { - self.draw_commands.push(DrawCommand { - render_target: self.render_target_index, - sort_key: current_batch.sort_key.clone(), - info: DrawCommandInfo::Batch(current_batch.batch_id), - }); - self.batches.push(current_batch); - } - } - - fn add_clear(&mut self, - sort_key: &DisplayItemKey, - clear_color: bool, - clear_z: bool, - clear_stencil: bool) { - self.flush_current_batch(); - - let clear_info = ClearInfo { - clear_color: clear_color, - clear_z: clear_z, - clear_stencil: clear_stencil, - }; - let cmd = DrawCommand { - render_target: self.render_target_index, - sort_key: sort_key.clone(), - info: DrawCommandInfo::Clear(clear_info), - }; - self.draw_commands.push(cmd); - } - - fn add_composite_item(&mut self, - operation: CompositionOp, - color_texture_id: TextureId, - rect: Rect, - sort_key: &DisplayItemKey) { - self.flush_current_batch(); - - let composite_info = CompositeInfo { - operation: operation, - rect: rect, - color_texture_id: color_texture_id, - }; - let cmd = DrawCommand { - render_target: self.render_target_index, - sort_key: sort_key.clone(), - info: DrawCommandInfo::Composite(composite_info) - }; - self.draw_commands.push(cmd); - } - - fn add_draw_item(&mut self, - sort_key: &DisplayItemKey, - color_texture_id: TextureId, - mask_texture_id: TextureId, - primitive: Primitive, - vertices: &mut [PackedVertex]) { - let program_id = self.quad_program_id; - let need_new_batch = self.current_batch.is_none() || - !self.current_batch.as_ref().unwrap().can_add_to_batch(color_texture_id, - mask_texture_id, - sort_key, - program_id); - - if need_new_batch { - if let Some(current_batch) = self.current_batch.take() { - self.draw_commands.push(DrawCommand { - render_target: self.render_target_index, - sort_key: current_batch.sort_key.clone(), - info: DrawCommandInfo::Batch(current_batch.batch_id), - }); - self.batches.push(current_batch); - } - self.current_batch = Some(RenderBatch::new(BatchId::new(), - sort_key.clone(), - program_id, - color_texture_id, - mask_texture_id)); - } - - let batch = self.current_batch.as_mut().unwrap(); - batch.add_draw_item(color_texture_id, - mask_texture_id, - primitive, - vertices, - sort_key); - } - - fn finalize(mut self) -> (Vec, Vec) { - if let Some(current_batch) = self.current_batch.take() { - self.draw_commands.push(DrawCommand { - render_target: self.render_target_index, - sort_key: current_batch.sort_key.clone(), - info: DrawCommandInfo::Batch(current_batch.batch_id), - }); - self.batches.push(current_batch); - } - - (self.batches, self.draw_commands) - } -} - -#[derive(Debug)] -struct IframeInfo { - offset: Point2D, - clip_rect: Rect, - id: PipelineId, -} - -impl IframeInfo { - fn new(id: PipelineId, - offset: Point2D, - clip_rect: Rect) -> IframeInfo { - IframeInfo { - offset: offset, - id: id, - clip_rect: clip_rect, - } - } -} - -struct RootStackingContext { - pipeline_id: PipelineId, - epoch: Epoch, - background_color: ColorF, - stacking_context: StackingContext, -} - -enum StackingContextKind<'a> { - Normal(&'a StackingContext), - Root(&'a RootStackingContext) -} - -pub struct RenderBackend { - thread_pool: scoped_threadpool::Pool, - api_rx: Receiver, - api_tx: Sender, - result_tx: Sender, - viewport: Rect, - device_pixel_ratio: f32, - root_pipeline_id: Option, - resource_cache: ResourceCache, - display_list_map: DisplayListMap, - draw_list_map: DrawListMap, - stacking_contexts: StackingContextMap, - next_draw_list_id: DrawListID, - next_namespace_id: IdNamespace, - - scene: Scene, -} - -impl RenderBackend { - pub fn new(api_rx: Receiver, - api_tx: Sender, - result_tx: Sender, - viewport: Rect, - device_pixel_ratio: f32, - quad_program_id: ProgramId, - white_image_id: TextureCacheItemId, - dummy_mask_image_id: TextureCacheItemId, - texture_cache: TextureCache) -> RenderBackend { - let mut thread_pool = scoped_threadpool::Pool::new(8); - - let resource_cache = ResourceCache::new(&mut thread_pool, - texture_cache, - white_image_id, - dummy_mask_image_id, - device_pixel_ratio); - - let backend = RenderBackend { - thread_pool: thread_pool, - api_rx: api_rx, - api_tx: api_tx, - result_tx: result_tx, - viewport: viewport, - device_pixel_ratio: device_pixel_ratio, - root_pipeline_id: None, - resource_cache: resource_cache, - scene: Scene::new(quad_program_id), - display_list_map: HashMap::with_hash_state(Default::default()), - draw_list_map: HashMap::with_hash_state(Default::default()), - stacking_contexts: HashMap::with_hash_state(Default::default()), - - next_namespace_id: IdNamespace(1), - next_draw_list_id: DrawListID(0), - }; - - backend - } - - fn remove_and_save_draw_list(&mut self, - saved_old_draw_lists: &mut HashMap, - draw_list_id: Option) { - if let Some(id) = draw_list_id { - saved_old_draw_lists.insert(id, self.draw_list_map.remove(&id).unwrap()); - } - } - - fn add_draw_list(&mut self, draw_list: DrawList) -> Option { - if draw_list.item_count() > 0 { - let id = self.next_draw_list_id; - - let DrawListID(current_id) = id; - self.next_draw_list_id = DrawListID(current_id+1); - - self.draw_list_map.insert(id, draw_list); - Some(id) - } else { - None - } - } - - pub fn run(&mut self, notifier: Box) { - let mut notifier = notifier; - - loop { - let msg = self.api_rx.recv(); - - match msg { - Ok(msg) => { - match msg { - ApiMsg::AddRawFont(id, bytes) => { - self.resource_cache - .add_font_template(id, FontTemplate::Raw(Arc::new(bytes))); - } - ApiMsg::AddNativeFont(id, native_font_handle) => { - self.resource_cache - .add_font_template(id, FontTemplate::Native(native_font_handle)); - } - ApiMsg::AddImage(id, width, height, format, bytes) => { - self.resource_cache.add_image_template(id, - width, - height, - format, - bytes); - } - ApiMsg::UpdateImage(id, width, height, format, bytes) => { - self.resource_cache.update_image_template(id, - width, - height, - format, - bytes); - } - ApiMsg::AddDisplayList(id, - pipeline_id, - epoch, - mut display_list_builder) => { - optimizer::optimize_display_list_builder(&mut display_list_builder); - - let display_list = DisplayList { - mode: display_list_builder.mode, - pipeline_id: pipeline_id, - epoch: epoch, - background_and_borders_id: self.add_draw_list(display_list_builder.background_and_borders), - block_backgrounds_and_borders_id: self.add_draw_list(display_list_builder.block_backgrounds_and_borders), - floats_id: self.add_draw_list(display_list_builder.floats), - content_id: self.add_draw_list(display_list_builder.content), - positioned_content_id: self.add_draw_list(display_list_builder.positioned_content), - outlines_id: self.add_draw_list(display_list_builder.outlines), - }; - - self.display_list_map.insert(id, display_list); - } - ApiMsg::CloneApi(sender) => { - let new_api = RenderApi { - tx: self.api_tx.clone(), - id_namespace: self.next_namespace_id, - next_id: Cell::new(ResourceId(0)), - }; - - let IdNamespace(id_namespace) = self.next_namespace_id; - self.next_namespace_id = IdNamespace(id_namespace + 1); - - sender.send(new_api).unwrap(); - } - ApiMsg::SetRootStackingContext(stacking_context, background_color, epoch, pipeline_id) => { - let _pf = util::ProfileScope::new("SetRootStackingContext"); - - let old_draw_lists = mem::replace(&mut self.scene.flat_draw_lists, - Vec::new()); - let old_display_list_keys: Vec<_> = self.display_list_map.iter() - .filter(|&(_, ref v)| { - v.pipeline_id == pipeline_id && - v.epoch < epoch - }) - .map(|(k, _)| k.clone()) - .collect(); - - self.stacking_contexts.insert(pipeline_id, RootStackingContext { - pipeline_id: pipeline_id, - epoch: epoch, - background_color: background_color, - stacking_context: stacking_context, - }); - - self.build_scene(old_draw_lists, &old_display_list_keys[..]); - self.render(&mut *notifier); - } - ApiMsg::SetRootPipeline(pipeline_id) => { - let _pf = util::ProfileScope::new("SetRootPipeline"); - - let old_draw_lists = mem::replace(&mut self.scene.flat_draw_lists, - Vec::new()); - self.root_pipeline_id = Some(pipeline_id); - self.build_scene(old_draw_lists, &[]); - - self.render(&mut *notifier); - } - ApiMsg::Scroll(delta) => { - let _pf = util::ProfileScope::new("Scroll"); - - self.scroll(&delta); - self.render(&mut *notifier); - } - ApiMsg::TranslatePointToLayerSpace(point, tx) => { - // TODO(pcwalton): Select other layers for mouse events. - let point = point / self.device_pixel_ratio; - match self.scene.layers.get_mut(&ScrollLayerId(0)) { - None => tx.send(point).unwrap(), - Some(layer) => tx.send(point - layer.scroll_offset).unwrap(), - } - } - } - } - Err(..) => { - break; - } - } - } - } - - fn build_scene(&mut self, - old_draw_lists: Vec, - old_display_list_keys: &[DisplayListID]) { - // Return all current draw lists to the hash - let mut old_draw_lists_or_ids = Vec::new(); - for flat_draw_list in old_draw_lists.into_iter() { - match flat_draw_list.id { - Some(id) => { - old_draw_lists_or_ids.push(DrawListOrId::Id(id)); - self.draw_list_map.insert(id, flat_draw_list.draw_list); - } - None => { - old_draw_lists_or_ids.push(DrawListOrId::DrawList(flat_draw_list.draw_list)); - } - } - } - - // Remove any old draw lists and display lists for this pipeline - let mut saved_old_draw_lists = HashMap::new(); - for key in old_display_list_keys { - let display_list = self.display_list_map.remove(key).unwrap(); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, - display_list.background_and_borders_id); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, - display_list.block_backgrounds_and_borders_id); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.floats_id); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.content_id); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, - display_list.positioned_content_id); - self.remove_and_save_draw_list(&mut saved_old_draw_lists, display_list.outlines_id); - } - - // Recreate the old flat draw lists. - let old_draw_lists: Vec<_> = old_draw_lists_or_ids.into_iter().flat_map(|draw_list_or_id| { - match draw_list_or_id { - DrawListOrId::DrawList(draw_list) => Some(draw_list), - DrawListOrId::Id(id) => saved_old_draw_lists.remove(&id), - } - }).collect(); - - // Flatten the stacking context hierarchy - if let Some(root_pipeline_id) = self.root_pipeline_id { - if let Some(root_sc) = self.stacking_contexts.get(&root_pipeline_id) { - // Clear out any state and return draw lists (if needed) - self.scene.reset(&mut self.resource_cache); - - let size = Size2D::new(self.viewport.size.width as u32, - self.viewport.size.height as u32); - let root_scroll_layer_id = root_sc.stacking_context - .scroll_layer_id - .expect("root layer must be a scroll layer!"); - - self.scene.push_render_target(size, None); - self.scene.flatten_stacking_context(StackingContextKind::Root(root_sc), - &Matrix4::identity(), - &Matrix4::identity(), - &self.display_list_map, - &mut self.draw_list_map, - root_scroll_layer_id, - &self.stacking_contexts, - self.device_pixel_ratio, - &mut self.resource_cache, - &root_sc.stacking_context.overflow); - self.scene.pop_render_target(); - } - } - - let can_reuse_old_batches = old_draw_lists.len() > 0 && - self.scene.draw_lists_are_identical(&old_draw_lists); - - if let Some(root_pipeline_id) = self.root_pipeline_id { - if let Some(root_sc) = self.stacking_contexts.get(&root_pipeline_id) { - // Init the AABB culling tree(s) - self.scene.build_layers(&root_sc.stacking_context.overflow, can_reuse_old_batches); - - // FIXME(pcwalton): This should be done somewhere else, probably when building the - // layer. - let root_scroll_layer_id = root_sc.stacking_context - .scroll_layer_id - .expect("root layer must be a scroll layer!"); - if let Some(root_scroll_layer) = self.scene.layers.get_mut(&root_scroll_layer_id) { - root_scroll_layer.scroll_boundaries = root_sc.stacking_context.overflow.size; - } - } - } - } - - fn render(&mut self, notifier: &mut RenderNotifier) { - let mut frame = self.scene.build_frame(&self.viewport, - &mut self.resource_cache, - &mut self.thread_pool); - - // Bit of a hack - if there was nothing visible, at least - // add one layer to the frame so that the screen gets - // cleared to the default UA background color. Perhaps - // there is a better way to handle this... - if frame.layers.len() == 0 { - frame.layers.push(DrawLayer { - texture_id: None, - size: Size2D::new(self.viewport.size.width as u32, - self.viewport.size.height as u32), - commands: Vec::new(), - }); - } - - let pending_update = self.resource_cache.pending_updates(); - if pending_update.updates.len() > 0 { - self.result_tx.send(ResultMsg::UpdateTextureCache(pending_update)).unwrap(); - } - - let pending_update = self.scene.pending_updates(); - if pending_update.updates.len() > 0 { - self.result_tx.send(ResultMsg::UpdateBatches(pending_update)).unwrap(); - } - - self.result_tx.send(ResultMsg::NewFrame(frame)).unwrap(); - notifier.new_frame_ready(); - } - - fn scroll(&mut self, delta: &Point2D) { - let viewport_size = Size2D::new(self.viewport.size.width as f32, - self.viewport.size.height as f32); - self.scene.scroll(delta, &viewport_size); - } - -} - -impl DrawCommandBuilder { - #[inline] - fn add_textured_rectangle(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - image_info: &TextureCacheItem, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - color: &ColorF) { - self.add_axis_aligned_gradient_with_texture(sort_key, - rect, - clip, - image_info, - resource_cache, - clip_buffers, - &[*color, *color, *color, *color]) - } - - #[inline] - fn add_color_rectangle(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - color: &ColorF) { - self.add_axis_aligned_gradient(sort_key, - rect, - clip, - resource_cache, - clip_buffers, - &[*color, *color, *color, *color]) - } - - fn add_composite(&mut self, - sort_key: &DisplayItemKey, - draw_context: &DrawContext, - rect: &Rect, - texture_id: RenderTargetID, - operation: CompositionOp) { - let RenderTargetID(texture_id) = texture_id; - - let origin = draw_context.final_transform.transform_point(&rect.origin); - let origin = Point2D::new(origin.x as u32, origin.y as u32); - let size = Size2D::new(rect.size.width as u32, rect.size.height as u32); - - self.add_composite_item(operation, - TextureId(texture_id), - Rect::new(origin, size), - sort_key); - } - - fn add_image(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - stretch_size: &Size2D, - image_key: ImageKey, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - color: &ColorF) { - // Should be caught higher up - debug_assert!(stretch_size.width > 0.0 && stretch_size.height > 0.0); - - let image_info = resource_cache.get_image(image_key); - - if rect.size.width == stretch_size.width && rect.size.height == stretch_size.height { - let uv = RectUv { - top_left: Point2D::new(image_info.u0, image_info.v0), - top_right: Point2D::new(image_info.u1, image_info.v0), - bottom_left: Point2D::new(image_info.u0, image_info.v1), - bottom_right: Point2D::new(image_info.u1, image_info.v1), - }; - - self.push_image_rect(color, - image_info, - clip, - &sort_key, - resource_cache, - clip_buffers, - rect, - &uv); - return - } - - let (image_info, tiled_size) = match TiledImageKey::create_if_necessary(image_key, - &rect.size, - stretch_size) { - Some(ref tiled_image_key) => { - (resource_cache.get_tiled_image(tiled_image_key), - Size2D::new(tiled_image_key.tiled_width as f32, - tiled_image_key.tiled_height as f32)) - } - None => (image_info, *stretch_size), - }; - - let uv = RectUv { - top_left: Point2D::new(image_info.u0, image_info.v0), - top_right: Point2D::new(image_info.u1, image_info.v0), - bottom_left: Point2D::new(image_info.u0, image_info.v1), - bottom_right: Point2D::new(image_info.u1, image_info.v1), - }; - - let mut y_offset = 0.0; - while y_offset < rect.size.height { - let mut x_offset = 0.0; - while x_offset < rect.size.width { - let origin = Point2D::new(rect.origin.x + x_offset, rect.origin.y + y_offset); - let tiled_rect = Rect::new(origin, tiled_size.clone()); - - self.push_image_rect(color, - image_info, - clip, - &sort_key, - resource_cache, - clip_buffers, - &tiled_rect, - &uv); - - x_offset = x_offset + tiled_size.width; - } - - y_offset = y_offset + tiled_size.height; - } - } - - fn push_image_rect(&mut self, - color: &ColorF, - image_info: &TextureCacheItem, - clip: &CombinedClipRegion, - sort_key: &DisplayItemKey, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - rect: &Rect, - uv: &RectUv) { - clipper::clip_rect_to_combined_region(RectPosUv { - pos: *rect, - uv: *uv - }, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.rect_pos_uv, - clip); - for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { - let mask = mask_for_clip_region(resource_cache, &clip_region, false); - let colors = [*color, *color, *color, *color]; - let mut vertices = clip_region.make_packed_vertices_for_rect(&colors, - mask, - image_info.texture_index); - - self.add_draw_item(sort_key, - image_info.texture_id, - mask.texture_id, - Primitive::Rectangles, - &mut vertices); - } - } - - fn add_text(&mut self, - sort_key: &DisplayItemKey, - draw_context: &DrawContext, - font_key: FontKey, - size: Au, - blur_radius: Au, - color: &ColorF, - glyphs: &Vec, - resource_cache: &ResourceCache) { - let dummy_mask_image = resource_cache.get_dummy_mask_image(); - //let dummy_mask_image = texture_cache.get(resource_cache.dummy_mask_image_id); - - // Logic below to pick the primary render item depends on len > 0! - assert!(glyphs.len() > 0); - - let device_pixel_ratio = draw_context.device_pixel_ratio; - - let mut glyph_key = GlyphKey::new(font_key, size, blur_radius, glyphs[0].index); - - let blur_offset = blur_radius.to_f32_px() * (BLUR_INFLATION_FACTOR as f32) / 2.0; - - let mut text_batches: HashMap, DefaultState> = - HashMap::with_hash_state(Default::default()); - - for glyph in glyphs { - glyph_key.index = glyph.index; - let image_info = resource_cache.get_glyph(&glyph_key); - - if image_info.width > 0 && image_info.height > 0 { - let x0 = glyph.x + image_info.user_x0 as f32 / device_pixel_ratio - blur_offset; - let y0 = glyph.y - image_info.user_y0 as f32 / device_pixel_ratio - blur_offset; - - let x1 = x0 + image_info.width as f32 / device_pixel_ratio; - let y1 = y0 + image_info.height as f32 / device_pixel_ratio; - - let vertex_buffer = match text_batches.entry(image_info.texture_id) { - Occupied(entry) => { - entry.into_mut() - } - Vacant(entry) => { - entry.insert(Vec::new()) - } - }; - vertex_buffer.push(PackedVertex::from_components( - x0, y0, - color, - image_info.u0, image_info.v0, - dummy_mask_image.u0, dummy_mask_image.v0, - image_info.texture_index, - dummy_mask_image.texture_index)); - vertex_buffer.push(PackedVertex::from_components( - x1, y0, - color, - image_info.u1, image_info.v0, - dummy_mask_image.u1, dummy_mask_image.v0, - image_info.texture_index, - dummy_mask_image.texture_index)); - vertex_buffer.push(PackedVertex::from_components( - x0, y1, - color, - image_info.u0, image_info.v1, - dummy_mask_image.u0, dummy_mask_image.v1, - image_info.texture_index, - dummy_mask_image.texture_index)); - vertex_buffer.push(PackedVertex::from_components( - x1, y1, - color, - image_info.u1, image_info.v1, - dummy_mask_image.v0, dummy_mask_image.v1, - image_info.texture_index, - dummy_mask_image.texture_index)); - } - } - - for (color_texture_id, mut vertex_buffer) in text_batches { - self.add_draw_item(sort_key, - color_texture_id, - dummy_mask_image.texture_id, - Primitive::Glyphs, - &mut vertex_buffer); - } - } - - // Colors are in the order: top left, top right, bottom right, bottom left. - #[inline] - fn add_axis_aligned_gradient(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - colors: &[ColorF; 4]) { - let white_image = resource_cache.get_dummy_color_image(); - self.add_axis_aligned_gradient_with_texture(sort_key, - rect, - clip, - white_image, - resource_cache, - clip_buffers, - colors); - } - - // Colors are in the order: top left, top right, bottom right, bottom left. - fn add_axis_aligned_gradient_with_texture(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - image_info: &TextureCacheItem, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - colors: &[ColorF; 4]) { - if rect.size.width == 0.0 || rect.size.height == 0.0 { - return - } - - let uv = RectUv { - top_left: Point2D::new(image_info.u0, image_info.v0), - top_right: Point2D::new(image_info.u1, image_info.v0), - bottom_left: Point2D::new(image_info.u0, image_info.v1), - bottom_right: Point2D::new(image_info.u1, image_info.v1), - }; - - clipper::clip_rect_to_combined_region( - RectPosUv { - pos: *rect, - uv: uv, - }, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.rect_pos_uv, - clip); - for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { - let mask = mask_for_clip_region(resource_cache, &clip_region, false); - - let mut vertices = clip_region.make_packed_vertices_for_rect(colors, - mask, - image_info.texture_index); - - self.add_draw_item(sort_key, - image_info.texture_id, - mask.texture_id, - Primitive::Rectangles, - &mut vertices); - } - } - - fn add_gradient(&mut self, - sort_key: &DisplayItemKey, - clip: &CombinedClipRegion, - start_point: &Point2D, - end_point: &Point2D, - stops: &[GradientStop], - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - let white_image = resource_cache.get_dummy_color_image(); - - debug_assert!(stops.len() >= 2); - - let dir_x = end_point.x - start_point.x; - let dir_y = end_point.y - start_point.y; - let dir_len = (dir_x * dir_x + dir_y * dir_y).sqrt(); - let dir_xn = dir_x / dir_len; - let dir_yn = dir_y / dir_len; - let perp_xn = -dir_yn; - let perp_yn = dir_xn; - - for i in 0..stops.len()-1 { - let stop0 = &stops[i]; - let stop1 = &stops[i+1]; - - if stop0.offset == stop1.offset { - continue; - } - - let color0 = &stop0.color; - let color1 = &stop1.color; - - let start_x = start_point.x + stop0.offset * (end_point.x - start_point.x); - let start_y = start_point.y + stop0.offset * (end_point.y - start_point.y); - - let end_x = start_point.x + stop1.offset * (end_point.x - start_point.x); - let end_y = start_point.y + stop1.offset * (end_point.y - start_point.y); - - let len_scale = 1000.0; // todo: determine this properly!! - - let x0 = start_x - perp_xn * len_scale; - let y0 = start_y - perp_yn * len_scale; - - let x1 = end_x - perp_xn * len_scale; - let y1 = end_y - perp_yn * len_scale; - - let x2 = end_x + perp_xn * len_scale; - let y2 = end_y + perp_yn * len_scale; - - let x3 = start_x + perp_xn * len_scale; - let y3 = start_y + perp_yn * len_scale; - - let gradient_polygon = PolygonPosColorUv { - vertices: vec![ - WorkVertex::new(x0, y0, color0, 0.0, 0.0), - WorkVertex::new(x1, y1, color1, 0.0, 0.0), - WorkVertex::new(x2, y2, color1, 0.0, 0.0), - WorkVertex::new(x3, y3, color0, 0.0, 0.0), - ], - }; - - { // scope for buffers - clipper::clip_rect_to_combined_region(gradient_polygon, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.polygon_pos_color_uv, - clip); - for clip_result in clip_buffers.polygon_pos_color_uv - .clip_rect_to_region_result_output - .drain(..) { - let mask = mask_for_clip_region(resource_cache, &clip_result, false); - - let mut packed_vertices = Vec::new(); - if clip_result.rect_result.vertices.len() >= 3 { - for vert in clip_result.rect_result.vertices.iter() { - packed_vertices.push(clip_result.make_packed_vertex( - &vert.position(), - &vert.uv(), - &vert.color(), - &mask, - white_image.texture_index)); - } - } - - if packed_vertices.len() > 0 { - self.add_draw_item(sort_key, - white_image.texture_id, - mask.texture_id, - Primitive::TriangleFan, - &mut packed_vertices); - } - } - } - } - } - - fn add_box_shadow(&mut self, - sort_key: &DisplayItemKey, - box_bounds: &Rect, - clip: &CombinedClipRegion, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); - - // Fast path. - if blur_radius == 0.0 && spread_radius == 0.0 && clip_mode == BoxShadowClipMode::None { - self.add_color_rectangle(sort_key, &rect, clip, resource_cache, clip_buffers, color); - return; - } - - // Draw the corners. - self.add_box_shadow_corners(sort_key, - box_bounds, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - clip_mode, - clip, - resource_cache, - clip_buffers); - - // Draw the sides. - self.add_box_shadow_sides(sort_key, - box_bounds, - clip, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - clip_mode, - resource_cache, - clip_buffers); - - match clip_mode { - BoxShadowClipMode::None => { - // Fill the center area. - self.add_color_rectangle(sort_key, - box_bounds, - clip, - resource_cache, - clip_buffers, - color); - } - BoxShadowClipMode::Outset => { - // Fill the center area. - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - let center_rect = Rect::new(metrics.tl_inner, - Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, - metrics.br_inner.y - metrics.tl_inner.y)); - let mut clip = *clip; - clip.clip_out(&ComplexClipRegion::from_rect(&box_bounds)); - self.add_color_rectangle(sort_key, - ¢er_rect, - &clip, - resource_cache, - clip_buffers, - color); - } - BoxShadowClipMode::Inset => { - // Fill in the outsides. - self.fill_outside_area_of_inset_box_shadow(sort_key, - box_bounds, - clip, - box_offset, - color, - blur_radius, - spread_radius, - border_radius, - resource_cache, - clip_buffers); - } - } - } - - fn add_box_shadow_corners(&mut self, - sort_key: &DisplayItemKey, - box_bounds: &Rect, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - clip: &CombinedClipRegion, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - // Draw the corners. - // - // +--+------------------+--+ - // |##| |##| - // +--+------------------+--+ - // | | | | - // | | | | - // | | | | - // +--+------------------+--+ - // |##| |##| - // +--+------------------+--+ - - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, - box_bounds, - border_radius, - clip_mode); - - self.add_box_shadow_corner(sort_key, - &metrics.tl_outer, - &metrics.tl_inner, - &color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Upright); - self.add_box_shadow_corner(sort_key, - &Point2D::new(metrics.tr_inner.x, metrics.tr_outer.y), - &Point2D::new(metrics.tr_outer.x, metrics.tr_inner.y), - &color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise90); - self.add_box_shadow_corner(sort_key, - &metrics.br_inner, - &metrics.br_outer, - &color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise180); - self.add_box_shadow_corner(sort_key, - &Point2D::new(metrics.bl_outer.x, metrics.bl_inner.y), - &Point2D::new(metrics.bl_inner.x, metrics.bl_outer.y), - &color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise270); - } - - fn add_box_shadow_sides(&mut self, - sort_key: &DisplayItemKey, - box_bounds: &Rect, - clip: &CombinedClipRegion, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, - box_bounds, - border_radius, - clip_mode); - - // Draw the sides. - // - // +--+------------------+--+ - // | |##################| | - // +--+------------------+--+ - // |##| |##| - // |##| |##| - // |##| |##| - // +--+------------------+--+ - // | |##################| | - // +--+------------------+--+ - - let horizontal_size = Size2D::new(metrics.br_inner.x - metrics.tl_inner.x, - metrics.edge_size); - let vertical_size = Size2D::new(metrics.edge_size, - metrics.br_inner.y - metrics.tl_inner.y); - let top_rect = Rect::new(metrics.tl_outer + Point2D::new(metrics.edge_size, 0.0), - horizontal_size); - let right_rect = - Rect::new(metrics.tr_outer + Point2D::new(-metrics.edge_size, metrics.edge_size), - vertical_size); - let bottom_rect = - Rect::new(metrics.bl_outer + Point2D::new(metrics.edge_size, -metrics.edge_size), - horizontal_size); - let left_rect = Rect::new(metrics.tl_outer + Point2D::new(0.0, metrics.edge_size), - vertical_size); - - self.add_box_shadow_edge(sort_key, - &top_rect.origin, - &top_rect.bottom_right(), - color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise270); - self.add_box_shadow_edge(sort_key, - &right_rect.origin, - &right_rect.bottom_right(), - color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Upright); - self.add_box_shadow_edge(sort_key, - &bottom_rect.origin, - &bottom_rect.bottom_right(), - color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise90); - self.add_box_shadow_edge(sort_key, - &left_rect.origin, - &left_rect.bottom_right(), - color, - blur_radius, - border_radius, - clip_mode, - &clip, - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise180); - } - - fn fill_outside_area_of_inset_box_shadow(&mut self, - sort_key: &DisplayItemKey, - box_bounds: &Rect, - clip: &CombinedClipRegion, - box_offset: &Point2D, - color: &ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); - let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); - - let clip = self.adjust_clip_for_box_shadow_clip_mode(clip, - box_bounds, - border_radius, - BoxShadowClipMode::Inset); - - // Fill in the outside area of the box. - // - // +------------------------------+ - // A --> |##############################| - // +--+--+------------------+--+--+ - // |##| | | |##| - // |##+--+------------------+--+##| - // |##| | | |##| - // D --> |##| | | |##| <-- B - // |##| | | |##| - // |##+--+------------------+--+##| - // |##| | | |##| - // +--+--+------------------+--+--+ - // C --> |##############################| - // +------------------------------+ - - // A: - self.add_color_rectangle(sort_key, - &Rect::new(box_bounds.origin, - Size2D::new(box_bounds.size.width, - metrics.tl_outer.y - box_bounds.origin.y)), - &clip, - resource_cache, - clip_buffers, - color); - - // B: - self.add_color_rectangle(sort_key, - &Rect::new(metrics.tr_outer, - Size2D::new(box_bounds.max_x() - metrics.tr_outer.x, - metrics.br_outer.y - metrics.tr_outer.y)), - &clip, - resource_cache, - clip_buffers, - color); - - // C: - self.add_color_rectangle(sort_key, - &Rect::new(Point2D::new(box_bounds.origin.x, metrics.bl_outer.y), - Size2D::new(box_bounds.size.width, - box_bounds.max_y() - metrics.br_outer.y)), - &clip, - resource_cache, - clip_buffers, - color); - - // D: - self.add_color_rectangle(sort_key, - &Rect::new(Point2D::new(box_bounds.origin.x, metrics.tl_outer.y), - Size2D::new(metrics.tl_outer.x - box_bounds.origin.x, - metrics.bl_outer.y - metrics.tl_outer.y)), - &clip, - resource_cache, - clip_buffers, - color); - } - - fn adjust_clip_for_box_shadow_clip_mode<'a>(&mut self, - clip: &CombinedClipRegion<'a>, - box_bounds: &Rect, - border_radius: f32, - clip_mode: BoxShadowClipMode) - -> CombinedClipRegion<'a> { - let mut clip = *clip; - match clip_mode { - BoxShadowClipMode::None => {} - BoxShadowClipMode::Inset => { - clip.clip_in(&ComplexClipRegion { - rect: *box_bounds, - radii: BorderRadius::uniform(border_radius), - }); - } - BoxShadowClipMode::Outset => { - // TODO(pcwalton): Support border radii here too, once we have inverted rounded - // rect clip masks. - clip.clip_out(&ComplexClipRegion::from_rect(box_bounds)); - } - } - clip - } - - #[inline] - fn add_border_edge(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - direction: BorderEdgeDirection, - color: &ColorF, - border_style: BorderStyle, - resource_cache: &ResourceCache, - clip_buffers: &mut clipper::ClipBuffers) { - // TODO: Check for zero width/height borders! - if color.a <= 0.0 { - return - } - - match border_style { - BorderStyle::Dashed => { - let (extent, step) = match direction { - BorderEdgeDirection::Horizontal => { - (rect.size.width, rect.size.height * BORDER_DASH_SIZE) - } - BorderEdgeDirection::Vertical => { - (rect.size.height, rect.size.width * BORDER_DASH_SIZE) - } - }; - let mut origin = 0.0; - while origin < extent { - let dash_rect = match direction { - BorderEdgeDirection::Horizontal => { - Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), - Size2D::new(f32::min(step, extent - origin), - rect.size.height)) - } - BorderEdgeDirection::Vertical => { - Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), - Size2D::new(rect.size.width, - f32::min(step, extent - origin))) - } - }; - - self.add_color_rectangle(sort_key, - &dash_rect, - clip, - resource_cache, - clip_buffers, - color); - - origin += step + step; - } - } - BorderStyle::Dotted => { - let (extent, step) = match direction { - BorderEdgeDirection::Horizontal => (rect.size.width, rect.size.height), - BorderEdgeDirection::Vertical => (rect.size.height, rect.size.width), - }; - let mut origin = 0.0; - while origin < extent { - let (dot_rect, mask_radius) = match direction { - BorderEdgeDirection::Horizontal => { - (Rect::new(Point2D::new(rect.origin.x + origin, rect.origin.y), - Size2D::new(f32::min(step, extent - origin), - rect.size.height)), - rect.size.height / 2.0) - } - BorderEdgeDirection::Vertical => { - (Rect::new(Point2D::new(rect.origin.x, rect.origin.y + origin), - Size2D::new(rect.size.width, - f32::min(step, extent - origin))), - rect.size.width / 2.0) - } - }; - - let raster_op = - BorderRadiusRasterOp::create(&Size2D::new(mask_radius, mask_radius), - &Size2D::new(0.0, 0.0), - false, - ImageFormat::RGBA8).expect( - "Didn't find border radius mask for dashed border!"); - let raster_item = RasterItem::BorderRadius(raster_op); - let color_image = resource_cache.get_raster(&raster_item); - - // Top left: - self.add_textured_rectangle(sort_key, - &Rect::new(dot_rect.origin, - Size2D::new(dot_rect.size.width / 2.0, - dot_rect.size.height / 2.0)), - clip, - color_image, - resource_cache, - clip_buffers, - color); - - // Top right: - self.add_textured_rectangle(sort_key, - &Rect::new(dot_rect.top_right(), - Size2D::new(-dot_rect.size.width / 2.0, - dot_rect.size.height / 2.0)), - clip, - color_image, - resource_cache, - clip_buffers, - color); - - // Bottom right: - self.add_textured_rectangle(sort_key, - &Rect::new(dot_rect.bottom_right(), - Size2D::new(-dot_rect.size.width / 2.0, - -dot_rect.size.height / 2.0)), - clip, - color_image, - resource_cache, - clip_buffers, - color); - - // Bottom left: - self.add_textured_rectangle(sort_key, - &Rect::new(dot_rect.bottom_left(), - Size2D::new(dot_rect.size.width / 2.0, - -dot_rect.size.height / 2.0)), - clip, - color_image, - resource_cache, - clip_buffers, - color); - - origin += step + step; - } - } - BorderStyle::Double => { - let (outer_rect, inner_rect) = match direction { - BorderEdgeDirection::Horizontal => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width, rect.size.height / 3.0)), - Rect::new(Point2D::new(rect.origin.x, - rect.origin.y + rect.size.height * 2.0 / 3.0), - Size2D::new(rect.size.width, rect.size.height / 3.0))) - } - BorderEdgeDirection::Vertical => { - (Rect::new(rect.origin, - Size2D::new(rect.size.width / 3.0, rect.size.height)), - Rect::new(Point2D::new(rect.origin.x + rect.size.width * 2.0 / 3.0, - rect.origin.y), - Size2D::new(rect.size.width / 3.0, rect.size.height))) - } - }; - self.add_color_rectangle(sort_key, - &outer_rect, - clip, - resource_cache, - clip_buffers, - color); - self.add_color_rectangle(sort_key, - &inner_rect, - clip, - resource_cache, - clip_buffers, - color); - } - _ => { - self.add_color_rectangle(sort_key, - rect, - clip, - resource_cache, - clip_buffers, - color); - } - } - } - - #[inline] - fn add_border_corner(&mut self, - sort_key: &DisplayItemKey, - clip: &CombinedClipRegion, - vertices_rect: &Rect, - color0: &ColorF, - color1: &ColorF, - outer_radius: &Size2D, - inner_radius: &Size2D, - resource_cache: &ResourceCache, - clip_buffers: &mut clipper::ClipBuffers, - rotation_angle: BasicRotationAngle) { - if color0.a <= 0.0 && color1.a <= 0.0 { - return - } - - // TODO: Check for zero width/height borders! - let white_image = resource_cache.get_dummy_color_image(); - let mask_image = match BorderRadiusRasterOp::create(outer_radius, - inner_radius, - false, - ImageFormat::A8) { - Some(raster_item) => { - let raster_item = RasterItem::BorderRadius(raster_item); - resource_cache.get_raster(&raster_item) - } - None => { - resource_cache.get_dummy_mask_image() - } - }; - - // FIXME(pcwalton): Either use RGBA8 textures instead of alpha masks here, or implement - // a mask combiner. - let mask_uv = RectUv::from_image_and_rotation_angle(mask_image, rotation_angle); - clipper::clip_rect_to_combined_region( - RectPosUv { - pos: *vertices_rect, - uv: mask_uv, - }, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.rect_pos_uv, - clip); - for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { - let v0; - let v1; - let muv0; - let muv1; - match rotation_angle { - BasicRotationAngle::Upright => { - v0 = clip_region.rect_result.pos.origin; - muv0 = clip_region.rect_result.uv.top_left; - v1 = clip_region.rect_result.pos.bottom_right(); - muv1 = clip_region.rect_result.uv.bottom_right; - } - BasicRotationAngle::Clockwise90 => { - v0 = clip_region.rect_result.pos.top_right(); - muv0 = clip_region.rect_result.uv.top_right; - v1 = clip_region.rect_result.pos.bottom_left(); - muv1 = clip_region.rect_result.uv.bottom_left; - } - BasicRotationAngle::Clockwise180 => { - v0 = clip_region.rect_result.pos.bottom_right(); - muv0 = clip_region.rect_result.uv.bottom_right; - v1 = clip_region.rect_result.pos.origin; - muv1 = clip_region.rect_result.uv.top_left; - } - BasicRotationAngle::Clockwise270 => { - v0 = clip_region.rect_result.pos.bottom_left(); - muv0 = clip_region.rect_result.uv.bottom_left; - v1 = clip_region.rect_result.pos.top_right(); - muv1 = clip_region.rect_result.uv.top_right; - } - } - - let mut vertices = [ - PackedVertex::from_components(v0.x, v0.y, - color0, - 0.0, 0.0, - muv0.x, muv0.y, - white_image.texture_index, - mask_image.texture_index), - PackedVertex::from_components(v1.x, v1.y, - color0, - 0.0, 0.0, - muv1.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v0.x, v1.y, - color0, - 0.0, 0.0, - muv0.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v0.x, v0.y, - color1, - 0.0, 0.0, - muv0.x, muv0.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v1.x, v0.y, - color1, - 0.0, 0.0, - muv1.x, muv0.y, - white_image.texture_index, mask_image.texture_index), - PackedVertex::from_components(v1.x, v1.y, - color1, - 0.0, 0.0, - muv1.x, muv1.y, - white_image.texture_index, mask_image.texture_index), - ]; - - self.add_draw_item(sort_key, - white_image.texture_id, - mask_image.texture_id, - Primitive::Triangles, - &mut vertices); - } - } - - fn add_color_image_rectangle(&mut self, - sort_key: &DisplayItemKey, - v0: &Point2D, - v1: &Point2D, - clip: &CombinedClipRegion, - color0: &ColorF, - color1: &ColorF, - color_image: &TextureCacheItem, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - rotation_angle: BasicRotationAngle) { - if color0.a <= 0.0 || color1.a <= 0.0 { - return - } - - let vertices_rect = Rect::new(*v0, Size2D::new(v1.x - v0.x, v1.y - v0.y)); - let color_uv = RectUv::from_image_and_rotation_angle(color_image, rotation_angle); - - clipper::clip_rect_to_combined_region(RectPosUv { - pos: vertices_rect, - uv: color_uv, - }, - &mut clip_buffers.sh_clip_buffers, - &mut clip_buffers.rect_pos_uv, - clip); - for clip_region in clip_buffers.rect_pos_uv.clip_rect_to_region_result_output.drain(..) { - let mask = mask_for_clip_region(resource_cache, - &clip_region, - false); - let colors = [*color0, *color0, *color1, *color1]; - let mut vertices = clip_region.make_packed_vertices_for_rect(&colors, - mask, - color_image.texture_index); - - self.add_draw_item(sort_key, - color_image.texture_id, - mask.texture_id, - Primitive::Rectangles, - &mut vertices); - } - } - - fn add_border(&mut self, - sort_key: &DisplayItemKey, - rect: &Rect, - clip: &CombinedClipRegion, - info: &BorderDisplayItem, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers) { - // TODO: If any border segment is alpha, place all in alpha pass. - // Is it ever worth batching at a per-segment level? - let radius = &info.radius; - let left = &info.left; - let right = &info.right; - let top = &info.top; - let bottom = &info.bottom; - - let tl_outer = Point2D::new(rect.origin.x, rect.origin.y); - let tl_inner = tl_outer + Point2D::new(radius.top_left.width.max(left.width), - radius.top_left.height.max(top.width)); - - let tr_outer = Point2D::new(rect.origin.x + rect.size.width, rect.origin.y); - let tr_inner = tr_outer + Point2D::new(-radius.top_right.width.max(right.width), - radius.top_right.height.max(top.width)); - - let bl_outer = Point2D::new(rect.origin.x, rect.origin.y + rect.size.height); - let bl_inner = bl_outer + Point2D::new(radius.bottom_left.width.max(left.width), - -radius.bottom_left.height.max(bottom.width)); - - let br_outer = Point2D::new(rect.origin.x + rect.size.width, - rect.origin.y + rect.size.height); - let br_inner = br_outer - Point2D::new(radius.bottom_right.width.max(right.width), - radius.bottom_right.height.max(bottom.width)); - - let left_color = left.border_color(1.0, 2.0/3.0, 0.3, 0.7); - let top_color = top.border_color(1.0, 2.0/3.0, 0.3, 0.7); - let right_color = right.border_color(2.0/3.0, 1.0, 0.7, 0.3); - let bottom_color = bottom.border_color(2.0/3.0, 1.0, 0.7, 0.3); - - // Edges - self.add_border_edge(sort_key, - &Rect::new(Point2D::new(tl_outer.x, tl_inner.y), - Size2D::new(left.width, bl_inner.y - tl_inner.y)), - clip, - BorderEdgeDirection::Vertical, - &left_color, - info.left.style, - resource_cache, - clip_buffers); - - self.add_border_edge(sort_key, - &Rect::new(Point2D::new(tl_inner.x, tl_outer.y), - Size2D::new(tr_inner.x - tl_inner.x, - tr_outer.y + top.width - tl_outer.y)), - clip, - BorderEdgeDirection::Horizontal, - &top_color, - info.top.style, - resource_cache, - clip_buffers); - - self.add_border_edge(sort_key, - &Rect::new(Point2D::new(br_outer.x - right.width, tr_inner.y), - Size2D::new(right.width, br_inner.y - tr_inner.y)), - clip, - BorderEdgeDirection::Vertical, - &right_color, - info.right.style, - resource_cache, - clip_buffers); - - self.add_border_edge(sort_key, - &Rect::new(Point2D::new(bl_inner.x, bl_outer.y - bottom.width), - Size2D::new(br_inner.x - bl_inner.x, - br_outer.y - bl_outer.y + bottom.width)), - clip, - BorderEdgeDirection::Horizontal, - &bottom_color, - info.bottom.style, - resource_cache, - clip_buffers); - - // Corners - self.add_border_corner(sort_key, - clip, - &Rect::new(tl_outer, - Size2D::new(tl_inner.x - tl_outer.x, - tl_inner.y - tl_outer.y)), - &left_color, - &top_color, - &radius.top_left, - &info.top_left_inner_radius(), - resource_cache, - clip_buffers, - BasicRotationAngle::Upright); - - self.add_border_corner(sort_key, - clip, - &Rect::new(Point2D::new(tr_inner.x, tr_outer.y), - Size2D::new(tr_outer.x - tr_inner.x, - tr_inner.y - tr_outer.y)), - &right_color, - &top_color, - &radius.top_right, - &info.top_right_inner_radius(), - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise90); - - self.add_border_corner(sort_key, - clip, - &Rect::new(br_inner, - Size2D::new(br_outer.x - br_inner.x, - br_outer.y - br_inner.y)), - &right_color, - &bottom_color, - &radius.bottom_right, - &info.bottom_right_inner_radius(), - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise180); - - self.add_border_corner(sort_key, - clip, - &Rect::new(Point2D::new(bl_outer.x, bl_inner.y), - Size2D::new(bl_inner.x - bl_outer.x, - bl_outer.y - bl_inner.y)), - &left_color, - &bottom_color, - &radius.bottom_left, - &info.bottom_left_inner_radius(), - resource_cache, - clip_buffers, - BasicRotationAngle::Clockwise270); - } - - // FIXME(pcwalton): Assumes rectangles are well-formed with origin in TL - fn add_box_shadow_corner(&mut self, - sort_key: &DisplayItemKey, - top_left: &Point2D, - bottom_right: &Point2D, - color: &ColorF, - blur_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - clip: &CombinedClipRegion, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - rotation_angle: BasicRotationAngle) { - let inverted = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, - BoxShadowClipMode::Inset => true, - }; - - let color_image = match BoxShadowRasterOp::create_corner(blur_radius, - border_radius, - inverted) { - Some(raster_item) => { - let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item) - } - None => resource_cache.get_dummy_color_image(), - }; - - self.add_color_image_rectangle(sort_key, - top_left, - bottom_right, - clip, - color, - color, - &color_image, - resource_cache, - clip_buffers, - rotation_angle) - } - - fn add_box_shadow_edge(&mut self, - sort_key: &DisplayItemKey, - top_left: &Point2D, - bottom_right: &Point2D, - color: &ColorF, - blur_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode, - clip: &CombinedClipRegion, - resource_cache: &ResourceCache, - clip_buffers: &mut ClipBuffers, - rotation_angle: BasicRotationAngle) { - let inverted = match clip_mode { - BoxShadowClipMode::Outset | BoxShadowClipMode::None => false, - BoxShadowClipMode::Inset => true, - }; - - let color_image = match BoxShadowRasterOp::create_edge(blur_radius, - border_radius, - inverted) { - Some(raster_item) => { - let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item) - } - None => resource_cache.get_dummy_color_image(), - }; - - self.add_color_image_rectangle(sort_key, - top_left, - bottom_right, - clip, - color, - color, - &color_image, - resource_cache, - clip_buffers, - rotation_angle) - } -} - -trait BuildRequiredResources { - fn build_resource_list(&mut self, flat_draw_lists: &FlatDrawListArray); -} - -impl BuildRequiredResources for AABBTreeNode { - fn build_resource_list(&mut self, flat_draw_lists: &FlatDrawListArray) { - //let _pf = util::ProfileScope::new(" build_resource_list"); - let mut resource_list = ResourceList::new(); - - for item_key in &self.src_items { - let display_item = flat_draw_lists.get_item(item_key); - - // Handle border radius for complex clipping regions. - for complex_clip_region in display_item.clip.complex.iter() { - resource_list.add_radius_raster_for_border_radii(&complex_clip_region.radii); - } - - match display_item.item { - SpecificDisplayItem::Image(ref info) => { - resource_list.add_image(info.image_key, - &display_item.rect.size, - &info.stretch_size); - } - SpecificDisplayItem::Text(ref info) => { - for glyph in &info.glyphs { - let glyph = Glyph::new(info.size, info.blur_radius, glyph.index); - resource_list.add_glyph(info.font_key, glyph); - } - } - SpecificDisplayItem::Rectangle(..) => {} - SpecificDisplayItem::Iframe(..) => {} - SpecificDisplayItem::Gradient(..) => {} - SpecificDisplayItem::Composite(..) => {} - SpecificDisplayItem::Clear(..) => {} - SpecificDisplayItem::BoxShadow(ref info) => { - resource_list.add_radius_raster_for_border_radii( - &BorderRadius::uniform(info.border_radius)); - resource_list.add_box_shadow_corner(info.blur_radius, - info.border_radius, - false); - resource_list.add_box_shadow_edge(info.blur_radius, info.border_radius, false); - if info.clip_mode == BoxShadowClipMode::Inset { - resource_list.add_box_shadow_corner(info.blur_radius, - info.border_radius, - true); - resource_list.add_box_shadow_edge(info.blur_radius, - info.border_radius, - true); - } - } - SpecificDisplayItem::Border(ref info) => { - resource_list.add_radius_raster(&info.radius.top_left, - &info.top_left_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.top_right, - &info.top_right_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.bottom_left, - &info.bottom_left_inner_radius(), - false, - ImageFormat::A8); - resource_list.add_radius_raster(&info.radius.bottom_right, - &info.bottom_right_inner_radius(), - false, - ImageFormat::A8); - - if info.top.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.top.width / 2.0, - info.top.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - ImageFormat::RGBA8); - } - if info.right.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.right.width / 2.0, - info.right.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - ImageFormat::RGBA8); - } - if info.bottom.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.bottom.width / 2.0, - info.bottom.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - ImageFormat::RGBA8); - } - if info.left.style == BorderStyle::Dotted { - resource_list.add_radius_raster(&Size2D::new(info.left.width / 2.0, - info.left.width / 2.0), - &Size2D::new(0.0, 0.0), - false, - ImageFormat::RGBA8); - } - } - } - } - - self.resource_list = Some(resource_list); - } -} - -trait BorderSideHelpers { - fn border_color(&self, - scale_factor_0: f32, - scale_factor_1: f32, - black_color_0: f32, - black_color_1: f32) -> ColorF; -} - -impl BorderSideHelpers for BorderSide { - fn border_color(&self, - scale_factor_0: f32, - scale_factor_1: f32, - black_color_0: f32, - black_color_1: f32) -> ColorF { - match self.style { - BorderStyle::Inset => { - if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { - self.color.scale_rgb(scale_factor_1) - } else { - ColorF::new(black_color_1, black_color_1, black_color_1, self.color.a) - } - } - BorderStyle::Outset => { - if self.color.r != 0.0 || self.color.g != 0.0 || self.color.b != 0.0 { - self.color.scale_rgb(scale_factor_0) - } else { - ColorF::new(black_color_0, black_color_0, black_color_0, self.color.a) - } - } - _ => self.color, - } - } -} - -fn mask_for_border_radius<'a>(resource_cache: &'a ResourceCache, - border_radius: f32, - inverted: bool) - -> &'a TextureCacheItem { - if border_radius == 0.0 { - return resource_cache.get_dummy_mask_image() - } - - let border_radius = Au::from_f32_px(border_radius); - resource_cache.get_raster(&RasterItem::BorderRadius(BorderRadiusRasterOp { - outer_radius_x: border_radius, - outer_radius_y: border_radius, - inner_radius_x: Au(0), - inner_radius_y: Au(0), - inverted: inverted, - image_format: ImageFormat::A8, - })) -} - -fn mask_for_clip_region<'a,P>(resource_cache: &'a ResourceCache, - clip_region: &ClipRectToRegionResult

, - inverted: bool) - -> &'a TextureCacheItem { - match clip_region.mask_result { - None => { - resource_cache.get_dummy_mask_image() - } - Some(ref mask_result) => { - mask_for_border_radius(resource_cache, - mask_result.border_radius, - inverted) - } - } -} - -trait NodeCompiler { - fn compile(&mut self, - flat_draw_lists: &FlatDrawListArray, - resource_cache: &ResourceCache, - node_info_map: &HashMap, - DefaultState>, - quad_program_id: ProgramId, - node_scroll_layer_id: ScrollLayerId); -} - -impl NodeCompiler for AABBTreeNode { - fn compile(&mut self, - flat_draw_lists: &FlatDrawListArray, - resource_cache: &ResourceCache, - node_info_map: &HashMap, - DefaultState>, - quad_program_id: ProgramId, - node_scroll_layer_id: ScrollLayerId) { - let color_white = ColorF::new(1.0, 1.0, 1.0, 1.0); - let mut compiled_node = CompiledNode::new(); - - let mut draw_cmd_builders = HashMap::new(); - let mut clip_buffers = ClipBuffers::new(); - - let iter = DisplayItemIterator::new(flat_draw_lists, &self.src_items); - for key in iter { - let (display_item, draw_context) = flat_draw_lists.get_item_and_draw_context(&key); - - if let Some(item_node_index) = display_item.node_index { - if item_node_index == self.node_index && - node_scroll_layer_id == draw_context.scroll_layer_id { - let clip_rect = display_item.clip.main.intersection(&draw_context.overflow); - - if let Some(ref clip_rect) = clip_rect { - let mut clip = CombinedClipRegion::from_clip_in_rect_and_stack( - clip_rect, - &display_item.clip.complex[..]); - - let builder = match draw_cmd_builders.entry(draw_context.render_target_index) { - Vacant(entry) => { - entry.insert(DrawCommandBuilder::new( - quad_program_id, - draw_context.render_target_index)) - } - Occupied(entry) => entry.into_mut(), - }; - - match display_item.item { - SpecificDisplayItem::Image(ref info) => { - builder.add_image(&key, - &display_item.rect, - &clip, - &info.stretch_size, - info.image_key, - resource_cache, - &mut clip_buffers, - &color_white); - } - SpecificDisplayItem::Text(ref info) => { - builder.add_text(&key, - draw_context, - info.font_key, - info.size, - info.blur_radius, - &info.color, - &info.glyphs, - resource_cache); - } - SpecificDisplayItem::Rectangle(ref info) => { - builder.add_color_rectangle(&key, - &display_item.rect, - &clip, - resource_cache, - &mut clip_buffers, - &info.color); - } - SpecificDisplayItem::Iframe(..) => {} - SpecificDisplayItem::Gradient(ref info) => { - clip.clip_in_rect(&display_item.rect); - builder.add_gradient(&key, - &clip, - &info.start_point, - &info.end_point, - &info.stops, - resource_cache, - &mut clip_buffers); - } - SpecificDisplayItem::BoxShadow(ref info) => { - builder.add_box_shadow(&key, - &info.box_bounds, - &clip, - &info.offset, - &info.color, - info.blur_radius, - info.spread_radius, - info.border_radius, - info.clip_mode, - resource_cache, - &mut clip_buffers); - } - SpecificDisplayItem::Border(ref info) => { - builder.add_border(&key, - &display_item.rect, - &clip, - info, - resource_cache, - &mut clip_buffers); - } - SpecificDisplayItem::Composite(ref info) => { - builder.add_composite(&key, - draw_context, - &display_item.rect, - info.texture_id, - info.operation); - } - SpecificDisplayItem::Clear(ref info) => { - builder.add_clear(&key, - info.clear_color, - info.clear_z, - info.clear_stencil); - } - } - } - } else { - // TODO: Cache this information!!! - let NodeIndex(node_index_for_item) = item_node_index; - let NodeIndex(node_index_for_node) = self.node_index; - - let info_list_for_item = node_info_map.get(&draw_context.scroll_layer_id).unwrap(); - let info_list_for_node = node_info_map.get(&node_scroll_layer_id).unwrap(); - - let info_for_item = &info_list_for_item[node_index_for_item as usize]; - let info_for_node = &info_list_for_node[node_index_for_node as usize]; - - // This node should be visible, else it shouldn't be getting compiled! - debug_assert!(info_for_node.is_visible); - - if info_for_item.is_visible { - let rect_for_info = &info_for_item.rect; - let rect_for_node = &info_for_node.rect; - - let nodes_overlap = rect_for_node.intersects(rect_for_info); - if nodes_overlap { - if let Some(builder) = draw_cmd_builders.remove(&draw_context.render_target_index) { - let (batches, commands) = builder.finalize(); - compiled_node.batches.extend(batches.into_iter()); - compiled_node.commands.extend(commands.into_iter()); - } - } - } - } - } - } - - for (_, builder) in draw_cmd_builders.into_iter() { - let (batches, commands) = builder.finalize(); - compiled_node.batches.extend(batches.into_iter()); - compiled_node.commands.extend(commands.into_iter()); - } - - self.compiled_node = Some(compiled_node); - } -} - -#[derive(Debug)] -struct BoxShadowMetrics { - edge_size: f32, - tl_outer: Point2D, - tl_inner: Point2D, - tr_outer: Point2D, - tr_inner: Point2D, - bl_outer: Point2D, - bl_inner: Point2D, - br_outer: Point2D, - br_inner: Point2D, -} - -impl BoxShadowMetrics { - fn new(box_bounds: &Rect, border_radius: f32, blur_radius: f32) -> BoxShadowMetrics { - let outside_edge_size = 3.0 * blur_radius; - let inside_edge_size = outside_edge_size.max(border_radius); - let edge_size = outside_edge_size + inside_edge_size; - let inner_rect = box_bounds.inflate(-inside_edge_size, -inside_edge_size); - let outer_rect = box_bounds.inflate(outside_edge_size, outside_edge_size); - - BoxShadowMetrics { - edge_size: edge_size, - tl_outer: outer_rect.origin, - tl_inner: inner_rect.origin, - tr_outer: outer_rect.top_right(), - tr_inner: inner_rect.top_right(), - bl_outer: outer_rect.bottom_left(), - bl_inner: inner_rect.bottom_left(), - br_outer: outer_rect.bottom_right(), - br_inner: inner_rect.bottom_right(), - } - } -} - -fn compute_box_shadow_rect(box_bounds: &Rect, - box_offset: &Point2D, - spread_radius: f32) - -> Rect { - let mut rect = (*box_bounds).clone(); - rect.origin.x += box_offset.x; - rect.origin.y += box_offset.y; - rect.inflate(spread_radius, spread_radius) -} - -enum DrawListOrId { - DrawList(DrawList), - Id(DrawListID), } - diff --git a/src/renderer.rs b/src/renderer.rs index 7e37706a21..ad16c20e3c 100644 --- a/src/renderer.rs +++ b/src/renderer.rs @@ -1,16 +1,15 @@ use app_units::Au; -use batch::RasterBatch; +use batch::{RasterBatch, VertexBufferId}; use device::{Device, ProgramId, TextureId, TextureIndex, UniformLocation, VAOId, VertexUsageHint}; use euclid::{Rect, Matrix4, Point2D, Size2D}; use fnv::FnvHasher; use gleam::gl; -use internal_types::{Frame, ResultMsg, TextureUpdateOp, BatchUpdateOp, BatchUpdateList}; +use internal_types::{RendererFrame, ResultMsg, TextureUpdateOp, BatchUpdateOp, BatchUpdateList}; use internal_types::{TextureUpdateDetails, TextureUpdateList, PackedVertex, RenderTargetMode}; -use internal_types::{BatchId, ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, DrawCommandInfo, BoxShadowPart}; -use internal_types::{PackedVertexForTextureCacheUpdate, TextureTarget, IdNamespace, ResourceId}; -use render_api::RenderApi; +use internal_types::{ORTHO_NEAR_PLANE, ORTHO_FAR_PLANE, BoxShadowPart}; +use internal_types::{PackedVertexForTextureCacheUpdate, TextureTarget}; +use internal_types::{CompositionOp, LowLevelFilterOp, BlurDirection, DrawCommand}; use render_backend::RenderBackend; -use std::cell::Cell; use std::collections::HashMap; use std::collections::hash_state::DefaultState; use std::f32; @@ -19,18 +18,14 @@ use std::path::PathBuf; use std::sync::mpsc::{channel, Receiver}; use std::thread; use texture_cache::{TextureCache, TextureInsertOp}; -use types::{ColorF, Epoch, PipelineId, RenderNotifier, ImageFormat, MixBlendMode}; -use types::{CompositionOp, LowLevelFilterOp, BlurDirection}; +use webrender_traits::{ColorF, Epoch, PipelineId, RenderNotifier}; +use webrender_traits::{ImageFormat, MixBlendMode, RenderApi}; //use util; pub const BLUR_INFLATION_FACTOR: u32 = 3; -struct Batch { - program_id: ProgramId, - color_texture_id: TextureId, - mask_texture_id: TextureId, +struct VertexBuffer { vao_id: VAOId, - index_count: gl::GLint, } struct RenderContext { @@ -47,10 +42,9 @@ pub struct Renderer { device: Device, pending_texture_updates: Vec, pending_batch_updates: Vec, - current_frame: Option, + current_frame: Option, device_pixel_ratio: f32, - batches: HashMap>, - batch_matrices: HashMap, DefaultState>, + vertex_buffers: HashMap>, raster_batches: Vec, quad_program_id: ProgramId, @@ -148,7 +142,6 @@ impl Renderer { result_tx, initial_viewport, device_pixel_ratio, - quad_program_id, white_image_id, dummy_mask_image_id, texture_cache); @@ -159,8 +152,7 @@ impl Renderer { result_rx: result_rx, device: device, current_frame: None, - batches: HashMap::with_hash_state(Default::default()), - batch_matrices: HashMap::with_hash_state(Default::default()), + vertex_buffers: HashMap::with_hash_state(Default::default()), raster_batches: Vec::new(), pending_texture_updates: Vec::new(), pending_batch_updates: Vec::new(), @@ -180,11 +172,7 @@ impl Renderer { u_quad_transform_array: u_quad_transform_array, }; - let api = RenderApi { - tx: api_tx, - id_namespace: IdNamespace(0), // special case - next_id: Cell::new(ResourceId(0)), - }; + let api = RenderApi::new(api_tx); (renderer, api) } @@ -231,37 +219,24 @@ impl Renderer { for update_list in pending_batch_updates.drain(..) { for update in update_list.updates { match update.op { - BatchUpdateOp::Create(vertices, - indices, - program_id, - color_texture_id, - mask_texture_id) => { + BatchUpdateOp::Create(vertices, indices) => { let vao_id = self.device.create_vao(); self.device.bind_vao(vao_id); - self.device.update_vao_indices(vao_id, &indices, VertexUsageHint::Static); + self.device.update_vao_indices(vao_id, + &indices, + VertexUsageHint::Static); self.device.update_vao_vertices(vao_id, &vertices, VertexUsageHint::Static); - self.batches.insert(update.id, Batch { + self.vertex_buffers.insert(update.id, VertexBuffer { vao_id: vao_id, - program_id: program_id, - color_texture_id: color_texture_id, - mask_texture_id: mask_texture_id, - index_count: indices.len() as gl::GLint, }); } - BatchUpdateOp::UpdateUniforms(matrices) => { - self.batch_matrices.insert(update.id, matrices); - } BatchUpdateOp::Destroy => { - let batch = self.batches.remove(&update.id).unwrap(); - self.device.delete_vao(batch.vao_id); - /*for (_, batch) in self.batches.iter() { - self.device.delete_vao(batch.vao_id); - } - self.batches.clear();*/ + let vertex_buffer = self.vertex_buffers.remove(&update.id).unwrap(); + self.device.delete_vao(vertex_buffer.vao_id); } } } @@ -844,7 +819,7 @@ impl Renderer { self.device.update_vao_indices(vao_id, &batch.indices[..], VertexUsageHint::Dynamic); self.device.update_vao_vertices(vao_id, &batch.vertices[..], VertexUsageHint::Dynamic); - self.device.draw_triangles_u16(batch.indices.len() as gl::GLint); + self.device.draw_triangles_u16(0, batch.indices.len() as gl::GLint); self.device.delete_vao(vao_id); } @@ -894,8 +869,8 @@ impl Renderer { gl::STENCIL_BUFFER_BIT); for cmd in &layer.commands { - match cmd.info { - DrawCommandInfo::Clear(ref info) => { + match cmd { + &DrawCommand::Clear(ref info) => { let mut clear_bits = 0; if info.clear_color { clear_bits |= gl::COLOR_BUFFER_BIT; @@ -908,30 +883,32 @@ impl Renderer { } gl::clear(clear_bits); } - DrawCommandInfo::Batch(batch_id) => { + &DrawCommand::Batch(ref info) => { // TODO: probably worth sorting front to back to minimize overdraw (if profiling shows fragment / rop bound) gl::enable(gl::BLEND); gl::blend_func(gl::SRC_ALPHA, gl::ONE_MINUS_SRC_ALPHA); gl::blend_equation(gl::FUNC_ADD); - let batch = &self.batches[&batch_id]; - let matrices = &self.batch_matrices[&batch_id]; + self.device.bind_program(self.quad_program_id, + &render_context.projection); - // TODO: hack - bind the uniform locations? this goes away if only one shader anyway... - let u_transform_array = if batch.program_id == self.quad_program_id { - self.u_quad_transform_array - } else { - panic!("unexpected batch shader!"); - }; + self.device.set_uniform_mat4_array(self.u_quad_transform_array, + &info.matrix_palette); + + for draw_call in &info.draw_calls { + let vao_id = self.vertex_buffers[&draw_call.vertex_buffer_id].vao_id; + self.device.bind_vao(vao_id); + + self.device.bind_mask_texture_for_noncomposite_operation(draw_call.mask_texture_id); + self.device.bind_color_texture_for_noncomposite_operation(draw_call.color_texture_id); - Renderer::draw_batch(&mut self.device, - batch, - matrices, - &mut render_context, - u_transform_array); + self.device.draw_triangles_u16(draw_call.first_vertex as i32, + draw_call.index_count as i32); + render_context.draw_calls += 1; + } } - DrawCommandInfo::Composite(ref info) => { + &DrawCommand::Composite(ref info) => { let needs_fb = match info.operation { CompositionOp::MixBlend(MixBlendMode::Normal) => unreachable!(), @@ -1127,7 +1104,7 @@ impl Renderer { self.device.update_vao_indices(vao_id, &indices, VertexUsageHint::Dynamic); self.device.update_vao_vertices(vao_id, &vertices, VertexUsageHint::Dynamic); - self.device.draw_triangles_u16(indices.len() as gl::GLint); + self.device.draw_triangles_u16(0, indices.len() as gl::GLint); render_context.draw_calls += 1; self.device.delete_vao(vao_id); @@ -1139,21 +1116,4 @@ impl Renderer { //println!("draw_calls {}", render_context.draw_calls); } } - - fn draw_batch(device: &mut Device, - batch: &Batch, - matrices: &Vec, - context: &mut RenderContext, - u_transform_array: UniformLocation) { - device.bind_program(batch.program_id, &context.projection); - device.set_uniform_mat4_array(u_transform_array, matrices); // The uniform loc here isn't always right! - - device.bind_mask_texture_for_noncomposite_operation(batch.mask_texture_id); - device.bind_color_texture_for_noncomposite_operation(batch.color_texture_id); - - device.bind_vao(batch.vao_id); - - device.draw_triangles_u16(batch.index_count); - context.draw_calls += 1; - } } diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 52ad796223..8a1940c63d 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -2,8 +2,9 @@ use app_units::Au; use device::{TextureId}; use euclid::Size2D; use fnv::FnvHasher; +use freelist::FreeList; use internal_types::{FontTemplate, GlyphKey, RasterItem, TiledImageKey}; -use internal_types::{TextureTarget, TextureUpdateList}; +use internal_types::{TextureTarget, TextureUpdateList, DrawListId, DrawList}; use platform::font::{FontContext, RasterizedGlyph}; use renderer::BLUR_INFLATION_FACTOR; use resource_list::ResourceList; @@ -16,7 +17,7 @@ use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; use std::sync::atomic::Ordering::SeqCst; use std::thread; use texture_cache::{TextureCache, TextureCacheItem, TextureCacheItemId, TextureInsertOp}; -use types::{Epoch, FontKey, ImageKey, ImageFormat}; +use webrender_traits::{Epoch, FontKey, ImageKey, ImageFormat, DisplayItem}; static FONT_CONTEXT_COUNT: AtomicUsize = ATOMIC_USIZE_INIT; @@ -47,6 +48,7 @@ pub struct ResourceCache { cached_images: HashMap>, cached_tiled_images: HashMap>, + draw_lists: FreeList, font_templates: HashMap>, image_templates: HashMap>, device_pixel_ratio: f32, @@ -85,6 +87,7 @@ impl ResourceCache { cached_rasters: HashMap::with_hash_state(Default::default()), cached_images: HashMap::with_hash_state(Default::default()), cached_tiled_images: HashMap::with_hash_state(Default::default()), + draw_lists: FreeList::new(), font_templates: HashMap::with_hash_state(Default::default()), image_templates: HashMap::with_hash_state(Default::default()), texture_cache: texture_cache, @@ -272,6 +275,22 @@ impl ResourceCache { } } + pub fn add_draw_list(&mut self, items: Vec) -> DrawListId { + self.draw_lists.insert(DrawList::new(items)) + } + + pub fn get_draw_list(&self, draw_list_id: DrawListId) -> &DrawList { + self.draw_lists.get(draw_list_id) + } + + pub fn get_draw_list_mut(&mut self, draw_list_id: DrawListId) -> &mut DrawList { + self.draw_lists.get_mut(draw_list_id) + } + + pub fn remove_draw_list(&mut self, draw_list_id: DrawListId) { + self.draw_lists.free(draw_list_id); + } + pub fn allocate_render_target(&mut self, target: TextureTarget, width: u32, @@ -349,7 +368,7 @@ fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, (*native_font_handle).clone()); } } - job.result = font_context.get_glyph(&job.glyph_key.font_key, + job.result = font_context.get_glyph(job.glyph_key.font_key, job.glyph_key.size, job.glyph_key.index, device_pixel_ratio); diff --git a/src/resource_list.rs b/src/resource_list.rs index 306c27b7f7..eabb6256c7 100644 --- a/src/resource_list.rs +++ b/src/resource_list.rs @@ -1,12 +1,15 @@ +use aabbtree::AABBTreeNode; use app_units::Au; use euclid::Size2D; use fnv::FnvHasher; -use internal_types::{BorderRadiusRasterOp, BoxShadowRasterOp}; +use internal_types::{BorderRadiusRasterOp, BoxShadowRasterOp, DrawListItemIndex}; use internal_types::{Glyph, GlyphKey, RasterItem, TiledImageKey}; +use resource_cache::ResourceCache; use std::collections::{HashMap, HashSet}; use std::collections::hash_map::Entry::{Occupied, Vacant}; use std::collections::hash_state::DefaultState; -use types::{BorderRadius, FontKey, ImageFormat, ImageKey}; +use webrender_traits::{BorderRadius, BorderStyle, BoxShadowClipMode}; +use webrender_traits::{FontKey, ImageFormat, ImageKey, SpecificDisplayItem}; type RequiredImageSet = HashSet>; type RequiredGlyphMap = HashMap, DefaultState>; @@ -168,3 +171,108 @@ impl TiledImageKey { } } +pub trait BuildRequiredResources { + fn build_resource_list(&mut self, resource_cache: &ResourceCache); +} + +impl BuildRequiredResources for AABBTreeNode { + fn build_resource_list(&mut self, resource_cache: &ResourceCache) { + //let _pf = util::ProfileScope::new(" build_resource_list"); + let mut resource_list = ResourceList::new(); + + for draw_list_index_buffer in &self.draw_lists { + let draw_list = resource_cache.get_draw_list(draw_list_index_buffer.draw_list_id); + + for index in &draw_list_index_buffer.indices { + let DrawListItemIndex(index) = *index; + let display_item = &draw_list.items[index as usize]; + + // Handle border radius for complex clipping regions. + for complex_clip_region in display_item.clip.complex.iter() { + resource_list.add_radius_raster_for_border_radii(&complex_clip_region.radii); + } + + match display_item.item { + SpecificDisplayItem::Image(ref info) => { + resource_list.add_image(info.image_key, + &display_item.rect.size, + &info.stretch_size); + } + SpecificDisplayItem::Text(ref info) => { + for glyph in &info.glyphs { + let glyph = Glyph::new(info.size, info.blur_radius, glyph.index); + resource_list.add_glyph(info.font_key, glyph); + } + } + SpecificDisplayItem::Rectangle(..) => {} + SpecificDisplayItem::Gradient(..) => {} + SpecificDisplayItem::BoxShadow(ref info) => { + resource_list.add_radius_raster_for_border_radii( + &BorderRadius::uniform(info.border_radius)); + resource_list.add_box_shadow_corner(info.blur_radius, + info.border_radius, + false); + resource_list.add_box_shadow_edge(info.blur_radius, info.border_radius, false); + if info.clip_mode == BoxShadowClipMode::Inset { + resource_list.add_box_shadow_corner(info.blur_radius, + info.border_radius, + true); + resource_list.add_box_shadow_edge(info.blur_radius, + info.border_radius, + true); + } + } + SpecificDisplayItem::Border(ref info) => { + resource_list.add_radius_raster(&info.radius.top_left, + &info.top_left_inner_radius(), + false, + ImageFormat::A8); + resource_list.add_radius_raster(&info.radius.top_right, + &info.top_right_inner_radius(), + false, + ImageFormat::A8); + resource_list.add_radius_raster(&info.radius.bottom_left, + &info.bottom_left_inner_radius(), + false, + ImageFormat::A8); + resource_list.add_radius_raster(&info.radius.bottom_right, + &info.bottom_right_inner_radius(), + false, + ImageFormat::A8); + + if info.top.style == BorderStyle::Dotted { + resource_list.add_radius_raster(&Size2D::new(info.top.width / 2.0, + info.top.width / 2.0), + &Size2D::new(0.0, 0.0), + false, + ImageFormat::RGBA8); + } + if info.right.style == BorderStyle::Dotted { + resource_list.add_radius_raster(&Size2D::new(info.right.width / 2.0, + info.right.width / 2.0), + &Size2D::new(0.0, 0.0), + false, + ImageFormat::RGBA8); + } + if info.bottom.style == BorderStyle::Dotted { + resource_list.add_radius_raster(&Size2D::new(info.bottom.width / 2.0, + info.bottom.width / 2.0), + &Size2D::new(0.0, 0.0), + false, + ImageFormat::RGBA8); + } + if info.left.style == BorderStyle::Dotted { + resource_list.add_radius_raster(&Size2D::new(info.left.width / 2.0, + info.left.width / 2.0), + &Size2D::new(0.0, 0.0), + false, + ImageFormat::RGBA8); + } + } + } + } + } + + self.resource_list = Some(resource_list); + } +} diff --git a/src/scene.rs b/src/scene.rs new file mode 100644 index 0000000000..5c1664ebb6 --- /dev/null +++ b/src/scene.rs @@ -0,0 +1,204 @@ +use fnv::FnvHasher; +use internal_types::DrawListId; +use optimizer; +use resource_cache::ResourceCache; +use std::collections::HashMap; +use std::collections::hash_state::DefaultState; +use webrender_traits::{PipelineId, Epoch}; +use webrender_traits::{DisplayListBuilder}; +use webrender_traits::{ColorF, DisplayListId, StackingContext, StackingContextId}; +use webrender_traits::{SpecificDisplayListItem}; +use webrender_traits::{StackingLevel, SpecificDisplayItem}; +use webrender_traits::{IframeInfo}; +use webrender_traits::{RectangleDisplayItem, ClipRegion, DisplayItem}; + +#[derive(Debug)] +pub struct ScenePipeline { + pub pipeline_id: PipelineId, + pub epoch: Epoch, + pub background_draw_list: Option, + pub root_stacking_context_id: StackingContextId, +} + +pub struct Scene { + pub root_pipeline_id: Option, + pub pipeline_map: HashMap>, + pub display_list_map: HashMap>, + pub stacking_context_map: HashMap>, +} + +#[derive(Clone, Debug)] +pub enum SpecificSceneItem { + DrawList(DrawListId), + StackingContext(StackingContextId), + Iframe(Box), +} + +#[derive(Clone, Debug)] +pub struct SceneItem { + pub stacking_level: StackingLevel, + pub specific: SpecificSceneItem, +} + +pub struct SceneDisplayList { + pub pipeline_id: PipelineId, + pub epoch: Epoch, + pub items: Vec, +} + +pub struct SceneStackingContext { + pub pipeline_id: PipelineId, + pub epoch: Epoch, + pub stacking_context: StackingContext, +} + +impl Scene { + pub fn new() -> Scene { + Scene { + root_pipeline_id: None, + pipeline_map: HashMap::with_hash_state(Default::default()), + display_list_map: HashMap::with_hash_state(Default::default()), + stacking_context_map: HashMap::with_hash_state(Default::default()), + } + } + + pub fn add_display_list(&mut self, + id: DisplayListId, + pipeline_id: PipelineId, + epoch: Epoch, + mut display_list_builder: DisplayListBuilder, + resource_cache: &mut ResourceCache) { + display_list_builder.finalize(); + optimizer::optimize_display_list_builder(&mut display_list_builder); + + let items = display_list_builder.items.into_iter().map(|item| { + match item.specific { + SpecificDisplayListItem::DrawList(info) => { + let draw_list_id = resource_cache.add_draw_list(info.items); + SceneItem { + stacking_level: item.stacking_level, + specific: SpecificSceneItem::DrawList(draw_list_id) + } + } + SpecificDisplayListItem::StackingContext(info) => { + SceneItem { + stacking_level: item.stacking_level, + specific: SpecificSceneItem::StackingContext(info.id) + } + } + SpecificDisplayListItem::Iframe(info) => { + SceneItem { + stacking_level: item.stacking_level, + specific: SpecificSceneItem::Iframe(info) + } + } + } + }).collect(); + + let display_list = SceneDisplayList { + pipeline_id: pipeline_id, + epoch: epoch, + items: items, + }; + + self.display_list_map.insert(id, display_list); + } + + pub fn add_stacking_context(&mut self, + id: StackingContextId, + pipeline_id: PipelineId, + epoch: Epoch, + stacking_context: StackingContext) { + let stacking_context = SceneStackingContext { + pipeline_id: pipeline_id, + epoch: epoch, + stacking_context: stacking_context, + }; + + self.stacking_context_map.insert(id, stacking_context); + } + + pub fn set_root_pipeline_id(&mut self, pipeline_id: PipelineId) { + self.root_pipeline_id = Some(pipeline_id); + } + + pub fn set_root_stacking_context(&mut self, + pipeline_id: PipelineId, + epoch: Epoch, + stacking_context_id: StackingContextId, + background_color: ColorF, + resource_cache: &mut ResourceCache) { + let old_display_list_keys: Vec<_> = self.display_list_map.iter() + .filter(|&(_, ref v)| { + v.pipeline_id == pipeline_id && + v.epoch < epoch + }) + .map(|(k, _)| k.clone()) + .collect(); + + // Remove any old draw lists and display lists for this pipeline + for key in old_display_list_keys { + let display_list = self.display_list_map.remove(&key).unwrap(); + for item in display_list.items { + match item.specific { + SpecificSceneItem::DrawList(draw_list_id) => { + resource_cache.remove_draw_list(draw_list_id); + } + SpecificSceneItem::StackingContext(..) | + SpecificSceneItem::Iframe(..) => {} + } + } + } + + let old_stacking_context_keys: Vec<_> = self.stacking_context_map.iter() + .filter(|&(_, ref v)| { + v.pipeline_id == pipeline_id && + v.epoch < epoch + }) + .map(|(k, _)| k.clone()) + .collect(); + + // Remove any old draw lists and display lists for this pipeline + for key in old_stacking_context_keys { + self.stacking_context_map.remove(&key).unwrap(); + + // TODO: Could remove all associated DLs here, + // and then the above code could just be a debug assert check... + } + + let background_draw_list = if background_color.a > 0.0 { + let overflow = self.stacking_context_map[&stacking_context_id].stacking_context.overflow; + + let rectangle_item = RectangleDisplayItem { + color: background_color, + }; + let clip = ClipRegion { + main: overflow, + complex: vec![], + }; + let root_bg_color_item = DisplayItem { + item: SpecificDisplayItem::Rectangle(rectangle_item), + rect: overflow, + clip: clip, + }; + + let draw_list_id = resource_cache.add_draw_list(vec![root_bg_color_item]); + Some(draw_list_id) + } else { + None + }; + + let new_pipeline = ScenePipeline { + pipeline_id: pipeline_id, + epoch: epoch, + background_draw_list: background_draw_list, + root_stacking_context_id: stacking_context_id, + }; + + if let Some(old_pipeline) = self.pipeline_map.insert(pipeline_id, new_pipeline) { + if let Some(background_draw_list) = old_pipeline.background_draw_list { + resource_cache.remove_draw_list(background_draw_list); + } + } + } +} diff --git a/src/stats.rs b/src/stats.rs deleted file mode 100644 index e69de29bb2..0000000000 diff --git a/src/texture_cache.rs b/src/texture_cache.rs index f304c317d2..8e49ed1fad 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -9,7 +9,7 @@ use std::collections::hash_map::Entry; use std::collections::hash_state::DefaultState; use freelist::{FreeList, FreeListItem, FreeListItemId}; use std::mem; -use types::ImageFormat; +use webrender_traits::ImageFormat; use util; const LEVELS_PER_TEXTURE: u8 = 8; @@ -148,7 +148,7 @@ impl TexturePage { } } -#[derive(Debug)] +#[derive(Debug, Clone)] pub struct TextureCacheItem { pub u0: f32, // todo(gw): don't precalc these? pub v0: f32, @@ -724,4 +724,3 @@ fn create_new_texture_page(pending_updates: &mut TextureUpdateList, }) } } - diff --git a/src/types.rs b/src/types.rs deleted file mode 100644 index 2c08ca7fb1..0000000000 --- a/src/types.rs +++ /dev/null @@ -1,725 +0,0 @@ -use app_units::Au; -use euclid::{Point2D, Rect, Size2D, Matrix4}; - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct FontKey(u32, u32); - -impl FontKey { - pub fn new(key0: u32, key1: u32) -> FontKey { - FontKey(key0, key1) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash, Serialize, Deserialize)] -pub struct ImageKey(u32, u32); - -impl ImageKey { - pub fn new(key0: u32, key1: u32) -> ImageKey { - ImageKey(key0, key1) - } -} - -#[derive(Clone, Copy, Debug, Eq, PartialEq)] -pub struct NodeIndex(pub u32); - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub enum ImageFormat { - Invalid, - A8, - RGB8, - RGBA8, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct ColorF { - pub r: f32, - pub g: f32, - pub b: f32, - pub a: f32, -} - -impl ColorF { - pub fn new(r: f32, g: f32, b: f32, a: f32) -> ColorF { - ColorF { - r: r, - g: g, - b: b, - a: a, - } - } - - pub fn scale_rgb(&self, scale: f32) -> ColorF { - ColorF { - r: self.r * scale, - g: self.g * scale, - b: self.b * scale, - a: self.a, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq)] -pub enum StackingLevel { - BackgroundAndBorders, - BlockBackgroundAndBorders, - Floats, - Content, - PositionedContent, - Outlines, -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, Ord, PartialOrd)] -pub struct Epoch(pub u32); - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct PipelineId(pub u32, pub u32); - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct RenderTargetID(pub u32); // TODO: HACK HACK HACK this is an alias for device::TextureId - -impl RenderTargetID { - pub fn new(id: u32) -> RenderTargetID { - RenderTargetID(id) - } -} - -// TODO: This is bogus - work out a clean way to generate scroll layer IDs that integrates well with servo... -const FIXED_SCROLL_LAYER_ID: usize = 0xffffffff; - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct ScrollLayerId(pub usize); - -impl ScrollLayerId { - pub fn new(value: usize) -> ScrollLayerId { - debug_assert!(value != FIXED_SCROLL_LAYER_ID); - ScrollLayerId(value) - } - - pub fn fixed_layer() -> ScrollLayerId { - ScrollLayerId(FIXED_SCROLL_LAYER_ID) - } -} - -#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)] -pub struct DisplayListID(pub u32, pub u32); - -#[derive(Debug, Copy, Clone, PartialEq)] -pub enum BoxShadowClipMode { - None, - Outset, - Inset, -} - -#[derive(Debug, PartialEq)] -pub struct BorderSide { - pub width: f32, - pub color: ColorF, - pub style: BorderStyle, -} - -#[derive(Debug, PartialEq)] -pub struct GradientStop { - pub offset: f32, - pub color: ColorF, -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct BorderRadius { - pub top_left: Size2D, - pub top_right: Size2D, - pub bottom_left: Size2D, - pub bottom_right: Size2D, -} - -impl BorderRadius { - pub fn zero() -> BorderRadius { - BorderRadius { - top_left: Size2D::new(0.0, 0.0), - top_right: Size2D::new(0.0, 0.0), - bottom_left: Size2D::new(0.0, 0.0), - bottom_right: Size2D::new(0.0, 0.0), - } - } - - pub fn uniform(radius: f32) -> BorderRadius { - BorderRadius { - top_left: Size2D::new(radius, radius), - top_right: Size2D::new(radius, radius), - bottom_left: Size2D::new(radius, radius), - bottom_right: Size2D::new(radius, radius), - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub enum BorderStyle { - None, - Solid, - Double, - Dotted, - Dashed, - Hidden, - Groove, - Ridge, - Inset, - Outset, -} - -#[derive(Debug, PartialEq)] -pub struct GlyphInstance { - pub index: u32, - pub x: f32, - pub y: f32, -} - -pub struct StackingContext { - pub scroll_layer_id: Option, - pub scroll_policy: ScrollPolicy, - pub bounds: Rect, - pub overflow: Rect, - pub z_index: i32, - pub display_lists: Vec, - pub children: Vec, - pub transform: Matrix4, - pub perspective: Matrix4, - pub establishes_3d_context: bool, - pub mix_blend_mode: MixBlendMode, - pub filters: Vec, -} - -impl StackingContext { - pub fn new(scroll_layer_id: Option, - scroll_policy: ScrollPolicy, - bounds: Rect, - overflow: Rect, - z_index: i32, - transform: &Matrix4, - perspective: &Matrix4, - establishes_3d_context: bool, - mix_blend_mode: MixBlendMode, - filters: Vec) - -> StackingContext { - StackingContext { - scroll_layer_id: scroll_layer_id, - scroll_policy: scroll_policy, - bounds: bounds, - overflow: overflow, - z_index: z_index, - display_lists: Vec::new(), - children: Vec::new(), - transform: transform.clone(), - perspective: perspective.clone(), - establishes_3d_context: establishes_3d_context, - mix_blend_mode: mix_blend_mode, - filters: filters, - } - } - - pub fn add_stacking_context(&mut self, stacking_context: StackingContext) { - self.children.push(stacking_context); - } - - pub fn add_display_list(&mut self, id: DisplayListID) { - self.display_lists.push(id); - } -} - -#[derive(Debug, PartialEq)] -pub struct TextDisplayItem { - pub glyphs: Vec, - pub font_key: FontKey, - pub size: Au, - pub color: ColorF, - pub blur_radius: Au, -} - -#[derive(Debug, PartialEq)] -pub struct ImageDisplayItem { - pub image_key: ImageKey, - pub stretch_size: Size2D, -} - -#[derive(Debug, PartialEq)] -pub struct RectangleDisplayItem { - pub color: ColorF, -} - -#[derive(Debug, PartialEq)] -pub struct BorderDisplayItem { - pub left: BorderSide, - pub right: BorderSide, - pub top: BorderSide, - pub bottom: BorderSide, - pub radius: BorderRadius, -} - -impl BorderDisplayItem { - pub fn top_left_inner_radius(&self) -> Size2D { - Size2D::new((self.radius.top_left.width - self.left.width).max(0.0), - (self.radius.top_left.height - self.top.width).max(0.0)) - } - - pub fn top_right_inner_radius(&self) -> Size2D { - Size2D::new((self.radius.top_right.width - self.right.width).max(0.0), - (self.radius.top_right.height - self.top.width).max(0.0)) - } - - pub fn bottom_left_inner_radius(&self) -> Size2D { - Size2D::new((self.radius.bottom_left.width - self.left.width).max(0.0), - (self.radius.bottom_left.height - self.bottom.width).max(0.0)) - } - - pub fn bottom_right_inner_radius(&self) -> Size2D { - Size2D::new((self.radius.bottom_right.width - self.right.width).max(0.0), - (self.radius.bottom_right.height - self.bottom.width).max(0.0)) - } -} - -#[derive(Debug, PartialEq)] -pub struct BoxShadowDisplayItem { - pub box_bounds: Rect, - pub offset: Point2D, - pub color: ColorF, - pub blur_radius: f32, - pub spread_radius: f32, - pub border_radius: f32, - pub clip_mode: BoxShadowClipMode, -} - -#[derive(Debug, PartialEq)] -pub struct GradientDisplayItem { - pub start_point: Point2D, - pub end_point: Point2D, - pub stops: Vec, -} - -#[derive(Debug, PartialEq)] -pub struct IframeDisplayItem { - pub iframe: PipelineId, -} - -#[derive(Debug, PartialEq)] -pub struct ClearDisplayItem { - pub clear_color: bool, - pub clear_z: bool, - pub clear_stencil: bool, -} - -#[derive(Debug, PartialEq)] -pub struct CompositeDisplayItem { - pub texture_id: RenderTargetID, - pub operation: CompositionOp, -} - -#[derive(Debug, PartialEq)] -pub enum SpecificDisplayItem { - Rectangle(RectangleDisplayItem), - Text(TextDisplayItem), - Image(ImageDisplayItem), - Border(BorderDisplayItem), - BoxShadow(BoxShadowDisplayItem), - Gradient(GradientDisplayItem), - Iframe(IframeDisplayItem), - - // Internal use only - Composite(CompositeDisplayItem), - Clear(ClearDisplayItem), -} - -#[derive(Debug, PartialEq)] -pub struct DisplayItem { - pub item: SpecificDisplayItem, - pub rect: Rect, - pub clip: ClipRegion, - - pub node_index: Option, -} - -pub enum DisplayListMode { - Default, - PseudoFloat, - PseudoPositionedContent, -} - -pub struct DrawList { - pub items: Vec, -} - -impl DrawList { - pub fn new() -> DrawList { - DrawList { - items: Vec::new(), - } - } - - #[inline] - pub fn push(&mut self, item: DisplayItem) { - self.items.push(item); - } - - #[inline] - pub fn item_count(&self) -> usize { - self.items.len() - } -} - -pub struct DisplayListBuilder { - pub mode: DisplayListMode, - - pub background_and_borders: DrawList, - pub block_backgrounds_and_borders: DrawList, - pub floats: DrawList, - pub content: DrawList, - pub positioned_content: DrawList, - pub outlines: DrawList, -} - -impl DisplayListBuilder { - pub fn new() -> DisplayListBuilder { - DisplayListBuilder { - mode: DisplayListMode::Default, - - background_and_borders: DrawList::new(), - block_backgrounds_and_borders: DrawList::new(), - floats: DrawList::new(), - content: DrawList::new(), - positioned_content: DrawList::new(), - outlines: DrawList::new(), - } - } - - pub fn set_mode(&mut self, mode: DisplayListMode) { - self.mode = mode; - } - - #[inline] - pub fn item_count(&self) -> usize { - self.background_and_borders.item_count() + - self.block_backgrounds_and_borders.item_count() + - self.floats.item_count() + - self.content.item_count() + - self.positioned_content.item_count() + - self.outlines.item_count() - } - - pub fn push_rect(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - color: ColorF) { - - let item = RectangleDisplayItem { - color: color, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Rectangle(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_image(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - stretch_size: Size2D, - key: ImageKey) { - let item = ImageDisplayItem { - image_key: key, - stretch_size: stretch_size, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Image(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_text(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - glyphs: Vec, - font_key: FontKey, - color: ColorF, - size: Au, - blur_radius: Au) { - let item = TextDisplayItem { - color: color, - glyphs: glyphs, - font_key: font_key, - size: size, - blur_radius: blur_radius, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Text(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_border(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - left: BorderSide, - top: BorderSide, - right: BorderSide, - bottom: BorderSide, - radius: BorderRadius) { - let item = BorderDisplayItem { - left: left, - top: top, - right: right, - bottom: bottom, - radius: radius, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Border(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_box_shadow(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - box_bounds: Rect, - offset: Point2D, - color: ColorF, - blur_radius: f32, - spread_radius: f32, - border_radius: f32, - clip_mode: BoxShadowClipMode) { - let item = BoxShadowDisplayItem { - box_bounds: box_bounds, - offset: offset, - color: color, - blur_radius: blur_radius, - spread_radius: spread_radius, - border_radius: border_radius, - clip_mode: clip_mode, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::BoxShadow(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_gradient(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - start_point: Point2D, - end_point: Point2D, - stops: Vec) { - let item = GradientDisplayItem { - start_point: start_point, - end_point: end_point, - stops: stops, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Gradient(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - pub fn push_iframe(&mut self, - level: StackingLevel, - rect: Rect, - clip: ClipRegion, - iframe: PipelineId) { - assert!(level == StackingLevel::Content || - level == StackingLevel::PositionedContent || - level == StackingLevel::Floats, format!("push_iframe: level={:?}", level)); // invariant in get_draw_lists_for_stacking_context - - let item = IframeDisplayItem { - iframe: iframe, - }; - - let display_item = DisplayItem { - item: SpecificDisplayItem::Iframe(item), - rect: rect, - clip: clip, - node_index: None, - }; - - self.push_item(level, display_item); - } - - fn push_item(&mut self, level: StackingLevel, item: DisplayItem) { - match level { - StackingLevel::BackgroundAndBorders => { - self.background_and_borders.push(item); - } - StackingLevel::BlockBackgroundAndBorders => { - self.block_backgrounds_and_borders.push(item); - } - StackingLevel::Floats => { - self.floats.push(item); - } - StackingLevel::Content => { - self.content.push(item); - } - StackingLevel::PositionedContent => { - self.positioned_content.push(item); - } - StackingLevel::Outlines => { - self.outlines.push(item); - } - } - } -} - -pub trait RenderNotifier : Send { - fn new_frame_ready(&mut self); -} - -#[derive(PartialEq, Eq, Hash, Clone, Debug)] -pub struct Glyph { - pub size: Au, - pub blur_radius: Au, - pub index: u32, -} - -impl Glyph { - #[inline] - pub fn new(size: Au, blur_radius: Au, index: u32) -> Glyph { - Glyph { - size: size, - blur_radius: blur_radius, - index: index, - } - } -} - -#[derive(Debug, Clone, PartialEq)] -pub struct ClipRegion { - pub main: Rect, - pub complex: Vec, -} - -impl ClipRegion { - pub fn new(rect: Rect, complex: Vec) -> ClipRegion { - ClipRegion { - main: rect, - complex: complex, - } - } -} - -#[derive(Debug, Clone, Copy, PartialEq)] -pub struct ComplexClipRegion { - /// The boundaries of the rectangle. - pub rect: Rect, - /// Border radii of this rectangle. - pub radii: BorderRadius, -} - -impl ComplexClipRegion { - pub fn new(rect: Rect, radii: BorderRadius) -> ComplexClipRegion { - ComplexClipRegion { - rect: rect, - radii: radii, - } - } - - pub fn from_rect(rect: &Rect) -> ComplexClipRegion { - ComplexClipRegion { - rect: *rect, - radii: BorderRadius::zero(), - } - } -} - -#[derive(Clone, PartialEq, Eq, Copy, Deserialize, Serialize, Debug)] -pub enum ScrollPolicy { - Scrollable, - Fixed, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum MixBlendMode { - Normal, - Multiply, - Screen, - Overlay, - Darken, - Lighten, - ColorDodge, - ColorBurn, - HardLight, - SoftLight, - Difference, - Exclusion, - Hue, - Saturation, - Color, - Luminosity, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum LowLevelFilterOp { - Blur(Au, BlurDirection), - Brightness(f32), - Contrast(f32), - Grayscale(f32), - HueRotate(f32), - Invert(f32), - Opacity(f32), - Saturate(f32), - Sepia(f32), -} - -#[derive(Clone, Copy, Debug)] -pub enum FilterOp { - Blur(Au), - Brightness(f32), - Contrast(f32), - Grayscale(f32), - HueRotate(f32), - Invert(f32), - Opacity(f32), - Saturate(f32), - Sepia(f32), -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum BlurDirection { - Horizontal, - Vertical, -} - -#[derive(Clone, Copy, Debug, PartialEq)] -pub enum CompositionOp { - MixBlend(MixBlendMode), - Filter(LowLevelFilterOp), -} - diff --git a/src/util.rs b/src/util.rs index 719c18493e..64ecada796 100644 --- a/src/util.rs +++ b/src/util.rs @@ -24,7 +24,6 @@ impl Drop for ProfileScope { if self.name.chars().next() != Some(' ') { let t1 = precise_time_ns(); let ms = (t1 - self.t0) as f64 / 1000000f64; - //if ms > 0.1 { println!("{} {}", self.name, ms); } } @@ -80,4 +79,3 @@ pub fn bilerp(point: &Point2D, quad: &Rect, uv: &RectUv) -> Point2D(rect: &Rect) -> bool { rect.size.width == Zero::zero() || rect.size.height == Zero::zero() } -