diff --git a/src/batch_builder.rs b/src/batch_builder.rs index e26720b55c..fa34c7957c 100644 --- a/src/batch_builder.rs +++ b/src/batch_builder.rs @@ -3,6 +3,7 @@ use batch::{BatchBuilder, TileParams}; use device::TextureId; use euclid::{Rect, Point2D, Size2D}; use fnv::FnvHasher; +use frame::FrameId; use internal_types::{AxisDirection, BasicRotationAngle, BorderRadiusRasterOp, BoxShadowRasterOp}; use internal_types::{GlyphKey, PackedVertexColorMode, RasterItem, RectColors, RectPolygon}; use internal_types::{RectSide, RectUv, DevicePixel}; @@ -82,7 +83,8 @@ impl<'a> BatchBuilder<'a> { uv_rect: &RectUv, colors: &[ColorF; 4], tile_params: Option, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { if pos_rect.size.width == 0.0 || pos_rect.size.height == 0.0 { return } @@ -174,15 +176,21 @@ impl<'a> BatchBuilder<'a> { let (mask_texture_id, muv_rect) = match mask_info { Some(clip_rect) => { - let mask_image = resource_cache.get_raster(&RasterItem::BorderRadius(BorderRadiusRasterOp { - outer_radius_x: DevicePixel::new(clip_rect.size.width, self.device_pixel_ratio), - outer_radius_y: DevicePixel::new(clip_rect.size.height, self.device_pixel_ratio), - inner_radius_x: DevicePixel::zero(), - inner_radius_y: DevicePixel::zero(), - inverted: false, - index: None, - image_format: ImageFormat::A8, - })); + let mask_image = resource_cache.get_raster( + &RasterItem::BorderRadius(BorderRadiusRasterOp { + outer_radius_x: + DevicePixel::new(clip_rect.size.width, + self.device_pixel_ratio), + outer_radius_y: + DevicePixel::new(clip_rect.size.height, + self.device_pixel_ratio), + inner_radius_x: DevicePixel::zero(), + inner_radius_y: DevicePixel::zero(), + inverted: false, + index: None, + image_format: ImageFormat::A8, + }), + frame_id); let mut x0_f = (x0 - clip_rect.origin.x) / clip_rect.size.width; let mut x1_f = (x1 - clip_rect.origin.x) / clip_rect.size.width; @@ -282,21 +290,24 @@ impl<'a> BatchBuilder<'a> { #[inline] pub fn add_color_rectangle(&mut self, rect: &Rect, + color: &ColorF, resource_cache: &ResourceCache, - color: &ColorF) { + frame_id: FrameId) { let white_image = resource_cache.get_dummy_color_image(); self.add_complex_clipped_rectangle(white_image.texture_id, rect, &white_image.uv_rect, &[*color, *color, *color, *color], None, - resource_cache); + resource_cache, + frame_id); } pub fn add_webgl_rectangle(&mut self, rect: &Rect, resource_cache: &ResourceCache, - webgl_context_id: &WebGLContextId) { + webgl_context_id: &WebGLContextId, + frame_id: FrameId) { let texture_id = resource_cache.get_webgl_texture(webgl_context_id); let color = ColorF::new(1.0, 1.0, 1.0, 1.0); @@ -312,7 +323,8 @@ impl<'a> BatchBuilder<'a> { &uv, &[color, color, color, color], None, - resource_cache); + resource_cache, + frame_id); } pub fn add_image(&mut self, @@ -320,10 +332,11 @@ impl<'a> BatchBuilder<'a> { stretch_size: &Size2D, image_key: ImageKey, image_rendering: ImageRendering, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { // 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, image_rendering); + let image_info = resource_cache.get_image(image_key, image_rendering, frame_id); let u1 = rect.size.width / stretch_size.width; let v1 = rect.size.height / stretch_size.height; @@ -351,7 +364,8 @@ impl<'a> BatchBuilder<'a> { &uv, &[color, color, color, color], Some(tile_params), - resource_cache); + resource_cache, + frame_id); } pub fn add_text(&mut self, @@ -362,6 +376,7 @@ impl<'a> BatchBuilder<'a> { color: &ColorF, glyphs: &[GlyphInstance], resource_cache: &ResourceCache, + frame_id: FrameId, device_pixel_ratio: f32) { let dummy_mask_image = resource_cache.get_dummy_mask_image(); @@ -378,7 +393,7 @@ impl<'a> BatchBuilder<'a> { for glyph in glyphs { glyph_key.index = glyph.index; - let image_info = resource_cache.get_glyph(&glyph_key); + let image_info = resource_cache.get_glyph(&glyph_key, frame_id); if let Some(image_info) = image_info { let mut x = (glyph.x * device_pixel_ratio + image_info.user_data.x0 as f32).round() / device_pixel_ratio; let mut y = (glyph.y * device_pixel_ratio - image_info.user_data.y0 as f32).round() / device_pixel_ratio; @@ -423,7 +438,8 @@ impl<'a> BatchBuilder<'a> { rect: &Rect, direction: AxisDirection, stops: &[GradientStop], - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { let white_image = resource_cache.get_dummy_color_image(); for i in 0..(stops.len() - 1) { @@ -462,7 +478,8 @@ impl<'a> BatchBuilder<'a> { &white_image.uv_rect, &piece_colors, None, - resource_cache); + resource_cache, + frame_id); } } @@ -470,7 +487,8 @@ impl<'a> BatchBuilder<'a> { start_point: &Point2D, end_point: &Point2D, stops: &[GradientStop], - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { // Fast paths for axis-aligned gradients: // // FIXME(pcwalton): Determine the start and end points properly! @@ -480,7 +498,8 @@ impl<'a> BatchBuilder<'a> { self.add_axis_aligned_gradient_with_stops(&rect, AxisDirection::Vertical, stops, - resource_cache); + resource_cache, + frame_id); return } if start_point.y == end_point.y { @@ -489,7 +508,8 @@ impl<'a> BatchBuilder<'a> { self.add_axis_aligned_gradient_with_stops(&rect, AxisDirection::Horizontal, stops, - resource_cache); + resource_cache, + frame_id); return } @@ -562,14 +582,13 @@ impl<'a> BatchBuilder<'a> { spread_radius: f32, border_radius: f32, clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { 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(&rect, - resource_cache, - color); + self.add_color_rectangle(&rect, color, resource_cache, frame_id); return; } @@ -581,7 +600,8 @@ impl<'a> BatchBuilder<'a> { spread_radius, border_radius, clip_mode, - resource_cache); + resource_cache, + frame_id); // Draw the sides. self.add_box_shadow_sides(box_bounds, @@ -591,14 +611,13 @@ impl<'a> BatchBuilder<'a> { spread_radius, border_radius, clip_mode, - resource_cache); + resource_cache, + frame_id); match clip_mode { BoxShadowClipMode::None => { // Fill the center area. - self.add_color_rectangle(box_bounds, - resource_cache, - color); + self.add_color_rectangle(box_bounds, color, resource_cache, frame_id); } BoxShadowClipMode::Outset => { // Fill the center area. @@ -614,9 +633,7 @@ impl<'a> BatchBuilder<'a> { // the case! let old_clip_out_rect = self.set_clip_out_rect(Some(*box_bounds)); - self.add_color_rectangle(¢er_rect, - resource_cache, - color); + self.add_color_rectangle(¢er_rect, color, resource_cache, frame_id); self.set_clip_out_rect(old_clip_out_rect); } @@ -629,7 +646,8 @@ impl<'a> BatchBuilder<'a> { blur_radius, spread_radius, border_radius, - resource_cache); + resource_cache, + frame_id); } } } @@ -642,7 +660,8 @@ impl<'a> BatchBuilder<'a> { spread_radius: f32, border_radius: f32, clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { // Draw the corners. // // +--+------------------+--+ @@ -678,6 +697,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Upright); self.add_box_shadow_corner(&Point2D::new(metrics.tr_outer.x - metrics.edge_size, metrics.tr_outer.y), @@ -691,6 +711,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise90); self.add_box_shadow_corner(&Point2D::new(metrics.br_outer.x - metrics.edge_size, metrics.br_outer.y - metrics.edge_size), @@ -703,6 +724,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise180); self.add_box_shadow_corner(&Point2D::new(metrics.bl_outer.x, metrics.bl_outer.y - metrics.edge_size), @@ -716,6 +738,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise270); self.undo_clip_state(clip_state); @@ -729,7 +752,8 @@ impl<'a> BatchBuilder<'a> { spread_radius: f32, border_radius: f32, clip_mode: BoxShadowClipMode, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); @@ -772,6 +796,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise90); self.add_box_shadow_edge(&right_rect.origin, &right_rect.bottom_right(), @@ -781,6 +806,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise180); self.add_box_shadow_edge(&bottom_rect.origin, &bottom_rect.bottom_right(), @@ -790,6 +816,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Clockwise270); self.add_box_shadow_edge(&left_rect.origin, &left_rect.bottom_right(), @@ -799,6 +826,7 @@ impl<'a> BatchBuilder<'a> { border_radius, clip_mode, resource_cache, + frame_id, BasicRotationAngle::Upright); self.undo_clip_state(clip_state); @@ -811,7 +839,8 @@ impl<'a> BatchBuilder<'a> { blur_radius: f32, spread_radius: f32, border_radius: f32, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { let rect = compute_box_shadow_rect(box_bounds, box_offset, spread_radius); let metrics = BoxShadowMetrics::new(&rect, border_radius, blur_radius); @@ -839,29 +868,33 @@ impl<'a> BatchBuilder<'a> { self.add_color_rectangle(&Rect::new(box_bounds.origin, Size2D::new(box_bounds.size.width, metrics.tl_outer.y - box_bounds.origin.y)), + color, resource_cache, - color); + frame_id); // B: self.add_color_rectangle(&Rect::new(metrics.tr_outer, Size2D::new(box_bounds.max_x() - metrics.tr_outer.x, metrics.br_outer.y - metrics.tr_outer.y)), + color, resource_cache, - color); + frame_id); // C: self.add_color_rectangle(&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)), + color, resource_cache, - color); + frame_id); // D: self.add_color_rectangle(&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)), + color, resource_cache, - color); + frame_id); self.undo_clip_state(clip_state); } @@ -905,7 +938,8 @@ impl<'a> BatchBuilder<'a> { side: RectSide, color: &ColorF, border_style: BorderStyle, - resource_cache: &ResourceCache) { + resource_cache: &ResourceCache, + frame_id: FrameId) { if color.a <= 0.0 { return } @@ -941,9 +975,7 @@ impl<'a> BatchBuilder<'a> { } }; - self.add_color_rectangle(&dash_rect, - resource_cache, - color); + self.add_color_rectangle(&dash_rect, color, resource_cache, frame_id); origin += step + step; } @@ -982,7 +1014,7 @@ impl<'a> BatchBuilder<'a> { 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); + let color_image = resource_cache.get_raster(&raster_item, frame_id); // Top left: self.add_simple_rectangle(color_image.texture_id, @@ -1048,12 +1080,8 @@ impl<'a> BatchBuilder<'a> { Size2D::new(rect.size.width / 3.0, rect.size.height))) } }; - self.add_color_rectangle(&outer_rect, - resource_cache, - color); - self.add_color_rectangle(&inner_rect, - resource_cache, - color); + self.add_color_rectangle(&outer_rect, color, resource_cache, frame_id); + self.add_color_rectangle(&inner_rect, color, resource_cache, frame_id); } BorderStyle::Groove | BorderStyle::Ridge => { let (tl_rect, br_rect) = match side { @@ -1073,17 +1101,11 @@ impl<'a> BatchBuilder<'a> { } }; let (tl_color, br_color) = groove_ridge_border_colors(color, border_style); - self.add_color_rectangle(&tl_rect, - resource_cache, - &tl_color); - self.add_color_rectangle(&br_rect, - resource_cache, - &br_color); + self.add_color_rectangle(&tl_rect, &tl_color, resource_cache, frame_id); + self.add_color_rectangle(&br_rect, &br_color, resource_cache, frame_id); } _ => { - self.add_color_rectangle(rect, - resource_cache, - color); + self.add_color_rectangle(rect, color, resource_cache, frame_id); } } } @@ -1125,6 +1147,7 @@ impl<'a> BatchBuilder<'a> { outer_radius: &Size2D, inner_radius: &Size2D, resource_cache: &ResourceCache, + frame_id: FrameId, rotation_angle: BasicRotationAngle, device_pixel_ratio: f32) { if color0.a <= 0.0 && color1.a <= 0.0 { @@ -1162,6 +1185,7 @@ impl<'a> BatchBuilder<'a> { outer_radius, inner_radius, resource_cache, + frame_id, rotation_angle, device_pixel_ratio); self.add_solid_border_corner(&inner_corner_rect, @@ -1171,19 +1195,16 @@ impl<'a> BatchBuilder<'a> { outer_radius, inner_radius, resource_cache, + frame_id, rotation_angle, device_pixel_ratio); // Draw the solid parts: if util::rect_is_well_formed_and_nonempty(&color0_rect) { - self.add_color_rectangle(&color0_rect, - resource_cache, - &color0_outer) + self.add_color_rectangle(&color0_rect, &color0_outer, resource_cache, frame_id) } if util::rect_is_well_formed_and_nonempty(&color1_rect) { - self.add_color_rectangle(&color1_rect, - resource_cache, - &color1_outer) + self.add_color_rectangle(&color1_rect, &color1_outer, resource_cache, frame_id) } } BorderStyle::Double => { @@ -1259,12 +1280,11 @@ impl<'a> BatchBuilder<'a> { outer_radius, &Size2D::new(0.0, 0.0), resource_cache, + frame_id, rotation_angle, device_pixel_ratio); - self.add_color_rectangle(&outer_side_rect_1, - resource_cache, - &color0); + self.add_color_rectangle(&outer_side_rect_1, &color0, resource_cache, frame_id); self.add_solid_border_corner(&inner_corner_rect, radius_extent, @@ -1273,12 +1293,11 @@ impl<'a> BatchBuilder<'a> { &Size2D::new(0.0, 0.0), inner_radius, resource_cache, + frame_id, rotation_angle, device_pixel_ratio); - self.add_color_rectangle(&outer_side_rect_0, - resource_cache, - &color1); + self.add_color_rectangle(&outer_side_rect_0, &color1, resource_cache, frame_id); } _ => { self.add_solid_border_corner(corner_bounds, @@ -1288,6 +1307,7 @@ impl<'a> BatchBuilder<'a> { outer_radius, inner_radius, resource_cache, + frame_id, rotation_angle, device_pixel_ratio) } @@ -1302,6 +1322,7 @@ impl<'a> BatchBuilder<'a> { outer_radius: &Size2D, inner_radius: &Size2D, resource_cache: &ResourceCache, + frame_id: FrameId, rotation_angle: BasicRotationAngle, _device_pixel_ratio: f32) { // TODO: Check for zero width/height borders! @@ -1334,7 +1355,7 @@ impl<'a> BatchBuilder<'a> { None,//Some(rect_index), ImageFormat::A8) { Some(raster_item) => { - resource_cache.get_raster(&RasterItem::BorderRadius(raster_item)) + resource_cache.get_raster(&RasterItem::BorderRadius(raster_item), frame_id) } None => dummy_mask_image, }; @@ -1371,14 +1392,10 @@ impl<'a> BatchBuilder<'a> { // Draw the two solid rects. if util::rect_is_well_formed_and_nonempty(&color0_rect) { - self.add_color_rectangle(&color0_rect, - resource_cache, - color0) + self.add_color_rectangle(&color0_rect, color0, resource_cache, frame_id) } if util::rect_is_well_formed_and_nonempty(&color1_rect) { - self.add_color_rectangle(&color1_rect, - resource_cache, - color1) + self.add_color_rectangle(&color1_rect, color1, resource_cache, frame_id) } } @@ -1485,6 +1502,7 @@ impl<'a> BatchBuilder<'a> { rect: &Rect, info: &BorderDisplayItem, resource_cache: &ResourceCache, + frame_id: FrameId, device_pixel_ratio: f32) { // TODO: If any border segment is alpha, place all in alpha pass. // Is it ever worth batching at a per-segment level? @@ -1522,7 +1540,8 @@ impl<'a> BatchBuilder<'a> { RectSide::Left, &left_color, info.left.style, - resource_cache); + resource_cache, + frame_id); self.add_border_edge(&Rect::new(Point2D::new(tl_inner.x, tl_outer.y), Size2D::new(tr_inner.x - tl_inner.x, @@ -1530,14 +1549,16 @@ impl<'a> BatchBuilder<'a> { RectSide::Top, &top_color, info.top.style, - resource_cache); + resource_cache, + frame_id); self.add_border_edge(&Rect::new(Point2D::new(br_outer.x - right.width, tr_inner.y), Size2D::new(right.width, br_inner.y - tr_inner.y)), RectSide::Right, &right_color, info.right.style, - resource_cache); + resource_cache, + frame_id); self.add_border_edge(&Rect::new(Point2D::new(bl_inner.x, bl_outer.y - bottom.width), Size2D::new(br_inner.x - bl_inner.x, @@ -1545,7 +1566,8 @@ impl<'a> BatchBuilder<'a> { RectSide::Bottom, &bottom_color, info.bottom.style, - resource_cache); + resource_cache, + frame_id); // Corners self.add_border_corner(info.left.style, @@ -1559,6 +1581,7 @@ impl<'a> BatchBuilder<'a> { &radius.top_left, &info.top_left_inner_radius(), resource_cache, + frame_id, BasicRotationAngle::Upright, device_pixel_ratio); @@ -1573,6 +1596,7 @@ impl<'a> BatchBuilder<'a> { &radius.top_right, &info.top_right_inner_radius(), resource_cache, + frame_id, BasicRotationAngle::Clockwise90, device_pixel_ratio); @@ -1587,6 +1611,7 @@ impl<'a> BatchBuilder<'a> { &radius.bottom_right, &info.bottom_right_inner_radius(), resource_cache, + frame_id, BasicRotationAngle::Clockwise180, device_pixel_ratio); @@ -1601,6 +1626,7 @@ impl<'a> BatchBuilder<'a> { &radius.bottom_left, &info.bottom_left_inner_radius(), resource_cache, + frame_id, BasicRotationAngle::Clockwise270, device_pixel_ratio); } @@ -1617,6 +1643,7 @@ impl<'a> BatchBuilder<'a> { border_radius: f32, clip_mode: BoxShadowClipMode, resource_cache: &ResourceCache, + frame_id: FrameId, rotation_angle: BasicRotationAngle) { let corner_area_rect = Rect::new(*corner_area_top_left, @@ -1636,7 +1663,7 @@ impl<'a> BatchBuilder<'a> { inverted) { Some(raster_item) => { let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item) + resource_cache.get_raster(&raster_item, frame_id) } None => resource_cache.get_dummy_color_image(), }; @@ -1661,6 +1688,7 @@ impl<'a> BatchBuilder<'a> { border_radius: f32, clip_mode: BoxShadowClipMode, resource_cache: &ResourceCache, + frame_id: FrameId, rotation_angle: BasicRotationAngle) { if top_left.x >= bottom_right.x || top_left.y >= bottom_right.y { return @@ -1677,7 +1705,7 @@ impl<'a> BatchBuilder<'a> { inverted) { Some(raster_item) => { let raster_item = RasterItem::BoxShadow(raster_item); - resource_cache.get_raster(&raster_item) + resource_cache.get_raster(&raster_item, frame_id) } None => resource_cache.get_dummy_color_image(), }; diff --git a/src/frame.rs b/src/frame.rs index dbe96b0400..31d5d1ea5a 100644 --- a/src/frame.rs +++ b/src/frame.rs @@ -25,6 +25,9 @@ use util; use webrender_traits::{PipelineId, Epoch, ScrollPolicy, ScrollLayerId, StackingContext}; use webrender_traits::{FilterOp, ImageFormat, MixBlendMode, StackingLevel}; +#[derive(Copy, Clone, PartialEq, PartialOrd, Debug)] +pub struct FrameId(pub u32); + pub struct DrawListGroup { pub id: DrawListGroupId, @@ -331,6 +334,7 @@ pub struct Frame { next_draw_list_group_id: DrawListGroupId, draw_list_groups: HashMap>, root_scroll_layer_id: Option, + id: FrameId, } enum SceneItemKind<'a> { @@ -538,6 +542,7 @@ impl Frame { next_draw_list_group_id: DrawListGroupId(0), draw_list_groups: HashMap::with_hash_state(Default::default()), root_scroll_layer_id: None, + id: FrameId(0), } } @@ -559,6 +564,9 @@ impl Frame { old_layer_offsets.insert(layer_id, old_layer.scroll_offset); } + // Advance to the next frame. + self.id.0 += 1; + old_layer_offsets } @@ -1005,13 +1013,10 @@ impl Frame { self.update_texture_cache_and_build_raster_jobs(resource_cache); // Rasterize needed glyphs on worker threads - self.raster_glyphs(thread_pool, - resource_cache); + self.raster_glyphs(thread_pool, resource_cache); // Compile nodes that have become visible - self.compile_visible_nodes(thread_pool, - resource_cache, - device_pixel_ratio); + self.compile_visible_nodes(thread_pool, resource_cache, device_pixel_ratio); // Update the batch cache from newly compiled nodes self.update_batch_cache(); @@ -1022,6 +1027,8 @@ impl Frame { // Collect the visible batches into a frame let frame = self.collect_and_sort_visible_batches(resource_cache, device_pixel_ratio); + resource_cache.expire_old_resources(self.id); + frame } @@ -1079,21 +1086,22 @@ impl Frame { resource_cache: &mut ResourceCache) { let _pf = util::ProfileScope::new(" update_texture_cache_and_build_raster_jobs"); + let frame_id = self.id; 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); + resource_cache.add_resource_list(resource_list, frame_id); } } } } pub fn raster_glyphs(&mut self, - thread_pool: &mut scoped_threadpool::Pool, - resource_cache: &mut ResourceCache) { + thread_pool: &mut scoped_threadpool::Pool, + resource_cache: &mut ResourceCache) { let _pf = util::ProfileScope::new(" raster_glyphs"); - resource_cache.raster_pending_glyphs(thread_pool); + resource_cache.raster_pending_glyphs(thread_pool, self.id); } pub fn compile_visible_nodes(&mut self, @@ -1105,6 +1113,7 @@ impl Frame { let layers = &mut self.layers; let stacking_context_info = &self.stacking_context_info; let draw_list_groups = &self.draw_list_groups; + let frame_id = self.id; thread_pool.scoped(|scope| { for (_, layer) in layers { @@ -1113,6 +1122,7 @@ impl Frame { if node.is_visible && node.compiled_node.is_none() { scope.execute(move || { node.compile(resource_cache, + frame_id, device_pixel_ratio, stacking_context_info, draw_list_groups); diff --git a/src/node_compiler.rs b/src/node_compiler.rs index 338a715582..5995b40875 100644 --- a/src/node_compiler.rs +++ b/src/node_compiler.rs @@ -1,7 +1,7 @@ use aabbtree::AABBTreeNode; use batch::{BatchBuilder, VertexBuffer}; use fnv::FnvHasher; -use frame::DrawListGroup; +use frame::{DrawListGroup, FrameId}; use internal_types::{DrawListItemIndex, CompiledNode, StackingContextInfo}; use internal_types::{BatchList, StackingContextIndex}; use internal_types::{DrawListGroupId}; @@ -13,6 +13,7 @@ use webrender_traits::SpecificDisplayItem; pub trait NodeCompiler { fn compile(&mut self, resource_cache: &ResourceCache, + frame_id: FrameId, device_pixel_ratio: f32, stacking_context_info: &Vec, draw_list_groups: &HashMap>); @@ -21,6 +22,7 @@ pub trait NodeCompiler { impl NodeCompiler for AABBTreeNode { fn compile(&mut self, resource_cache: &ResourceCache, + frame_id: FrameId, device_pixel_ratio: f32, stacking_context_info: &Vec, draw_list_groups: &HashMap>) { @@ -67,14 +69,16 @@ impl NodeCompiler for AABBTreeNode { SpecificDisplayItem::WebGL(ref info) => { builder.add_webgl_rectangle(&display_item.rect, resource_cache, - &info.context_id); + &info.context_id, + frame_id); } SpecificDisplayItem::Image(ref info) => { builder.add_image(&display_item.rect, &info.stretch_size, info.image_key, info.image_rendering, - resource_cache); + resource_cache, + frame_id); } SpecificDisplayItem::Text(ref info) => { builder.add_text(&display_item.rect, @@ -84,18 +88,21 @@ impl NodeCompiler for AABBTreeNode { &info.color, &info.glyphs, resource_cache, + frame_id, device_pixel_ratio); } SpecificDisplayItem::Rectangle(ref info) => { builder.add_color_rectangle(&display_item.rect, + &info.color, resource_cache, - &info.color); + frame_id); } SpecificDisplayItem::Gradient(ref info) => { builder.add_gradient(&info.start_point, &info.end_point, &info.stops, - resource_cache); + resource_cache, + frame_id); } SpecificDisplayItem::BoxShadow(ref info) => { builder.add_box_shadow(&info.box_bounds, @@ -105,12 +112,14 @@ impl NodeCompiler for AABBTreeNode { info.spread_radius, info.border_radius, info.clip_mode, - resource_cache); + resource_cache, + frame_id); } SpecificDisplayItem::Border(ref info) => { builder.add_border(&display_item.rect, info, resource_cache, + frame_id, device_pixel_ratio); } } diff --git a/src/resource_cache.rs b/src/resource_cache.rs index 13cf229d15..d901072686 100644 --- a/src/resource_cache.rs +++ b/src/resource_cache.rs @@ -2,6 +2,7 @@ use app_units::Au; use device::{TextureFilter, TextureId}; use euclid::Size2D; use fnv::FnvHasher; +use frame::FrameId; use freelist::FreeList; use internal_types::{FontTemplate, GlyphKey, RasterItem}; use internal_types::{TextureUpdateList, DrawListId, DrawList}; @@ -11,8 +12,10 @@ use resource_list::ResourceList; use scoped_threadpool; use std::cell::RefCell; use std::collections::HashMap; -use std::collections::hash_map::Entry::{Occupied, Vacant}; +use std::collections::hash_map::Entry::{self, Occupied, Vacant}; use std::collections::hash_state::DefaultState; +use std::fmt::Debug; +use std::hash::Hash; use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT}; use std::sync::atomic::Ordering::SeqCst; use std::thread; @@ -44,10 +47,73 @@ struct CachedImageInfo { epoch: Epoch, } +pub struct ResourceClassCache { + resources: HashMap>, + last_access_times: HashMap>, +} + +impl ResourceClassCache where K: Clone + Hash + Eq + Debug, V: Resource { + fn new() -> ResourceClassCache { + ResourceClassCache { + resources: HashMap::with_hash_state(Default::default()), + last_access_times: HashMap::with_hash_state(Default::default()), + } + } + + fn contains_key(&self, key: &K) -> bool { + self.resources.contains_key(key) + } + + fn get(&self, key: &K, frame: FrameId) -> &V { + // This assert catches cases in which we accidentally request a resource that we forgot to + // mark as needed this frame. + debug_assert!(frame == *self.last_access_times + .get(key) + .expect("Didn't find the access time for a cached resource \ + with that ID!")); + self.resources.get(key).expect("Didn't find a cached resource with that ID!") + } + + fn insert(&mut self, key: K, value: V, frame: FrameId) { + self.last_access_times.insert(key.clone(), frame); + self.resources.insert(key, value); + } + + fn entry(&mut self, key: K, frame: FrameId) -> Entry { + self.last_access_times.insert(key.clone(), frame); + self.resources.entry(key) + } + + fn mark_as_needed(&mut self, key: &K, frame: FrameId) { + self.last_access_times.insert((*key).clone(), frame); + } + + fn expire_old_resources(&mut self, texture_cache: &mut TextureCache, frame_id: FrameId) { + let mut resources_to_destroy = vec![]; + for (key, this_frame_id) in self.last_access_times.iter() { + if *this_frame_id < frame_id { + resources_to_destroy.push((*key).clone()) + } + } + for key in resources_to_destroy { + let resource = + self.resources + .remove(&key) + .expect("Resource was in `last_access_times` but not in `resources`!"); + self.last_access_times.remove(&key); + if let Some(texture_cache_item_id) = resource.texture_cache_item_id() { + texture_cache.free(texture_cache_item_id) + } + } + } +} + pub struct ResourceCache { - cached_glyphs: HashMap, DefaultState>, - cached_rasters: HashMap>, - cached_images: HashMap<(ImageKey, ImageRendering), CachedImageInfo, DefaultState>, + cached_glyphs: ResourceClassCache>, + cached_rasters: ResourceClassCache, + cached_images: ResourceClassCache<(ImageKey, ImageRendering), CachedImageInfo>, + + // TODO(pcwalton): Figure out the lifecycle of these. webgl_textures: HashMap>, draw_lists: FreeList, @@ -87,9 +153,9 @@ impl ResourceCache { }); ResourceCache { - cached_glyphs: HashMap::with_hash_state(Default::default()), - cached_rasters: HashMap::with_hash_state(Default::default()), - cached_images: HashMap::with_hash_state(Default::default()), + cached_glyphs: ResourceClassCache::new(), + cached_rasters: ResourceClassCache::new(), + cached_images: ResourceClassCache::new(), webgl_textures: HashMap::with_hash_state(Default::default()), draw_lists: FreeList::new(), font_templates: HashMap::with_hash_state(Default::default()), @@ -156,7 +222,7 @@ impl ResourceCache { self.texture_cache.add_raw_update(texture_id, size); } - pub fn add_resource_list(&mut self, resource_list: &ResourceList) { + pub fn add_resource_list(&mut self, resource_list: &ResourceList, frame_id: FrameId) { // Update texture cache with any GPU generated procedural items. resource_list.for_each_raster(|raster_item| { if !self.cached_rasters.contains_key(raster_item) { @@ -164,8 +230,9 @@ impl ResourceCache { self.texture_cache.insert_raster_op(image_id, raster_item, self.device_pixel_ratio); - self.cached_rasters.insert(raster_item.clone(), image_id); + self.cached_rasters.insert(raster_item.clone(), image_id, frame_id); } + self.cached_rasters.mark_as_needed(raster_item, frame_id); }); // Update texture cache with any images that aren't yet uploaded to GPU. @@ -173,7 +240,7 @@ impl ResourceCache { let cached_images = &mut self.cached_images; let image_template = &self.image_templates[&image_key]; - match cached_images.entry((image_key, image_rendering)) { + match cached_images.entry((image_key, image_rendering), frame_id) { Occupied(entry) => { if entry.get().epoch != image_template.epoch { let image_id = entry.get().texture_cache_id; @@ -227,11 +294,13 @@ impl ResourceCache { result: None, }); } + self.cached_glyphs.mark_as_needed(glyph_key, frame_id); }); } pub fn raster_pending_glyphs(&mut self, - thread_pool: &mut scoped_threadpool::Pool) { + thread_pool: &mut scoped_threadpool::Pool, + frame_id: FrameId) { // Run raster jobs in parallel run_raster_jobs(thread_pool, &mut self.pending_raster_jobs, @@ -276,7 +345,7 @@ impl ResourceCache { } else { None }; - self.cached_glyphs.insert(job.glyph_key, image_id); + self.cached_glyphs.insert(job.glyph_key, image_id, frame_id); } } @@ -323,34 +392,41 @@ impl ResourceCache { } #[inline] - pub fn get_glyph(&self, glyph_key: &GlyphKey) -> Option<&TextureCacheItem> { - let image_id = self.cached_glyphs[glyph_key]; + pub fn get_glyph(&self, glyph_key: &GlyphKey, frame_id: FrameId) -> Option<&TextureCacheItem> { + let image_id = self.cached_glyphs.get(glyph_key, frame_id); image_id.map(|image_id| self.texture_cache.get(image_id)) } #[inline] pub fn get_image(&self, image_key: ImageKey, - image_rendering: ImageRendering) -> &TextureCacheItem { - let image_info = &self.cached_images[&(image_key, image_rendering)]; + image_rendering: ImageRendering, + frame_id: FrameId) + -> &TextureCacheItem { + let image_info = &self.cached_images.get(&(image_key, image_rendering), frame_id); self.texture_cache.get(image_info.texture_cache_id) } #[inline] - pub fn get_raster(&self, raster_item: &RasterItem) -> &TextureCacheItem { - let image_id = self.cached_rasters[raster_item]; - self.texture_cache.get(image_id) + pub fn get_raster(&self, raster_item: &RasterItem, frame_id: FrameId) -> &TextureCacheItem { + let image_id = self.cached_rasters.get(raster_item, frame_id); + self.texture_cache.get(*image_id) } #[inline] - pub fn get_webgl_texture(&self, - context_id: &WebGLContextId) -> TextureId { + pub fn get_webgl_texture(&self, context_id: &WebGLContextId) -> TextureId { self.webgl_textures.get(context_id).unwrap().clone() } pub fn device_pixel_ratio(&self) -> f32 { self.device_pixel_ratio } + + pub fn expire_old_resources(&mut self, frame_id: FrameId) { + self.cached_glyphs.expire_old_resources(&mut self.texture_cache, frame_id); + self.cached_rasters.expire_old_resources(&mut self.texture_cache, frame_id); + self.cached_images.expire_old_resources(&mut self.texture_cache, frame_id); + } } fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, @@ -388,3 +464,26 @@ fn run_raster_jobs(thread_pool: &mut scoped_threadpool::Pool, } }); } + +pub trait Resource { + fn texture_cache_item_id(&self) -> Option; +} + +impl Resource for TextureCacheItemId { + fn texture_cache_item_id(&self) -> Option { + Some(*self) + } +} + +impl Resource for Option { + fn texture_cache_item_id(&self) -> Option { + *self + } +} + +impl Resource for CachedImageInfo { + fn texture_cache_item_id(&self) -> Option { + Some(self.texture_cache_id) + } +} + diff --git a/src/texture_cache.rs b/src/texture_cache.rs index 18e5365a6f..4221192915 100644 --- a/src/texture_cache.rs +++ b/src/texture_cache.rs @@ -297,7 +297,6 @@ impl TexturePage { self.dirty = false; } -/* fn free(&mut self, rect: &Rect) { debug_assert!(self.allocations > 0); self.allocations -= 1; @@ -308,7 +307,7 @@ impl TexturePage { self.free_list.push(rect); self.dirty = true - }*/ + } } // TODO(gw): This is used to store data specific to glyphs. @@ -513,6 +512,18 @@ impl TextureCacheArena { //render_target_pages: Vec::new(), } } + + fn texture_page_for_id(&mut self, id: TextureId) -> &mut TexturePage { + for page in self.pages_a8.iter_mut().chain(self.pages_rgb8.iter_mut()) + .chain(self.pages_rgba8.iter_mut()) + .chain(self.alternate_pages_a8.iter_mut()) + .chain(self.alternate_pages_rgba8.iter_mut()) { + if page.texture_id == id { + return page + } + } + panic!("No texture page for ID {:?}", id) + } } pub struct TextureCache { @@ -1066,6 +1077,13 @@ impl TextureCache { self.items.get(id) } + pub fn free(&mut self, id: TextureCacheItemId) { + { + let item = self.items.get(id); + self.arena.texture_page_for_id(item.texture_id).free(&item.allocated_rect); + } + self.items.free(id) + } } fn texture_create_op(texture_size: u32, format: ImageFormat, mode: RenderTargetMode)