Skip to content

Commit 9921f2e

Browse files
author
bors-servo
authored
Auto merge of #2702 - glennw:slab-rects, r=mrobinson
Support non-square slab allocator pages in texture cache. Text runs are typically rectangles that are much longer in one dimension than the other dimension (depending on horizontal or vertical layout direction). Storing rectangles of this shape in the texture cache was very inefficient, since only square allocations are supported. This patch adds support for texture cache regions to support non-square slab sizes. This is an efficiency win for any image that is rectangular, and will become particularly important once we start caching text runs in the texture cache. <!-- Reviewable:start --> --- This change is [<img src="https://reviewable.io/review_button.svg" height="34" align="absmiddle" alt="Reviewable"/>](https://reviewable.io/reviews/servo/webrender/2702) <!-- Reviewable:end -->
2 parents 4440d85 + f653283 commit 9921f2e

File tree

1 file changed

+69
-51
lines changed

1 file changed

+69
-51
lines changed

webrender/src/texture_cache.rs

Lines changed: 69 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -639,7 +639,7 @@ impl TextureCache {
639639
// - We have freed an item that will definitely allow us to
640640
// fit the currently requested allocation.
641641
let needed_slab_size =
642-
SlabSize::new(required_alloc.width, required_alloc.height).get_size();
642+
SlabSize::new(required_alloc.width, required_alloc.height);
643643
let mut found_matching_slab = false;
644644
let mut freed_complete_page = false;
645645
let mut evicted_items = 0;
@@ -765,11 +765,12 @@ impl TextureCache {
765765
allowed_in_shared_cache = false;
766766
}
767767

768-
// Anything larger than 512 goes in a standalone texture.
768+
// Anything larger than TEXTURE_REGION_DIMENSIONS goes in a standalone texture.
769769
// TODO(gw): If we find pages that suffer from batch breaks in this
770770
// case, add support for storing these in a standalone
771771
// texture array.
772-
if descriptor.width > 512 || descriptor.height > 512 {
772+
if descriptor.width > TEXTURE_REGION_DIMENSIONS ||
773+
descriptor.height > TEXTURE_REGION_DIMENSIONS {
773774
allowed_in_shared_cache = false;
774775
}
775776

@@ -900,43 +901,48 @@ impl TextureCache {
900901
}
901902
}
902903

903-
// A list of the block sizes that a region can be initialized with.
904+
#[cfg_attr(feature = "capture", derive(Serialize))]
905+
#[cfg_attr(feature = "replay", derive(Deserialize))]
904906
#[derive(Copy, Clone, PartialEq)]
905-
enum SlabSize {
906-
Size16x16,
907-
Size32x32,
908-
Size64x64,
909-
Size128x128,
910-
Size256x256,
911-
Size512x512,
907+
struct SlabSize {
908+
width: u32,
909+
height: u32,
912910
}
913911

914912
impl SlabSize {
915913
fn new(width: u32, height: u32) -> SlabSize {
916-
// TODO(gw): Consider supporting non-square
917-
// allocator sizes here.
918-
let max_dim = cmp::max(width, height);
919-
920-
match max_dim {
921-
0 => unreachable!(),
922-
1...16 => SlabSize::Size16x16,
923-
17...32 => SlabSize::Size32x32,
924-
33...64 => SlabSize::Size64x64,
925-
65...128 => SlabSize::Size128x128,
926-
129...256 => SlabSize::Size256x256,
927-
257...512 => SlabSize::Size512x512,
928-
_ => panic!("Invalid dimensions for cache!"),
914+
let x_size = quantize_dimension(width);
915+
let y_size = quantize_dimension(height);
916+
917+
assert!(x_size > 0 && x_size <= TEXTURE_REGION_DIMENSIONS);
918+
assert!(y_size > 0 && y_size <= TEXTURE_REGION_DIMENSIONS);
919+
920+
let (width, height) = match (x_size, y_size) {
921+
// Special cased rectangular slab pages.
922+
(512, 256) => (512, 256),
923+
(512, 128) => (512, 128),
924+
(512, 64) => (512, 64),
925+
(256, 512) => (256, 512),
926+
(128, 512) => (128, 512),
927+
( 64, 512) => ( 64, 512),
928+
929+
// If none of those fit, use a square slab size.
930+
(x_size, y_size) => {
931+
let square_size = cmp::max(x_size, y_size);
932+
(square_size, square_size)
933+
}
934+
};
935+
936+
SlabSize {
937+
width,
938+
height,
929939
}
930940
}
931941

932-
fn get_size(&self) -> u32 {
933-
match *self {
934-
SlabSize::Size16x16 => 16,
935-
SlabSize::Size32x32 => 32,
936-
SlabSize::Size64x64 => 64,
937-
SlabSize::Size128x128 => 128,
938-
SlabSize::Size256x256 => 256,
939-
SlabSize::Size512x512 => 512,
942+
fn invalid() -> SlabSize {
943+
SlabSize {
944+
width: 0,
945+
height: 0,
940946
}
941947
}
942948
}
@@ -960,9 +966,8 @@ impl TextureLocation {
960966
struct TextureRegion {
961967
layer_index: i32,
962968
region_size: u32,
963-
slab_size: u32,
969+
slab_size: SlabSize,
964970
free_slots: Vec<TextureLocation>,
965-
slots_per_axis: u32,
966971
total_slot_count: usize,
967972
origin: DeviceUintPoint,
968973
}
@@ -972,25 +977,25 @@ impl TextureRegion {
972977
TextureRegion {
973978
layer_index,
974979
region_size,
975-
slab_size: 0,
980+
slab_size: SlabSize::invalid(),
976981
free_slots: Vec::new(),
977-
slots_per_axis: 0,
978982
total_slot_count: 0,
979983
origin,
980984
}
981985
}
982986

983987
// Initialize a region to be an allocator for a specific slab size.
984988
fn init(&mut self, slab_size: SlabSize) {
985-
debug_assert!(self.slab_size == 0);
989+
debug_assert!(self.slab_size == SlabSize::invalid());
986990
debug_assert!(self.free_slots.is_empty());
987991

988-
self.slab_size = slab_size.get_size();
989-
self.slots_per_axis = self.region_size / self.slab_size;
992+
self.slab_size = slab_size;
993+
let slots_per_x_axis = self.region_size / self.slab_size.width;
994+
let slots_per_y_axis = self.region_size / self.slab_size.height;
990995

991996
// Add each block to a freelist.
992-
for y in 0 .. self.slots_per_axis {
993-
for x in 0 .. self.slots_per_axis {
997+
for y in 0 .. slots_per_y_axis {
998+
for x in 0 .. slots_per_x_axis {
994999
self.free_slots.push(TextureLocation::new(x, y));
9951000
}
9961001
}
@@ -1001,30 +1006,31 @@ impl TextureRegion {
10011006
// Deinit a region, allowing it to become a region with
10021007
// a different allocator size.
10031008
fn deinit(&mut self) {
1004-
self.slab_size = 0;
1009+
self.slab_size = SlabSize::invalid();
10051010
self.free_slots.clear();
1006-
self.slots_per_axis = 0;
10071011
self.total_slot_count = 0;
10081012
}
10091013

10101014
fn is_empty(&self) -> bool {
1011-
self.slab_size == 0
1015+
self.slab_size == SlabSize::invalid()
10121016
}
10131017

10141018
// Attempt to allocate a fixed size block from this region.
10151019
fn alloc(&mut self) -> Option<DeviceUintPoint> {
1020+
debug_assert!(self.slab_size != SlabSize::invalid());
1021+
10161022
self.free_slots.pop().map(|location| {
10171023
DeviceUintPoint::new(
1018-
self.origin.x + self.slab_size * location.0 as u32,
1019-
self.origin.y + self.slab_size * location.1 as u32,
1024+
self.origin.x + self.slab_size.width * location.0 as u32,
1025+
self.origin.y + self.slab_size.height * location.1 as u32,
10201026
)
10211027
})
10221028
}
10231029

10241030
// Free a block in this region.
10251031
fn free(&mut self, point: DeviceUintPoint) {
1026-
let x = (point.x - self.origin.x) / self.slab_size;
1027-
let y = (point.y - self.origin.y) / self.slab_size;
1032+
let x = (point.x - self.origin.x) / self.slab_size.width;
1033+
let y = (point.y - self.origin.y) / self.slab_size.height;
10281034
self.free_slots.push(TextureLocation::new(x, y));
10291035

10301036
// If this region is completely unused, deinit it
@@ -1118,7 +1124,6 @@ impl TextureArray {
11181124
// Quantize the size of the allocation to select a region to
11191125
// allocate from.
11201126
let slab_size = SlabSize::new(width, height);
1121-
let slab_size_dim = slab_size.get_size();
11221127

11231128
// TODO(gw): For simplicity, the initial implementation just
11241129
// has a single vec<> of regions. We could easily
@@ -1134,9 +1139,9 @@ impl TextureArray {
11341139
// Run through the existing regions of this size, and see if
11351140
// we can find a free block in any of them.
11361141
for (i, region) in self.regions.iter_mut().enumerate() {
1137-
if region.slab_size == 0 {
1142+
if region.is_empty() {
11381143
empty_region_index = Some(i);
1139-
} else if region.slab_size == slab_size_dim {
1144+
} else if region.slab_size == slab_size {
11401145
if let Some(location) = region.alloc() {
11411146
entry_kind = Some(EntryKind::Cache {
11421147
layer_index: region.layer_index as u16,
@@ -1244,3 +1249,16 @@ impl TextureUpdate {
12441249
}
12451250
}
12461251
}
1252+
1253+
fn quantize_dimension(size: u32) -> u32 {
1254+
match size {
1255+
0 => unreachable!(),
1256+
1...16 => 16,
1257+
17...32 => 32,
1258+
33...64 => 64,
1259+
65...128 => 128,
1260+
129...256 => 256,
1261+
257...512 => 512,
1262+
_ => panic!("Invalid dimensions for cache!"),
1263+
}
1264+
}

0 commit comments

Comments
 (0)