зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1581058 - Rename a lot of scene building related structures. r=gw
Differential Revision: https://phabricator.services.mozilla.com/D45853 --HG-- rename : gfx/wr/webrender/src/display_list_flattener.rs => gfx/wr/webrender/src/scene_building.rs extra : moz-landing-system : lando
This commit is contained in:
Родитель
823e2e6a75
Коммит
3a51eac38e
|
@ -7,7 +7,7 @@ use api::{NormalBorder as ApiNormalBorder, RepeatMode};
|
|||
use api::units::*;
|
||||
use crate::ellipse::Ellipse;
|
||||
use euclid::vec2;
|
||||
use crate::display_list_flattener::DisplayListFlattener;
|
||||
use crate::scene_building::SceneBuilder;
|
||||
use crate::gpu_types::{BorderInstance, BorderSegment, BrushFlags};
|
||||
use crate::prim_store::{BorderSegmentInfo, BrushSegment, NinePatchDescriptor};
|
||||
use crate::prim_store::{EdgeAaSegmentMask, ScrollNodeAndClipChain};
|
||||
|
@ -208,7 +208,7 @@ pub fn ensure_no_corner_overlap(
|
|||
}
|
||||
}
|
||||
|
||||
impl<'a> DisplayListFlattener<'a> {
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
pub fn add_normal_border(
|
||||
&mut self,
|
||||
info: &LayoutPrimitiveInfo,
|
||||
|
|
|
@ -6,7 +6,7 @@ use api::{BorderRadius, BoxShadowClipMode, ClipMode, ColorF, PrimitiveKeyKind};
|
|||
use api::MAX_BLUR_RADIUS;
|
||||
use api::units::*;
|
||||
use crate::clip::{ClipItemKey, ClipItemKeyKind};
|
||||
use crate::display_list_flattener::DisplayListFlattener;
|
||||
use crate::scene_building::SceneBuilder;
|
||||
use crate::gpu_cache::GpuCacheHandle;
|
||||
use crate::gpu_types::BoxShadowStretchMode;
|
||||
use crate::prim_store::ScrollNodeAndClipChain;
|
||||
|
@ -68,7 +68,7 @@ pub struct BoxShadowCacheKey {
|
|||
pub br_bottom_left: DeviceIntSize,
|
||||
}
|
||||
|
||||
impl<'a> DisplayListFlattener<'a> {
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
pub fn add_box_shadow(
|
||||
&mut self,
|
||||
clip_and_scroll: ScrollNodeAndClipChain,
|
||||
|
|
|
@ -6,23 +6,17 @@ use api::{ColorF, DebugFlags, DocumentLayer, FontRenderMode, PremultipliedColorF
|
|||
use api::{PipelineId};
|
||||
use api::units::*;
|
||||
use crate::batch::{BatchBuilder, AlphaBatchBuilder, AlphaBatchContainer};
|
||||
use crate::clip::{ClipDataStore, ClipStore, ClipChainStack};
|
||||
use crate::clip::{ClipStore, ClipChainStack};
|
||||
use crate::clip_scroll_tree::{ClipScrollTree, ROOT_SPATIAL_NODE_INDEX, SpatialNodeIndex};
|
||||
use crate::debug_render::DebugItem;
|
||||
use crate::display_list_flattener::{DisplayListFlattener};
|
||||
use crate::gpu_cache::{GpuCache, GpuCacheHandle};
|
||||
use crate::gpu_types::{PrimitiveHeaders, TransformPalette, UvRectKind, ZBufferIdGenerator};
|
||||
use crate::gpu_types::TransformData;
|
||||
use crate::hit_test::{HitTester, HitTestingScene};
|
||||
#[cfg(feature = "replay")]
|
||||
use crate::hit_test::HitTestingSceneStats;
|
||||
use crate::internal_types::{FastHashMap, PlaneSplitter, SavedTargetIndex};
|
||||
use crate::picture::{PictureUpdateState, SurfaceInfo, ROOT_SURFACE_INDEX, SurfaceIndex, RecordedDirtyRegion};
|
||||
use crate::picture::{RetainedTiles, TileCacheInstance, DirtyRegion, SurfaceRenderTasks, SubpixelMode};
|
||||
use crate::prim_store::{PrimitiveStore, SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
|
||||
use crate::prim_store::{SpaceMapper, PictureIndex, PrimitiveDebugId, PrimitiveScratchBuffer};
|
||||
use crate::prim_store::{DeferredResolve, PrimitiveVisibilityMask};
|
||||
#[cfg(feature = "replay")]
|
||||
use crate::prim_store::{PrimitiveStoreStats};
|
||||
use crate::profiler::{FrameProfileCounters, GpuCacheProfileCounters, TextureCacheProfileCounters};
|
||||
use crate::render_backend::{DataStores, FrameStamp, FrameId};
|
||||
use crate::render_target::{RenderTarget, PictureCacheTarget, TextureCacheRenderTarget};
|
||||
|
@ -31,8 +25,7 @@ use crate::render_task_graph::{RenderTaskId, RenderTaskGraph, RenderTaskGraphCou
|
|||
use crate::render_task_graph::{RenderPassKind, RenderPass};
|
||||
use crate::render_task::{RenderTask, RenderTaskLocation, RenderTaskKind};
|
||||
use crate::resource_cache::{ResourceCache};
|
||||
use crate::scene::{ScenePipeline, SceneProperties};
|
||||
use crate::scene_builder_thread::DocumentStats;
|
||||
use crate::scene::{BuiltScene, ScenePipeline, SceneProperties};
|
||||
use crate::segment::SegmentBuilder;
|
||||
use std::{f32, mem};
|
||||
use std::sync::Arc;
|
||||
|
@ -106,20 +99,12 @@ impl FrameGlobalResources {
|
|||
}
|
||||
}
|
||||
|
||||
/// A builder structure for `render_task_graph::Frame`
|
||||
/// Produces the frames that are sent to the renderer.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
pub struct FrameBuilder {
|
||||
output_rect: DeviceIntRect,
|
||||
background_color: Option<ColorF>,
|
||||
root_pic_index: PictureIndex,
|
||||
/// Cache of surface tiles from the previous frame builder
|
||||
/// that can optionally be consumed by this frame builder.
|
||||
pending_retained_tiles: RetainedTiles,
|
||||
pub prim_store: PrimitiveStore,
|
||||
pub clip_store: ClipStore,
|
||||
#[cfg_attr(feature = "capture", serde(skip))] //TODO
|
||||
pub hit_testing_scene: Arc<HitTestingScene>,
|
||||
pub config: FrameBuilderConfig,
|
||||
pub globals: FrameGlobalResources,
|
||||
}
|
||||
|
||||
|
@ -212,82 +197,17 @@ pub struct PictureState {
|
|||
}
|
||||
|
||||
impl FrameBuilder {
|
||||
#[cfg(feature = "replay")]
|
||||
pub fn empty() -> Self {
|
||||
pub fn new() -> Self {
|
||||
FrameBuilder {
|
||||
hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
|
||||
prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
|
||||
clip_store: ClipStore::new(),
|
||||
output_rect: DeviceIntRect::zero(),
|
||||
background_color: None,
|
||||
root_pic_index: PictureIndex(0),
|
||||
pending_retained_tiles: RetainedTiles::new(),
|
||||
globals: FrameGlobalResources::empty(),
|
||||
config: FrameBuilderConfig {
|
||||
default_font_render_mode: FontRenderMode::Mono,
|
||||
dual_source_blending_is_enabled: true,
|
||||
dual_source_blending_is_supported: false,
|
||||
chase_primitive: ChasePrimitive::Nothing,
|
||||
enable_picture_caching: false,
|
||||
testing: false,
|
||||
gpu_supports_fast_clears: false,
|
||||
gpu_supports_advanced_blend: false,
|
||||
advanced_blend_is_coherent: false,
|
||||
batch_lookback_count: 0,
|
||||
background_color: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Provide any cached surface tiles from the previous frame builder
|
||||
/// to a new frame builder. These will be consumed or dropped the
|
||||
/// first time a new frame builder creates a frame.
|
||||
pub fn set_retained_resources(
|
||||
&mut self,
|
||||
retained_tiles: RetainedTiles,
|
||||
globals: FrameGlobalResources,
|
||||
) {
|
||||
assert!(self.pending_retained_tiles.caches.is_empty());
|
||||
self.pending_retained_tiles = retained_tiles;
|
||||
self.globals = globals;
|
||||
}
|
||||
|
||||
pub fn with_display_list_flattener(
|
||||
output_rect: DeviceIntRect,
|
||||
background_color: Option<ColorF>,
|
||||
flattener: DisplayListFlattener,
|
||||
) -> Self {
|
||||
FrameBuilder {
|
||||
hit_testing_scene: Arc::new(flattener.hit_testing_scene),
|
||||
prim_store: flattener.prim_store,
|
||||
clip_store: flattener.clip_store,
|
||||
root_pic_index: flattener.root_pic_index,
|
||||
output_rect,
|
||||
background_color,
|
||||
pending_retained_tiles: RetainedTiles::new(),
|
||||
config: flattener.config,
|
||||
globals: FrameGlobalResources::empty(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the memory usage statistics to pre-allocate for the next scene.
|
||||
pub fn get_stats(&self) -> DocumentStats {
|
||||
DocumentStats {
|
||||
prim_store_stats: self.prim_store.get_stats(),
|
||||
hit_test_stats: self.hit_testing_scene.get_stats(),
|
||||
}
|
||||
}
|
||||
|
||||
/// Destroy an existing frame builder. This is called just before
|
||||
/// a frame builder is replaced with a newly built scene.
|
||||
pub fn destroy(
|
||||
self,
|
||||
retained_tiles: &mut RetainedTiles,
|
||||
) -> FrameGlobalResources {
|
||||
self.prim_store.destroy(
|
||||
retained_tiles,
|
||||
);
|
||||
|
||||
pub fn set_retained_resources(&mut self, retained_tiles: RetainedTiles) {
|
||||
// In general, the pending retained tiles are consumed by the frame
|
||||
// builder the first time a frame is built after a new scene has
|
||||
// arrived. However, if two scenes arrive in quick succession, the
|
||||
|
@ -296,18 +216,15 @@ impl FrameBuilder {
|
|||
// be lost, causing a full invalidation of the entire screen. To
|
||||
// avoid this, if there are still pending tiles, include them in
|
||||
// the retained tiles passed to the next frame builder.
|
||||
retained_tiles.merge(self.pending_retained_tiles);
|
||||
|
||||
self.globals
|
||||
self.pending_retained_tiles.merge(retained_tiles);
|
||||
}
|
||||
|
||||
/// 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,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
render_tasks: &mut RenderTaskGraph,
|
||||
|
@ -323,34 +240,34 @@ impl FrameBuilder {
|
|||
) -> Option<RenderTaskId> {
|
||||
profile_scope!("cull");
|
||||
|
||||
if self.prim_store.pictures.is_empty() {
|
||||
if scene.prim_store.pictures.is_empty() {
|
||||
return None
|
||||
}
|
||||
|
||||
scratch.begin_frame();
|
||||
|
||||
let root_spatial_node_index = clip_scroll_tree.root_reference_frame_index();
|
||||
let root_spatial_node_index = scene.clip_scroll_tree.root_reference_frame_index();
|
||||
|
||||
const MAX_CLIP_COORD: f32 = 1.0e9;
|
||||
|
||||
let frame_context = FrameBuildingContext {
|
||||
global_device_pixel_scale,
|
||||
scene_properties,
|
||||
pipelines,
|
||||
pipelines: &scene.src.pipelines,
|
||||
global_screen_world_rect,
|
||||
clip_scroll_tree,
|
||||
clip_scroll_tree: &scene.clip_scroll_tree,
|
||||
max_local_clip: LayoutRect::new(
|
||||
LayoutPoint::new(-MAX_CLIP_COORD, -MAX_CLIP_COORD),
|
||||
LayoutSize::new(2.0 * MAX_CLIP_COORD, 2.0 * MAX_CLIP_COORD),
|
||||
),
|
||||
debug_flags,
|
||||
fb_config: &self.config,
|
||||
fb_config: &scene.config,
|
||||
};
|
||||
|
||||
let root_render_task = RenderTask::new_picture(
|
||||
RenderTaskLocation::Fixed(self.output_rect),
|
||||
self.output_rect.size.to_f32(),
|
||||
self.root_pic_index,
|
||||
RenderTaskLocation::Fixed(scene.output_rect),
|
||||
scene.output_rect.size.to_f32(),
|
||||
scene.root_pic_index,
|
||||
DeviceIntPoint::zero(),
|
||||
UvRectKind::Rect,
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
|
@ -368,7 +285,7 @@ impl FrameBuilder {
|
|||
ROOT_SPATIAL_NODE_INDEX,
|
||||
0.0,
|
||||
global_screen_world_rect,
|
||||
clip_scroll_tree,
|
||||
&scene.clip_scroll_tree,
|
||||
global_device_pixel_scale,
|
||||
);
|
||||
surfaces.push(root_surface);
|
||||
|
@ -387,11 +304,11 @@ impl FrameBuilder {
|
|||
// be rendered this frame.
|
||||
PictureUpdateState::update_all(
|
||||
surfaces,
|
||||
self.root_pic_index,
|
||||
&mut self.prim_store.pictures,
|
||||
scene.root_pic_index,
|
||||
&mut scene.prim_store.pictures,
|
||||
&frame_context,
|
||||
gpu_cache,
|
||||
&self.clip_store,
|
||||
&scene.clip_store,
|
||||
data_stores,
|
||||
);
|
||||
|
||||
|
@ -400,18 +317,18 @@ impl FrameBuilder {
|
|||
|
||||
let visibility_context = FrameVisibilityContext {
|
||||
global_device_pixel_scale,
|
||||
clip_scroll_tree,
|
||||
clip_scroll_tree: &scene.clip_scroll_tree,
|
||||
global_screen_world_rect,
|
||||
surfaces,
|
||||
debug_flags,
|
||||
scene_properties,
|
||||
config: &self.config,
|
||||
config: &scene.config,
|
||||
};
|
||||
|
||||
let mut visibility_state = FrameVisibilityState {
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
clip_store: &mut self.clip_store,
|
||||
clip_store: &mut scene.clip_store,
|
||||
scratch,
|
||||
tile_cache: None,
|
||||
retained_tiles: &mut retained_tiles,
|
||||
|
@ -420,8 +337,8 @@ impl FrameBuilder {
|
|||
render_tasks,
|
||||
};
|
||||
|
||||
self.prim_store.update_visibility(
|
||||
self.root_pic_index,
|
||||
scene.prim_store.update_visibility(
|
||||
scene.root_pic_index,
|
||||
ROOT_SURFACE_INDEX,
|
||||
&global_screen_world_rect,
|
||||
&visibility_context,
|
||||
|
@ -432,7 +349,7 @@ impl FrameBuilder {
|
|||
let mut frame_state = FrameBuildingState {
|
||||
render_tasks,
|
||||
profile_counters,
|
||||
clip_store: &mut self.clip_store,
|
||||
clip_store: &mut scene.clip_store,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
transforms: transform_palette,
|
||||
|
@ -460,11 +377,11 @@ impl FrameBuilder {
|
|||
);
|
||||
frame_state.push_dirty_region(default_dirty_region);
|
||||
|
||||
let (pic_context, mut pic_state, mut prim_list) = self
|
||||
let (pic_context, mut pic_state, mut prim_list) = scene
|
||||
.prim_store
|
||||
.pictures[self.root_pic_index.0]
|
||||
.pictures[scene.root_pic_index.0]
|
||||
.take_context(
|
||||
self.root_pic_index,
|
||||
scene.root_pic_index,
|
||||
WorldRect::max_rect(),
|
||||
root_spatial_node_index,
|
||||
root_spatial_node_index,
|
||||
|
@ -478,7 +395,7 @@ impl FrameBuilder {
|
|||
{
|
||||
profile_marker!("PreparePrims");
|
||||
|
||||
self.prim_store.prepare_primitives(
|
||||
scene.prim_store.prepare_primitives(
|
||||
&mut prim_list,
|
||||
&pic_context,
|
||||
&mut pic_state,
|
||||
|
@ -489,7 +406,7 @@ impl FrameBuilder {
|
|||
);
|
||||
}
|
||||
|
||||
let pic = &mut self.prim_store.pictures[self.root_pic_index.0];
|
||||
let pic = &mut scene.prim_store.pictures[scene.root_pic_index.0];
|
||||
pic.restore_context(
|
||||
prim_list,
|
||||
pic_context,
|
||||
|
@ -512,11 +429,10 @@ impl FrameBuilder {
|
|||
|
||||
pub fn build(
|
||||
&mut self,
|
||||
scene: &mut BuiltScene,
|
||||
resource_cache: &mut ResourceCache,
|
||||
gpu_cache: &mut GpuCache,
|
||||
stamp: FrameStamp,
|
||||
clip_scroll_tree: &mut ClipScrollTree,
|
||||
pipelines: &FastHashMap<PipelineId, Arc<ScenePipeline>>,
|
||||
global_device_pixel_scale: DevicePixelScale,
|
||||
layer: DocumentLayer,
|
||||
device_origin: DeviceIntPoint,
|
||||
|
@ -535,20 +451,20 @@ impl FrameBuilder {
|
|||
let mut profile_counters = FrameProfileCounters::new();
|
||||
profile_counters
|
||||
.total_primitives
|
||||
.set(self.prim_store.prim_count());
|
||||
.set(scene.prim_store.prim_count());
|
||||
|
||||
resource_cache.begin_frame(stamp);
|
||||
gpu_cache.begin_frame(stamp);
|
||||
|
||||
self.globals.update(gpu_cache);
|
||||
|
||||
clip_scroll_tree.update_tree(
|
||||
scene.clip_scroll_tree.update_tree(
|
||||
pan,
|
||||
global_device_pixel_scale,
|
||||
scene_properties,
|
||||
);
|
||||
let mut transform_palette = clip_scroll_tree.build_transform_palette();
|
||||
self.clip_store.clear_old_instances();
|
||||
let mut transform_palette = scene.clip_scroll_tree.build_transform_palette();
|
||||
scene.clip_store.clear_old_instances();
|
||||
|
||||
let mut render_tasks = RenderTaskGraph::new(
|
||||
stamp.frame_id(),
|
||||
|
@ -556,13 +472,12 @@ impl FrameBuilder {
|
|||
);
|
||||
let mut surfaces = Vec::new();
|
||||
|
||||
let output_size = self.output_rect.size.to_i32();
|
||||
let screen_world_rect = (self.output_rect.to_f32() / global_device_pixel_scale).round_out();
|
||||
let output_size = scene.output_rect.size.to_i32();
|
||||
let screen_world_rect = (scene.output_rect.to_f32() / global_device_pixel_scale).round_out();
|
||||
|
||||
let main_render_task_id = self.build_layer_screen_rects_and_cull_layers(
|
||||
scene,
|
||||
screen_world_rect,
|
||||
clip_scroll_tree,
|
||||
pipelines,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
&mut render_tasks,
|
||||
|
@ -588,24 +503,24 @@ impl FrameBuilder {
|
|||
passes = render_tasks.generate_passes(
|
||||
main_render_task_id,
|
||||
output_size,
|
||||
self.config.gpu_supports_fast_clears,
|
||||
scene.config.gpu_supports_fast_clears,
|
||||
);
|
||||
|
||||
// Used to generated a unique z-buffer value per primitive.
|
||||
let mut z_generator = ZBufferIdGenerator::new(layer);
|
||||
let use_dual_source_blending = self.config.dual_source_blending_is_enabled &&
|
||||
self.config.dual_source_blending_is_supported;
|
||||
let use_dual_source_blending = scene.config.dual_source_blending_is_enabled &&
|
||||
scene.config.dual_source_blending_is_supported;
|
||||
|
||||
for pass in &mut passes {
|
||||
let mut ctx = RenderTargetContext {
|
||||
global_device_pixel_scale,
|
||||
prim_store: &self.prim_store,
|
||||
prim_store: &scene.prim_store,
|
||||
resource_cache,
|
||||
use_dual_source_blending,
|
||||
use_advanced_blending: self.config.gpu_supports_advanced_blend,
|
||||
break_advanced_blend_batches: !self.config.advanced_blend_is_coherent,
|
||||
batch_lookback_count: self.config.batch_lookback_count,
|
||||
clip_scroll_tree,
|
||||
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,
|
||||
clip_scroll_tree: &scene.clip_scroll_tree,
|
||||
data_stores,
|
||||
surfaces: &surfaces,
|
||||
scratch,
|
||||
|
@ -619,7 +534,7 @@ impl FrameBuilder {
|
|||
gpu_cache,
|
||||
&mut render_tasks,
|
||||
&mut deferred_resolves,
|
||||
&self.clip_store,
|
||||
&scene.clip_store,
|
||||
&mut transform_palette,
|
||||
&mut prim_headers,
|
||||
&mut z_generator,
|
||||
|
@ -646,12 +561,12 @@ impl FrameBuilder {
|
|||
resource_cache.end_frame(texture_cache_profile);
|
||||
|
||||
Frame {
|
||||
content_origin: self.output_rect.origin,
|
||||
content_origin: scene.output_rect.origin,
|
||||
device_rect: DeviceIntRect::new(
|
||||
device_origin,
|
||||
self.output_rect.size,
|
||||
scene.output_rect.size,
|
||||
),
|
||||
background_color: self.background_color,
|
||||
background_color: scene.background_color,
|
||||
layer,
|
||||
profile_counters,
|
||||
passes,
|
||||
|
@ -666,19 +581,6 @@ impl FrameBuilder {
|
|||
debug_items: mem::replace(&mut scratch.debug_items, Vec::new()),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_hit_tester(
|
||||
&mut self,
|
||||
clip_scroll_tree: &ClipScrollTree,
|
||||
clip_data_store: &ClipDataStore,
|
||||
) -> HitTester {
|
||||
HitTester::new(
|
||||
Arc::clone(&self.hit_testing_scene),
|
||||
clip_scroll_tree,
|
||||
&self.clip_store,
|
||||
clip_data_store,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Processes this pass to prepare it for rendering.
|
||||
|
|
|
@ -88,7 +88,6 @@ mod debug_render;
|
|||
#[cfg(feature = "debugger")]
|
||||
mod debug_server;
|
||||
mod device;
|
||||
mod display_list_flattener;
|
||||
mod ellipse;
|
||||
mod filterdata;
|
||||
mod frame_builder;
|
||||
|
@ -116,6 +115,7 @@ mod renderer;
|
|||
mod resource_cache;
|
||||
mod scene;
|
||||
mod scene_builder_thread;
|
||||
mod scene_building;
|
||||
mod screen_capture;
|
||||
mod segment;
|
||||
mod shade;
|
||||
|
|
|
@ -6,7 +6,7 @@ use api::{NormalBorder, PremultipliedColorF, Shadow};
|
|||
use api::units::*;
|
||||
use crate::border::create_border_segments;
|
||||
use crate::border::NormalBorderAu;
|
||||
use crate::display_list_flattener::{CreateShadow, IsVisible};
|
||||
use crate::scene_building::{CreateShadow, IsVisible};
|
||||
use crate::frame_builder::{FrameBuildingState};
|
||||
use crate::gpu_cache::{GpuCache, GpuDataRequest};
|
||||
use crate::intern;
|
||||
|
|
|
@ -7,7 +7,7 @@ use api::{
|
|||
PremultipliedColorF, LineOrientation,
|
||||
};
|
||||
use api::units::{LayoutPoint, LayoutSize, LayoutVector2D};
|
||||
use crate::display_list_flattener::IsVisible;
|
||||
use crate::scene_building::IsVisible;
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use crate::frame_builder::FrameBuildingState;
|
||||
use crate::gpu_cache::{GpuCacheHandle, GpuDataRequest};
|
||||
|
|
|
@ -8,7 +8,7 @@ use api::{
|
|||
PremultipliedColorF, Shadow, YuvColorSpace, ColorRange, YuvFormat,
|
||||
};
|
||||
use api::units::*;
|
||||
use crate::display_list_flattener::{CreateShadow, IsVisible};
|
||||
use crate::scene_building::{CreateShadow, IsVisible};
|
||||
use crate::frame_builder::FrameBuildingState;
|
||||
use crate::gpu_cache::{GpuCache, GpuDataRequest};
|
||||
use crate::intern::{Internable, InternDebug, Handle as InternHandle};
|
||||
|
|
|
@ -7,7 +7,7 @@ use api::{
|
|||
LineOrientation, LineStyle, PremultipliedColorF, Shadow,
|
||||
};
|
||||
use api::units::{Au, LayoutSizeAu, LayoutVector2D};
|
||||
use crate::display_list_flattener::{CreateShadow, IsVisible};
|
||||
use crate::scene_building::{CreateShadow, IsVisible};
|
||||
use crate::frame_builder::{FrameBuildingState};
|
||||
use crate::gpu_cache::GpuDataRequest;
|
||||
use crate::intern;
|
||||
|
|
|
@ -15,7 +15,7 @@ use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, Coordinat
|
|||
use crate::clip::{ClipDataStore, ClipNodeFlags, ClipChainId, ClipChainInstance, ClipItemKind};
|
||||
use crate::debug_colors;
|
||||
use crate::debug_render::DebugItem;
|
||||
use crate::display_list_flattener::{CreateShadow, IsVisible};
|
||||
use crate::scene_building::{CreateShadow, IsVisible};
|
||||
use euclid::{SideOffsets2D, Transform3D, Rect, Scale, Size2D, Point2D};
|
||||
use euclid::approxeq::ApproxEq;
|
||||
use crate::frame_builder::{FrameBuildingContext, FrameBuildingState, PictureContext, PictureState};
|
||||
|
@ -1840,7 +1840,7 @@ impl PrimitiveStore {
|
|||
/// Destroy an existing primitive store. This is called just before
|
||||
/// a primitive store is replaced with a newly built scene.
|
||||
pub fn destroy(
|
||||
mut self,
|
||||
&mut self,
|
||||
retained_tiles: &mut RetainedTiles,
|
||||
) {
|
||||
for pic in &mut self.pictures {
|
||||
|
@ -4082,7 +4082,7 @@ fn update_opacity_binding(
|
|||
}
|
||||
|
||||
/// Trait for primitives that are directly internable.
|
||||
/// see DisplayListFlattener::add_primitive<P>
|
||||
/// see SceneBuilder::add_primitive<P>
|
||||
pub trait InternablePrimitive: intern::Internable<InternData = PrimitiveSceneData> + Sized {
|
||||
/// Build a new key from self with `info`.
|
||||
fn into_key(
|
||||
|
|
|
@ -7,7 +7,7 @@ use api::{
|
|||
PropertyBinding, PropertyBindingId, CompositeOperator,
|
||||
};
|
||||
use api::units::{Au, LayoutSize, LayoutVector2D};
|
||||
use crate::display_list_flattener::IsVisible;
|
||||
use crate::scene_building::IsVisible;
|
||||
use crate::filterdata::SFilterData;
|
||||
use crate::intern::ItemUid;
|
||||
use crate::intern::{Internable, InternDebug, Handle as InternHandle};
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
|
||||
use api::{ColorF, GlyphInstance, RasterSpace, Shadow};
|
||||
use api::units::{DevicePixelScale, LayoutToWorldTransform, LayoutVector2D};
|
||||
use crate::display_list_flattener::{CreateShadow, IsVisible};
|
||||
use crate::scene_building::{CreateShadow, IsVisible};
|
||||
use crate::frame_builder::FrameBuildingState;
|
||||
use crate::glyph_rasterizer::{FontInstance, FontTransform, GlyphKey, FONT_SIZE_LIMIT};
|
||||
use crate::gpu_cache::GpuCache;
|
||||
|
|
|
@ -13,7 +13,7 @@ use api::{ApiMsg, BuiltDisplayList, ClearCache, DebugCommand, DebugFlags};
|
|||
use api::{BuiltDisplayListIter, DisplayItem};
|
||||
use api::{DocumentId, DocumentLayer, ExternalScrollId, FrameMsg, HitTestFlags, HitTestResult};
|
||||
use api::{IdNamespace, MemoryReport, PipelineId, RenderNotifier, SceneMsg, ScrollClamping};
|
||||
use api::{ScrollLocation, ScrollNodeState, TransactionMsg, ResourceUpdate, BlobImageKey};
|
||||
use api::{ScrollLocation, TransactionMsg, ResourceUpdate, BlobImageKey};
|
||||
use api::{NotificationRequest, Checkpoint};
|
||||
use api::{ClipIntern, FilterDataIntern, PrimitiveKeyKind};
|
||||
use api::units::*;
|
||||
|
@ -22,7 +22,7 @@ use api::channel::{MsgReceiver, MsgSender, Payload};
|
|||
use api::CaptureBits;
|
||||
#[cfg(feature = "replay")]
|
||||
use api::CapturedDocument;
|
||||
use crate::clip_scroll_tree::{SpatialNodeIndex, ClipScrollTree};
|
||||
use crate::clip_scroll_tree::SpatialNodeIndex;
|
||||
#[cfg(feature = "debugger")]
|
||||
use crate::debug_server;
|
||||
use crate::frame_builder::{FrameBuilder, FrameBuilderConfig};
|
||||
|
@ -45,7 +45,7 @@ use crate::resource_cache::ResourceCache;
|
|||
use crate::resource_cache::PlainCacheOwn;
|
||||
#[cfg(any(feature = "capture", feature = "replay"))]
|
||||
use crate::resource_cache::PlainResources;
|
||||
use crate::scene::{Scene, SceneProperties};
|
||||
use crate::scene::{BuiltScene, SceneProperties};
|
||||
use crate::scene_builder_thread::*;
|
||||
#[cfg(feature = "serialize")]
|
||||
use serde::{Serialize, Deserialize};
|
||||
|
@ -328,30 +328,27 @@ impl DataStores {
|
|||
}
|
||||
|
||||
struct Document {
|
||||
// The id of this document
|
||||
/// The id of this document
|
||||
id: DocumentId,
|
||||
// The latest built scene, usable to build frames.
|
||||
// received from the scene builder thread.
|
||||
scene: Scene,
|
||||
|
||||
// Temporary list of removed pipelines received from the scene builder
|
||||
// thread and forwarded to the renderer.
|
||||
/// Temporary list of removed pipelines received from the scene builder
|
||||
/// thread and forwarded to the renderer.
|
||||
removed_pipelines: Vec<(PipelineId, DocumentId)>,
|
||||
|
||||
view: DocumentView,
|
||||
|
||||
/// The ClipScrollTree for this document which tracks SpatialNodes, ClipNodes, and ClipChains.
|
||||
/// This is stored here so that we are able to preserve scrolling positions between rendered
|
||||
/// frames.
|
||||
clip_scroll_tree: ClipScrollTree,
|
||||
|
||||
/// The id and time of the current frame.
|
||||
stamp: FrameStamp,
|
||||
|
||||
// the `Option` here is only to deal with borrow checker
|
||||
frame_builder: Option<FrameBuilder>,
|
||||
// A set of pipelines that the caller has requested be
|
||||
// made available as output textures.
|
||||
/// The latest built scene, usable to build frames.
|
||||
/// received from the scene builder thread.
|
||||
scene: BuiltScene,
|
||||
|
||||
/// The builder object that prodces frames, kept around to preserve some retained state.
|
||||
frame_builder: FrameBuilder,
|
||||
|
||||
/// A set of pipelines that the caller has requested be
|
||||
/// made available as output textures.
|
||||
output_pipelines: FastHashSet<PipelineId>,
|
||||
|
||||
/// A data structure to allow hit testing against rendered frames. This is updated
|
||||
|
@ -367,8 +364,8 @@ struct Document {
|
|||
frame_is_valid: bool,
|
||||
hit_tester_is_valid: bool,
|
||||
rendered_frame_is_valid: bool,
|
||||
// We track this information to be able to display debugging information from the
|
||||
// renderer.
|
||||
/// We track this information to be able to display debugging information from the
|
||||
/// renderer.
|
||||
has_built_scene: bool,
|
||||
|
||||
data_stores: DataStores,
|
||||
|
@ -391,7 +388,6 @@ impl Document {
|
|||
) -> Self {
|
||||
Document {
|
||||
id,
|
||||
scene: Scene::new(),
|
||||
removed_pipelines: Vec::new(),
|
||||
view: DocumentView {
|
||||
device_rect: size.into(),
|
||||
|
@ -401,9 +397,9 @@ impl Document {
|
|||
pinch_zoom_factor: 1.0,
|
||||
device_pixel_ratio: default_device_pixel_ratio,
|
||||
},
|
||||
clip_scroll_tree: ClipScrollTree::new(),
|
||||
stamp: FrameStamp::first(id),
|
||||
frame_builder: None,
|
||||
scene: BuiltScene::empty(),
|
||||
frame_builder: FrameBuilder::new(),
|
||||
output_pipelines: FastHashSet::default(),
|
||||
hit_tester: None,
|
||||
dynamic_properties: SceneProperties::new(),
|
||||
|
@ -418,7 +414,7 @@ impl Document {
|
|||
}
|
||||
|
||||
fn can_render(&self) -> bool {
|
||||
self.frame_builder.is_some() && self.scene.has_root_pipeline()
|
||||
self.scene.src.has_root_pipeline()
|
||||
}
|
||||
|
||||
fn has_pixels(&self) -> bool {
|
||||
|
@ -431,7 +427,7 @@ impl Document {
|
|||
) -> DocumentOps {
|
||||
match message {
|
||||
FrameMsg::UpdateEpoch(pipeline_id, epoch) => {
|
||||
self.scene.update_epoch(pipeline_id, epoch);
|
||||
self.scene.src.update_epoch(pipeline_id, epoch);
|
||||
}
|
||||
FrameMsg::Scroll(delta, cursor) => {
|
||||
profile_scope!("Scroll");
|
||||
|
@ -498,7 +494,7 @@ impl Document {
|
|||
}
|
||||
FrameMsg::GetScrollNodeState(tx) => {
|
||||
profile_scope!("GetScrollNodeState");
|
||||
tx.send(self.get_scroll_node_state()).unwrap();
|
||||
tx.send(self.scene.clip_scroll_tree.get_scroll_node_state()).unwrap();
|
||||
}
|
||||
FrameMsg::UpdateDynamicProperties(property_bindings) => {
|
||||
self.dynamic_properties.set_properties(property_bindings);
|
||||
|
@ -513,7 +509,7 @@ impl Document {
|
|||
}
|
||||
}
|
||||
FrameMsg::SetIsTransformPinchZooming(is_zooming, animation_id) => {
|
||||
let node = self.clip_scroll_tree.spatial_nodes.iter_mut()
|
||||
let node = self.scene.clip_scroll_tree.spatial_nodes.iter_mut()
|
||||
.find(|node| node.is_transform_bound_to_property(animation_id));
|
||||
if let Some(node) = node {
|
||||
if node.is_pinch_zooming != is_zooming {
|
||||
|
@ -544,13 +540,11 @@ impl Document {
|
|||
"First frame increment must happen before build_frame()");
|
||||
|
||||
let frame = {
|
||||
let frame_builder = self.frame_builder.as_mut().unwrap();
|
||||
let frame = frame_builder.build(
|
||||
let frame = self.frame_builder.build(
|
||||
&mut self.scene,
|
||||
resource_cache,
|
||||
gpu_cache,
|
||||
self.stamp,
|
||||
&mut self.clip_scroll_tree,
|
||||
&self.scene.pipelines,
|
||||
accumulated_scale_factor,
|
||||
self.view.layer,
|
||||
self.view.device_rect.origin,
|
||||
|
@ -563,10 +557,7 @@ impl Document {
|
|||
&mut self.render_task_counters,
|
||||
debug_flags,
|
||||
);
|
||||
self.hit_tester = Some(frame_builder.create_hit_tester(
|
||||
&self.clip_scroll_tree,
|
||||
&self.data_stores.clip,
|
||||
));
|
||||
self.hit_tester = Some(self.scene.create_hit_tester(&self.data_stores.clip));
|
||||
frame
|
||||
};
|
||||
|
||||
|
@ -583,35 +574,30 @@ impl Document {
|
|||
}
|
||||
|
||||
fn rebuild_hit_tester(&mut self) {
|
||||
if let Some(ref mut frame_builder) = self.frame_builder {
|
||||
let accumulated_scale_factor = self.view.accumulated_scale_factor();
|
||||
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
|
||||
let accumulated_scale_factor = self.view.accumulated_scale_factor();
|
||||
let pan = self.view.pan.to_f32() / accumulated_scale_factor;
|
||||
|
||||
self.clip_scroll_tree.update_tree(
|
||||
self.scene.clip_scroll_tree.update_tree(
|
||||
pan,
|
||||
accumulated_scale_factor,
|
||||
&self.dynamic_properties,
|
||||
);
|
||||
|
||||
self.hit_tester = Some(frame_builder.create_hit_tester(
|
||||
&self.clip_scroll_tree,
|
||||
&self.data_stores.clip,
|
||||
));
|
||||
self.hit_tester_is_valid = true;
|
||||
}
|
||||
self.hit_tester = Some(self.scene.create_hit_tester(&self.data_stores.clip));
|
||||
self.hit_tester_is_valid = true;
|
||||
}
|
||||
|
||||
pub fn updated_pipeline_info(&mut self) -> PipelineInfo {
|
||||
let removed_pipelines = self.removed_pipelines.take_and_preallocate();
|
||||
PipelineInfo {
|
||||
epochs: self.scene.pipeline_epochs.iter()
|
||||
epochs: self.scene.src.pipeline_epochs.iter()
|
||||
.map(|(&pipeline_id, &epoch)| ((pipeline_id, self.id), epoch)).collect(),
|
||||
removed_pipelines,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn discard_frame_state_for_pipeline(&mut self, pipeline_id: PipelineId) {
|
||||
self.clip_scroll_tree
|
||||
self.scene.clip_scroll_tree
|
||||
.discard_frame_state_for_pipeline(pipeline_id);
|
||||
}
|
||||
|
||||
|
@ -621,7 +607,7 @@ impl Document {
|
|||
scroll_location: ScrollLocation,
|
||||
scroll_node_index: Option<SpatialNodeIndex>,
|
||||
) -> bool {
|
||||
self.clip_scroll_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index)
|
||||
self.scene.clip_scroll_tree.scroll_nearest_scrolling_ancestor(scroll_location, scroll_node_index)
|
||||
}
|
||||
|
||||
/// Returns true if the node actually changed position or false otherwise.
|
||||
|
@ -631,46 +617,37 @@ impl Document {
|
|||
id: ExternalScrollId,
|
||||
clamp: ScrollClamping
|
||||
) -> bool {
|
||||
self.clip_scroll_tree.scroll_node(origin, id, clamp)
|
||||
}
|
||||
|
||||
pub fn get_scroll_node_state(&self) -> Vec<ScrollNodeState> {
|
||||
self.clip_scroll_tree.get_scroll_node_state()
|
||||
self.scene.clip_scroll_tree.scroll_node(origin, id, clamp)
|
||||
}
|
||||
|
||||
pub fn new_async_scene_ready(
|
||||
&mut self,
|
||||
mut built_scene: BuiltScene,
|
||||
built_scene: BuiltScene,
|
||||
recycler: &mut Recycler,
|
||||
) {
|
||||
self.scene = built_scene.scene;
|
||||
self.frame_is_valid = false;
|
||||
self.hit_tester_is_valid = false;
|
||||
|
||||
// Give the old frame builder a chance to destroy any resources.
|
||||
// Give the old scene a chance to destroy any resources.
|
||||
// Right now, all this does is build a hash map of any cached
|
||||
// surface tiles, that can be provided to the next frame builder.
|
||||
// surface tiles, that can be provided to the next scene.
|
||||
// TODO(nical) - It's a bit awkward how these retained tiles live
|
||||
// in the scene's prim store then temporarily in the frame builder
|
||||
// and then presumably back in the prim store during the next frame
|
||||
// build.
|
||||
let mut retained_tiles = RetainedTiles::new();
|
||||
if let Some(frame_builder) = self.frame_builder.take() {
|
||||
let globals = frame_builder.destroy(
|
||||
&mut retained_tiles,
|
||||
);
|
||||
self.scene.prim_store.destroy(&mut retained_tiles);
|
||||
let old_scrolling_states = self.scene.clip_scroll_tree.drain();
|
||||
|
||||
// Provide any cached tiles from the previous frame builder to
|
||||
// the newly built one.
|
||||
built_scene.frame_builder.set_retained_resources(
|
||||
retained_tiles,
|
||||
globals,
|
||||
);
|
||||
}
|
||||
self.scene = built_scene;
|
||||
|
||||
self.frame_builder = Some(built_scene.frame_builder);
|
||||
// Provide any cached tiles from the previous scene to
|
||||
// the newly built one.
|
||||
self.frame_builder.set_retained_resources(retained_tiles);
|
||||
|
||||
self.scratch.recycle(recycler);
|
||||
|
||||
let old_scrolling_states = self.clip_scroll_tree.drain();
|
||||
self.clip_scroll_tree = built_scene.clip_scroll_tree;
|
||||
self.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
|
||||
self.scene.clip_scroll_tree.finalize_and_apply_pending_scroll_offsets(old_scrolling_states);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -1152,15 +1129,15 @@ impl RenderBackend {
|
|||
for (id, doc) in &self.documents {
|
||||
let captured = CapturedDocument {
|
||||
document_id: *id,
|
||||
root_pipeline_id: doc.scene.root_pipeline_id,
|
||||
root_pipeline_id: doc.scene.src.root_pipeline_id,
|
||||
};
|
||||
tx.send(captured).unwrap();
|
||||
|
||||
// notify the active recorder
|
||||
if let Some(ref mut r) = self.recorder {
|
||||
let pipeline_id = doc.scene.root_pipeline_id.unwrap();
|
||||
let epoch = doc.scene.pipeline_epochs[&pipeline_id];
|
||||
let pipeline = &doc.scene.pipelines[&pipeline_id];
|
||||
let pipeline_id = doc.scene.src.root_pipeline_id.unwrap();
|
||||
let epoch = doc.scene.src.pipeline_epochs[&pipeline_id];
|
||||
let pipeline = &doc.scene.src.pipelines[&pipeline_id];
|
||||
let scene_msg = SceneMsg::SetDisplayList {
|
||||
list_descriptor: pipeline.display_list.descriptor().clone(),
|
||||
epoch,
|
||||
|
@ -1630,7 +1607,7 @@ impl RenderBackend {
|
|||
for (_, doc) in &self.documents {
|
||||
let mut debug_doc = debug_server::TreeNode::new("document");
|
||||
|
||||
for (_, pipeline) in &doc.scene.pipelines {
|
||||
for (_, pipeline) in &doc.scene.src.pipelines {
|
||||
let mut debug_dl = debug_server::TreeNode::new("display-list");
|
||||
self.traverse_items(&mut pipeline.display_list.iter(), &mut debug_dl);
|
||||
debug_doc.add_child(debug_dl);
|
||||
|
@ -1657,7 +1634,7 @@ impl RenderBackend {
|
|||
let debug_node = debug_server::TreeNode::new("document clip-scroll tree");
|
||||
let mut builder = debug_server::TreeNodeBuilder::new(debug_node);
|
||||
|
||||
doc.clip_scroll_tree.print_with(&mut builder);
|
||||
doc.scene.clip_scroll_tree.print_with(&mut builder);
|
||||
|
||||
debug_root.add(builder.build());
|
||||
}
|
||||
|
@ -1671,9 +1648,7 @@ impl RenderBackend {
|
|||
let op = ops.size_of_op;
|
||||
report.gpu_cache_metadata = self.gpu_cache.size_of(ops);
|
||||
for (_id, doc) in &self.documents {
|
||||
if let Some(ref fb) = doc.frame_builder {
|
||||
report.clip_stores += fb.clip_store.size_of(ops);
|
||||
}
|
||||
report.clip_stores += doc.scene.clip_store.size_of(ops);
|
||||
report.hit_testers += doc.hit_tester.size_of(ops);
|
||||
|
||||
doc.data_stores.report_memory(ops, &mut report)
|
||||
|
@ -1734,7 +1709,7 @@ impl RenderBackend {
|
|||
debug!("\tdocument {:?}", id);
|
||||
if config.bits.contains(CaptureBits::SCENE) {
|
||||
let file_name = format!("scene-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize(&doc.scene, file_name);
|
||||
config.serialize(&doc.scene.src, file_name);
|
||||
}
|
||||
if config.bits.contains(CaptureBits::FRAME) {
|
||||
let rendered_document = doc.build_frame(
|
||||
|
@ -1754,9 +1729,9 @@ impl RenderBackend {
|
|||
let file_name = format!("frame-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize(&rendered_document.frame, file_name);
|
||||
let file_name = format!("clip-scroll-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize_tree(&doc.clip_scroll_tree, file_name);
|
||||
config.serialize_tree(&doc.scene.clip_scroll_tree, file_name);
|
||||
let file_name = format!("builder-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize(doc.frame_builder.as_ref().unwrap(), file_name);
|
||||
config.serialize(&doc.frame_builder, file_name);
|
||||
let file_name = format!("scratch-{}-{}", id.namespace_id.0, id.id);
|
||||
config.serialize(&doc.scratch, file_name);
|
||||
let file_name = format!("properties-{}-{}", id.namespace_id.0, id.id);
|
||||
|
@ -1826,6 +1801,7 @@ impl RenderBackend {
|
|||
profile_counters: &mut BackendProfileCounters,
|
||||
) {
|
||||
use crate::capture::CaptureConfig;
|
||||
use crate::scene::Scene;
|
||||
|
||||
debug!("capture: loading {:?}", root);
|
||||
let backend = CaptureConfig::deserialize::<PlainRenderBackend, _>(root, "backend")
|
||||
|
@ -1871,14 +1847,16 @@ impl RenderBackend {
|
|||
let data_stores = CaptureConfig::deserialize::<DataStores, _>(root, &data_stores_name)
|
||||
.expect(&format!("Unable to open {}.ron", data_stores_name));
|
||||
|
||||
let mut built_scene = BuiltScene::empty();
|
||||
built_scene.src = scene;
|
||||
|
||||
let doc = Document {
|
||||
id,
|
||||
scene: scene.clone(),
|
||||
scene: built_scene,
|
||||
removed_pipelines: Vec::new(),
|
||||
view: view.clone(),
|
||||
clip_scroll_tree: ClipScrollTree::new(),
|
||||
stamp: FrameStamp::first(id),
|
||||
frame_builder: Some(FrameBuilder::empty()),
|
||||
frame_builder: FrameBuilder::new(),
|
||||
output_pipelines: FastHashSet::default(),
|
||||
dynamic_properties: SceneProperties::new(),
|
||||
hit_tester: None,
|
||||
|
@ -1920,7 +1898,7 @@ impl RenderBackend {
|
|||
|
||||
scenes_to_build.push(LoadScene {
|
||||
document_id: id,
|
||||
scene: doc.scene.clone(),
|
||||
scene: doc.scene.src.clone(),
|
||||
view: view.clone(),
|
||||
config: self.frame_config.clone(),
|
||||
output_pipelines: doc.output_pipelines.clone(),
|
||||
|
|
|
@ -2,10 +2,15 @@
|
|||
* 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::{BuiltDisplayList, ColorF, DynamicProperties, Epoch};
|
||||
use api::{BuiltDisplayList, ColorF, DynamicProperties, Epoch, FontRenderMode};
|
||||
use api::{PipelineId, PropertyBinding, PropertyBindingId, MixBlendMode, StackingContext};
|
||||
use api::units::{LayoutSize, LayoutTransform};
|
||||
use api::units::*;
|
||||
use crate::clip::{ClipStore, ClipDataStore};
|
||||
use crate::clip_scroll_tree::ClipScrollTree;
|
||||
use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig};
|
||||
use crate::hit_test::{HitTester, HitTestingScene, HitTestingSceneStats};
|
||||
use crate::internal_types::FastHashMap;
|
||||
use crate::prim_store::{PrimitiveStore, PrimitiveStoreStats, PictureIndex};
|
||||
use std::sync::Arc;
|
||||
|
||||
/// Stores a map of the animated property bindings for the current display list. These
|
||||
|
@ -207,3 +212,84 @@ impl StackingContextHelpers for StackingContext {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/// WebRender's internal representation of the scene.
|
||||
pub struct BuiltScene {
|
||||
/// The scene this object was built from.
|
||||
pub src: Scene,
|
||||
pub output_rect: DeviceIntRect,
|
||||
pub background_color: Option<ColorF>,
|
||||
pub root_pic_index: PictureIndex,
|
||||
pub prim_store: PrimitiveStore,
|
||||
pub clip_store: ClipStore,
|
||||
pub config: FrameBuilderConfig,
|
||||
pub clip_scroll_tree: ClipScrollTree,
|
||||
pub hit_testing_scene: Arc<HitTestingScene>,
|
||||
}
|
||||
|
||||
impl BuiltScene {
|
||||
pub fn empty() -> Self {
|
||||
BuiltScene {
|
||||
src: Scene::new(),
|
||||
output_rect: DeviceIntRect::zero(),
|
||||
background_color: None,
|
||||
root_pic_index: PictureIndex(0),
|
||||
prim_store: PrimitiveStore::new(&PrimitiveStoreStats::empty()),
|
||||
clip_store: ClipStore::new(),
|
||||
clip_scroll_tree: ClipScrollTree::new(),
|
||||
hit_testing_scene: Arc::new(HitTestingScene::new(&HitTestingSceneStats::empty())),
|
||||
config: FrameBuilderConfig {
|
||||
default_font_render_mode: FontRenderMode::Mono,
|
||||
dual_source_blending_is_enabled: true,
|
||||
dual_source_blending_is_supported: false,
|
||||
chase_primitive: ChasePrimitive::Nothing,
|
||||
enable_picture_caching: false,
|
||||
testing: false,
|
||||
gpu_supports_fast_clears: false,
|
||||
gpu_supports_advanced_blend: false,
|
||||
advanced_blend_is_coherent: false,
|
||||
batch_lookback_count: 0,
|
||||
background_color: None,
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
/// Get the memory usage statistics to pre-allocate for the next scene.
|
||||
pub fn get_stats(&self) -> SceneStats {
|
||||
SceneStats {
|
||||
prim_store_stats: self.prim_store.get_stats(),
|
||||
hit_test_stats: self.hit_testing_scene.get_stats(),
|
||||
}
|
||||
}
|
||||
|
||||
pub fn create_hit_tester(
|
||||
&mut self,
|
||||
clip_data_store: &ClipDataStore,
|
||||
) -> HitTester {
|
||||
HitTester::new(
|
||||
Arc::clone(&self.hit_testing_scene),
|
||||
&self.clip_scroll_tree,
|
||||
&self.clip_store,
|
||||
clip_data_store,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
/// Stores the allocation sizes of various arrays in the built
|
||||
/// scene. This is retrieved from the current frame builder
|
||||
/// and used to reserve an approximately correct capacity of
|
||||
/// the arrays for the next scene that is getting built.
|
||||
pub struct SceneStats {
|
||||
pub prim_store_stats: PrimitiveStoreStats,
|
||||
pub hit_test_stats: HitTestingSceneStats,
|
||||
}
|
||||
|
||||
impl SceneStats {
|
||||
pub fn empty() -> Self {
|
||||
SceneStats {
|
||||
prim_store_stats: PrimitiveStoreStats::empty(),
|
||||
hit_test_stats: HitTestingSceneStats::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -10,14 +10,11 @@ use api::channel::MsgSender;
|
|||
use api::units::LayoutSize;
|
||||
#[cfg(feature = "capture")]
|
||||
use crate::capture::CaptureConfig;
|
||||
use crate::frame_builder::{FrameBuilderConfig, FrameBuilder};
|
||||
use crate::clip_scroll_tree::ClipScrollTree;
|
||||
use crate::display_list_flattener::DisplayListFlattener;
|
||||
use crate::hit_test::HitTestingSceneStats;
|
||||
use crate::frame_builder::FrameBuilderConfig;
|
||||
use crate::scene_building::SceneBuilder;
|
||||
use crate::intern::{Internable, Interner, UpdateList};
|
||||
use crate::internal_types::{FastHashMap, FastHashSet};
|
||||
use malloc_size_of::{MallocSizeOf, MallocSizeOfOps};
|
||||
use crate::prim_store::PrimitiveStoreStats;
|
||||
use crate::prim_store::backdrop::Backdrop;
|
||||
use crate::prim_store::borders::{ImageBorder, NormalBorderPrim};
|
||||
use crate::prim_store::gradient::{LinearGradient, RadialGradient};
|
||||
|
@ -28,7 +25,7 @@ use crate::prim_store::text_run::TextRun;
|
|||
use crate::resource_cache::{AsyncBlobImageInfo, FontInstanceMap};
|
||||
use crate::render_backend::DocumentView;
|
||||
use crate::renderer::{PipelineInfo, SceneBuilderHooks};
|
||||
use crate::scene::Scene;
|
||||
use crate::scene::{Scene, BuiltScene, SceneStats};
|
||||
use std::iter;
|
||||
use std::sync::mpsc::{channel, Receiver, Sender};
|
||||
use std::mem::replace;
|
||||
|
@ -130,12 +127,6 @@ pub struct LoadScene {
|
|||
pub interners: Interners,
|
||||
}
|
||||
|
||||
pub struct BuiltScene {
|
||||
pub scene: Scene,
|
||||
pub frame_builder: FrameBuilder,
|
||||
pub clip_scroll_tree: ClipScrollTree,
|
||||
}
|
||||
|
||||
// Message from render backend to scene builder.
|
||||
pub enum SceneBuilderRequest {
|
||||
Transactions(Vec<Box<Transaction>>),
|
||||
|
@ -228,24 +219,6 @@ macro_rules! declare_interners {
|
|||
|
||||
enumerate_interners!(declare_interners);
|
||||
|
||||
/// Stores the allocation sizes of various arrays in the frame
|
||||
/// builder. This is retrieved from the current frame builder
|
||||
/// and used to reserve an approximately correct capacity of
|
||||
/// the arrays for the next scene that is getting built.
|
||||
pub struct DocumentStats {
|
||||
pub prim_store_stats: PrimitiveStoreStats,
|
||||
pub hit_test_stats: HitTestingSceneStats,
|
||||
}
|
||||
|
||||
impl DocumentStats {
|
||||
pub fn empty() -> DocumentStats {
|
||||
DocumentStats {
|
||||
prim_store_stats: PrimitiveStoreStats::empty(),
|
||||
hit_test_stats: HitTestingSceneStats::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// A document in the scene builder contains the current scene,
|
||||
// as well as a persistent clip interner. This allows clips
|
||||
// to be de-duplicated, and persisted in the GPU cache between
|
||||
|
@ -253,7 +226,7 @@ impl DocumentStats {
|
|||
struct Document {
|
||||
scene: Scene,
|
||||
interners: Interners,
|
||||
doc_stats: DocumentStats,
|
||||
stats: SceneStats,
|
||||
}
|
||||
|
||||
impl Document {
|
||||
|
@ -261,7 +234,7 @@ impl Document {
|
|||
Document {
|
||||
scene,
|
||||
interners: Interners::default(),
|
||||
doc_stats: DocumentStats::empty(),
|
||||
stats: SceneStats::empty(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -398,30 +371,19 @@ impl SceneBuilderThread {
|
|||
let mut interner_updates = None;
|
||||
|
||||
if item.scene.has_root_pipeline() {
|
||||
let mut clip_scroll_tree = ClipScrollTree::new();
|
||||
let mut new_scene = Scene::new();
|
||||
|
||||
let frame_builder = DisplayListFlattener::create_frame_builder(
|
||||
built_scene = Some(SceneBuilder::build(
|
||||
&item.scene,
|
||||
&mut clip_scroll_tree,
|
||||
item.font_instances,
|
||||
&item.view,
|
||||
&item.output_pipelines,
|
||||
&self.config,
|
||||
&mut new_scene,
|
||||
&mut item.interners,
|
||||
&DocumentStats::empty(),
|
||||
);
|
||||
&SceneStats::empty(),
|
||||
));
|
||||
|
||||
interner_updates = Some(
|
||||
item.interners.end_frame_and_get_pending_updates()
|
||||
);
|
||||
|
||||
built_scene = Some(BuiltScene {
|
||||
scene: new_scene,
|
||||
frame_builder,
|
||||
clip_scroll_tree,
|
||||
});
|
||||
}
|
||||
|
||||
self.documents.insert(
|
||||
|
@ -429,7 +391,7 @@ impl SceneBuilderThread {
|
|||
Document {
|
||||
scene: item.scene,
|
||||
interners: item.interners,
|
||||
doc_stats: DocumentStats::empty(),
|
||||
stats: SceneStats::empty(),
|
||||
},
|
||||
);
|
||||
|
||||
|
@ -493,34 +455,25 @@ impl SceneBuilderThread {
|
|||
let mut interner_updates = None;
|
||||
if scene.has_root_pipeline() {
|
||||
if let Some(request) = txn.request_scene_build.take() {
|
||||
let mut clip_scroll_tree = ClipScrollTree::new();
|
||||
let mut new_scene = Scene::new();
|
||||
|
||||
let frame_builder = DisplayListFlattener::create_frame_builder(
|
||||
let built = SceneBuilder::build(
|
||||
&scene,
|
||||
&mut clip_scroll_tree,
|
||||
request.font_instances,
|
||||
&request.view,
|
||||
&request.output_pipelines,
|
||||
&self.config,
|
||||
&mut new_scene,
|
||||
&mut doc.interners,
|
||||
&doc.doc_stats,
|
||||
&doc.stats,
|
||||
);
|
||||
|
||||
// Update the allocation stats for next scene
|
||||
doc.doc_stats = frame_builder.get_stats();
|
||||
doc.stats = built.get_stats();
|
||||
|
||||
// Retrieve the list of updates from the clip interner.
|
||||
interner_updates = Some(
|
||||
doc.interners.end_frame_and_get_pending_updates()
|
||||
);
|
||||
|
||||
built_scene = Some(BuiltScene {
|
||||
scene: new_scene,
|
||||
frame_builder,
|
||||
clip_scroll_tree,
|
||||
});
|
||||
built_scene = Some(built);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -564,7 +517,7 @@ impl SceneBuilderThread {
|
|||
.filter(|txn| txn.built_scene.is_some())
|
||||
.map(|txn| {
|
||||
txn.built_scene.as_ref().unwrap()
|
||||
.scene.pipeline_epochs.iter()
|
||||
.src.pipeline_epochs.iter()
|
||||
.zip(iter::repeat(txn.document_id))
|
||||
.map(|((&pipeline_id, &epoch), document_id)| ((pipeline_id, document_id), epoch))
|
||||
}).flatten().collect(),
|
||||
|
|
|
@ -14,7 +14,7 @@ use api::{ClipMode, PrimitiveKeyKind, TransformStyle, YuvColorSpace, ColorRange,
|
|||
use api::units::*;
|
||||
use crate::clip::{ClipChainId, ClipRegion, ClipItemKey, ClipStore, ClipItemKeyKind, ClipDataHandle, ClipNodeKind};
|
||||
use crate::clip_scroll_tree::{ROOT_SPATIAL_NODE_INDEX, ClipScrollTree, SpatialNodeIndex};
|
||||
use crate::frame_builder::{ChasePrimitive, FrameBuilder, FrameBuilderConfig};
|
||||
use crate::frame_builder::{ChasePrimitive, FrameBuilderConfig};
|
||||
use crate::glyph_rasterizer::FontInstance;
|
||||
use crate::hit_test::{HitTestingItem, HitTestingScene};
|
||||
use crate::image::simplify_repeated_primitive;
|
||||
|
@ -37,8 +37,8 @@ use crate::prim_store::picture::{Picture, PictureCompositeKey, PictureKey};
|
|||
use crate::prim_store::text_run::TextRun;
|
||||
use crate::render_backend::{DocumentView};
|
||||
use crate::resource_cache::{FontInstanceMap, ImageRequest};
|
||||
use crate::scene::{Scene, StackingContextHelpers};
|
||||
use crate::scene_builder_thread::{DocumentStats, Interners};
|
||||
use crate::scene::{Scene, BuiltScene, SceneStats, StackingContextHelpers};
|
||||
use crate::scene_builder_thread::Interners;
|
||||
use crate::spatial_node::{StickyFrameInfo, ScrollFrameKind};
|
||||
use std::{f32, mem, usize, ops};
|
||||
use std::collections::vec_deque::VecDeque;
|
||||
|
@ -239,15 +239,12 @@ impl CompositeOps {
|
|||
}
|
||||
|
||||
/// A structure that converts a serialized display list into a form that WebRender
|
||||
/// can use to later build a frame. This structure produces a FrameBuilder. Public
|
||||
/// members are typically those that are destructured into the FrameBuilder.
|
||||
pub struct DisplayListFlattener<'a> {
|
||||
/// The scene that we are currently flattening.
|
||||
/// can use to later build a frame. This structure produces a BuiltScene. Public
|
||||
/// members are typically those that are destructured into the BuiltScene.
|
||||
pub struct SceneBuilder<'a> {
|
||||
/// The scene that we are currently building.
|
||||
scene: &'a Scene,
|
||||
|
||||
/// The ClipScrollTree that we are currently building during flattening.
|
||||
clip_scroll_tree: &'a mut ClipScrollTree,
|
||||
|
||||
/// The map of all font instances.
|
||||
font_instances: FontInstanceMap,
|
||||
|
||||
|
@ -255,7 +252,7 @@ pub struct DisplayListFlattener<'a> {
|
|||
/// output textures.
|
||||
output_pipelines: &'a FastHashSet<PipelineId>,
|
||||
|
||||
/// The data structure that converting between ClipId/SpatialId and the various
|
||||
/// The data structure that converts between ClipId/SpatialId and the various
|
||||
/// index types that the ClipScrollTree uses.
|
||||
id_to_index_mapper: NodeIdToIndexMapper,
|
||||
|
||||
|
@ -268,6 +265,9 @@ pub struct DisplayListFlattener<'a> {
|
|||
/// The stack keeping track of the root clip chains associated with pipelines.
|
||||
pipeline_clip_chain_stack: Vec<ClipChainId>,
|
||||
|
||||
/// The ClipScrollTree that we are currently building during building.
|
||||
pub clip_scroll_tree: ClipScrollTree,
|
||||
|
||||
/// The store of primitives.
|
||||
pub prim_store: PrimitiveStore,
|
||||
|
||||
|
@ -284,7 +284,7 @@ pub struct DisplayListFlattener<'a> {
|
|||
/// Reference to the set of data that is interned across display lists.
|
||||
interners: &'a mut Interners,
|
||||
|
||||
/// The root picture index for this flattener. This is the picture
|
||||
/// The root picture index for this builder. This is the picture
|
||||
/// to start the culling phase from.
|
||||
pub root_pic_index: PictureIndex,
|
||||
|
||||
|
@ -295,22 +295,20 @@ pub struct DisplayListFlattener<'a> {
|
|||
external_scroll_mapper: ScrollOffsetMapper,
|
||||
|
||||
/// If true, a stacking context with create_tile_cache set to true was found
|
||||
/// during flattening.
|
||||
/// during building.
|
||||
found_explicit_tile_cache: bool,
|
||||
}
|
||||
|
||||
impl<'a> DisplayListFlattener<'a> {
|
||||
pub fn create_frame_builder(
|
||||
impl<'a> SceneBuilder<'a> {
|
||||
pub fn build(
|
||||
scene: &Scene,
|
||||
clip_scroll_tree: &mut ClipScrollTree,
|
||||
font_instances: FontInstanceMap,
|
||||
view: &DocumentView,
|
||||
output_pipelines: &FastHashSet<PipelineId>,
|
||||
frame_builder_config: &FrameBuilderConfig,
|
||||
new_scene: &mut Scene,
|
||||
interners: &mut Interners,
|
||||
doc_stats: &DocumentStats,
|
||||
) -> FrameBuilder {
|
||||
stats: &SceneStats,
|
||||
) -> BuiltScene {
|
||||
// We checked that the root pipeline is available on the render backend.
|
||||
let root_pipeline_id = scene.root_pipeline_id.unwrap();
|
||||
let root_pipeline = scene.pipelines.get(&root_pipeline_id).unwrap();
|
||||
|
@ -319,18 +317,18 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
.background_color
|
||||
.and_then(|color| if color.a > 0.0 { Some(color) } else { None });
|
||||
|
||||
let mut flattener = DisplayListFlattener {
|
||||
let mut builder = SceneBuilder {
|
||||
scene,
|
||||
clip_scroll_tree,
|
||||
clip_scroll_tree: ClipScrollTree::new(),
|
||||
font_instances,
|
||||
config: *frame_builder_config,
|
||||
output_pipelines,
|
||||
id_to_index_mapper: NodeIdToIndexMapper::default(),
|
||||
hit_testing_scene: HitTestingScene::new(&doc_stats.hit_test_stats),
|
||||
hit_testing_scene: HitTestingScene::new(&stats.hit_test_stats),
|
||||
pending_shadow_items: VecDeque::new(),
|
||||
sc_stack: Vec::new(),
|
||||
pipeline_clip_chain_stack: vec![ClipChainId::NONE],
|
||||
prim_store: PrimitiveStore::new(&doc_stats.prim_store_stats),
|
||||
prim_store: PrimitiveStore::new(&stats.prim_store_stats),
|
||||
clip_store: ClipStore::new(),
|
||||
interners,
|
||||
root_pic_index: PictureIndex(0),
|
||||
|
@ -341,7 +339,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
|
||||
let device_pixel_scale = view.accumulated_scale_factor_for_snapping();
|
||||
|
||||
flattener.push_root(
|
||||
builder.push_root(
|
||||
root_pipeline_id,
|
||||
&root_pipeline.viewport_size,
|
||||
&root_pipeline.content_size,
|
||||
|
@ -359,7 +357,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
// Note that we don't do this for iframes, even if they're pipeline
|
||||
// roots, because they should be entirely contained within a stacking
|
||||
// context, and we probably wouldn't crash if they weren't.
|
||||
flattener.push_stacking_context(
|
||||
builder.push_stacking_context(
|
||||
root_pipeline.pipeline_id,
|
||||
CompositeOps::default(),
|
||||
TransformStyle::Flat,
|
||||
|
@ -372,25 +370,27 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
device_pixel_scale,
|
||||
);
|
||||
|
||||
flattener.flatten_items(
|
||||
builder.flatten_items(
|
||||
&mut root_pipeline.display_list.iter(),
|
||||
root_pipeline.pipeline_id,
|
||||
true,
|
||||
);
|
||||
|
||||
flattener.pop_stacking_context();
|
||||
builder.pop_stacking_context();
|
||||
|
||||
debug_assert!(flattener.sc_stack.is_empty());
|
||||
debug_assert!(builder.sc_stack.is_empty());
|
||||
|
||||
new_scene.root_pipeline_id = Some(root_pipeline_id);
|
||||
new_scene.pipeline_epochs = scene.pipeline_epochs.clone();
|
||||
new_scene.pipelines = scene.pipelines.clone();
|
||||
|
||||
FrameBuilder::with_display_list_flattener(
|
||||
view.device_rect.size.into(),
|
||||
BuiltScene {
|
||||
src: scene.clone(),
|
||||
output_rect: view.device_rect.size.into(),
|
||||
background_color,
|
||||
flattener,
|
||||
)
|
||||
hit_testing_scene: Arc::new(builder.hit_testing_scene),
|
||||
clip_scroll_tree: builder.clip_scroll_tree,
|
||||
prim_store: builder.prim_store,
|
||||
clip_store: builder.clip_store,
|
||||
root_pic_index: builder.root_pic_index,
|
||||
config: builder.config,
|
||||
}
|
||||
}
|
||||
|
||||
/// Retrieve the current offset to allow converting a stacking context
|
||||
|
@ -1006,7 +1006,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device;
|
||||
snap_to_device.set_target_spatial_node(
|
||||
spatial_node_index,
|
||||
self.clip_scroll_tree,
|
||||
&self.clip_scroll_tree,
|
||||
);
|
||||
|
||||
let bounds = snap_to_device.snap_rect(
|
||||
|
@ -1101,7 +1101,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device;
|
||||
snap_to_device.set_target_spatial_node(
|
||||
clip_and_scroll.spatial_node_index,
|
||||
self.clip_scroll_tree
|
||||
&self.clip_scroll_tree
|
||||
);
|
||||
|
||||
let clip_rect = common.clip_rect.translate(current_offset);
|
||||
|
@ -1125,7 +1125,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device;
|
||||
snap_to_device.set_target_spatial_node(
|
||||
target_spatial_node,
|
||||
self.clip_scroll_tree
|
||||
&self.clip_scroll_tree
|
||||
);
|
||||
snap_to_device.snap_rect(rect)
|
||||
}
|
||||
|
@ -2340,7 +2340,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
spatial_node_index,
|
||||
ROOT_SPATIAL_NODE_INDEX,
|
||||
device_pixel_scale,
|
||||
self.clip_scroll_tree,
|
||||
&self.clip_scroll_tree,
|
||||
);
|
||||
|
||||
let content_size = snap_to_device.snap_size(content_size);
|
||||
|
@ -2381,7 +2381,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device;
|
||||
snap_to_device.set_target_spatial_node(
|
||||
spatial_node_index,
|
||||
self.clip_scroll_tree,
|
||||
&self.clip_scroll_tree,
|
||||
);
|
||||
|
||||
let snapped_clip_rect = snap_to_device.snap_rect(&clip_region.main);
|
||||
|
@ -2713,7 +2713,7 @@ impl<'a> DisplayListFlattener<'a> {
|
|||
let snap_to_device = &mut self.sc_stack.last_mut().unwrap().snap_to_device;
|
||||
snap_to_device.set_target_spatial_node(
|
||||
pending_primitive.clip_and_scroll.spatial_node_index,
|
||||
self.clip_scroll_tree
|
||||
&self.clip_scroll_tree,
|
||||
);
|
||||
|
||||
// Offset the local rect and clip rect by the shadow offset. The pending
|
Загрузка…
Ссылка в новой задаче