use api::ColorF;
use api::PropertyBindingId;
use api::units::*;
use smallvec::SmallVec;
use crate::composite::CompositeState;
use crate::internal_types::{FastHashMap, FrameId};
use crate::invalidation::compare::ImageDependency;
use crate::invalidation::compare::{ColorBinding, OpacityBinding, OpacityBindingInfo, PrimitiveComparisonKey};
use crate::invalidation::compare::{PrimitiveComparer, PrimitiveDependency, ColorBindingInfo};
use crate::invalidation::{InvalidationReason, PrimitiveCompareResult, quadtree::TileNode};
use crate::invalidation::vert_buffer::{CornersCache, VertRange};
use crate::intern::ItemUid;
use crate::picture::{PictureCompositeMode, SurfaceIndex, clampf};
use crate::print_tree::PrintTreePrinter;
use crate::resource_cache::ResourceCache;
use crate::space::SpaceMapper;
use crate::visibility::FrameVisibilityContext;
use peek_poke::poke_into_vec;
use std::mem;
pub struct CachedSurface {
pub current_descriptor: CachedSurfaceDescriptor,
pub prev_descriptor: CachedSurfaceDescriptor,
pub is_valid: bool,
pub local_valid_rect: PictureBox2D,
pub local_dirty_rect: PictureRect,
pub local_rect: PictureRect,
pub root: TileNode,
pub background_color: Option<ColorF>,
pub invalidation_reason: Option<InvalidationReason>,
pub sub_graphs: Vec<(PictureRect, Vec<(PictureCompositeMode, SurfaceIndex)>)>,
}
impl CachedSurface {
pub fn new() -> Self {
CachedSurface {
current_descriptor: CachedSurfaceDescriptor::new(),
prev_descriptor: CachedSurfaceDescriptor::new(),
is_valid: false,
local_valid_rect: PictureBox2D::zero(),
local_dirty_rect: PictureRect::zero(),
local_rect: PictureRect::zero(),
root: TileNode::new_leaf(Vec::new()),
background_color: None,
invalidation_reason: None,
sub_graphs: Vec::new(),
}
}
pub fn print(&self, pt: &mut dyn PrintTreePrinter) {
pt.add_item(format!("background_color: {:?}", self.background_color));
pt.add_item(format!("invalidation_reason: {:?}", self.invalidation_reason));
self.current_descriptor.print(pt);
}
pub fn pre_update(
&mut self,
background_color: Option<ColorF>,
local_tile_rect: PictureRect,
frame_id: FrameId,
is_visible: bool,
) {
self.local_valid_rect = PictureBox2D::new(
PicturePoint::new( 1.0e32, 1.0e32),
PicturePoint::new(-1.0e32, -1.0e32),
);
self.invalidation_reason = None;
self.sub_graphs.clear();
if !is_visible {
return;
}
if background_color != self.background_color {
self.invalidate(None, InvalidationReason::BackgroundColor);
self.background_color = background_color;
}
mem::swap(
&mut self.current_descriptor,
&mut self.prev_descriptor,
);
self.current_descriptor.clear();
self.root.clear(local_tile_rect);
self.current_descriptor.last_updated_frame_id = frame_id;
}
pub fn add_prim_dependency(
&mut self,
info: &PrimitiveDependencyInfo,
corners_cache: &CornersCache,
prim_clamp_to_tile: bool,
local_raster_rect: &RasterRect,
local_tile_rect: PictureRect,
) {
self.local_valid_rect = self.local_valid_rect.union(&info.prim_clip_box);
let pmin = local_tile_rect.min;
let pmax = local_tile_rect.max;
let prim_clip_box = PictureBox2D::new(
PicturePoint::new(
clampf(info.prim_clip_box.min.x, pmin.x, pmax.x),
clampf(info.prim_clip_box.min.y, pmin.y, pmax.y),
),
PicturePoint::new(
clampf(info.prim_clip_box.max.x, pmin.x, pmax.x),
clampf(info.prim_clip_box.max.y, pmin.y, pmax.y),
),
);
let vert_data = &mut self.current_descriptor.vert_data;
let (prim_corners, coverage_corners) = if prim_clamp_to_tile {
(
corners_cache.push_verts_clamped(info.prim_scratch, local_raster_rect, vert_data),
corners_cache.push_verts_clamped(info.cov_scratch, local_raster_rect, vert_data),
)
} else {
(
corners_cache.push_verts(info.prim_scratch, vert_data),
corners_cache.push_verts(info.cov_scratch, vert_data),
)
};
let prim_index = PrimitiveDependencyIndex(self.current_descriptor.prims.len() as u32);
let dep_offset = self.current_descriptor.dep_data.len() as u32;
let mut dep_count = 0;
for &(clip_uid, clip_scratch) in info.clips.iter() {
dep_count += 1;
poke_into_vec(
&PrimitiveDependency::Clip { prim_uid: clip_uid, vert_range: corners_cache.push_verts(clip_scratch, vert_data) },
&mut self.current_descriptor.dep_data,
);
}
for image in &info.images {
dep_count += 1;
poke_into_vec(
&PrimitiveDependency::Image {
image: *image,
},
&mut self.current_descriptor.dep_data,
);
}
for binding in &info.opacity_bindings {
dep_count += 1;
poke_into_vec(
&PrimitiveDependency::OpacityBinding {
binding: *binding,
},
&mut self.current_descriptor.dep_data,
);
}
if let Some(ref binding) = info.color_binding {
dep_count += 1;
poke_into_vec(
&PrimitiveDependency::ColorBinding {
binding: *binding,
},
&mut self.current_descriptor.dep_data,
);
}
self.current_descriptor.prims.push(PrimitiveDescriptor {
prim_clip_box,
dep_offset,
dep_count,
prim_uid: info.prim_uid,
prim_corners,
coverage_corners,
});
self.root.add_prim(prim_index, &info.prim_clip_box);
}
fn update_dirty_rects(
&mut self,
ctx: &TileUpdateDirtyContext,
state: &mut TileUpdateDirtyState,
invalidation_reason: &mut Option<InvalidationReason>,
frame_context: &FrameVisibilityContext,
) -> PictureRect {
let mut prim_comparer = PrimitiveComparer::new(
&self.prev_descriptor,
&self.current_descriptor,
state.resource_cache,
ctx.opacity_bindings,
ctx.color_bindings,
);
let mut dirty_rect = PictureBox2D::zero();
self.root.update_dirty_rects(
&self.prev_descriptor.prims,
&self.current_descriptor.prims,
&mut prim_comparer,
&mut dirty_rect,
state.compare_cache,
invalidation_reason,
frame_context,
);
dirty_rect
}
pub fn update_content_validity(
&mut self,
ctx: &TileUpdateDirtyContext,
state: &mut TileUpdateDirtyState,
frame_context: &FrameVisibilityContext,
) {
state.compare_cache.clear();
let mut invalidation_reason = None;
let dirty_rect = self.update_dirty_rects(
ctx,
state,
&mut invalidation_reason,
frame_context,
);
if !dirty_rect.is_empty() {
self.invalidate(
Some(dirty_rect),
invalidation_reason.expect("bug: no invalidation_reason")
);
}
if ctx.invalidate_all {
self.invalidate(None, InvalidationReason::ScaleChanged);
}
if self.current_descriptor.local_valid_rect != self.prev_descriptor.local_valid_rect {
self.invalidate(None, InvalidationReason::ValidRectChanged);
state.composite_state.dirty_rects_are_valid = false;
}
}
pub fn invalidate(
&mut self,
invalidation_rect: Option<PictureRect>,
reason: InvalidationReason,
) {
self.is_valid = false;
match invalidation_rect {
Some(rect) => {
self.local_dirty_rect = self.local_dirty_rect.union(&rect);
}
None => {
self.local_dirty_rect = self.local_rect;
}
}
if self.invalidation_reason.is_none() {
self.invalidation_reason = Some(reason);
}
}
}
pub struct TileUpdateDirtyContext<'a> {
pub pic_to_world_mapper: SpaceMapper<PicturePixel, WorldPixel>,
pub global_device_pixel_scale: DevicePixelScale,
pub opacity_bindings: &'a FastHashMap<PropertyBindingId, OpacityBindingInfo>,
pub color_bindings: &'a FastHashMap<PropertyBindingId, ColorBindingInfo>,
pub local_rect: PictureRect,
pub invalidate_all: bool,
}
pub struct TileUpdateDirtyState<'a> {
pub resource_cache: &'a mut ResourceCache,
pub composite_state: &'a mut CompositeState,
pub compare_cache: &'a mut FastHashMap<PrimitiveComparisonKey, PrimitiveCompareResult>,
}
pub struct PrimitiveDependencyInfo {
pub prim_clip_box: PictureBox2D,
pub images: SmallVec<[ImageDependency; 8]>,
pub opacity_bindings: SmallVec<[OpacityBinding; 4]>,
pub color_binding: Option<ColorBinding>,
pub prim_uid: ItemUid,
pub prim_scratch: VertRange,
pub cov_scratch: VertRange,
pub clips: SmallVec<[(ItemUid, VertRange); 4]>,
}
impl PrimitiveDependencyInfo {
pub fn new(prim_uid: ItemUid, prim_clip_box: PictureBox2D) -> Self {
PrimitiveDependencyInfo {
prim_clip_box,
images: smallvec::SmallVec::new(),
opacity_bindings: smallvec::SmallVec::new(),
color_binding: None,
prim_uid,
prim_scratch: VertRange::INVALID,
cov_scratch: VertRange::INVALID,
clips: smallvec::SmallVec::new(),
}
}
}
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveDescriptor {
pub prim_clip_box: PictureBox2D,
pub dep_offset: u32,
pub dep_count: u32,
pub prim_uid: ItemUid,
pub prim_corners: VertRange,
pub coverage_corners: VertRange,
}
#[derive(Debug, Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct PrimitiveDependencyIndex(pub u32);
#[cfg_attr(any(feature="capture",feature="replay"), derive(Clone))]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CachedSurfaceDescriptor {
pub prims: Vec<PrimitiveDescriptor>,
pub local_valid_rect: PictureRect,
pub last_updated_frame_id: FrameId,
pub dep_data: Vec<u8>,
pub vert_data: Vec<i32>,
}
impl CachedSurfaceDescriptor {
pub fn new() -> Self {
CachedSurfaceDescriptor {
local_valid_rect: PictureRect::zero(),
dep_data: Vec::new(),
vert_data: Vec::new(),
prims: Vec::new(),
last_updated_frame_id: FrameId::INVALID,
}
}
pub fn print(&self, pt: &mut dyn crate::print_tree::PrintTreePrinter) {
pt.new_level("current_descriptor".to_string());
pt.new_level("prims".to_string());
for prim in &self.prims {
pt.new_level(format!("prim uid={}", prim.prim_uid.get_uid()));
pt.add_item(format!("clip: p0={},{} p1={},{}",
prim.prim_clip_box.min.x,
prim.prim_clip_box.min.y,
prim.prim_clip_box.max.x,
prim.prim_clip_box.max.y,
));
pt.end_level();
}
pt.end_level();
pt.end_level();
}
pub fn clear(&mut self) {
self.local_valid_rect = PictureRect::zero();
self.prims.clear();
self.dep_data.clear();
self.vert_data.clear();
}
}