Bug 1511661 - Update webrender to commit 1619d945e853db14a9d62ed75dce7216ff3cdbc2 (WR PR #3366). r=kats
authorWR Updater Bot <graphics-team@mozilla.staktrace.com>
Sun, 02 Dec 2018 00:37:23 +0000
changeset 505550 df8c8aa616045a2076d1f0c8b1ebedf322aec212
parent 505549 be65d09ef059e78daf60df9737503cfcb6dc8a86
child 505551 ebc8ca6c03b47d2a9b560d764bac2bfbca941ba1
push id10290
push userffxbld-merge
push dateMon, 03 Dec 2018 16:23:23 +0000
treeherdermozilla-beta@700bed2445e6 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewerskats
bugs1511661
milestone65.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1511661 - Update webrender to commit 1619d945e853db14a9d62ed75dce7216ff3cdbc2 (WR PR #3366). r=kats https://github.com/servo/webrender/pull/3366 Differential Revision: https://phabricator.services.mozilla.com/D13627
gfx/webrender_bindings/revision.txt
gfx/wr/webrender/src/intern.rs
gfx/wr/webrender/src/util.rs
--- a/gfx/webrender_bindings/revision.txt
+++ b/gfx/webrender_bindings/revision.txt
@@ -1,1 +1,1 @@
-dbaa10971f08f964120ba339f5b0ab3e7ace77d6
+1619d945e853db14a9d62ed75dce7216ff3cdbc2
--- a/gfx/wr/webrender/src/intern.rs
+++ b/gfx/wr/webrender/src/intern.rs
@@ -1,19 +1,18 @@
 /* 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 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;
 
 /*
 
  The interning module provides a generic data structure
  interning container. It is similar in concept to a
  traditional string interning container, but it is
  specialized to the WR thread model.
 
@@ -55,17 +54,19 @@ impl Epoch {
 }
 
 /// A list of updates to be applied to the data store,
 /// provided by the interning structure.
 pub struct UpdateList<S> {
     /// The current epoch of the scene builder.
     epoch: Epoch,
     /// The additions and removals to apply.
-    updates: Vec<Update<S>>,
+    updates: Vec<Update>,
+    /// Actual new data to insert.
+    data: Vec<S>,
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 #[derive(Debug, Copy, Clone, Eq, Hash, PartialEq)]
 pub struct ItemUid<T> {
     uid: usize,
     _marker: PhantomData<T>,
@@ -84,27 +85,27 @@ pub struct Handle<T> {
 impl <T> Handle<T> where T: Copy {
     pub fn uid(&self) -> ItemUid<T> {
         self.uid
     }
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub enum UpdateKind<S> {
-    Insert(S),
+pub enum UpdateKind {
+    Insert,
     Remove,
     UpdateEpoch,
 }
 
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
-pub struct Update<S> {
+pub struct Update {
     index: usize,
-    kind: UpdateKind<S>,
+    kind: UpdateKind,
 }
 
 /// The data item is stored with an epoch, for validating
 /// correct access patterns.
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 struct Item<T> {
     epoch: Epoch,
@@ -132,37 +133,34 @@ impl<S, T, M> DataStore<S, T, M> where S
     }
 
     /// Apply any updates from the scene builder thread to
     /// this data store.
     pub fn apply_updates(
         &mut self,
         update_list: UpdateList<S>,
     ) {
+        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;
                 }
                 UpdateKind::UpdateEpoch => {
                     self.items[update.index].epoch = update_list.epoch;
                 }
             }
         }
+        debug_assert!(data_iter.next().is_none());
     }
 }
 
 /// Retrieve an item from the store via handle
 impl<S, T, M> ops::Index<Handle<M>> for DataStore<S, T, M> {
     type Output = T;
     fn index(&self, handle: Handle<M>) -> &T {
         let item = &self.items[handle.index as usize];
@@ -189,33 +187,36 @@ impl<S, T, M> ops::IndexMut<Handle<M>> f
 #[cfg_attr(feature = "capture", derive(Serialize))]
 #[cfg_attr(feature = "replay", derive(Deserialize))]
 pub struct Interner<S : Eq + Hash + Clone + Debug, D, M> {
     /// Uniquely map an interning key to a handle
     map: FastHashMap<S, Handle<M>>,
     /// List of free slots in the data store for re-use.
     free_list: Vec<usize>,
     /// Pending list of updates that need to be applied.
-    updates: Vec<Update<S>>,
+    updates: Vec<Update>,
+    /// Pending new data to insert.
+    update_data: Vec<S>,
     /// The current epoch for the interner.
     current_epoch: Epoch,
     /// Incrementing counter for identifying stable values.
     next_uid: usize,
     /// The information associated with each interned
     /// item that can be accessed by the interner.
     local_data: Vec<Item<D>>,
 }
 
 impl<S, D, M> Interner<S, D, M> where S: Eq + Hash + Clone + Debug, M: Copy + Debug {
     /// Construct a new interner
     pub fn new() -> Self {
         Interner {
             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(),
         }
     }
 
     /// Intern a data structure, and return a handle to
     /// that data. The handle can then be stored in the
@@ -254,18 +255,19 @@ impl<S, D, M> Interner<S, D, M> where S:
         let index = match self.free_list.pop() {
             Some(index) => index,
             None => self.local_data.len(),
         };
 
         // 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 {
             index: index as u32,
             epoch: self.current_epoch,
             uid: ItemUid {
                 uid: self.next_uid,
                 _marker: PhantomData,
@@ -275,34 +277,31 @@ impl<S, D, M> Interner<S, D, M> where S:
 
         // Store this handle so the next time it is
         // interned, it gets re-used.
         self.map.insert(data.clone(), handle);
         self.next_uid += 1;
 
         // 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
     }
 
     /// Retrieve the pending list of updates for an interner
     /// that need to be applied to the data store. Also run
     /// a GC step that removes old entries.
     pub fn end_frame_and_get_pending_updates(&mut self) -> UpdateList<S> {
         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;
 
         // First, run a GC step. Walk through the handles, and
         // if we find any that haven't been used for some time,
         // remove them. If this ever shows up in profiles, we
         // can make the GC step partial (scan only part of the
         // map each frame). It also might make sense in the
@@ -322,16 +321,17 @@ impl<S, D, M> Interner<S, D, M> where S:
                 return false;
             }
 
             true
         });
 
         let updates = UpdateList {
             updates,
+            data,
             epoch: self.current_epoch,
         };
 
         // Begin the next epoch
         self.current_epoch = Epoch(self.current_epoch.0 + 1);
 
         updates
     }
--- a/gfx/wr/webrender/src/util.rs
+++ b/gfx/wr/webrender/src/util.rs
@@ -12,17 +12,16 @@ use std::{i32, f32, fmt, ptr};
 use std::borrow::Cow;
 
 
 // Matches the definition of SK_ScalarNearlyZero in Skia.
 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<T>,
     index: usize,
 }
 
 impl<'a, T> Allocation<'a, T> {
     // writing is safe because alloc() ensured enough capacity
     // and `Allocation` holds a mutable borrow to prevent anyone else
@@ -32,31 +31,62 @@ impl<'a, T> Allocation<'a, T> {
         unsafe {
             ptr::write(self.vec.as_mut_ptr().add(self.index), value);
             self.vec.set_len(self.index + 1);
         }
         self.index
     }
 }
 
+/// 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<T> {
+    /// Growns the vector by a single entry, returning the allocation.
     fn alloc(&mut self) -> Allocation<T>;
+    /// 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<T>;
 }
 
 impl<T> VecHelper<T> for Vec<T> {
     fn alloc(&mut self) -> Allocation<T> {
         let index = self.len();
         if self.capacity() == index {
             self.reserve(1);
         }
         Allocation {
             vec: self,
             index,
         }
     }
+
+    fn entry(&mut self, index: usize) -> VecEntry<T> {
+        if index < self.len() {
+            VecEntry::Occupied(unsafe {
+                self.get_unchecked_mut(index)
+            })
+        } else {
+            assert_eq!(index, self.len());
+            VecEntry::Vacant(self.alloc())
+        }
+    }
 }
 
 
 // Represents an optimized transform where there is only
 // a scale and translation (which are guaranteed to maintain
 // an axis align rectangle under transformation). The
 // scaling is applied first, followed by the translation.
 // TODO(gw): We should try and incorporate F <-> T units here,