Skip to content

Commit

Permalink
Merge #1074
Browse files Browse the repository at this point in the history
1074: Implement ETC2 and ASTC textures r=kvark a=cwfitzgerald

**Connections**

Closes #1070. Makes progress towards #1069.

**Description**

This PR has multiple functions:
 - Adds ETC and ASTC compressed textures behind features.
 - Adds three helper functions on `Extent3d` that help in calculating mip sizes: `at_mip_level`, `max_mips`, and `physical_size`.
 - Refactors various conversions into a public `TextureFormat::describe` function. I have used a decl macro to ease in the declaration and modifcation of an otherwise horribly verbose function.

I have tried to use clever multi-select based data copying to reduce the possibility for errors, but there's a _lot_ of data moving around here.

**Testing**

Upcoming wgpu-rs pr adding wider compressed texture support to the skybox example. Helper functions were tested with doctests.

Marked as draft until I can get the wgpu-rs pr done and prove it works, it is, however, ready for review.


Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
  • Loading branch information
bors[bot] and cwfitzgerald authored Dec 6, 2020
2 parents d430e04 + 19fb491 commit 7b886f4
Show file tree
Hide file tree
Showing 7 changed files with 677 additions and 212 deletions.
66 changes: 57 additions & 9 deletions wgpu-core/src/command/transfer.rs
Original file line number Diff line number Diff line change
Expand Up @@ -166,7 +166,7 @@ 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;

let (block_width, block_height) = conv::texture_block_size(format);
let (block_width, block_height) = format.describe().block_dimensions;
let block_width = block_width as BufferAddress;
let block_height = block_height as BufferAddress;
let block_size = bytes_per_block;
Expand Down Expand Up @@ -228,9 +228,18 @@ pub(crate) fn validate_texture_copy_range(
texture_side: CopySide,
copy_size: &Extent3d,
) -> Result<(), TransferError> {
let (block_width, block_height) = conv::texture_block_size(texture_format);
let (block_width, block_height) = texture_format.describe().block_dimensions;
let block_width = block_width as u32;
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) {
Expand Down Expand Up @@ -503,21 +512,32 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size,
)?;

let (block_width, _) = conv::texture_block_size(dst_texture.format);
let (block_width, _) = dst_texture.format.describe().block_dimensions;
if !conv::is_valid_copy_dst_texture_format(dst_texture.format) {
Err(TransferError::CopyToForbiddenTextureFormat(
dst_texture.format,
))?
}

let buffer_width = (source.layout.bytes_per_row / bytes_per_block) * block_width;
// 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,
buffer_width,
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 {
Expand Down Expand Up @@ -632,21 +652,33 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
copy_size,
)?;

let (block_width, _) = conv::texture_block_size(src_texture.format);
let (block_width, _) = src_texture.format.describe().block_dimensions;
if !conv::is_valid_copy_src_texture_format(src_texture.format) {
Err(TransferError::CopyFromForbiddenTextureFormat(
src_texture.format,
))?
}

