Fix validation of compressed mipmaps

This commit is contained in:
Connor Fitzgerald 2020-12-04 23:45:11 -05:00
parent 8f430ab1ef
commit 19fb491262
3 changed files with 73 additions and 4 deletions

View File

@ -233,6 +233,13 @@ pub(crate) fn validate_texture_copy_range(
let block_height = block_height as u32;
let mut extent = texture_dimension.level_extent(texture_copy_view.mip_level as u8);
// Adjust extent for the physical size of mips
if texture_copy_view.mip_level != 0 {
extent.width = conv::align_up(extent.width, block_width);
extent.height = conv::align_up(extent.height, block_height);
}
match texture_dimension {
hal::image::Kind::D1(..) => {
if (copy_size.height, copy_size.depth) != (1, 1) {
@ -512,6 +519,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
))?
}
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let max_image_extent = dst_texture.kind.level_extent(destination.mip_level as _);
let image_extent = Extent3d {
width: copy_size.width.min(max_image_extent.width),
height: copy_size.height.min(max_image_extent.height),
depth: copy_size.depth,
};
let buffer_width = (source.layout.bytes_per_row / bytes_per_block) * block_width as u32;
let region = hal::command::BufferImageCopy {
buffer_offset: source.layout.offset,
@ -519,7 +537,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_height: source.layout.rows_per_image,
image_layers: dst_layers,
image_offset: dst_offset,
image_extent: conv::map_extent(copy_size, dst_texture.dimension),
image_extent: conv::map_extent(&image_extent, dst_texture.dimension),
};
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
unsafe {
@ -641,6 +659,17 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
))?
}
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let max_image_extent = src_texture.kind.level_extent(source.mip_level as _);
let image_extent = Extent3d {
width: copy_size.width.min(max_image_extent.width),
height: copy_size.height.min(max_image_extent.height),
depth: copy_size.depth,
};
let buffer_width =
(destination.layout.bytes_per_row / bytes_per_block) * block_width as u32;
let region = hal::command::BufferImageCopy {
@ -649,7 +678,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
buffer_height: destination.layout.rows_per_image,
image_layers: src_layers,
image_offset: src_offset,
image_extent: conv::map_extent(copy_size, src_texture.dimension),
image_extent: conv::map_extent(&image_extent, src_texture.dimension),
};
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
unsafe {
@ -765,12 +794,28 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size,
)?;
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let max_src_image_extent = src_texture.kind.level_extent(source.mip_level as _);
let max_dst_image_extent = dst_texture.kind.level_extent(destination.mip_level as _);
let image_extent = Extent3d {
width: copy_size
.width
.min(max_src_image_extent.width.min(max_dst_image_extent.width)),
height: copy_size
.height
.min(max_src_image_extent.height.min(max_dst_image_extent.height)),
depth: copy_size.depth,
};
let region = hal::command::ImageCopy {
src_subresource: src_layers,
src_offset,
dst_subresource: dst_layers,
dst_offset,
extent: conv::map_extent(copy_size, src_texture.dimension),
extent: conv::map_extent(&image_extent, src_texture.dimension),
};
let cmb_raw = cmd_buf.raw.last_mut().unwrap();
unsafe {

View File

@ -755,3 +755,16 @@ pub fn map_index_format(index_format: wgt::IndexFormat) -> hal::IndexType {
wgt::IndexFormat::Uint32 => hal::IndexType::U32,
}
}
/// Take `value` and round it up to the nearest alignment `alignment`.
///
/// ```text
/// (0, 3) -> 0
/// (1, 3) -> 3
/// (2, 3) -> 3
/// (3, 3) -> 3
/// (4, 3) -> 6
/// ...
pub fn align_up(value: u32, alignment: u32) -> u32 {
((value + alignment - 1) / alignment) * alignment
}

View File

@ -409,13 +409,24 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
stage.memory.flush_range(&device.raw, 0, None)?;
}
// WebGPU uses the physical size of the texture for copies whereas vulkan uses
// the virtual size. We have passed validation, so it's safe to use the
// image extent data directly. We want the provided copy size to be no larger than
// the virtual size.
let max_image_extent = dst.kind.level_extent(destination.mip_level as _);
let image_extent = wgt::Extent3d {
width: size.width.min(max_image_extent.width),
height: size.height.min(max_image_extent.height),
depth: size.depth,
};
let region = hal::command::BufferImageCopy {
buffer_offset: 0,
buffer_width: (stage_bytes_per_row / bytes_per_block) * block_width,
buffer_height: texel_rows_per_image,
image_layers,
image_offset,
image_extent: conv::map_extent(size, dst.dimension),
image_extent: conv::map_extent(&image_extent, dst.dimension),
};
unsafe {
stage.cmdbuf.pipeline_barrier(