Correctly take image mipmaps into account (#797)

* Add mipmap_dimensions() and num_mipmaps()

* Correctly check mipmaps count in blit_image

* Use num_mipmaps() in UnsafeImage

* Use mipmap_dimensions() in check_copy_buffer_image()

* Documentation and rename to max_mipmaps()
This commit is contained in:
tomaka 2017-09-04 13:16:06 +02:00 committed by GitHub
parent 9f3f2dc000
commit e2b63b8184
4 changed files with 164 additions and 41 deletions

View File

@ -85,17 +85,15 @@ pub fn check_blit_image<S, D>(device: &Device, source: &S, source_top_left: [i32
});
}
if source_mip_level >= source.mipmap_levels() {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);
}
let source_dimensions = match source.dimensions().mipmap_dimensions(source_mip_level) {
Some(d) => d,
None => return Err(CheckBlitImageError::SourceCoordinatesOutOfRange),
};
if destination_mip_level >= destination.mipmap_levels() {
return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange);
}
// TODO: wrong because we don't take the mipmap level into account for the dimensions
let source_dimensions = source.dimensions();
let destination_dimensions = destination.dimensions();
let destination_dimensions = match destination.dimensions().mipmap_dimensions(destination_mip_level) {
Some(d) => d,
None => return Err(CheckBlitImageError::DestinationCoordinatesOutOfRange),
};
if source_base_array_layer + layer_count > source_dimensions.array_layers() {
return Err(CheckBlitImageError::SourceCoordinatesOutOfRange);

View File

@ -74,12 +74,10 @@ pub fn check_copy_buffer_image<B, I, P>(device: &Device, buffer: &B, image: &I,
return Err(CheckCopyBufferImageError::UnexpectedMultisampled);
}
if image_mipmap >= image.mipmap_levels() {
return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange);
}
// TODO: wrong because we don't take the mipmap level into account for the dimensions
let image_dimensions = image.dimensions();
let image_dimensions = match image.dimensions().mipmap_dimensions(image_mipmap) {
Some(d) => d,
None => return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange),
};
if image_first_layer + image_num_layers > image_dimensions.array_layers() {
return Err(CheckCopyBufferImageError::ImageCoordinatesOutOfRange);

View File

@ -46,6 +46,8 @@
//! To be written.
//!
use std::cmp;
pub use self::attachment::AttachmentImage;
pub use self::immutable::ImmutableImage;
pub use self::layout::ImageLayout;
@ -396,4 +398,151 @@ impl ImageDimensions {
pub fn num_texels(&self) -> u32 {
self.width() * self.height() * self.depth() * self.array_layers()
}
/// Returns the maximum number of mipmaps for these image dimensions.
///
/// The returned value is always at least superior or equal to 1.
///
/// # Example
///
/// ```
/// use vulkano::image::ImageDimensions;
///
/// let dims = ImageDimensions::Dim2d {
/// width: 32,
/// height: 50,
/// cubemap_compatible: false,
/// array_layers: 1,
/// };
///
/// assert_eq!(dims.max_mipmaps(), 7);
/// ```
///
/// # Panic
///
/// May panic if the dimensions are 0.
///
pub fn max_mipmaps(&self) -> u32 {
let max_dim = cmp::max(cmp::max(self.width(), self.height()), self.depth());
let num_zeroes = 32 - (max_dim - 1).leading_zeros();
num_zeroes + 1
}
/// Returns the dimensions of the `level`th mipmap level. If `level` is 0, then the dimensions
/// are left unchanged.
///
/// Returns `None` if `level` is superior or equal to `max_mipmaps()`.
///
/// # Example
///
/// ```
/// use vulkano::image::ImageDimensions;
///
/// let dims = ImageDimensions::Dim2d {
/// width: 963,
/// height: 256,
/// cubemap_compatible: false,
/// array_layers: 1,
/// };
///
/// assert_eq!(dims.mipmap_dimensions(0), Some(dims));
/// assert_eq!(dims.mipmap_dimensions(1), Some(ImageDimensions::Dim2d {
/// width: 512,
/// height: 128,
/// cubemap_compatible: false,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(6), Some(ImageDimensions::Dim2d {
/// width: 16,
/// height: 4,
/// cubemap_compatible: false,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(9), Some(ImageDimensions::Dim2d {
/// width: 2,
/// height: 1,
/// cubemap_compatible: false,
/// array_layers: 1,
/// }));
/// assert_eq!(dims.mipmap_dimensions(11), None);
/// ```
///
/// # Panic
///
/// In debug mode, panicks if `width`, `height` or `depth` is equal to 0. In release, returns
/// an unspecified value.
///
pub fn mipmap_dimensions(&self, level: u32) -> Option<ImageDimensions> {
if level == 0 {
return Some(*self);
}
if level >= self.max_mipmaps() {
return None;
}
Some(match *self {
ImageDimensions::Dim1d { width, array_layers } => {
debug_assert_ne!(width, 0);
ImageDimensions::Dim1d {
array_layers: array_layers,
width: (((width - 1) >> level) + 1).next_power_of_two(),
}
},
ImageDimensions::Dim2d { width, height, array_layers, cubemap_compatible } => {
debug_assert_ne!(width, 0);
debug_assert_ne!(height, 0);
ImageDimensions::Dim2d {
width: (((width - 1) >> level) + 1).next_power_of_two(),
height: (((height - 1) >> level) + 1).next_power_of_two(),
array_layers: array_layers,
cubemap_compatible: cubemap_compatible,
}
},
ImageDimensions::Dim3d { width, height, depth } => {
debug_assert_ne!(width, 0);
debug_assert_ne!(height, 0);
ImageDimensions::Dim3d {
width: (((width - 1) >> level) + 1).next_power_of_two(),
height: (((height - 1) >> level) + 1).next_power_of_two(),
depth: (((depth - 1) >> level) + 1).next_power_of_two(),
}
},
})
}
}
#[cfg(test)]
mod tests {
use image::ImageDimensions;
#[test]
fn max_mipmaps() {
let dims = ImageDimensions::Dim2d { width: 2, height: 1, cubemap_compatible: false, array_layers: 1 };
assert_eq!(dims.max_mipmaps(), 2);
let dims = ImageDimensions::Dim2d { width: 2, height: 3, cubemap_compatible: false, array_layers: 1 };
assert_eq!(dims.max_mipmaps(), 3);
let dims = ImageDimensions::Dim2d { width: 512, height: 512, cubemap_compatible: false, array_layers: 1 };
assert_eq!(dims.max_mipmaps(), 10);
}
#[test]
fn mipmap_dimensions() {
let dims = ImageDimensions::Dim2d { width: 283, height: 175, cubemap_compatible: false, array_layers: 1 };
assert_eq!(dims.mipmap_dimensions(0), Some(dims));
assert_eq!(dims.mipmap_dimensions(1), Some(ImageDimensions::Dim2d { width: 256, height: 128, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(2), Some(ImageDimensions::Dim2d { width: 128, height: 64, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(3), Some(ImageDimensions::Dim2d { width: 64, height: 32, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(4), Some(ImageDimensions::Dim2d { width: 32, height: 16, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(5), Some(ImageDimensions::Dim2d { width: 16, height: 8, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(6), Some(ImageDimensions::Dim2d { width: 8, height: 4, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(7), Some(ImageDimensions::Dim2d { width: 4, height: 2, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(8), Some(ImageDimensions::Dim2d { width: 2, height: 1, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(9), Some(ImageDimensions::Dim2d { width: 1, height: 1, cubemap_compatible: false, array_layers: 1 }));
assert_eq!(dims.mipmap_dimensions(10), None);
}
}

View File

@ -191,33 +191,11 @@ impl UnsafeImage {
// check for additional image capabilities (section 31.4 of the specs).
let mut capabilities_error = None;
// Compute the maximum number of mipmaps.
// TODO: only compte if necessary?
let max_mipmaps = {
let smallest_dim: u32 = match dimensions {
ImageDimensions::Dim1d { width, .. } => width,
ImageDimensions::Dim2d { width, height, .. } => {
if width < height { width } else { height }
},
ImageDimensions::Dim3d {
width,
height,
depth,
} => {
if width < height {
if depth < width { depth } else { width }
} else {
if depth < height { depth } else { height }
}
},
};
32 - smallest_dim.leading_zeros()
};
// Compute the number of mipmaps.
let mipmaps = match mipmaps.into() {
MipmapsCount::Specific(num) => {
let max_mipmaps = dimensions.max_mipmaps();
debug_assert!(max_mipmaps >= 1);
if num < 1 {
return Err(ImageCreationError::InvalidMipmapsCount {
obtained: num,
@ -232,7 +210,7 @@ impl UnsafeImage {
num
},
MipmapsCount::Log2 => max_mipmaps,
MipmapsCount::Log2 => dimensions.max_mipmaps(),
MipmapsCount::One => 1,
};