Skip to content

Commit

Permalink
Implement TEXTURE_COMPRESSION_BC extension
Browse files Browse the repository at this point in the history
  • Loading branch information
cwfitzgerald committed Jul 29, 2020
1 parent 43aa8b1 commit e5c6477
Show file tree
Hide file tree
Showing 7 changed files with 356 additions and 33 deletions.
56 changes: 36 additions & 20 deletions wgpu-core/src/command/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,10 @@ pub enum TransferError {
UnalignedCopyWidth,
#[error("copy height is not a multiple of block height")]
UnalignedCopyHeight,
#[error("copy origin's x component is not a multiple of block width")]
UnalignedCopyOriginX,
#[error("copy origin's y component is not a multiple of block height")]
UnalignedCopyOriginY,
#[error("bytes per row does not respect `COPY_BYTES_PER_ROW_ALIGNMENT`")]
UnalignedBytesPerRow,
#[error("number of rows per image is not a multiple of block height")]
Expand Down Expand Up @@ -103,8 +107,9 @@ pub(crate) fn texture_copy_view_to_hal<B: hal::Backend>(
/// Function copied with minor modifications from webgpu standard https://gpuweb.github.io/gpuweb/#valid-texture-copy-range
pub(crate) fn validate_linear_texture_data(
layout: &TextureDataLayout,
format: wgt::TextureFormat,
buffer_size: BufferAddress,
bytes_per_texel: BufferAddress,
bytes_per_block: BufferAddress,
copy_size: &Extent3d,
) -> Result {
// Convert all inputs to BufferAddress (u64) to prevent overflow issues
Expand All @@ -116,10 +121,10 @@ pub(crate) fn validate_linear_texture_data(
let rows_per_image = layout.rows_per_image as BufferAddress;
let bytes_per_row = layout.bytes_per_row as BufferAddress;

// TODO: Once compressed textures are supported, these needs to be fixed
let block_width: BufferAddress = 1;
let block_height: BufferAddress = 1;
let block_size = bytes_per_texel;
let (block_width, block_height) = conv::texture_block_size(format);
let block_width = block_width as BufferAddress;
let block_height = block_height as BufferAddress;
let block_size = bytes_per_block;

if copy_width % block_width != 0 {
return Err(TransferError::UnalignedCopyWidth);
Expand Down Expand Up @@ -168,12 +173,11 @@ pub(crate) fn validate_linear_texture_data(
/// Function copied with minor modifications from webgpu standard https://gpuweb.github.io/gpuweb/#valid-texture-copy-range
pub(crate) fn validate_texture_copy_range(
texture_copy_view: &TextureCopyView,
texture_format: wgt::TextureFormat,
texture_dimension: hal::image::Kind,
copy_size: &Extent3d,
) -> Result {
// TODO: Once compressed textures are supported, these needs to be fixed
let block_width: u32 = 1;
let block_height: u32 = 1;
let (block_width, block_height) = conv::texture_block_size(texture_format);

let mut extent = texture_dimension.level_extent(texture_copy_view.mip_level as u8);
match texture_dimension {
Expand Down Expand Up @@ -201,6 +205,12 @@ pub(crate) fn validate_texture_copy_range(
return Err(TransferError::BufferOverrun);
}

if texture_copy_view.origin.x % block_width != 0 {
return Err(TransferError::UnalignedCopyOriginX);
}
if texture_copy_view.origin.y % block_height != 0 {
return Err(TransferError::UnalignedCopyOriginY);
}
if copy_size.width % block_width != 0 {
return Err(TransferError::UnalignedCopyWidth);
}
Expand Down Expand Up @@ -359,26 +369,29 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barriers = dst_pending.map(|pending| pending.into_hal(dst_texture));

let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT;
let bytes_per_texel = conv::map_texture_format(dst_texture.format, cmb.private_features)
let bytes_per_block = conv::map_texture_format(dst_texture.format, cmb.private_features)
.surface_desc()
.bits as u32
/ BITS_PER_BYTE;
let src_bytes_per_row = source.layout.bytes_per_row;
if bytes_per_row_alignment % bytes_per_texel != 0 {
if bytes_per_row_alignment % bytes_per_block != 0 {
return Err(TransferError::UnalignedBytesPerRow);
}
if src_bytes_per_row % bytes_per_row_alignment != 0 {
return Err(TransferError::UnalignedBytesPerRow);
}
validate_texture_copy_range(destination, dst_texture.kind, copy_size)?;
validate_texture_copy_range(destination, dst_texture.format, dst_texture.kind, copy_size)?;
validate_linear_texture_data(
&source.layout,
dst_texture.format,
src_buffer.size,
bytes_per_texel as BufferAddress,
bytes_per_block as BufferAddress,
copy_size,
)?;

let buffer_width = source.layout.bytes_per_row / bytes_per_texel;
let (block_width, _) = conv::texture_block_size(dst_texture.format);

let buffer_width = (source.layout.bytes_per_row / bytes_per_block) * block_width;
let region = hal::command::BufferImageCopy {
buffer_offset: source.layout.offset,
buffer_width,
Expand Down Expand Up @@ -459,26 +472,29 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let dst_barrier = dst_barriers.map(|pending| pending.into_hal(dst_buffer));

let bytes_per_row_alignment = wgt::COPY_BYTES_PER_ROW_ALIGNMENT;
let bytes_per_texel = conv::map_texture_format(src_texture.format, cmb.private_features)
let bytes_per_block = conv::map_texture_format(src_texture.format, cmb.private_features)
.surface_desc()
.bits as u32
/ BITS_PER_BYTE;
let dst_bytes_per_row = destination.layout.bytes_per_row;
if bytes_per_row_alignment % bytes_per_texel != 0 {
if bytes_per_row_alignment % bytes_per_block != 0 {
return Err(TransferError::UnalignedBytesPerRow);
}
if dst_bytes_per_row % bytes_per_row_alignment != 0 {
return Err(TransferError::UnalignedBytesPerRow);
}
validate_texture_copy_range(source, src_texture.kind, copy_size)?;
validate_texture_copy_range(source, src_texture.format, src_texture.kind, copy_size)?;
validate_linear_texture_data(
&destination.layout,
src_texture.format,
dst_buffer.size,
bytes_per_texel as BufferAddress,
bytes_per_block as BufferAddress,
copy_size,
)?;

let buffer_width = destination.layout.bytes_per_row / bytes_per_texel;
let (block_width, _) = conv::texture_block_size(src_texture.format);

let buffer_width = (destination.layout.bytes_per_row / bytes_per_block) * block_width;
let region = hal::command::BufferImageCopy {
buffer_offset: destination.layout.offset,
buffer_width,
Expand Down Expand Up @@ -567,8 +583,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}
barriers.extend(dst_pending.map(|pending| pending.into_hal(dst_texture)));

validate_texture_copy_range(source, src_texture.kind, copy_size)?;
validate_texture_copy_range(destination, dst_texture.kind, copy_size)?;
validate_texture_copy_range(source, src_texture.format, src_texture.kind, copy_size)?;
validate_texture_copy_range(destination, dst_texture.format, dst_texture.kind, copy_size)?;

let region = hal::command::ImageCopy {
src_subresource: src_layers,
Expand Down
135 changes: 135 additions & 0 deletions wgpu-core/src/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -392,6 +392,141 @@ pub(crate) fn map_texture_format(
H::D32SfloatS8Uint
}
}

// BCn compressed formats
Tf::Bc1RgbaUnorm => H::Bc1RgbaUnorm,
Tf::Bc1RgbaUnormSrgb => H::Bc1RgbaSrgb,
Tf::Bc2RgbaUnorm => H::Bc2Unorm,
Tf::Bc2RgbaUnormSrgb => H::Bc2Srgb,
Tf::Bc3RgbaUnorm => H::Bc3Unorm,
Tf::Bc3RgbaUnormSrgb => H::Bc3Srgb,
Tf::Bc4RUnorm => H::Bc4Unorm,
Tf::Bc4RSnorm => H::Bc4Snorm,
Tf::Bc5RgUnorm => H::Bc5Unorm,
Tf::Bc5RgSnorm => H::Bc5Snorm,
Tf::Bc6hRgbSfloat => H::Bc6hSfloat,
Tf::Bc6hRgbUfloat => H::Bc6hUfloat,
Tf::Bc7RgbaUnorm => H::Bc7Unorm,
Tf::Bc7RgbaUnormSrgb => H::Bc7Srgb,
}
}

pub fn texture_block_size(format: wgt::TextureFormat) -> (u32, u32) {
use wgt::TextureFormat as Tf;
match format {
Tf::R8Unorm
| Tf::R8Snorm
| Tf::R8Uint
| Tf::R8Sint
| Tf::R16Uint
| Tf::R16Sint
| Tf::R16Float
| Tf::Rg8Unorm
| Tf::Rg8Snorm
| Tf::Rg8Uint
| Tf::Rg8Sint
| Tf::R32Uint
| Tf::R32Sint
| Tf::R32Float
| Tf::Rg16Uint
| Tf::Rg16Sint
| Tf::Rg16Float
| Tf::Rgba8Unorm
| Tf::Rgba8UnormSrgb
| Tf::Rgba8Snorm
| Tf::Rgba8Uint
| Tf::Rgba8Sint
| Tf::Bgra8Unorm
| Tf::Bgra8UnormSrgb
| Tf::Rgb10a2Unorm
| Tf::Rg11b10Float
| Tf::Rg32Uint
| Tf::Rg32Sint
| Tf::Rg32Float
| Tf::Rgba16Uint
| Tf::Rgba16Sint
| Tf::Rgba16Float
| Tf::Rgba32Uint
| Tf::Rgba32Sint
| Tf::Rgba32Float => (1, 1),

Tf::Depth32Float | Tf::Depth24Plus | Tf::Depth24PlusStencil8 => {
unreachable!("unexpected depth format")
}

Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb
| Tf::Bc2RgbaUnorm
| Tf::Bc2RgbaUnormSrgb
| Tf::Bc3RgbaUnorm
| Tf::Bc3RgbaUnormSrgb
| Tf::Bc4RUnorm
| Tf::Bc4RSnorm
| Tf::Bc5RgUnorm
| Tf::Bc5RgSnorm
| Tf::Bc6hRgbUfloat
| Tf::Bc6hRgbSfloat
| Tf::Bc7RgbaUnorm
| Tf::Bc7RgbaUnormSrgb => (4, 4),
}
}

pub fn texture_features(format: wgt::TextureFormat) -> wgt::Features {
use wgt::TextureFormat as Tf;
match format {
Tf::R8Unorm
| Tf::R8Snorm
| Tf::R8Uint
| Tf::R8Sint
| Tf::R16Uint
| Tf::R16Sint
| Tf::R16Float
| Tf::Rg8Unorm
| Tf::Rg8Snorm
| Tf::Rg8Uint
| Tf::Rg8Sint
| Tf::R32Uint
| Tf::R32Sint
| Tf::R32Float
| Tf::Rg16Uint
| Tf::Rg16Sint
| Tf::Rg16Float
| Tf::Rgba8Unorm
| Tf::Rgba8UnormSrgb
| Tf::Rgba8Snorm
| Tf::Rgba8Uint
| Tf::Rgba8Sint
| Tf::Bgra8Unorm
| Tf::Bgra8UnormSrgb
| Tf::Rgb10a2Unorm
| Tf::Rg11b10Float
| Tf::Rg32Uint
| Tf::Rg32Sint
| Tf::Rg32Float
| Tf::Rgba16Uint
| Tf::Rgba16Sint
| Tf::Rgba16Float
| Tf::Rgba32Uint
| Tf::Rgba32Sint
| Tf::Rgba32Float
| Tf::Depth32Float
| Tf::Depth24Plus
| Tf::Depth24PlusStencil8 => wgt::Features::empty(),

Tf::Bc1RgbaUnorm
| Tf::Bc1RgbaUnormSrgb
| Tf::Bc2RgbaUnorm
| Tf::Bc2RgbaUnormSrgb
| Tf::Bc3RgbaUnorm
| Tf::Bc3RgbaUnormSrgb
| Tf::Bc4RUnorm
| Tf::Bc4RSnorm
| Tf::Bc5RgUnorm
| Tf::Bc5RgSnorm
| Tf::Bc6hRgbUfloat
| Tf::Bc6hRgbSfloat
| Tf::Bc7RgbaUnorm
| Tf::Bc7RgbaUnormSrgb => wgt::Features::TEXTURE_COMPRESSION_BC,
}
}

Expand Down
11 changes: 11 additions & 0 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -948,6 +948,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
None => (),
};

let texture_features = conv::texture_features(desc.format);
if texture_features != wgt::Features::empty() && !device.features.contains(texture_features)
{
return Err(CreateTextureError::MissingFeature(
texture_features,
desc.format,
));
}

device
.trackers
.lock()
Expand Down Expand Up @@ -3088,6 +3097,8 @@ pub enum CreateTextureError {
MAX_MIP_LEVELS
)]
InvalidMipLevelCount(u32),
#[error("Feature {0:?} must be enabled to create a texture of type {1:?}")]
MissingFeature(wgt::Features, TextureFormat),
#[error(transparent)]
HeapsError(#[from] gfx_memory::HeapsError),
#[error("not enough memory left")]
Expand Down
30 changes: 19 additions & 11 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -275,24 +275,32 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
}

let texture_format = texture_guard[destination.texture].format;
let bytes_per_texel = conv::map_texture_format(texture_format, device.private_features)
let bytes_per_block = conv::map_texture_format(texture_format, device.private_features)
.surface_desc()
.bits as u32
/ BITS_PER_BYTE;
crate::command::validate_linear_texture_data(
data_layout,
texture_format,
data.len() as wgt::BufferAddress,
bytes_per_texel as wgt::BufferAddress,
bytes_per_block as wgt::BufferAddress,
size,
)?;
let (block_width, block_height) = conv::texture_block_size(texture_format);
let width_blocks = size.width / block_width;
let height_blocks = size.height / block_width;

let texel_rows_per_image = data_layout.rows_per_image;
let block_rows_per_image = data_layout.rows_per_image / block_height;

let bytes_per_row_alignment = get_lowest_common_denom(
device.hal_limits.optimal_buffer_copy_pitch_alignment as u32,
bytes_per_texel,
bytes_per_block,
);
let stage_bytes_per_row = align_to(bytes_per_texel * size.width, bytes_per_row_alignment);
let stage_size = stage_bytes_per_row as u64
* ((size.depth - 1) * data_layout.rows_per_image + size.height) as u64;
let stage_bytes_per_row = align_to(bytes_per_block * width_blocks, bytes_per_row_alignment);

let block_rows_in_copy = (size.depth - 1) * block_rows_per_image + height_blocks;
let stage_size = stage_bytes_per_row as u64 * block_rows_in_copy as u64;
let mut stage = device.prepare_stage(stage_size)?;
{
let mut mapped = stage
Expand All @@ -312,8 +320,8 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let copy_bytes_per_row =
stage_bytes_per_row.min(data_layout.bytes_per_row) as usize;
for layer in 0..size.depth {
let rows_offset = layer * data_layout.rows_per_image;
for row in 0..size.height {
let rows_offset = layer * block_rows_per_image;
for row in 0..height_blocks {
let data_offset =
(rows_offset + row) as usize * data_layout.bytes_per_row as usize;
let stage_offset =
Expand All @@ -333,14 +341,14 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
TextureUse::COPY_DST,
);
check_texture_usage(dst.usage, wgt::TextureUsage::COPY_DST)?;
crate::command::validate_texture_copy_range(destination, dst.kind, size)?;
crate::command::validate_texture_copy_range(destination, dst.format, dst.kind, size)?;

dst.life_guard.use_at(device.active_submission_index + 1);

let region = hal::command::BufferImageCopy {
buffer_offset: 0,
buffer_width: stage_bytes_per_row / bytes_per_texel,
buffer_height: data_layout.rows_per_image,
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),
Expand Down
4 changes: 4 additions & 0 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -131,6 +131,10 @@ impl<B: hal::Backend> Adapter<B> {
wgt::Features::DEPTH_CLAMPING,
adapter_features.contains(hal::Features::DEPTH_CLAMP),
);
features.set(
wgt::Features::TEXTURE_COMPRESSION_BC,
adapter_features.contains(hal::Features::FORMAT_BC),
);
features.set(
wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY),
Expand Down
Loading

0 comments on commit e5c6477

Please sign in to comment.