From 5ce49af3d148331c0f8ef06c002a43de9372efec Mon Sep 17 00:00:00 2001 From: Nicolas Silva Date: Fri, 10 Jun 2022 18:29:20 +0200 Subject: [PATCH] Align the validation of Device::create_texture with the WebGPU specification. --- wgpu-core/src/conv.rs | 9 +-- wgpu-core/src/device/mod.rs | 106 ++++++++++++++++++++++++++++-------- wgpu-core/src/resource.rs | 22 +++++++- wgpu-types/src/lib.rs | 1 + 4 files changed, 107 insertions(+), 31 deletions(-) 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,19 +678,32 @@ impl Device { adapter: &crate::instance::Adapter, desc: &resource::TextureDescriptor, ) -> Result, 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, )); @@ -698,45 +711,94 @@ impl Device { // 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 { 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 {