Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


Quelle  resource_cache.rs   Sprache: unbekannt

 
/* 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 api::{BlobImageRequest, ImageDescriptorFlags, ImageFormat, RasterizedBlobImage};
use api::{DebugFlags, FontInstanceKey, FontKey, FontTemplate, GlyphIndex};
use api::{ExternalImageData, ExternalImageType, ExternalImageId, BlobImageResult};
use api::{DirtyRect, GlyphDimensions, IdNamespace, DEFAULT_TILE_SIZE};
use api::{ColorF, ImageData, ImageDescriptor, ImageKey, ImageRendering, TileSize};
use api::{BlobImageHandler, BlobImageKey, VoidPtrToSizeFn};
use api::units::*;
use euclid::size2;
use crate::render_target::RenderTargetKind;
use crate::render_task::{RenderTaskLocation, StaticRenderTaskSurface};
use crate::{render_api::{ClearCache, AddFont, ResourceUpdate, MemoryReport}, util::WeakTable};
use crate::prim_store::image::AdjustedImageSource;
use crate::image_tiling::{compute_tile_size, compute_tile_range};
#[cfg(feature = "capture")]
use crate::capture::ExternalCaptureImage;
#[cfg(feature = "replay")]
use crate::capture::PlainExternalImage;
#[cfg(any(feature = "replay", feature = "png", feature="capture"))]
use crate::capture::CaptureConfig;
use crate::composite::{NativeSurfaceId, NativeSurfaceOperation, NativeTileId, NativeSurfaceOperationDetails};
use crate::device::TextureFilter;
use crate::glyph_cache::{GlyphCache, CachedGlyphInfo};
use crate::glyph_cache::GlyphCacheEntry;
use glyph_rasterizer::{GLYPH_FLASHING, FontInstance, GlyphFormat, GlyphKey, GlyphRasterizer, GlyphRasterJob};
use glyph_rasterizer::{SharedFontResources, BaseFontInstance};
use crate::gpu_cache::{GpuCache, GpuCacheAddress, GpuCacheHandle};
use crate::gpu_types::UvRectKind;
use crate::internal_types::{
    CacheTextureId, FastHashMap, FastHashSet, TextureSource, ResourceUpdateList,
    FrameId, FrameStamp,
};
use crate::profiler::{self, TransactionProfile, bytes_to_mb};
use crate::render_task_graph::{RenderTaskId, RenderTaskGraphBuilder};
use crate::render_task_cache::{RenderTaskCache, RenderTaskCacheKey, RenderTaskParent};
use crate::render_task_cache::{RenderTaskCacheEntry, RenderTaskCacheEntryHandle};
use crate::renderer::GpuBufferBuilderF;
use crate::surface::SurfaceBuilder;
use euclid::point2;
use smallvec::SmallVec;
use std::collections::hash_map::Entry::{self, Occupied, Vacant};
use std::collections::hash_map::{Iter, IterMut};
use std::collections::VecDeque;
use std::{cmp, mem};
use std::fmt::Debug;
use std::hash::Hash;
use std::os::raw::c_void;
#[cfg(any(feature = "capture", feature = "replay"))]
use std::path::PathBuf;
use std::sync::Arc;
use std::sync::atomic::{AtomicUsize, Ordering};
use std::u32;
use crate::texture_cache::{TextureCache, TextureCacheHandle, Eviction, TargetShader};
use crate::picture_textures::PictureTextures;
use peek_poke::PeekPoke;

// Counter for generating unique native surface ids
static NEXT_NATIVE_SURFACE_ID: AtomicUsize = AtomicUsize::new(0);

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct GlyphFetchResult {
    pub index_in_text_run: i32,
    pub uv_rect_address: GpuCacheAddress,
    pub offset: DevicePoint,
    pub size: DeviceIntSize,
    pub scale: f32,
}

// These coordinates are always in texels.
// They are converted to normalized ST
// values in the vertex shader. The reason
// for this is that the texture may change
// dimensions (e.g. the pages in a texture
// atlas can grow). When this happens, by
// storing the coordinates as texel values
// we don't need to go through and update
// various CPU-side structures.
#[derive(Debug, Clone)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct CacheItem {
    pub texture_id: TextureSource,
    pub uv_rect_handle: GpuCacheHandle,
    pub uv_rect: DeviceIntRect,
    pub user_data: [f32; 4],
}

impl CacheItem {
    pub fn invalid() -> Self {
        CacheItem {
            texture_id: TextureSource::Invalid,
            uv_rect_handle: GpuCacheHandle::new(),
            uv_rect: DeviceIntRect::zero(),
            user_data: [0.0; 4],
        }
    }

    pub fn is_valid(&self) -> bool {
        self.texture_id != TextureSource::Invalid
    }
}

/// Represents the backing store of an image in the cache.
/// This storage can take several forms.
#[derive(Clone, Debug)]
pub enum CachedImageData {
    /// A simple series of bytes, provided by the embedding and owned by WebRender.
    /// The format is stored out-of-band, currently in ImageDescriptor.
    Raw(Arc<Vec<u8>>),
    /// An series of commands that can be rasterized into an image via an
    /// embedding-provided callback.
    ///
    /// The commands are stored elsewhere and this variant is used as a placeholder.
    Blob,
    /// A stacking context for which a snapshot has been requested.
    ///
    /// The snapshot is grabbed from GPU-side rasterized pixels so there is no
    /// CPU-side data to store here.
    Snapshot,
    /// An image owned by the embedding, and referenced by WebRender. This may
    /// take the form of a texture or a heap-allocated buffer.
    External(ExternalImageData),
}

impl From<ImageData> for CachedImageData {
    fn from(img_data: ImageData) -> Self {
        match img_data {
            ImageData::Raw(data) => CachedImageData::Raw(data),
            ImageData::External(data) => CachedImageData::External(data),
        }
    }
}

impl CachedImageData {
    /// Returns true if this represents a blob.
    #[inline]
    pub fn is_blob(&self) -> bool {
        match *self {
            CachedImageData::Blob => true,
            _ => false,
        }
    }

    #[inline]
    pub fn is_snapshot(&self) -> bool {
        match *self {
            CachedImageData::Snapshot => true,
            _ => false,
        }
    }

    /// Returns true if this variant of CachedImageData should go through the texture
    /// cache.
    #[inline]
    pub fn uses_texture_cache(&self) -> bool {
        match *self {
            CachedImageData::External(ref ext_data) => match ext_data.image_type {
                ExternalImageType::TextureHandle(_) => false,
                ExternalImageType::Buffer => true,
            },
            CachedImageData::Blob => true,
            CachedImageData::Raw(_) => true,
            CachedImageData::Snapshot => true,
        }
    }
}

#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ImageProperties {
    pub descriptor: ImageDescriptor,
    pub external_image: Option<ExternalImageData>,
    pub tiling: Option<TileSize>,
    // Potentially a subset of the image's total rectangle. This rectangle is what
    // we map to the (layout space) display item bounds.
    pub visible_rect: DeviceIntRect,
    pub adjustment: AdjustedImageSource,
}

#[derive(Debug, Copy, Clone, PartialEq)]
enum State {
    Idle,
    AddResources,
    QueryResources,
}

/// Post scene building state.
type RasterizedBlob = FastHashMap<TileOffset, RasterizedBlobImage>;

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
#[derive(Debug, Copy, Clone, PartialEq, PeekPoke, Default)]
pub struct ImageGeneration(pub u32);

impl ImageGeneration {
    pub const INVALID: ImageGeneration = ImageGeneration(u32::MAX);
}

struct ImageResource {
    data: CachedImageData,
    descriptor: ImageDescriptor,
    tiling: Option<TileSize>,
    /// This is used to express images that are virtually very large
    /// but with only a visible sub-set that is valid at a given time.
    visible_rect: DeviceIntRect,
    adjustment: AdjustedImageSource,
    generation: ImageGeneration,
}

#[derive(Default)]
struct ImageTemplates {
    images: FastHashMap<ImageKey, ImageResource>,
}

impl ImageTemplates {
    fn insert(&mut self, key: ImageKey, resource: ImageResource) {
        self.images.insert(key, resource);
    }

    fn remove(&mut self, key: ImageKey) -> Option<ImageResource> {
        self.images.remove(&key)
    }

    fn get(&self, key: ImageKey) -> Option<&ImageResource> {
        self.images.get(&key)
    }

    fn get_mut(&mut self, key: ImageKey) -> Option<&mut ImageResource> {
        self.images.get_mut(&key)
    }
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct CachedImageInfo {
    texture_cache_handle: TextureCacheHandle,
    dirty_rect: ImageDirtyRect,
    manual_eviction: bool,
}

impl CachedImageInfo {
    fn mark_unused(&mut self, texture_cache: &mut TextureCache) {
        texture_cache.evict_handle(&self.texture_cache_handle);
        self.manual_eviction = false;
    }
}

#[cfg(debug_assertions)]
impl Drop for CachedImageInfo {
    fn drop(&mut self) {
        debug_assert!(!self.manual_eviction, "Manual eviction requires cleanup");
    }
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ResourceClassCache<K: Hash + Eq, V, U: Default> {
    resources: FastHashMap<K, V>,
    pub user_data: U,
}

impl<K, V, U> ResourceClassCache<K, V, U>
where
    K: Clone + Hash + Eq + Debug,
    U: Default,
{
    pub fn new() -> Self {
        ResourceClassCache {
            resources: FastHashMap::default(),
            user_data: Default::default(),
        }
    }

    pub fn get(&self, key: &K) -> &V {
        self.resources.get(key)
            .expect("Didn't find a cached resource with that ID!")
    }

    pub fn try_get(&self, key: &K) -> Option<&V> {
        self.resources.get(key)
    }

    pub fn insert(&mut self, key: K, value: V) {
        self.resources.insert(key, value);
    }

    pub fn remove(&mut self, key: &K) -> Option<V> {
        self.resources.remove(key)
    }

    pub fn get_mut(&mut self, key: &K) -> &mut V {
        self.resources.get_mut(key)
            .expect("Didn't find a cached resource with that ID!")
    }

    pub fn try_get_mut(&mut self, key: &K) -> Option<&mut V> {
        self.resources.get_mut(key)
    }

    pub fn entry(&mut self, key: K) -> Entry<K, V> {
        self.resources.entry(key)
    }

    pub fn iter(&self) -> Iter<K, V> {
        self.resources.iter()
    }

    pub fn iter_mut(&mut self) -> IterMut<K, V> {
        self.resources.iter_mut()
    }

    pub fn is_empty(&mut self) -> bool {
        self.resources.is_empty()
    }

    pub fn clear(&mut self) {
        self.resources.clear();
    }

    pub fn retain<F>(&mut self, f: F)
    where
        F: FnMut(&K, &mut V) -> bool,
    {
        self.resources.retain(f);
    }
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
struct CachedImageKey {
    pub rendering: ImageRendering,
    pub tile: Option<TileOffset>,
}

#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash)]
#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub struct ImageRequest {
    pub key: ImageKey,
    pub rendering: ImageRendering,
    pub tile: Option<TileOffset>,
}

impl ImageRequest {
    pub fn with_tile(&self, offset: TileOffset) -> Self {
        ImageRequest {
            key: self.key,
            rendering: self.rendering,
            tile: Some(offset),
        }
    }

    pub fn is_untiled_auto(&self) -> bool {
        self.tile.is_none() && self.rendering == ImageRendering::Auto
    }
}

impl Into<BlobImageRequest> for ImageRequest {
    fn into(self) -> BlobImageRequest {
        BlobImageRequest {
            key: BlobImageKey(self.key),
            tile: self.tile.unwrap(),
        }
    }
}

impl Into<CachedImageKey> for ImageRequest {
    fn into(self) -> CachedImageKey {
        CachedImageKey {
            rendering: self.rendering,
            tile: self.tile,
        }
    }
}

#[derive(Debug)]
#[cfg_attr(feature = "capture", derive(Clone, Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
pub enum ImageCacheError {
    OverLimitSize,
}

#[cfg_attr(feature = "capture", derive(Serialize))]
#[cfg_attr(feature = "replay", derive(Deserialize))]
enum ImageResult {
    UntiledAuto(CachedImageInfo),
    Multi(ResourceClassCache<CachedImageKey, CachedImageInfo, ()>),
    Err(ImageCacheError),
}

impl ImageResult {
    /// Releases any texture cache entries held alive by this ImageResult.
    fn drop_from_cache(&mut self, texture_cache: &mut TextureCache) {
        match *self {
            ImageResult::UntiledAuto(ref mut entry) => {
                entry.mark_unused(texture_cache);
            },
            ImageResult::Multi(ref mut entries) => {
                for entry in entries.resources.values_mut() {
                    entry.mark_unused(texture_cache);
                }
            },
            ImageResult::Err(_) => {},
        }
    }
}

type ImageCache = ResourceClassCache<ImageKey, ImageResult, ()>;

struct Resources {
    fonts: SharedFontResources,
    image_templates: ImageTemplates,
    // We keep a set of Weak references to the fonts so that we're able to include them in memory
    // reports even if only the OS is holding on to the Vec<u8>. PtrWeakHashSet will periodically
    // drop any references that have gone dead.
    weak_fonts: WeakTable
}

// We only use this to report glyph dimensions to the user of the API, so using
// the font instance key should be enough. If we start using it to cache dimensions
// for internal font instances we should change the hash key accordingly.
pub type GlyphDimensionsCache = FastHashMap<(FontInstanceKey, GlyphIndex), Option<GlyphDimensions>>;

/// Internal information about allocated render targets in the pool
struct RenderTarget {
    size: DeviceIntSize,
    format: ImageFormat,
    texture_id: CacheTextureId,
    /// If true, this is currently leant out, and not available to other passes
    is_active: bool,
    last_frame_used: FrameId,
}

impl RenderTarget {
    fn size_in_bytes(&self) -> usize {
        let bpp = self.format.bytes_per_pixel() as usize;
        (self.size.width * self.size.height) as usize * bpp
    }

    /// Returns true if this texture was used within `threshold` frames of
    /// the current frame.
    pub fn used_recently(&self, current_frame_id: FrameId, threshold: u64) -> bool {
        self.last_frame_used + threshold >= current_frame_id
    }
}

/// High-level container for resources managed by the `RenderBackend`.
///
/// This includes a variety of things, including images, fonts, and glyphs,
/// which may be stored as memory buffers, GPU textures, or handles to resources
/// managed by the OS or other parts of WebRender.
pub struct ResourceCache {
    cached_glyphs: GlyphCache,
    cached_images: ImageCache,
    cached_render_tasks: RenderTaskCache,

    resources: Resources,
    state: State,
    current_frame_id: FrameId,

    #[cfg(feature = "capture")]
    /// Used for capture sequences. If the resource cache is updated, then we
    /// mark it as dirty. When the next frame is captured in the sequence, we
    /// dump the state of the resource cache.
    capture_dirty: bool,

    pub texture_cache: TextureCache,
    pub picture_textures: PictureTextures,

    /// TODO(gw): We should expire (parts of) this cache semi-regularly!
    cached_glyph_dimensions: GlyphDimensionsCache,
    glyph_rasterizer: GlyphRasterizer,

    /// The set of images that aren't present or valid in the texture cache,
    /// and need to be rasterized and/or uploaded this frame. This includes
    /// both blobs and regular images.
    pending_image_requests: FastHashSet<ImageRequest>,

    rasterized_blob_images: FastHashMap<BlobImageKey, RasterizedBlob>,

    /// A log of the last three frames worth of deleted image keys kept
    /// for debugging purposes.
    deleted_blob_keys: VecDeque<Vec<BlobImageKey>>,

    /// We keep one around to be able to call clear_namespace
    /// after the api object is deleted. For most purposes the
    /// api object's blob handler should be used instead.
    blob_image_handler: Option<Box<dyn BlobImageHandler>>,

    /// A list of queued compositor surface updates to apply next frame.
    pending_native_surface_updates: Vec<NativeSurfaceOperation>,

    image_templates_memory: usize,
    font_templates_memory: usize,

    /// A pool of render targets for use by the render task graph
    render_target_pool: Vec<RenderTarget>,
}

impl ResourceCache {
    pub fn new(
        texture_cache: TextureCache,
        picture_textures: PictureTextures,
        glyph_rasterizer: GlyphRasterizer,
        cached_glyphs: GlyphCache,
        fonts: SharedFontResources,
        blob_image_handler: Option<Box<dyn BlobImageHandler>>,
    ) -> Self {
        ResourceCache {
            cached_glyphs,
            cached_images: ResourceClassCache::new(),
            cached_render_tasks: RenderTaskCache::new(),
            resources: Resources {
                fonts,
                image_templates: ImageTemplates::default(),
                weak_fonts: WeakTable::new(),
            },
            cached_glyph_dimensions: FastHashMap::default(),
            texture_cache,
            picture_textures,
            state: State::Idle,
            current_frame_id: FrameId::INVALID,
            pending_image_requests: FastHashSet::default(),
            glyph_rasterizer,
            rasterized_blob_images: FastHashMap::default(),
            // We want to keep three frames worth of delete blob keys
            deleted_blob_keys: vec![Vec::new(), Vec::new(), Vec::new()].into(),
            blob_image_handler,
            pending_native_surface_updates: Vec::new(),
            #[cfg(feature = "capture")]
            capture_dirty: true,
            image_templates_memory: 0,
            font_templates_memory: 0,
            render_target_pool: Vec::new(),
        }
    }

    /// Construct a resource cache for use in unit tests.
    #[cfg(test)]
    pub fn new_for_testing() -> Self {
        use rayon::ThreadPoolBuilder;

        let texture_cache = TextureCache::new_for_testing(
            4096,
            ImageFormat::RGBA8,
        );
        let workers = Arc::new(ThreadPoolBuilder::new().build().unwrap());
        let glyph_rasterizer = GlyphRasterizer::new(workers, None, true);
        let cached_glyphs = GlyphCache::new();
        let fonts = SharedFontResources::new(IdNamespace(0));
        let picture_textures = PictureTextures::new(
            crate::picture::TILE_SIZE_DEFAULT,
            TextureFilter::Nearest,
        );

        ResourceCache::new(
            texture_cache,
            picture_textures,
            glyph_rasterizer,
            cached_glyphs,
            fonts,
            None,
        )
    }

    pub fn max_texture_size(&self) -> i32 {
        self.texture_cache.max_texture_size()
    }

    /// Maximum texture size before we consider it preferrable to break the texture
    /// into tiles.
    pub fn tiling_threshold(&self) -> i32 {
        self.texture_cache.tiling_threshold()
    }

    pub fn enable_multithreading(&mut self, enable: bool) {
        self.glyph_rasterizer.enable_multithreading(enable);
    }

    fn should_tile(limit: i32, descriptor: &ImageDescriptor, data: &CachedImageData) -> bool {
        let size_check = descriptor.size.width > limit || descriptor.size.height > limit;
        match *data {
            CachedImageData::Raw(_) | CachedImageData::Blob => size_check,
            CachedImageData::External(info) => {
                // External handles already represent existing textures so it does
                // not make sense to tile them into smaller ones.
                info.image_type == ExternalImageType::Buffer && size_check
            }
            CachedImageData::Snapshot => false,
        }
    }

    /// Request an optionally cacheable render task.
    ///
    /// If the render task cache key is None, the render task is
    /// not cached.
    /// Otherwise, if the item is already cached, the texture cache
    /// handle will be returned. Otherwise, the user supplied
    /// closure will be invoked to generate the render task
    /// chain that is required to draw this task.
    ///
    /// This function takes care of adding the render task as a
    /// dependency to its parent task or surface.
    pub fn request_render_task(
        &mut self,
        key: Option<RenderTaskCacheKey>,
        is_opaque: bool,
        parent: RenderTaskParent,
        gpu_cache: &mut GpuCache,
        gpu_buffer_builder: &mut GpuBufferBuilderF,
        rg_builder: &mut RenderTaskGraphBuilder,
        surface_builder: &mut SurfaceBuilder,
        f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF, &mut GpuCache) -> RenderTaskId,
    ) -> RenderTaskId {
        self.cached_render_tasks.request_render_task(
            key.clone(),
            &mut self.texture_cache,
            is_opaque,
            parent,
            gpu_cache,
            gpu_buffer_builder,
            rg_builder,
            surface_builder,
            f
        )
    }

    pub fn render_as_image(
        &mut self,
        image_key: ImageKey,
        size: DeviceIntSize,
        rg_builder: &mut RenderTaskGraphBuilder,
        gpu_buffer_builder: &mut GpuBufferBuilderF,
        gpu_cache: &mut GpuCache,
        is_opaque: bool,
        adjustment: &AdjustedImageSource,
        f: &mut dyn FnMut(&mut RenderTaskGraphBuilder, &mut GpuBufferBuilderF, &mut GpuCache) -> RenderTaskId,
    ) -> RenderTaskId {

        let task_id = f(rg_builder, gpu_buffer_builder, gpu_cache);

        let render_task = rg_builder.get_task_mut(task_id);

        let mut texture_cache_handle = TextureCacheHandle::invalid();

        let flags = if is_opaque {
            ImageDescriptorFlags::IS_OPAQUE
        } else {
            ImageDescriptorFlags::empty()
        };

        let descriptor = ImageDescriptor::new(
            size.width,
            size.height,
            self.texture_cache.shared_color_expected_format(),
            flags,
        );
 
        // Allocate space in the texture cache, but don't supply
        // and CPU-side data to be uploaded.
        let user_data = [0.0; 4];
        self.texture_cache.update(
            &mut texture_cache_handle,
            descriptor,
            TextureFilter::Linear,
            None,
            user_data,
            DirtyRect::All,
            gpu_cache,
            None,
            render_task.uv_rect_kind(),
            Eviction::Manual,
            TargetShader::Default,
        );

        // Get the allocation details in the texture cache, and store
        // this in the render task. The renderer will draw this task
        // into the appropriate rect of the texture cache on this frame.
        let (texture_id, uv_rect, _, _, _) =
            self.texture_cache.get_cache_location(&texture_cache_handle);

        render_task.location = RenderTaskLocation::Static {
            surface: StaticRenderTaskSurface::TextureCache {
                texture: texture_id,
                target_kind: RenderTargetKind::Color,
            },
            rect: uv_rect.to_i32(),
        };

        self.cached_images.insert(
            image_key,
            ImageResult::UntiledAuto(CachedImageInfo {
                texture_cache_handle,
                dirty_rect: ImageDirtyRect::All,
                manual_eviction: true,
            })
        );

        self.resources.image_templates
            .get_mut(image_key)
            .unwrap()
            .adjustment = *adjustment;

        task_id
    }

    pub fn post_scene_building_update(
        &mut self,
        updates: Vec<ResourceUpdate>,
        profile: &mut TransactionProfile,
    ) {
        // TODO, there is potential for optimization here, by processing updates in
        // bulk rather than one by one (for example by sorting allocations by size or
        // in a way that reduces fragmentation in the atlas).
        #[cfg(feature = "capture")]
        match updates.is_empty() {
            false => self.capture_dirty = true,
            _ => {},
        }

        for update in updates {
            match update {
                ResourceUpdate::AddImage(img) => {
                    if let ImageData::Raw(ref bytes) = img.data {
                        self.image_templates_memory += bytes.len();
                        profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory));
                    }
                    self.add_image_template(
                        img.key,
                        img.descriptor,
                        img.data.into(),
                        &img.descriptor.size.into(),
                        img.tiling,
                    );
                    profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len());
                }
                ResourceUpdate::UpdateImage(img) => {
                    self.update_image_template(img.key, img.descriptor, img.data.into(), &img.dirty_rect);
                }
                ResourceUpdate::AddBlobImage(img) => {
                    self.add_image_template(
                        img.key.as_image(),
                        img.descriptor,
                        CachedImageData::Blob,
                        &img.visible_rect,
                        Some(img.tile_size),
                    );
                }
                ResourceUpdate::UpdateBlobImage(img) => {
                    self.update_image_template(
                        img.key.as_image(),
                        img.descriptor,
                        CachedImageData::Blob,
                        &to_image_dirty_rect(
                            &img.dirty_rect
                        ),
                    );
                    self.discard_tiles_outside_visible_area(img.key, &img.visible_rect); // TODO: remove?
                    self.set_image_visible_rect(img.key.as_image(), &img.visible_rect);
                }
                ResourceUpdate::DeleteImage(img) => {
                    self.delete_image_template(img);
                    profile.set(profiler::IMAGE_TEMPLATES, self.resources.image_templates.images.len());
                    profile.set(profiler::IMAGE_TEMPLATES_MEM, bytes_to_mb(self.image_templates_memory));
                }
                ResourceUpdate::DeleteBlobImage(img) => {
                    self.delete_image_template(img.as_image());
                }
                ResourceUpdate::AddSnapshotImage(img) => {
                    let format = self.texture_cache.shared_color_expected_format();
                    self.add_image_template(
                        img.key.as_image(),
                        ImageDescriptor {
                            format,
                            // We'll know about the size when creating the render task.
                            size: DeviceIntSize::zero(),
                            stride: None,
                            offset: 0,
                            flags: ImageDescriptorFlags::empty(),
                        },
                        CachedImageData::Snapshot,
                        &DeviceIntRect::zero(),
                        None,
                    );
                }
                ResourceUpdate::DeleteSnapshotImage(img) => {
                    self.delete_image_template(img.as_image());
                }
                ResourceUpdate::DeleteFont(font) => {
                    if let Some(shared_key) = self.resources.fonts.font_keys.delete_key(&font) {
                        self.delete_font_template(shared_key);
                        if let Some(ref mut handler) = &mut self.blob_image_handler {
                            handler.delete_font(shared_key);
                        }
                        profile.set(profiler::FONT_TEMPLATES, self.resources.fonts.templates.len());
                        profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory));
                    }
                }
                ResourceUpdate::DeleteFontInstance(font) => {
                    if let Some(shared_key) = self.resources.fonts.instance_keys.delete_key(&font) {
                        self.delete_font_instance(shared_key);
                    }
                    if let Some(ref mut handler) = &mut self.blob_image_handler {
                        handler.delete_font_instance(font);
                    }
                }
                ResourceUpdate::SetBlobImageVisibleArea(key, area) => {
                    self.discard_tiles_outside_visible_area(key, &area);
                    self.set_image_visible_rect(key.as_image(), &area);
                }
                ResourceUpdate::AddFont(font) => {
                    // The shared key was already added in ApiResources, but the first time it is
                    // seen on the backend we still need to do some extra initialization here.
                    let (key, template) = match font {
                        AddFont::Raw(key, bytes, index) => {
                            (key, FontTemplate::Raw(bytes, index))
                        }
                        AddFont::Native(key, native_font_handle) => {
                            (key, FontTemplate::Native(native_font_handle))
                        }
                    };
                    let shared_key = self.resources.fonts.font_keys.map_key(&key);
                    if !self.glyph_rasterizer.has_font(shared_key) {
                        self.add_font_template(shared_key, template);
                        profile.set(profiler::FONT_TEMPLATES, self.resources.fonts.templates.len());
                        profile.set(profiler::FONT_TEMPLATES_MEM, bytes_to_mb(self.font_templates_memory));
                    }
                }
                ResourceUpdate::AddFontInstance(..) => {
                    // Already added in ApiResources.
                }
            }
        }
    }

    pub fn add_rasterized_blob_images(
        &mut self,
        images: Vec<(BlobImageRequest, BlobImageResult)>,
        profile: &mut TransactionProfile,
    ) {
        for (request, result) in images {
            let data = match result {
                Ok(data) => data,
                Err(..) => {
                    warn!("Failed to rasterize a blob image");
                    continue;
                }
            };

            profile.add(profiler::RASTERIZED_BLOBS_PX, data.rasterized_rect.area());

            // First make sure we have an entry for this key (using a placeholder
            // if need be).
            let tiles = self.rasterized_blob_images.entry(request.key).or_insert_with(
                || { RasterizedBlob::default() }
            );

            tiles.insert(request.tile, data);

            match self.cached_images.try_get_mut(&request.key.as_image()) {
                Some(&mut ImageResult::Multi(ref mut entries)) => {
                    let cached_key = CachedImageKey {
                        rendering: ImageRendering::Auto, // TODO(nical)
                        tile: Some(request.tile),
                    };
                    if let Some(entry) = entries.try_get_mut(&cached_key) {
                        entry.dirty_rect = DirtyRect::All;
                    }
                }
                _ => {}
            }
        }
    }

    pub fn add_font_template(&mut self, font_key: FontKey, template: FontTemplate) {
        // Push the new font to the font renderer, and also store
        // it locally for glyph metric requests.
        if let FontTemplate::Raw(ref data, _) = template {
            self.resources.weak_fonts.insert(Arc::downgrade(data));
            self.font_templates_memory += data.len();
        }
        self.glyph_rasterizer.add_font(font_key, template.clone());
        self.resources.fonts.templates.add_font(font_key, template);
    }

    pub fn delete_font_template(&mut self, font_key: FontKey) {
        self.glyph_rasterizer.delete_font(font_key);
        if let Some(FontTemplate::Raw(data, _)) = self.resources.fonts.templates.delete_font(&font_key)&nbsp;{
            self.font_templates_memory -= data.len();
        }
        self.cached_glyphs.delete_fonts(&[font_key]);
    }

    pub fn delete_font_instance(&mut self, instance_key: FontInstanceKey) {
        self.resources.fonts.instances.delete_font_instance(instance_key);
    }

    pub fn get_font_instance(&self, instance_key: FontInstanceKey) -> Option<Arc<BaseFontInstance>> {
        self.resources.fonts.instances.get_font_instance(instance_key)
    }

    pub fn get_fonts(&self) -> SharedFontResources {
        self.resources.fonts.clone()
    }

    pub fn add_image_template(
        &mut self,
        image_key: ImageKey,
        descriptor: ImageDescriptor,
        data: CachedImageData,
        visible_rect: &DeviceIntRect,
        mut tiling: Option<TileSize>,
    ) {
        if let Some(ref mut tile_size) = tiling {
            // Sanitize the value since it can be set by a pref.
            *tile_size = (*tile_size).max(16).min(2048);
        }

        if tiling.is_none() && Self::should_tile(self.tiling_threshold(), &descriptor, &data) {
            // We aren't going to be able to upload a texture this big, so tile it, even
            // if tiling was not requested.
            tiling = Some(DEFAULT_TILE_SIZE);
        }

        let resource = ImageResource {
            descriptor,
            data,
            tiling,
            visible_rect: *visible_rect,
            adjustment: AdjustedImageSource::new(),
            generation: ImageGeneration(0),
        };

        self.resources.image_templates.insert(image_key, resource);
    }

    pub fn update_image_template(
        &mut self,
        image_key: ImageKey,
        descriptor: ImageDescriptor,
        data: CachedImageData,
        dirty_rect: &ImageDirtyRect,
    ) {
        let tiling_threshold = self.tiling_threshold();
        let image = match self.resources.image_templates.get_mut(image_key) {
            Some(res) => res,
            None => panic!("Attempt to update non-existent image"),
        };

        let mut tiling = image.tiling;
        if tiling.is_none() && Self::should_tile(tiling_threshold, &descriptor, &data) {
            tiling = Some(DEFAULT_TILE_SIZE);
        }

        // Each cache entry stores its own copy of the image's dirty rect. This allows them to be
        // updated independently.
        match self.cached_images.try_get_mut(&image_key) {
            Some(&mut ImageResult::UntiledAuto(ref mut entry)) => {
                entry.dirty_rect = entry.dirty_rect.union(dirty_rect);
            }
            Some(&mut ImageResult::Multi(ref mut entries)) => {
                for (key, entry) in entries.iter_mut() {
                    // We want the dirty rect relative to the tile and not the whole image.
                    let local_dirty_rect = match (tiling, key.tile) {
                        (Some(tile_size), Some(tile)) => {
                            dirty_rect.map(|mut rect|{
                                let tile_offset = DeviceIntPoint::new(
                                    tile.x as i32,
                                    tile.y as i32,
                                ) * tile_size as i32;
                                rect = rect.translate(-tile_offset.to_vector());

                                let tile_rect = compute_tile_size(
                                    &descriptor.size.into(),
                                    tile_size,
                                    tile,
                                ).into();

                                rect.intersection(&tile_rect).unwrap_or_else(DeviceIntRect::zero)
                            })
                        }
                        (None, Some(..)) => DirtyRect::All,
                        _ => *dirty_rect,
                    };
                    entry.dirty_rect = entry.dirty_rect.union(&local_dirty_rect);
                }
            }
            _ => {}
        }

        if image.descriptor.format != descriptor.format {
            // could be a stronger warning/error?
            trace!("Format change {:?} -> {:?}", image.descriptor.format, descriptor.format);
        }
        *image = ImageResource {
            descriptor,
            data,
            tiling,
            visible_rect: descriptor.size.into(),
            adjustment: AdjustedImageSource::new(),
            generation: ImageGeneration(image.generation.0 + 1),
        };
    }

    pub fn increment_image_generation(&mut self, key: ImageKey) {
        if let Some(image) = self.resources.image_templates.get_mut(key) {
            image.generation.0 += 1;
        }
    }

    pub fn delete_image_template(&mut self, image_key: ImageKey) {
        // Remove the template.
        let value = self.resources.image_templates.remove(image_key);

        // Release the corresponding texture cache entry, if any.
        if let Some(mut cached) = self.cached_images.remove(&image_key) {
            cached.drop_from_cache(&mut self.texture_cache);
        }

        match value {
            Some(image) => if image.data.is_blob() {
                if let CachedImageData::Raw(data) = image.data {
                    self.image_templates_memory -= data.len();
                }

                let blob_key = BlobImageKey(image_key);
                self.deleted_blob_keys.back_mut().unwrap().push(blob_key);
                self.rasterized_blob_images.remove(&blob_key);
            },
            None => {
                warn!("Delete the non-exist key");
                debug!("key={:?}", image_key);
            }
        }
    }

    /// Return the current generation of an image template
    pub fn get_image_generation(&self, key: ImageKey) -> ImageGeneration {
        self.resources
            .image_templates
            .get(key)
            .map_or(ImageGeneration::INVALID, |template| template.generation)
    }

    /// Requests an image to ensure that it will be in the texture cache this frame.
    ///
    /// returns the size in device pixel of the image or tile.
    pub fn request_image(
        &mut self,
        request: ImageRequest,
        gpu_cache: &mut GpuCache,
    ) -> DeviceIntSize {
        debug_assert_eq!(self.state, State::AddResources);

        let template = match self.resources.image_templates.get(request.key) {
            Some(template) => template,
            None => {
                warn!("ERROR: Trying to render deleted / non-existent key");
                debug!("key={:?}", request.key);
                return DeviceIntSize::zero();
            }
        };

        let size = match request.tile {
            Some(tile) => compute_tile_size(&template.visible_rect, template.tiling.unwrap(), tile),
            None => template.descriptor.size,
        };

        // Images that don't use the texture cache can early out.
        if !template.data.uses_texture_cache() {
            return size;
        }

        let side_size =
            template.tiling.map_or(cmp::max(template.descriptor.size.width, template.descriptor.size.height),
                                   |tile_size| tile_size as i32);
        if side_size > self.texture_cache.max_texture_size() {
            // The image or tiling size is too big for hardware texture size.
            warn!("Dropping image, image:(w:{},h:{}, tile:{}) is too big for hardware!",
                  template.descriptor.size.width, template.descriptor.size.height, template.tiling.unwrap_or(0));
            self.cached_images.insert(request.key, ImageResult::Err(ImageCacheError::OverLimitSize));
            return DeviceIntSize::zero();
        }

        let storage = match self.cached_images.entry(request.key) {
            Occupied(e) => {
                // We might have an existing untiled entry, and need to insert
                // a second entry. In such cases we need to move the old entry
                // out first, replacing it with a dummy entry, and then creating
                // the tiled/multi-entry variant.
                let entry = e.into_mut();
                if !request.is_untiled_auto() {
                    let untiled_entry = match entry {
                        &mut ImageResult::UntiledAuto(ref mut entry) => {
                            Some(mem::replace(entry, CachedImageInfo {
                                texture_cache_handle: TextureCacheHandle::invalid(),
                                dirty_rect: DirtyRect::All,
                                manual_eviction: false,
                            }))
                        }
                        _ => None
                    };

                    if let Some(untiled_entry) = untiled_entry {
                        let mut entries = ResourceClassCache::new();
                        let untiled_key = CachedImageKey {
                            rendering: ImageRendering::Auto,
                            tile: None,
                        };
                        entries.insert(untiled_key, untiled_entry);
                        *entry = ImageResult::Multi(entries);
                    }
                }
                entry
            }
            Vacant(entry) => {
                entry.insert(if request.is_untiled_auto() {
                    ImageResult::UntiledAuto(CachedImageInfo {
                        texture_cache_handle: TextureCacheHandle::invalid(),
                        dirty_rect: DirtyRect::All,
                        manual_eviction: false,
                    })
                } else {
                    ImageResult::Multi(ResourceClassCache::new())
                })
            }
        };

        // If this image exists in the texture cache, *and* the dirty rect
        // in the cache is empty, then it is valid to use as-is.
        let entry = match *storage {
            ImageResult::UntiledAuto(ref mut entry) => entry,
            ImageResult::Multi(ref mut entries) => {
                entries.entry(request.into())
                    .or_insert(CachedImageInfo {
                        texture_cache_handle: TextureCacheHandle::invalid(),
                        dirty_rect: DirtyRect::All,
                        manual_eviction: false,
                    })
            },
            ImageResult::Err(_) => panic!("Errors should already have been handled"),
        };

        let needs_upload = self.texture_cache.request(&entry.texture_cache_handle, gpu_cache);

        if !needs_upload && entry.dirty_rect.is_empty() {
            return size;
        }

        if !self.pending_image_requests.insert(request) {
            return size;
        }

        if template.data.is_blob() {
            let request: BlobImageRequest = request.into();
            let missing = match self.rasterized_blob_images.get(&request.key) {
                Some(tiles) => !tiles.contains_key(&request.tile),
                _ => true,
            };

            assert!(!missing);
        }

        size
    }

    fn discard_tiles_outside_visible_area(
        &mut self,
        key: BlobImageKey,
        area: &DeviceIntRect
    ) {
        let tile_size = match self.resources.image_templates.get(key.as_image()) {
            Some(template) => template.tiling.unwrap(),
            None => {
                //debug!("Missing image template (key={:?})!", key);
                return;
            }
        };

        let tiles = match self.rasterized_blob_images.get_mut(&key) {
            Some(tiles) => tiles,
            _ => { return; }
        };

        let tile_range = compute_tile_range(
            &area,
            tile_size,
        );

        tiles.retain(|tile, _| { tile_range.contains(*tile) });

        let texture_cache = &mut self.texture_cache;
        match self.cached_images.try_get_mut(&key.as_image()) {
            Some(&mut ImageResult::Multi(ref mut entries)) => {
                entries.retain(|key, entry| {
                    if key.tile.is_none() || tile_range.contains(key.tile.unwrap()) {
                        return true;
                    }
                    entry.mark_unused(texture_cache);
                    return false;
                });
            }
            _ => {}
        }
    }

    fn set_image_visible_rect(&mut self, key: ImageKey, rect: &DeviceIntRect) {
        if let Some(image) = self.resources.image_templates.get_mut(key) {
            image.visible_rect = *rect;
            image.descriptor.size = rect.size();
        }
    }

    pub fn request_glyphs(
        &mut self,
        mut font: FontInstance,
        glyph_keys: &[GlyphKey],
        gpu_cache: &mut GpuCache,
    ) {
        debug_assert_eq!(self.state, State::AddResources);

        self.glyph_rasterizer.prepare_font(&mut font);
        let glyph_key_cache = self.cached_glyphs.insert_glyph_key_cache_for_font(&font);
        let texture_cache = &mut self.texture_cache;
        self.glyph_rasterizer.request_glyphs(
            font,
            glyph_keys,
            |key| {
                if let Some(entry) = glyph_key_cache.try_get(key) {
                    match entry {
                        GlyphCacheEntry::Cached(ref glyph) => {
                            // Skip the glyph if it is already has a valid texture cache handle.
                            if !texture_cache.request(&glyph.texture_cache_handle, gpu_cache) {
                                return false;
                            }
                            // This case gets hit when we already rasterized the glyph, but the
                            // glyph has been evicted from the texture cache. Just force it to
                            // pending so it gets rematerialized.
                        }
                        // Otherwise, skip the entry if it is blank or pending.
                        GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => return false,
                    }
                };

                glyph_key_cache.add_glyph(*key, GlyphCacheEntry::Pending);

                true
            }
        );
    }

    pub fn pending_updates(&mut self) -> ResourceUpdateList {
        ResourceUpdateList {
            texture_updates: self.texture_cache.pending_updates(),
            native_surface_updates: mem::replace(&mut self.pending_native_surface_updates, Vec::new()),
        }
    }

    pub fn fetch_glyphs<F>(
        &self,
        mut font: FontInstance,
        glyph_keys: &[GlyphKey],
        fetch_buffer: &mut Vec<GlyphFetchResult>,
        gpu_cache: &mut GpuCache,
        mut f: F,
    ) where
        F: FnMut(TextureSource, GlyphFormat, &[GlyphFetchResult]),
    {
        debug_assert_eq!(self.state, State::QueryResources);

        self.glyph_rasterizer.prepare_font(&mut font);
        let glyph_key_cache = self.cached_glyphs.get_glyph_key_cache_for_font(&font);

        let mut current_texture_id = TextureSource::Invalid;
        let mut current_glyph_format = GlyphFormat::Subpixel;
        debug_assert!(fetch_buffer.is_empty());

        for (loop_index, key) in glyph_keys.iter().enumerate() {
            let (cache_item, glyph_format) = match *glyph_key_cache.get(key) {
                GlyphCacheEntry::Cached(ref glyph) => {
                    (self.texture_cache.get(&glyph.texture_cache_handle), glyph.format)
                }
                GlyphCacheEntry::Blank | GlyphCacheEntry::Pending => continue,
            };
            if current_texture_id != cache_item.texture_id ||
                current_glyph_format != glyph_format {
                if !fetch_buffer.is_empty() {
                    f(current_texture_id, current_glyph_format, fetch_buffer);
                    fetch_buffer.clear();
                }
                current_texture_id = cache_item.texture_id;
                current_glyph_format = glyph_format;
            }
            fetch_buffer.push(GlyphFetchResult {
                index_in_text_run: loop_index as i32,
                uv_rect_address: gpu_cache.get_address(&cache_item.uv_rect_handle),
                offset: DevicePoint::new(cache_item.user_data[0], cache_item.user_data[1]),
                size: cache_item.uv_rect.size(),
                scale: cache_item.user_data[2],
            });
        }

        if !fetch_buffer.is_empty() {
            f(current_texture_id, current_glyph_format, fetch_buffer);
            fetch_buffer.clear();
        }
    }

    pub fn map_font_key(&self, key: FontKey) -> FontKey {
        self.resources.fonts.font_keys.map_key(&key)
    }

    pub fn map_font_instance_key(&self, key: FontInstanceKey) -> FontInstanceKey {
        self.resources.fonts.instance_keys.map_key(&key)
    }

    pub fn get_glyph_dimensions(
        &mut self,
        font: &FontInstance,
        glyph_index: GlyphIndex,
    ) -> Option<GlyphDimensions> {
        match self.cached_glyph_dimensions.entry((font.instance_key, glyph_index)) {
            Occupied(entry) => *entry.get(),
            Vacant(entry) => *entry.insert(
                self.glyph_rasterizer
                    .get_glyph_dimensions(font, glyph_index),
            ),
        }
    }

    pub fn get_glyph_index(&mut self, font_key: FontKey, ch: char) -> Option<u32> {
        self.glyph_rasterizer.get_glyph_index(font_key, ch)
    }

    #[inline]
    pub fn get_cached_image(&self, request: ImageRequest) -> Result<CacheItem, ()> {
        debug_assert_eq!(self.state, State::QueryResources);
        let image_info = self.get_image_info(request)?;
        Ok(self.get_texture_cache_item(&image_info.texture_cache_handle))
    }

    pub fn get_cached_render_task(
        &self,
        handle: &RenderTaskCacheEntryHandle,
    ) -> &RenderTaskCacheEntry {
        self.cached_render_tasks.get_cache_entry(handle)
    }

    #[inline]
    fn get_image_info(&self, request: ImageRequest) -> Result<&CachedImageInfo, ()> {
        // TODO(Jerry): add a debug option to visualize the corresponding area for
        // the Err() case of CacheItem.
        match *self.cached_images.get(&request.key) {
            ImageResult::UntiledAuto(ref image_info) => Ok(image_info),
            ImageResult::Multi(ref entries) => Ok(entries.get(&request.into())),
            ImageResult::Err(_) => Err(()),
        }
    }

    #[inline]
    pub fn get_texture_cache_item(&self, handle: &TextureCacheHandle) -> CacheItem {
        self.texture_cache.get(handle)
    }

    pub fn get_image_properties(&self, image_key: ImageKey) -> Option<ImageProperties> {
        let image_template = &self.resources.image_templates.get(image_key);

        image_template.map(|image_template| {
            let external_image = match image_template.data {
                CachedImageData::External(ext_image) => match ext_image.image_type {
                    ExternalImageType::TextureHandle(_) => Some(ext_image),
                    // external buffer uses resource_cache.
                    ExternalImageType::Buffer => None,
                },
                // raw and blob image are all using resource_cache.
                CachedImageData::Raw(..)
                | CachedImageData::Blob
                | CachedImageData::Snapshot
                 => None,
            };

            ImageProperties {
                descriptor: image_template.descriptor,
                external_image,
                tiling: image_template.tiling,
                visible_rect: image_template.visible_rect,
                adjustment: image_template.adjustment,
            }
        })
    }

    pub fn begin_frame(&mut self, stamp: FrameStamp, gpu_cache: &mut GpuCache, profile: &mut ;TransactionProfile) {
        profile_scope!("begin_frame");
        debug_assert_eq!(self.state, State::Idle);
        self.state = State::AddResources;
        self.texture_cache.begin_frame(stamp, profile);
        self.picture_textures.begin_frame(stamp, &mut self.texture_cache.pending_updates);

        self.cached_glyphs.begin_frame(
            stamp,
            &mut self.texture_cache,
            &mut self.glyph_rasterizer,
        );
        self.cached_render_tasks.begin_frame(&mut self.texture_cache);
        self.current_frame_id = stamp.frame_id();

        // Pop the old frame and push a new one.
        // Recycle the allocation if any.
        let mut v = self.deleted_blob_keys.pop_front().unwrap_or_else(Vec::new);
        v.clear();
        self.deleted_blob_keys.push_back(v);

        self.texture_cache.run_compaction(gpu_cache);
    }

    pub fn block_until_all_resources_added(
        &mut self,
        gpu_cache: &mut GpuCache,
        profile: &mut TransactionProfile,
    ) {
        profile_scope!("block_until_all_resources_added");

        debug_assert_eq!(self.state, State::AddResources);
        self.state = State::QueryResources;

        let cached_glyphs = &mut self.cached_glyphs;
        let texture_cache = &mut self.texture_cache;

        self.glyph_rasterizer.resolve_glyphs(
            |job, can_use_r8_format| {
                let GlyphRasterJob { font, key, result } = job;
                let glyph_key_cache = cached_glyphs.get_glyph_key_cache_for_font_mut(&*font);
                let glyph_info = match result {
                    Err(_) => GlyphCacheEntry::Blank,
                    Ok(ref glyph) if glyph.width == 0 || glyph.height == 0 => {
                        GlyphCacheEntry::Blank
                    }
                    Ok(glyph) => {
                        let mut texture_cache_handle = TextureCacheHandle::invalid();
                        texture_cache.request(&texture_cache_handle, gpu_cache);
                        texture_cache.update(
                            &mut texture_cache_handle,
                            ImageDescriptor {
                                size: size2(glyph.width, glyph.height),
                                stride: None,
                                format: glyph.format.image_format(can_use_r8_format),
                                flags: ImageDescriptorFlags::empty(),
                                offset: 0,
                            },
                            TextureFilter::Linear,
                            Some(CachedImageData::Raw(Arc::new(glyph.bytes))),
                            [glyph.left, -glyph.top, glyph.scale, 0.0],
                            DirtyRect::All,
                            gpu_cache,
                            Some(glyph_key_cache.eviction_notice()),
                            UvRectKind::Rect,
                            Eviction::Auto,
                            TargetShader::Text,
                        );
                        GlyphCacheEntry::Cached(CachedGlyphInfo {
                            texture_cache_handle,
                            format: glyph.format,
                        })
                    }
                };
                glyph_key_cache.insert(key, glyph_info);
            },
            profile,
        );

        // Apply any updates of new / updated images (incl. blobs) to the texture cache.
        self.update_texture_cache(gpu_cache);
    }

    fn update_texture_cache(&mut self, gpu_cache: &mut GpuCache) {
        profile_scope!("update_texture_cache");
        for request in self.pending_image_requests.drain() {
            let image_template = self.resources.image_templates.get_mut(request.key).unwrap();
            debug_assert!(image_template.data.uses_texture_cache());

            let mut updates: SmallVec<[(CachedImageData, Option<DeviceIntRect>); 1]> = SmallVec::new();

            match image_template.data {
                CachedImageData::Snapshot => {
                    // The update is done in ResourceCache::render_as_image.
                }
                CachedImageData::Raw(..)
                | CachedImageData::External(..) => {
                    // Safe to clone here since the Raw image data is an
                    // Arc, and the external image data is small.
                    updates.push((image_template.data.clone(), None));
                }
                CachedImageData::Blob => {
                    let blob_image = self.rasterized_blob_images.get_mut(&BlobImageKey(request.key)).unwrap();
                    let img = &blob_image[&request.tile.unwrap()];
                    updates.push((
                        CachedImageData::Raw(Arc::clone(&img.data)),
                        Some(img.rasterized_rect)
                    ));
                }
            };

            for (image_data, blob_rasterized_rect) in updates {
                let entry = match *self.cached_images.get_mut(&request.key) {
                    ImageResult::UntiledAuto(ref mut entry) => entry,
                    ImageResult::Multi(ref mut entries) => entries.get_mut(&request.into()),
                    ImageResult::Err(_) => panic!("Update requested for invalid entry")
                };

                let mut descriptor = image_template.descriptor.clone();
                let mut dirty_rect = entry.dirty_rect.replace_with_empty();

                if let Some(tile) = request.tile {
                    let tile_size = image_template.tiling.unwrap();
                    let clipped_tile_size = compute_tile_size(&image_template.visible_rect, tile_size, tile);
                    // The tiled image could be stored on the CPU as one large image or be
                    // already broken up into tiles. This affects the way we compute the stride
                    // and offset.
                    let tiled_on_cpu = image_template.data.is_blob();
                    if !tiled_on_cpu {
                        // we don't expect to have partial tiles at the top and left of non-blob
                        // images.
                        debug_assert_eq!(image_template.visible_rect.min, point2(0, 0));
                        let bpp = descriptor.format.bytes_per_pixel();
                        let stride = descriptor.compute_stride();
                        descriptor.stride = Some(stride);
                        descriptor.offset +=
                            tile.y as i32 * tile_size as i32 * stride +
                            tile.x as i32 * tile_size as i32 * bpp;
                    }

                    descriptor.size = clipped_tile_size;
                }

                // If we are uploading the dirty region of a blob image we might have several
                // rects to upload so we use each of these rasterized rects rather than the
                // overall dirty rect of the image.
                if let Some(rect) = blob_rasterized_rect {
                    dirty_rect = DirtyRect::Partial(rect);
                }

                let filter = match request.rendering {
                    ImageRendering::Pixelated => {
                        TextureFilter::Nearest
                    }
                    ImageRendering::Auto | ImageRendering::CrispEdges => {
                        // If the texture uses linear filtering, enable mipmaps and
                        // trilinear filtering, for better image quality. We only
                        // support this for now on textures that are not placed
                        // into the shared cache. This accounts for any image
                        // that is > 512 in either dimension, so it should cover
                        // the most important use cases. We may want to support
                        // mip-maps on shared cache items in the future.
                        if descriptor.allow_mipmaps() &&
                           descriptor.size.width > 512 &&
                           descriptor.size.height > 512 &&
                           !self.texture_cache.is_allowed_in_shared_cache(
                            TextureFilter::Linear,
                            &descriptor,
                        ) {
                            TextureFilter::Trilinear
                        } else {
                            TextureFilter::Linear
                        }
                    }
                };

                let eviction = match &image_template.data {
                    CachedImageData::Blob | CachedImageData::Snapshot => {
                        entry.manual_eviction = true;
                        Eviction::Manual
                    }
                    _ => {
                        Eviction::Auto
                    }
                };

                //Note: at this point, the dirty rectangle is local to the descriptor space
                self.texture_cache.update(
                    &mut entry.texture_cache_handle,
                    descriptor,
                    filter,
                    Some(image_data),
                    [0.0; 4],
                    dirty_rect,
                    gpu_cache,
                    None,
                    UvRectKind::Rect,
                    eviction,
                    TargetShader::Default,
                );
            }
        }
    }

    pub fn create_compositor_backdrop_surface(
        &mut self,
        color: ColorF
    ) -> NativeSurfaceId {
        let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64);

        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::CreateBackdropSurface {
                    id,
                    color,
                },
            }
        );

        id
    }

    /// Queue up allocation of a new OS native compositor surface with the
    /// specified tile size.
    pub fn create_compositor_surface(
        &mut self,
        virtual_offset: DeviceIntPoint,
        tile_size: DeviceIntSize,
        is_opaque: bool,
    ) -> NativeSurfaceId {
        let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64);

        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::CreateSurface {
                    id,
                    virtual_offset,
                    tile_size,
                    is_opaque,
                },
            }
        );

        id
    }

    pub fn create_compositor_external_surface(
        &mut self,
        is_opaque: bool,
    ) -> NativeSurfaceId {
        let id = NativeSurfaceId(NEXT_NATIVE_SURFACE_ID.fetch_add(1, Ordering::Relaxed) as u64);

        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::CreateExternalSurface {
                    id,
                    is_opaque,
                },
            }
        );

        id
    }

    /// Queue up destruction of an existing native OS surface. This is used when
    /// a picture cache surface is dropped or resized.
    pub fn destroy_compositor_surface(
        &mut self,
        id: NativeSurfaceId,
    ) {
        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::DestroySurface {
                    id,
                }
            }
        );
    }

    /// Queue construction of a native compositor tile on a given surface.
    pub fn create_compositor_tile(
        &mut self,
        id: NativeTileId,
    ) {
        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::CreateTile {
                    id,
                },
            }
        );
    }

    /// Queue destruction of a native compositor tile.
    pub fn destroy_compositor_tile(
        &mut self,
        id: NativeTileId,
    ) {
        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::DestroyTile {
                    id,
                },
            }
        );
    }

    pub fn attach_compositor_external_image(
        &mut self,
        id: NativeSurfaceId,
        external_image: ExternalImageId,
    ) {
        self.pending_native_surface_updates.push(
            NativeSurfaceOperation {
                details: NativeSurfaceOperationDetails::AttachExternalImage {
                    id,
                    external_image,
                },
            }
        );
    }


    pub fn end_frame(&mut self, profile: &mut TransactionProfile) {
        debug_assert_eq!(self.state, State::QueryResources);
        profile_scope!("end_frame");
        self.state = State::Idle;

        // GC the render target pool, if it's currently > 64 MB in size.
        //
        // We use a simple scheme whereby we drop any texture that hasn't been used
        // in the last 60 frames, until we are below the size threshold. This should
        // generally prevent any sustained build-up of unused textures, unless we don't
        // generate frames for a long period. This can happen when the window is
        // minimized, and we probably want to flush all the WebRender caches in that case [1].
        // There is also a second "red line" memory threshold which prevents
        // memory exhaustion if many render targets are allocated within a small
--> --------------------

--> maximum size reached

--> --------------------

[ 0.53Quellennavigators  Projekt   ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge