Skip to content

Commit

Permalink
- MortonEncoder2D was not correctly interopolating its dimensions size
Browse files Browse the repository at this point in the history
with its allocation size, causing tile overwrites
- `MortonEncoder2D` now bounds its size to actual min/max encoded
dimensions, instead of power of 2.
- Added test_encoders test sweeps to cover all encoder cases
- adds a upper-bound optimization to the classics
`MortonEncoder`, limiting xyz size to the highest possible indexed value
for the given dimensions`

cargo fmt
  • Loading branch information
jaynus committed Dec 5, 2019
1 parent c4a0eaa commit 54663c7
Show file tree
Hide file tree
Showing 3 changed files with 64 additions and 24 deletions.
9 changes: 4 additions & 5 deletions amethyst_tiles/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -34,9 +34,8 @@ pub trait CoordinateEncoder: 'static + Clone + Default + Send + Sync {
/// Decode the provided 1-dimensional array index into its associated 3-dimensional coordinates.
fn decode(&self, morton: u32) -> Option<(u32, u32, u32)>;

/// This function transforms the provided dimensions, performing any extra allocation needed for
/// padding our indexing method.
fn allocation_size(dimensions: Vector3<u32>) -> Vector3<u32>;
/// This function returns the actual number of elements allocated for a given dimension set and encoder.
fn allocation_size(dimensions: Vector3<u32>) -> usize;
}

/// The most basic encoder, which strictly flattens the 3d space into 1d coordinates in a linear fashion.
Expand Down Expand Up @@ -77,7 +76,7 @@ impl CoordinateEncoder for FlatEncoder {
}

#[must_use]
fn allocation_size(dimensions: Vector3<u32>) -> Vector3<u32> {
dimensions
fn allocation_size(dimensions: Vector3<u32>) -> usize {
(dimensions.x * dimensions.y * dimensions.z) as usize
}
}
15 changes: 7 additions & 8 deletions amethyst_tiles/src/map.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,12 +145,11 @@ impl<T: Tile, E: CoordinateEncoder> TileMap<T, E> {
let transform = create_transform(&dimensions, &tile_dimensions);

// Round the dimensions to the nearest multiplier for morton rounding
let encoder_dimensions = E::allocation_size(dimensions);
let size = (encoder_dimensions.x * encoder_dimensions.y * encoder_dimensions.z) as usize;
let size = E::allocation_size(dimensions);
let mut data = Vec::with_capacity(size);
data.resize_with(size, T::default);

let encoder = E::from_dimensions(encoder_dimensions);
let encoder = E::from_dimensions(dimensions);

Self {
data,
Expand Down Expand Up @@ -409,8 +408,8 @@ mod tests {
let mut inner = TileMap::<TestTile, E>::new(dimensions, Vector3::new(10, 10, 1), None);
let map = UnsafeWrapper::new(&mut inner);

(0..dimensions.x).into_par_iter().for_each(|x| {
(0..dimensions.y).into_par_iter().for_each(|y| {
(0..dimensions.x).into_iter().for_each(|x| {
(0..dimensions.y).into_iter().for_each(|y| {
for z in 0..dimensions.z {
let point = Point3::new(x, y, z);

Expand All @@ -419,8 +418,8 @@ mod tests {
});
});

(0..dimensions.x).into_par_iter().for_each(|x| {
(0..dimensions.y).into_par_iter().for_each(|y| {
(0..dimensions.x).into_iter().for_each(|x| {
(0..dimensions.y).into_iter().for_each(|y| {
for z in 0..dimensions.z {
let point = Point3::new(x, y, z);
assert_eq!(map.get().get(&Point3::new(x, y, z)).unwrap().point, point);
Expand All @@ -444,7 +443,7 @@ mod tests {
Vector3::new(1, 2, 5),
];

test_dimensions.par_iter().for_each(|dimensions| {
test_dimensions.into_par_iter().for_each(|dimensions| {
test_single_map::<MortonEncoder>(*dimensions);
test_single_map::<MortonEncoder2D>(*dimensions);
test_single_map::<FlatEncoder>(*dimensions);
Expand Down
64 changes: 53 additions & 11 deletions amethyst_tiles/src/morton/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -110,13 +110,8 @@ impl CoordinateEncoder for MortonEncoder {
}

#[must_use]
fn allocation_size(dimensions: Vector3<u32>) -> Vector3<u32> {
let max = dimensions
.x
.max(dimensions.y)
.max(dimensions.z)
.next_power_of_two();
Vector3::new(max, max, max)
fn allocation_size(dimensions: Vector3<u32>) -> usize {
encode(dimensions.x, dimensions.y, dimensions.z) as usize
}
}

Expand All @@ -138,8 +133,10 @@ pub struct MortonEncoder2D {
impl CoordinateEncoder for MortonEncoder2D {
#[must_use]
fn from_dimensions(dimensions: Vector3<u32>) -> Self {
use bitintr::Pdep;

Self {
len: dimensions.x * dimensions.y,
len: dimensions.x.pdep(0x5555_5555) | dimensions.y.pdep(0xAAAA_AAAA),
}
}

Expand Down Expand Up @@ -175,9 +172,10 @@ impl CoordinateEncoder for MortonEncoder2D {
}

#[must_use]
fn allocation_size(dimensions: Vector3<u32>) -> Vector3<u32> {
let max = dimensions.x.max(dimensions.y).next_power_of_two();
Vector3::new(max, max, dimensions.z)
fn allocation_size(dimensions: Vector3<u32>) -> usize {
use bitintr::Pdep;

((dimensions.x.pdep(0x5555_5555) | dimensions.y.pdep(0xAAAA_AAAA)) * dimensions.z) as usize
}
}

Expand Down Expand Up @@ -208,6 +206,50 @@ mod tests {
use more_asserts::*;
use rayon::prelude::*;

pub fn test_encoder<E: CoordinateEncoder>(dimensions: Vector3<u32>) {
let encoder = E::from_dimensions(dimensions);

for x in 0..dimensions.x {
for y in 0..dimensions.y {
for z in 0..dimensions.z {
let value = encoder.encode(x, y, z);
assert!(value.is_some());
let (x2, y2, z2) = encoder.decode(value.unwrap()).unwrap();
assert_eq!(x, x2);
assert_eq!(y, y2);
assert_eq!(z, z2);
}
}
}
}

#[test]
fn test_encoders() {
let test_dimensions = [
Vector3::new(50, 50, 3),
Vector3::new(10, 58, 54),
Vector3::new(66, 5, 20),
Vector3::new(199, 100, 1),
Vector3::new(5, 55, 6),
Vector3::new(15, 23, 1),
Vector3::new(20, 12, 12),
Vector3::new(48, 48, 12),
Vector3::new(12, 55, 12),
Vector3::new(26, 25, 1),
Vector3::new(1, 2, 5),
];

test_dimensions
.into_par_iter()
.for_each(|dimensions| test_encoder::<crate::FlatEncoder>(*dimensions));
test_dimensions
.into_par_iter()
.for_each(|dimensions| test_encoder::<MortonEncoder>(*dimensions));
test_dimensions
.into_par_iter()
.for_each(|dimensions| test_encoder::<MortonEncoder2D>(*dimensions));
}

#[test]
fn morton_minmax() {
let zero = encode(0, 0, 0);
Expand Down

0 comments on commit 54663c7

Please sign in to comment.