/* This Source Code Form is subject to the terms of the Mozilla Public *License,v.2.0.IfacopyoftheMPLwasnotdistributedwiththis
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
use std::mem; use smallvec::SmallVec; use api::{ImageFormat, ImageBufferKind, DebugFlags, TextureCacheCategory}; use api::units::*; usecrate::device::TextureFilter; usecrate::internal_types::{
CacheTextureId, TextureUpdateList, Swizzle, TextureCacheAllocInfo,
TextureSource, FrameStamp, FrameId,
}; usecrate::profiler::{self, TransactionProfile}; usecrate::freelist::{FreeList, FreeListHandle, WeakFreeListHandle};
// Stores information related to a single entry in the texture // cache. This is stored for each item whether it's in the shared // cache or a standalone texture. #[derive(Debug)] #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct PictureCacheEntry { /// The last frame this item was requested for rendering. pub last_access: FrameStamp, /// The actual device texture ID this is part of. pub texture_id: CacheTextureId,
}
/// The textures used to hold picture cache tiles. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] struct PictureTexture {
texture_id: CacheTextureId,
size: DeviceIntSize,
is_allocated: bool,
last_frame_used: FrameId,
}
/// The textures used to hold picture cache tiles. #[cfg_attr(feature = "capture", derive(Serialize))] #[cfg_attr(feature = "replay", derive(Deserialize))] pubstruct PictureTextures { /// Current list of textures in the pool
textures: Vec<PictureTexture>, /// Default tile size for content tiles
default_tile_size: DeviceIntSize, /// Number of currently allocated textures in the pool
allocated_texture_count: usize, /// Texture filter to use for picture cache textures
filter: TextureFilter,
debug_flags: DebugFlags,
/// Cache of picture cache entries.
cache_entries: FreeList<PictureCacheEntry, PictureCacheEntryMarker>, /// Strong handles for the picture_cache_entries FreeList.
cache_handles: Vec<FreeListHandle<PictureCacheEntryMarker>>,
// Expire picture cache tiles that haven't been referenced in the last frame. // The picture cache code manually keeps tiles alive by calling `request` on // them if it wants to retain a tile that is currently not visible. self.expire_old_tiles(pending_updates);
}
for texture in &mutself.textures { if texture.size == tile_size && !texture.is_allocated { // Found a target that's not currently in use which matches. Update // the last_frame_used for GC purposes.
texture.is_allocated = true;
texture.last_frame_used = FrameId::INVALID;
texture_id = Some(texture.texture_id); break;
}
}
// Need to create a new render target and add it to the pool
let texture_id = texture_id.unwrap_or_else(|| { let texture_id = *next_texture_id;
next_texture_id.0 += 1;
// Push a command to allocate device storage of the right size / format. let info = TextureCacheAllocInfo {
target: ImageBufferKind::Texture2D,
width: tile_size.width,
height: tile_size.height,
format: ImageFormat::RGBA8,
filter: self.filter,
is_shared_cache: false,
has_depth: true,
category: TextureCacheCategory::PictureTile,
};
let cache_entry = PictureCacheEntry {
last_access: self.now,
texture_id,
};
// Add the cache entry to the picture_textures.cache_entries FreeList. let strong_handle = self.cache_entries.insert(cache_entry); let new_handle = strong_handle.weak();
pubfn request(&mutself, handle: &PictureCacheTextureHandle) -> bool { let entry = self.cache_entries.get_opt_mut(handle); let now = self.now;
entry.map_or(true, |entry| { // If an image is requested that is already in the cache, // refresh the GPU cache data associated with this item.
entry.last_access = now; false
})
}
pubfn get_texture_source(&self, handle: &PictureCacheTextureHandle) -> TextureSource { let entry = self.cache_entries.get_opt(handle)
.expect("BUG: was dropped from cache or not updated!");
/// Expire picture cache tiles that haven't been referenced in the last frame. /// The picture cache code manually keeps tiles alive by calling `request` on /// them if it wants to retain a tile that is currently not visible. pubfn expire_old_tiles(&mutself, pending_updates: &'color:red'>mut TextureUpdateList) { for i in (0 .. self.cache_handles.len()).rev() { let evict = { let entry = self.cache_entries.get(
&self.cache_handles[i]
);
// This function is called at the beginning of the frame, // so we don't yet know which picture cache tiles will be // requested this frame. Therefore only evict picture cache // tiles which weren't requested in the *previous* frame.
entry.last_access.frame_id() < self.now.frame_id() - 1
};
if evict { let handle = self.cache_handles.swap_remove(i); let entry = self.cache_entries.free(handle); self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
}
}
}
pubfn clear(&mutself, pending_updates: &>mut TextureUpdateList) { for handle in mem::take(&mutself.cache_handles) { let entry = self.cache_entries.free(handle); self.free_tile(entry.texture_id, self.now.frame_id(), pending_updates);
}
for texture inself.textures.drain(..) {
pending_updates.push_free(texture.texture_id);
}
}
/// Simple garbage collect of picture cache tiles pubfn gc(
&mutself,
pending_updates: &mut TextureUpdateList,
) { // Allow the picture cache pool to keep 25% of the current allocated tile count // as free textures to be reused. This ensures the allowed tile count is appropriate // based on current window size. let free_texture_count = self.textures.len() - self.allocated_texture_count; let allowed_retained_count = (self.allocated_texture_count as f32 * 0.25).ceil() as usize; let do_gc = free_texture_count > allowed_retained_count;
if do_gc { // Sort the current pool by age, so that we remove oldest textures first self.textures.sort_unstable_by_key(|t| cmp::Reverse(t.last_frame_used));
// We can't just use retain() because `PictureTexture` requires manual cleanup. letmut allocated_targets = SmallVec::<[PictureTexture; 32]>::new(); letmut retained_targets = SmallVec::<[PictureTexture; 32]>::new();
for target inself.textures.drain(..) { if target.is_allocated { // Allocated targets can't be collected
allocated_targets.push(target);
} elseif retained_targets.len() < allowed_retained_count { // Retain the most recently used targets up to the allowed count
retained_targets.push(target);
} else { // The rest of the targets get freed
assert_ne!(target.last_frame_used, FrameId::INVALID);
pending_updates.push_free(target.texture_id);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.