Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  frame_builder.rs   Sprache: unbekannt

 
/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */

use api::{ColorF, DebugFlags, ExternalScrollId, FontRenderMode, ImageKey, MinimapData, PremultipliedColorF};
use api::units::*;
use plane_split::BspSplitter;
use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
use crate::clip::{ClipStore, ClipTree};
use crate::command_buffer::{PrimitiveCommand, CommandBufferList, CommandBufferIndex};
use crate::debug_colors;
use crate::spatial_node::SpatialNodeType;
use crate::spatial_tree::{SpatialTree, SpatialNodeIndex};
use crate::composite::{CompositorKind, CompositeState, CompositeStatePreallocator};
use crate::debug_item::DebugItem;
use crate::gpu_cache::{GpuCache, GpuCacheHandle};
use crate::gpu_types::{PrimitiveHeaders, TransformPalette, ZBufferIdGenerator};
use crate::gpu_types::{QuadSegment, TransformData};
use crate::internal_types::{FastHashMap, PlaneSplitter, FrameId, FrameStamp};
use crate::picture::{DirtyRegion, SliceId, TileCacheInstance};
use crate::picture::{SurfaceInfo, SurfaceIndex};
use crate::picture::{SubpixelMode, RasterConfig, PictureCompositeMode};
use crate::prepare::prepare_picture;
use crate::prim_store::{PictureIndex, PrimitiveScratchBuffer};
use crate::prim_store::{DeferredResolve, PrimitiveInstance};
use crate::profiler::{self, TransactionProfile};
use crate::render_backend::{DataStores, ScratchBuffer};
use crate::renderer::{GpuBufferF, GpuBufferBuilderF, GpuBufferI, GpuBufferBuilderI, GpuBufferBuilder};
use crate::render_target::{PictureCacheTarget, PictureCacheTargetKind};
use crate::render_target::{RenderTargetContext, RenderTargetKind, RenderTarget};
use crate::render_task_graph::{Pass, RenderTaskGraph, RenderTaskId, SubPassSurface};
use crate::render_task_graph::{RenderPass, RenderTaskGraphBuilder};
use crate::render_task::{RenderTaskKind, StaticRenderTaskSurface};
use crate::resource_cache::ResourceCache;
use crate::scene::{BuiltScene, SceneProperties};
use crate::space::SpaceMapper;
use crate::segment::SegmentBuilder;
use crate::surface::SurfaceBuilder;
use std::{f32, mem};
use crate::util::{MaxRect, VecHelper, Preallocator};
use crate::visibility::{update_prim_visibility, FrameVisibilityState, FrameVisibilityContext};
use crate::internal_types::{FrameVec, FrameMemory};

#[derive(Clone, Copy, Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct FrameBuilderConfig {
    pub default_font_render_mode: FontRenderMode,
    pub dual_source_blending_is_supported: bool,
    /// True if we're running tests (i.e. via wrench).
    pub testing: bool,
    pub gpu_supports_fast_clears: bool,
    pub gpu_supports_advanced_blend: bool,
    pub advanced_blend_is_coherent: bool,
    pub gpu_supports_render_target_partial_update: bool,
    /// Whether ImageBufferKind::TextureExternal images must first be copied
    /// to a regular texture before rendering.
    pub external_images_require_copy: bool,
    pub batch_lookback_count: usize,
    pub background_color: Option<ColorF>,
    pub compositor_kind: CompositorKind,
    pub tile_size_override: Option<DeviceIntSize>,
    pub max_surface_override: Option<usize>,
    pub max_depth_ids: i32,
    pub max_target_size: i32,
    pub force_invalidation: bool,
    pub is_software: bool,
    pub low_quality_pinch_zoom: bool,
    pub max_shared_surface_size: i32,
}

/// A set of common / global resources that are retained between
/// new display lists, such that any GPU cache handles can be
/// persisted even when a new display list arrives.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct FrameGlobalResources {
    /// The image shader block for the most common / default
    /// set of image parameters (color white, stretch == rect.size).
    pub default_image_handle: GpuCacheHandle,

    /// A GPU cache config for drawing cut-out rectangle primitives.
    /// This is used to 'cut out' overlay tiles where a compositor
    /// surface exists.
    pub default_black_rect_handle: GpuCacheHandle,
}

impl FrameGlobalResources {
    pub fn empty() -> Self {
        FrameGlobalResources {
            default_image_handle: GpuCacheHandle::new(),
            default_black_rect_handle: GpuCacheHandle::new(),
        }
    }

    pub fn update(
        &mut self,
        gpu_cache: &mut GpuCache,
    ) {
        if let Some(mut request) = gpu_cache.request(&mut self.default_image_handle) {
            request.push(PremultipliedColorF::WHITE);
            request.push(PremultipliedColorF::WHITE);
            request.push([
                -1.0,       // -ve means use prim rect for stretch size
                0.0,
                0.0,
                0.0,
            ]);
        }

        if let Some(mut request) = gpu_cache.request(&mut self.default_black_rect_handle) {
            request.push(PremultipliedColorF::BLACK);
        }
    }
}

pub struct FrameScratchBuffer {
    dirty_region_stack: Vec<DirtyRegion>,
    surface_stack: Vec<(PictureIndex, SurfaceIndex)>,
}

impl Default for FrameScratchBuffer {
    fn default() -> Self {
        FrameScratchBuffer {
            dirty_region_stack: Vec::new(),
            surface_stack: Vec::new(),
        }
    }
}

impl FrameScratchBuffer {
    pub fn begin_frame(&mut self) {
        self.dirty_region_stack.clear();
        self.surface_stack.clear();
    }
}

/// Produces the frames that are sent to the renderer.
#[cfg_attr(feature = "capture", derive(Serialize))]
pub struct FrameBuilder {
    pub globals: FrameGlobalResources,
    #[cfg_attr(feature = "capture", serde(skip))]
    prim_headers_prealloc: Preallocator,
    #[cfg_attr(feature = "capture", serde(skip))]
    composite_state_prealloc: CompositeStatePreallocator,
    #[cfg_attr(feature = "capture", serde(skip))]
    plane_splitters: Vec<PlaneSplitter>,
}

pub struct FrameBuildingContext<'a> {
    pub global_device_pixel_scale: DevicePixelScale,
    pub scene_properties: &'a SceneProperties,
    pub global_screen_world_rect: WorldRect,
    pub spatial_tree: &'a SpatialTree,
    pub max_local_clip: LayoutRect,
    pub debug_flags: DebugFlags,
    pub fb_config: &'a FrameBuilderConfig,
    pub root_spatial_node_index: SpatialNodeIndex,
}

pub struct FrameBuildingState<'a> {
    pub rg_builder: &'a mut RenderTaskGraphBuilder,
    pub clip_store: &'a mut ClipStore,
    pub resource_cache: &'a mut ResourceCache,
    pub gpu_cache: &'a mut GpuCache,
    pub transforms: &'a mut TransformPalette,
    pub segment_builder: SegmentBuilder,
    pub surfaces: &'a mut Vec<SurfaceInfo>,
    pub dirty_region_stack: Vec<DirtyRegion>,
    pub composite_state: &'a mut CompositeState,
    pub num_visible_primitives: u32,
    pub plane_splitters: &'a mut [PlaneSplitter],
    pub surface_builder: SurfaceBuilder,
    pub cmd_buffers: &'a mut CommandBufferList,
    pub clip_tree: &'a ClipTree,
    pub frame_gpu_data: &'a mut GpuBufferBuilder,
    /// When using a render task to produce pixels that are associated with
    /// an image key (for example snapshotted pictures), inserting the image
    /// key / task id association in this hashmap allows the image item to
    /// register a dependency to the render task. This ensures that the
    /// render task is produced before the image that renders it if they
    /// are happening in the same frame.
    /// This mechanism relies on the item producing the render task to be
    /// traversed before the image that displays it (in other words, the
    /// picture must appear before the image in the display list).
    pub image_dependencies: FastHashMap<ImageKey, RenderTaskId>,
    pub visited_pictures: &'a mut [bool],
}

impl<'a> FrameBuildingState<'a> {
    /// Retrieve the current dirty region during primitive traversal.
    pub fn current_dirty_region(&self) -> &DirtyRegion {
        self.dirty_region_stack.last().unwrap()
    }

    /// Push a new dirty region for child primitives to cull / clip against.
    pub fn push_dirty_region(&mut self, region: DirtyRegion) {
        self.dirty_region_stack.push(region);
    }

    /// Pop the top dirty region from the stack.
    pub fn pop_dirty_region(&mut self) {
        self.dirty_region_stack.pop().unwrap();
    }

    /// Push a primitive command to a set of command buffers
    pub fn push_prim(
        &mut self,
        cmd: &PrimitiveCommand,
        spatial_node_index: SpatialNodeIndex,
        targets: &[CommandBufferIndex],
    ) {
        for cmd_buffer_index in targets {
            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
            cmd_buffer.add_prim(cmd, spatial_node_index);
        }
    }

    /// Push a command to a set of command buffers
    pub fn push_cmd(
        &mut self,
        cmd: &PrimitiveCommand,
        targets: &[CommandBufferIndex],
    ) {
        for cmd_buffer_index in targets {
            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
            cmd_buffer.add_cmd(cmd);
        }
    }

    /// Set the active list of segments in a set of command buffers
    pub fn set_segments(
        &mut self,
        segments: &[QuadSegment],
        targets: &[CommandBufferIndex],
    ) {
        for cmd_buffer_index in targets {
            let cmd_buffer = self.cmd_buffers.get_mut(*cmd_buffer_index);
            cmd_buffer.set_segments(segments);
        }
    }
}

/// Immutable context of a picture when processing children.
#[derive(Debug)]
pub struct PictureContext {
    pub pic_index: PictureIndex,
    pub surface_spatial_node_index: SpatialNodeIndex,
    pub raster_spatial_node_index: SpatialNodeIndex,
    /// The surface that this picture will render on.
    pub surface_index: SurfaceIndex,
    pub dirty_region_count: usize,
    pub subpixel_mode: SubpixelMode,
}

/// Mutable state of a picture that gets modified when
/// the children are processed.
pub struct PictureState {
    pub map_local_to_pic: SpaceMapper<LayoutPixel, PicturePixel>,
    pub map_pic_to_world: SpaceMapper<PicturePixel, WorldPixel>,
}

impl FrameBuilder {
    pub fn new() -> Self {
        FrameBuilder {
            globals: FrameGlobalResources::empty(),
            prim_headers_prealloc: Preallocator::new(0),
            composite_state_prealloc: CompositeStatePreallocator::default(),
            plane_splitters: Vec::new(),
        }
    }

