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