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::{BuiltDisplayList, ColorF, DeviceIntPoint, DeviceIntRect, DevicePixelScale};
6 use api::{DeviceUintPoint, DeviceUintRect, DeviceUintSize, DocumentLayer, FontRenderMode};
7 use api::{LayerRect, LayerSize, PipelineId, PremultipliedColorF, WorldPoint};
8 use clip::{ClipChain, ClipStore};
9 use clip_scroll_node::{ClipScrollNode};
10 use clip_scroll_tree::{ClipScrollNodeIndex, ClipScrollTree};
11 use display_list_flattener::{DisplayListFlattener};
12 use gpu_cache::GpuCache;
13 use gpu_types::{ClipChainRectIndex, ClipScrollNodeData, PictureType};
14 use hit_test::{HitTester, HitTestingRun};
15 use internal_types::{FastHashMap};
16 use picture::{ContentOrigin};
17 use prim_store::{CachedGradient, PrimitiveIndex, PrimitiveRun, PrimitiveStore};
18 use profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
19 use render_backend::FrameId;
20 use render_task::{ClearMode, RenderTask, RenderTaskId, RenderTaskLocation, RenderTaskTree};
21 use resource_cache::{ResourceCache};
22 use scene::{ScenePipeline, SceneProperties};
23 use std::{mem, f32};
24 use std::sync::Arc;
25 use tiling::{Frame, RenderPass, RenderPassKind, RenderTargetContext, RenderTargetKind};
26 use tiling::ScrollbarPrimitive;
27 use util::{self, MaxRect, WorldToLayerFastTransform};
28 
29 #[derive(Clone, Copy)]
30 #[cfg_attr(feature = "capture", derive(Serialize))]
31 #[cfg_attr(feature = "replay", derive(Deserialize))]
32 pub struct FrameBuilderConfig {
33     pub enable_scrollbars: bool,
34     pub default_font_render_mode: FontRenderMode,
35     pub debug: bool,
36     pub dual_source_blending_is_supported: bool,
37     pub dual_source_blending_is_enabled: bool,
38 }
39 
40 /// A builder structure for `tiling::Frame`
41 pub struct FrameBuilder {
42     screen_rect: DeviceUintRect,
43     background_color: Option<ColorF>,
44     window_size: DeviceUintSize,
45     pub prim_store: PrimitiveStore,
46     pub clip_store: ClipStore,
47     pub hit_testing_runs: Vec<HitTestingRun>,
48     pub config: FrameBuilderConfig,
49     pub cached_gradients: Vec<CachedGradient>,
50     pub scrollbar_prims: Vec<ScrollbarPrimitive>,
51 }
52 
53 pub struct FrameBuildingContext<'a> {
54     pub device_pixel_scale: DevicePixelScale,
55     pub scene_properties: &'a SceneProperties,
56     pub pipelines: &'a FastHashMap<PipelineId, Arc<ScenePipeline>>,
57     pub screen_rect: DeviceIntRect,
58     pub clip_scroll_tree: &'a ClipScrollTree,
59     pub node_data: &'a [ClipScrollNodeData],
60 }
61 
62 pub struct FrameBuildingState<'a> {
63     pub render_tasks: &'a mut RenderTaskTree,
64     pub profile_counters: &'a mut FrameProfileCounters,
65     pub clip_store: &'a mut ClipStore,
66     pub local_clip_rects: &'a mut Vec<LayerRect>,
67     pub resource_cache: &'a mut ResourceCache,
68     pub gpu_cache: &'a mut GpuCache,
69     pub cached_gradients: &'a mut [CachedGradient],
70 }
71 
72 pub struct PictureContext<'a> {
73     pub pipeline_id: PipelineId,
74     pub perform_culling: bool,
75     pub prim_runs: Vec<PrimitiveRun>,
76     pub original_reference_frame_index: Option<ClipScrollNodeIndex>,
77     pub display_list: &'a BuiltDisplayList,
78     pub draw_text_transformed: bool,
79     pub inv_world_transform: Option<WorldToLayerFastTransform>,
80 }
81 
82 pub struct PictureState {
83     pub tasks: Vec<RenderTaskId>,
84 }
85 
86 impl PictureState {
new() -> PictureState87     pub fn new() -> PictureState {
88         PictureState {
89             tasks: Vec::new(),
90         }
91     }
92 }
93 
94 pub struct PrimitiveRunContext<'a> {
95     pub clip_chain: &'a ClipChain,
96     pub scroll_node: &'a ClipScrollNode,
97     pub clip_chain_rect_index: ClipChainRectIndex,
98 }
99 
100 impl<'a> PrimitiveRunContext<'a> {
new( clip_chain: &'a ClipChain, scroll_node: &'a ClipScrollNode, clip_chain_rect_index: ClipChainRectIndex, ) -> Self101     pub fn new(
102         clip_chain: &'a ClipChain,
103         scroll_node: &'a ClipScrollNode,
104         clip_chain_rect_index: ClipChainRectIndex,
105     ) -> Self {
106         PrimitiveRunContext {
107             clip_chain,
108             scroll_node,
109             clip_chain_rect_index,
110         }
111     }
112 }
113 
114 impl FrameBuilder {
empty() -> Self115     pub fn empty() -> Self {
116         FrameBuilder {
117             hit_testing_runs: Vec::new(),
118             cached_gradients: Vec::new(),
119             scrollbar_prims: Vec::new(),
120             prim_store: PrimitiveStore::new(),
121             clip_store: ClipStore::new(),
122             screen_rect: DeviceUintRect::zero(),
123             window_size: DeviceUintSize::zero(),
124             background_color: None,
125             config: FrameBuilderConfig {
126                 enable_scrollbars: false,
127                 default_font_render_mode: FontRenderMode::Mono,
128                 debug: false,
129                 dual_source_blending_is_enabled: true,
130                 dual_source_blending_is_supported: false,
131             },
132         }
133     }
134 
with_display_list_flattener( screen_rect: DeviceUintRect, background_color: Option<ColorF>, window_size: DeviceUintSize, flattener: DisplayListFlattener, ) -> Self135     pub fn with_display_list_flattener(
136         screen_rect: DeviceUintRect,
137         background_color: Option<ColorF>,
138         window_size: DeviceUintSize,
139         flattener: DisplayListFlattener,
140     ) -> Self {
141         FrameBuilder {
142             hit_testing_runs: flattener.hit_testing_runs,
143             cached_gradients: flattener.cached_gradients,
144             scrollbar_prims: flattener.scrollbar_prims,
145             prim_store: flattener.prim_store,
146             clip_store: flattener.clip_store,
147             screen_rect,
148             background_color,
149             window_size,
150             config: flattener.config,
151         }
152     }
153 
154     /// Compute the contribution (bounding rectangles, and resources) of layers and their
155     /// primitives in screen space.
build_layer_screen_rects_and_cull_layers( &mut self, clip_scroll_tree: &ClipScrollTree, pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, render_tasks: &mut RenderTaskTree, profile_counters: &mut FrameProfileCounters, device_pixel_scale: DevicePixelScale, scene_properties: &SceneProperties, local_clip_rects: &mut Vec<LayerRect>, node_data: &[ClipScrollNodeData], ) -> Option<RenderTaskId>156     fn build_layer_screen_rects_and_cull_layers(
157         &mut self,
158         clip_scroll_tree: &ClipScrollTree,
159         pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
160         resource_cache: &mut ResourceCache,
161         gpu_cache: &mut GpuCache,
162         render_tasks: &mut RenderTaskTree,
163         profile_counters: &mut FrameProfileCounters,
164         device_pixel_scale: DevicePixelScale,
165         scene_properties: &SceneProperties,
166         local_clip_rects: &mut Vec<LayerRect>,
167         node_data: &[ClipScrollNodeData],
168     ) -> Option<RenderTaskId> {
169         profile_scope!("cull");
170 
171         if self.prim_store.cpu_pictures.is_empty() {
172             return None
173         }
174 
175         // The root picture is always the first one added.
176         let root_clip_scroll_node =
177             &clip_scroll_tree.nodes[clip_scroll_tree.root_reference_frame_index().0];
178 
179         let display_list = &pipelines
180             .get(&root_clip_scroll_node.pipeline_id)
181             .expect("No display list?")
182             .display_list;
183 
184         let frame_context = FrameBuildingContext {
185             device_pixel_scale,
186             scene_properties,
187             pipelines,
188             screen_rect: self.screen_rect.to_i32(),
189             clip_scroll_tree,
190             node_data,
191         };
192 
193         let mut frame_state = FrameBuildingState {
194             render_tasks,
195             profile_counters,
196             clip_store: &mut self.clip_store,
197             local_clip_rects,
198             resource_cache,
199             gpu_cache,
200             cached_gradients: &mut self.cached_gradients,
201         };
202 
203         let pic_context = PictureContext {
204             pipeline_id: root_clip_scroll_node.pipeline_id,
205             perform_culling: true,
206             prim_runs: mem::replace(&mut self.prim_store.cpu_pictures[0].runs, Vec::new()),
207             original_reference_frame_index: None,
208             display_list,
209             draw_text_transformed: true,
210             inv_world_transform: None,
211         };
212 
213         let mut pic_state = PictureState::new();
214 
215         self.prim_store.reset_prim_visibility();
216         self.prim_store.prepare_prim_runs(
217             &pic_context,
218             &mut pic_state,
219             &frame_context,
220             &mut frame_state,
221         );
222 
223         let pic = &mut self.prim_store.cpu_pictures[0];
224         pic.runs = pic_context.prim_runs;
225 
226         let root_render_task = RenderTask::new_picture(
227             RenderTaskLocation::Fixed(frame_context.screen_rect),
228             PrimitiveIndex(0),
229             RenderTargetKind::Color,
230             ContentOrigin::Screen(DeviceIntPoint::zero()),
231             PremultipliedColorF::TRANSPARENT,
232             ClearMode::Transparent,
233             pic_state.tasks,
234             PictureType::Image,
235         );
236 
237         let render_task_id = frame_state.render_tasks.add(root_render_task);
238         pic.surface = Some(render_task_id);
239         Some(render_task_id)
240     }
241 
update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache)242     fn update_scroll_bars(&mut self, clip_scroll_tree: &ClipScrollTree, gpu_cache: &mut GpuCache) {
243         static SCROLLBAR_PADDING: f32 = 8.0;
244 
245         for scrollbar_prim in &self.scrollbar_prims {
246             let metadata = &mut self.prim_store.cpu_metadata[scrollbar_prim.prim_index.0];
247             let scroll_frame = &clip_scroll_tree.nodes[scrollbar_prim.scroll_frame_index.0];
248 
249             // Invalidate what's in the cache so it will get rebuilt.
250             gpu_cache.invalidate(&metadata.gpu_location);
251 
252             let scrollable_distance = scroll_frame.scrollable_size().height;
253             if scrollable_distance <= 0.0 {
254                 metadata.local_clip_rect.size = LayerSize::zero();
255                 continue;
256             }
257             let amount_scrolled = -scroll_frame.scroll_offset().y / scrollable_distance;
258 
259             let frame_rect = scrollbar_prim.frame_rect;
260             let min_y = frame_rect.origin.y + SCROLLBAR_PADDING;
261             let max_y = frame_rect.origin.y + frame_rect.size.height -
262                 (SCROLLBAR_PADDING + metadata.local_rect.size.height);
263 
264             metadata.local_rect.origin.x = frame_rect.origin.x + frame_rect.size.width -
265                 (metadata.local_rect.size.width + SCROLLBAR_PADDING);
266             metadata.local_rect.origin.y = util::lerp(min_y, max_y, amount_scrolled);
267             metadata.local_clip_rect = metadata.local_rect;
268         }
269     }
270 
build( &mut self, resource_cache: &mut ResourceCache, gpu_cache: &mut GpuCache, frame_id: FrameId, clip_scroll_tree: &mut ClipScrollTree, pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>, device_pixel_scale: DevicePixelScale, layer: DocumentLayer, pan: WorldPoint, texture_cache_profile: &mut TextureCacheProfileCounters, gpu_cache_profile: &mut GpuCacheProfileCounters, scene_properties: &SceneProperties, ) -> Frame271     pub fn build(
272         &mut self,
273         resource_cache: &mut ResourceCache,
274         gpu_cache: &mut GpuCache,
275         frame_id: FrameId,
276         clip_scroll_tree: &mut ClipScrollTree,
277         pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
278         device_pixel_scale: DevicePixelScale,
279         layer: DocumentLayer,
280         pan: WorldPoint,
281         texture_cache_profile: &mut TextureCacheProfileCounters,
282         gpu_cache_profile: &mut GpuCacheProfileCounters,
283         scene_properties: &SceneProperties,
284     ) -> Frame {
285         profile_scope!("build");
286         debug_assert!(
287             DeviceUintRect::new(DeviceUintPoint::zero(), self.window_size)
288                 .contains_rect(&self.screen_rect)
289         );
290 
291         let mut profile_counters = FrameProfileCounters::new();
292         profile_counters
293             .total_primitives
294             .set(self.prim_store.prim_count());
295 
296         resource_cache.begin_frame(frame_id);
297         gpu_cache.begin_frame();
298 
299         let mut node_data = Vec::with_capacity(clip_scroll_tree.nodes.len());
300         let total_prim_runs =
301             self.prim_store.cpu_pictures.iter().fold(1, |count, ref pic| count + pic.runs.len());
302         let mut clip_chain_local_clip_rects = Vec::with_capacity(total_prim_runs);
303         clip_chain_local_clip_rects.push(LayerRect::max_rect());
304 
305         clip_scroll_tree.update_tree(
306             &self.screen_rect.to_i32(),
307             device_pixel_scale,
308             &mut self.clip_store,
309             resource_cache,
310             gpu_cache,
311             pan,
312             &mut node_data,
313             scene_properties,
314         );
315 
316         self.update_scroll_bars(clip_scroll_tree, gpu_cache);
317 
318         let mut render_tasks = RenderTaskTree::new();
319 
320         let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
321             clip_scroll_tree,
322             pipelines,
323             resource_cache,
324             gpu_cache,
325             &mut render_tasks,
326             &mut profile_counters,
327             device_pixel_scale,
328             scene_properties,
329             &mut clip_chain_local_clip_rects,
330             &node_data,
331         );
332 
333         let mut passes = Vec::new();
334         resource_cache.block_until_all_resources_added(gpu_cache, texture_cache_profile);
335 
336         if let Some(main_render_task_id) = main_render_task_id {
337             let mut required_pass_count = 0;
338             render_tasks.max_depth(main_render_task_id, 0, &mut required_pass_count);
339             assert_ne!(required_pass_count, 0);
340 
341             // Do the allocations now, assigning each tile's tasks to a render
342             // pass and target as required.
343             for _ in 0 .. required_pass_count - 1 {
344                 passes.push(RenderPass::new_off_screen(self.screen_rect.size.to_i32()));
345             }
346             passes.push(RenderPass::new_main_framebuffer(self.screen_rect.size.to_i32()));
347 
348             render_tasks.assign_to_passes(
349                 main_render_task_id,
350                 required_pass_count - 1,
351                 &mut passes,
352             );
353         }
354 
355         let mut deferred_resolves = vec![];
356         let mut has_texture_cache_tasks = false;
357         let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
358                                        self.config.dual_source_blending_is_supported;
359 
360         for pass in &mut passes {
361             let ctx = RenderTargetContext {
362                 device_pixel_scale,
363                 prim_store: &self.prim_store,
364                 resource_cache,
365                 clip_scroll_tree,
366                 use_dual_source_blending,
367                 node_data: &node_data,
368                 cached_gradients: &self.cached_gradients,
369             };
370 
371             pass.build(
372                 &ctx,
373                 gpu_cache,
374                 &mut render_tasks,
375                 &mut deferred_resolves,
376                 &self.clip_store,
377             );
378 
379             if let RenderPassKind::OffScreen { ref texture_cache, .. } = pass.kind {
380                 has_texture_cache_tasks |= !texture_cache.is_empty();
381             }
382         }
383 
384         let gpu_cache_frame_id = gpu_cache.end_frame(gpu_cache_profile);
385 
386         render_tasks.build();
387 
388         resource_cache.end_frame();
389 
390         Frame {
391             window_size: self.window_size,
392             inner_rect: self.screen_rect,
393             device_pixel_ratio: device_pixel_scale.0,
394             background_color: self.background_color,
395             layer,
396             profile_counters,
397             passes,
398             node_data,
399             clip_chain_local_clip_rects,
400             render_tasks,
401             deferred_resolves,
402             gpu_cache_frame_id,
403             has_been_rendered: false,
404             has_texture_cache_tasks,
405         }
406     }
407 
create_hit_tester(&mut self, clip_scroll_tree: &ClipScrollTree) -> HitTester408     pub fn create_hit_tester(&mut self, clip_scroll_tree: &ClipScrollTree) -> HitTester {
409         HitTester::new(
410             &self.hit_testing_runs,
411             &clip_scroll_tree,
412             &self.clip_store
413         )
414     }
415 }
416 
417