Bug 1607836 - use SmallVec for render task dependencies. r=gw
authorNicolas Silva <nsilva@mozilla.com>
Thu, 16 Jan 2020 21:18:32 +0000
changeset 510828 db0378df4c6616330779c46cd830dd2f76f53b1b
parent 510827 a2e37e6c3ef6f40e27476400eec9d46fc65b0d89
child 510829 dfe4fa983d2b1eaaa3db2141a5e1c0f5ae58d5d7
push id37036
push userdvarga@mozilla.com
push dateTue, 21 Jan 2020 00:17:43 +0000
treeherdermozilla-central@83b429758070 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersgw
bugs1607836
milestone74.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
Bug 1607836 - use SmallVec for render task dependencies. r=gw 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. Differential Revision: https://phabricator.services.mozilla.com/D60151
gfx/wr/webrender/src/render_task.rs
gfx/wr/webrender/src/render_task_graph.rs
--- 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);