let buffer_width = (destination.layout.bytes_per_row / bytes_per_block) * block_width;
// 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 {
buffer_offset: destination.layout.offset,
buffer_width,
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 {
Expand Down Expand Up @@ -762,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 {
Expand Down
171 changes: 54 additions & 117 deletions wgpu-core/src/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -396,124 +396,48 @@ pub(crate) fn map_texture_format(
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
| Tf::Depth32Float
| Tf::Depth24Plus
| Tf::Depth24PlusStencil8 => (1, 1),

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,
// ETC compressed formats
Tf::Etc2RgbUnorm => H::Etc2R8g8b8Unorm,
Tf::Etc2RgbUnormSrgb => H::Etc2R8g8b8Srgb,
Tf::Etc2RgbA1Unorm => H::Etc2R8g8b8a1Unorm,
Tf::Etc2RgbA1UnormSrgb => H::Etc2R8g8b8a1Srgb,
Tf::Etc2RgbA8Unorm => H::Etc2R8g8b8a8Unorm,
Tf::Etc2RgbA8UnormSrgb => H::Etc2R8g8b8a8Unorm,
Tf::EacRUnorm => H::EacR11Unorm,
Tf::EacRSnorm => H::EacR11Snorm,
Tf::EtcRgUnorm => H::EacR11g11Unorm,
Tf::EtcRgSnorm => H::EacR11g11Snorm,

// ASTC compressed formats
Tf::Astc4x4RgbaUnorm => H::Astc4x4Srgb,
Tf::Astc4x4RgbaUnormSrgb => H::Astc4x4Srgb,
Tf::Astc5x4RgbaUnorm => H::Astc5x4Unorm,
Tf::Astc5x4RgbaUnormSrgb => H::Astc5x4Srgb,
Tf::Astc5x5RgbaUnorm => H::Astc5x5Unorm,
Tf::Astc5x5RgbaUnormSrgb => H::Astc5x5Srgb,
Tf::Astc6x5RgbaUnorm => H::Astc6x5Unorm,
Tf::Astc6x5RgbaUnormSrgb => H::Astc6x5Srgb,
Tf::Astc6x6RgbaUnorm => H::Astc6x6Unorm,
Tf::Astc6x6RgbaUnormSrgb => H::Astc6x6Srgb,
Tf::Astc8x5RgbaUnorm => H::Astc8x5Unorm,
Tf::Astc8x5RgbaUnormSrgb => H::Astc8x5Srgb,
Tf::Astc8x6RgbaUnorm => H::Astc8x6Unorm,
Tf::Astc8x6RgbaUnormSrgb => H::Astc8x6Srgb,
Tf::Astc10x5RgbaUnorm => H::Astc10x5Unorm,
Tf::Astc10x5RgbaUnormSrgb => H::Astc10x5Srgb,
Tf::Astc10x6RgbaUnorm => H::Astc10x6Unorm,
Tf::Astc10x6RgbaUnormSrgb => H::Astc10x6Srgb,
Tf::Astc8x8RgbaUnorm => H::Astc8x8Unorm,
Tf::Astc8x8RgbaUnormSrgb => H::Astc8x8Srgb,
Tf::Astc10x8RgbaUnorm => H::Astc10x8Unorm,
Tf::Astc10x8RgbaUnormSrgb => H::Astc10x8Srgb,
Tf::Astc10x10RgbaUnorm => H::Astc10x10Unorm,
Tf::Astc10x10RgbaUnormSrgb => H::Astc10x10Srgb,
Tf::Astc12x10RgbaUnorm => H::Astc12x10Unorm,
Tf::Astc12x10RgbaUnormSrgb => H::Astc12x10Srgb,
Tf::Astc12x12RgbaUnorm => H::Astc12x12Unorm,
Tf::Astc12x12RgbaUnormSrgb => H::Astc12x12Srgb,
}
}

Expand Down Expand Up @@ -831,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
}
2 changes: 1 addition & 1 deletion wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -506,7 +506,7 @@ impl<B: GfxBackend> Device<B> {
) -> Result<resource::Texture<B>, resource::CreateTextureError> {
debug_assert_eq!(self_id.backend(), B::VARIANT);

let features = conv::texture_features(desc.format);
let features = desc.format.describe().features;
if !self.features.contains(features) {
return Err(resource::CreateTextureError::MissingFeature(
features,
Expand Down
19 changes: 17 additions & 2 deletions wgpu-core/src/device/queue.rs
Original file line number Diff line number Diff line change
Expand Up @@ -324,7 +324,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
bytes_per_block as wgt::BufferAddress,
size,
)?;
let (block_width, block_height) = conv::texture_block_size(texture_format);

let (block_width, block_height) = texture_format.describe().block_dimensions;
let block_width = block_width as u32;
let block_height = block_height as u32;

if !conv::is_valid_copy_dst_texture_format(texture_format) {
Err(TransferError::CopyToForbiddenTextureFormat(texture_format))?
}
Expand Down Expand Up @@ -405,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(
Expand Down
23 changes: 23 additions & 0 deletions wgpu-core/src/instance.rs
Original file line number Diff line number Diff line change
Expand Up @@ -144,6 +144,14 @@ impl<B: GfxBackend> Adapter<B> {
wgt::Features::TEXTURE_COMPRESSION_BC,
adapter_features.contains(hal::Features::FORMAT_BC),
);
features.set(
wgt::Features::TEXTURE_COMPRESSION_ETC2,
adapter_features.contains(hal::Features::FORMAT_ETC2),
);
features.set(
wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR,
adapter_features.contains(hal::Features::FORMAT_ASTC_LDR),
);
features.set(
wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY,
adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY),
Expand Down Expand Up @@ -270,6 +278,21 @@ impl<B: GfxBackend> Adapter<B> {
}

// Features
enabled_features.set(
hal::Features::FORMAT_BC,
desc.features
.contains(wgt::Features::TEXTURE_COMPRESSION_BC),
);
enabled_features.set(
hal::Features::FORMAT_ETC2,
desc.features
.contains(wgt::Features::TEXTURE_COMPRESSION_ETC2),
);
enabled_features.set(
hal::Features::FORMAT_ASTC_LDR,
desc.features
.contains(wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR),
);
enabled_features.set(
hal::Features::TEXTURE_DESCRIPTOR_ARRAY,
desc.features
Expand Down
Loading

0 comments on commit 7b886f4

Please sign in to comment.