    /// Compute the contribution (bounding rectangles, and resources) of layers and their
    /// primitives in screen space.
    fn build_layer_screen_rects_and_cull_layers(
        &mut self,
        scene: &mut BuiltScene,
        global_screen_world_rect: WorldRect,
        resource_cache: &mut ResourceCache,
        gpu_cache: &mut GpuCache,
        rg_builder: &mut RenderTaskGraphBuilder,
        global_device_pixel_scale: DevicePixelScale,
        scene_properties: &SceneProperties,
        transform_palette: &mut TransformPalette,
        data_stores: &mut DataStores,
        scratch: &mut ScratchBuffer,
        debug_flags: DebugFlags,
        composite_state: &mut CompositeState,
        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
        spatial_tree: &SpatialTree,
        cmd_buffers: &mut CommandBufferList,
        frame_gpu_data: &mut GpuBufferBuilder,
        frame_memory: &FrameMemory,
        profile: &mut TransactionProfile,
    ) {
        profile_scope!("build_layer_screen_rects_and_cull_layers");

        let root_spatial_node_index = spatial_tree.root_reference_frame_index();

        const MAX_CLIP_COORD: f32 = 1.0e9;

        // Reset all plane splitters. These are retained from frame to frame to reduce
        // per-frame allocations
        self.plane_splitters.resize_with(scene.num_plane_splitters, BspSplitter::new);
        for splitter in &mut self.plane_splitters {
            splitter.reset();
        }

        let frame_context = FrameBuildingContext {
            global_device_pixel_scale,
            scene_properties,
            global_screen_world_rect,
            spatial_tree,
            max_local_clip: LayoutRect {
                min: LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
                max: LayoutPoint::new(MAX_CLIP_COORD, MAX_CLIP_COORD),
            },
            debug_flags,
            fb_config: &scene.config,
            root_spatial_node_index,
        };

        scene.picture_graph.build_update_passes(
            &mut scene.prim_store.pictures,
            &frame_context,
        );

        scene.picture_graph.assign_surfaces(
            &mut scene.prim_store.pictures,
            &mut scene.surfaces,
            tile_caches,
            &frame_context,
        );

        // Add a "fake" surface that we will use as parent for
        // snapshotted pictures.
        let root_spatial_node = frame_context.spatial_tree.root_reference_frame_index();
        let snapshot_surface = SurfaceIndex(scene.surfaces.len());
        scene.surfaces.push(SurfaceInfo::new(
            root_spatial_node,
            root_spatial_node,
            WorldRect::max_rect(),
            &frame_context.spatial_tree,
            euclid::Scale::new(1.0),
            (1.0, 1.0),
            (1.0, 1.0),
            false,
            false,
        ));

        scene.picture_graph.propagate_bounding_rects(
            &mut scene.prim_store.pictures,
            &mut scene.surfaces,
            &frame_context,
        );

        // In order to handle picture snapshots consistently we need
        // the visibility and prepare passes to visit them first before
        // traversing the scene. This ensures that out-of-view snapshots
        // are rendered and that snapshots are consistently produced
        // relative to the root spatial node.
        // However it means that the visibility and prepare passes may
        // visit some pictures multiple times, so we keep track of visited
        // pictures during each traversal to avoid that.
        let n_pics = scene.prim_store.pictures.len();
        let mut visited_pictures = frame_memory.new_vec_with_capacity(n_pics);
        for _ in 0..n_pics {
            visited_pictures.push(false);
        }

        {
            profile_scope!("UpdateVisibility");
            profile_marker!("UpdateVisibility");
            profile.start_time(profiler::FRAME_VISIBILITY_TIME);

            let visibility_context = FrameVisibilityContext {
                global_device_pixel_scale,
                spatial_tree,
                global_screen_world_rect,
                debug_flags,
                scene_properties,
                config: scene.config,
                root_spatial_node_index,
            };

            for pic_index in scene.snapshot_pictures.iter() {
                let mut visibility_state = FrameVisibilityState {
                    clip_store: &mut scene.clip_store,
                    resource_cache,
                    gpu_cache,
                    data_stores,
                    clip_tree: &mut scene.clip_tree,
                    composite_state,
                    rg_builder,
                    prim_instances: &mut scene.prim_instances,
                    surfaces: &mut scene.surfaces,
                    surface_stack: scratch.frame.surface_stack.take(),
                    profile,
                    scratch,
                    visited_pictures: &mut visited_pictures,
                };

                let world_culling_rect = WorldRect::max_rect();

                // For now, snapshots are updated every frame. For the
                // pictures displaying the snapshot via images pick up
                // the changes, we have to make sure that the image's
                // generation counter is incremented early in the frame,
                // before the main visibility pass visits the image items.
                let snapshot = scene.prim_store
                    .pictures[pic_index.0]
                    .snapshot
                    .unwrap();
                let key = snapshot.key.as_image();
                visibility_state.resource_cache
                    .increment_image_generation(key);

                update_prim_visibility(
                    *pic_index,
                    None,
                    &world_culling_rect,
                    &scene.prim_store,
                    true,
                    &visibility_context,
                    &mut visibility_state,
                    &mut None,
                );
            }

            for pic_index in scene.tile_cache_pictures.iter().rev() {
                let pic = &mut scene.prim_store.pictures[pic_index.0];

                match pic.raster_config {
                    Some(RasterConfig { surface_index, composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
                        let tile_cache = tile_caches
                            .get_mut(&slice_id)
                            .expect("bug: non-existent tile cache");

                        let mut visibility_state = FrameVisibilityState {
                            clip_store: &mut scene.clip_store,
                            resource_cache,
                            gpu_cache,
                            data_stores,
                            clip_tree: &mut scene.clip_tree,
                            composite_state,
                            rg_builder,
                            prim_instances: &mut scene.prim_instances,
                            surfaces: &mut scene.surfaces,
                            surface_stack: scratch.frame.surface_stack.take(),
                            profile,
                            scratch,
                            visited_pictures: &mut visited_pictures,
                        };

                        // If we have a tile cache for this picture, see if any of the
                        // relative transforms have changed, which means we need to
                        // re-map the dependencies of any child primitives.
                        let world_culling_rect = tile_cache.pre_update(
                            surface_index,
                            &visibility_context,
                            &mut visibility_state,
                        );

                        // Push a new surface, supplying the list of clips that should be
                        // ignored, since they are handled by clipping when drawing this surface.
                        visibility_state.push_surface(
                            *pic_index,
                            surface_index,
                        );
                        visibility_state.clip_tree.push_clip_root_node(tile_cache.shared_clip_node_id);

                        update_prim_visibility(
                            *pic_index,
                            None,
                            &world_culling_rect,
                            &scene.prim_store,
                            true,
                            &visibility_context,
                            &mut visibility_state,
                            &mut Some(tile_cache),
                        );

                        // Build the dirty region(s) for this tile cache.
                        tile_cache.post_update(
                            &visibility_context,
                            &mut visibility_state.composite_state,
                            &mut visibility_state.resource_cache,
                        );

                        visibility_state.clip_tree.pop_clip_root();
                        visibility_state.pop_surface();
                        visibility_state.scratch.frame.surface_stack = visibility_state.surface_stack.take();
                    }
                    _ => {
                        panic!("bug: not a tile cache");
                    }
                }
            }

            profile.end_time(profiler::FRAME_VISIBILITY_TIME);
        }

        profile.start_time(profiler::FRAME_PREPARE_TIME);

        // Reset the visited pictures for the prepare pass.
        visited_pictures.clear();
        for _ in 0..n_pics {
            visited_pictures.push(false);
        }
        let mut frame_state = FrameBuildingState {
            rg_builder,
            clip_store: &mut scene.clip_store,
            resource_cache,
            gpu_cache,
            transforms: transform_palette,
            segment_builder: SegmentBuilder::new(),
            surfaces: &mut scene.surfaces,
            dirty_region_stack: scratch.frame.dirty_region_stack.take(),
            composite_state,
            num_visible_primitives: 0,
            plane_splitters: &mut self.plane_splitters,
            surface_builder: SurfaceBuilder::new(),
            cmd_buffers,
            clip_tree: &mut scene.clip_tree,
            frame_gpu_data,
            image_dependencies: FastHashMap::default(),
            visited_pictures: &mut visited_pictures,
        };


        if !scene.snapshot_pictures.is_empty() {
            // Push a default dirty region which does not cull any
            // primitive.
            let mut default_dirty_region = DirtyRegion::new(
                root_spatial_node_index,
            );
            default_dirty_region.add_dirty_region(
                PictureRect::max_rect(),
                frame_context.spatial_tree,
            );
            frame_state.push_dirty_region(default_dirty_region);

            frame_state.surface_builder.push_surface(
                snapshot_surface,
                false,
                PictureRect::max_rect(),
                None,
                frame_state.surfaces,
                frame_state.rg_builder,
            );
        }

        for pic_index in &scene.snapshot_pictures {

            prepare_picture(
                *pic_index,
                &mut scene.prim_store,
                Some(snapshot_surface),
                SubpixelMode::Allow,
                &frame_context,
                &mut frame_state,
                data_stores,
                &mut scratch.primitive,
                tile_caches,
                &mut scene.prim_instances
            );
        }

        if !scene.snapshot_pictures.is_empty() {
            frame_state.surface_builder.pop_empty_surface();
            frame_state.pop_dirty_region();
        }

        // Push a default dirty region which culls primitives
        // against the screen world rect, in absence of any
        // other dirty regions.
        let mut default_dirty_region = DirtyRegion::new(
            root_spatial_node_index,
        );
        default_dirty_region.add_dirty_region(
            frame_context.global_screen_world_rect.cast_unit(),
            frame_context.spatial_tree,
        );
        frame_state.push_dirty_region(default_dirty_region);

        for pic_index in &scene.tile_cache_pictures {
            prepare_picture(
                *pic_index,
                &mut scene.prim_store,
                None,
                SubpixelMode::Allow,
                &frame_context,
                &mut frame_state,
                data_stores,
                &mut scratch.primitive,
                tile_caches,
                &mut scene.prim_instances
            );
        }

        frame_state.pop_dirty_region();
        frame_state.surface_builder.finalize();
        profile.end_time(profiler::FRAME_PREPARE_TIME);
        profile.set(profiler::VISIBLE_PRIMITIVES, frame_state.num_visible_primitives);

        scratch.frame.dirty_region_stack = frame_state.dirty_region_stack.take();

        {
            profile_marker!("BlockOnResources");

            resource_cache.block_until_all_resources_added(
                gpu_cache,
                profile,
            );
        }
    }

    pub fn build(
        &mut self,
        scene: &mut BuiltScene,
        resource_cache: &mut ResourceCache,
        gpu_cache: &mut GpuCache,
        rg_builder: &mut RenderTaskGraphBuilder,
        stamp: FrameStamp,
        device_origin: DeviceIntPoint,
        scene_properties: &SceneProperties,
        data_stores: &mut DataStores,
        scratch: &mut ScratchBuffer,
        debug_flags: DebugFlags,
        tile_caches: &mut FastHashMap<SliceId, Box<TileCacheInstance>>,
        spatial_tree: &mut SpatialTree,
        dirty_rects_are_valid: bool,
        profile: &mut TransactionProfile,
        minimap_data: FastHashMap<ExternalScrollId, MinimapData>,
        mut frame_memory: FrameMemory,
    ) -> Frame {
        profile_scope!("build");
        profile_marker!("BuildFrame");

        frame_memory.begin_frame(stamp.frame_id());

        profile.set(profiler::PRIMITIVES, scene.prim_instances.len());
        profile.set(profiler::PICTURE_CACHE_SLICES, scene.tile_cache_config.picture_cache_slice_count);
        scratch.begin_frame();
        gpu_cache.begin_frame(stamp);
        resource_cache.begin_frame(stamp, gpu_cache, profile);

        // TODO(gw): Follow up patches won't clear this, as they'll be assigned
        //           statically during scene building.
        scene.surfaces.clear();

        self.globals.update(gpu_cache);

        spatial_tree.update_tree(scene_properties);
        let mut transform_palette = spatial_tree.build_transform_palette(&frame_memory);
        scene.clip_store.begin_frame(&mut scratch.clip_store);

        rg_builder.begin_frame(stamp.frame_id());

        // TODO(dp): Remove me completely!!
        let global_device_pixel_scale = DevicePixelScale::new(1.0);

        let output_size = scene.output_rect.size();
        let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();

        let mut composite_state = CompositeState::new(
            scene.config.compositor_kind,
            scene.config.max_depth_ids,
            dirty_rects_are_valid,
            scene.config.low_quality_pinch_zoom,
            &frame_memory,
        );

        self.composite_state_prealloc.preallocate(&mut composite_state);

        let mut cmd_buffers = CommandBufferList::new();

        // TODO(gw): Recycle backing vec buffers for gpu buffer builder between frames
        let mut gpu_buffer_builder = GpuBufferBuilder {
            f32: GpuBufferBuilderF::new(&frame_memory),
            i32: GpuBufferBuilderI::new(&frame_memory),
        };

        self.build_layer_screen_rects_and_cull_layers(
            scene,
            screen_world_rect,
            resource_cache,
            gpu_cache,
            rg_builder,
            global_device_pixel_scale,
            scene_properties,
            &mut transform_palette,
            data_stores,
            scratch,
            debug_flags,
            &mut composite_state,
            tile_caches,
            spatial_tree,
            &mut cmd_buffers,
            &mut gpu_buffer_builder,
            &frame_memory,
            profile,
        );

        self.render_minimap(&mut scratch.primitive, &spatial_tree, minimap_data);

        profile.start_time(profiler::FRAME_BATCHING_TIME);

        let mut deferred_resolves = frame_memory.new_vec();

        // Finish creating the frame graph and build it.
        let render_tasks = rg_builder.end_frame(
            resource_cache,
            gpu_cache,
            &mut deferred_resolves,
            scene.config.max_shared_surface_size,
            &frame_memory,
        );

        let mut passes = frame_memory.new_vec();
        let mut has_texture_cache_tasks = false;
        let mut prim_headers = PrimitiveHeaders::new(&frame_memory);
        self.prim_headers_prealloc.preallocate_framevec(&mut prim_headers.headers_int);
        self.prim_headers_prealloc.preallocate_framevec(&mut prim_headers.headers_float);

        {
            profile_marker!("Batching");

            // Used to generated a unique z-buffer value per primitive.
            let mut z_generator = ZBufferIdGenerator::new(scene.config.max_depth_ids);
            let use_dual_source_blending = scene.config.dual_source_blending_is_supported;

            for pass in render_tasks.passes.iter().rev() {
                let mut ctx = RenderTargetContext {
                    global_device_pixel_scale,
                    prim_store: &scene.prim_store,
                    clip_store: &scene.clip_store,
                    resource_cache,
                    use_dual_source_blending,
                    use_advanced_blending: scene.config.gpu_supports_advanced_blend,
                    break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
                    batch_lookback_count: scene.config.batch_lookback_count,
                    spatial_tree,
                    data_stores,
                    surfaces: &scene.surfaces,
                    scratch: &mut scratch.primitive,
                    screen_world_rect,
                    globals: &self.globals,
                    tile_caches,
                    root_spatial_node_index: spatial_tree.root_reference_frame_index(),
                    frame_memory: &mut frame_memory,
                };

                let pass = build_render_pass(
                    pass,
                    output_size,
                    &mut ctx,
                    gpu_cache,
                    &mut gpu_buffer_builder,
                    &render_tasks,
                    &scene.clip_store,
                    &mut transform_palette,
                    &mut prim_headers,
                    &mut z_generator,
                    scene.config.gpu_supports_fast_clears,
                    &scene.prim_instances,
                    &cmd_buffers,
                );

                has_texture_cache_tasks |= !pass.texture_cache.is_empty();
                has_texture_cache_tasks |= !pass.picture_cache.is_empty();

                passes.push(pass);
            }

            let mut ctx = RenderTargetContext {
                global_device_pixel_scale,
                clip_store: &scene.clip_store,
                prim_store: &scene.prim_store,
                resource_cache,
                use_dual_source_blending,
                use_advanced_blending: scene.config.gpu_supports_advanced_blend,
                break_advanced_blend_batches: !scene.config.advanced_blend_is_coherent,
                batch_lookback_count: scene.config.batch_lookback_count,
                spatial_tree,
                data_stores,
                surfaces: &scene.surfaces,
                scratch: &mut scratch.primitive,
                screen_world_rect,
                globals: &self.globals,
                tile_caches,
                root_spatial_node_index: spatial_tree.root_reference_frame_index(),
                frame_memory: &mut frame_memory,
            };

            self.build_composite_pass(
                scene,
                &mut ctx,
                gpu_cache,
                &mut deferred_resolves,
                &mut composite_state,
            );
        }

        profile.end_time(profiler::FRAME_BATCHING_TIME);

        let gpu_cache_frame_id = gpu_cache.end_frame(profile).frame_id();

        resource_cache.end_frame(profile);

        self.prim_headers_prealloc.record_vec(&prim_headers.headers_int);
        self.composite_state_prealloc.record(&composite_state);

        composite_state.end_frame();
        scene.clip_store.end_frame(&mut scratch.clip_store);
        scratch.end_frame();

        let gpu_buffer_f = gpu_buffer_builder.f32.finalize(&render_tasks);
        let gpu_buffer_i = gpu_buffer_builder.i32.finalize(&render_tasks);

        Frame {
            device_rect: DeviceIntRect::from_origin_and_size(
                device_origin,
                scene.output_rect.size(),
            ),
            passes,
            transform_palette: transform_palette.finish(),
            render_tasks,
            deferred_resolves,
            gpu_cache_frame_id,
            has_been_rendered: false,
            has_texture_cache_tasks,
            prim_headers,
            debug_items: mem::replace(&mut scratch.primitive.debug_items, Vec::new()),
            composite_state,
            gpu_buffer_f,
            gpu_buffer_i,
            allocator_memory: frame_memory,
        }
    }

    fn render_minimap(
        &self,
        scratch: &mut PrimitiveScratchBuffer,
        spatial_tree: &SpatialTree,
        minimap_data_store: FastHashMap<ExternalScrollId, MinimapData>) {
      // TODO: Replace minimap_data_store with Option<FastHastMap>?
      if minimap_data_store.is_empty() {
        return
      }

      // In our main walk over the spatial tree (below), for nodes inside a
      // subtree rooted at a root-content node, we need some information from
      // that enclosing root-content node. To collect this information, do an
      // preliminary walk over the spatial tree now and collect the root-content
      // info in a HashMap.
      struct RootContentInfo {
        transform: LayoutToWorldTransform,
        clip: LayoutRect
      }
      let mut root_content_info = FastHashMap::<ExternalScrollId, RootContentInfo>::default();
      spatial_tree.visit_nodes(|index, node| {
        if let SpatialNodeType::ScrollFrame(ref scroll_frame_info) = node.node_type {
          if let Some(minimap_data) = minimap_data_store.get(&scroll_frame_info.external_id) {
            if minimap_data.is_root_content {
              let transform = spatial_tree.get_world_viewport_transform(index).into_transform();
              root_content_info.insert(scroll_frame_info.external_id, RootContentInfo{
                transform,
                clip: scroll_frame_info.viewport_rect
              });
            }
          }
        }
      });

      // This is the main walk over the spatial tree. For every scroll frame node which
      // has minimap data, compute the rects we want to render for that minimap in world
      // coordinates and add them to `scratch.debug_items`.
      spatial_tree.visit_nodes(|index, node| {
        if let SpatialNodeType::ScrollFrame(ref scroll_frame_info) = node.node_type {
          if let Some(minimap_data) = minimap_data_store.get(&scroll_frame_info.external_id) {
            const HORIZONTAL_PADDING: f32 = 5.0;
            const VERTICAL_PADDING: f32 = 10.0;
            const PAGE_BORDER_COLOR: ColorF = debug_colors::BLACK;
            const BACKGROUND_COLOR: ColorF = ColorF { r: 0.3, g: 0.3, b: 0.3, a: 0.3};
            const DISPLAYPORT_BACKGROUND_COLOR: ColorF = ColorF { r: 1.0, g: 1.0, b: 1.0, a: 0.4};
            const LAYOUT_PORT_COLOR: ColorF = debug_colors::RED;
            const VISUAL_PORT_COLOR: ColorF = debug_colors::BLUE;
            const DISPLAYPORT_COLOR: ColorF = debug_colors::LIME;

            let viewport = scroll_frame_info.viewport_rect;

            // Scale the minimap to make it 100px wide (if there's space), and the full height
            // of the scroll frame's viewport, minus some padding. Position it at the left edge
            // of the scroll frame's viewport.
            let scale_factor_x = 100f32.min(viewport.width() - (2.0 * HORIZONTAL_PADDING))
                                   / minimap_data.scrollable_rect.width();
            let scale_factor_y = (viewport.height() - (2.0 * VERTICAL_PADDING))
                                / minimap_data.scrollable_rect.height();
            if scale_factor_x <= 0.0 || scale_factor_y <= 0.0 {
              return;
            }
            let transform = LayoutTransform::scale(scale_factor_x, scale_factor_y, 1.0)
                .then_translate(LayoutVector3D::new(HORIZONTAL_PADDING, VERTICAL_PADDING, 0.0))
                .then_translate(LayoutVector3D::new(viewport.min.x, viewport.min.y, 0.0));

            // Transforms for transforming rects in this scroll frame's local coordintes, to world coordinates.
            // For scroll frames inside a root-content subtree, we apply this transform in two parts
            // (local to root-content, and root-content to world), so that we can make additional
            // adjustments in root-content space. For scroll frames outside of a root-content subtree,
            // the entire world transform will be in `local_to_root_content`.
            let world_transform = spatial_tree
                .get_world_viewport_transform(index)
                .into_transform();
            let mut local_to_root_content =
                world_transform.with_destination::<LayoutPixel>();
            let mut root_content_to_world = LayoutToWorldTransform::default();
            let mut root_content_clip = None;
            if minimap_data.root_content_scroll_id != 0 {
              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)) {
                // Exclude the root-content node's zoom transform from `local_to_root_content`.
                // This ensures that the minimap remains unaffected by pinch-zooming
                // (in essence, remaining attached to the *visual* viewport, rather than to
                // the *layout* viewport which is what happens by default).
                let zoom_transform = minimap_data.zoom_transform;
                local_to_root_content = world_transform
                  .then(&root_content_transform.inverse().unwrap())
                  .then(&zoom_transform.inverse().unwrap());
                root_content_to_world = root_content_transform.clone();
                root_content_clip = Some(clip);
              }
            }

            let mut add_rect = |rect, border, fill| -> Option<()> {
              const STROKE_WIDTH: f32 = 2.0;
              // Place rect in scroll frame's local coordinate space
              let transformed_rect = transform.outer_transformed_box2d(&rect)?;

              // Transform to world coordinates, using root-content coords as an intermediate step.
              let mut root_content_rect = local_to_root_content.outer_transformed_box2d(&transformed_rect)?;
              // In root-content coords, apply the root content node's viewport clip.
              // This prevents subframe minimaps from leaking into the chrome area when the root
              // scroll frame is scrolled.
              // TODO: The minimaps of nested subframes can still leak outside of the viewports of
              // their containing subframes. Should have a more proper fix for this.
              if let Some(clip) = root_content_clip {
                root_content_rect = root_content_rect.intersection(clip)?;
              }
              let world_rect = root_content_to_world.outer_transformed_box2d(&root_content_rect)?;

              scratch.push_debug_rect_with_stroke_width(world_rect, border, STROKE_WIDTH);

              // Add world coordinate rects to scratch.debug_items
              if let Some(fill_color) = fill {
                let interior_world_rect = WorldRect::new(
                    world_rect.min + WorldVector2D::new(STROKE_WIDTH, STROKE_WIDTH),
                    world_rect.max - WorldVector2D::new(STROKE_WIDTH, STROKE_WIDTH)
                );
                scratch.push_debug_rect(interior_world_rect * DevicePixelScale::new(1.0), 1, border, fill_color);
              }

              Some(())
            };

            add_rect(minimap_data.scrollable_rect, PAGE_BORDER_COLOR, Some(BACKGROUND_COLOR));
            add_rect(minimap_data.displayport, DISPLAYPORT_COLOR, Some(DISPLAYPORT_BACKGROUND_COLOR));
            // Only render a distinct layout viewport for the root content.
            // For other scroll frames, the visual and layout viewports coincide.
            if minimap_data.is_root_content {
              add_rect(minimap_data.layout_viewport, LAYOUT_PORT_COLOR, None);
            }
            add_rect(minimap_data.visual_viewport, VISUAL_PORT_COLOR, None);
          }
        }
      });
    }

    fn build_composite_pass(
        &self,
        scene: &BuiltScene,
        ctx: &RenderTargetContext,
        gpu_cache: &mut GpuCache,
        deferred_resolves: &mut FrameVec<DeferredResolve>,
        composite_state: &mut CompositeState,
    ) {
        for pic_index in &scene.tile_cache_pictures {
            let pic = &ctx.prim_store.pictures[pic_index.0];

            match pic.raster_config {
                Some(RasterConfig { composite_mode: PictureCompositeMode::TileCache { slice_id }, .. }) => {
                    // Tile cache instances are added to the composite config, rather than
                    // directly added to batches. This allows them to be drawn with various
                    // present modes during render, such as partial present etc.
                    let tile_cache = &ctx.tile_caches[&slice_id];
                    let map_local_to_world = SpaceMapper::new_with_target(
                        ctx.root_spatial_node_index,
                        tile_cache.spatial_node_index,
                        ctx.screen_world_rect,
                        ctx.spatial_tree,
                    );
                    let world_clip_rect = map_local_to_world
                        .map(&tile_cache.local_clip_rect)
                        .expect("bug: unable to map clip rect");
                    let device_clip_rect = (world_clip_rect * ctx.global_device_pixel_scale).round();

                    composite_state.push_surface(
                        tile_cache,
                        device_clip_rect,
                        ctx.resource_cache,
                        gpu_cache,
                        deferred_resolves,
                    );
                }
                _ => {
                    panic!("bug: found a top-level prim that isn't a tile cache");
                }
            }
        }
    }
}

