diff options
author | Jack Moffitt <jack@metajack.im> | 2014-08-28 09:34:23 -0600 |
---|---|---|
committer | Jack Moffitt <jack@metajack.im> | 2014-09-08 20:21:42 -0600 |
commit | c6ab60dbfc6da7b4f800c9e40893c8b58413960c (patch) | |
tree | d1d74076cf7fa20e4f77ec7cb82cae98b67362cb /components/gfx/buffer_map.rs | |
parent | db2f642c32fc5bed445bb6f2e45b0f6f0b4342cf (diff) | |
download | servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.tar.gz servo-c6ab60dbfc6da7b4f800c9e40893c8b58413960c.zip |
Cargoify servo
Diffstat (limited to 'components/gfx/buffer_map.rs')
-rw-r--r-- | components/gfx/buffer_map.rs | 156 |
1 files changed, 156 insertions, 0 deletions
diff --git a/components/gfx/buffer_map.rs b/components/gfx/buffer_map.rs new file mode 100644 index 00000000000..0551385f717 --- /dev/null +++ b/components/gfx/buffer_map.rs @@ -0,0 +1,156 @@ +/* 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 std::collections::hashmap::HashMap; +use geom::size::Size2D; +use layers::platform::surface::NativePaintingGraphicsContext; +use layers::layers::LayerBuffer; +use std::hash::Hash; +use std::hash::sip::SipState; +use std::mem; + +/// This is a struct used to store buffers when they are not in use. +/// The render task can quickly query for a particular size of buffer when it +/// needs it. +pub struct BufferMap { + /// A HashMap that stores the Buffers. + map: HashMap<BufferKey, BufferValue>, + /// The current amount of memory stored by the BufferMap's buffers. + mem: uint, + /// The maximum allowed memory. Unused buffers will be deleted + /// when this threshold is exceeded. + max_mem: uint, + /// A monotonically increasing counter to track how recently tile sizes were used. + counter: uint, +} + +/// A key with which to store buffers. It is based on the size of the buffer. +#[deriving(Eq)] +struct BufferKey([uint, ..2]); + +impl Hash for BufferKey { + fn hash(&self, state: &mut SipState) { + let BufferKey(ref bytes) = *self; + bytes.as_slice().hash(state); + } +} + +impl PartialEq for BufferKey { + fn eq(&self, other: &BufferKey) -> bool { + let BufferKey(s) = *self; + let BufferKey(o) = *other; + s[0] == o[0] && s[1] == o[1] + } +} + +/// Create a key from a given size +impl BufferKey { + fn get(input: Size2D<uint>) -> BufferKey { + BufferKey([input.width, input.height]) + } +} + +/// A helper struct to keep track of buffers in the HashMap +struct BufferValue { + /// An array of buffers, all the same size + buffers: Vec<Box<LayerBuffer>>, + /// The counter when this size was last requested + last_action: uint, +} + +impl BufferMap { + // Creates a new BufferMap with a given buffer limit. + pub fn new(max_mem: uint) -> BufferMap { + BufferMap { + map: HashMap::new(), + mem: 0u, + max_mem: max_mem, + counter: 0u, + } + } + + /// Insert a new buffer into the map. + pub fn insert(&mut self, graphics_context: &NativePaintingGraphicsContext, new_buffer: Box<LayerBuffer>) { + let new_key = BufferKey::get(new_buffer.get_size_2d()); + + // If all our buffers are the same size and we're already at our + // memory limit, no need to store this new buffer; just let it drop. + if self.mem + new_buffer.get_mem() > self.max_mem && self.map.len() == 1 && + self.map.contains_key(&new_key) { + new_buffer.destroy(graphics_context); + return; + } + + self.mem += new_buffer.get_mem(); + // use lazy insertion function to prevent unnecessary allocation + let counter = &self.counter; + self.map.find_or_insert_with(new_key, |_| BufferValue { + buffers: vec!(), + last_action: *counter + }).buffers.push(new_buffer); + + let mut opt_key: Option<BufferKey> = None; + while self.mem > self.max_mem { + let old_key = match opt_key { + Some(key) => key, + None => { + match self.map.iter().min_by(|&(_, x)| x.last_action) { + Some((k, _)) => *k, + None => fail!("BufferMap: tried to delete with no elements in map"), + } + } + }; + if { + let list = &mut self.map.get_mut(&old_key).buffers; + let condemned_buffer = list.pop().take_unwrap(); + self.mem -= condemned_buffer.get_mem(); + condemned_buffer.destroy(graphics_context); + list.is_empty() + } + { // then + self.map.pop(&old_key); // Don't store empty vectors! + opt_key = None; + } else { + opt_key = Some(old_key); + } + } + } + + // Try to find a buffer for the given size. + pub fn find(&mut self, size: Size2D<uint>) -> Option<Box<LayerBuffer>> { + let mut flag = false; // True if key needs to be popped after retrieval. + let key = BufferKey::get(size); + let ret = match self.map.find_mut(&key) { + Some(ref mut buffer_val) => { + buffer_val.last_action = self.counter; + self.counter += 1; + + let buffer = buffer_val.buffers.pop().take_unwrap(); + self.mem -= buffer.get_mem(); + if buffer_val.buffers.is_empty() { + flag = true; + } + Some(buffer) + } + None => None, + }; + + if flag { + self.map.pop(&key); // Don't store empty vectors! + } + + ret + } + + /// Destroys all buffers. + pub fn clear(&mut self, graphics_context: &NativePaintingGraphicsContext) { + let map = mem::replace(&mut self.map, HashMap::new()); + for (_, value) in map.move_iter() { + for tile in value.buffers.move_iter() { + tile.destroy(graphics_context) + } + } + self.mem = 0 + } +} |