mirror of
https://gitlab.com/veloren/veloren.git
synced 2024-08-30 18:12:32 +00:00
Reduce chunks / chonk by trimming the ends.
This improves the defragment operation for chonks by letting them remove chunks at the top that match above, and bottom that match below. This reduces the chunks / chonk from around 5.9 to around 3.4 at origin. From my investigations, adding something for water would probably get us a full 50% reduction, if we could collapse intermediate chunks; block types other than rock / air / water never appear to have full chunks of the same block, so any additional optimization will require changes to the subchunk compression format or changes to the actual chunks we generate.
This commit is contained in:
parent
25ab132d73
commit
69f68ddf29
@ -90,7 +90,47 @@ impl<V, S: RectVolSize, M: Clone> Chonk<V, S, M> {
|
||||
where
|
||||
V: Clone + Eq + Hash,
|
||||
{
|
||||
// First, defragment all subchunks.
|
||||
self.sub_chunks.iter_mut().for_each(SubChunk::defragment);
|
||||
// For each homogeneous subchunk (i.e. those where all blocks are the same),
|
||||
// find those which match `below` at the bottom of the cunk, or `above`
|
||||
// at the top, since these subchunks are redundant and can be removed.
|
||||
// Note that we find (and drain) the above chunks first, so that when we
|
||||
// remove the below chunks we have fewer remaining chunks to backshift.
|
||||
// Note that we use `take_while` instead of `rposition` here because `rposition`
|
||||
// goes one past the end, which we only want in the forward direction.
|
||||
let above_count = self
|
||||
.sub_chunks
|
||||
.iter()
|
||||
.rev()
|
||||
.take_while(|subchunk| subchunk.homogeneous() == Some(&self.above))
|
||||
.count();
|
||||
// Unfortunately, `TakeWhile` doesn't implement `ExactSizeIterator` or
|
||||
// `DoubleEndedIterator`, so we have to recreate the same state by calling
|
||||
// `nth_back` (note that passing 0 to nth_back goes back 1 time, not 0
|
||||
// times!).
|
||||
let mut subchunks = self.sub_chunks.iter();
|
||||
if above_count > 0 {
|
||||
subchunks.nth_back(above_count - 1);
|
||||
}
|
||||
// `above_index` is now the number of remaining elements, since all the elements
|
||||
// we drained were at the end.
|
||||
let above_index = subchunks.len();
|
||||
// `below_len` now needs to be applied to the state after the `above` chunks are
|
||||
// drained, to make sure we don't accidentally have overlap (this is
|
||||
// possible if self.above == self.below).
|
||||
let below_len = subchunks.position(|subchunk| subchunk.homogeneous() != Some(&self.below));
|
||||
let below_len = below_len
|
||||
// NOTE: If `below_index` is `None`, then every *remaining* chunk after we drained
|
||||
// `above` was full and matched `below`.
|
||||
.unwrap_or(above_index);
|
||||
// Now, actually remove the redundant chunks.
|
||||
self.sub_chunks.truncate(above_index);
|
||||
self.sub_chunks.drain(..below_len);
|
||||
// Finally, bump the z_offset to account for the removed subchunks at the
|
||||
// bottom. TODO: Add invariants to justify why `below_len` must fit in
|
||||
// i32.
|
||||
self.z_offset += below_len as i32 * SubChunkSize::<S>::SIZE.z as i32;
|
||||
}
|
||||
}
|
||||
|
||||
@ -131,6 +171,10 @@ impl<V: Clone + PartialEq, S: RectVolSize, M: Clone> WriteVol for Chonk<V, S, M>
|
||||
let mut sub_chunk_idx = self.sub_chunk_idx(pos.z);
|
||||
|
||||
if pos.z < self.get_min_z() {
|
||||
// Make sure we're not adding a redundant chunk.
|
||||
if block == self.below {
|
||||
return Ok(());
|
||||
}
|
||||
// Prepend exactly sufficiently many SubChunks via Vec::splice
|
||||
let c = Chunk::<V, SubChunkSize<S>, M>::filled(self.below.clone(), self.meta.clone());
|
||||
let n = (-sub_chunk_idx) as usize;
|
||||
@ -138,6 +182,10 @@ impl<V: Clone + PartialEq, S: RectVolSize, M: Clone> WriteVol for Chonk<V, S, M>
|
||||
self.z_offset += sub_chunk_idx * SubChunkSize::<S>::SIZE.z as i32;
|
||||
sub_chunk_idx = 0;
|
||||
} else if pos.z >= self.get_max_z() {
|
||||
// Make sure we're not adding a redundant chunk.
|
||||
if block == self.above {
|
||||
return Ok(());
|
||||
}
|
||||
// Append exactly sufficiently many SubChunks via Vec::extend
|
||||
let c = Chunk::<V, SubChunkSize<S>, M>::filled(self.above.clone(), self.meta.clone());
|
||||
let n = 1 + sub_chunk_idx as usize - self.sub_chunks.len();
|
||||
|
@ -204,6 +204,18 @@ impl<V, S: VolSize, M> Chunk<V, S, M> {
|
||||
|
||||
pub fn num_groups(&self) -> usize { self.vox.len() / Self::GROUP_VOLUME as usize }
|
||||
|
||||
/// Returns `Some(v)` if the block is homogeneous and contains nothing but
|
||||
/// voxels of value `v`, and `None` otherwise. This method is
|
||||
/// conservative (it may return None when the chunk is
|
||||
/// actually homogeneous) unless called immediately after `defragment`.
|
||||
pub fn homogeneous(&self) -> Option<&V> {
|
||||
if self.num_groups() == 0 {
|
||||
Some(&self.default)
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
|
||||
#[inline(always)]
|
||||
fn grp_idx(pos: Vec3<i32>) -> u32 {
|
||||
let grp_pos = pos.map2(Self::GROUP_SIZE, |e, s| e as u32 / s);
|
||||
|
Loading…
Reference in New Issue
Block a user