/// Processes this pass to prepare it for rendering.
///
/// Among other things, this allocates output regions for each of our tasks
/// (added via `add_render_task`) in a RenderTarget and assigns it into that
/// target.
pub fn build_render_pass(
    src_pass: &Pass,
    screen_size: DeviceIntSize,
    ctx: &mut RenderTargetContext,
    gpu_cache: &mut GpuCache,
    gpu_buffer_builder: &mut GpuBufferBuilder,
    render_tasks: &RenderTaskGraph,
    clip_store: &ClipStore,
    transforms: &mut TransformPalette,
    prim_headers: &mut PrimitiveHeaders,
    z_generator: &mut ZBufferIdGenerator,
    gpu_supports_fast_clears: bool,
    prim_instances: &[PrimitiveInstance],
    cmd_buffers: &CommandBufferList,
) -> RenderPass {
    profile_scope!("build_render_pass");

    // TODO(gw): In this initial frame graph work, we try to maintain the existing
    //           build_render_pass code as closely as possible, to make the review
    //           simpler and reduce chance of regressions. However, future work should
    //           include refactoring this to more closely match the built frame graph.
    let mut pass = RenderPass::new(src_pass, ctx.frame_memory);

    for sub_pass in &src_pass.sub_passes {
        match sub_pass.surface {
            SubPassSurface::Dynamic { target_kind, texture_id, used_rect } => {
                match target_kind {
                    RenderTargetKind::Color => {
                        let mut target = RenderTarget::new(
                            RenderTargetKind::Color,
                            false,
                            texture_id,
                            screen_size,
                            gpu_supports_fast_clears,
                            Some(used_rect),
                            &ctx.frame_memory,
                        );

                        for task_id in &sub_pass.task_ids {
                            target.add_task(
                                *task_id,
                                ctx,
                                gpu_cache,
                                gpu_buffer_builder,
                                render_tasks,
                                clip_store,
                                transforms,
                            );
                        }

                        pass.color.targets.push(target);
                    }
                    RenderTargetKind::Alpha => {
                        let mut target = RenderTarget::new(
                            RenderTargetKind::Alpha,
                            false,
                            texture_id,
                            screen_size,
                            gpu_supports_fast_clears,
                            Some(used_rect),
                            &ctx.frame_memory,
                        );

                        for task_id in &sub_pass.task_ids {
                            target.add_task(
                                *task_id,
                                ctx,
                                gpu_cache,
                                gpu_buffer_builder,
                                render_tasks,
                                clip_store,
                                transforms,
                            );
                        }

                        pass.alpha.targets.push(target);
                    }
                }
            }
            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::PictureCache { ref surface, .. }, .. } => {
                assert_eq!(sub_pass.task_ids.len(), 1);
                let task_id = sub_pass.task_ids[0];
                let task = &render_tasks[task_id];
                let target_rect = task.get_target_rect();

                match task.kind {
                    RenderTaskKind::Picture(ref pic_task) => {
                        let cmd_buffer = cmd_buffers.get(pic_task.cmd_buffer_index);
                        let scissor_rect = pic_task.scissor_rect.expect("bug: must be set for cache tasks");
                        let valid_rect = pic_task.valid_rect.expect("bug: must be set for cache tasks");

                        let batcher = AlphaBatchBuilder::new(
                            screen_size,
                            ctx.break_advanced_blend_batches,
                            ctx.batch_lookback_count,
                            task_id,
                            task_id.into(),
                            &ctx.frame_memory,
                        );

                        let mut batch_builder = BatchBuilder::new(batcher);

                        cmd_buffer.iter_prims(&mut |cmd, spatial_node_index, segments| {
                            batch_builder.add_prim_to_batch(
                                cmd,
                                spatial_node_index,
                                ctx,
                                gpu_cache,
                                render_tasks,
                                prim_headers,
                                transforms,
                                pic_task.raster_spatial_node_index,
                                pic_task.surface_spatial_node_index,
                                z_generator,
                                prim_instances,
                                gpu_buffer_builder,
                                segments,
                            );
                        });

                        let batcher = batch_builder.finalize();

                        let mut batch_containers = ctx.frame_memory.new_vec();
                        let mut alpha_batch_container = AlphaBatchContainer::new(
                            Some(scissor_rect),
                            &ctx.frame_memory
                        );

                        batcher.build(
                            &mut batch_containers,
                            &mut alpha_batch_container,
                            target_rect,
                            None,
                        );
                        debug_assert!(batch_containers.is_empty());

                        let target = PictureCacheTarget {
                            surface: surface.clone(),
                            clear_color: pic_task.clear_color,
                            kind: PictureCacheTargetKind::Draw {
                                alpha_batch_container,
                            },
                            dirty_rect: scissor_rect,
                            valid_rect,
                        };

                        pass.picture_cache.push(target);
                    }
                    RenderTaskKind::TileComposite(ref tile_task) => {
                        let target = PictureCacheTarget {
                            surface: surface.clone(),
                            clear_color: Some(tile_task.clear_color),
                            kind: PictureCacheTargetKind::Blit {
                                task_id: tile_task.task_id.expect("bug: no source task_id set"),
                                sub_rect_offset: tile_task.sub_rect_offset,
                            },
                            dirty_rect: tile_task.scissor_rect,
                            valid_rect: tile_task.valid_rect,
                        };

                        pass.picture_cache.push(target);
                    }
                    _ => {
                        unreachable!();
                    }
                };
            }
            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::TextureCache { target_kind, texture, .. } } => {
                let texture = pass.texture_cache
                    .entry(texture)
                    .or_insert_with(||
                        RenderTarget::new(
                            target_kind,
                            true,
                            texture,
                            screen_size,
                            gpu_supports_fast_clears,
                            None,
                            &ctx.frame_memory
                        )
                    );
                for task_id in &sub_pass.task_ids {
                    texture.add_task(
                        *task_id,
                        ctx,
                        gpu_cache,
                        gpu_buffer_builder,
                        render_tasks,
                        clip_store,
                        transforms,
                    );
                }
            }
            SubPassSurface::Persistent { surface: StaticRenderTaskSurface::ReadOnly { .. } } => {
                panic!("Should not create a render pass for read-only task locations.");
            }
        }
    }

    pass.color.build(
        ctx,
        gpu_cache,
        render_tasks,
        prim_headers,
        transforms,
        z_generator,
        prim_instances,
        cmd_buffers,
        gpu_buffer_builder,
    );
    pass.alpha.build(
        ctx,
        gpu_cache,
        render_tasks,
        prim_headers,
        transforms,
        z_generator,
        prim_instances,
        cmd_buffers,
        gpu_buffer_builder,
    );

    for target in &mut pass.texture_cache.values_mut() {
        target.build(
            ctx,
            gpu_cache,
            render_tasks,
            prim_headers,
            transforms,
            z_generator,
            prim_instances,
            cmd_buffers,
            gpu_buffer_builder,
        );
    }

    pass
}

