diff --git a/gfx/webrender_bindings/revision.txt b/gfx/webrender_bindings/revision.txt index e67fee83e0851..39c30d5c8473f 100644 --- a/gfx/webrender_bindings/revision.txt +++ b/gfx/webrender_bindings/revision.txt @@ -1 +1 @@ -dbaa10971f08f964120ba339f5b0ab3e7ace77d6 +1619d945e853db14a9d62ed75dce7216ff3cdbc2 diff --git a/gfx/wr/webrender/src/intern.rs b/gfx/wr/webrender/src/intern.rs index 2c4162ce568fa..e235b456cb24a 100644 --- a/gfx/wr/webrender/src/intern.rs +++ b/gfx/wr/webrender/src/intern.rs @@ -6,9 +6,8 @@ use internal_types::FastHashMap; use std::fmt::Debug; use std::hash::Hash; use std::marker::PhantomData; -use std::mem; -use std::ops; -use std::u64; +use std::{mem, ops, u64}; +use util::VecHelper; /* @@ -60,7 +59,9 @@ pub struct UpdateList { /// The current epoch of the scene builder. epoch: Epoch, /// The additions and removals to apply. - updates: Vec>, + updates: Vec, + /// Actual new data to insert. + data: Vec, } #[cfg_attr(feature = "capture", derive(Serialize))] @@ -89,17 +90,17 @@ impl Handle where T: Copy { #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub enum UpdateKind { - Insert(S), +pub enum UpdateKind { + Insert, Remove, UpdateEpoch, } #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] -pub struct Update { +pub struct Update { index: usize, - kind: UpdateKind, + kind: UpdateKind, } /// The data item is stored with an epoch, for validating @@ -137,18 +138,14 @@ impl DataStore where S: Debug, T: From, M: Debug { &mut self, update_list: UpdateList, ) { + let mut data_iter = update_list.data.into_iter(); for update in update_list.updates { match update.kind { - UpdateKind::Insert(data) => { - let item = Item { - data: T::from(data), + UpdateKind::Insert => { + self.items.entry(update.index).set(Item { + data: T::from(data_iter.next().unwrap()), epoch: update_list.epoch, - }; - if self.items.len() == update.index { - self.items.push(item) - } else { - self.items[update.index] = item; - } + }); } UpdateKind::Remove => { self.items[update.index].epoch = Epoch::INVALID; @@ -158,6 +155,7 @@ impl DataStore where S: Debug, T: From, M: Debug { } } } + debug_assert!(data_iter.next().is_none()); } } @@ -194,7 +192,9 @@ pub struct Interner { /// List of free slots in the data store for re-use. free_list: Vec, /// Pending list of updates that need to be applied. - updates: Vec>, + updates: Vec, + /// Pending new data to insert. + update_data: Vec, /// The current epoch for the interner. current_epoch: Epoch, /// Incrementing counter for identifying stable values. @@ -211,6 +211,7 @@ impl Interner where S: Eq + Hash + Clone + Debug, M: Copy + De map: FastHashMap::default(), free_list: Vec::new(), updates: Vec::new(), + update_data: Vec::new(), current_epoch: Epoch(1), next_uid: 0, local_data: Vec::new(), @@ -259,8 +260,9 @@ impl Interner where S: Eq + Hash + Clone + Debug, M: Copy + De // Add a pending update to insert the new data. self.updates.push(Update { index, - kind: UpdateKind::Insert(data.clone()), + kind: UpdateKind::Insert, }); + self.update_data.alloc().init(data.clone()); // Generate a handle for access via the data store. let handle = Handle { @@ -280,15 +282,10 @@ impl Interner where S: Eq + Hash + Clone + Debug, M: Copy + De // Create the local data for this item that is // being interned. - let local_item = Item { + self.local_data.entry(index).set(Item { epoch: self.current_epoch, data: f(), - }; - if self.local_data.len() == index { - self.local_data.push(local_item); - } else { - self.local_data[index] = local_item; - } + }); handle } @@ -298,6 +295,8 @@ impl Interner where S: Eq + Hash + Clone + Debug, M: Copy + De /// a GC step that removes old entries. pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList { let mut updates = mem::replace(&mut self.updates, Vec::new()); + let data = mem::replace(&mut self.update_data, Vec::new()); + let free_list = &mut self.free_list; let current_epoch = self.current_epoch.0; @@ -327,6 +326,7 @@ impl Interner where S: Eq + Hash + Clone + Debug, M: Copy + De let updates = UpdateList { updates, + data, epoch: self.current_epoch, }; diff --git a/gfx/wr/webrender/src/util.rs b/gfx/wr/webrender/src/util.rs index 3fa87b3733477..298bc885793dc 100644 --- a/gfx/wr/webrender/src/util.rs +++ b/gfx/wr/webrender/src/util.rs @@ -17,7 +17,6 @@ const NEARLY_ZERO: f32 = 1.0 / 4096.0; /// A typesafe helper that separates new value construction from /// vector growing, allowing LLVM to ideally construct the element in place. -#[must_use] pub struct Allocation<'a, T: 'a> { vec: &'a mut Vec, index: usize, @@ -37,8 +36,28 @@ impl<'a, T> Allocation<'a, T> { } } +/// An entry into a vector, similar to `std::collections::hash_map::Entry`. +pub enum VecEntry<'a, T: 'a> { + Vacant(Allocation<'a, T>), + Occupied(&'a mut T), +} + +impl<'a, T> VecEntry<'a, T> { + #[inline(always)] + pub fn set(self, value: T) { + match self { + VecEntry::Vacant(alloc) => { alloc.init(value); } + VecEntry::Occupied(slot) => { *slot = value; } + } + } +} + pub trait VecHelper { + /// Growns the vector by a single entry, returning the allocation. fn alloc(&mut self) -> Allocation; + /// Either returns an existing elemenet, or grows the vector by one. + /// Doesn't expect indices to be higher than the current length. + fn entry(&mut self, index: usize) -> VecEntry; } impl VecHelper for Vec { @@ -52,6 +71,17 @@ impl VecHelper for Vec { index, } } + + fn entry(&mut self, index: usize) -> VecEntry { + if index < self.len() { + VecEntry::Occupied(unsafe { + self.get_unchecked_mut(index) + }) + } else { + assert_eq!(index, self.len()); + VecEntry::Vacant(self.alloc()) + } + } }