зеркало из https://github.com/mozilla/gecko-dev.git
Bug 1682365 - Pt 3 - Remove array textures from picture cache tiles. r=nical,jnicol
This patch makes picture cache tiles use normal textures instead of array textures. With this and the previous patch, WR no longer uses array textures at all (except when provided by the external image handler trait). Differential Revision: https://phabricator.services.mozilla.com/D99013
This commit is contained in:
Родитель
1ef677536a
Коммит
cee05879d4
|
@ -4760,14 +4760,14 @@ impl Renderer {
|
|||
.borrow_mut()
|
||||
.get_composite_shader(
|
||||
CompositeSurfaceFormat::Rgba,
|
||||
ImageBufferKind::Texture2DArray,
|
||||
ImageBufferKind::Texture2D,
|
||||
).bind(
|
||||
&mut self.device,
|
||||
projection,
|
||||
&mut self.renderer_errors
|
||||
);
|
||||
|
||||
let mut current_shader_params = (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray);
|
||||
let mut current_shader_params = (CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2D);
|
||||
let mut current_textures = BatchTextures::empty();
|
||||
let mut instances = Vec::new();
|
||||
|
||||
|
@ -4837,7 +4837,7 @@ impl Renderer {
|
|||
tile.z_id,
|
||||
),
|
||||
BatchTextures::composite_rgb(texture),
|
||||
(CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2DArray),
|
||||
(CompositeSurfaceFormat::Rgba, ImageBufferKind::Texture2D),
|
||||
)
|
||||
}
|
||||
CompositeTileSurface::ExternalSurface { external_surface_index } => {
|
||||
|
|
|
@ -18,12 +18,13 @@ use crate::internal_types::{
|
|||
};
|
||||
use crate::lru_cache::LRUCache;
|
||||
use crate::profiler::{self, TransactionProfile};
|
||||
use crate::render_backend::FrameStamp;
|
||||
use crate::render_backend::{FrameStamp, FrameId};
|
||||
use crate::resource_cache::{CacheItem, CachedImageData};
|
||||
use crate::atlas_allocator::*;
|
||||
use crate::slab_allocator::*;
|
||||
use smallvec::SmallVec;
|
||||
use std::cell::Cell;
|
||||
use std::mem;
|
||||
use std::{cmp, mem};
|
||||
use std::rc::Rc;
|
||||
use euclid::size2;
|
||||
|
||||
|
@ -42,11 +43,6 @@ pub enum TargetShader {
|
|||
/// The size of each region in shared cache texture arrays.
|
||||
pub const TEXTURE_REGION_DIMENSIONS: i32 = 512;
|
||||
|
||||
const PICTURE_TEXTURE_SLICE_COUNT: usize = 8;
|
||||
|
||||
/// The chosen image format for picture tiles.
|
||||
const PICTURE_TILE_FORMAT: ImageFormat = ImageFormat::RGBA8;
|
||||
|
||||
/// Items in the texture cache can either be standalone textures,
|
||||
/// or a sub-rect inside the shared cache.
|
||||
#[derive(Debug)]
|
||||
|
@ -58,10 +54,8 @@ enum EntryDetails {
|
|||
size_in_bytes: usize,
|
||||
},
|
||||
Picture {
|
||||
// Index in the picture_textures array
|
||||
texture_index: usize,
|
||||
// Slice in the texture array
|
||||
layer_index: usize,
|
||||
/// Size of the tile (used for debug clears only)
|
||||
size: DeviceIntSize,
|
||||
},
|
||||
Cache {
|
||||
/// Origin within the texture layer where this item exists.
|
||||
|
@ -72,10 +66,11 @@ enum EntryDetails {
|
|||
}
|
||||
|
||||
impl EntryDetails {
|
||||
// TODO(gw): We can remove LayerIndex from here completely in a follow up
|
||||
fn describe(&self) -> (LayerIndex, DeviceIntPoint) {
|
||||
match *self {
|
||||
EntryDetails::Standalone { .. } => (0, DeviceIntPoint::zero()),
|
||||
EntryDetails::Picture { layer_index, .. } => (layer_index, DeviceIntPoint::zero()),
|
||||
EntryDetails::Picture { .. } => (0, DeviceIntPoint::zero()),
|
||||
EntryDetails::Cache { origin, .. } => (0, origin),
|
||||
}
|
||||
}
|
||||
|
@ -351,12 +346,34 @@ impl SharedTextures {
|
|||
}
|
||||
}
|
||||
|
||||
/// The texture arrays used to hold picture cache tiles.
|
||||
/// 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,
|
||||
format: ImageFormat,
|
||||
is_allocated: bool,
|
||||
last_frame_used: FrameId,
|
||||
}
|
||||
|
||||
impl PictureTexture {
|
||||
fn size_in_bytes(&self) -> usize {
|
||||
let bpp = self.format.bytes_per_pixel() as usize;
|
||||
(self.size.width * self.size.height) as usize * bpp
|
||||
}
|
||||
}
|
||||
|
||||
/// The textures used to hold picture cache tiles.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
struct PictureTextures {
|
||||
textures: Vec<WholeTextureArray>,
|
||||
/// 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,
|
||||
}
|
||||
|
||||
impl PictureTextures {
|
||||
|
@ -366,6 +383,7 @@ impl PictureTextures {
|
|||
PictureTextures {
|
||||
textures: Vec::new(),
|
||||
default_tile_size,
|
||||
allocated_texture_count: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -376,50 +394,86 @@ impl PictureTextures {
|
|||
next_texture_id: &mut CacheTextureId,
|
||||
pending_updates: &mut TextureUpdateList,
|
||||
) -> CacheEntry {
|
||||
// Attempt to find an existing texture with matching tile size and
|
||||
// and available slice.
|
||||
for (i, texture) in self.textures.iter_mut().enumerate() {
|
||||
if texture.size == tile_size {
|
||||
if let Some(layer_index) = texture.find_free() {
|
||||
return texture.occupy(i, layer_index, now);
|
||||
}
|
||||
let mut texture_id = None;
|
||||
self.allocated_texture_count += 1;
|
||||
|
||||
for texture in &mut self.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;
|
||||
}
|
||||
}
|
||||
|
||||
// Allocate a new texture with fixed number of slices.
|
||||
let mut slices = Vec::new();
|
||||
for _ in 0 .. PICTURE_TEXTURE_SLICE_COUNT {
|
||||
slices.push(WholeTextureSlice {
|
||||
uv_rect_handle: None,
|
||||
// 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: TextureFilter::Linear,
|
||||
layer_count: 1,
|
||||
is_shared_cache: false,
|
||||
has_depth: true,
|
||||
};
|
||||
|
||||
pending_updates.push_alloc(texture_id, info);
|
||||
|
||||
self.textures.push(PictureTexture {
|
||||
texture_id,
|
||||
is_allocated: true,
|
||||
format: ImageFormat::RGBA8,
|
||||
size: tile_size,
|
||||
last_frame_used: FrameId::INVALID,
|
||||
});
|
||||
}
|
||||
let mut texture = WholeTextureArray {
|
||||
|
||||
texture_id
|
||||
});
|
||||
|
||||
CacheEntry {
|
||||
size: tile_size,
|
||||
user_data: [0.0; 3],
|
||||
last_access: now,
|
||||
details: EntryDetails::Picture {
|
||||
size: tile_size,
|
||||
},
|
||||
uv_rect_handle: GpuCacheHandle::new(),
|
||||
input_format: ImageFormat::RGBA8,
|
||||
filter: TextureFilter::Nearest,
|
||||
format: PICTURE_TILE_FORMAT,
|
||||
texture_id: *next_texture_id,
|
||||
slices,
|
||||
has_depth: true,
|
||||
};
|
||||
next_texture_id.0 += 1;
|
||||
|
||||
// Occupy the first slice of the new texture
|
||||
let entry = texture.occupy(
|
||||
self.textures.len(),
|
||||
0,
|
||||
now,
|
||||
);
|
||||
|
||||
// Push the alloc to render thread pending updates
|
||||
let info = texture.to_info();
|
||||
pending_updates.push_alloc(texture.texture_id, info);
|
||||
self.textures.push(texture);
|
||||
|
||||
entry
|
||||
swizzle: Swizzle::default(),
|
||||
texture_id,
|
||||
eviction_notice: None,
|
||||
uv_rect_kind: UvRectKind::Rect,
|
||||
shader: TargetShader::Default,
|
||||
}
|
||||
}
|
||||
|
||||
fn get(&mut self, index: usize) -> &mut WholeTextureArray {
|
||||
&mut self.textures[index]
|
||||
fn free_tile(
|
||||
&mut self,
|
||||
id: CacheTextureId,
|
||||
current_frame_id: FrameId,
|
||||
) {
|
||||
self.allocated_texture_count -= 1;
|
||||
|
||||
let texture = self.textures
|
||||
.iter_mut()
|
||||
.find(|t| t.texture_id == id)
|
||||
.expect("bug: invalid texture id");
|
||||
|
||||
assert!(texture.is_allocated);
|
||||
texture.is_allocated = false;
|
||||
|
||||
assert_eq!(texture.last_frame_used, FrameId::INVALID);
|
||||
texture.last_frame_used = current_frame_id;
|
||||
}
|
||||
|
||||
fn clear(&mut self, pending_updates: &mut TextureUpdateList) {
|
||||
|
@ -429,17 +483,56 @@ impl PictureTextures {
|
|||
}
|
||||
|
||||
fn update_profile(&self, profile: &mut TransactionProfile) {
|
||||
// For now, this profile counter just accumulates the slices and bytes
|
||||
// from all picture cache texture arrays.
|
||||
let mut picture_slices = 0;
|
||||
// For now, this profile counter just accumulates the tiles and bytes
|
||||
// from all picture cache textures.
|
||||
let mut picture_tiles = 0;
|
||||
let mut picture_bytes = 0;
|
||||
for texture in &self.textures {
|
||||
picture_slices += texture.slices.len();
|
||||
picture_tiles += 1;
|
||||
picture_bytes += texture.size_in_bytes();
|
||||
}
|
||||
profile.set(profiler::PICTURE_TILES, picture_slices);
|
||||
profile.set(profiler::PICTURE_TILES, picture_tiles);
|
||||
profile.set(profiler::PICTURE_TILES_MEM, profiler::bytes_to_mb(picture_bytes));
|
||||
}
|
||||
|
||||
/// Simple garbage collect of picture cache tiles
|
||||
fn gc(
|
||||
&mut self,
|
||||
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.
|
||||
let mut allocated_targets = SmallVec::<[PictureTexture; 32]>::new();
|
||||
let mut retained_targets = SmallVec::<[PictureTexture; 32]>::new();
|
||||
|
||||
for target in self.textures.drain(..) {
|
||||
if target.is_allocated {
|
||||
// Allocated targets can't be collected
|
||||
allocated_targets.push(target);
|
||||
} else if 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);
|
||||
}
|
||||
}
|
||||
|
||||
self.textures.extend(retained_targets);
|
||||
self.textures.extend(allocated_targets);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Container struct for the various parameters used in cache allocation.
|
||||
|
@ -637,6 +730,9 @@ impl TextureCache {
|
|||
pub fn end_frame(&mut self, profile: &mut TransactionProfile) {
|
||||
debug_assert!(self.now.is_valid());
|
||||
self.expire_old_picture_cache_tiles();
|
||||
self.picture_textures.gc(
|
||||
&mut self.pending_updates,
|
||||
);
|
||||
|
||||
let updates = &mut self.pending_updates; // To avoid referring to self in the closure.
|
||||
let callback = &mut|texture_id| { updates.push_free(texture_id); };
|
||||
|
@ -988,9 +1084,8 @@ impl TextureCache {
|
|||
// Free a cache entry from the standalone list or shared cache.
|
||||
fn free(&mut self, entry: &CacheEntry) {
|
||||
match entry.details {
|
||||
EntryDetails::Picture { texture_index, layer_index } => {
|
||||
let picture_texture = self.picture_textures.get(texture_index);
|
||||
picture_texture.slices[layer_index].uv_rect_handle = None;
|
||||
EntryDetails::Picture { size } => {
|
||||
self.picture_textures.free_tile(entry.texture_id, self.now.frame_id());
|
||||
if self.debug_flags.contains(
|
||||
DebugFlags::TEXTURE_CACHE_DBG |
|
||||
DebugFlags::TEXTURE_CACHE_DBG_CLEAR_EVICTED)
|
||||
|
@ -998,9 +1093,9 @@ impl TextureCache {
|
|||
self.pending_updates.push_debug_clear(
|
||||
entry.texture_id,
|
||||
DeviceIntPoint::zero(),
|
||||
picture_texture.size.width,
|
||||
picture_texture.size.height,
|
||||
layer_index,
|
||||
size.width,
|
||||
size.height,
|
||||
0,
|
||||
);
|
||||
}
|
||||
}
|
||||
|
@ -1315,99 +1410,6 @@ pub struct TextureParameters {
|
|||
pub filter: TextureFilter,
|
||||
}
|
||||
|
||||
/// A tracking structure for each slice in `WholeTextureArray`.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
#[derive(Clone, Copy, Debug)]
|
||||
struct WholeTextureSlice {
|
||||
uv_rect_handle: Option<GpuCacheHandle>,
|
||||
}
|
||||
|
||||
/// A texture array that allocates whole slices and doesn't do any region tracking.
|
||||
#[cfg_attr(feature = "capture", derive(Serialize))]
|
||||
#[cfg_attr(feature = "replay", derive(Deserialize))]
|
||||
struct WholeTextureArray {
|
||||
size: DeviceIntSize,
|
||||
filter: TextureFilter,
|
||||
format: ImageFormat,
|
||||
texture_id: CacheTextureId,
|
||||
slices: Vec<WholeTextureSlice>,
|
||||
has_depth: bool,
|
||||
}
|
||||
|
||||
impl WholeTextureArray {
|
||||
fn to_info(&self) -> TextureCacheAllocInfo {
|
||||
TextureCacheAllocInfo {
|
||||
target: ImageBufferKind::Texture2DArray,
|
||||
width: self.size.width,
|
||||
height: self.size.height,
|
||||
format: self.format,
|
||||
filter: self.filter,
|
||||
layer_count: self.slices.len() as i32,
|
||||
is_shared_cache: true, //TODO: reconsider
|
||||
has_depth: self.has_depth,
|
||||
}
|
||||
}
|
||||
|
||||
/// Returns the number of GPU bytes consumed by this texture array.
|
||||
fn size_in_bytes(&self) -> usize {
|
||||
let bpp = self.format.bytes_per_pixel() as usize;
|
||||
self.slices.len() * (self.size.width * self.size.height) as usize * bpp
|
||||
}
|
||||
|
||||
/// Find an free slice.
|
||||
fn find_free(&self) -> Option<LayerIndex> {
|
||||
self.slices.iter().position(|slice| slice.uv_rect_handle.is_none())
|
||||
}
|
||||
|
||||
fn cache_entry_impl(
|
||||
&self,
|
||||
texture_index: usize,
|
||||
layer_index: usize,
|
||||
now: FrameStamp,
|
||||
uv_rect_handle: GpuCacheHandle,
|
||||
texture_id: CacheTextureId,
|
||||
) -> CacheEntry {
|
||||
CacheEntry {
|
||||
size: self.size,
|
||||
user_data: [0.0; 3],
|
||||
last_access: now,
|
||||
details: EntryDetails::Picture {
|
||||
texture_index,
|
||||
layer_index,
|
||||
},
|
||||
uv_rect_handle,
|
||||
input_format: self.format,
|
||||
filter: self.filter,
|
||||
swizzle: Swizzle::default(),
|
||||
texture_id,
|
||||
eviction_notice: None,
|
||||
uv_rect_kind: UvRectKind::Rect,
|
||||
shader: TargetShader::Default,
|
||||
}
|
||||
}
|
||||
|
||||
/// Occupy a specified slice by a cache entry.
|
||||
fn occupy(
|
||||
&mut self,
|
||||
texture_index: usize,
|
||||
layer_index: usize,
|
||||
now: FrameStamp,
|
||||
) -> CacheEntry {
|
||||
let uv_rect_handle = GpuCacheHandle::new();
|
||||
assert!(self.slices[layer_index].uv_rect_handle.is_none());
|
||||
self.slices[layer_index].uv_rect_handle = Some(uv_rect_handle);
|
||||
self.cache_entry_impl(
|
||||
texture_index,
|
||||
layer_index,
|
||||
now,
|
||||
uv_rect_handle,
|
||||
self.texture_id,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
impl TextureCacheUpdate {
|
||||
// Constructs a TextureCacheUpdate operation to be passed to the
|
||||
// rendering thread in order to do an upload to the right
|
||||
|
|
|
@ -15,7 +15,7 @@ skip_on(android,device) == filter-color-matrix.yaml filter-color-matrix-ref.yaml
|
|||
== filter-invert.yaml filter-invert-ref.yaml
|
||||
== filter-invert-2.yaml filter-invert-2-ref.yaml
|
||||
platform(linux,mac) fuzzy(1,133) == filter-large-blur-radius.yaml filter-large-blur-radius.png
|
||||
skip_on(android,device) fuzzy(1,12) fuzzy-if(platform(swgl),2,12276) == draw_calls(6) color_targets(6) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png # fails on Pixel2
|
||||
skip_on(android,device) fuzzy(1,12) fuzzy-if(platform(swgl),2,12276) == draw_calls(7) color_targets(6) alpha_targets(0) filter-small-blur-radius.yaml filter-small-blur-radius.png # fails on Pixel2
|
||||
== filter-saturate-red-1.yaml filter-saturate-red-1-ref.yaml
|
||||
== filter-saturate-red-2.yaml filter-saturate-red-2-ref.yaml
|
||||
== filter-saturate-red-3.yaml filter-saturate-red-3-ref.yaml
|
||||
|
|
Загрузка…
Ссылка в новой задаче