/// A rendering-oriented representation of the frame built by the render backend
/// and presented to the renderer.
///
/// # Safety
///
/// The frame's allocator memory must be dropped after all of the frame's containers.
/// This is handled in the renderer and in `RenderedDocument`'s Drop implementation.
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct Frame {
    /// The rectangle to show the frame in, on screen.
    pub device_rect: DeviceIntRect,
    pub passes: FrameVec<RenderPass>,

    pub transform_palette: FrameVec<TransformData>,
    pub render_tasks: RenderTaskGraph,
    pub prim_headers: PrimitiveHeaders,

    /// The GPU cache frame that the contents of Self depend on
    pub gpu_cache_frame_id: FrameId,

    /// List of textures that we don't know about yet
    /// from the backend thread. The render thread
    /// will use a callback to resolve these and
    /// patch the data structures.
    pub deferred_resolves: FrameVec<DeferredResolve>,

    /// True if this frame contains any render tasks
    /// that write to the texture cache.
    pub has_texture_cache_tasks: bool,

    /// True if this frame has been drawn by the
    /// renderer.
    pub has_been_rendered: bool,

    /// Debugging information to overlay for this frame.
    pub debug_items: Vec<DebugItem>,

    /// Contains picture cache tiles, and associated information.
    /// Used by the renderer to composite tiles into the framebuffer,
    /// or hand them off to an OS compositor.
    pub composite_state: CompositeState,

    /// Main GPU data buffer constructed (primarily) during the prepare
    /// pass for primitives that were visible and dirty.
    pub gpu_buffer_f: GpuBufferF,
    pub gpu_buffer_i: GpuBufferI,

    /// The backing store for the frame's allocator.
    ///
    /// # Safety
    ///
    /// Must not be dropped while frame allocations are alive.
    ///
    /// Rust has deterministic drop order [1]. We rely on `allocator_memory`
    /// being the last member of the `Frame` struct so that it is dropped
    /// after the frame's containers.
    ///
    /// [1]: https://doc.rust-lang.org/reference/destructors.html
    pub allocator_memory: FrameMemory,
}

impl Frame {
    // This frame must be flushed if it writes to the
    // texture cache, and hasn't been drawn yet.
    pub fn must_be_drawn(&self) -> bool {
        self.has_texture_cache_tasks && !self.has_been_rendered
    }

    // Returns true if this frame doesn't alter what is on screen currently.
    pub fn is_nop(&self) -> bool {
        // If there are no off-screen passes, that implies that there are no
        // picture cache tiles, and no texture cache tasks being updates. If this
        // is the case, we can consider the frame a nop (higher level checks
        // test if a composite is needed due to picture cache surfaces moving
        // or external surfaces being updated).
        self.passes.is_empty()
    }
}

[ Dauer der Verarbeitung: 0.47 Sekunden  (vorverarbeitet)  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge