| author | Nicolas Silva <nsilva@mozilla.com> |
| Thu, 16 Jan 2020 21:18:32 +0000 | |
| changeset 510828 | db0378df4c6616330779c46cd830dd2f76f53b1b |
| parent 510827 | a2e37e6c3ef6f40e27476400eec9d46fc65b0d89 |
| child 510829 | dfe4fa983d2b1eaaa3db2141a5e1c0f5ae58d5d7 |
| push id | 37036 |
| push user | dvarga@mozilla.com |
| push date | Tue, 21 Jan 2020 00:17:43 +0000 |
| treeherder | mozilla-central@83b429758070 [default view] [failures only] |
| perfherder | [talos] [build metrics] [platform microbench] (compared to previous push) |
| reviewers | gw |
| bugs | 1607836 |
| milestone | 74.0a1 |
| first release with | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| last release without | nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
|
| gfx/wr/webrender/src/render_task.rs | file | annotate | diff | comparison | revisions | |
| gfx/wr/webrender/src/render_task_graph.rs | file | annotate | diff | comparison | revisions |
--- a/gfx/wr/webrender/src/render_task.rs +++ b/gfx/wr/webrender/src/render_task.rs @@ -18,16 +18,17 @@ use crate::prim_store::image::ImageCache use crate::prim_store::gradient::{GRADIENT_FP_STOPS, GradientStopKey}; #[cfg(feature = "debugger")] use crate::print_tree::{PrintTreePrinter}; use crate::resource_cache::ResourceCache; use std::{usize, f32, i32, u32}; use crate::render_target::{RenderTargetIndex, RenderTargetKind}; use crate::render_task_graph::{RenderTaskGraph, RenderTaskId}; use crate::render_task_cache::{RenderTaskCacheKey, RenderTaskCacheKeyKind}; +use smallvec::SmallVec; const RENDER_TASK_SIZE_SANITY_CHECK: i32 = 16000; const FLOATS_PER_RENDER_TASK_INFO: usize = 8; pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0; pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8; fn render_task_sanity_check(size: &DeviceIntSize) { if size.width > RENDER_TASK_SIZE_SANITY_CHECK || @@ -335,31 +336,37 @@ impl BlurTaskKey { // a lot of precision. const QUANTIZATION_FACTOR: f32 = 1024.0; let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32; let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32; BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y } } } +// The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that +// typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements +// avoids many tiny heap allocations in pages with a lot of text shadows and other +// types of render tasks. +pub type TaskDependencies = SmallVec<[RenderTaskId;2]>; + #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pub struct RenderTask { pub location: RenderTaskLocation, - pub children: Vec<RenderTaskId>, + pub children: TaskDependencies, pub kind: RenderTaskKind, pub clear_mode: ClearMode, pub saved_index: Option<SavedTargetIndex>, } impl RenderTask { #[inline] pub fn with_dynamic_location( size: DeviceIntSize, - children: Vec<RenderTaskId>, + children: TaskDependencies, kind: RenderTaskKind, clear_mode: ClearMode, ) -> Self { render_task_sanity_check(&size); RenderTask { location: RenderTaskLocation::Dynamic(None, size), children, @@ -368,17 +375,17 @@ impl RenderTask { saved_index: None, } } #[cfg(test)] pub fn new_test( target: RenderTargetKind, location: RenderTaskLocation, - children: Vec<RenderTaskId>, + children: TaskDependencies, ) -> Self { RenderTask { location, children, kind: RenderTaskKind::Test(target), clear_mode: ClearMode::Transparent, saved_index: None, } @@ -404,17 +411,17 @@ impl RenderTask { render_task_sanity_check(&size); let can_merge = size.width as f32 >= unclipped_size.width && size.height as f32 >= unclipped_size.height; RenderTask { location, - children: Vec::new(), + children: TaskDependencies::new(), kind: RenderTaskKind::Picture(PictureTask { pic_index, content_origin, can_merge, uv_rect_handle: GpuCacheHandle::new(), uv_rect_kind, surface_spatial_node_index, device_pixel_scale, @@ -430,31 +437,31 @@ impl RenderTask { size: DeviceIntSize, stops: [GradientStopKey; GRADIENT_FP_STOPS], orientation: LineOrientation, start_point: f32, end_point: f32, ) -> Self { RenderTask::with_dynamic_location( size, - Vec::new(), + TaskDependencies::new(), RenderTaskKind::Gradient(GradientTask { stops, orientation, start_point, end_point, }), ClearMode::DontCare, ) } pub fn new_readback(screen_rect: DeviceIntRect) -> Self { RenderTask::with_dynamic_location( screen_rect.size, - Vec::new(), + TaskDependencies::new(), RenderTaskKind::Readback(screen_rect), ClearMode::Transparent, ) } pub fn new_blit( size: DeviceIntSize, source: BlitSource, @@ -468,18 +475,18 @@ impl RenderTask { source: BlitSource, ) -> Self { // If this blit uses a render task as a source, // ensure it's added as a child task. This will // ensure it gets allocated in the correct pass // and made available as an input when this task // executes. let children = match source { - BlitSource::RenderTask { task_id } => vec![task_id], - BlitSource::Image { .. } => vec![], + BlitSource::RenderTask { task_id } => smallvec![task_id], + BlitSource::Image { .. } => smallvec![], }; RenderTask::with_dynamic_location( padded_size, children, RenderTaskKind::Blit(BlitTask { source, padding, @@ -492,17 +499,17 @@ impl RenderTask { size: DeviceIntSize, style: LineStyle, orientation: LineOrientation, wavy_line_thickness: f32, local_size: LayoutSize, ) -> Self { RenderTask::with_dynamic_location( size, - Vec::new(), + TaskDependencies::new(), RenderTaskKind::LineDecoration(LineDecorationTask { style, orientation, wavy_line_thickness, local_size, }), ClearMode::Transparent, ) @@ -600,17 +607,17 @@ impl RenderTask { let clear_mode = if needs_clear { ClearMode::One } else { ClearMode::DontCare }; RenderTask::with_dynamic_location( outer_rect.size, - vec![], + smallvec![], RenderTaskKind::CacheMask(CacheMaskTask { actual_rect: outer_rect, clip_node_range, root_spatial_node_index, device_pixel_scale, }), clear_mode, ) @@ -626,17 +633,17 @@ impl RenderTask { let clear_mode = if fb_config.gpu_supports_fast_clears { ClearMode::One } else { ClearMode::DontCare }; RenderTask::with_dynamic_location( size, - Vec::new(), + TaskDependencies::new(), RenderTaskKind::ClipRegion(ClipRegionTask { clip_data_address, local_pos, device_pixel_scale, }), clear_mode, ) } @@ -737,32 +744,32 @@ impl RenderTask { None => None, }; let blur_region = blur_region / (scale_factor as i32); let blur_task_id = cached_task.unwrap_or_else(|| { let blur_task_v = RenderTask::with_dynamic_location( adjusted_blur_target_size, - vec![downscaling_src_task_id], + smallvec![downscaling_src_task_id], RenderTaskKind::VerticalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation.height, target_kind, uv_rect_handle: GpuCacheHandle::new(), blur_region, uv_rect_kind, }), clear_mode, ); let blur_task_v_id = render_tasks.add(blur_task_v); let blur_task_h = RenderTask::with_dynamic_location( adjusted_blur_target_size, - vec![blur_task_v_id], + smallvec![blur_task_v_id], RenderTaskKind::HorizontalBlur(BlurTask { blur_std_deviation: adjusted_blur_std_deviation.width, target_kind, uv_rect_handle: GpuCacheHandle::new(), blur_region, uv_rect_kind, }), clear_mode, @@ -779,17 +786,17 @@ impl RenderTask { } pub fn new_border_segment( size: DeviceIntSize, instances: Vec<BorderInstance>, ) -> Self { RenderTask::with_dynamic_location( size, - Vec::new(), + TaskDependencies::new(), RenderTaskKind::Border(BorderTask { instances, }), ClearMode::Transparent, ) } pub fn new_scaling( @@ -810,18 +817,18 @@ impl RenderTask { pub fn new_scaling_with_padding( source: BlitSource, render_tasks: &mut RenderTaskGraph, target_kind: RenderTargetKind, padded_size: DeviceIntSize, padding: DeviceIntSideOffsets, ) -> Self { let (uv_rect_kind, children, image) = match source { - BlitSource::RenderTask { task_id } => (render_tasks[task_id].uv_rect_kind(), vec![task_id], None), - BlitSource::Image { key } => (UvRectKind::Rect, vec![], Some(key)), + BlitSource::RenderTask { task_id } => (render_tasks[task_id].uv_rect_kind(), smallvec![task_id], None), + BlitSource::Image { key } => (UvRectKind::Rect, smallvec![], Some(key)), }; RenderTask::with_dynamic_location( padded_size, children, RenderTaskKind::Scaling(ScalingTask { target_kind, image, @@ -860,26 +867,26 @@ impl RenderTask { let (mut task_id, input_color_space) = match input.to_index(cur_index) { Some(index) => (outputs[index], filter_primitives[index].color_space), None => (original, ColorSpace::Srgb), }; match (input_color_space, color_space) { (ColorSpace::Srgb, ColorSpace::LinearRgb) => { let task = RenderTask::new_svg_filter_primitive( - vec![task_id], + smallvec![task_id], content_size, uv_rect_kind, SvgFilterInfo::SrgbToLinear, ); task_id = render_tasks.add(task); }, (ColorSpace::LinearRgb, ColorSpace::Srgb) => { let task = RenderTask::new_svg_filter_primitive( - vec![task_id], + smallvec![task_id], content_size, uv_rect_kind, SvgFilterInfo::LinearToSrgb, ); task_id = render_tasks.add(task); }, _ => {}, } @@ -919,26 +926,26 @@ impl RenderTask { render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); let task = RenderTask::new_svg_filter_primitive( - vec![input_1_task_id, input_2_task_id], + smallvec![input_1_task_id, input_2_task_id], content_size, uv_rect_kind, SvgFilterInfo::Blend(blend.mode), ); render_tasks.add(task) }, FilterPrimitiveKind::Flood(ref flood) => { let task = RenderTask::new_svg_filter_primitive( - vec![], + smallvec![], content_size, uv_rect_kind, SvgFilterInfo::Flood(flood.color), ); render_tasks.add(task) } FilterPrimitiveKind::Blur(ref blur) => { let blur_std_deviation = blur.radius * device_pixel_scale.0; @@ -949,17 +956,17 @@ impl RenderTask { cur_index, &outputs, original_task_id, primitive.color_space ); // TODO: This is a hack to ensure that a blur task's input is always in the blur's previous pass. let svg_task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Identity, ); RenderTask::new_blur( DeviceSize::new(blur_std_deviation, blur_std_deviation), render_tasks.add(svg_task), @@ -977,17 +984,17 @@ impl RenderTask { render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); let task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Opacity(opacity.opacity), ); render_tasks.add(task) } FilterPrimitiveKind::ColorMatrix(ref color_matrix) => { let input_task_id = get_task_input( @@ -996,17 +1003,17 @@ impl RenderTask { render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); let task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::ColorMatrix(Box::new(color_matrix.matrix)), ); render_tasks.add(task) } FilterPrimitiveKind::DropShadow(ref drop_shadow) => { let input_task_id = get_task_input( @@ -1018,17 +1025,17 @@ impl RenderTask { original_task_id, primitive.color_space ); let blur_std_deviation = drop_shadow.shadow.blur_radius * device_pixel_scale.0; let offset = drop_shadow.shadow.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale; let offset_task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Offset(offset), ); let offset_task_id = render_tasks.add(offset_task); let blur_task_id = RenderTask::new_blur( DeviceSize::new(blur_std_deviation, blur_std_deviation), @@ -1036,17 +1043,17 @@ impl RenderTask { render_tasks, RenderTargetKind::Color, ClearMode::Transparent, None, content_size, ); let task = RenderTask::new_svg_filter_primitive( - vec![input_task_id, blur_task_id], + smallvec![input_task_id, blur_task_id], content_size, uv_rect_kind, SvgFilterInfo::DropShadow(drop_shadow.shadow.color), ); render_tasks.add(task) } FilterPrimitiveKind::ComponentTransfer(ref component_transfer) => { let input_task_id = get_task_input( @@ -1060,17 +1067,17 @@ impl RenderTask { ); let filter_data = &filter_datas[cur_filter_data]; cur_filter_data += 1; if filter_data.is_identity() { input_task_id } else { let task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::ComponentTransfer(filter_data.clone()), ); render_tasks.add(task) } } FilterPrimitiveKind::Offset(ref info) => { @@ -1081,17 +1088,17 @@ impl RenderTask { cur_index, &outputs, original_task_id, primitive.color_space ); let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale; let offset_task = RenderTask::new_svg_filter_primitive( - vec![input_task_id], + smallvec![input_task_id], content_size, uv_rect_kind, SvgFilterInfo::Offset(offset), ); render_tasks.add(offset_task) } FilterPrimitiveKind::Composite(info) => { let input_1_task_id = get_task_input( @@ -1109,46 +1116,46 @@ impl RenderTask { render_tasks, cur_index, &outputs, original_task_id, primitive.color_space ); let task = RenderTask::new_svg_filter_primitive( - vec![input_1_task_id, input_2_task_id], + smallvec![input_1_task_id, input_2_task_id], content_size, uv_rect_kind, SvgFilterInfo::Composite(info.operator), ); render_tasks.add(task) } }; outputs.push(render_task_id); } // The output of a filter is the output of the last primitive in the chain. let mut render_task_id = *outputs.last().unwrap(); // Convert to sRGB if needed if filter_primitives.last().unwrap().color_space == ColorSpace::LinearRgb { let task = RenderTask::new_svg_filter_primitive( - vec![render_task_id], + smallvec![render_task_id], content_size, uv_rect_kind, SvgFilterInfo::LinearToSrgb, ); render_task_id = render_tasks.add(task); } render_task_id } pub fn new_svg_filter_primitive( - tasks: Vec<RenderTaskId>, + tasks: TaskDependencies, target_size: DeviceIntSize, uv_rect_kind: UvRectKind, info: SvgFilterInfo, ) -> Self { RenderTask::with_dynamic_location( target_size, tasks, RenderTaskKind::SvgFilter(SvgFilterTask {
--- a/gfx/wr/webrender/src/render_task_graph.rs +++ b/gfx/wr/webrender/src/render_task_graph.rs @@ -687,16 +687,18 @@ fn dump_task_dependency_link( .fill(Fill::None) .stroke(Stroke::Color(rgb(100, 100, 100), 3.0)) ).unwrap(); } } #[cfg(test)] use euclid::{size2, rect}; +#[cfg(test)] +use smallvec::SmallVec; #[cfg(test)] fn dyn_location(w: i32, h: i32) -> RenderTaskLocation { RenderTaskLocation::Dynamic(None, size2(w, h)) } #[test] fn diamond_task_graph() { @@ -708,24 +710,24 @@ fn diamond_task_graph() { // \ / // [b2] let color = RenderTargetKind::Color; let counters = RenderTaskGraphCounters::new(); let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - let a = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new())); - let b1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a])); - let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a])); + let a = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let b1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a])); + let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a])); let main_pic = tasks.add(RenderTask::new_test( color, RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), - vec![b1, b2], + smallvec![b1, b2], )); let initial_number_of_tasks = tasks.tasks.len(); let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); // We should not have added any blits. assert_eq!(tasks.tasks.len(), initial_number_of_tasks); @@ -745,44 +747,44 @@ fn blur_task_graph() { // This test simulates a complicated shadow stack effect with target allocation // conflicts to resolve. let color = RenderTargetKind::Color; let counters = RenderTaskGraphCounters::new(); let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - let pic = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new())); - let scale1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![pic])); - let scale2 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![scale1])); - let scale3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![scale2])); - let scale4 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale3])); + let pic = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let scale1 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![pic])); + let scale2 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), smallvec![scale1])); + let scale3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), smallvec![scale2])); + let scale4 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale3])); - let vblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale4])); - let hblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![vblur1])); + let vblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale4])); + let hblur1 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), smallvec![vblur1])); - let vblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![scale4])); - let hblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), vec![vblur2])); + let vblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), smallvec![scale4])); + let hblur2 = tasks.add(RenderTask::new_test(color, dyn_location(40, 40), smallvec![vblur2])); // Insert a task that is an even number of passes away from its dependency. // This means the source and destination are on the same target and we have to resolve // this conflict by automatically inserting a blit task. - let vblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![scale3])); - let hblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), vec![vblur3])); + let vblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), smallvec![scale3])); + let hblur3 = tasks.add(RenderTask::new_test(color, dyn_location(80, 80), smallvec![vblur3])); // Insert a task that is an odd number > 1 of passes away from its dependency. // This should force us to mark the dependency "for saving" to keep its content valid // until the task can access it. - let vblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![scale2])); - let hblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), vec![vblur4])); + let vblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), smallvec![scale2])); + let hblur4 = tasks.add(RenderTask::new_test(color, dyn_location(160, 160), smallvec![vblur4])); let main_pic = tasks.add(RenderTask::new_test( color, RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), - vec![hblur1, hblur2, hblur3, hblur4], + smallvec![hblur1, hblur2, hblur3, hblur4], )); let initial_number_of_tasks = tasks.tasks.len(); let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); // We should have added a single blit task. assert_eq!(tasks.tasks.len(), initial_number_of_tasks + 1); @@ -830,27 +832,27 @@ fn culled_tasks() { // This test checks that tasks that do not contribute to the frame don't appear in the // generated passes. let color = RenderTargetKind::Color; let counters = RenderTaskGraphCounters::new(); let mut tasks = RenderTaskGraph::new(FrameId::first(), &counters); - let a1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new())); - let _a2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![a1])); + let a1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let _a2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![a1])); - let b1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), Vec::new())); - let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![b1])); - let _b3 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), vec![b2])); + let b1 = tasks.add(RenderTask::new_test(color, dyn_location(640, 640), SmallVec::new())); + let b2 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![b1])); + let _b3 = tasks.add(RenderTask::new_test(color, dyn_location(320, 320), smallvec![b2])); let main_pic = tasks.add(RenderTask::new_test( color, RenderTaskLocation::Fixed(rect(0, 0, 3200, 1800)), - vec![b2], + smallvec![b2], )); let initial_number_of_tasks = tasks.tasks.len(); let passes = tasks.generate_passes(Some(main_pic), size2(3200, 1800), true); // We should not have added any blits. assert_eq!(tasks.tasks.len(), initial_number_of_tasks);