azul_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, FontRenderMode, PremultipliedColorF};
6use api::units::*;
7use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
8use crate::clip::{ClipStore, ClipChainStack};
9use crate::spatial_tree::{SpatialTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
10use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator};
11use crate::debug_item::DebugItem;
12use crate::gpu_cache::{GpuCache, GpuCacheHandle};
13use crate::gpu_types::{PrimitiveHeaders, TransformPalette, ZBufferIdGenerator};
14use crate::gpu_types::TransformData;
15use crate::internal_types::{FastHashMap, PlaneSplitter};
16use crate::picture::{DirtyRegion, PictureUpdateState, SliceId, TileCacheInstance};
17use crate::picture::{SurfaceInfo, SurfaceIndex, ROOT_SURFACE_INDEX, SurfaceRenderTasks, SubSliceIndex};
18use crate::picture::{BackdropKind, SubpixelMode, TileCacheLogger, RasterConfig, PictureCompositeMode};
19use crate::prepare::prepare_primitives;
20use crate::prim_store::{PictureIndex, PrimitiveDebugId};
21use crate::prim_store::{DeferredResolve};
22use crate::profiler::{self, TransactionProfile};
23use crate::render_backend::{DataStores, FrameStamp, FrameId, ScratchBuffer};
24use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget};
25use crate::render_target::{RenderTargetContext, RenderTargetKind, AlphaRenderTarget, ColorRenderTarget};
26use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, Pass, SubPassSurface};
27use crate::render_task_graph::{RenderPass, RenderTaskGraphBuilder};
28use crate::render_task::{RenderTaskLocation, RenderTaskKind, StaticRenderTaskSurface};
29use crate::resource_cache::{ResourceCache};
30use crate::scene::{BuiltScene, SceneProperties};
31use crate::space::SpaceMapper;
32use crate::segment::SegmentBuilder;
33use std::{f32, mem};
34use crate::util::{VecHelper, Recycler, Preallocator};
35use crate::visibility::{update_primitive_visibility, FrameVisibilityState, FrameVisibilityContext};
36
37
38#[derive(Clone, Copy, Debug, PartialEq)]
39#[cfg_attr(feature = "capture", derive(Serialize))]
40#[cfg_attr(feature = "replay", derive(Deserialize))]
41pub enum ChasePrimitive {
42    Nothing,
43    Id(PrimitiveDebugId),
44    LocalRect(LayoutRect),
45}
46
47impl Default for ChasePrimitive {
48    fn default() -> Self {
49        ChasePrimitive::Nothing
50    }
51}
52
53#[derive(Clone, Copy, Debug)]
54#[cfg_attr(feature = "capture", derive(Serialize))]
55#[cfg_attr(feature = "replay", derive(Deserialize))]
56pub struct FrameBuilderConfig {
57    pub default_font_render_mode: FontRenderMode,
58    pub dual_source_blending_is_supported: bool,
59    pub dual_source_blending_is_enabled: bool,
60    pub chase_primitive: ChasePrimitive,
61    /// True if we're running tests (i.e. via wrench).
62    pub testing: bool,
63    pub gpu_supports_fast_clears: bool,
64    pub gpu_supports_advanced_blend: bool,
65    pub advanced_blend_is_coherent: bool,
66    pub gpu_supports_render_target_partial_update: bool,
67    /// Whether ImageBufferKind::TextureExternal images must first be copied
68    /// to a regular texture before rendering.
69    pub external_images_require_copy: bool,
70    pub batch_lookback_count: usize,
71    pub background_color: Option<ColorF>,
72    pub compositor_kind: CompositorKind,
73    pub tile_size_override: Option<DeviceIntSize>,
74    pub max_depth_ids: i32,
75    pub max_target_size: i32,
76    pub force_invalidation: bool,
77    pub is_software: bool,
78    pub low_quality_pinch_zoom: bool,
79}
80
81/// A set of common / global resources that are retained between
82/// new display lists, such that any GPU cache handles can be
83/// persisted even when a new display list arrives.
84#[cfg_attr(feature = "capture", derive(Serialize))]
85pub struct FrameGlobalResources {
86    /// The image shader block for the most common / default
87    /// set of image parameters (color white, stretch == rect.size).
88    pub default_image_handle: GpuCacheHandle,
89
90    /// A GPU cache config for drawing transparent rectangle primitives.
91    /// This is used to 'cut out' overlay tiles where a compositor
92    /// surface exists.
93    pub default_transparent_rect_handle: GpuCacheHandle,
94}
95
96impl FrameGlobalResources {
97    pub fn empty() -> Self {
98        FrameGlobalResources {
99            default_image_handle: GpuCacheHandle::new(),
100            default_transparent_rect_handle: GpuCacheHandle::new(),
101        }
102    }
103
104    pub fn update(
105        &mut self,
106        gpu_cache: &mut GpuCache,
107    ) {
108        if let Some(mut request) = gpu_cache.request(&mut self.default_image_handle) {
109            request.push(PremultipliedColorF::WHITE);
110            request.push(PremultipliedColorF::WHITE);
111            request.push([
112                -1.0,       // -ve means use prim rect for stretch size
113                0.0,
114                0.0,
115                0.0,
116            ]);
117        }
118
119        if let Some(mut request) = gpu_cache.request(&mut self.default_transparent_rect_handle) {
120            request.push(PremultipliedColorF::TRANSPARENT);
121        }
122    }
123}
124
125pub struct FrameScratchBuffer {
126    surfaces: Vec<SurfaceInfo>,
127    dirty_region_stack: Vec<DirtyRegion>,
128    surface_stack: Vec<SurfaceIndex>,
129    clip_chain_stack: ClipChainStack,
130}
131
132impl Default for FrameScratchBuffer {
133    fn default() -> Self {
134        FrameScratchBuffer {
135            surfaces: Vec::new(),
136            dirty_region_stack: Vec::new(),
137            surface_stack: Vec::new(),
138            clip_chain_stack: ClipChainStack::new(),
139        }
140    }
141}
142
143impl FrameScratchBuffer {
144    pub fn begin_frame(&mut self) {
145        self.surfaces.clear();
146        self.dirty_region_stack.clear();
147        self.surface_stack.clear();
148        self.clip_chain_stack.clear();
149    }
150
151    pub fn recycle(&mut self, recycler: &mut Recycler) {
152        recycler.recycle_vec(&mut self.surfaces);
153        // Don't call recycle on the stacks because the reycler's
154        // role is to get rid of allocations when the capacity
155        // is much larger than the lengths. with stacks the
156        // length varies through the frame but is supposedly
157        // back to zero by the end so we would always throw the
158        // allocation away.
159    }
160}
161
162/// Produces the frames that are sent to the renderer.
163#[cfg_attr(feature = "capture", derive(Serialize))]
164pub struct FrameBuilder {
165    pub globals: FrameGlobalResources,
166    #[cfg_attr(feature = "capture", serde(skip))]
167    prim_headers_prealloc: Preallocator,
168    #[cfg_attr(feature = "capture", serde(skip))]
169    composite_state_prealloc: CompositeStatePreallocator,
170}
171
172pub struct FrameBuildingContext<'a> {
173    pub global_device_pixel_scale: DevicePixelScale,
174    pub scene_properties: &'a SceneProperties,
175    pub global_screen_world_rect: WorldRect,
176    pub spatial_tree: &'a SpatialTree,
177    pub max_local_clip: LayoutRect,
178    pub debug_flags: DebugFlags,
179    pub fb_config: &'a FrameBuilderConfig,
180}
181
182pub struct FrameBuildingState<'a> {
183    pub rg_builder: &'a mut RenderTaskGraphBuilder,
184    pub clip_store: &'a mut ClipStore,
185    pub resource_cache: &'a mut ResourceCache,
186    pub gpu_cache: &'a mut GpuCache,
187    pub transforms: &'a mut TransformPalette,
188    pub segment_builder: SegmentBuilder,
189    pub surfaces: &'a mut Vec<SurfaceInfo>,
190    pub dirty_region_stack: Vec<DirtyRegion>,
191    pub composite_state: &'a mut CompositeState,
192    pub num_visible_primitives: u32,
193}
194
195impl<'a> FrameBuildingState<'a> {
196    /// Retrieve the current dirty region during primitive traversal.
197    pub fn current_dirty_region(&self) -> &DirtyRegion {
198        self.dirty_region_stack.last().unwrap()
199    }
200
201    /// Push a new dirty region for child primitives to cull / clip against.
202    pub fn push_dirty_region(&mut self, region: DirtyRegion) {
203        self.dirty_region_stack.push(region);
204    }
205
206    /// Pop the top dirty region from the stack.
207    pub fn pop_dirty_region(&mut self) {
208        self.dirty_region_stack.pop().unwrap();
209    }
210
211    /// Initialize render tasks for a surface that is tiled (currently applies
212    /// only to picture cache surfaces).
213    pub fn init_surface_tiled(
214        &mut self,
215        surface_index: SurfaceIndex,
216        tasks: Vec<RenderTaskId>,
217        raster_rect: DeviceRect,
218    ) {
219        let surface = &mut self.surfaces[surface_index.0];
220        assert!(surface.render_tasks.is_none());
221        surface.render_tasks = Some(SurfaceRenderTasks::Tiled(tasks));
222        surface.raster_rect = Some(raster_rect);
223    }
224
225    /// Initialize render tasks for a simple surface, that contains only a
226    /// single render task.
227    pub fn init_surface(
228        &mut self,
229        surface_index: SurfaceIndex,
230        task_id: RenderTaskId,
231        parent_surface_index: SurfaceIndex,
232        raster_rect: DeviceRect,
233    ) {
234        let surface = &mut self.surfaces[surface_index.0];
235        assert!(surface.render_tasks.is_none());
236        surface.render_tasks = Some(SurfaceRenderTasks::Simple(task_id));
237        surface.raster_rect = Some(raster_rect);
238
239        self.add_child_render_task(
240            parent_surface_index,
241            task_id,
242        );
243    }
244
245    /// Initialize render tasks for a surface that is made up of a chain of
246    /// render tasks, where the final output render task is different than the
247    /// input render task (for example, a blur pass on a picture).
248    pub fn init_surface_chain(
249        &mut self,
250        surface_index: SurfaceIndex,
251        root_task_id: RenderTaskId,
252        port_task_id: RenderTaskId,
253        parent_surface_index: SurfaceIndex,
254        raster_rect: DeviceRect,
255    ) {
256        let surface = &mut self.surfaces[surface_index.0];
257        assert!(surface.render_tasks.is_none());
258        surface.render_tasks = Some(SurfaceRenderTasks::Chained { root_task_id, port_task_id });
259        surface.raster_rect = Some(raster_rect);
260
261        self.add_child_render_task(
262            parent_surface_index,
263            root_task_id,
264        );
265    }
266
267    /// Add a render task as a dependency of a given surface.
268    pub fn add_child_render_task(
269        &mut self,
270        surface_index: SurfaceIndex,
271        child_task_id: RenderTaskId,
272    ) {
273        add_child_render_task(
274            surface_index,
275            child_task_id,
276            self.surfaces,
277            self.rg_builder,
278        );
279    }
280}
281
282/// Immutable context of a picture when processing children.
283#[derive(Debug)]
284pub struct PictureContext {
285    pub pic_index: PictureIndex,
286    pub apply_local_clip_rect: bool,
287    pub surface_spatial_node_index: SpatialNodeIndex,
288    pub raster_spatial_node_index: SpatialNodeIndex,
289    /// The surface that this picture will render on.
290    pub surface_index: SurfaceIndex,
291    pub dirty_region_count: usize,
292    pub subpixel_mode: SubpixelMode,
293}
294
295/// Mutable state of a picture that gets modified when
296/// the children are processed.
297pub struct PictureState {
298    pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
299    pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
300    pub map_pic_to_raster: SpaceMapper<PicturePixel, RasterPixel>,
301    pub map_raster_to_world: SpaceMapper<RasterPixel, WorldPixel>,
302    /// If the plane splitter, the primitives get added to it instead of
303    /// batching into their parent pictures.
304    pub plane_splitter: Option<PlaneSplitter>,
305}
306
307impl FrameBuilder {
308    pub fn new() -> Self {
309        FrameBuilder {
310            globals: FrameGlobalResources::empty(),
311            prim_headers_prealloc: Preallocator::new(0),
312            composite_state_prealloc: CompositeStatePreallocator::default(),
313        }
314    }
315
316    /// Compute the contribution (bounding rectangles, and resources) of layers and their
317    /// primitives in screen space.
318    fn build_layer_screen_rects_and_cull_layers(
319        &mut self,
320        scene: &mut BuiltScene,
321        global_screen_world_rect: WorldRect,
322        resource_cache: &mut ResourceCache,
323        gpu_cache: &mut GpuCache,
324        rg_builder: &mut RenderTaskGraphBuilder,
325        global_device_pixel_scale: DevicePixelScale,
326        scene_properties: &SceneProperties,
327        transform_palette: &mut TransformPalette,
328        data_stores: &mut DataStores,
329        scratch: &mut ScratchBuffer,
330        debug_flags: DebugFlags,
331        composite_state: &mut CompositeState,
332        tile_cache_logger: &mut TileCacheLogger,
333        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
334        profile: &mut TransactionProfile,
335    ) {
336        profile_scope!("build_layer_screen_rects_and_cull_layers");
337
338        let root_spatial_node_index = scene.spatial_tree.root_reference_frame_index();
339
340        const MAX_CLIP_COORD: f32 = 1.0e9;
341
342        let frame_context = FrameBuildingContext {
343            global_device_pixel_scale,
344            scene_properties,
345            global_screen_world_rect,
346            spatial_tree: &scene.spatial_tree,
347            max_local_clip: LayoutRect {
348                min: LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
349                max: LayoutPoint::new(MAX_CLIP_COORD, MAX_CLIP_COORD),
350            },
351            debug_flags,
352            fb_config: &scene.config,
353        };
354
355        // Construct a dummy root surface, that represents the
356        // main framebuffer surface.
357        let root_surface = SurfaceInfo::new(
358            ROOT_SPATIAL_NODE_INDEX,
359            ROOT_SPATIAL_NODE_INDEX,
360            0.0,
361            global_screen_world_rect,
362            &scene.spatial_tree,
363            global_device_pixel_scale,
364            (1.0, 1.0),
365        );
366        let mut surfaces = scratch.frame.surfaces.take();
367        surfaces.push(root_surface);
368
369        // The first major pass of building a frame is to walk the picture
370        // tree. This pass must be quick (it should never touch individual
371        // primitives). For now, all we do here is determine which pictures
372        // will create surfaces. In the future, this will be expanded to
373        // set up render tasks, determine scaling of surfaces, and detect
374        // which surfaces have valid cached surfaces that don't need to
375        // be rendered this frame.
376        for pic_index in &scene.tile_cache_pictures {
377            PictureUpdateState::update_all(
378                &mut scratch.picture,
379                &mut surfaces,
380                *pic_index,
381                &mut scene.prim_store.pictures,
382                &frame_context,
383                gpu_cache,
384                &scene.clip_store,
385                data_stores,
386                tile_caches,
387            );
388        }
389
390        {
391            profile_scope!("UpdateVisibility");
392            profile_marker!("UpdateVisibility");
393            profile.start_time(profiler::FRAME_VISIBILITY_TIME);
394
395            let visibility_context = FrameVisibilityContext {
396                global_device_pixel_scale,
397                spatial_tree: &scene.spatial_tree,
398                global_screen_world_rect,
399                surfaces: &mut surfaces,
400                debug_flags,
401                scene_properties,
402                config: scene.config,
403            };
404
405            let mut visibility_state = FrameVisibilityState {
406                clip_chain_stack: scratch.frame.clip_chain_stack.take(),
407                surface_stack: scratch.frame.surface_stack.take(),
408                resource_cache,
409                gpu_cache,
410                clip_store: &mut scene.clip_store,
411                scratch,
412                tile_cache: None,
413                data_stores,
414                composite_state,
415            };
416
417            for pic_index in scene.tile_cache_pictures.iter().rev() {
418                update_primitive_visibility(
419                    &mut scene.prim_store,
420                    *pic_index,
421                    ROOT_SURFACE_INDEX,
422                    &global_screen_world_rect,
423                    &visibility_context,
424                    &mut visibility_state,
425                    tile_caches,
426                    true,
427                );
428            }
429
430            visibility_state.scratch.frame.clip_chain_stack = visibility_state.clip_chain_stack.take();
431            visibility_state.scratch.frame.surface_stack = visibility_state.surface_stack.take();
432
433            profile.end_time(profiler::FRAME_VISIBILITY_TIME);
434        }
435
436        profile.start_time(profiler::FRAME_PREPARE_TIME);
437
438        let mut frame_state = FrameBuildingState {
439            rg_builder,
440            clip_store: &mut scene.clip_store,
441            resource_cache,
442            gpu_cache,
443            transforms: transform_palette,
444            segment_builder: SegmentBuilder::new(),
445            surfaces: &mut surfaces,
446            dirty_region_stack: scratch.frame.dirty_region_stack.take(),
447            composite_state,
448            num_visible_primitives: 0,
449        };
450
451        // Push a default dirty region which culls primitives
452        // against the screen world rect, in absence of any
453        // other dirty regions.
454        let mut default_dirty_region = DirtyRegion::new(
455            ROOT_SPATIAL_NODE_INDEX,
456        );
457        default_dirty_region.add_dirty_region(
458            frame_context.global_screen_world_rect.cast_unit(),
459            SubSliceIndex::DEFAULT,
460            frame_context.spatial_tree,
461        );
462        frame_state.push_dirty_region(default_dirty_region);
463
464        for pic_index in &scene.tile_cache_pictures {
465            if let Some((pic_context, mut pic_state, mut prim_list)) = scene
466                .prim_store
467                .pictures[pic_index.0]
468                .take_context(
469                    *pic_index,
470                    root_spatial_node_index,
471                    root_spatial_node_index,
472                    ROOT_SURFACE_INDEX,
473                    SubpixelMode::Allow,
474                    &mut frame_state,
475                    &frame_context,
476                    &mut scratch.primitive,
477                    tile_cache_logger,
478                    tile_caches,
479                )
480            {
481                profile_marker!("PreparePrims");
482
483                prepare_primitives(
484                    &mut scene.prim_store,
485                    &mut prim_list,
486                    &pic_context,
487                    &mut pic_state,
488                    &frame_context,
489                    &mut frame_state,
490                    data_stores,
491                    &mut scratch.primitive,
492                    tile_cache_logger,
493                    tile_caches,
494                );
495
496                let pic = &mut scene.prim_store.pictures[pic_index.0];
497                pic.restore_context(
498                    prim_list,
499                    pic_context,
500                    pic_state,
501                    &mut frame_state,
502                );
503            }
504        }
505
506        tile_cache_logger.advance();
507        frame_state.pop_dirty_region();
508        profile.end_time(profiler::FRAME_PREPARE_TIME);
509        profile.set(profiler::VISIBLE_PRIMITIVES, frame_state.num_visible_primitives);
510
511        scratch.frame.dirty_region_stack = frame_state.dirty_region_stack.take();
512        scratch.frame.surfaces = surfaces.take();
513
514        {
515            profile_marker!("BlockOnResources");
516
517            resource_cache.block_until_all_resources_added(
518                gpu_cache,
519                profile,
520            );
521        }
522    }
523
524    pub fn build(
525        &mut self,
526        scene: &mut BuiltScene,
527        resource_cache: &mut ResourceCache,
528        gpu_cache: &mut GpuCache,
529        rg_builder: &mut RenderTaskGraphBuilder,
530        stamp: FrameStamp,
531        device_origin: DeviceIntPoint,
532        scene_properties: &SceneProperties,
533        data_stores: &mut DataStores,
534        scratch: &mut ScratchBuffer,
535        debug_flags: DebugFlags,
536        tile_cache_logger: &mut TileCacheLogger,
537        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
538        dirty_rects_are_valid: bool,
539        profile: &mut TransactionProfile,
540    ) -> Frame {
541        profile_scope!("build");
542        profile_marker!("BuildFrame");
543
544        profile.set(profiler::PRIMITIVES, scene.prim_store.prim_count());
545        profile.set(profiler::PICTURE_CACHE_SLICES, scene.tile_cache_config.picture_cache_slice_count);
546        scratch.begin_frame();
547        resource_cache.begin_frame(stamp, profile);
548        gpu_cache.begin_frame(stamp);
549
550        self.globals.update(gpu_cache);
551
552        scene.spatial_tree.update_tree(scene_properties);
553        let mut transform_palette = scene.spatial_tree.build_transform_palette();
554        scene.clip_store.begin_frame(&mut scratch.clip_store);
555
556        rg_builder.begin_frame(stamp.frame_id());
557
558        // TODO(dp): Remove me completely!!
559        let global_device_pixel_scale = DevicePixelScale::new(1.0);
560
561        let output_size = scene.output_rect.size();
562        let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
563
564        let mut composite_state = CompositeState::new(
565            scene.config.compositor_kind,
566            scene.config.max_depth_ids,
567            dirty_rects_are_valid,
568            scene.config.low_quality_pinch_zoom,
569        );
570
571        self.composite_state_prealloc.preallocate(&mut composite_state);
572
573        self.build_layer_screen_rects_and_cull_layers(
574            scene,
575            screen_world_rect,
576            resource_cache,
577            gpu_cache,
578            rg_builder,
579            global_device_pixel_scale,
580            scene_properties,
581            &mut transform_palette,
582            data_stores,
583            scratch,
584            debug_flags,
585            &mut composite_state,
586            tile_cache_logger,
587            tile_caches,
588            profile,
589        );
590
591        profile.start_time(profiler::FRAME_BATCHING_TIME);
592
593        let mut deferred_resolves = vec![];
594
595        // Finish creating the frame graph and build it.
596        let render_tasks = rg_builder.end_frame(
597            resource_cache,
598            gpu_cache,
599            &mut deferred_resolves,
600        );
601
602        let mut passes = Vec::new();
603        let mut has_texture_cache_tasks = false;
604        let mut prim_headers = PrimitiveHeaders::new();
605        self.prim_headers_prealloc.preallocate_vec(&mut prim_headers.headers_int);
606        self.prim_headers_prealloc.preallocate_vec(&mut prim_headers.headers_float);
607
608        {
609            profile_marker!("Batching");
610
611            // Used to generated a unique z-buffer value per primitive.
612            let mut z_generator = ZBufferIdGenerator::new(scene.config.max_depth_ids);
613            let use_dual_source_blending = scene.config.dual_source_blending_is_enabled &&
614                                           scene.config.dual_source_blending_is_supported;
615
616            for pass in render_tasks.passes.iter().rev() {
617                let mut ctx = RenderTargetContext {
618                    global_device_pixel_scale,
619                    prim_store: &scene.prim_store,
620                    resource_cache,
621                    use_dual_source_blending,
622                    use_advanced_blending: scene.config.gpu_supports_advanced_blend,
623                    break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
624                    batch_lookback_count: scene.config.batch_lookback_count,
625                    spatial_tree: &scene.spatial_tree,
626                    data_stores,
627                    surfaces: &scratch.frame.surfaces,
628                    scratch: &mut scratch.primitive,
629                    screen_world_rect,
630                    globals: &self.globals,
631                    tile_caches,
632                };
633
634                let pass = build_render_pass(
635                    pass,
636                    output_size,
637                    &mut ctx,
638                    gpu_cache,
639                    &render_tasks,
640                    &mut deferred_resolves,
641                    &scene.clip_store,
642                    &mut transform_palette,
643                    &mut prim_headers,
644                    &mut z_generator,
645                    &mut composite_state,
646                    scene.config.gpu_supports_fast_clears,
647                );
648
649                has_texture_cache_tasks |= !pass.texture_cache.is_empty();
650                has_texture_cache_tasks |= !pass.picture_cache.is_empty();
651
652                passes.push(pass);
653            }
654
655            let mut ctx = RenderTargetContext {
656                global_device_pixel_scale,
657                prim_store: &scene.prim_store,
658                resource_cache,
659                use_dual_source_blending,
660                use_advanced_blending: scene.config.gpu_supports_advanced_blend,
661                break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
662                batch_lookback_count: scene.config.batch_lookback_count,
663                spatial_tree: &scene.spatial_tree,
664                data_stores,
665                surfaces: &scratch.frame.surfaces,
666                scratch: &mut scratch.primitive,
667                screen_world_rect,
668                globals: &self.globals,
669                tile_caches,
670            };
671
672            self.build_composite_pass(
673                scene,
674                &mut ctx,
675                gpu_cache,
676                &mut deferred_resolves,
677                &mut composite_state,
678            );
679        }
680
681        profile.end_time(profiler::FRAME_BATCHING_TIME);
682
683        let gpu_cache_frame_id = gpu_cache.end_frame(profile).frame_id();
684
685        resource_cache.end_frame(profile);
686
687        self.prim_headers_prealloc.record_vec(&mut prim_headers.headers_int);
688        self.composite_state_prealloc.record(&composite_state);
689
690        composite_state.end_frame();
691        scene.clip_store.end_frame(&mut scratch.clip_store);
692        scratch.end_frame();
693
694        Frame {
695            device_rect: DeviceIntRect::from_origin_and_size(
696                device_origin,
697                scene.output_rect.size(),
698            ),
699            passes,
700            transform_palette: transform_palette.finish(),
701            render_tasks,
702            deferred_resolves,
703            gpu_cache_frame_id,
704            has_been_rendered: false,
705            has_texture_cache_tasks,
706            prim_headers,
707            debug_items: mem::replace(&mut scratch.primitive.debug_items, Vec::new()),
708            composite_state,
709        }
710    }
711
712    fn build_composite_pass(
713        &self,
714        scene: &BuiltScene,
715        ctx: &RenderTargetContext,
716        gpu_cache: &mut GpuCache,
717        deferred_resolves: &mut Vec<DeferredResolve>,
718        composite_state: &mut CompositeState,
719    ) {
720        for pic_index in &scene.tile_cache_pictures {
721            let pic = &ctx.prim_store.pictures[pic_index.0];
722
723            match pic.raster_config {
724                Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
725                    // Tile cache instances are added to the composite config, rather than
726                    // directly added to batches. This allows them to be drawn with various
727                    // present modes during render, such as partial present etc.
728                    let tile_cache = &ctx.tile_caches[&slice_id];
729                    let map_local_to_world = SpaceMapper::new_with_target(
730                        ROOT_SPATIAL_NODE_INDEX,
731                        tile_cache.spatial_node_index,
732                        ctx.screen_world_rect,
733                        ctx.spatial_tree,
734                    );
735                    let world_clip_rect = map_local_to_world
736                        .map(&tile_cache.local_clip_rect)
737                        .expect("bug: unable to map clip rect");
738                    let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();
739
740                    composite_state.push_surface(
741                        tile_cache,
742                        device_clip_rect,
743                        ctx.resource_cache,
744                        gpu_cache,
745                        deferred_resolves,
746                    );
747                }
748                _ => {
749                    panic!("bug: found a top-level prim that isn't a tile cache");
750                }
751            }
752        }
753    }
754}
755
756/// Processes this pass to prepare it for rendering.
757///
758/// Among other things, this allocates output regions for each of our tasks
759/// (added via `add_render_task`) in a RenderTarget and assigns it into that
760/// target.
761pub fn build_render_pass(
762    src_pass: &Pass,
763    screen_size: DeviceIntSize,
764    ctx: &mut RenderTargetContext,
765    gpu_cache: &mut GpuCache,
766    render_tasks: &RenderTaskGraph,
767    deferred_resolves: &mut Vec<DeferredResolve>,
768    clip_store: &ClipStore,
769    transforms: &mut TransformPalette,
770    prim_headers: &mut PrimitiveHeaders,
771    z_generator: &mut ZBufferIdGenerator,
772    composite_state: &mut CompositeState,
773    gpu_supports_fast_clears: bool,
774) -> RenderPass {
775    profile_scope!("build_render_pass");
776
777    // TODO(gw): In this initial frame graph work, we try to maintain the existing
778    //           build_render_pass code as closely as possible, to make the review
779    //           simpler and reduce chance of regressions. However, future work should
780    //           include refactoring this to more closely match the built frame graph.
781
782    // Collect a list of picture cache tasks, keyed by picture index.
783    // This allows us to only walk that picture root once, adding the
784    // primitives to all relevant batches at the same time.
785    let mut picture_cache_tasks = FastHashMap::default();
786    let mut pass = RenderPass::new(src_pass);
787
788    for sub_pass in &src_pass.sub_passes {
789        match sub_pass.surface {
790            SubPassSurface::Dynamic { target_kind, texture_id, used_rect } => {
791                match target_kind {
792                    RenderTargetKind::Color => {
793                        let mut target = ColorRenderTarget::new(
794                            texture_id,
795                            screen_size,
796                            gpu_supports_fast_clears,
797                            used_rect,
798                        );
799
800                        for task_id in &sub_pass.task_ids {
801                            target.add_task(
802                                *task_id,
803                                ctx,
804                                gpu_cache,
805                                render_tasks,
806                                clip_store,
807                                transforms,
808                            );
809                        }
810
811                        pass.color.targets.push(target);
812                    }
813                    RenderTargetKind::Alpha => {
814                        let mut target = AlphaRenderTarget::new(
815                            texture_id,
816                            screen_size,
817                            gpu_supports_fast_clears,
818                            used_rect,
819                        );
820
821                        for task_id in &sub_pass.task_ids {
822                            target.add_task(
823                                *task_id,
824                                ctx,
825                                gpu_cache,
826                                render_tasks,
827                                clip_store,
828                                transforms,
829                            );
830                        }
831
832                        pass.alpha.targets.push(target);
833                    }
834                }
835            }
836            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::PictureCache { .. }, .. } => {
837                assert_eq!(sub_pass.task_ids.len(), 1);
838                let task_id = sub_pass.task_ids[0];
839                let task = &render_tasks[task_id];
840
841                // For picture cache tiles, just store them in the map
842                // of picture cache tasks, to be handled below.
843                let pic_index = match task.kind {
844                    RenderTaskKind::Picture(ref info) => {
845                        info.pic_index
846                    }
847                    _ => {
848                        unreachable!();
849                    }
850                };
851
852                picture_cache_tasks
853                    .entry(pic_index)
854                    .or_insert_with(Vec::new)
855                    .push(task_id);
856            }
857            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, .. } } => {
858                let texture = pass.texture_cache
859                    .entry(texture)
860                    .or_insert_with(||
861                        TextureCacheRenderTarget::new(target_kind)
862                    );
863                for task_id in &sub_pass.task_ids {
864                    texture.add_task(*task_id, render_tasks, gpu_cache);
865                }
866            }
867            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
868                panic!("Should not create a render pass for read-only task locations.");
869            }
870        }
871    }
872
873    // For each picture in this pass that has picture cache tiles, create
874    // a batcher per task, and then build batches for each of the tasks
875    // at the same time.
876    for (pic_index, task_ids) in picture_cache_tasks {
877        profile_scope!("picture_cache_task");
878        let pic = &ctx.prim_store.pictures[pic_index.0];
879
880        // Extract raster/surface spatial nodes for this surface.
881        let (root_spatial_node_index, surface_spatial_node_index, tile_cache) = match pic.raster_config {
882            Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
883                let surface = &ctx.surfaces[surface_index.0];
884                (
885                    surface.raster_spatial_node_index,
886                    surface.surface_spatial_node_index,
887                    &ctx.tile_caches[&slice_id],
888                )
889            }
890            _ => {
891                unreachable!();
892            }
893        };
894
895        // Create an alpha batcher for each of the tasks of this picture.
896        let mut batchers = Vec::new();
897        for task_id in &task_ids {
898            let task_id = *task_id;
899            let batch_filter = match render_tasks[task_id].kind {
900                RenderTaskKind::Picture(ref info) => info.batch_filter,
901                _ => unreachable!(),
902            };
903            batchers.push(AlphaBatchBuilder::new(
904                screen_size,
905                ctx.break_advanced_blend_batches,
906                ctx.batch_lookback_count,
907                task_id,
908                task_id.into(),
909                batch_filter,
910                0,
911            ));
912        }
913
914        // Run the batch creation code for this picture, adding items to
915        // all relevant per-task batchers.
916        let mut batch_builder = BatchBuilder::new(batchers);
917        {
918        profile_scope!("add_pic_to_batch");
919        batch_builder.add_pic_to_batch(
920            pic,
921            ctx,
922            gpu_cache,
923            render_tasks,
924            deferred_resolves,
925            prim_headers,
926            transforms,
927            root_spatial_node_index,
928            surface_spatial_node_index,
929            z_generator,
930            composite_state,
931        );
932        }
933
934        // Create picture cache targets, one per render task, and assign
935        // the correct batcher to them.
936        let batchers = batch_builder.finalize();
937        for (task_id, batcher) in task_ids.into_iter().zip(batchers.into_iter()) {
938            profile_scope!("task");
939            let task = &render_tasks[task_id];
940            let target_rect = task.get_target_rect();
941
942            match task.location {
943                RenderTaskLocation::Static { surface: StaticRenderTaskSurface::PictureCache { ref surface, .. }, .. } => {
944                    // TODO(gw): The interface here is a bit untidy since it's
945                    //           designed to support batch merging, which isn't
946                    //           relevant for picture cache targets. We
947                    //           can restructure / tidy this up a bit.
948                    let (scissor_rect, valid_rect, clear_color)  = match render_tasks[task_id].kind {
949                        RenderTaskKind::Picture(ref info) => {
950                            let mut clear_color = ColorF::TRANSPARENT;
951
952                            // TODO(gw): The way we check the batch filter for is_primary is a bit hacky, tidy up somehow?
953                            if let Some(batch_filter) = info.batch_filter {
954                                if batch_filter.sub_slice_index.is_primary() {
955                                    if let Some(background_color) = tile_cache.background_color {
956                                        clear_color = background_color;
957                                    }
958
959                                    // If this picture cache has a valid color backdrop, we will use
960                                    // that as the clear color, skipping the draw of the backdrop
961                                    // primitive (and anything prior to it) during batching.
962                                    if let Some(BackdropKind::Color { color }) = tile_cache.backdrop.kind {
963                                        clear_color = color;
964                                    }
965                                }
966                            }
967
968                            (
969                                info.scissor_rect.expect("bug: must be set for cache tasks"),
970                                info.valid_rect.expect("bug: must be set for cache tasks"),
971                                clear_color,
972                            )
973                        }
974                        _ => unreachable!(),
975                    };
976                    let mut batch_containers = Vec::new();
977                    let mut alpha_batch_container = AlphaBatchContainer::new(Some(scissor_rect));
978                    batcher.build(
979                        &mut batch_containers,
980                        &mut alpha_batch_container,
981                        target_rect,
982                        None,
983                    );
984                    debug_assert!(batch_containers.is_empty());
985
986                    let target = PictureCacheTarget {
987                        surface: surface.clone(),
988                        clear_color: Some(clear_color),
989                        alpha_batch_container,
990                        dirty_rect: scissor_rect,
991                        valid_rect,
992                    };
993
994                    pass.picture_cache.push(target);
995                }
996                _ => {
997                    unreachable!()
998                }
999            }
1000        }
1001    }
1002
1003    pass.color.build(
1004        ctx,
1005        gpu_cache,
1006        render_tasks,
1007        deferred_resolves,
1008        prim_headers,
1009        transforms,
1010        z_generator,
1011        composite_state,
1012    );
1013    pass.alpha.build(
1014        ctx,
1015        gpu_cache,
1016        render_tasks,
1017        deferred_resolves,
1018        prim_headers,
1019        transforms,
1020        z_generator,
1021        composite_state,
1022    );
1023
1024    pass
1025}
1026
1027/// A rendering-oriented representation of the frame built by the render backend
1028/// and presented to the renderer.
1029#[cfg_attr(feature = "capture", derive(Serialize))]
1030#[cfg_attr(feature = "replay", derive(Deserialize))]
1031pub struct Frame {
1032    /// The rectangle to show the frame in, on screen.
1033    pub device_rect: DeviceIntRect,
1034    pub passes: Vec<RenderPass>,
1035
1036    pub transform_palette: Vec<TransformData>,
1037    pub render_tasks: RenderTaskGraph,
1038    pub prim_headers: PrimitiveHeaders,
1039
1040    /// The GPU cache frame that the contents of Self depend on
1041    pub gpu_cache_frame_id: FrameId,
1042
1043    /// List of textures that we don't know about yet
1044    /// from the backend thread. The render thread
1045    /// will use a callback to resolve these and
1046    /// patch the data structures.
1047    pub deferred_resolves: Vec<DeferredResolve>,
1048
1049    /// True if this frame contains any render tasks
1050    /// that write to the texture cache.
1051    pub has_texture_cache_tasks: bool,
1052
1053    /// True if this frame has been drawn by the
1054    /// renderer.
1055    pub has_been_rendered: bool,
1056
1057    /// Debugging information to overlay for this frame.
1058    pub debug_items: Vec<DebugItem>,
1059
1060    /// Contains picture cache tiles, and associated information.
1061    /// Used by the renderer to composite tiles into the framebuffer,
1062    /// or hand them off to an OS compositor.
1063    pub composite_state: CompositeState,
1064}
1065
1066impl Frame {
1067    // This frame must be flushed if it writes to the
1068    // texture cache, and hasn't been drawn yet.
1069    pub fn must_be_drawn(&self) -> bool {
1070        self.has_texture_cache_tasks && !self.has_been_rendered
1071    }
1072
1073    // Returns true if this frame doesn't alter what is on screen currently.
1074    pub fn is_nop(&self) -> bool {
1075        // If there are no off-screen passes, that implies that there are no
1076        // picture cache tiles, and no texture cache tasks being updates. If this
1077        // is the case, we can consider the frame a nop (higher level checks
1078        // test if a composite is needed due to picture cache surfaces moving
1079        // or external surfaces being updated).
1080        self.passes.is_empty()
1081    }
1082}
1083
1084/// Add a child render task as a dependency to a surface. This is a free
1085/// function for now as it's also used by the render task cache.
1086// TODO(gw): Find a more appropriate place for this to live - probably clearer
1087//           once SurfaceInfo gets refactored.
1088pub fn add_child_render_task(
1089    surface_index: SurfaceIndex,
1090    child_task_id: RenderTaskId,
1091    surfaces: &[SurfaceInfo],
1092    rg_builder: &mut RenderTaskGraphBuilder,
1093) {
1094    let surface_tasks = surfaces[surface_index.0]
1095        .render_tasks
1096        .as_ref()
1097        .expect("bug: no task for surface");
1098
1099    match surface_tasks {
1100        SurfaceRenderTasks::Tiled(ref tasks) => {
1101            // For a tiled render task, add as a dependency to every tile.
1102            for parent_id in tasks {
1103                rg_builder.add_dependency(*parent_id, child_task_id);
1104            }
1105        }
1106        SurfaceRenderTasks::Simple(parent_id) => {
1107            rg_builder.add_dependency(*parent_id, child_task_id);
1108        }
1109        SurfaceRenderTasks::Chained { port_task_id, .. } => {
1110            // For chained render tasks, add as a dependency of the lowest part of
1111            // the chain (the picture content)
1112            rg_builder.add_dependency(*port_task_id, child_task_id);
1113        }
1114    }
1115}