1 /* This Source Code Form is subject to the terms of the Mozilla Public
2  * License, v. 2.0. If a copy of the MPL was not distributed with this
3  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
4 
5 use api::{CompositeOperator, FilterPrimitive, FilterPrimitiveInput, FilterPrimitiveKind};
6 use api::{LineStyle, LineOrientation, ClipMode, MixBlendMode, ColorF, ColorSpace};
7 use api::units::*;
8 use crate::batch::BatchFilter;
9 use crate::clip::{ClipDataStore, ClipItemKind, ClipStore, ClipNodeRange};
10 use crate::spatial_tree::SpatialNodeIndex;
11 use crate::filterdata::SFilterData;
12 use crate::frame_builder::FrameBuilderConfig;
13 use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
14 use crate::gpu_types::{BorderInstance, ImageSource, UvRectKind};
15 use crate::internal_types::{CacheTextureId, FastHashMap, TextureSource, Swizzle};
16 use crate::picture::{ResolvedSurfaceTexture, SurfaceInfo};
17 use crate::prim_store::{ClipData, PictureIndex};
18 use crate::prim_store::gradient::{
19     FastLinearGradientTask, RadialGradientTask,
20     ConicGradientTask, LinearGradientTask,
21 };
22 use crate::resource_cache::{ResourceCache, ImageRequest};
23 use std::{usize, f32, i32, u32};
24 use crate::render_target::RenderTargetKind;
25 use crate::render_task_graph::{PassId, RenderTaskId, RenderTaskGraphBuilder};
26 use crate::render_task_cache::{RenderTaskCacheEntryHandle, RenderTaskCacheKey, RenderTaskCacheKeyKind, RenderTaskParent};
27 use smallvec::SmallVec;
28 
29 const FLOATS_PER_RENDER_TASK_INFO: usize = 8;
30 pub const MAX_RENDER_TASK_SIZE: i32 = 16384;
31 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
32 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 8;
33 
render_task_sanity_check(size: &DeviceIntSize)34 fn render_task_sanity_check(size: &DeviceIntSize) {
35     if size.width > MAX_RENDER_TASK_SIZE ||
36         size.height > MAX_RENDER_TASK_SIZE {
37         error!("Attempting to create a render task of size {}x{}", size.width, size.height);
38         panic!();
39     }
40 }
41 
42 #[derive(Debug, Copy, Clone, PartialEq)]
43 #[repr(C)]
44 #[cfg_attr(feature = "capture", derive(Serialize))]
45 #[cfg_attr(feature = "replay", derive(Deserialize))]
46 pub struct RenderTaskAddress(pub u16);
47 
48 impl Into<RenderTaskAddress> for RenderTaskId {
into(self) -> RenderTaskAddress49     fn into(self) -> RenderTaskAddress {
50         RenderTaskAddress(self.index as u16)
51     }
52 }
53 
54 /// A render task location that targets a persistent output buffer which
55 /// will be retained over multiple frames.
56 #[derive(Clone, Debug, Eq, PartialEq, Hash)]
57 #[cfg_attr(feature = "capture", derive(Serialize))]
58 #[cfg_attr(feature = "replay", derive(Deserialize))]
59 pub enum StaticRenderTaskSurface {
60     /// The output of the `RenderTask` will be persisted beyond this frame, and
61     /// thus should be drawn into the `TextureCache`.
62     TextureCache {
63         /// Which texture in the texture cache should be drawn into.
64         texture: CacheTextureId,
65         /// What format this texture cache surface is
66         target_kind: RenderTargetKind,
67     },
68     /// Only used as a source for render tasks, can be any texture including an
69     /// external one.
70     ReadOnly {
71         source: TextureSource,
72     },
73     /// This render task will be drawn to a picture cache texture that is
74     /// persisted between both frames and scenes, if the content remains valid.
75     PictureCache {
76         /// Describes either a WR texture or a native OS compositor target
77         surface: ResolvedSurfaceTexture,
78     },
79 }
80 
81 /// Identifies the output buffer location for a given `RenderTask`.
82 #[derive(Clone, Debug)]
83 #[cfg_attr(feature = "capture", derive(Serialize))]
84 #[cfg_attr(feature = "replay", derive(Deserialize))]
85 pub enum RenderTaskLocation {
86     // Towards the beginning of the frame, most task locations are typically not
87     // known yet, in which case they are set to one of the following variants:
88 
89     /// A dynamic task that has not yet been allocated a texture and rect.
90     Unallocated {
91         /// Requested size of this render task
92         size: DeviceIntSize,
93     },
94     /// Will be replaced by a Static location after the texture cache update.
95     CacheRequest {
96         size: DeviceIntSize,
97     },
98 
99     // Before batching begins, we expect that locations have been resolved to
100     // one of the following variants:
101 
102     /// The `RenderTask` should be drawn to a target provided by the atlas
103     /// allocator. This is the most common case.
104     Dynamic {
105         /// Texture that this task was allocated to render on
106         texture_id: CacheTextureId,
107         /// Rectangle in the texture this task occupies
108         rect: DeviceIntRect,
109     },
110     /// A task that is output to a persistent / retained target.
111     Static {
112         /// Target to draw to
113         surface: StaticRenderTaskSurface,
114         /// Rectangle in the texture this task occupies
115         rect: DeviceIntRect,
116     },
117 }
118 
119 impl RenderTaskLocation {
120     /// Returns true if this is a dynamic location.
is_dynamic(&self) -> bool121     pub fn is_dynamic(&self) -> bool {
122         match *self {
123             RenderTaskLocation::Dynamic { .. } => true,
124             _ => false,
125         }
126     }
127 
size(&self) -> DeviceIntSize128     pub fn size(&self) -> DeviceIntSize {
129         match self {
130             RenderTaskLocation::Unallocated { size } => *size,
131             RenderTaskLocation::Dynamic { rect, .. } => rect.size(),
132             RenderTaskLocation::Static { rect, .. } => rect.size(),
133             RenderTaskLocation::CacheRequest { size } => *size,
134         }
135     }
136 }
137 
138 #[derive(Debug)]
139 #[cfg_attr(feature = "capture", derive(Serialize))]
140 #[cfg_attr(feature = "replay", derive(Deserialize))]
141 pub struct CachedTask {
142     pub target_kind: RenderTargetKind,
143 }
144 
145 #[derive(Debug)]
146 #[cfg_attr(feature = "capture", derive(Serialize))]
147 #[cfg_attr(feature = "replay", derive(Deserialize))]
148 pub struct CacheMaskTask {
149     pub actual_rect: DeviceRect,
150     pub root_spatial_node_index: SpatialNodeIndex,
151     pub clip_node_range: ClipNodeRange,
152     pub device_pixel_scale: DevicePixelScale,
153     pub clear_to_one: bool,
154 }
155 
156 #[derive(Debug)]
157 #[cfg_attr(feature = "capture", derive(Serialize))]
158 #[cfg_attr(feature = "replay", derive(Deserialize))]
159 pub struct ClipRegionTask {
160     pub local_pos: LayoutPoint,
161     pub device_pixel_scale: DevicePixelScale,
162     pub clip_data: ClipData,
163     pub clear_to_one: bool,
164 }
165 
166 #[cfg_attr(feature = "capture", derive(Serialize))]
167 #[cfg_attr(feature = "replay", derive(Deserialize))]
168 pub struct PictureTask {
169     pub pic_index: PictureIndex,
170     pub can_merge: bool,
171     pub content_origin: DevicePoint,
172     pub surface_spatial_node_index: SpatialNodeIndex,
173     pub device_pixel_scale: DevicePixelScale,
174     pub batch_filter: Option<BatchFilter>,
175     pub scissor_rect: Option<DeviceIntRect>,
176     pub valid_rect: Option<DeviceIntRect>,
177 }
178 
179 #[derive(Debug)]
180 #[cfg_attr(feature = "capture", derive(Serialize))]
181 #[cfg_attr(feature = "replay", derive(Deserialize))]
182 pub struct BlurTask {
183     pub blur_std_deviation: f32,
184     pub target_kind: RenderTargetKind,
185     pub blur_region: DeviceIntSize,
186 }
187 
188 impl BlurTask {
189     // In order to do the blur down-scaling passes without introducing errors, we need the
190     // source of each down-scale pass to be a multuple of two. If need be, this inflates
191     // the source size so that each down-scale pass will sample correctly.
adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize192     pub fn adjusted_blur_source_size(original_size: DeviceSize, mut std_dev: DeviceSize) -> DeviceSize {
193         let mut adjusted_size = original_size;
194         let mut scale_factor = 1.0;
195         while std_dev.width > MAX_BLUR_STD_DEVIATION && std_dev.height > MAX_BLUR_STD_DEVIATION {
196             if adjusted_size.width < MIN_DOWNSCALING_RT_SIZE as f32 ||
197                adjusted_size.height < MIN_DOWNSCALING_RT_SIZE as f32 {
198                 break;
199             }
200             std_dev = std_dev * 0.5;
201             scale_factor *= 2.0;
202             adjusted_size = (original_size.to_f32() / scale_factor).ceil();
203         }
204 
205         adjusted_size * scale_factor
206     }
207 }
208 
209 #[derive(Debug)]
210 #[cfg_attr(feature = "capture", derive(Serialize))]
211 #[cfg_attr(feature = "replay", derive(Deserialize))]
212 pub struct ScalingTask {
213     pub target_kind: RenderTargetKind,
214     pub padding: DeviceIntSideOffsets,
215 }
216 
217 #[derive(Debug)]
218 #[cfg_attr(feature = "capture", derive(Serialize))]
219 #[cfg_attr(feature = "replay", derive(Deserialize))]
220 pub struct BorderTask {
221     pub instances: Vec<BorderInstance>,
222 }
223 
224 #[derive(Debug)]
225 #[cfg_attr(feature = "capture", derive(Serialize))]
226 #[cfg_attr(feature = "replay", derive(Deserialize))]
227 pub struct BlitTask {
228     pub source: RenderTaskId,
229 }
230 
231 #[derive(Debug)]
232 #[cfg_attr(feature = "capture", derive(Serialize))]
233 #[cfg_attr(feature = "replay", derive(Deserialize))]
234 pub struct LineDecorationTask {
235     pub wavy_line_thickness: f32,
236     pub style: LineStyle,
237     pub orientation: LineOrientation,
238     pub local_size: LayoutSize,
239 }
240 
241 #[derive(Debug)]
242 #[cfg_attr(feature = "capture", derive(Serialize))]
243 #[cfg_attr(feature = "replay", derive(Deserialize))]
244 pub enum SvgFilterInfo {
245     Blend(MixBlendMode),
246     Flood(ColorF),
247     LinearToSrgb,
248     SrgbToLinear,
249     Opacity(f32),
250     ColorMatrix(Box<[f32; 20]>),
251     DropShadow(ColorF),
252     Offset(DeviceVector2D),
253     ComponentTransfer(SFilterData),
254     Composite(CompositeOperator),
255     // TODO: This is used as a hack to ensure that a blur task's input is always in the blur's previous pass.
256     Identity,
257 }
258 
259 #[derive(Debug)]
260 #[cfg_attr(feature = "capture", derive(Serialize))]
261 #[cfg_attr(feature = "replay", derive(Deserialize))]
262 pub struct SvgFilterTask {
263     pub info: SvgFilterInfo,
264     pub extra_gpu_cache_handle: Option<GpuCacheHandle>,
265 }
266 
267 #[cfg_attr(feature = "capture", derive(Serialize))]
268 #[cfg_attr(feature = "replay", derive(Deserialize))]
269 pub struct ReadbackTask {
270     // The offset of the rect that needs to be read back, in the
271     // device space of the surface that will be read back from.
272     // If this is None, there is no readback surface available
273     // and this is a dummy (empty) readback.
274     pub readback_origin: Option<DevicePoint>,
275 }
276 
277 #[derive(Debug)]
278 #[cfg_attr(feature = "capture", derive(Serialize))]
279 #[cfg_attr(feature = "replay", derive(Deserialize))]
280 pub struct RenderTaskData {
281     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
282 }
283 
284 #[cfg_attr(feature = "capture", derive(Serialize))]
285 #[cfg_attr(feature = "replay", derive(Deserialize))]
286 pub enum RenderTaskKind {
287     Image(ImageRequest),
288     Cached(CachedTask),
289     Picture(PictureTask),
290     CacheMask(CacheMaskTask),
291     ClipRegion(ClipRegionTask),
292     VerticalBlur(BlurTask),
293     HorizontalBlur(BlurTask),
294     Readback(ReadbackTask),
295     Scaling(ScalingTask),
296     Blit(BlitTask),
297     Border(BorderTask),
298     LineDecoration(LineDecorationTask),
299     FastLinearGradient(FastLinearGradientTask),
300     LinearGradient(LinearGradientTask),
301     RadialGradient(RadialGradientTask),
302     ConicGradient(ConicGradientTask),
303     SvgFilter(SvgFilterTask),
304     #[cfg(test)]
305     Test(RenderTargetKind),
306 }
307 
308 impl RenderTaskKind {
is_a_rendering_operation(&self) -> bool309     pub fn is_a_rendering_operation(&self) -> bool {
310         match self {
311             &RenderTaskKind::Image(..) => false,
312             &RenderTaskKind::Cached(..) => false,
313             _ => true,
314         }
315     }
316 
as_str(&self) -> &'static str317     pub fn as_str(&self) -> &'static str {
318         match *self {
319             RenderTaskKind::Image(..) => "Image",
320             RenderTaskKind::Cached(..) => "Cached",
321             RenderTaskKind::Picture(..) => "Picture",
322             RenderTaskKind::CacheMask(..) => "CacheMask",
323             RenderTaskKind::ClipRegion(..) => "ClipRegion",
324             RenderTaskKind::VerticalBlur(..) => "VerticalBlur",
325             RenderTaskKind::HorizontalBlur(..) => "HorizontalBlur",
326             RenderTaskKind::Readback(..) => "Readback",
327             RenderTaskKind::Scaling(..) => "Scaling",
328             RenderTaskKind::Blit(..) => "Blit",
329             RenderTaskKind::Border(..) => "Border",
330             RenderTaskKind::LineDecoration(..) => "LineDecoration",
331             RenderTaskKind::FastLinearGradient(..) => "FastLinearGradient",
332             RenderTaskKind::LinearGradient(..) => "LinearGradient",
333             RenderTaskKind::RadialGradient(..) => "RadialGradient",
334             RenderTaskKind::ConicGradient(..) => "ConicGradient",
335             RenderTaskKind::SvgFilter(..) => "SvgFilter",
336             #[cfg(test)]
337             RenderTaskKind::Test(..) => "Test",
338         }
339     }
340 
target_kind(&self) -> RenderTargetKind341     pub fn target_kind(&self) -> RenderTargetKind {
342         match *self {
343             RenderTaskKind::Image(..) |
344             RenderTaskKind::LineDecoration(..) |
345             RenderTaskKind::Readback(..) |
346             RenderTaskKind::Border(..) |
347             RenderTaskKind::FastLinearGradient(..) |
348             RenderTaskKind::LinearGradient(..) |
349             RenderTaskKind::RadialGradient(..) |
350             RenderTaskKind::ConicGradient(..) |
351             RenderTaskKind::Picture(..) |
352             RenderTaskKind::Blit(..) |
353             RenderTaskKind::SvgFilter(..) => {
354                 RenderTargetKind::Color
355             }
356 
357             RenderTaskKind::ClipRegion(..) |
358             RenderTaskKind::CacheMask(..) => {
359                 RenderTargetKind::Alpha
360             }
361 
362             RenderTaskKind::VerticalBlur(ref task_info) |
363             RenderTaskKind::HorizontalBlur(ref task_info) => {
364                 task_info.target_kind
365             }
366 
367             RenderTaskKind::Scaling(ref task_info) => {
368                 task_info.target_kind
369             }
370 
371             RenderTaskKind::Cached(ref task_info) => {
372                 task_info.target_kind
373             }
374 
375             #[cfg(test)]
376             RenderTaskKind::Test(kind) => kind,
377         }
378     }
379 
new_picture( size: DeviceIntSize, unclipped_size: DeviceSize, pic_index: PictureIndex, content_origin: DevicePoint, surface_spatial_node_index: SpatialNodeIndex, device_pixel_scale: DevicePixelScale, batch_filter: Option<BatchFilter>, scissor_rect: Option<DeviceIntRect>, valid_rect: Option<DeviceIntRect>, ) -> Self380     pub fn new_picture(
381         size: DeviceIntSize,
382         unclipped_size: DeviceSize,
383         pic_index: PictureIndex,
384         content_origin: DevicePoint,
385         surface_spatial_node_index: SpatialNodeIndex,
386         device_pixel_scale: DevicePixelScale,
387         batch_filter: Option<BatchFilter>,
388         scissor_rect: Option<DeviceIntRect>,
389         valid_rect: Option<DeviceIntRect>,
390     ) -> Self {
391         render_task_sanity_check(&size);
392 
393         let can_merge = size.width as f32 >= unclipped_size.width &&
394                         size.height as f32 >= unclipped_size.height;
395 
396         RenderTaskKind::Picture(PictureTask {
397             pic_index,
398             content_origin,
399             can_merge,
400             surface_spatial_node_index,
401             device_pixel_scale,
402             batch_filter,
403             scissor_rect,
404             valid_rect,
405         })
406     }
407 
new_readback( readback_origin: Option<DevicePoint>, ) -> Self408     pub fn new_readback(
409         readback_origin: Option<DevicePoint>,
410     ) -> Self {
411         RenderTaskKind::Readback(
412             ReadbackTask {
413                 readback_origin,
414             }
415         )
416     }
417 
new_line_decoration( style: LineStyle, orientation: LineOrientation, wavy_line_thickness: f32, local_size: LayoutSize, ) -> Self418     pub fn new_line_decoration(
419         style: LineStyle,
420         orientation: LineOrientation,
421         wavy_line_thickness: f32,
422         local_size: LayoutSize,
423     ) -> Self {
424         RenderTaskKind::LineDecoration(LineDecorationTask {
425             style,
426             orientation,
427             wavy_line_thickness,
428             local_size,
429         })
430     }
431 
new_border_segment( instances: Vec<BorderInstance>, ) -> Self432     pub fn new_border_segment(
433         instances: Vec<BorderInstance>,
434     ) -> Self {
435         RenderTaskKind::Border(BorderTask {
436             instances,
437         })
438     }
439 
new_rounded_rect_mask( local_pos: LayoutPoint, clip_data: ClipData, device_pixel_scale: DevicePixelScale, fb_config: &FrameBuilderConfig, ) -> Self440     pub fn new_rounded_rect_mask(
441         local_pos: LayoutPoint,
442         clip_data: ClipData,
443         device_pixel_scale: DevicePixelScale,
444         fb_config: &FrameBuilderConfig,
445     ) -> Self {
446         RenderTaskKind::ClipRegion(ClipRegionTask {
447             local_pos,
448             device_pixel_scale,
449             clip_data,
450             clear_to_one: fb_config.gpu_supports_fast_clears,
451         })
452     }
453 
new_mask( outer_rect: DeviceRect, clip_node_range: ClipNodeRange, root_spatial_node_index: SpatialNodeIndex, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, rg_builder: &mut RenderTaskGraphBuilder, clip_data_store: &mut ClipDataStore, device_pixel_scale: DevicePixelScale, fb_config: &FrameBuilderConfig, surfaces: &[SurfaceInfo], ) -> RenderTaskId454     pub fn new_mask(
455         outer_rect: DeviceRect,
456         clip_node_range: ClipNodeRange,
457         root_spatial_node_index: SpatialNodeIndex,
458         clip_store: &mut ClipStore,
459         gpu_cache: &mut GpuCache,
460         resource_cache: &mut ResourceCache,
461         rg_builder: &mut RenderTaskGraphBuilder,
462         clip_data_store: &mut ClipDataStore,
463         device_pixel_scale: DevicePixelScale,
464         fb_config: &FrameBuilderConfig,
465         surfaces: &[SurfaceInfo],
466     ) -> RenderTaskId {
467         // Step through the clip sources that make up this mask. If we find
468         // any box-shadow clip sources, request that image from the render
469         // task cache. This allows the blurred box-shadow rect to be cached
470         // in the texture cache across frames.
471         // TODO(gw): Consider moving this logic outside this function, especially
472         //           as we add more clip sources that depend on render tasks.
473         // TODO(gw): If this ever shows up in a profile, we could pre-calculate
474         //           whether a ClipSources contains any box-shadows and skip
475         //           this iteration for the majority of cases.
476         let task_size = outer_rect.size().to_i32();
477 
478         // If we have a potentially tiled clip mask, clear the mask area first. Otherwise,
479         // the first (primary) clip mask will overwrite all the clip mask pixels with
480         // blending disabled to set to the initial value.
481 
482         let clip_task_id = rg_builder.add().init(
483             RenderTask::new_dynamic(
484                 task_size,
485                 RenderTaskKind::CacheMask(CacheMaskTask {
486                     actual_rect: outer_rect,
487                     clip_node_range,
488                     root_spatial_node_index,
489                     device_pixel_scale,
490                     clear_to_one: fb_config.gpu_supports_fast_clears,
491                 }),
492             )
493         );
494 
495         for i in 0 .. clip_node_range.count {
496             let clip_instance = clip_store.get_instance_from_range(&clip_node_range, i);
497             let clip_node = &mut clip_data_store[clip_instance.handle];
498             match clip_node.item.kind {
499                 ClipItemKind::BoxShadow { ref mut source } => {
500                     let (cache_size, cache_key) = source.cache_key
501                         .as_ref()
502                         .expect("bug: no cache key set")
503                         .clone();
504                     let blur_radius_dp = cache_key.blur_radius_dp as f32;
505                     let device_pixel_scale = DevicePixelScale::new(cache_key.device_pixel_scale.to_f32_px());
506 
507                     // Request a cacheable render task with a blurred, minimal
508                     // sized box-shadow rect.
509                     source.render_task = Some(resource_cache.request_render_task(
510                         RenderTaskCacheKey {
511                             size: cache_size,
512                             kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
513                         },
514                         gpu_cache,
515                         rg_builder,
516                         None,
517                         false,
518                         RenderTaskParent::RenderTask(clip_task_id),
519                         surfaces,
520                         |rg_builder| {
521                             let clip_data = ClipData::rounded_rect(
522                                 source.minimal_shadow_rect.size(),
523                                 &source.shadow_radius,
524                                 ClipMode::Clip,
525                             );
526 
527                             // Draw the rounded rect.
528                             let mask_task_id = rg_builder.add().init(RenderTask::new_dynamic(
529                                 cache_size,
530                                 RenderTaskKind::new_rounded_rect_mask(
531                                     source.minimal_shadow_rect.min,
532                                     clip_data,
533                                     device_pixel_scale,
534                                     fb_config,
535                                 ),
536                             ));
537 
538                             // Blur it
539                             RenderTask::new_blur(
540                                 DeviceSize::new(blur_radius_dp, blur_radius_dp),
541                                 mask_task_id,
542                                 rg_builder,
543                                 RenderTargetKind::Alpha,
544                                 None,
545                                 cache_size,
546                             )
547                         }
548                     ));
549                 }
550                 ClipItemKind::Rectangle { .. } |
551                 ClipItemKind::RoundedRectangle { .. } |
552                 ClipItemKind::Image { .. } => {}
553             }
554         }
555 
556         clip_task_id
557     }
558 
559     // Write (up to) 8 floats of data specific to the type
560     // of render task that is provided to the GPU shaders
561     // via a vertex texture.
write_task_data( &self, target_rect: DeviceIntRect, ) -> RenderTaskData562     pub fn write_task_data(
563         &self,
564         target_rect: DeviceIntRect,
565     ) -> RenderTaskData {
566         // NOTE: The ordering and layout of these structures are
567         //       required to match both the GPU structures declared
568         //       in prim_shared.glsl, and also the uses in submit_batch()
569         //       in renderer.rs.
570         // TODO(gw): Maybe there's a way to make this stuff a bit
571         //           more type-safe. Although, it will always need
572         //           to be kept in sync with the GLSL code anyway.
573 
574         let data = match self {
575             RenderTaskKind::Picture(ref task) => {
576                 // Note: has to match `PICTURE_TYPE_*` in shaders
577                 [
578                     task.device_pixel_scale.0,
579                     task.content_origin.x,
580                     task.content_origin.y,
581                     0.0,
582                 ]
583             }
584             RenderTaskKind::CacheMask(ref task) => {
585                 [
586                     task.device_pixel_scale.0,
587                     task.actual_rect.min.x,
588                     task.actual_rect.min.y,
589                     0.0,
590                 ]
591             }
592             RenderTaskKind::ClipRegion(ref task) => {
593                 [
594                     task.device_pixel_scale.0,
595                     0.0,
596                     0.0,
597                     0.0,
598                 ]
599             }
600             RenderTaskKind::VerticalBlur(ref task) |
601             RenderTaskKind::HorizontalBlur(ref task) => {
602                 [
603                     task.blur_std_deviation,
604                     task.blur_region.width as f32,
605                     task.blur_region.height as f32,
606                     0.0,
607                 ]
608             }
609             RenderTaskKind::Image(..) |
610             RenderTaskKind::Cached(..) |
611             RenderTaskKind::Readback(..) |
612             RenderTaskKind::Scaling(..) |
613             RenderTaskKind::Border(..) |
614             RenderTaskKind::LineDecoration(..) |
615             RenderTaskKind::FastLinearGradient(..) |
616             RenderTaskKind::LinearGradient(..) |
617             RenderTaskKind::RadialGradient(..) |
618             RenderTaskKind::ConicGradient(..) |
619             RenderTaskKind::Blit(..) => {
620                 [0.0; 4]
621             }
622 
623 
624             RenderTaskKind::SvgFilter(ref task) => {
625                 match task.info {
626                     SvgFilterInfo::Opacity(opacity) => [opacity, 0.0, 0.0, 0.0],
627                     SvgFilterInfo::Offset(offset) => [offset.x, offset.y, 0.0, 0.0],
628                     _ => [0.0; 4]
629                 }
630             }
631 
632             #[cfg(test)]
633             RenderTaskKind::Test(..) => {
634                 [0.0; 4]
635             }
636         };
637 
638         RenderTaskData {
639             data: [
640                 target_rect.min.x as f32,
641                 target_rect.min.y as f32,
642                 target_rect.max.x as f32,
643                 target_rect.max.y as f32,
644                 data[0],
645                 data[1],
646                 data[2],
647                 data[3],
648             ]
649         }
650     }
651 
write_gpu_blocks( &mut self, gpu_cache: &mut GpuCache, )652     pub fn write_gpu_blocks(
653         &mut self,
654         gpu_cache: &mut GpuCache,
655     ) {
656         if let RenderTaskKind::SvgFilter(ref mut filter_task) = self {
657             match filter_task.info {
658                 SvgFilterInfo::ColorMatrix(ref matrix) => {
659                     let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
660                     if let Some(mut request) = gpu_cache.request(handle) {
661                         for i in 0..5 {
662                             request.push([matrix[i*4], matrix[i*4+1], matrix[i*4+2], matrix[i*4+3]]);
663                         }
664                     }
665                 }
666                 SvgFilterInfo::DropShadow(color) |
667                 SvgFilterInfo::Flood(color) => {
668                     let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
669                     if let Some(mut request) = gpu_cache.request(handle) {
670                         request.push(color.to_array());
671                     }
672                 }
673                 SvgFilterInfo::ComponentTransfer(ref data) => {
674                     let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
675                     if let Some(request) = gpu_cache.request(handle) {
676                         data.update(request);
677                     }
678                 }
679                 SvgFilterInfo::Composite(ref operator) => {
680                     if let CompositeOperator::Arithmetic(k_vals) = operator {
681                         let handle = filter_task.extra_gpu_cache_handle.get_or_insert_with(GpuCacheHandle::new);
682                         if let Some(mut request) = gpu_cache.request(handle) {
683                             request.push(*k_vals);
684                         }
685                     }
686                 }
687                 _ => {},
688             }
689         }
690     }
691 }
692 
693 /// In order to avoid duplicating the down-scaling and blur passes when a picture has several blurs,
694 /// we use a local (primitive-level) cache of the render tasks generated for a single shadowed primitive
695 /// in a single frame.
696 pub type BlurTaskCache = FastHashMap<BlurTaskKey, RenderTaskId>;
697 
698 /// Since we only use it within a single primitive, the key only needs to contain the down-scaling level
699 /// and the blur std deviation.
700 #[derive(Copy, Clone, Debug, PartialEq, Eq, Hash)]
701 pub enum BlurTaskKey {
702     DownScale(u32),
703     Blur { downscale_level: u32, stddev_x: u32, stddev_y: u32 },
704 }
705 
706 impl BlurTaskKey {
downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self707     fn downscale_and_blur(downscale_level: u32, blur_stddev: DeviceSize) -> Self {
708         // Quantise the std deviations and store it as integers to work around
709         // Eq and Hash's f32 allergy.
710         // The blur radius is rounded before RenderTask::new_blur so we don't need
711         // a lot of precision.
712         const QUANTIZATION_FACTOR: f32 = 1024.0;
713         let stddev_x = (blur_stddev.width * QUANTIZATION_FACTOR) as u32;
714         let stddev_y = (blur_stddev.height * QUANTIZATION_FACTOR) as u32;
715         BlurTaskKey::Blur { downscale_level, stddev_x, stddev_y }
716     }
717 }
718 
719 // The majority of render tasks have 0, 1 or 2 dependencies, except for pictures that
720 // typically have dozens to hundreds of dependencies. SmallVec with 2 inline elements
721 // avoids many tiny heap allocations in pages with a lot of text shadows and other
722 // types of render tasks.
723 pub type TaskDependencies = SmallVec<[RenderTaskId;2]>;
724 
725 #[cfg_attr(feature = "capture", derive(Serialize))]
726 #[cfg_attr(feature = "replay", derive(Deserialize))]
727 pub struct RenderTask {
728     pub location: RenderTaskLocation,
729     pub children: TaskDependencies,
730     pub kind: RenderTaskKind,
731 
732     // TODO(gw): These fields and perhaps others can become private once the
733     //           frame_graph / render_task source files are unified / cleaned up.
734     pub free_after: PassId,
735     pub render_on: PassId,
736 
737     /// The gpu cache handle for the render task's destination rect.
738     ///
739     /// Will be set to None if the render task is cached, in which case the texture cache
740     /// manages the handle.
741     pub uv_rect_handle: GpuCacheHandle,
742     pub cache_handle: Option<RenderTaskCacheEntryHandle>,
743     uv_rect_kind: UvRectKind,
744 }
745 
746 impl RenderTask {
new( location: RenderTaskLocation, kind: RenderTaskKind, ) -> Self747     pub fn new(
748         location: RenderTaskLocation,
749         kind: RenderTaskKind,
750     ) -> Self {
751         render_task_sanity_check(&location.size());
752 
753         RenderTask {
754             location,
755             children: TaskDependencies::new(),
756             kind,
757             free_after: PassId::MAX,
758             render_on: PassId::MIN,
759             uv_rect_handle: GpuCacheHandle::new(),
760             uv_rect_kind: UvRectKind::Rect,
761             cache_handle: None,
762         }
763     }
764 
new_dynamic( size: DeviceIntSize, kind: RenderTaskKind, ) -> Self765     pub fn new_dynamic(
766         size: DeviceIntSize,
767         kind: RenderTaskKind,
768     ) -> Self {
769         RenderTask::new(
770             RenderTaskLocation::Unallocated { size },
771             kind,
772         )
773     }
774 
with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self775     pub fn with_uv_rect_kind(mut self, uv_rect_kind: UvRectKind) -> Self {
776         self.uv_rect_kind = uv_rect_kind;
777         self
778     }
779 
new_image( size: DeviceIntSize, request: ImageRequest, ) -> Self780     pub fn new_image(
781         size: DeviceIntSize,
782         request: ImageRequest,
783     ) -> Self {
784         // Note: this is a special constructor for image render tasks that does not
785         // do the render task size sanity check. This is because with SWGL we purposefully
786         // avoid tiling large images. There is no upload with SWGL so whatever was
787         // successfully allocated earlier will be what shaders read, regardless of the size
788         // and copying into tiles would only slow things down.
789         // As a result we can run into very large images being added to the frame graph
790         // (this is covered by a few reftests on the CI).
791 
792         RenderTask {
793             location: RenderTaskLocation::CacheRequest { size, },
794             children: TaskDependencies::new(),
795             kind: RenderTaskKind::Image(request),
796             free_after: PassId::MAX,
797             render_on: PassId::MIN,
798             uv_rect_handle: GpuCacheHandle::new(),
799             uv_rect_kind: UvRectKind::Rect,
800             cache_handle: None,
801         }
802     }
803 
804 
805     #[cfg(test)]
new_test( location: RenderTaskLocation, target: RenderTargetKind, ) -> Self806     pub fn new_test(
807         location: RenderTaskLocation,
808         target: RenderTargetKind,
809     ) -> Self {
810         RenderTask {
811             location,
812             children: TaskDependencies::new(),
813             kind: RenderTaskKind::Test(target),
814             free_after: PassId::MAX,
815             render_on: PassId::MIN,
816             uv_rect_handle: GpuCacheHandle::new(),
817             uv_rect_kind: UvRectKind::Rect,
818             cache_handle: None,
819         }
820     }
821 
new_blit( size: DeviceIntSize, source: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, ) -> RenderTaskId822     pub fn new_blit(
823         size: DeviceIntSize,
824         source: RenderTaskId,
825         rg_builder: &mut RenderTaskGraphBuilder,
826     ) -> RenderTaskId {
827         // If this blit uses a render task as a source,
828         // ensure it's added as a child task. This will
829         // ensure it gets allocated in the correct pass
830         // and made available as an input when this task
831         // executes.
832 
833         let blit_task_id = rg_builder.add().init(RenderTask::new_dynamic(
834             size,
835             RenderTaskKind::Blit(BlitTask { source }),
836         ));
837 
838         rg_builder.add_dependency(blit_task_id, source);
839 
840         blit_task_id
841     }
842 
843     // Construct a render task to apply a blur to a primitive.
844     // The render task chain that is constructed looks like:
845     //
846     //    PrimitiveCacheTask: Draw the primitives.
847     //           ^
848     //           |
849     //    DownscalingTask(s): Each downscaling task reduces the size of render target to
850     //           ^            half. Also reduce the std deviation to half until the std
851     //           |            deviation less than 4.0.
852     //           |
853     //           |
854     //    VerticalBlurTask: Apply the separable vertical blur to the primitive.
855     //           ^
856     //           |
857     //    HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
858     //           |
859     //           +---- This is stored as the input task to the primitive shader.
860     //
new_blur( blur_std_deviation: DeviceSize, src_task_id: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, mut blur_cache: Option<&mut BlurTaskCache>, blur_region: DeviceIntSize, ) -> RenderTaskId861     pub fn new_blur(
862         blur_std_deviation: DeviceSize,
863         src_task_id: RenderTaskId,
864         rg_builder: &mut RenderTaskGraphBuilder,
865         target_kind: RenderTargetKind,
866         mut blur_cache: Option<&mut BlurTaskCache>,
867         blur_region: DeviceIntSize,
868     ) -> RenderTaskId {
869         // Adjust large std deviation value.
870         let mut adjusted_blur_std_deviation = blur_std_deviation;
871         let (blur_target_size, uv_rect_kind) = {
872             let src_task = rg_builder.get_task(src_task_id);
873             (src_task.location.size(), src_task.uv_rect_kind())
874         };
875         let mut adjusted_blur_target_size = blur_target_size;
876         let mut downscaling_src_task_id = src_task_id;
877         let mut scale_factor = 1.0;
878         let mut n_downscales = 1;
879         while adjusted_blur_std_deviation.width > MAX_BLUR_STD_DEVIATION &&
880               adjusted_blur_std_deviation.height > MAX_BLUR_STD_DEVIATION {
881             if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
882                adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
883                 break;
884             }
885             adjusted_blur_std_deviation = adjusted_blur_std_deviation * 0.5;
886             scale_factor *= 2.0;
887             adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
888 
889             let cached_task = match blur_cache {
890                 Some(ref mut cache) => cache.get(&BlurTaskKey::DownScale(n_downscales)).cloned(),
891                 None => None,
892             };
893 
894             downscaling_src_task_id = cached_task.unwrap_or_else(|| {
895                 RenderTask::new_scaling(
896                     downscaling_src_task_id,
897                     rg_builder,
898                     target_kind,
899                     adjusted_blur_target_size,
900                 )
901             });
902 
903             if let Some(ref mut cache) = blur_cache {
904                 cache.insert(BlurTaskKey::DownScale(n_downscales), downscaling_src_task_id);
905             }
906 
907             n_downscales += 1;
908         }
909 
910 
911         let blur_key = BlurTaskKey::downscale_and_blur(n_downscales, adjusted_blur_std_deviation);
912 
913         let cached_task = match blur_cache {
914             Some(ref mut cache) => cache.get(&blur_key).cloned(),
915             None => None,
916         };
917 
918         let blur_region = blur_region / (scale_factor as i32);
919 
920         let blur_task_id = cached_task.unwrap_or_else(|| {
921             let blur_task_v = rg_builder.add().init(RenderTask::new_dynamic(
922                 adjusted_blur_target_size,
923                 RenderTaskKind::VerticalBlur(BlurTask {
924                     blur_std_deviation: adjusted_blur_std_deviation.height,
925                     target_kind,
926                     blur_region,
927                 }),
928             ).with_uv_rect_kind(uv_rect_kind));
929             rg_builder.add_dependency(blur_task_v, downscaling_src_task_id);
930 
931             let task_id = rg_builder.add().init(RenderTask::new_dynamic(
932                 adjusted_blur_target_size,
933                 RenderTaskKind::HorizontalBlur(BlurTask {
934                     blur_std_deviation: adjusted_blur_std_deviation.width,
935                     target_kind,
936                     blur_region,
937                 }),
938             ).with_uv_rect_kind(uv_rect_kind));
939             rg_builder.add_dependency(task_id, blur_task_v);
940 
941             task_id
942         });
943 
944         if let Some(ref mut cache) = blur_cache {
945             cache.insert(blur_key, blur_task_id);
946         }
947 
948         blur_task_id
949     }
950 
new_scaling( src_task_id: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, size: DeviceIntSize, ) -> RenderTaskId951     pub fn new_scaling(
952         src_task_id: RenderTaskId,
953         rg_builder: &mut RenderTaskGraphBuilder,
954         target_kind: RenderTargetKind,
955         size: DeviceIntSize,
956     ) -> RenderTaskId {
957         Self::new_scaling_with_padding(
958             src_task_id,
959             rg_builder,
960             target_kind,
961             size,
962             DeviceIntSideOffsets::zero(),
963         )
964     }
965 
new_scaling_with_padding( source: RenderTaskId, rg_builder: &mut RenderTaskGraphBuilder, target_kind: RenderTargetKind, padded_size: DeviceIntSize, padding: DeviceIntSideOffsets, ) -> RenderTaskId966     pub fn new_scaling_with_padding(
967         source: RenderTaskId,
968         rg_builder: &mut RenderTaskGraphBuilder,
969         target_kind: RenderTargetKind,
970         padded_size: DeviceIntSize,
971         padding: DeviceIntSideOffsets,
972     ) -> RenderTaskId {
973         let uv_rect_kind = rg_builder.get_task(source).uv_rect_kind();
974 
975         let task_id = rg_builder.add().init(
976             RenderTask::new_dynamic(
977                 padded_size,
978                 RenderTaskKind::Scaling(ScalingTask {
979                     target_kind,
980                     padding,
981                 }),
982             ).with_uv_rect_kind(uv_rect_kind)
983         );
984 
985         rg_builder.add_dependency(task_id, source);
986 
987         task_id
988     }
989 
new_svg_filter( filter_primitives: &[FilterPrimitive], filter_datas: &[SFilterData], rg_builder: &mut RenderTaskGraphBuilder, content_size: DeviceIntSize, uv_rect_kind: UvRectKind, original_task_id: RenderTaskId, device_pixel_scale: DevicePixelScale, ) -> RenderTaskId990     pub fn new_svg_filter(
991         filter_primitives: &[FilterPrimitive],
992         filter_datas: &[SFilterData],
993         rg_builder: &mut RenderTaskGraphBuilder,
994         content_size: DeviceIntSize,
995         uv_rect_kind: UvRectKind,
996         original_task_id: RenderTaskId,
997         device_pixel_scale: DevicePixelScale,
998     ) -> RenderTaskId {
999 
1000         if filter_primitives.is_empty() {
1001             return original_task_id;
1002         }
1003 
1004         // Resolves the input to a filter primitive
1005         let get_task_input = |
1006             input: &FilterPrimitiveInput,
1007             filter_primitives: &[FilterPrimitive],
1008             rg_builder: &mut RenderTaskGraphBuilder,
1009             cur_index: usize,
1010             outputs: &[RenderTaskId],
1011             original: RenderTaskId,
1012             color_space: ColorSpace,
1013         | {
1014             // TODO(cbrewster): Not sure we can assume that the original input is sRGB.
1015             let (mut task_id, input_color_space) = match input.to_index(cur_index) {
1016                 Some(index) => (outputs[index], filter_primitives[index].color_space),
1017                 None => (original, ColorSpace::Srgb),
1018             };
1019 
1020             match (input_color_space, color_space) {
1021                 (ColorSpace::Srgb, ColorSpace::LinearRgb) => {
1022                     task_id = RenderTask::new_svg_filter_primitive(
1023                         smallvec![task_id],
1024                         content_size,
1025                         uv_rect_kind,
1026                         SvgFilterInfo::SrgbToLinear,
1027                         rg_builder,
1028                     );
1029                 },
1030                 (ColorSpace::LinearRgb, ColorSpace::Srgb) => {
1031                     task_id = RenderTask::new_svg_filter_primitive(
1032                         smallvec![task_id],
1033                         content_size,
1034                         uv_rect_kind,
1035                         SvgFilterInfo::LinearToSrgb,
1036                         rg_builder,
1037                     );
1038                 },
1039                 _ => {},
1040             }
1041 
1042             task_id
1043         };
1044 
1045         let mut outputs = vec![];
1046         let mut cur_filter_data = 0;
1047         for (cur_index, primitive) in filter_primitives.iter().enumerate() {
1048             let render_task_id = match primitive.kind {
1049                 FilterPrimitiveKind::Identity(ref identity) => {
1050                     // Identity does not create a task, it provides its input's render task
1051                     get_task_input(
1052                         &identity.input,
1053                         filter_primitives,
1054                         rg_builder,
1055                         cur_index,
1056                         &outputs,
1057                         original_task_id,
1058                         primitive.color_space
1059                     )
1060                 }
1061                 FilterPrimitiveKind::Blend(ref blend) => {
1062                     let input_1_task_id = get_task_input(
1063                         &blend.input1,
1064                         filter_primitives,
1065                         rg_builder,
1066                         cur_index,
1067                         &outputs,
1068                         original_task_id,
1069                         primitive.color_space
1070                     );
1071                     let input_2_task_id = get_task_input(
1072                         &blend.input2,
1073                         filter_primitives,
1074                         rg_builder,
1075                         cur_index,
1076                         &outputs,
1077                         original_task_id,
1078                         primitive.color_space
1079                     );
1080 
1081                     RenderTask::new_svg_filter_primitive(
1082                         smallvec![input_1_task_id, input_2_task_id],
1083                         content_size,
1084                         uv_rect_kind,
1085                         SvgFilterInfo::Blend(blend.mode),
1086                         rg_builder,
1087                     )
1088                 },
1089                 FilterPrimitiveKind::Flood(ref flood) => {
1090                     RenderTask::new_svg_filter_primitive(
1091                         smallvec![],
1092                         content_size,
1093                         uv_rect_kind,
1094                         SvgFilterInfo::Flood(flood.color),
1095                         rg_builder,
1096                     )
1097                 }
1098                 FilterPrimitiveKind::Blur(ref blur) => {
1099                     let width_std_deviation = blur.width * device_pixel_scale.0;
1100                     let height_std_deviation = blur.height * device_pixel_scale.0;
1101                     let input_task_id = get_task_input(
1102                         &blur.input,
1103                         filter_primitives,
1104                         rg_builder,
1105                         cur_index,
1106                         &outputs,
1107                         original_task_id,
1108                         primitive.color_space
1109                     );
1110 
1111                     RenderTask::new_blur(
1112                         DeviceSize::new(width_std_deviation, height_std_deviation),
1113                         // TODO: This is a hack to ensure that a blur task's input is always
1114                         // in the blur's previous pass.
1115                         RenderTask::new_svg_filter_primitive(
1116                             smallvec![input_task_id],
1117                             content_size,
1118                             uv_rect_kind,
1119                             SvgFilterInfo::Identity,
1120                             rg_builder,
1121                         ),
1122                         rg_builder,
1123                         RenderTargetKind::Color,
1124                         None,
1125                         content_size,
1126                     )
1127                 }
1128                 FilterPrimitiveKind::Opacity(ref opacity) => {
1129                     let input_task_id = get_task_input(
1130                         &opacity.input,
1131                         filter_primitives,
1132                         rg_builder,
1133                         cur_index,
1134                         &outputs,
1135                         original_task_id,
1136                         primitive.color_space
1137                     );
1138 
1139                     RenderTask::new_svg_filter_primitive(
1140                         smallvec![input_task_id],
1141                         content_size,
1142                         uv_rect_kind,
1143                         SvgFilterInfo::Opacity(opacity.opacity),
1144                         rg_builder,
1145                     )
1146                 }
1147                 FilterPrimitiveKind::ColorMatrix(ref color_matrix) => {
1148                     let input_task_id = get_task_input(
1149                         &color_matrix.input,
1150                         filter_primitives,
1151                         rg_builder,
1152                         cur_index,
1153                         &outputs,
1154                         original_task_id,
1155                         primitive.color_space
1156                     );
1157 
1158                     RenderTask::new_svg_filter_primitive(
1159                         smallvec![input_task_id],
1160                         content_size,
1161                         uv_rect_kind,
1162                         SvgFilterInfo::ColorMatrix(Box::new(color_matrix.matrix)),
1163                         rg_builder,
1164                     )
1165                 }
1166                 FilterPrimitiveKind::DropShadow(ref drop_shadow) => {
1167                     let input_task_id = get_task_input(
1168                         &drop_shadow.input,
1169                         filter_primitives,
1170                         rg_builder,
1171                         cur_index,
1172                         &outputs,
1173                         original_task_id,
1174                         primitive.color_space
1175                     );
1176 
1177                     let blur_std_deviation = drop_shadow.shadow.blur_radius * device_pixel_scale.0;
1178                     let offset = drop_shadow.shadow.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1179 
1180                     let offset_task_id = RenderTask::new_svg_filter_primitive(
1181                         smallvec![input_task_id],
1182                         content_size,
1183                         uv_rect_kind,
1184                         SvgFilterInfo::Offset(offset),
1185                         rg_builder,
1186                     );
1187 
1188                     let blur_task_id = RenderTask::new_blur(
1189                         DeviceSize::new(blur_std_deviation, blur_std_deviation),
1190                         offset_task_id,
1191                         rg_builder,
1192                         RenderTargetKind::Color,
1193                         None,
1194                         content_size,
1195                     );
1196 
1197                     RenderTask::new_svg_filter_primitive(
1198                         smallvec![input_task_id, blur_task_id],
1199                         content_size,
1200                         uv_rect_kind,
1201                         SvgFilterInfo::DropShadow(drop_shadow.shadow.color),
1202                         rg_builder,
1203                     )
1204                 }
1205                 FilterPrimitiveKind::ComponentTransfer(ref component_transfer) => {
1206                     let input_task_id = get_task_input(
1207                         &component_transfer.input,
1208                         filter_primitives,
1209                         rg_builder,
1210                         cur_index,
1211                         &outputs,
1212                         original_task_id,
1213                         primitive.color_space
1214                     );
1215 
1216                     let filter_data = &filter_datas[cur_filter_data];
1217                     cur_filter_data += 1;
1218                     if filter_data.is_identity() {
1219                         input_task_id
1220                     } else {
1221                         RenderTask::new_svg_filter_primitive(
1222                             smallvec![input_task_id],
1223                             content_size,
1224                             uv_rect_kind,
1225                             SvgFilterInfo::ComponentTransfer(filter_data.clone()),
1226                             rg_builder,
1227                         )
1228                     }
1229                 }
1230                 FilterPrimitiveKind::Offset(ref info) => {
1231                     let input_task_id = get_task_input(
1232                         &info.input,
1233                         filter_primitives,
1234                         rg_builder,
1235                         cur_index,
1236                         &outputs,
1237                         original_task_id,
1238                         primitive.color_space
1239                     );
1240 
1241                     let offset = info.offset * LayoutToWorldScale::new(1.0) * device_pixel_scale;
1242                     RenderTask::new_svg_filter_primitive(
1243                         smallvec![input_task_id],
1244                         content_size,
1245                         uv_rect_kind,
1246                         SvgFilterInfo::Offset(offset),
1247                         rg_builder,
1248                     )
1249                 }
1250                 FilterPrimitiveKind::Composite(info) => {
1251                     let input_1_task_id = get_task_input(
1252                         &info.input1,
1253                         filter_primitives,
1254                         rg_builder,
1255                         cur_index,
1256                         &outputs,
1257                         original_task_id,
1258                         primitive.color_space
1259                     );
1260                     let input_2_task_id = get_task_input(
1261                         &info.input2,
1262                         filter_primitives,
1263                         rg_builder,
1264                         cur_index,
1265                         &outputs,
1266                         original_task_id,
1267                         primitive.color_space
1268                     );
1269 
1270                     RenderTask::new_svg_filter_primitive(
1271                         smallvec![input_1_task_id, input_2_task_id],
1272                         content_size,
1273                         uv_rect_kind,
1274                         SvgFilterInfo::Composite(info.operator),
1275                         rg_builder,
1276                     )
1277                 }
1278             };
1279             outputs.push(render_task_id);
1280         }
1281 
1282         // The output of a filter is the output of the last primitive in the chain.
1283         let mut render_task_id = *outputs.last().unwrap();
1284 
1285         // Convert to sRGB if needed
1286         if filter_primitives.last().unwrap().color_space == ColorSpace::LinearRgb {
1287             render_task_id = RenderTask::new_svg_filter_primitive(
1288                 smallvec![render_task_id],
1289                 content_size,
1290                 uv_rect_kind,
1291                 SvgFilterInfo::LinearToSrgb,
1292                 rg_builder,
1293             );
1294         }
1295 
1296         render_task_id
1297     }
1298 
new_svg_filter_primitive( tasks: TaskDependencies, target_size: DeviceIntSize, uv_rect_kind: UvRectKind, info: SvgFilterInfo, rg_builder: &mut RenderTaskGraphBuilder, ) -> RenderTaskId1299     pub fn new_svg_filter_primitive(
1300         tasks: TaskDependencies,
1301         target_size: DeviceIntSize,
1302         uv_rect_kind: UvRectKind,
1303         info: SvgFilterInfo,
1304         rg_builder: &mut RenderTaskGraphBuilder,
1305     ) -> RenderTaskId {
1306         let task_id = rg_builder.add().init(RenderTask::new_dynamic(
1307             target_size,
1308             RenderTaskKind::SvgFilter(SvgFilterTask {
1309                 extra_gpu_cache_handle: None,
1310                 info,
1311             }),
1312         ).with_uv_rect_kind(uv_rect_kind));
1313 
1314         for child_id in tasks {
1315             rg_builder.add_dependency(task_id, child_id);
1316         }
1317 
1318         task_id
1319     }
1320 
uv_rect_kind(&self) -> UvRectKind1321     pub fn uv_rect_kind(&self) -> UvRectKind {
1322         self.uv_rect_kind
1323     }
1324 
get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress1325     pub fn get_texture_address(&self, gpu_cache: &GpuCache) -> GpuCacheAddress {
1326         gpu_cache.get_address(&self.uv_rect_handle)
1327     }
1328 
get_dynamic_size(&self) -> DeviceIntSize1329     pub fn get_dynamic_size(&self) -> DeviceIntSize {
1330         self.location.size()
1331     }
1332 
get_target_texture(&self) -> CacheTextureId1333     pub fn get_target_texture(&self) -> CacheTextureId {
1334         match self.location {
1335             RenderTaskLocation::Dynamic { texture_id, .. } => {
1336                 assert_ne!(texture_id, CacheTextureId::INVALID);
1337                 texture_id
1338             }
1339             RenderTaskLocation::CacheRequest { .. } |
1340             RenderTaskLocation::Unallocated { .. } |
1341             RenderTaskLocation::Static { .. } => {
1342                 unreachable!();
1343             }
1344         }
1345     }
1346 
get_texture_source(&self) -> TextureSource1347     pub fn get_texture_source(&self) -> TextureSource {
1348         match self.location {
1349             RenderTaskLocation::Dynamic { texture_id, .. } => {
1350                 assert_ne!(texture_id, CacheTextureId::INVALID);
1351                 TextureSource::TextureCache(texture_id, Swizzle::default())
1352             }
1353             RenderTaskLocation::Static { surface:  StaticRenderTaskSurface::ReadOnly { source }, .. } => {
1354                 source
1355             }
1356             RenderTaskLocation::Static { surface: StaticRenderTaskSurface::TextureCache { texture, .. }, .. } => {
1357                 TextureSource::TextureCache(texture, Swizzle::default())
1358             }
1359             RenderTaskLocation::Static { .. } |
1360             RenderTaskLocation::CacheRequest { .. } |
1361             RenderTaskLocation::Unallocated { .. } => {
1362                 unreachable!();
1363             }
1364         }
1365     }
1366 
get_target_rect(&self) -> DeviceIntRect1367     pub fn get_target_rect(&self) -> DeviceIntRect {
1368         match self.location {
1369             // Previously, we only added render tasks after the entire
1370             // primitive chain was determined visible. This meant that
1371             // we could assert any render task in the list was also
1372             // allocated (assigned to passes). Now, we add render
1373             // tasks earlier, and the picture they belong to may be
1374             // culled out later, so we can't assert that the task
1375             // has been allocated.
1376             // Render tasks that are created but not assigned to
1377             // passes consume a row in the render task texture, but
1378             // don't allocate any space in render targets nor
1379             // draw any pixels.
1380             // TODO(gw): Consider some kind of tag or other method
1381             //           to mark a task as unused explicitly. This
1382             //           would allow us to restore this debug check.
1383             RenderTaskLocation::Dynamic { rect, .. } => rect,
1384             RenderTaskLocation::Static { rect, .. } => rect,
1385             RenderTaskLocation::CacheRequest { .. }
1386             | RenderTaskLocation::Unallocated { .. } => {
1387                 panic!("bug: get_target_rect called before allocating");
1388             }
1389         }
1390     }
1391 
target_kind(&self) -> RenderTargetKind1392     pub fn target_kind(&self) -> RenderTargetKind {
1393         self.kind.target_kind()
1394     }
1395 
write_gpu_blocks( &mut self, target_rect: DeviceIntRect, gpu_cache: &mut GpuCache, )1396     pub fn write_gpu_blocks(
1397         &mut self,
1398         target_rect: DeviceIntRect,
1399         gpu_cache: &mut GpuCache,
1400     ) {
1401         profile_scope!("write_gpu_blocks");
1402 
1403         self.kind.write_gpu_blocks(gpu_cache);
1404 
1405         if self.cache_handle.is_some() {
1406             // The uv rect handle of cached render tasks is requested and set by the
1407             // render task cache.
1408             return;
1409         }
1410 
1411         if let Some(mut request) = gpu_cache.request(&mut self.uv_rect_handle) {
1412             let p0 = target_rect.min.to_f32();
1413             let p1 = target_rect.max.to_f32();
1414             let image_source = ImageSource {
1415                 p0,
1416                 p1,
1417                 user_data: [0.0; 4],
1418                 uv_rect_kind: self.uv_rect_kind,
1419             };
1420             image_source.write_gpu_blocks(&mut request);
1421         }
1422     }
1423 
1424     /// Called by the render task cache.
1425     ///
1426     /// Tells the render task that it is cached (which means its gpu cache
1427     /// handle is managed by the texture cache).
mark_cached(&mut self, handle: RenderTaskCacheEntryHandle)1428     pub fn mark_cached(&mut self, handle: RenderTaskCacheEntryHandle) {
1429         self.cache_handle = Some(handle);
1430     }
1431 }
1432