Skip to main content

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, ExternalScrollId, FontRenderMode, ImageKey, MinimapData, PremultipliedColorF};
6use api::units::*;
7use plane_split::BspSplitter;
8use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
9use crate::clip::{ClipStore, ClipTree};
10use crate::command_buffer::{PrimitiveCommand, CommandBufferList, CommandBufferIndex};
11use crate::{debug_colors, ChunkPool};
12use crate::spatial_node::SpatialNodeType;
13use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
14use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator};
15use crate::debug_item::DebugItem;
16use crate::gpu_types::{ImageBrushPrimitiveData, PrimitiveHeaders, ZBufferIdGenerator};
17use crate::gpu_types::QuadSegment;
18use crate::internal_types::{FastHashMap, PlaneSplitter, FrameStamp};
19use crate::invalidation::DirtyRegion;
20use crate::tile_cache::{SliceId, TileCacheInstance};
21use crate::picture::PictureInstance;
22use crate::picture::{SurfaceInfo, SurfaceIndex, ResolvedSurfaceTexture};
23use crate::picture::{SubpixelMode, RasterConfig, PictureCompositeMode, PictureScratch};
24use crate::prepare::prepare_picture;
25use crate::prim_store::{PictureIndex, PrimitiveScratchBuffer};
26use crate::prim_store::{DeferredResolve, PrimitiveInstance};
27use crate::prim_store::storage;
28use crate::profiler::{self, TransactionProfile};
29use crate::render_backend::{DataStores, ScratchBuffer};
30use crate::renderer::{GpuBufferAddress, GpuBufferBuilder, GpuBufferBuilderF, GpuBufferBuilderI, GpuBufferF, GpuBufferI, GpuBufferDataF};
31use crate::render_target::{PictureCacheTarget, PictureCacheTargetKind};
32use crate::render_target::{RenderTargetContext, RenderTargetKind, RenderTarget};
33use crate::render_task_graph::{Pass, RenderTaskGraph, RenderTaskId, SubPassSurface};
34use crate::render_task_graph::{RenderPass, RenderTaskGraphBuilder};
35use crate::render_task::{RenderTaskKind, StaticRenderTaskSurface};
36use crate::resource_cache::ResourceCache;
37use crate::scene::{BuiltScene, SceneProperties};
38use crate::space::SpaceMapper;
39use crate::segment::SegmentBuilder;
40use crate::surface::SurfaceBuilder;
41use crate::transform::{TransformPalette, TransformData};
42use std::sync::Arc;
43use std::{f32, mem};
44use crate::util::{MaxRect, VecHelper, Preallocator};
45use crate::visibility::{update_prim_visibility, FrameVisibilityState, FrameVisibilityContext};
46use crate::internal_types::{FrameVec, FrameMemory};
47
48#[derive(Clone, Copy, Debug)]
49#[cfg_attr(feature = "capture", derive(Serialize))]
50#[cfg_attr(feature = "replay", derive(Deserialize))]
51pub struct FrameBuilderConfig {
52    pub default_font_render_mode: FontRenderMode,
53    pub dual_source_blending_is_supported: bool,
54    /// True if we're running tests (i.e. via wrench).
55    pub testing: bool,
56    pub gpu_supports_fast_clears: bool,
57    pub gpu_supports_advanced_blend: bool,
58    pub advanced_blend_is_coherent: bool,
59    pub gpu_supports_render_target_partial_update: bool,
60    /// Whether ImageBufferKind::TextureExternal images must first be copied
61    /// to a regular texture before rendering.
62    pub external_images_require_copy: bool,
63    pub batch_lookback_count: usize,
64    pub background_color: Option<ColorF>,
65    pub compositor_kind: CompositorKind,
66    pub tile_size_override: Option<DeviceIntSize>,
67    pub max_surface_override: Option<usize>,
68    pub max_depth_ids: i32,
69    pub max_target_size: i32,
70    pub force_invalidation: bool,
71    pub is_software: bool,
72    pub low_quality_pinch_zoom: bool,
73    pub max_shared_surface_size: i32,
74    pub enable_dithering: bool,
75}
76
77/// A set of default / global resources that are re-built each frame.
78#[cfg_attr(feature = "capture", derive(Serialize))]
79pub struct FrameGlobalResources {
80    /// The image shader block for the most common / default
81    /// set of image parameters (color white, stretch == rect.size).
82    pub default_image_data: GpuBufferAddress,
83
84    /// A GPU cache config for drawing cut-out rectangle primitives.
85    /// This is used to 'cut out' overlay tiles where a compositor
86    /// surface exists.
87    pub default_black_rect_address: GpuBufferAddress,
88}
89
90impl FrameGlobalResources {
91    pub fn new(gpu_buffers: &mut GpuBufferBuilder) -> Self {
92        let mut writer = gpu_buffers.f32.write_blocks(ImageBrushPrimitiveData::NUM_BLOCKS);
93        writer.push(&ImageBrushPrimitiveData {
94            color: PremultipliedColorF::WHITE,
95            background_color: PremultipliedColorF::WHITE,
96            // -ve means use prim rect for stretch size
97            stretch_size: LayoutSize::new(-1.0, 0.0),
98        });
99        let default_image_data = writer.finish();
100
101        let mut writer = gpu_buffers.f32.write_blocks(1);
102        writer.push_one(PremultipliedColorF::BLACK);
103        let default_black_rect_address = writer.finish();
104
105        FrameGlobalResources {
106            default_image_data,
107            default_black_rect_address,
108        }
109    }
110}
111
112pub struct FrameScratchBuffer {
113    dirty_region_stack: Vec<DirtyRegion>,
114    surface_stack: Vec<(PictureIndex, SurfaceIndex)>,
115}
116
117impl Default for FrameScratchBuffer {
118    fn default() -> Self {
119        FrameScratchBuffer {
120            dirty_region_stack: Vec::new(),
121            surface_stack: Vec::new(),
122        }
123    }
124}
125
126impl FrameScratchBuffer {
127    pub fn begin_frame(&mut self) {
128        self.dirty_region_stack.clear();
129        self.surface_stack.clear();
130    }
131}
132
133/// Produces the frames that are sent to the renderer.
134#[cfg_attr(feature = "capture", derive(Serialize))]
135pub struct FrameBuilder {
136    #[cfg_attr(feature = "capture", serde(skip))]
137    prim_headers_prealloc: Preallocator,
138    #[cfg_attr(feature = "capture", serde(skip))]
139    composite_state_prealloc: CompositeStatePreallocator,
140    #[cfg_attr(feature = "capture", serde(skip))]
141    plane_splitters: Vec<PlaneSplitter>,
142}
143
144pub struct FrameBuildingContext<'a> {
145    pub global_device_pixel_scale: DevicePixelScale,
146    pub scene_properties: &'a SceneProperties,
147    pub global_screen_world_rect: WorldRect,
148    pub spatial_tree: &'a SpatialTree,
149    pub max_local_clip: LayoutRect,
150    pub debug_flags: DebugFlags,
151    pub fb_config: &'a FrameBuilderConfig,
152    pub root_spatial_node_index: SpatialNodeIndex,
153}
154
155pub struct FrameBuildingState<'a> {
156    pub rg_builder: &'a mut RenderTaskGraphBuilder,
157    pub clip_store: &'a mut ClipStore,
158    pub resource_cache: &'a mut ResourceCache,
159    pub transforms: &'a mut TransformPalette,
160    pub segment_builder: SegmentBuilder,
161    pub surfaces: &'a mut Vec<SurfaceInfo>,
162    pub dirty_region_stack: Vec<DirtyRegion>,
163    pub composite_state: &'a mut CompositeState,
164    pub num_visible_primitives: u32,
165    pub plane_splitters: &'a mut [PlaneSplitter],
166    pub surface_builder: SurfaceBuilder,
167    pub cmd_buffers: &'a mut CommandBufferList,
168    pub clip_tree: &'a ClipTree,
169    pub frame_gpu_data: &'a mut GpuBufferBuilder,
170    /// When using a render task to produce pixels that are associated with
171    /// an image key (for example snapshotted pictures), inserting the image
172    /// key / task id association in this hashmap allows the image item to
173    /// register a dependency to the render task. This ensures that the
174    /// render task is produced before the image that renders it if they
175    /// are happening in the same frame.
176    /// This mechanism relies on the item producing the render task to be
177    /// traversed before the image that displays it (in other words, the
178    /// picture must appear before the image in the display list).
179    pub image_dependencies: FastHashMap<ImageKey, RenderTaskId>,
180    /// Per-picture scratch-index slot used by the prepare pass. `None`
181    /// means "not yet visited this frame"; `Some(handle)` means the
182    /// picture was visited and `handle` indexes into
183    /// `PrimitiveFrameScratch.pictures` (or is `INVALID` if take_context
184    /// failed). Replaces the prepare pass's earlier `visited_pictures`
185    /// bool slice — visibility keeps its own bool slice on
186    /// `FrameVisibilityState`.
187    pub picture_scratch_handles: &'a mut [Option<storage::Index<PictureScratch>>],
188}
189
190impl<'a> FrameBuildingState<'a> {
191    /// Retrieve the current dirty region during primitive traversal.
192    pub fn current_dirty_region(&self) -> &DirtyRegion {
193        self.dirty_region_stack.last().unwrap()
194    }
195
196    /// Push a new dirty region for child primitives to cull / clip against.
197    pub fn push_dirty_region(&mut self, region: DirtyRegion) {
198        self.dirty_region_stack.push(region);
199    }
200
201    /// Pop the top dirty region from the stack.
202    pub fn pop_dirty_region(&mut self) {
203        self.dirty_region_stack.pop().unwrap();
204    }
205
206    /// Push a primitive command to a set of command buffers
207    pub fn push_prim(
208        &mut self,
209        cmd: &PrimitiveCommand,
210        spatial_node_index: SpatialNodeIndex,
211        targets: &[CommandBufferIndex],
212    ) {
213        for cmd_buffer_index in targets {
214            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
215            cmd_buffer.add_prim(cmd, spatial_node_index);
216        }
217    }
218
219    /// Push a command to a set of command buffers
220    pub fn push_cmd(
221        &mut self,
222        cmd: &PrimitiveCommand,
223        targets: &[CommandBufferIndex],
224    ) {
225        for cmd_buffer_index in targets {
226            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
227            cmd_buffer.add_cmd(cmd);
228        }
229    }
230
231    /// Set the active list of segments in a set of command buffers
232    pub fn set_segments(
233        &mut self,
234        segments: &[QuadSegment],
235        targets: &[CommandBufferIndex],
236    ) {
237        for cmd_buffer_index in targets {
238            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
239            cmd_buffer.set_segments(segments);
240        }
241    }
242}
243
244/// Immutable context of a picture when processing children.
245#[derive(Debug)]
246pub struct PictureContext {
247    pub pic_index: PictureIndex,
248    pub surface_spatial_node_index: SpatialNodeIndex,
249    pub raster_spatial_node_index: SpatialNodeIndex,
250    pub visibility_spatial_node_index: SpatialNodeIndex,
251    /// The surface that this picture will render on.
252    pub surface_index: SurfaceIndex,
253    pub dirty_region_count: usize,
254    pub subpixel_mode: SubpixelMode,
255}
256
257/// Mutable state of a picture that gets modified when
258/// the children are processed.
259pub struct PictureState {
260    pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
261    pub map_pic_to_vis: SpaceMapper<PicturePixel, VisPixel>,
262}
263
264impl FrameBuilder {
265    pub fn new() -> Self {
266        FrameBuilder {
267            prim_headers_prealloc: Preallocator::new(0),
268            composite_state_prealloc: CompositeStatePreallocator::default(),
269            plane_splitters: Vec::new(),
270        }
271    }
272
273    /// Compute the contribution (bounding rectangles, and resources) of layers and their
274    /// primitives in screen space.
275    fn build_layer_screen_rects_and_cull_layers(
276        &mut self,
277        scene: &mut BuiltScene,
278        present: bool,
279        global_screen_world_rect: WorldRect,
280        resource_cache: &mut ResourceCache,
281        rg_builder: &mut RenderTaskGraphBuilder,
282        global_device_pixel_scale: DevicePixelScale,
283        scene_properties: &SceneProperties,
284        transform_palette: &mut TransformPalette,
285        data_stores: &mut DataStores,
286        scratch: &mut ScratchBuffer,
287        debug_flags: DebugFlags,
288        composite_state: &mut CompositeState,
289        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
290        spatial_tree: &SpatialTree,
291        cmd_buffers: &mut CommandBufferList,
292        frame_gpu_data: &mut GpuBufferBuilder,
293        frame_memory: &FrameMemory,
294        profile: &mut TransactionProfile,
295    ) {
296        profile_scope!("build_layer_screen_rects_and_cull_layers");
297
298        let render_picture_cache_slices = present;
299
300        let root_spatial_node_index = spatial_tree.root_reference_frame_index();
301
302        const MAX_CLIP_COORD: f32 = 1.0e9;
303
304        // Reset all plane splitters. These are retained from frame to frame to reduce
305        // per-frame allocations
306        self.plane_splitters.resize_with(scene.num_plane_splitters, BspSplitter::new);
307        for splitter in &mut self.plane_splitters {
308            splitter.reset();
309        }
310
311        let frame_context = FrameBuildingContext {
312            global_device_pixel_scale,
313            scene_properties,
314            global_screen_world_rect,
315            spatial_tree,
316            max_local_clip: LayoutRect {
317                min: LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
318                max: LayoutPoint::new(MAX_CLIP_COORD, MAX_CLIP_COORD),
319            },
320            debug_flags,
321            fb_config: &scene.config,
322            root_spatial_node_index,
323        };
324
325        scene.picture_graph.build_update_passes(
326            &mut scene.prim_store.pictures,
327            &frame_context,
328        );
329
330        scene.picture_graph.assign_surfaces(
331            &mut scene.prim_store.pictures,
332            &mut scene.surfaces,
333            tile_caches,
334            &frame_context,
335        );
336
337        // Add a "fake" surface that we will use as parent for
338        // snapshotted pictures.
339        let root_spatial_node = frame_context.spatial_tree.root_reference_frame_index();
340        let snapshot_surface = SurfaceIndex(scene.surfaces.len());
341        scene.surfaces.push(SurfaceInfo::new(
342            root_spatial_node,
343            root_spatial_node,
344            WorldRect::max_rect(),
345            &frame_context.spatial_tree,
346            euclid::Scale::new(1.0),
347            (1.0, 1.0),
348            (1.0, 1.0),
349            false,
350            false,
351        ));
352
353        // Build the per-frame draw header storage with one entry per prim
354        // instance. Identity-indexed by `PrimitiveInstanceIndex.0` for now;
355        // a follow-up will switch this to push-per-draw. The per-prim
356        // `snapped_local_rect` is filled in by `snap_frame_rects` below.
357        scratch.primitive.frame.draws.clear();
358        scratch.primitive.frame.draws.resize_with(
359            scene.prim_instances.len(),
360            crate::visibility::PrimitiveDrawHeader::new,
361        );
362
363        // Snap prim, cluster, and clip-tree rects against the current spatial
364        // tree. Must precede `propagate_bounding_rects` (which reads
365        // `cluster.snapped_bounding_rect`) and the visibility / prepare /
366        // batch passes (which read `draws[i].snapped_local_rect` and the
367        // clip-tree `snapped_*` fields).
368        crate::frame_snap::snap_frame_rects(
369            &mut scene.prim_store,
370            &scene.prim_instances,
371            &mut scene.clip_tree,
372            &mut scratch.primitive.frame.draws,
373            spatial_tree,
374        );
375
376        scene.picture_graph.propagate_bounding_rects(
377            &mut scene.prim_store.pictures,
378            &mut scene.surfaces,
379            &frame_context,
380        );
381
382        // In order to handle picture snapshots consistently we need
383        // the visibility and prepare passes to visit them first before
384        // traversing the scene. This ensures that out-of-view snapshots
385        // are rendered and that snapshots are consistently produced
386        // relative to the root spatial node.
387        // However it means that the visibility and prepare passes may
388        // visit some pictures multiple times, so we keep track of visited
389        // pictures during each traversal to avoid that.
390        let n_pics = scene.prim_store.pictures.len();
391        let mut visited_pictures = frame_memory.new_vec_with_capacity(n_pics);
392        for _ in 0..n_pics {
393            visited_pictures.push(false);
394        }
395
396        {
397            profile_scope!("UpdateVisibility");
398            profile_marker!("UpdateVisibility");
399            profile.start_time(profiler::FRAME_VISIBILITY_TIME);
400
401            let visibility_context = FrameVisibilityContext {
402                global_device_pixel_scale,
403                spatial_tree,
404                global_screen_world_rect,
405                debug_flags,
406                scene_properties,
407                config: scene.config,
408                root_spatial_node_index,
409            };
410
411            for pic_index in scene.snapshot_pictures.iter() {
412                let mut visibility_state = FrameVisibilityState {
413                    clip_store: &mut scene.clip_store,
414                    resource_cache,
415                    frame_gpu_data,
416                    data_stores,
417                    clip_tree: &mut scene.clip_tree,
418                    composite_state,
419                    rg_builder,
420                    prim_instances: &mut scene.prim_instances,
421                    surfaces: &mut scene.surfaces,
422                    surface_stack: scratch.frame.surface_stack.take(),
423                    profile,
424                    scratch,
425                    visited_pictures: &mut visited_pictures,
426                };
427
428                let world_culling_rect = WorldRect::max_rect();
429
430                // For now, snapshots are updated every frame. For the
431                // pictures displaying the snapshot via images pick up
432                // the changes, we have to make sure that the image's
433                // generation counter is incremented early in the frame,
434                // before the main visibility pass visits the image items.
435                let pic = &scene.prim_store.pictures[pic_index.0];
436                let snapshot = pic.snapshot
437                    .unwrap();
438                let key = snapshot.key.as_image();
439                visibility_state.resource_cache
440                    .increment_image_generation(key);
441
442                if let Some(node) = pic.clip_root {
443                    visibility_state.clip_tree.push_clip_root_node(node);
444                }
445                update_prim_visibility(
446                    *pic_index,
447                    None,
448                    &world_culling_rect,
449                    &scene.prim_store,
450                    true,
451                    &visibility_context,
452                    &mut visibility_state,
453                    &mut None,
454                );
455                if scene.prim_store.pictures[pic_index.0].clip_root.is_some() {
456                    visibility_state.clip_tree.pop_clip_root();
457                }
458            }
459
460            for pic_index in scene.tile_cache_pictures.iter().rev() {
461                if !render_picture_cache_slices {
462                    break;
463                }
464                let pic = &mut scene.prim_store.pictures[pic_index.0];
465
466                match pic.raster_config {
467                    Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
468                        let tile_cache = tile_caches
469                            .get_mut(&slice_id)
470                            .expect("bug: non-existent tile cache");
471
472                        let mut visibility_state = FrameVisibilityState {
473                            clip_store: &mut scene.clip_store,
474                            resource_cache,
475                            frame_gpu_data,
476                            data_stores,
477                            clip_tree: &mut scene.clip_tree,
478                            composite_state,
479                            rg_builder,
480                            prim_instances: &mut scene.prim_instances,
481                            surfaces: &mut scene.surfaces,
482                            surface_stack: scratch.frame.surface_stack.take(),
483                            profile,
484                            scratch,
485                            visited_pictures: &mut visited_pictures,
486                        };
487
488                        // If we have a tile cache for this picture, see if any of the
489                        // relative transforms have changed, which means we need to
490                        // re-map the dependencies of any child primitives.
491                        let world_culling_rect = tile_cache.pre_update(
492                            surface_index,
493                            &visibility_context,
494                            &mut visibility_state,
495                        );
496
497                        // Push a new surface, supplying the list of clips that should be
498                        // ignored, since they are handled by clipping when drawing this surface.
499                        visibility_state.push_surface(
500                            *pic_index,
501                            surface_index,
502                        );
503                        visibility_state.clip_tree.push_clip_root_node(tile_cache.shared_clip_node_id);
504
505                        update_prim_visibility(
506                            *pic_index,
507                            None,
508                            &world_culling_rect,
509                            &scene.prim_store,
510                            true,
511                            &visibility_context,
512                            &mut visibility_state,
513                            &mut Some(tile_cache),
514                        );
515
516                        // Build the dirty region(s) for this tile cache.
517                        tile_cache.post_update(
518                            &visibility_context,
519                            &mut visibility_state.prim_instances,
520                            &mut visibility_state.composite_state,
521                            &mut visibility_state.resource_cache,
522                            &mut visibility_state.scratch.primitive,
523                        );
524
525                        visibility_state.clip_tree.pop_clip_root();
526                        visibility_state.pop_surface();
527                        visibility_state.scratch.frame.surface_stack = visibility_state.surface_stack.take();
528                    }
529                    _ => {
530                        panic!("bug: not a tile cache");
531                    }
532                }
533            }
534
535            profile.end_time(profiler::FRAME_VISIBILITY_TIME);
536        }
537
538        self.skip_occluded_pictures_with_clips(
539            &scene.tile_cache_pictures,
540            &mut scene.prim_store.pictures,
541            tile_caches,
542            &frame_context,
543            composite_state);
544
545        profile.start_time(profiler::FRAME_PREPARE_TIME);
546
547        let mut picture_scratch_handles = frame_memory.new_vec_with_capacity(n_pics);
548        for _ in 0..n_pics {
549            picture_scratch_handles.push(None);
550        }
551        let mut frame_state = FrameBuildingState {
552            rg_builder,
553            clip_store: &mut scene.clip_store,
554            resource_cache,
555            transforms: transform_palette,
556            segment_builder: SegmentBuilder::new(),
557            surfaces: &mut scene.surfaces,
558            dirty_region_stack: scratch.frame.dirty_region_stack.take(),
559            composite_state,
560            num_visible_primitives: 0,
561            plane_splitters: &mut self.plane_splitters,
562            surface_builder: SurfaceBuilder::new(),
563            cmd_buffers,
564            clip_tree: &mut scene.clip_tree,
565            frame_gpu_data,
566            image_dependencies: FastHashMap::default(),
567            picture_scratch_handles: &mut picture_scratch_handles,
568        };
569
570
571        if !scene.snapshot_pictures.is_empty() {
572            // Push a default dirty region which does not cull any
573            // primitive.
574            let mut default_dirty_region = DirtyRegion::new(
575                root_spatial_node_index,
576                root_spatial_node_index,
577            );
578            default_dirty_region.add_dirty_region(
579                PictureRect::max_rect(),
580                frame_context.spatial_tree,
581            );
582            frame_state.push_dirty_region(default_dirty_region);
583
584            frame_state.surface_builder.push_surface(
585                snapshot_surface,
586                false,
587                PictureRect::max_rect(),
588                None,
589                frame_state.surfaces,
590                frame_state.rg_builder,
591            );
592        }
593
594        for pic_index in &scene.snapshot_pictures {
595
596            prepare_picture(
597                *pic_index,
598                &mut scene.prim_store,
599                Some(snapshot_surface),
600                SubpixelMode::Allow,
601                &frame_context,
602                &mut frame_state,
603                data_stores,
604                &mut scratch.primitive,
605                tile_caches,
606                &mut scene.prim_instances
607            );
608        }
609
610        if !scene.snapshot_pictures.is_empty() {
611            frame_state.surface_builder.pop_empty_surface();
612            frame_state.pop_dirty_region();
613        }
614
615        // Push a default dirty region which culls primitives
616        // against the screen world rect, in absence of any
617        // other dirty regions.
618        let mut default_dirty_region = DirtyRegion::new(
619            root_spatial_node_index,
620            root_spatial_node_index,
621        );
622        default_dirty_region.add_dirty_region(
623            frame_context.global_screen_world_rect.cast_unit(),
624            frame_context.spatial_tree,
625        );
626        frame_state.push_dirty_region(default_dirty_region);
627
628        for pic_index in &scene.tile_cache_pictures {
629            if !render_picture_cache_slices {
630                break;
631            }
632
633            prepare_picture(
634                *pic_index,
635                &mut scene.prim_store,
636                None,
637                SubpixelMode::Allow,
638                &frame_context,
639                &mut frame_state,
640                data_stores,
641                &mut scratch.primitive,
642                tile_caches,
643                &mut scene.prim_instances
644            );
645        }
646
647        frame_state.pop_dirty_region();
648        frame_state.surface_builder.finalize();
649        profile.end_time(profiler::FRAME_PREPARE_TIME);
650        profile.set(profiler::VISIBLE_PRIMITIVES, frame_state.num_visible_primitives);
651
652        scratch.frame.dirty_region_stack = frame_state.dirty_region_stack.take();
653
654        {
655            profile_marker!("BlockOnResources");
656
657            resource_cache.block_until_all_resources_added(
658                frame_gpu_data,
659                profile,
660            );
661        }
662    }
663
664    pub fn build(
665        &mut self,
666        scene: &mut BuiltScene,
667        present: bool,
668        resource_cache: &mut ResourceCache,
669        rg_builder: &mut RenderTaskGraphBuilder,
670        stamp: FrameStamp,
671        device_origin: DeviceIntPoint,
672        scene_properties: &SceneProperties,
673        data_stores: &mut DataStores,
674        scratch: &mut ScratchBuffer,
675        debug_flags: DebugFlags,
676        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
677        spatial_tree: &mut SpatialTree,
678        dirty_rects_are_valid: bool,
679        profile: &mut TransactionProfile,
680        minimap_data: FastHashMap<ExternalScrollId, MinimapData>,
681        chunk_pool: Arc<ChunkPool>,
682    ) -> Frame {
683        profile_scope!("build");
684        profile_marker!("BuildFrame");
685
686        let mut frame_memory = FrameMemory::new(chunk_pool, stamp.frame_id());
687        // TODO(gw): Recycle backing vec buffers for gpu buffer builder between frames
688        let mut gpu_buffer_builder = GpuBufferBuilder {
689            f32: GpuBufferBuilderF::new(&frame_memory, 8 * 1024, stamp.frame_id()),
690            i32: GpuBufferBuilderI::new(&frame_memory, 2 * 1024, stamp.frame_id()),
691        };
692
693        profile.set(profiler::PRIMITIVES, scene.prim_instances.len());
694        profile.set(profiler::PICTURE_CACHE_SLICES, scene.tile_cache_config.picture_cache_slice_count);
695        scratch.begin_frame();
696        resource_cache.begin_frame(stamp, profile);
697
698        // TODO(gw): Follow up patches won't clear this, as they'll be assigned
699        //           statically during scene building.
700        scene.surfaces.clear();
701
702        let globals = FrameGlobalResources::new(&mut gpu_buffer_builder);
703
704        spatial_tree.update_tree(scene_properties);
705        let mut transform_palette = spatial_tree.build_transform_palette(&frame_memory);
706        scene.clip_store.begin_frame(&mut scratch.clip_store);
707
708        rg_builder.begin_frame(stamp.frame_id());
709
710        // TODO(dp): Remove me completely!!
711        let global_device_pixel_scale = DevicePixelScale::new(1.0);
712
713        let output_size = scene.output_rect.size();
714        let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
715
716        let mut composite_state = CompositeState::new(
717            scene.config.compositor_kind,
718            scene.config.max_depth_ids,
719            dirty_rects_are_valid,
720            scene.config.low_quality_pinch_zoom,
721            &frame_memory,
722        );
723
724        self.composite_state_prealloc.preallocate(&mut composite_state);
725
726        let mut cmd_buffers = CommandBufferList::new();
727
728        self.build_layer_screen_rects_and_cull_layers(
729            scene,
730            present,
731            screen_world_rect,
732            resource_cache,
733            rg_builder,
734            global_device_pixel_scale,
735            scene_properties,
736            &mut transform_palette,
737            data_stores,
738            scratch,
739            debug_flags,
740            &mut composite_state,
741            tile_caches,
742            spatial_tree,
743            &mut cmd_buffers,
744            &mut gpu_buffer_builder,
745            &frame_memory,
746            profile,
747        );
748
749        self.render_minimap(&mut scratch.primitive, &spatial_tree, minimap_data);
750
751        profile.start_time(profiler::FRAME_BATCHING_TIME);
752
753        let mut deferred_resolves = frame_memory.new_vec();
754
755        // Finish creating the frame graph and build it.
756        let render_tasks = rg_builder.end_frame(
757            resource_cache,
758            &mut gpu_buffer_builder,
759            &mut deferred_resolves,
760            scene.config.max_shared_surface_size,
761            &frame_memory,
762        );
763
764        let mut passes = frame_memory.new_vec();
765        let mut has_texture_cache_tasks = false;
766        let mut prim_headers = PrimitiveHeaders::new(&frame_memory);
767        self.prim_headers_prealloc.preallocate_framevec(&mut prim_headers.headers_int);
768        self.prim_headers_prealloc.preallocate_framevec(&mut prim_headers.headers_float);
769
770        {
771            profile_marker!("Batching");
772
773            // Used to generated a unique z-buffer value per primitive.
774            let mut z_generator = ZBufferIdGenerator::new(scene.config.max_depth_ids);
775            let use_dual_source_blending = scene.config.dual_source_blending_is_supported;
776
777            for pass in render_tasks.passes.iter().rev() {
778                let mut ctx = RenderTargetContext {
779                    global_device_pixel_scale,
780                    prim_store: &scene.prim_store,
781                    resource_cache,
782                    use_dual_source_blending,
783                    use_advanced_blending: scene.config.gpu_supports_advanced_blend,
784                    break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
785                    batch_lookback_count: scene.config.batch_lookback_count,
786                    spatial_tree,
787                    data_stores,
788                    surfaces: &scene.surfaces,
789                    scratch: &mut scratch.primitive,
790                    screen_world_rect,
791                    globals: &globals,
792                    tile_caches,
793                    root_spatial_node_index: spatial_tree.root_reference_frame_index(),
794                    frame_memory: &mut frame_memory,
795                };
796
797                let pass = build_render_pass(
798                    pass,
799                    output_size,
800                    &mut ctx,
801                    &mut gpu_buffer_builder,
802                    &render_tasks,
803                    &scene.clip_store,
804                    &mut transform_palette,
805                    &mut prim_headers,
806                    &mut z_generator,
807                    scene.config.gpu_supports_fast_clears,
808                    &scene.prim_instances,
809                    &cmd_buffers,
810                );
811
812                has_texture_cache_tasks |= !pass.texture_cache.is_empty();
813                has_texture_cache_tasks |= !pass.picture_cache.is_empty();
814
815                passes.push(pass);
816            }
817
818            if present {
819                let mut ctx = RenderTargetContext {
820                    global_device_pixel_scale,
821                    prim_store: &scene.prim_store,
822                    resource_cache,
823                    use_dual_source_blending,
824                    use_advanced_blending: scene.config.gpu_supports_advanced_blend,
825                    break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
826                    batch_lookback_count: scene.config.batch_lookback_count,
827                    spatial_tree,
828                    data_stores,
829                    surfaces: &scene.surfaces,
830                    scratch: &mut scratch.primitive,
831                    screen_world_rect,
832                    globals: &globals,
833                    tile_caches,
834                    root_spatial_node_index: spatial_tree.root_reference_frame_index(),
835                    frame_memory: &mut frame_memory,
836                };
837
838                self.build_composite_pass(
839                    scene,
840                    &mut ctx,
841                    &mut gpu_buffer_builder,
842                    &mut deferred_resolves,
843                    &mut composite_state,
844                );
845            }
846        }
847
848        profile.end_time(profiler::FRAME_BATCHING_TIME);
849
850        resource_cache.end_frame(profile);
851
852        self.prim_headers_prealloc.record_vec(&prim_headers.headers_int);
853        self.composite_state_prealloc.record(&composite_state);
854
855        composite_state.end_frame();
856        scene.clip_store.end_frame(&mut scratch.clip_store);
857        scratch.end_frame();
858
859        let gpu_buffer_f = gpu_buffer_builder.f32.finalize(&render_tasks);
860        let gpu_buffer_i = gpu_buffer_builder.i32.finalize(&render_tasks);
861
862        Frame {
863            device_rect: DeviceIntRect::from_origin_and_size(
864                device_origin,
865                scene.output_rect.size(),
866            ),
867            present,
868            passes,
869            transform_palette: transform_palette.finish(),
870            render_tasks,
871            deferred_resolves,
872            has_been_rendered: false,
873            has_texture_cache_tasks,
874            prim_headers,
875            debug_items: mem::replace(&mut scratch.primitive.frame.debug_items, Vec::new()),
876            composite_state,
877            gpu_buffer_f,
878            gpu_buffer_i,
879            allocator_memory: frame_memory,
880        }
881    }
882
883    fn render_minimap(
884        &self,
885        scratch: &mut PrimitiveScratchBuffer,
886        spatial_tree: &SpatialTree,
887        minimap_data_store: FastHashMap<ExternalScrollId, MinimapData>) {
888      // TODO: Replace minimap_data_store with Option<FastHastMap>?
889      if minimap_data_store.is_empty() {
890        return
891      }
892
893      // In our main walk over the spatial tree (below), for nodes inside a
894      // subtree rooted at a root-content node, we need some information from
895      // that enclosing root-content node. To collect this information, do an
896      // preliminary walk over the spatial tree now and collect the root-content
897      // info in a HashMap.
898      struct RootContentInfo {
899        transform: LayoutToWorldTransform,
900        clip: LayoutRect
901      }
902      let mut root_content_info = FastHashMap::<ExternalScrollId, RootContentInfo>::default();
903      spatial_tree.visit_nodes(|index, node| {
904        if let SpatialNodeType::ScrollFrame(ref scroll_frame_info) = node.node_type {
905          if let Some(minimap_data) = minimap_data_store.get(&scroll_frame_info.external_id) {
906            if minimap_data.is_root_content {
907              let transform = spatial_tree.get_world_viewport_transform(index).into_transform();
908              root_content_info.insert(scroll_frame_info.external_id, RootContentInfo{
909                transform,
910                clip: scroll_frame_info.viewport_rect
911              });
912            }
913          }
914        }
915      });
916
917      // This is the main walk over the spatial tree. For every scroll frame node which
918      // has minimap data, compute the rects we want to render for that minimap in world
919      // coordinates and add them to `scratch.frame.debug_items`.
920      spatial_tree.visit_nodes(|index, node| {
921        if let SpatialNodeType::ScrollFrame(ref scroll_frame_info) = node.node_type {
922          if let Some(minimap_data) = minimap_data_store.get(&scroll_frame_info.external_id) {
923            const HORIZONTAL_PADDING: f32 = 5.0;
924            const VERTICAL_PADDING: f32 = 10.0;
925            const PAGE_BORDER_COLOR: ColorF = debug_colors::BLACK;
926            const BACKGROUND_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.3};
927            const DISPLAYPORT_BACKGROUND_COLOR: ColorF = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 0.4};
928            const LAYOUT_PORT_COLOR: ColorF = debug_colors::RED;
929            const VISUAL_PORT_COLOR: ColorF = debug_colors::BLUE;
930            const DISPLAYPORT_COLOR: ColorF = debug_colors::LIME;
931
932            let viewport = scroll_frame_info.viewport_rect;
933
934            // Scale the minimap to make it 100px wide (if there's space), and the full height
935            // of the scroll frame's viewport, minus some padding. Position it at the left edge
936            // of the scroll frame's viewport.
937            let scale_factor_x = 100f32.min(viewport.width() - (2.0 * HORIZONTAL_PADDING))
938                                   / minimap_data.scrollable_rect.width();
939            let scale_factor_y = (viewport.height() - (2.0 * VERTICAL_PADDING))
940                                / minimap_data.scrollable_rect.height();
941            if scale_factor_x <= 0.0 || scale_factor_y <= 0.0 {
942              return;
943            }
944            let transform = LayoutTransform::scale(scale_factor_x, scale_factor_y, 1.0)
945                .then_translate(LayoutVector3D::new(HORIZONTAL_PADDING, VERTICAL_PADDING, 0.0))
946                .then_translate(LayoutVector3D::new(viewport.min.x, viewport.min.y, 0.0));
947
948            // Transforms for transforming rects in this scroll frame's local coordintes, to world coordinates.
949            // For scroll frames inside a root-content subtree, we apply this transform in two parts
950            // (local to root-content, and root-content to world), so that we can make additional
951            // adjustments in root-content space. For scroll frames outside of a root-content subtree,
952            // the entire world transform will be in `local_to_root_content`.
953            let world_transform = spatial_tree
954                .get_world_viewport_transform(index)
955                .into_transform();
956            let mut local_to_root_content =
957                world_transform.with_destination::<LayoutPixel>();
958            let mut root_content_to_world = LayoutToWorldTransform::default();
959            let mut root_content_clip = None;
960            if minimap_data.root_content_scroll_id != 0 {
961              if let Some(RootContentInfo{transform: root_content_transform, clip}) = root_content_info.get(&ExternalScrollId(minimap_data.root_content_scroll_id, minimap_data.root_content_pipeline_id)) {
962                // Exclude the root-content node's zoom transform from `local_to_root_content`.
963                // This ensures that the minimap remains unaffected by pinch-zooming
964                // (in essence, remaining attached to the *visual* viewport, rather than to
965                // the *layout* viewport which is what happens by default).
966                let zoom_transform = minimap_data.zoom_transform;
967                local_to_root_content = world_transform
968                  .then(&root_content_transform.inverse().unwrap())
969                  .then(&zoom_transform.inverse().unwrap());
970                root_content_to_world = root_content_transform.clone();
971                root_content_clip = Some(clip);
972              }
973            }
974
975            let mut add_rect = |rect, border, fill| -> Option<()> {
976              const STROKE_WIDTH: f32 = 2.0;
977              // Place rect in scroll frame's local coordinate space
978              let transformed_rect = transform.outer_transformed_box2d(&rect)?;
979
980              // Transform to world coordinates, using root-content coords as an intermediate step.
981              let mut root_content_rect = local_to_root_content.outer_transformed_box2d(&transformed_rect)?;
982              // In root-content coords, apply the root content node's viewport clip.
983              // This prevents subframe minimaps from leaking into the chrome area when the root
984              // scroll frame is scrolled.
985              // TODO: The minimaps of nested subframes can still leak outside of the viewports of
986              // their containing subframes. Should have a more proper fix for this.
987              if let Some(clip) = root_content_clip {
988                root_content_rect = root_content_rect.intersection(clip)?;
989              }
990              let world_rect = root_content_to_world.outer_transformed_box2d(&root_content_rect)?;
991
992              scratch.push_debug_rect_with_stroke_width(world_rect, border, STROKE_WIDTH);
993
994              // Add world coordinate rects to scratch.frame.debug_items
995              if let Some(fill_color) = fill {
996                let interior_world_rect = WorldRect::new(
997                    world_rect.min + WorldVector2D::new(STROKE_WIDTH, STROKE_WIDTH),
998                    world_rect.max - WorldVector2D::new(STROKE_WIDTH, STROKE_WIDTH)
999                );
1000                scratch.push_debug_rect(interior_world_rect * DevicePixelScale::new(1.0), 1, border, fill_color);
1001              }
1002
1003              Some(())
1004            };
1005
1006            add_rect(minimap_data.scrollable_rect, PAGE_BORDER_COLOR, Some(BACKGROUND_COLOR));
1007            add_rect(minimap_data.displayport, DISPLAYPORT_COLOR, Some(DISPLAYPORT_BACKGROUND_COLOR));
1008            // Only render a distinct layout viewport for the root content.
1009            // For other scroll frames, the visual and layout viewports coincide.
1010            if minimap_data.is_root_content {
1011              add_rect(minimap_data.layout_viewport, LAYOUT_PORT_COLOR, None);
1012            }
1013            add_rect(minimap_data.visual_viewport, VISUAL_PORT_COLOR, None);
1014          }
1015        }
1016      });
1017    }
1018
1019    /// Skip pictures that are fully covered by another opaque picture with the same rounded rect clip.
1020    ///
1021    /// Since the clip adds some transparency to the occluder, the opaque rectangles occlusion detection
1022    /// fails to discard stacks of opaque rounded corners.
1023    /// This is done specifically to avoid conflation artifacts with the rounded rectangle around web content
1024    /// in Firefox.
1025    fn skip_occluded_pictures_with_clips(
1026        &self,
1027        tile_cache_pictures: &Vec<PictureIndex>,
1028        pictures: &mut [PictureInstance],
1029        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
1030        frame_context: &FrameBuildingContext,
1031        composite_state: &mut CompositeState,
1032    ) {
1033        let mut current_opaque_clip = None;
1034
1035        for pic_index in tile_cache_pictures.iter().rev() {
1036            let pic = &mut pictures[pic_index.0];
1037
1038            match pic.raster_config {
1039                Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
1040                    let tile_cache = tile_caches
1041                        .get_mut(&slice_id)
1042                        .expect("bug: non-existent tile cache");
1043                    // Only check for compositor clips (rounded clips). The main
1044                    // rectangle occlusion for compositor tiles handles the rest fine.
1045                    if tile_cache.compositor_clip.is_none() {
1046                        continue;
1047                    }
1048
1049                    // Get the rounded clip for this picture cache.
1050                    let (rounded_clip_rect, rounded_clip_radii) = composite_state.compositor_clip_params(
1051                        tile_cache.compositor_clip,
1052                        DeviceRect::max_rect(),
1053                    );
1054
1055                    // Simple compare against the previous compositor clip we found
1056                    if let Some((current_clip_rect, current_clip_radius)) = current_opaque_clip {
1057                        if current_clip_rect == rounded_clip_rect &&
1058                        current_clip_radius == rounded_clip_radii {
1059                            for sub_slice in tile_cache.sub_slices.iter_mut() {
1060                                for tile in sub_slice.tiles.values_mut() {
1061                                    tile.is_visible = false;
1062                                }
1063                            }
1064                        }
1065                    }
1066
1067                    let backdrop_rect = tile_cache.backdrop.backdrop_rect
1068                        .intersection(&tile_cache.local_rect)
1069                        .and_then(|r| {
1070                            r.intersection(&tile_cache.local_clip_rect)
1071                    });
1072
1073                    if let Some(backdrop_rect) = backdrop_rect {
1074                        let map_local_to_world = SpaceMapper::new_with_target(
1075                            frame_context.root_spatial_node_index,
1076                            tile_cache.spatial_node_index,
1077                            frame_context.global_screen_world_rect,
1078                            frame_context.spatial_tree,
1079                        );
1080                        let world_backdrop_rect = map_local_to_world
1081                            .map(&backdrop_rect)
1082                            .expect("bug: unable to map backdrop rect");
1083                        let device_backdrop_rect = (world_backdrop_rect * frame_context.global_device_pixel_scale).round();
1084
1085                        if device_backdrop_rect.contains_box(&rounded_clip_rect) {
1086                            // Save compositor clip for checking against subsequent slices
1087                            current_opaque_clip = Some((rounded_clip_rect, rounded_clip_radii));
1088                        }
1089                    }
1090                }
1091                _ => {
1092                     panic!("bug: found a top-level prim that isn't a tile cache");
1093                }
1094            }
1095        }
1096    }
1097
1098    fn build_composite_pass(
1099        &self,
1100        scene: &BuiltScene,
1101        ctx: &RenderTargetContext,
1102        gpu_buffers: &mut GpuBufferBuilder,
1103        deferred_resolves: &mut FrameVec<DeferredResolve>,
1104        composite_state: &mut CompositeState,
1105    ) {
1106        for pic_index in &scene.tile_cache_pictures {
1107            let pic = &ctx.prim_store.pictures[pic_index.0];
1108
1109            match pic.raster_config {
1110                Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
1111                    // Tile cache instances are added to the composite config, rather than
1112                    // directly added to batches. This allows them to be drawn with various
1113                    // present modes during render, such as partial present etc.
1114                    let tile_cache = &ctx.tile_caches[&slice_id];
1115                    let map_local_to_world = SpaceMapper::new_with_target(
1116                        ctx.root_spatial_node_index,
1117                        tile_cache.spatial_node_index,
1118                        ctx.screen_world_rect,
1119                        ctx.spatial_tree,
1120                    );
1121                    let world_clip_rect = map_local_to_world
1122                        .map(&tile_cache.local_clip_rect)
1123                        .expect("bug: unable to map clip rect");
1124                    let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();
1125
1126                    composite_state.push_surface(
1127                        tile_cache,
1128                        device_clip_rect,
1129                        ctx.resource_cache,
1130                        &mut gpu_buffers.f32,
1131                        deferred_resolves,
1132                    );
1133                }
1134                _ => {
1135                    panic!("bug: found a top-level prim that isn't a tile cache");
1136                }
1137            }
1138        }
1139    }
1140}
1141
1142/// Processes this pass to prepare it for rendering.
1143///
1144/// Among other things, this allocates output regions for each of our tasks
1145/// (added via `add_render_task`) in a RenderTarget and assigns it into that
1146/// target.
1147pub fn build_render_pass(
1148    src_pass: &Pass,
1149    screen_size: DeviceIntSize,
1150    ctx: &mut RenderTargetContext,
1151    gpu_buffer_builder: &mut GpuBufferBuilder,
1152    render_tasks: &RenderTaskGraph,
1153    clip_store: &ClipStore,
1154    transforms: &mut TransformPalette,
1155    prim_headers: &mut PrimitiveHeaders,
1156    z_generator: &mut ZBufferIdGenerator,
1157    gpu_supports_fast_clears: bool,
1158    prim_instances: &[PrimitiveInstance],
1159    cmd_buffers: &CommandBufferList,
1160) -> RenderPass {
1161    profile_scope!("build_render_pass");
1162
1163    // TODO(gw): In this initial frame graph work, we try to maintain the existing
1164    //           build_render_pass code as closely as possible, to make the review
1165    //           simpler and reduce chance of regressions. However, future work should
1166    //           include refactoring this to more closely match the built frame graph.
1167    let mut pass = RenderPass::new(src_pass, ctx.frame_memory);
1168
1169    for sub_pass in &src_pass.sub_passes {
1170        match sub_pass.surface {
1171            SubPassSurface::Dynamic { target_kind, texture_id, used_rect } => {
1172                match target_kind {
1173                    RenderTargetKind::Color => {
1174                        let mut target = RenderTarget::new(
1175                            RenderTargetKind::Color,
1176                            false,
1177                            texture_id,
1178                            screen_size,
1179                            gpu_supports_fast_clears,
1180                            Some(used_rect),
1181                            &ctx.frame_memory,
1182                        );
1183
1184                        for task_id in &sub_pass.task_ids {
1185                            target.add_task(
1186                                *task_id,
1187                                ctx,
1188                                gpu_buffer_builder,
1189                                render_tasks,
1190                                clip_store,
1191                                transforms,
1192                            );
1193                        }
1194
1195                        pass.color.targets.push(target);
1196                    }
1197                    RenderTargetKind::Alpha => {
1198                        let mut target = RenderTarget::new(
1199                            RenderTargetKind::Alpha,
1200                            false,
1201                            texture_id,
1202                            screen_size,
1203                            gpu_supports_fast_clears,
1204                            Some(used_rect),
1205                            &ctx.frame_memory,
1206                        );
1207
1208                        for task_id in &sub_pass.task_ids {
1209                            target.add_task(
1210                                *task_id,
1211                                ctx,
1212                                gpu_buffer_builder,
1213                                render_tasks,
1214                                clip_store,
1215                                transforms,
1216                            );
1217                        }
1218
1219                        pass.alpha.targets.push(target);
1220                    }
1221                }
1222            }
1223            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::PictureCache { ref surface, .. }, .. } => {
1224                assert_eq!(sub_pass.task_ids.len(), 1);
1225                let task_id = sub_pass.task_ids[0];
1226                let task = &render_tasks[task_id];
1227                let target_rect = task.get_target_rect();
1228
1229                match task.kind {
1230                    RenderTaskKind::Picture(ref pic_task) => {
1231                        let cmd_buffer = cmd_buffers.get(pic_task.cmd_buffer_index);
1232                        let mut dirty_rect = pic_task.scissor_rect.expect("bug: must be set for cache tasks");
1233                        let mut valid_rect = pic_task.valid_rect.expect("bug: must be set for cache tasks");
1234
1235                        // If we have a surface size, clip the dirty and vaild rects
1236                        // to that size. This ensures that native compositors will
1237                        // pass sanity checks (Bug 1971296).
1238                        if let ResolvedSurfaceTexture::Native { size, .. } = surface {
1239                            let surface_size_rect = <DeviceIntRect>::from_size(*size);
1240                            dirty_rect = dirty_rect.intersection(&surface_size_rect).unwrap_or_default();
1241                            valid_rect = valid_rect.intersection(&surface_size_rect).unwrap_or_default();
1242                        }
1243
1244                        let batcher = AlphaBatchBuilder::new(
1245                            screen_size,
1246                            ctx.break_advanced_blend_batches,
1247                            ctx.batch_lookback_count,
1248                            task_id,
1249                            task_id.into(),
1250                            &ctx.frame_memory,
1251                        );
1252
1253                        let mut batch_builder = BatchBuilder::new(batcher);
1254
1255                        cmd_buffer.iter_prims(&mut |cmd, spatial_node_index, segments| {
1256                            batch_builder.add_prim_to_batch(
1257                                cmd,
1258                                spatial_node_index,
1259                                ctx,
1260                                render_tasks,
1261                                prim_headers,
1262                                transforms,
1263                                pic_task.raster_spatial_node_index,
1264                                pic_task.surface_spatial_node_index,
1265                                z_generator,
1266                                prim_instances,
1267                                gpu_buffer_builder,
1268                                segments,
1269                            );
1270                        });
1271
1272                        let batcher = batch_builder.finalize();
1273
1274                        let mut batch_containers = ctx.frame_memory.new_vec();
1275                        let mut alpha_batch_container = AlphaBatchContainer::new(
1276                            Some(dirty_rect),
1277                            &ctx.frame_memory
1278                        );
1279
1280                        batcher.build(
1281                            &mut batch_containers,
1282                            &mut alpha_batch_container,
1283                            target_rect,
1284                            None,
1285                        );
1286                        debug_assert!(batch_containers.is_empty());
1287
1288                        let target = PictureCacheTarget {
1289                            surface: surface.clone(),
1290                            clear_color: pic_task.clear_color,
1291                            kind: PictureCacheTargetKind::Draw {
1292                                alpha_batch_container,
1293                            },
1294                            dirty_rect,
1295                            valid_rect,
1296                        };
1297
1298                        pass.picture_cache.push(target);
1299                    }
1300                    RenderTaskKind::TileComposite(ref tile_task) => {
1301                        let mut dirty_rect = tile_task.scissor_rect;
1302                        let mut valid_rect = tile_task.valid_rect;
1303                        // If we have a surface size, clip the dirty and vaild rects
1304                        // to that size. This ensures that native compositors will
1305                        // pass sanity checks (Bug 1971296).
1306                        if let ResolvedSurfaceTexture::Native { size, .. } = surface {
1307                            let surface_size_rect = <DeviceIntRect>::from_size(*size);
1308                            dirty_rect = dirty_rect.intersection(&surface_size_rect).unwrap_or_default();
1309                            valid_rect = valid_rect.intersection(&surface_size_rect).unwrap_or_default();
1310                        }
1311
1312                        let target = PictureCacheTarget {
1313                            surface: surface.clone(),
1314                            clear_color: Some(tile_task.clear_color),
1315                            kind: PictureCacheTargetKind::Blit {
1316                                task_id: tile_task.task_id.expect("bug: no source task_id set"),
1317                                sub_rect_offset: tile_task.sub_rect_offset,
1318                            },
1319                            dirty_rect,
1320                            valid_rect,
1321                        };
1322
1323                        pass.picture_cache.push(target);
1324                    }
1325                    _ => {
1326                        unreachable!();
1327                    }
1328                };
1329            }
1330            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, .. } } => {
1331                let texture = pass.texture_cache
1332                    .entry(texture)
1333                    .or_insert_with(||
1334                        RenderTarget::new(
1335                            target_kind,
1336                            true,
1337                            texture,
1338                            screen_size,
1339                            gpu_supports_fast_clears,
1340                            None,
1341                            &ctx.frame_memory
1342                        )
1343                    );
1344                for task_id in &sub_pass.task_ids {
1345                    texture.add_task(
1346                        *task_id,
1347                        ctx,
1348                        gpu_buffer_builder,
1349                        render_tasks,
1350                        clip_store,
1351                        transforms,
1352                    );
1353                }
1354            }
1355            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
1356                panic!("Should not create a render pass for read-only task locations.");
1357            }
1358        }
1359    }
1360
1361    pass.color.build(
1362        ctx,
1363        render_tasks,
1364        prim_headers,
1365        transforms,
1366        z_generator,
1367        prim_instances,
1368        cmd_buffers,
1369        gpu_buffer_builder,
1370    );
1371    pass.alpha.build(
1372        ctx,
1373        render_tasks,
1374        prim_headers,
1375        transforms,
1376        z_generator,
1377        prim_instances,
1378        cmd_buffers,
1379        gpu_buffer_builder,
1380    );
1381
1382    for target in &mut pass.texture_cache.values_mut() {
1383        target.build(
1384            ctx,
1385            render_tasks,
1386            prim_headers,
1387            transforms,
1388            z_generator,
1389            prim_instances,
1390            cmd_buffers,
1391            gpu_buffer_builder,
1392        );
1393    }
1394
1395    pass
1396}
1397
1398/// A rendering-oriented representation of the frame built by the render backend
1399/// and presented to the renderer.
1400///
1401/// # Safety
1402///
1403/// The frame's allocator memory must be dropped after all of the frame's containers.
1404/// This is handled in the renderer and in `RenderedDocument`'s Drop implementation.
1405#[cfg_attr(feature = "capture", derive(Serialize))]
1406#[cfg_attr(feature = "replay", derive(Deserialize))]
1407pub struct Frame {
1408    /// The rectangle to show the frame in, on screen.
1409    pub device_rect: DeviceIntRect,
1410    pub present: bool,
1411    pub passes: FrameVec<RenderPass>,
1412
1413    pub transform_palette: FrameVec<TransformData>,
1414    pub render_tasks: RenderTaskGraph,
1415    pub prim_headers: PrimitiveHeaders,
1416
1417    /// List of textures that we don't know about yet
1418    /// from the backend thread. The render thread
1419    /// will use a callback to resolve these and
1420    /// patch the data structures.
1421    pub deferred_resolves: FrameVec<DeferredResolve>,
1422
1423    /// True if this frame contains any render tasks
1424    /// that write to the texture cache.
1425    pub has_texture_cache_tasks: bool,
1426
1427    /// True if this frame has been drawn by the
1428    /// renderer.
1429    pub has_been_rendered: bool,
1430
1431    /// Debugging information to overlay for this frame.
1432    pub debug_items: Vec<DebugItem>,
1433
1434    /// Contains picture cache tiles, and associated information.
1435    /// Used by the renderer to composite tiles into the framebuffer,
1436    /// or hand them off to an OS compositor.
1437    pub composite_state: CompositeState,
1438
1439    /// Main GPU data buffer constructed (primarily) during the prepare
1440    /// pass for primitives that were visible and dirty.
1441    pub gpu_buffer_f: GpuBufferF,
1442    pub gpu_buffer_i: GpuBufferI,
1443
1444    /// The backing store for the frame's allocator.
1445    ///
1446    /// # Safety
1447    ///
1448    /// Must not be dropped while frame allocations are alive.
1449    ///
1450    /// Rust has deterministic drop order [1]. We rely on `allocator_memory`
1451    /// being the last member of the `Frame` struct so that it is dropped
1452    /// after the frame's containers.
1453    ///
1454    /// [1]: https://doc.rust-lang.org/reference/destructors.html
1455    pub allocator_memory: FrameMemory,
1456}
1457
1458impl Frame {
1459    // This frame must be flushed if it writes to the
1460    // texture cache, and hasn't been drawn yet.
1461    pub fn must_be_drawn(&self) -> bool {
1462        self.has_texture_cache_tasks && !self.has_been_rendered
1463    }
1464
1465    // Returns true if this frame doesn't alter what is on screen currently.
1466    pub fn is_nop(&self) -> bool {
1467        // If there are no off-screen passes, that implies that there are no
1468        // picture cache tiles, and no texture cache tasks being updates. If this
1469        // is the case, we can consider the frame a nop (higher level checks
1470        // test if a composite is needed due to picture cache surfaces moving
1471        // or external surfaces being updated).
1472        self.passes.is_empty()
1473    }
1474}