webrender/
frame_builder.rs

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
5use api::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF};
6use api::units::*;
7use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
8use crate::clip::{ClipStore, ClipChainStack};
9use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex, CoordinateSystemId};
10use crate::composite::{CompositorKind, CompositeState};
11use crate::debug_render::DebugItem;
12use crate::gpu_cache::{GpuCache, GpuCacheHandle};
13use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
14use crate::gpu_types::TransformData;
15use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
16use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
17use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
18use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
19use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask};
20use crate::profiler::{FrameProfileCounters, TextureCacheProfileCounters, ResourceProfileCounters};
21use crate::render_backend::{DataStores, FrameStamp, FrameId};
22use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget};
23use crate::render_target::{RenderTargetContext, RenderTargetKind};
24use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, RenderTaskGraphCounters};
25use crate::render_task_graph::{RenderPassKind, RenderPass};
26use crate::render_task::{RenderTask, RenderTaskLocation, RenderTaskKind};
27use crate::resource_cache::{ResourceCache};
28use crate::scene::{BuiltScene, SceneProperties};
29use crate::segment::SegmentBuilder;
30use std::{f32, mem};
31use crate::util::MaxRect;
32
33
34#[derive(Clone, Copy, Debug, PartialEq)]
35#[cfg_attr(feature = "capture", derive(Serialize))]
36#[cfg_attr(feature = "replay", derive(Deserialize))]
37pub enum ChasePrimitive {
38    Nothing,
39    Id(PrimitiveDebugId),
40    LocalRect(LayoutRect),
41}
42
43impl Default for ChasePrimitive {
44    fn default() -> Self {
45        ChasePrimitive::Nothing
46    }
47}
48
49#[derive(Clone, Copy, Debug)]
50#[cfg_attr(feature = "capture", derive(Serialize))]
51#[cfg_attr(feature = "replay", derive(Deserialize))]
52pub struct FrameBuilderConfig {
53    pub default_font_render_mode: FontRenderMode,
54    pub dual_source_blending_is_supported: bool,
55    pub dual_source_blending_is_enabled: bool,
56    pub chase_primitive: ChasePrimitive,
57    /// The immutable global picture caching enable from `RendererOptions`
58    pub global_enable_picture_caching: bool,
59    /// True if we're running tests (i.e. via wrench).
60    pub testing: bool,
61    pub gpu_supports_fast_clears: bool,
62    pub gpu_supports_advanced_blend: bool,
63    pub advanced_blend_is_coherent: bool,
64    pub batch_lookback_count: usize,
65    pub background_color: Option<ColorF>,
66    pub compositor_kind: CompositorKind,
67}
68
69/// A set of common / global resources that are retained between
70/// new display lists, such that any GPU cache handles can be
71/// persisted even when a new display list arrives.
72#[cfg_attr(feature = "capture", derive(Serialize))]
73pub struct FrameGlobalResources {
74    /// The image shader block for the most common / default
75    /// set of image parameters (color white, stretch == rect.size).
76    pub default_image_handle: GpuCacheHandle,
77}
78
79impl FrameGlobalResources {
80    pub fn empty() -> Self {
81        FrameGlobalResources {
82            default_image_handle: GpuCacheHandle::new(),
83        }
84    }
85
86    pub fn update(
87        &mut self,
88        gpu_cache: &mut GpuCache,
89    ) {
90        if let Some(mut request) = gpu_cache.request(&mut self.default_image_handle) {
91            request.push(PremultipliedColorF::WHITE);
92            request.push(PremultipliedColorF::WHITE);
93            request.push([
94                -1.0,       // -ve means use prim rect for stretch size
95                0.0,
96                0.0,
97                0.0,
98            ]);
99        }
100    }
101}
102
103/// Produces the frames that are sent to the renderer.
104#[cfg_attr(feature = "capture", derive(Serialize))]
105pub struct FrameBuilder {
106    /// Cache of surface tiles from the previous frame builder
107    /// that can optionally be consumed by this frame builder.
108    pending_retained_tiles: RetainedTiles,
109    pub globals: FrameGlobalResources,
110}
111
112pub struct FrameVisibilityContext<'a> {
113    pub clip_scroll_tree: &'a ClipScrollTree,
114    pub global_screen_world_rect: WorldRect,
115    pub global_device_pixel_scale: DevicePixelScale,
116    pub surfaces: &'a [SurfaceInfo],
117    pub debug_flags: DebugFlags,
118    pub scene_properties: &'a SceneProperties,
119    pub config: &'a FrameBuilderConfig,
120}
121
122pub struct FrameVisibilityState<'a> {
123    pub clip_store: &'a mut ClipStore,
124    pub resource_cache: &'a mut ResourceCache,
125    pub gpu_cache: &'a mut GpuCache,
126    pub scratch: &'a mut PrimitiveScratchBuffer,
127    pub tile_cache: Option<Box<TileCacheInstance>>,
128    pub retained_tiles: &'a mut RetainedTiles,
129    pub data_stores: &'a mut DataStores,
130    pub clip_chain_stack: ClipChainStack,
131    pub render_tasks: &'a mut RenderTaskGraph,
132    pub composite_state: &'a mut CompositeState,
133}
134
135pub struct FrameBuildingContext<'a> {
136    pub global_device_pixel_scale: DevicePixelScale,
137    pub scene_properties: &'a SceneProperties,
138    pub global_screen_world_rect: WorldRect,
139    pub clip_scroll_tree: &'a ClipScrollTree,
140    pub max_local_clip: LayoutRect,
141    pub debug_flags: DebugFlags,
142    pub fb_config: &'a FrameBuilderConfig,
143}
144
145pub struct FrameBuildingState<'a> {
146    pub render_tasks: &'a mut RenderTaskGraph,
147    pub profile_counters: &'a mut FrameProfileCounters,
148    pub clip_store: &'a mut ClipStore,
149    pub resource_cache: &'a mut ResourceCache,
150    pub gpu_cache: &'a mut GpuCache,
151    pub transforms: &'a mut TransformPalette,
152    pub segment_builder: SegmentBuilder,
153    pub surfaces: &'a mut Vec<SurfaceInfo>,
154    pub dirty_region_stack: Vec<DirtyRegion>,
155    pub composite_state: &'a mut CompositeState,
156}
157
158impl<'a> FrameBuildingState<'a> {
159    /// Retrieve the current dirty region during primitive traversal.
160    pub fn current_dirty_region(&self) -> &DirtyRegion {
161        self.dirty_region_stack.last().unwrap()
162    }
163
164    /// Push a new dirty region for child primitives to cull / clip against.
165    pub fn push_dirty_region(&mut self, region: DirtyRegion) {
166        self.dirty_region_stack.push(region);
167    }
168
169    /// Pop the top dirty region from the stack.
170    pub fn pop_dirty_region(&mut self) {
171        self.dirty_region_stack.pop().unwrap();
172    }
173}
174
175/// Immutable context of a picture when processing children.
176#[derive(Debug)]
177pub struct PictureContext {
178    pub pic_index: PictureIndex,
179    pub apply_local_clip_rect: bool,
180    pub is_passthrough: bool,
181    pub surface_spatial_node_index: SpatialNodeIndex,
182    pub raster_spatial_node_index: SpatialNodeIndex,
183    /// The surface that this picture will render on.
184    pub surface_index: SurfaceIndex,
185    pub dirty_region_count: usize,
186    pub subpixel_mode: SubpixelMode,
187}
188
189/// Mutable state of a picture that gets modified when
190/// the children are processed.
191pub struct PictureState {
192    pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
193    pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
194    pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
195    pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
196    /// If the plane splitter, the primitives get added to it instead of
197    /// batching into their parent pictures.
198    pub plane_splitter: Option<PlaneSplitter>,
199}
200
201impl FrameBuilder {
202    pub fn new() -> Self {
203        FrameBuilder {
204            pending_retained_tiles: RetainedTiles::new(),
205            globals: FrameGlobalResources::empty(),
206        }
207    }
208
209    /// Provide any cached surface tiles from the previous frame builder
210    /// to a new frame builder. These will be consumed or dropped the
211    /// first time a new frame builder creates a frame.
212    pub fn set_retained_resources(&mut self, retained_tiles: RetainedTiles) {
213        // In general, the pending retained tiles are consumed by the frame
214        // builder the first time a frame is built after a new scene has
215        // arrived. However, if two scenes arrive in quick succession, the
216        // frame builder may not have had a chance to build a frame and
217        // consume the pending tiles. In this case, the pending tiles will
218        // be lost, causing a full invalidation of the entire screen. To
219        // avoid this, if there are still pending tiles, include them in
220        // the retained tiles passed to the next frame builder.
221        self.pending_retained_tiles.merge(retained_tiles);
222    }
223
224    /// Compute the contribution (bounding rectangles, and resources) of layers and their
225    /// primitives in screen space.
226    fn build_layer_screen_rects_and_cull_layers(
227        &mut self,
228        scene: &mut BuiltScene,
229        global_screen_world_rect: WorldRect,
230        resource_cache: &mut ResourceCache,
231        gpu_cache: &mut GpuCache,
232        render_tasks: &mut RenderTaskGraph,
233        profile_counters: &mut FrameProfileCounters,
234        global_device_pixel_scale: DevicePixelScale,
235        scene_properties: &SceneProperties,
236        transform_palette: &mut TransformPalette,
237        data_stores: &mut DataStores,
238        surfaces: &mut Vec<SurfaceInfo>,
239        scratch: &mut PrimitiveScratchBuffer,
240        debug_flags: DebugFlags,
241        texture_cache_profile: &mut TextureCacheProfileCounters,
242        composite_state: &mut CompositeState,
243    ) -> Option<RenderTaskId> {
244        profile_scope!("cull");
245
246        if scene.prim_store.pictures.is_empty() {
247            return None
248        }
249
250        scratch.begin_frame();
251
252        let root_spatial_node_index = scene.clip_scroll_tree.root_reference_frame_index();
253
254        const MAX_CLIP_COORD: f32 = 1.0e9;
255
256        let frame_context = FrameBuildingContext {
257            global_device_pixel_scale,
258            scene_properties,
259            global_screen_world_rect,
260            clip_scroll_tree: &scene.clip_scroll_tree,
261            max_local_clip: LayoutRect::new(
262                LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
263                LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD),
264            ),
265            debug_flags,
266            fb_config: &scene.config,
267        };
268
269        let root_render_task = RenderTask::new_picture(
270            RenderTaskLocation::Fixed(scene.output_rect),
271            scene.output_rect.size.to_f32(),
272            scene.root_pic_index,
273            DeviceIntPoint::zero(),
274            UvRectKind::Rect,
275            ROOT_SPATIAL_NODE_INDEX,
276            global_device_pixel_scale,
277            PrimitiveVisibilityMask::all(),
278            None,
279        );
280
281        let root_render_task_id = render_tasks.add(root_render_task);
282
283        // Construct a dummy root surface, that represents the
284        // main framebuffer surface.
285        let root_surface = SurfaceInfo::new(
286            ROOT_SPATIAL_NODE_INDEX,
287            ROOT_SPATIAL_NODE_INDEX,
288            0.0,
289            global_screen_world_rect,
290            &scene.clip_scroll_tree,
291            global_device_pixel_scale,
292        );
293        surfaces.push(root_surface);
294
295        let mut retained_tiles = mem::replace(
296            &mut self.pending_retained_tiles,
297            RetainedTiles::new(),
298        );
299
300        // The first major pass of building a frame is to walk the picture
301        // tree. This pass must be quick (it should never touch individual
302        // primitives). For now, all we do here is determine which pictures
303        // will create surfaces. In the future, this will be expanded to
304        // set up render tasks, determine scaling of surfaces, and detect
305        // which surfaces have valid cached surfaces that don't need to
306        // be rendered this frame.
307        PictureUpdateState::update_all(
308            surfaces,
309            scene.root_pic_index,
310            &mut scene.prim_store.pictures,
311            &frame_context,
312            gpu_cache,
313            &scene.clip_store,
314            data_stores,
315            composite_state,
316        );
317
318        {
319            profile_marker!("UpdateVisibility");
320
321            let visibility_context = FrameVisibilityContext {
322                global_device_pixel_scale,
323                clip_scroll_tree: &scene.clip_scroll_tree,
324                global_screen_world_rect,
325                surfaces,
326                debug_flags,
327                scene_properties,
328                config: &scene.config,
329            };
330
331            let mut visibility_state = FrameVisibilityState {
332                resource_cache,
333                gpu_cache,
334                clip_store: &mut scene.clip_store,
335                scratch,
336                tile_cache: None,
337                retained_tiles: &mut retained_tiles,
338                data_stores,
339                clip_chain_stack: ClipChainStack::new(),
340                render_tasks,
341                composite_state,
342            };
343
344            scene.prim_store.update_visibility(
345                scene.root_pic_index,
346                ROOT_SURFACE_INDEX,
347                &global_screen_world_rect,
348                &visibility_context,
349                &mut visibility_state,
350            );
351
352            // When a new display list is processed by WR, the existing tiles from
353            // any picture cache are stored in the `retained_tiles` field above. This
354            // allows the first frame of a new display list to reuse any existing tiles
355            // and surfaces that match. Once the `update_visibility` call above is
356            // complete, any tiles that are left remaining in the `retained_tiles`
357            // map are not needed and will be dropped. For simple compositing mode,
358            // this is fine, since texture cache handles are garbage collected at
359            // the end of each frame. However, if we're in native compositor mode,
360            // we need to manually clean up any native compositor surfaces that were
361            // allocated by these tiles.
362            for (_, mut cache_state) in visibility_state.retained_tiles.caches.drain() {
363                if let Some(native_surface_id) = cache_state.native_surface_id.take() {
364                    visibility_state.resource_cache.destroy_compositor_surface(native_surface_id);
365                }
366            }
367        }
368
369        let mut frame_state = FrameBuildingState {
370            render_tasks,
371            profile_counters,
372            clip_store: &mut scene.clip_store,
373            resource_cache,
374            gpu_cache,
375            transforms: transform_palette,
376            segment_builder: SegmentBuilder::new(),
377            surfaces,
378            dirty_region_stack: Vec::new(),
379            composite_state,
380        };
381
382        frame_state
383            .surfaces
384            .first_mut()
385            .unwrap()
386            .render_tasks = Some(SurfaceRenderTasks {
387                root: root_render_task_id,
388                port: root_render_task_id,
389            });
390
391        // Push a default dirty region which culls primitives
392        // against the screen world rect, in absence of any
393        // other dirty regions.
394        let mut default_dirty_region = DirtyRegion::new();
395        default_dirty_region.push(
396            frame_context.global_screen_world_rect,
397            PrimitiveVisibilityMask::all(),
398        );
399        frame_state.push_dirty_region(default_dirty_region);
400
401        let (pic_context, mut pic_state, mut prim_list) = scene
402            .prim_store
403            .pictures[scene.root_pic_index.0]
404            .take_context(
405                scene.root_pic_index,
406                WorldRect::max_rect(),
407                root_spatial_node_index,
408                root_spatial_node_index,
409                ROOT_SURFACE_INDEX,
410                SubpixelMode::Allow,
411                &mut frame_state,
412                &frame_context,
413                scratch,
414            )
415            .unwrap();
416
417        {
418            profile_marker!("PreparePrims");
419
420            scene.prim_store.prepare_primitives(
421                &mut prim_list,
422                &pic_context,
423                &mut pic_state,
424                &frame_context,
425                &mut frame_state,
426                data_stores,
427                scratch,
428            );
429        }
430
431        let pic = &mut scene.prim_store.pictures[scene.root_pic_index.0];
432        pic.restore_context(
433            prim_list,
434            pic_context,
435            pic_state,
436            &mut frame_state,
437        );
438
439        frame_state.pop_dirty_region();
440
441        {
442            profile_marker!("BlockOnResources");
443
444            resource_cache.block_until_all_resources_added(gpu_cache,
445                                                           render_tasks,
446                                                           texture_cache_profile);
447        }
448
449        Some(root_render_task_id)
450    }
451
452    pub fn build(
453        &mut self,
454        scene: &mut BuiltScene,
455        resource_cache: &mut ResourceCache,
456        gpu_cache: &mut GpuCache,
457        stamp: FrameStamp,
458        global_device_pixel_scale: DevicePixelScale,
459        layer: DocumentLayer,
460        device_origin: DeviceIntPoint,
461        pan: WorldPoint,
462        resource_profile: &mut ResourceProfileCounters,
463        scene_properties: &SceneProperties,
464        data_stores: &mut DataStores,
465        scratch: &mut PrimitiveScratchBuffer,
466        render_task_counters: &mut RenderTaskGraphCounters,
467        debug_flags: DebugFlags,
468    ) -> Frame {
469        profile_scope!("build");
470        profile_marker!("BuildFrame");
471
472        let mut profile_counters = FrameProfileCounters::new();
473        profile_counters
474            .total_primitives
475            .set(scene.prim_store.prim_count());
476        resource_profile.content_slices.set(scene.content_slice_count);
477        resource_cache.begin_frame(stamp);
478        gpu_cache.begin_frame(stamp);
479
480        self.globals.update(gpu_cache);
481
482        scene.clip_scroll_tree.update_tree(
483            pan,
484            global_device_pixel_scale,
485            scene_properties,
486        );
487        let mut transform_palette = scene.clip_scroll_tree.build_transform_palette();
488        scene.clip_store.clear_old_instances();
489
490        let mut render_tasks = RenderTaskGraph::new(
491            stamp.frame_id(),
492            render_task_counters,
493        );
494        let mut surfaces = Vec::new();
495
496        let output_size = scene.output_rect.size.to_i32();
497        let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
498
499        // Determine if we will draw this frame with picture caching enabled. This depends on:
500        // (1) If globally enabled when WR was initialized
501        // (2) If current debug flags allow picture caching
502        // (3) Whether we are currently pinch zooming
503        // (4) If any picture cache spatial nodes are not in the root coordinate system
504        let picture_caching_is_enabled =
505            scene.config.global_enable_picture_caching &&
506            !debug_flags.contains(DebugFlags::DISABLE_PICTURE_CACHING) &&
507            !scene.picture_cache_spatial_nodes.iter().any(|spatial_node_index| {
508                let spatial_node = &scene
509                    .clip_scroll_tree
510                    .spatial_nodes[spatial_node_index.0 as usize];
511                spatial_node.coordinate_system_id != CoordinateSystemId::root() ||
512                    spatial_node.is_ancestor_or_self_zooming
513            });
514
515        let mut composite_state = CompositeState::new(
516            scene.config.compositor_kind,
517            picture_caching_is_enabled,
518            global_device_pixel_scale,
519        );
520
521        let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
522            scene,
523            screen_world_rect,
524            resource_cache,
525            gpu_cache,
526            &mut render_tasks,
527            &mut profile_counters,
528            global_device_pixel_scale,
529            scene_properties,
530            &mut transform_palette,
531            data_stores,
532            &mut surfaces,
533            scratch,
534            debug_flags,
535            &mut resource_profile.texture_cache,
536            &mut composite_state,
537        );
538
539        let mut passes;
540        let mut deferred_resolves = vec![];
541        let mut has_texture_cache_tasks = false;
542        let mut prim_headers = PrimitiveHeaders::new();
543
544        {
545            profile_marker!("Batching");
546
547            passes = render_tasks.generate_passes(
548                main_render_task_id,
549                output_size,
550                scene.config.gpu_supports_fast_clears,
551            );
552
553            // Used to generated a unique z-buffer value per primitive.
554            let mut z_generator = ZBufferIdGenerator::new(layer);
555            let use_dual_source_blending = scene.config.dual_source_blending_is_enabled &&
556                                           scene.config.dual_source_blending_is_supported;
557
558            for pass in &mut passes {
559                let mut ctx = RenderTargetContext {
560                    global_device_pixel_scale,
561                    prim_store: &scene.prim_store,
562                    resource_cache,
563                    use_dual_source_blending,
564                    use_advanced_blending: scene.config.gpu_supports_advanced_blend,
565                    break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
566                    batch_lookback_count: scene.config.batch_lookback_count,
567                    clip_scroll_tree: &scene.clip_scroll_tree,
568                    data_stores,
569                    surfaces: &surfaces,
570                    scratch,
571                    screen_world_rect,
572                    globals: &self.globals,
573                };
574
575                build_render_pass(
576                    pass,
577                    &mut ctx,
578                    gpu_cache,
579                    &mut render_tasks,
580                    &mut deferred_resolves,
581                    &scene.clip_store,
582                    &mut transform_palette,
583                    &mut prim_headers,
584                    &mut z_generator,
585                    &mut composite_state,
586                );
587
588                match pass.kind {
589                    RenderPassKind::MainFramebuffer { .. } => {}
590                    RenderPassKind::OffScreen {
591                        ref texture_cache,
592                        ref picture_cache,
593                        ..
594                    } => {
595                        has_texture_cache_tasks |= !texture_cache.is_empty();
596                        has_texture_cache_tasks |= !picture_cache.is_empty();
597                    }
598                }
599            }
600        }
601
602        let gpu_cache_frame_id = gpu_cache.end_frame(&mut resource_profile.gpu_cache).frame_id();
603
604        render_tasks.write_task_data();
605        *render_task_counters = render_tasks.counters();
606        resource_cache.end_frame(&mut resource_profile.texture_cache);
607
608        Frame {
609            content_origin: scene.output_rect.origin,
610            device_rect: DeviceIntRect::new(
611                device_origin,
612                scene.output_rect.size,
613            ),
614            layer,
615            profile_counters,
616            passes,
617            transform_palette: transform_palette.finish(),
618            render_tasks,
619            deferred_resolves,
620            gpu_cache_frame_id,
621            has_been_rendered: false,
622            has_texture_cache_tasks,
623            prim_headers,
624            recorded_dirty_regions: mem::replace(&mut scratch.recorded_dirty_regions, Vec::new()),
625            debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
626            composite_state,
627        }
628    }
629}
630
631/// Processes this pass to prepare it for rendering.
632///
633/// Among other things, this allocates output regions for each of our tasks
634/// (added via `add_render_task`) in a RenderTarget and assigns it into that
635/// target.
636pub fn build_render_pass(
637    pass: &mut RenderPass,
638    ctx: &mut RenderTargetContext,
639    gpu_cache: &mut GpuCache,
640    render_tasks: &mut RenderTaskGraph,
641    deferred_resolves: &mut Vec<DeferredResolve>,
642    clip_store: &ClipStore,
643    transforms: &mut TransformPalette,
644    prim_headers: &mut PrimitiveHeaders,
645    z_generator: &mut ZBufferIdGenerator,
646    composite_state: &mut CompositeState,
647) {
648    profile_scope!("RenderPass::build");
649
650    match pass.kind {
651        RenderPassKind::MainFramebuffer { ref mut main_target, .. } => {
652            for &task_id in &pass.tasks {
653                assert_eq!(render_tasks[task_id].target_kind(), RenderTargetKind::Color);
654                main_target.add_task(
655                    task_id,
656                    ctx,
657                    gpu_cache,
658                    render_tasks,
659                    clip_store,
660                    transforms,
661                    deferred_resolves,
662                );
663            }
664            main_target.build(
665                ctx,
666                gpu_cache,
667                render_tasks,
668                deferred_resolves,
669                prim_headers,
670                transforms,
671                z_generator,
672                composite_state,
673            );
674        }
675        RenderPassKind::OffScreen {
676            ref mut color,
677            ref mut alpha,
678            ref mut texture_cache,
679            ref mut picture_cache,
680        } => {
681            let saved_color = if pass.tasks.iter().any(|&task_id| {
682                let t = &render_tasks[task_id];
683                t.target_kind() == RenderTargetKind::Color && t.saved_index.is_some()
684            }) {
685                Some(render_tasks.save_target())
686            } else {
687                None
688            };
689            let saved_alpha = if pass.tasks.iter().any(|&task_id| {
690                let t = &render_tasks[task_id];
691                t.target_kind() == RenderTargetKind::Alpha && t.saved_index.is_some()
692            }) {
693                Some(render_tasks.save_target())
694            } else {
695                None
696            };
697
698            // Collect a list of picture cache tasks, keyed by picture index.
699            // This allows us to only walk that picture root once, adding the
700            // primitives to all relevant batches at the same time.
701            let mut picture_cache_tasks = FastHashMap::default();
702
703            // Step through each task, adding to batches as appropriate.
704            for &task_id in &pass.tasks {
705                let (target_kind, texture_target, layer) = {
706                    let task = &mut render_tasks[task_id];
707                    let target_kind = task.target_kind();
708
709                    // Find a target to assign this task to, or create a new
710                    // one if required.
711                    let (texture_target, layer) = match task.location {
712                        RenderTaskLocation::TextureCache { texture, layer, .. } => {
713                            (Some(texture), layer)
714                        }
715                        RenderTaskLocation::Fixed(..) => {
716                            (None, 0)
717                        }
718                        RenderTaskLocation::Dynamic(ref mut origin, size) => {
719                            let (target_index, alloc_origin) =  match target_kind {
720                                RenderTargetKind::Color => color.allocate(size),
721                                RenderTargetKind::Alpha => alpha.allocate(size),
722                            };
723                            *origin = Some((alloc_origin, target_index));
724                            (None, target_index.0)
725                        }
726                        RenderTaskLocation::PictureCache { .. } => {
727                            // For picture cache tiles, just store them in the map
728                            // of picture cache tasks, to be handled below.
729                            let pic_index = match task.kind {
730                                RenderTaskKind::Picture(ref info) => {
731                                    info.pic_index
732                                }
733                                _ => {
734                                    unreachable!();
735                                }
736                            };
737
738                            picture_cache_tasks
739                                .entry(pic_index)
740                                .or_insert_with(Vec::new)
741                                .push(task_id);
742
743                            continue;
744                        }
745                    };
746
747                    // Replace the pending saved index with a real one
748                    if let Some(index) = task.saved_index {
749                        assert_eq!(index, SavedTargetIndex::PENDING);
750                        task.saved_index = match target_kind {
751                            RenderTargetKind::Color => saved_color,
752                            RenderTargetKind::Alpha => saved_alpha,
753                        };
754                    }
755
756                    // Give the render task an opportunity to add any
757                    // information to the GPU cache, if appropriate.
758                    task.write_gpu_blocks(gpu_cache);
759
760                    (target_kind, texture_target, layer)
761                };
762
763                match texture_target {
764                    Some(texture_target) => {
765                        let texture = texture_cache
766                            .entry((texture_target, layer))
767                            .or_insert_with(||
768                                TextureCacheRenderTarget::new(target_kind)
769                            );
770                        texture.add_task(task_id, render_tasks);
771                    }
772                    None => {
773                        match target_kind {
774                            RenderTargetKind::Color => {
775                                color.targets[layer].add_task(
776                                    task_id,
777                                    ctx,
778                                    gpu_cache,
779                                    render_tasks,
780                                    clip_store,
781                                    transforms,
782                                    deferred_resolves,
783                                )
784                            }
785                            RenderTargetKind::Alpha => {
786                                alpha.targets[layer].add_task(
787                                    task_id,
788                                    ctx,
789                                    gpu_cache,
790                                    render_tasks,
791                                    clip_store,
792                                    transforms,
793                                    deferred_resolves,
794                                )
795                            }
796                        }
797                    }
798                }
799            }
800
801            // For each picture in this pass that has picture cache tiles, create
802            // a batcher per task, and then build batches for each of the tasks
803            // at the same time.
804            for (pic_index, task_ids) in picture_cache_tasks {
805                let pic = &ctx.prim_store.pictures[pic_index.0];
806                let tile_cache = pic.tile_cache.as_ref().expect("bug");
807
808                // Extract raster/surface spatial nodes for this surface.
809                let (root_spatial_node_index, surface_spatial_node_index) = match pic.raster_config {
810                    Some(ref rc) => {
811                        let surface = &ctx.surfaces[rc.surface_index.0];
812                        (surface.raster_spatial_node_index, surface.surface_spatial_node_index)
813                    }
814                    None => {
815                        unreachable!();
816                    }
817                };
818
819                // Determine the clear color for this picture cache.
820                // If the entire tile cache is opaque, we can skip clear completely.
821                // If it's the first layer, clear it to white to allow subpixel AA on that
822                // first layer even if it's technically transparent.
823                // Otherwise, clear to transparent and composite with alpha.
824                // TODO(gw): We can detect per-tile opacity for the clear color here
825                //           which might be a significant win on some pages?
826                let forced_opaque = match tile_cache.background_color {
827                    Some(color) => color.a >= 1.0,
828                    None => false,
829                };
830                // TODO(gw): Once we have multiple slices enabled, take advantage of
831                //           option to skip clears if the slice is opaque.
832                let clear_color = if forced_opaque {
833                    Some(ColorF::WHITE)
834                } else {
835                    Some(ColorF::TRANSPARENT)
836                };
837
838                // Create an alpha batcher for each of the tasks of this picture.
839                let mut batchers = Vec::new();
840                for task_id in &task_ids {
841                    let task_id = *task_id;
842                    let vis_mask = match render_tasks[task_id].kind {
843                        RenderTaskKind::Picture(ref info) => info.vis_mask,
844                        _ => unreachable!(),
845                    };
846                    batchers.push(AlphaBatchBuilder::new(
847                        pass.screen_size,
848                        ctx.break_advanced_blend_batches,
849                        ctx.batch_lookback_count,
850                        task_id,
851                        render_tasks.get_task_address(task_id),
852                        vis_mask,
853                    ));
854                }
855
856                // Run the batch creation code for this picture, adding items to
857                // all relevant per-task batchers.
858                let mut batch_builder = BatchBuilder::new(batchers);
859                batch_builder.add_pic_to_batch(
860                    pic,
861                    ctx,
862                    gpu_cache,
863                    render_tasks,
864                    deferred_resolves,
865                    prim_headers,
866                    transforms,
867                    root_spatial_node_index,
868                    surface_spatial_node_index,
869                    z_generator,
870                    composite_state,
871                );
872
873                // Create picture cache targets, one per render task, and assign
874                // the correct batcher to them.
875                let batchers = batch_builder.finalize();
876                for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) {
877                    let task = &render_tasks[task_id];
878                    let (target_rect, _) = task.get_target_rect();
879
880                    match task.location {
881                        RenderTaskLocation::PictureCache { ref surface, .. } => {
882                            // TODO(gw): The interface here is a bit untidy since it's
883                            //           designed to support batch merging, which isn't
884                            //           relevant for picture cache targets. We
885                            //           can restructure / tidy this up a bit.
886                            let scissor_rect  = match render_tasks[task_id].kind {
887                                RenderTaskKind::Picture(ref info) => info.scissor_rect,
888                                _ => unreachable!(),
889                            }.expect("bug: dirty rect must be set for picture cache tasks");
890                            let mut batch_containers = Vec::new();
891                            let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect));
892                            batcher.build(
893                                &mut batch_containers,
894                                &mut alpha_batch_container,
895                                target_rect,
896                                None,
897                            );
898                            debug_assert!(batch_containers.is_empty());
899
900                            let target = PictureCacheTarget {
901                                surface: surface.clone(),
902                                clear_color,
903                                alpha_batch_container,
904                                dirty_rect: scissor_rect,
905                            };
906
907                            picture_cache.push(target);
908                        }
909                        _ => {
910                            unreachable!()
911                        }
912                    }
913                }
914            }
915
916            color.build(
917                ctx,
918                gpu_cache,
919                render_tasks,
920                deferred_resolves,
921                saved_color,
922                prim_headers,
923                transforms,
924                z_generator,
925                composite_state,
926            );
927            alpha.build(
928                ctx,
929                gpu_cache,
930                render_tasks,
931                deferred_resolves,
932                saved_alpha,
933                prim_headers,
934                transforms,
935                z_generator,
936                composite_state,
937            );
938        }
939    }
940}
941
942/// A rendering-oriented representation of the frame built by the render backend
943/// and presented to the renderer.
944#[cfg_attr(feature = "capture", derive(Serialize))]
945#[cfg_attr(feature = "replay", derive(Deserialize))]
946pub struct Frame {
947    /// The origin on content produced by the render tasks.
948    pub content_origin: DeviceIntPoint,
949    /// The rectangle to show the frame in, on screen.
950    pub device_rect: DeviceIntRect,
951    pub layer: DocumentLayer,
952    pub passes: Vec<RenderPass>,
953    #[cfg_attr(any(feature = "capture", feature = "replay"), serde(default = "FrameProfileCounters::new", skip))]
954    pub profile_counters: FrameProfileCounters,
955
956    pub transform_palette: Vec<TransformData>,
957    pub render_tasks: RenderTaskGraph,
958    pub prim_headers: PrimitiveHeaders,
959
960    /// The GPU cache frame that the contents of Self depend on
961    pub gpu_cache_frame_id: FrameId,
962
963    /// List of textures that we don't know about yet
964    /// from the backend thread. The render thread
965    /// will use a callback to resolve these and
966    /// patch the data structures.
967    pub deferred_resolves: Vec<DeferredResolve>,
968
969    /// True if this frame contains any render tasks
970    /// that write to the texture cache.
971    pub has_texture_cache_tasks: bool,
972
973    /// True if this frame has been drawn by the
974    /// renderer.
975    pub has_been_rendered: bool,
976
977    /// Dirty regions recorded when generating this frame. Empty when not in
978    /// testing.
979    #[cfg_attr(feature = "serde", serde(skip))]
980    pub recorded_dirty_regions: Vec<RecordedDirtyRegion>,
981
982    /// Debugging information to overlay for this frame.
983    pub debug_items: Vec<DebugItem>,
984
985    /// Contains picture cache tiles, and associated information.
986    /// Used by the renderer to composite tiles into the framebuffer,
987    /// or hand them off to an OS compositor.
988    pub composite_state: CompositeState,
989}
990
991impl Frame {
992    // This frame must be flushed if it writes to the
993    // texture cache, and hasn't been drawn yet.
994    pub fn must_be_drawn(&self) -> bool {
995        self.has_texture_cache_tasks && !self.has_been_rendered
996    }
997
998    // Returns true if this frame doesn't alter what is on screen currently.
999    pub fn is_nop(&self) -> bool {
1000        // If picture caching is disabled, we don't have enough information
1001        // to know if this frame is a nop, so it gets drawn unconditionally.
1002        if !self.composite_state.picture_caching_is_enabled {
1003            return false;
1004        }
1005
1006        // When picture caching is enabled, the first (main framebuffer) pass
1007        // consists of compositing tiles only (whether via the simple compositor
1008        // or the native OS compositor). If there are no other passes, that
1009        // implies that none of the picture cache tiles were updated, and thus
1010        // the frame content must be exactly the same as last frame. If this is
1011        // true, drawing this frame is a no-op and can be skipped.
1012
1013        if self.passes.len() > 1 {
1014            return false;
1015        }
1016
1017        true
1018    }
1019}