Skip to content

Commit

Permalink
Align the validation of Device::create_texture with the WebGPU specif…
Browse files Browse the repository at this point in the history
…ication.
  • Loading branch information
nical committed Jun 10, 2022
1 parent f94f25f commit 5ce49af
Show file tree
Hide file tree
Showing 4 changed files with 107 additions and 31 deletions.
9 changes: 1 addition & 8 deletions wgpu-core/src/conv.rs
Original file line number Diff line number Diff line change
Expand Up @@ -116,14 +116,7 @@ pub fn check_texture_dimension_size(
use wgt::TextureDimension::*;

let (extent_limits, sample_limit) = match dimension {
D1 => (
[
limits.max_texture_dimension_1d,
1,
limits.max_texture_array_layers,
],
1,
),
D1 => ([limits.max_texture_dimension_1d, 1, 1], 1),
D2 => (
[
limits.max_texture_dimension_2d,
Expand Down
106 changes: 84 additions & 22 deletions wgpu-core/src/device/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -678,65 +678,127 @@ impl<A: HalApi> Device<A> {
adapter: &crate::instance::Adapter<A>,
desc: &resource::TextureDescriptor,
) -> Result<resource::Texture<A>, resource::CreateTextureError> {
use resource::{CreateTextureError, TextureDimensionError};

if desc.usage.is_empty() {
return Err(CreateTextureError::EmptyUsage);
}

conv::check_texture_dimension_size(
desc.dimension,
desc.size,
desc.sample_count,
&self.limits,
)?;

let format_desc = desc.format.describe();

if desc.dimension != wgt::TextureDimension::D2 {
// Depth textures can only be 2D
if format_desc.sample_type == wgt::TextureSampleType::Depth {
return Err(resource::CreateTextureError::InvalidDepthDimension(
return Err(CreateTextureError::InvalidDepthDimension(
desc.dimension,
desc.format,
));
}
// Renderable textures can only be 2D
if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
return Err(resource::CreateTextureError::InvalidDimensionUsages(
return Err(CreateTextureError::InvalidDimensionUsages(
wgt::TextureUsages::RENDER_ATTACHMENT,
desc.dimension,
));
}

// Compressed textures can only be 2D
if format_desc.is_compressed() {
return Err(resource::CreateTextureError::InvalidCompressedDimension(
return Err(CreateTextureError::InvalidCompressedDimension(
desc.dimension,
desc.format,
));
}
}

let format_features = self
.describe_format_features(adapter, desc.format)
.map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?;
if format_desc.is_compressed() {
let block_width = format_desc.block_dimensions.0 as u32;
let block_height = format_desc.block_dimensions.1 as u32;

if desc.usage.is_empty() {
return Err(resource::CreateTextureError::EmptyUsage);
}
if desc.size.width % block_width != 0 {
return Err(CreateTextureError::InvalidDimension(
TextureDimensionError::NotMultipleOfBlockWidth {
width: desc.size.width,
block_width,
format: desc.format,
},
));
}

let missing_allowed_usages = desc.usage - format_features.allowed_usages;
if !missing_allowed_usages.is_empty() {
return Err(resource::CreateTextureError::InvalidFormatUsages(
missing_allowed_usages,
desc.format,
));
if desc.size.height % block_height != 0 {
return Err(CreateTextureError::InvalidDimension(
TextureDimensionError::NotMultipleOfBlockHeight {
height: desc.size.height,
block_height,
format: desc.format,
},
));
}
}

conv::check_texture_dimension_size(
desc.dimension,
desc.size,
desc.sample_count,
&self.limits,
)?;
if desc.sample_count > 1 {
if desc.mip_level_count != 1 {
return Err(CreateTextureError::InvalidMipLevelCount {
requested: desc.mip_level_count,
maximum: 1,
});
}

if desc.size.depth_or_array_layers != 1 {
return Err(CreateTextureError::InvalidDimension(
TextureDimensionError::MultisampledDepthOrArrayLayer(
desc.size.depth_or_array_layers,
),
));
}

if desc.usage.contains(wgt::TextureUsages::STORAGE_BINDING) {
return Err(CreateTextureError::InvalidMultisampledStorageBinding);
}

if !desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) {
return Err(CreateTextureError::MultisampledNotRenderAttachment);
}

if !format_desc
.guaranteed_format_features
.flags
.contains(wgt::TextureFormatFeatureFlags::MULTISAMPLE)
{
return Err(CreateTextureError::InvalidMultisampledFormat(desc.format));
}
}

let mips = desc.mip_level_count;
let max_levels_allowed = desc.size.max_mips(desc.dimension).min(hal::MAX_MIP_LEVELS);
if mips == 0 || mips > max_levels_allowed {
return Err(resource::CreateTextureError::InvalidMipLevelCount {
return Err(CreateTextureError::InvalidMipLevelCount {
requested: mips,
maximum: max_levels_allowed,
});
}

let format_features = self
.describe_format_features(adapter, desc.format)
.map_err(|error| CreateTextureError::MissingFeatures(desc.format, error))?;

let missing_allowed_usages = desc.usage - format_features.allowed_usages;
if !missing_allowed_usages.is_empty() {
return Err(CreateTextureError::InvalidFormatUsages(
missing_allowed_usages,
desc.format,
));
}

// TODO: validate missing TextureDescriptor::view_formats.

// Enforce having COPY_DST/DEPTH_STENCIL_WRIT/COLOR_TARGET otherwise we wouldn't be able to initialize the texture.
let hal_usage = conv::map_texture_usage(desc.usage, desc.format.into())
| if format_desc.sample_type == wgt::TextureSampleType::Depth {
Expand Down
22 changes: 21 additions & 1 deletion wgpu-core/src/resource.rs
Original file line number Diff line number Diff line change
Expand Up @@ -336,8 +336,22 @@ pub enum TextureDimensionError {
given: u32,
limit: u32,
},
#[error("sample count {0} is invalid")]
#[error("Sample count {0} is invalid")]
InvalidSampleCount(u32),
#[error("Width {width} is not a multiple of {format:?}'s block width ({block_width})")]
NotMultipleOfBlockWidth {
width: u32,
block_width: u32,
format: wgt::TextureFormat,
},
#[error("Height {height} is not a multiple of {format:?}'s block height ({block_height})")]
NotMultipleOfBlockHeight {
height: u32,
block_height: u32,
format: wgt::TextureFormat,
},
#[error("Multisampled texture depth or array layers must be 1, got {0}")]
MultisampledDepthOrArrayLayer(u32),
}

#[derive(Clone, Debug, Error)]
Expand All @@ -360,6 +374,12 @@ pub enum CreateTextureError {
InvalidFormatUsages(wgt::TextureUsages, wgt::TextureFormat),
#[error("Texture usages {0:?} are not allowed on a texture of dimensions {1:?}")]
InvalidDimensionUsages(wgt::TextureUsages, wgt::TextureDimension),
#[error("Texture usage STORAGE_BINDING is not allowed for multisampled textures")]
InvalidMultisampledStorageBinding,
#[error("Format {0:?} does not support multisampling")]
InvalidMultisampledFormat(wgt::TextureFormat),
#[error("Multisampled textures must have RENDER_ATTACHMENT usage")]
MultisampledNotRenderAttachment,
#[error("Texture format {0:?} can't be used due to missing features.")]
MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures),
}
Expand Down
1 change: 1 addition & 0 deletions wgpu-types/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3306,6 +3306,7 @@ pub struct TextureDescriptor<L> {
pub format: TextureFormat,
/// Allowed usages of the texture. If used in other ways, the operation will panic.
pub usage: TextureUsages,
// TODO: missing view_formats https://www.w3.org/TR/webgpu/#dom-gputexturedescriptor-viewformats
}

impl<L> TextureDescriptor<L> {
Expand Down

0 comments on commit 5ce49af

Please sign in to comment.