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::{DeviceIntPoint, DeviceIntRect, DeviceIntSize, ImageDescriptor, ImageFormat};
6 use api::{DeviceSize, PremultipliedColorF};
7 use box_shadow::{BoxShadowCacheKey};
8 use clip::{ClipSource, ClipStore, ClipWorkItem};
9 use clip_scroll_tree::CoordinateSystemId;
10 use device::TextureFilter;
11 use gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
12 use gpu_types::{ImageSource, PictureType, RasterizationSpace};
13 use internal_types::{FastHashMap, SavedTargetIndex, SourceTexture};
14 use picture::ContentOrigin;
15 use prim_store::{PrimitiveIndex, ImageCacheKey};
16 #[cfg(feature = "debugger")]
17 use print_tree::{PrintTreePrinter};
18 use resource_cache::{CacheItem, ResourceCache};
19 use std::{cmp, ops, usize, f32, i32};
20 use texture_cache::{TextureCache, TextureCacheHandle};
21 use tiling::{RenderPass, RenderTargetIndex};
22 use tiling::{RenderTargetKind};
23 
24 const FLOATS_PER_RENDER_TASK_INFO: usize = 12;
25 pub const MAX_BLUR_STD_DEVIATION: f32 = 4.0;
26 pub const MIN_DOWNSCALING_RT_SIZE: i32 = 128;
27 
28 #[derive(Copy, Clone, PartialEq, Eq, Hash, Debug)]
29 #[cfg_attr(feature = "capture", derive(Serialize))]
30 #[cfg_attr(feature = "replay", derive(Deserialize))]
31 pub struct RenderTaskId(pub u32); // TODO(gw): Make private when using GPU cache!
32 
33 #[derive(Debug, Copy, Clone)]
34 #[repr(C)]
35 #[cfg_attr(feature = "capture", derive(Serialize))]
36 #[cfg_attr(feature = "replay", derive(Deserialize))]
37 pub struct RenderTaskAddress(pub u32);
38 
39 #[derive(Debug)]
40 #[cfg_attr(feature = "capture", derive(Serialize))]
41 #[cfg_attr(feature = "replay", derive(Deserialize))]
42 pub struct RenderTaskTree {
43     pub tasks: Vec<RenderTask>,
44     pub task_data: Vec<RenderTaskData>,
45     next_saved: SavedTargetIndex,
46 }
47 
48 impl RenderTaskTree {
new() -> Self49     pub fn new() -> Self {
50         RenderTaskTree {
51             tasks: Vec::new(),
52             task_data: Vec::new(),
53             next_saved: SavedTargetIndex(0),
54         }
55     }
56 
add(&mut self, task: RenderTask) -> RenderTaskId57     pub fn add(&mut self, task: RenderTask) -> RenderTaskId {
58         let id = RenderTaskId(self.tasks.len() as u32);
59         self.tasks.push(task);
60         id
61     }
62 
max_depth(&self, id: RenderTaskId, depth: usize, max_depth: &mut usize)63     pub fn max_depth(&self, id: RenderTaskId, depth: usize, max_depth: &mut usize) {
64         let depth = depth + 1;
65         *max_depth = cmp::max(*max_depth, depth);
66         let task = &self.tasks[id.0 as usize];
67         for child in &task.children {
68             self.max_depth(*child, depth, max_depth);
69         }
70     }
71 
assign_to_passes( &self, id: RenderTaskId, pass_index: usize, passes: &mut Vec<RenderPass>, )72     pub fn assign_to_passes(
73         &self,
74         id: RenderTaskId,
75         pass_index: usize,
76         passes: &mut Vec<RenderPass>,
77     ) {
78         let task = &self.tasks[id.0 as usize];
79 
80         for child in &task.children {
81             self.assign_to_passes(*child, pass_index - 1, passes);
82         }
83 
84         // Sanity check - can be relaxed if needed
85         match task.location {
86             RenderTaskLocation::Fixed(..) => {
87                 debug_assert!(pass_index == passes.len() - 1);
88             }
89             RenderTaskLocation::Dynamic(..) |
90             RenderTaskLocation::TextureCache(..) => {
91                 debug_assert!(pass_index < passes.len() - 1);
92             }
93         }
94 
95         // If this task can be shared between multiple
96         // passes, render it in the first pass so that
97         // it is available to all subsequent passes.
98         let pass_index = if task.is_shared() {
99             debug_assert!(task.children.is_empty());
100             0
101         } else {
102             pass_index
103         };
104 
105         let pass = &mut passes[pass_index];
106         pass.add_render_task(id, task.get_dynamic_size(), task.target_kind());
107     }
108 
get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress109     pub fn get_task_address(&self, id: RenderTaskId) -> RenderTaskAddress {
110         RenderTaskAddress(id.0)
111     }
112 
build(&mut self)113     pub fn build(&mut self) {
114         for task in &self.tasks {
115             self.task_data.push(task.write_task_data());
116         }
117     }
118 
save_target(&mut self) -> SavedTargetIndex119     pub fn save_target(&mut self) -> SavedTargetIndex {
120         let id = self.next_saved;
121         self.next_saved.0 += 1;
122         id
123     }
124 }
125 
126 impl ops::Index<RenderTaskId> for RenderTaskTree {
127     type Output = RenderTask;
index(&self, id: RenderTaskId) -> &RenderTask128     fn index(&self, id: RenderTaskId) -> &RenderTask {
129         &self.tasks[id.0 as usize]
130     }
131 }
132 
133 impl ops::IndexMut<RenderTaskId> for RenderTaskTree {
index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask134     fn index_mut(&mut self, id: RenderTaskId) -> &mut RenderTask {
135         &mut self.tasks[id.0 as usize]
136     }
137 }
138 
139 #[derive(Debug)]
140 #[cfg_attr(feature = "capture", derive(Serialize))]
141 #[cfg_attr(feature = "replay", derive(Deserialize))]
142 pub enum RenderTaskLocation {
143     Fixed(DeviceIntRect),
144     Dynamic(Option<(DeviceIntPoint, RenderTargetIndex)>, DeviceIntSize),
145     TextureCache(SourceTexture, i32, DeviceIntRect),
146 }
147 
148 #[derive(Debug)]
149 #[cfg_attr(feature = "capture", derive(Serialize))]
150 #[cfg_attr(feature = "replay", derive(Deserialize))]
151 pub struct CacheMaskTask {
152     actual_rect: DeviceIntRect,
153     pub clips: Vec<ClipWorkItem>,
154     pub coordinate_system_id: CoordinateSystemId,
155 }
156 
157 #[derive(Debug)]
158 #[cfg_attr(feature = "capture", derive(Serialize))]
159 #[cfg_attr(feature = "replay", derive(Deserialize))]
160 pub struct ClipRegionTask {
161     pub clip_data_address: GpuCacheAddress,
162 }
163 
164 #[derive(Debug)]
165 #[cfg_attr(feature = "capture", derive(Serialize))]
166 #[cfg_attr(feature = "replay", derive(Deserialize))]
167 pub struct PictureTask {
168     pub prim_index: PrimitiveIndex,
169     pub target_kind: RenderTargetKind,
170     pub content_origin: ContentOrigin,
171     pub color: PremultipliedColorF,
172     pub pic_type: PictureType,
173     pub uv_rect_handle: GpuCacheHandle,
174 }
175 
176 #[derive(Debug)]
177 #[cfg_attr(feature = "capture", derive(Serialize))]
178 #[cfg_attr(feature = "replay", derive(Deserialize))]
179 pub struct BlurTask {
180     pub blur_std_deviation: f32,
181     pub target_kind: RenderTargetKind,
182     pub color: PremultipliedColorF,
183     pub uv_rect_handle: GpuCacheHandle,
184 }
185 
186 impl BlurTask {
187     #[cfg(feature = "debugger")]
print_with<T: PrintTreePrinter>(&self, pt: &mut T)188     fn print_with<T: PrintTreePrinter>(&self, pt: &mut T) {
189         pt.add_item(format!("std deviation: {}", self.blur_std_deviation));
190         pt.add_item(format!("target: {:?}", self.target_kind));
191     }
192 }
193 
194 // Where the source data for a blit task can be found.
195 #[derive(Debug)]
196 #[cfg_attr(feature = "capture", derive(Serialize))]
197 #[cfg_attr(feature = "replay", derive(Deserialize))]
198 pub enum BlitSource {
199     Image {
200         key: ImageCacheKey,
201     },
202     RenderTask {
203         task_id: RenderTaskId,
204     },
205 }
206 
207 #[derive(Debug)]
208 #[cfg_attr(feature = "capture", derive(Serialize))]
209 #[cfg_attr(feature = "replay", derive(Deserialize))]
210 pub struct BlitTask {
211     pub source: BlitSource,
212 }
213 
214 #[derive(Debug)]
215 #[cfg_attr(feature = "capture", derive(Serialize))]
216 #[cfg_attr(feature = "replay", derive(Deserialize))]
217 pub struct RenderTaskData {
218     pub data: [f32; FLOATS_PER_RENDER_TASK_INFO],
219 }
220 
221 #[derive(Debug)]
222 #[cfg_attr(feature = "capture", derive(Serialize))]
223 #[cfg_attr(feature = "replay", derive(Deserialize))]
224 pub enum RenderTaskKind {
225     Picture(PictureTask),
226     CacheMask(CacheMaskTask),
227     ClipRegion(ClipRegionTask),
228     VerticalBlur(BlurTask),
229     HorizontalBlur(BlurTask),
230     Readback(DeviceIntRect),
231     Scaling(RenderTargetKind),
232     Blit(BlitTask),
233 }
234 
235 #[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
236 #[cfg_attr(feature = "capture", derive(Serialize))]
237 #[cfg_attr(feature = "replay", derive(Deserialize))]
238 pub enum ClearMode {
239     // Applicable to color and alpha targets.
240     Zero,
241     One,
242 
243     // Applicable to color targets only.
244     Transparent,
245 }
246 
247 #[derive(Debug)]
248 #[cfg_attr(feature = "capture", derive(Serialize))]
249 #[cfg_attr(feature = "replay", derive(Deserialize))]
250 pub struct RenderTask {
251     pub location: RenderTaskLocation,
252     pub children: Vec<RenderTaskId>,
253     pub kind: RenderTaskKind,
254     pub clear_mode: ClearMode,
255     pub saved_index: Option<SavedTargetIndex>,
256 }
257 
258 impl RenderTask {
new_picture( location: RenderTaskLocation, prim_index: PrimitiveIndex, target_kind: RenderTargetKind, content_origin: ContentOrigin, color: PremultipliedColorF, clear_mode: ClearMode, children: Vec<RenderTaskId>, pic_type: PictureType, ) -> Self259     pub fn new_picture(
260         location: RenderTaskLocation,
261         prim_index: PrimitiveIndex,
262         target_kind: RenderTargetKind,
263         content_origin: ContentOrigin,
264         color: PremultipliedColorF,
265         clear_mode: ClearMode,
266         children: Vec<RenderTaskId>,
267         pic_type: PictureType,
268     ) -> Self {
269         RenderTask {
270             children,
271             location,
272             kind: RenderTaskKind::Picture(PictureTask {
273                 prim_index,
274                 target_kind,
275                 content_origin,
276                 color,
277                 pic_type,
278                 uv_rect_handle: GpuCacheHandle::new(),
279             }),
280             clear_mode,
281             saved_index: None,
282         }
283     }
284 
new_readback(screen_rect: DeviceIntRect) -> Self285     pub fn new_readback(screen_rect: DeviceIntRect) -> Self {
286         RenderTask {
287             children: Vec::new(),
288             location: RenderTaskLocation::Dynamic(None, screen_rect.size),
289             kind: RenderTaskKind::Readback(screen_rect),
290             clear_mode: ClearMode::Transparent,
291             saved_index: None,
292         }
293     }
294 
new_blit( size: DeviceIntSize, source: BlitSource, ) -> Self295     pub fn new_blit(
296         size: DeviceIntSize,
297         source: BlitSource,
298     ) -> Self {
299         let mut children = Vec::new();
300 
301         // If this blit uses a render task as a source,
302         // ensure it's added as a child task. This will
303         // ensure it gets allocated in the correct pass
304         // and made available as an input when this task
305         // executes.
306         if let BlitSource::RenderTask { task_id } = source {
307             children.push(task_id);
308         }
309 
310         RenderTask {
311             children,
312             location: RenderTaskLocation::Dynamic(None, size),
313             kind: RenderTaskKind::Blit(BlitTask {
314                 source,
315             }),
316             clear_mode: ClearMode::Transparent,
317             saved_index: None,
318         }
319     }
320 
new_mask( outer_rect: DeviceIntRect, clips: Vec<ClipWorkItem>, prim_coordinate_system_id: CoordinateSystemId, clip_store: &mut ClipStore, gpu_cache: &mut GpuCache, resource_cache: &mut ResourceCache, render_tasks: &mut RenderTaskTree, ) -> Self321     pub fn new_mask(
322         outer_rect: DeviceIntRect,
323         clips: Vec<ClipWorkItem>,
324         prim_coordinate_system_id: CoordinateSystemId,
325         clip_store: &mut ClipStore,
326         gpu_cache: &mut GpuCache,
327         resource_cache: &mut ResourceCache,
328         render_tasks: &mut RenderTaskTree,
329     ) -> Self {
330         let mut children = Vec::new();
331 
332         // Step through the clip sources that make up this mask. If we find
333         // any box-shadow clip sources, request that image from the render
334         // task cache. This allows the blurred box-shadow rect to be cached
335         // in the texture cache across frames.
336         // TODO(gw): Consider moving this logic outside this function, especially
337         //           as we add more clip sources that depend on render tasks.
338         // TODO(gw): If this ever shows up in a profile, we could pre-calculate
339         //           whether a ClipSources contains any box-shadows and skip
340         //           this iteration for the majority of cases.
341         for clip_item in &clips {
342             let clip_sources = clip_store.get_opt_mut(&clip_item.clip_sources).expect("bug");
343             for &mut (ref mut clip, _) in &mut clip_sources.clips {
344                 match *clip {
345                     ClipSource::BoxShadow(ref mut info) => {
346                         let (cache_size, cache_key) = info.cache_key
347                             .as_ref()
348                             .expect("bug: no cache key set")
349                             .clone();
350                         let blur_radius_dp = cache_key.blur_radius_dp as f32;
351                         let clip_data_address = gpu_cache.get_address(&info.clip_data_handle);
352 
353                         // Request a cacheable render task with a blurred, minimal
354                         // sized box-shadow rect.
355                         info.cache_item = resource_cache.request_render_task(
356                             RenderTaskCacheKey {
357                                 size: cache_size,
358                                 kind: RenderTaskCacheKeyKind::BoxShadow(cache_key),
359                             },
360                             gpu_cache,
361                             render_tasks,
362                             |render_tasks| {
363                                 // Draw the rounded rect.
364                                 let mask_task = RenderTask::new_rounded_rect_mask(
365                                     cache_size,
366                                     clip_data_address,
367                                 );
368 
369                                 let mask_task_id = render_tasks.add(mask_task);
370 
371                                 // Blur it
372                                 let blur_render_task = RenderTask::new_blur(
373                                     blur_radius_dp,
374                                     mask_task_id,
375                                     render_tasks,
376                                     RenderTargetKind::Alpha,
377                                     ClearMode::Zero,
378                                     PremultipliedColorF::TRANSPARENT,
379                                 );
380 
381                                 let root_task_id = render_tasks.add(blur_render_task);
382                                 children.push(root_task_id);
383 
384                                 (root_task_id, false)
385                             }
386                         );
387                     }
388                     ClipSource::Rectangle(..) |
389                     ClipSource::RoundedRectangle(..) |
390                     ClipSource::Image(..) |
391                     ClipSource::BorderCorner(..) => {}
392                 }
393             }
394         }
395 
396         RenderTask {
397             children,
398             location: RenderTaskLocation::Dynamic(None, outer_rect.size),
399             kind: RenderTaskKind::CacheMask(CacheMaskTask {
400                 actual_rect: outer_rect,
401                 clips,
402                 coordinate_system_id: prim_coordinate_system_id,
403             }),
404             clear_mode: ClearMode::One,
405             saved_index: None,
406         }
407     }
408 
new_rounded_rect_mask( size: DeviceIntSize, clip_data_address: GpuCacheAddress, ) -> Self409     pub fn new_rounded_rect_mask(
410         size: DeviceIntSize,
411         clip_data_address: GpuCacheAddress,
412     ) -> Self {
413         RenderTask {
414             children: Vec::new(),
415             location: RenderTaskLocation::Dynamic(None, size),
416             kind: RenderTaskKind::ClipRegion(ClipRegionTask {
417                 clip_data_address,
418             }),
419             clear_mode: ClearMode::One,
420             saved_index: None,
421         }
422     }
423 
424     // Construct a render task to apply a blur to a primitive.
425     // The render task chain that is constructed looks like:
426     //
427     //    PrimitiveCacheTask: Draw the primitives.
428     //           ^
429     //           |
430     //    DownscalingTask(s): Each downscaling task reduces the size of render target to
431     //           ^            half. Also reduce the std deviation to half until the std
432     //           |            deviation less than 4.0.
433     //           |
434     //           |
435     //    VerticalBlurTask: Apply the separable vertical blur to the primitive.
436     //           ^
437     //           |
438     //    HorizontalBlurTask: Apply the separable horizontal blur to the vertical blur.
439     //           |
440     //           +---- This is stored as the input task to the primitive shader.
441     //
new_blur( blur_std_deviation: f32, src_task_id: RenderTaskId, render_tasks: &mut RenderTaskTree, target_kind: RenderTargetKind, clear_mode: ClearMode, color: PremultipliedColorF, ) -> Self442     pub fn new_blur(
443         blur_std_deviation: f32,
444         src_task_id: RenderTaskId,
445         render_tasks: &mut RenderTaskTree,
446         target_kind: RenderTargetKind,
447         clear_mode: ClearMode,
448         color: PremultipliedColorF,
449     ) -> Self {
450         // Adjust large std deviation value.
451         let mut adjusted_blur_std_deviation = blur_std_deviation;
452         let blur_target_size = render_tasks[src_task_id].get_dynamic_size();
453         let mut adjusted_blur_target_size = blur_target_size;
454         let mut downscaling_src_task_id = src_task_id;
455         let mut scale_factor = 1.0;
456         while adjusted_blur_std_deviation > MAX_BLUR_STD_DEVIATION {
457             if adjusted_blur_target_size.width < MIN_DOWNSCALING_RT_SIZE ||
458                adjusted_blur_target_size.height < MIN_DOWNSCALING_RT_SIZE {
459                 break;
460             }
461             adjusted_blur_std_deviation *= 0.5;
462             scale_factor *= 2.0;
463             adjusted_blur_target_size = (blur_target_size.to_f32() / scale_factor).to_i32();
464             let downscaling_task = RenderTask::new_scaling(
465                 target_kind,
466                 downscaling_src_task_id,
467                 adjusted_blur_target_size,
468             );
469             downscaling_src_task_id = render_tasks.add(downscaling_task);
470         }
471 
472         let blur_task_v = RenderTask {
473             children: vec![downscaling_src_task_id],
474             location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
475             kind: RenderTaskKind::VerticalBlur(BlurTask {
476                 blur_std_deviation: adjusted_blur_std_deviation,
477                 target_kind,
478                 color,
479                 uv_rect_handle: GpuCacheHandle::new(),
480             }),
481             clear_mode,
482             saved_index: None,
483         };
484 
485         let blur_task_v_id = render_tasks.add(blur_task_v);
486 
487         let blur_task_h = RenderTask {
488             children: vec![blur_task_v_id],
489             location: RenderTaskLocation::Dynamic(None, adjusted_blur_target_size),
490             kind: RenderTaskKind::HorizontalBlur(BlurTask {
491                 blur_std_deviation: adjusted_blur_std_deviation,
492                 target_kind,
493                 color,
494                 uv_rect_handle: GpuCacheHandle::new(),
495             }),
496             clear_mode,
497             saved_index: None,
498         };
499 
500         blur_task_h
501     }
502 
new_scaling( target_kind: RenderTargetKind, src_task_id: RenderTaskId, target_size: DeviceIntSize, ) -> Self503     pub fn new_scaling(
504         target_kind: RenderTargetKind,
505         src_task_id: RenderTaskId,
506         target_size: DeviceIntSize,
507     ) -> Self {
508         RenderTask {
509             children: vec![src_task_id],
510             location: RenderTaskLocation::Dynamic(None, target_size),
511             kind: RenderTaskKind::Scaling(target_kind),
512             clear_mode: match target_kind {
513                 RenderTargetKind::Color => ClearMode::Transparent,
514                 RenderTargetKind::Alpha => ClearMode::One,
515             },
516             saved_index: None,
517         }
518     }
519 
520     // Write (up to) 8 floats of data specific to the type
521     // of render task that is provided to the GPU shaders
522     // via a vertex texture.
write_task_data(&self) -> RenderTaskData523     pub fn write_task_data(&self) -> RenderTaskData {
524         // NOTE: The ordering and layout of these structures are
525         //       required to match both the GPU structures declared
526         //       in prim_shared.glsl, and also the uses in submit_batch()
527         //       in renderer.rs.
528         // TODO(gw): Maybe there's a way to make this stuff a bit
529         //           more type-safe. Although, it will always need
530         //           to be kept in sync with the GLSL code anyway.
531 
532         let (data1, data2) = match self.kind {
533             RenderTaskKind::Picture(ref task) => {
534                 (
535                     // Note: has to match `PICTURE_TYPE_*` in shaders
536                     // TODO(gw): Instead of using the sign of the picture
537                     //           type here, we should consider encoding it
538                     //           as a set of flags that get casted here
539                     //           and in the shader. This is a bit tidier
540                     //           and allows for future expansion of flags.
541                     match task.content_origin {
542                         ContentOrigin::Local(point) => [
543                             point.x, point.y, task.pic_type as u32 as f32,
544                         ],
545                         ContentOrigin::Screen(point) => [
546                             point.x as f32, point.y as f32, -(task.pic_type as u32 as f32),
547                         ],
548                     },
549                     task.color.to_array()
550                 )
551             }
552             RenderTaskKind::CacheMask(ref task) => {
553                 (
554                     [
555                         task.actual_rect.origin.x as f32,
556                         task.actual_rect.origin.y as f32,
557                         RasterizationSpace::Screen as i32 as f32,
558                     ],
559                     [0.0; 4],
560                 )
561             }
562             RenderTaskKind::ClipRegion(..) => {
563                 (
564                     [
565                         0.0,
566                         0.0,
567                         RasterizationSpace::Local as i32 as f32,
568                     ],
569                     [0.0; 4],
570                 )
571             }
572             RenderTaskKind::VerticalBlur(ref task) |
573             RenderTaskKind::HorizontalBlur(ref task) => {
574                 (
575                     [
576                         task.blur_std_deviation,
577                         0.0,
578                         0.0,
579                     ],
580                     task.color.to_array()
581                 )
582             }
583             RenderTaskKind::Readback(..) |
584             RenderTaskKind::Scaling(..) |
585             RenderTaskKind::Blit(..) => {
586                 (
587                     [0.0; 3],
588                     [0.0; 4],
589                 )
590             }
591         };
592 
593         let (target_rect, target_index) = self.get_target_rect();
594 
595         RenderTaskData {
596             data: [
597                 target_rect.origin.x as f32,
598                 target_rect.origin.y as f32,
599                 target_rect.size.width as f32,
600                 target_rect.size.height as f32,
601                 target_index.0 as f32,
602                 data1[0],
603                 data1[1],
604                 data1[2],
605                 data2[0],
606                 data2[1],
607                 data2[2],
608                 data2[3],
609             ]
610         }
611     }
612 
get_texture_handle(&self) -> &GpuCacheHandle613     pub fn get_texture_handle(&self) -> &GpuCacheHandle {
614         match self.kind {
615             RenderTaskKind::Picture(ref info) => {
616                 &info.uv_rect_handle
617             }
618             RenderTaskKind::VerticalBlur(ref info) |
619             RenderTaskKind::HorizontalBlur(ref info) => {
620                 &info.uv_rect_handle
621             }
622             RenderTaskKind::ClipRegion(..) |
623             RenderTaskKind::Readback(..) |
624             RenderTaskKind::Scaling(..) |
625             RenderTaskKind::Blit(..) |
626             RenderTaskKind::CacheMask(..) => {
627                 panic!("texture handle not supported for this task kind");
628             }
629         }
630     }
631 
get_dynamic_size(&self) -> DeviceIntSize632     pub fn get_dynamic_size(&self) -> DeviceIntSize {
633         match self.location {
634             RenderTaskLocation::Fixed(..) => DeviceIntSize::zero(),
635             RenderTaskLocation::Dynamic(_, size) => size,
636             RenderTaskLocation::TextureCache(_, _, rect) => rect.size,
637         }
638     }
639 
get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex)640     pub fn get_target_rect(&self) -> (DeviceIntRect, RenderTargetIndex) {
641         match self.location {
642             RenderTaskLocation::Fixed(rect) => {
643                 (rect, RenderTargetIndex(0))
644             }
645             // Previously, we only added render tasks after the entire
646             // primitive chain was determined visible. This meant that
647             // we could assert any render task in the list was also
648             // allocated (assigned to passes). Now, we add render
649             // tasks earlier, and the picture they belong to may be
650             // culled out later, so we can't assert that the task
651             // has been allocated.
652             // Render tasks that are created but not assigned to
653             // passes consume a row in the render task texture, but
654             // don't allocate any space in render targets nor
655             // draw any pixels.
656             // TODO(gw): Consider some kind of tag or other method
657             //           to mark a task as unused explicitly. This
658             //           would allow us to restore this debug check.
659             RenderTaskLocation::Dynamic(Some((origin, target_index)), size) => {
660                 (DeviceIntRect::new(origin, size), target_index)
661             }
662             RenderTaskLocation::Dynamic(None, _) => {
663                 (DeviceIntRect::zero(), RenderTargetIndex(0))
664             }
665             RenderTaskLocation::TextureCache(_, layer, rect) => {
666                 (rect, RenderTargetIndex(layer as usize))
667             }
668         }
669     }
670 
target_kind(&self) -> RenderTargetKind671     pub fn target_kind(&self) -> RenderTargetKind {
672         match self.kind {
673             RenderTaskKind::Readback(..) => RenderTargetKind::Color,
674 
675             RenderTaskKind::ClipRegion(..) |
676             RenderTaskKind::CacheMask(..) => {
677                 RenderTargetKind::Alpha
678             }
679 
680             RenderTaskKind::VerticalBlur(ref task_info) |
681             RenderTaskKind::HorizontalBlur(ref task_info) => {
682                 task_info.target_kind
683             }
684 
685             RenderTaskKind::Scaling(target_kind) => {
686                 target_kind
687             }
688 
689             RenderTaskKind::Picture(ref task_info) => {
690                 task_info.target_kind
691             }
692 
693             RenderTaskKind::Blit(..) => {
694                 RenderTargetKind::Color
695             }
696         }
697     }
698 
699     // Check if this task wants to be made available as an input
700     // to all passes (except the first) in the render task tree.
701     // To qualify for this, the task needs to have no children / dependencies.
702     // Currently, this is only supported for A8 targets, but it can be
703     // trivially extended to also support RGBA8 targets in the future
704     // if we decide that is useful.
is_shared(&self) -> bool705     pub fn is_shared(&self) -> bool {
706         match self.kind {
707             RenderTaskKind::Picture(..) |
708             RenderTaskKind::VerticalBlur(..) |
709             RenderTaskKind::Readback(..) |
710             RenderTaskKind::HorizontalBlur(..) |
711             RenderTaskKind::Scaling(..) |
712             RenderTaskKind::ClipRegion(..) |
713             RenderTaskKind::Blit(..) => false,
714 
715             // TODO(gw): For now, we've disabled the shared clip mask
716             //           optimization. It's of dubious value in the
717             //           future once we start to cache clip tasks anyway.
718             //           I have left shared texture support here though,
719             //           just in case we want it in the future.
720             RenderTaskKind::CacheMask(..) => false,
721         }
722     }
723 
prepare_for_render( &mut self, gpu_cache: &mut GpuCache, )724     pub fn prepare_for_render(
725         &mut self,
726         gpu_cache: &mut GpuCache,
727     ) {
728         let (target_rect, target_index) = self.get_target_rect();
729 
730         let (cache_handle, color) = match self.kind {
731             RenderTaskKind::HorizontalBlur(ref mut info) |
732             RenderTaskKind::VerticalBlur(ref mut info) => {
733                 (&mut info.uv_rect_handle, info.color)
734             }
735             RenderTaskKind::Picture(ref mut info) => {
736                 (&mut info.uv_rect_handle, info.color)
737             }
738             RenderTaskKind::Readback(..) |
739             RenderTaskKind::Scaling(..) |
740             RenderTaskKind::Blit(..) |
741             RenderTaskKind::ClipRegion(..) |
742             RenderTaskKind::CacheMask(..) => {
743                 return;
744             }
745         };
746 
747         if let Some(mut request) = gpu_cache.request(cache_handle) {
748             let image_source = ImageSource {
749                 p0: target_rect.origin.to_f32(),
750                 p1: target_rect.bottom_right().to_f32(),
751                 color,
752                 texture_layer: target_index.0 as f32,
753                 user_data: [0.0; 3],
754             };
755             image_source.write_gpu_blocks(&mut request);
756         }
757     }
758 
759     #[cfg(feature = "debugger")]
print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool760     pub fn print_with<T: PrintTreePrinter>(&self, pt: &mut T, tree: &RenderTaskTree) -> bool {
761         match self.kind {
762             RenderTaskKind::Picture(ref task) => {
763                 pt.new_level(format!("Picture of {:?}", task.prim_index));
764                 pt.add_item(format!("kind: {:?}", task.target_kind));
765             }
766             RenderTaskKind::CacheMask(ref task) => {
767                 pt.new_level(format!("CacheMask with {} clips", task.clips.len()));
768                 pt.add_item(format!("rect: {:?}", task.actual_rect));
769             }
770             RenderTaskKind::ClipRegion(..) => {
771                 pt.new_level("ClipRegion".to_owned());
772             }
773             RenderTaskKind::VerticalBlur(ref task) => {
774                 pt.new_level("VerticalBlur".to_owned());
775                 task.print_with(pt);
776             }
777             RenderTaskKind::HorizontalBlur(ref task) => {
778                 pt.new_level("HorizontalBlur".to_owned());
779                 task.print_with(pt);
780             }
781             RenderTaskKind::Readback(ref rect) => {
782                 pt.new_level("Readback".to_owned());
783                 pt.add_item(format!("rect: {:?}", rect));
784             }
785             RenderTaskKind::Scaling(ref kind) => {
786                 pt.new_level("Scaling".to_owned());
787                 pt.add_item(format!("kind: {:?}", kind));
788             }
789             RenderTaskKind::Blit(ref task) => {
790                 pt.new_level("Blit".to_owned());
791                 pt.add_item(format!("source: {:?}", task.source));
792             }
793         }
794 
795         pt.add_item(format!("clear to: {:?}", self.clear_mode));
796 
797         for &child_id in &self.children {
798             if tree[child_id].print_with(pt, tree) {
799                 pt.add_item(format!("self: {:?}", child_id))
800             }
801         }
802 
803         pt.end_level();
804         true
805     }
806 
807     /// Mark this render task for keeping the results alive up until the end of the frame.
mark_for_saving(&mut self)808     pub fn mark_for_saving(&mut self) {
809         match self.location {
810             RenderTaskLocation::Fixed(..) |
811             RenderTaskLocation::Dynamic(..) => {
812                 self.saved_index = Some(SavedTargetIndex::PENDING);
813             }
814             RenderTaskLocation::TextureCache(..) => {
815                 panic!("Unable to mark a permanently cached task for saving!");
816             }
817         }
818     }
819 }
820 
821 #[derive(Debug, Hash, PartialEq, Eq)]
822 #[cfg_attr(feature = "capture", derive(Serialize))]
823 #[cfg_attr(feature = "replay", derive(Deserialize))]
824 pub enum RenderTaskCacheKeyKind {
825     BoxShadow(BoxShadowCacheKey),
826     Image(ImageCacheKey),
827 }
828 
829 #[derive(Debug, Hash, PartialEq, Eq)]
830 #[cfg_attr(feature = "capture", derive(Serialize))]
831 #[cfg_attr(feature = "replay", derive(Deserialize))]
832 pub struct RenderTaskCacheKey {
833     pub size: DeviceIntSize,
834     pub kind: RenderTaskCacheKeyKind,
835 }
836 
837 #[cfg_attr(feature = "capture", derive(Serialize))]
838 #[cfg_attr(feature = "replay", derive(Deserialize))]
839 struct RenderTaskCacheEntry {
840     handle: TextureCacheHandle,
841 }
842 
843 // A cache of render tasks that are stored in the texture
844 // cache for usage across frames.
845 #[cfg_attr(feature = "capture", derive(Serialize))]
846 #[cfg_attr(feature = "replay", derive(Deserialize))]
847 pub struct RenderTaskCache {
848     entries: FastHashMap<RenderTaskCacheKey, RenderTaskCacheEntry>,
849 }
850 
851 impl RenderTaskCache {
new() -> Self852     pub fn new() -> Self {
853         RenderTaskCache {
854             entries: FastHashMap::default(),
855         }
856     }
857 
clear(&mut self)858     pub fn clear(&mut self) {
859         self.entries.clear();
860     }
861 
begin_frame( &mut self, texture_cache: &mut TextureCache, )862     pub fn begin_frame(
863         &mut self,
864         texture_cache: &mut TextureCache,
865     ) {
866         // Drop any items from the cache that have been
867         // evicted from the texture cache.
868         //
869         // This isn't actually necessary for the texture
870         // cache to be able to evict old render tasks.
871         // It will evict render tasks as required, since
872         // the access time in the texture cache entry will
873         // be stale if this task hasn't been requested
874         // for a while.
875         //
876         // Nonetheless, we should remove stale entries
877         // from here so that this hash map doesn't
878         // grow indefinitely!
879         self.entries.retain(|_, value| {
880             texture_cache.is_allocated(&value.handle)
881         });
882     }
883 
request_render_task<F>( &mut self, key: RenderTaskCacheKey, texture_cache: &mut TextureCache, gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, mut f: F, ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool)884     pub fn request_render_task<F>(
885         &mut self,
886         key: RenderTaskCacheKey,
887         texture_cache: &mut TextureCache,
888         gpu_cache: &mut GpuCache,
889         render_tasks: &mut RenderTaskTree,
890         mut f: F,
891     ) -> CacheItem where F: FnMut(&mut RenderTaskTree) -> (RenderTaskId, bool) {
892         // Get the texture cache handle for this cache key,
893         // or create one.
894         let cache_entry = self.entries
895                               .entry(key)
896                               .or_insert(RenderTaskCacheEntry {
897                                   handle: TextureCacheHandle::new(),
898                               });
899 
900         // Check if this texture cache handle is valie.
901         if texture_cache.request(&mut cache_entry.handle, gpu_cache) {
902             // Invoke user closure to get render task chain
903             // to draw this into the texture cache.
904             let (render_task_id, is_opaque) = f(render_tasks);
905             let render_task = &mut render_tasks[render_task_id];
906 
907             // Select the right texture page to allocate from.
908             let image_format = match render_task.target_kind() {
909                 RenderTargetKind::Color => ImageFormat::BGRA8,
910                 RenderTargetKind::Alpha => ImageFormat::R8,
911             };
912 
913             // Find out what size to alloc in the texture cache.
914             let size = match render_task.location {
915                 RenderTaskLocation::Fixed(..) |
916                 RenderTaskLocation::TextureCache(..) => {
917                     panic!("BUG: dynamic task was expected");
918                 }
919                 RenderTaskLocation::Dynamic(_, size) => size,
920             };
921 
922             // TODO(gw): Support color tasks in the texture cache,
923             //           and perhaps consider if we can determine
924             //           if some tasks are opaque as an optimization.
925             let descriptor = ImageDescriptor::new(
926                 size.width as u32,
927                 size.height as u32,
928                 image_format,
929                 is_opaque,
930             );
931 
932             // Allocate space in the texture cache, but don't supply
933             // and CPU-side data to be uploaded.
934             texture_cache.update(
935                 &mut cache_entry.handle,
936                 descriptor,
937                 TextureFilter::Linear,
938                 None,
939                 [0.0; 3],
940                 None,
941                 gpu_cache,
942             );
943 
944             // Get the allocation details in the texture cache, and store
945             // this in the render task. The renderer will draw this
946             // task into the appropriate layer and rect of the texture
947             // cache on this frame.
948             let (texture_id, texture_layer, uv_rect) =
949                 texture_cache.get_cache_location(&cache_entry.handle);
950 
951             render_task.location = RenderTaskLocation::TextureCache(
952                 texture_id,
953                 texture_layer,
954                 uv_rect.to_i32()
955             );
956         }
957 
958         // Finally, return the texture cache handle that we know
959         // is now up to date.
960         texture_cache.get(&cache_entry.handle)
961     }
962 }
963 
964 // TODO(gw): Rounding the content rect here to device pixels is not
965 // technically correct. Ideally we should ceil() here, and ensure that
966 // the extra part pixel in the case of fractional sizes is correctly
967 // handled. For now, just use rounding which passes the existing
968 // Gecko tests.
969 // Note: zero-square tasks are prohibited in WR task tree, so
970 // we ensure each dimension to be at least the length of 1 after rounding.
to_cache_size(size: DeviceSize) -> DeviceIntSize971 pub fn to_cache_size(size: DeviceSize) -> DeviceIntSize {
972     DeviceIntSize::new(
973         1.max(size.width.round() as i32),
974         1.max(size.height.round() as i32),
975     )
976 }
977