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