diff --git a/wgpu-core/src/conv.rs b/wgpu-core/src/conv.rs index 9ab3cbbecb..fd59a25eb4 100644 --- a/wgpu-core/src/conv.rs +++ b/wgpu-core/src/conv.rs @@ -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, diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 016232e51f..796a12fa84 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -678,47 +678,10 @@ impl Device { adapter: &crate::instance::Adapter, desc: &resource::TextureDescriptor, ) -> Result, resource::CreateTextureError> { - 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( - desc.dimension, - desc.format, - )); - } - // Renderable textures can only be 2D - if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) { - return Err(resource::CreateTextureError::InvalidDimensionUsages( - wgt::TextureUsages::RENDER_ATTACHMENT, - desc.dimension, - )); - } - - // Compressed textures can only be 2D - if format_desc.is_compressed() { - return Err(resource::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))?; + use resource::{CreateTextureError, TextureDimensionError}; if desc.usage.is_empty() { - return Err(resource::CreateTextureError::EmptyUsage); - } - - 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, - )); + return Err(CreateTextureError::EmptyUsage); } conv::check_texture_dimension_size( @@ -728,15 +691,114 @@ impl Device { &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(CreateTextureError::InvalidDepthDimension( + desc.dimension, + desc.format, + )); + } + // Renderable textures can only be 2D + if desc.usage.contains(wgt::TextureUsages::RENDER_ATTACHMENT) { + return Err(CreateTextureError::InvalidDimensionUsages( + wgt::TextureUsages::RENDER_ATTACHMENT, + desc.dimension, + )); + } + + // Compressed textures can only be 2D + if format_desc.is_compressed() { + return Err(CreateTextureError::InvalidCompressedDimension( + desc.dimension, + desc.format, + )); + } + } + + 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.size.width % block_width != 0 { + return Err(CreateTextureError::InvalidDimension( + TextureDimensionError::NotMultipleOfBlockWidth { + width: desc.size.width, + block_width, + format: desc.format, + }, + )); + } + + if desc.size.height % block_height != 0 { + return Err(CreateTextureError::InvalidDimension( + TextureDimensionError::NotMultipleOfBlockHeight { + height: desc.size.height, + block_height, + format: desc.format, + }, + )); + } + } + + 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 { diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 6f45e26399..dffe52c0be 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -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)] @@ -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), } diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 0ba849e3e6..3b09ce0cf9 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -3306,6 +3306,7 @@ pub struct TextureDescriptor { 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 TextureDescriptor {