diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 622e6c97b3..0a4fd65d63 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -5,7 +5,7 @@ use crate::{ device::{ descriptor::{DescriptorSet, DescriptorTotalCount}, - DeviceError, SHADER_STAGE_COUNT, + DeviceError, MissingFeatures, SHADER_STAGE_COUNT, }, hub::Resource, id::{BindGroupLayoutId, BufferId, DeviceId, SamplerId, TextureViewId, Valid}, @@ -29,16 +29,26 @@ use std::{ use thiserror::Error; +#[derive(Clone, Debug, Error)] +pub enum BindGroupLayoutEntryError { + #[error("arrays of bindings unsupported for this type of binding")] + ArrayUnsupported, + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), +} + #[derive(Clone, Debug, Error)] pub enum CreateBindGroupLayoutError { #[error(transparent)] Device(#[from] DeviceError), - #[error("arrays of bindings unsupported for this type of binding")] - ArrayUnsupported, #[error("conflicting binding at index {0}")] ConflictBinding(u32), - #[error("required device feature is missing: {0:?}")] - MissingFeature(wgt::Features), + #[error("binding {binding} entry is invalid")] + Entry { + binding: u32, + #[source] + error: BindGroupLayoutEntryError, + }, #[error(transparent)] TooManyBindings(BindingTypeMaxCountError), } @@ -83,8 +93,6 @@ pub enum CreateBindGroupError { MissingBufferUsage(#[from] MissingBufferUsageError), #[error(transparent)] MissingTextureUsage(#[from] MissingTextureUsageError), - #[error("required device features not enabled: {0:?}")] - MissingFeatures(wgt::Features), #[error("binding declared as a single item, but bind group is using it as an array")] SingleBindingExpected, #[error("unable to create a bind group with a swap chain image")] @@ -419,8 +427,8 @@ pub enum CreatePipelineLayoutError { wgt::PUSH_CONSTANT_ALIGNMENT )] MisalignedPushConstantRange { index: usize, bound: u32 }, - #[error("device does not have required feature: {0:?}")] - MissingFeature(wgt::Features), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), #[error("push constant range (index {index}) provides for stage(s) {provided:?} but there exists another range that provides stage(s) {intersected:?}. Each stage may only be provided by one range")] MoreThanOnePushConstantRangePerStage { index: usize, diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index c84702190c..e35a19663e 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -342,7 +342,6 @@ impl Device { spv::Capability::Image1D, spv::Capability::SampledCubeArray, spv::Capability::ImageCubeArray, - spv::Capability::ImageMSArray, spv::Capability::StorageImageExtendedFormats, ] .iter() @@ -393,6 +392,14 @@ impl Device { }) } + pub(crate) fn require_features(&self, feature: wgt::Features) -> Result<(), MissingFeatures> { + if self.features.contains(feature) { + Ok(()) + } else { + Err(MissingFeatures(feature)) + } + } + pub(crate) fn last_completed_submission_index(&self) -> SubmissionIndex { self.life_guard.submission_index.load(Ordering::Acquire) } @@ -616,13 +623,8 @@ impl Device { debug_assert_eq!(self_id.backend(), B::VARIANT); let format_desc = desc.format.describe(); - let required_features = format_desc.required_features; - if !self.features.contains(required_features) { - return Err(resource::CreateTextureError::MissingFeature( - required_features, - desc.format, - )); - } + self.require_features(format_desc.required_features) + .map_err(|error| resource::CreateTextureError::MissingFeatures(desc.format, error))?; // Ensure `D24Plus` textures cannot be copied match desc.format { @@ -917,17 +919,12 @@ impl Device { self_id: id::DeviceId, desc: &resource::SamplerDescriptor, ) -> Result, resource::CreateSamplerError> { - let clamp_to_border_enabled = self - .features - .contains(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER); - let clamp_to_border_found = desc + if desc .address_modes .iter() - .any(|am| am == &wgt::AddressMode::ClampToBorder); - if clamp_to_border_found && !clamp_to_border_enabled { - return Err(resource::CreateSamplerError::MissingFeature( - wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, - )); + .any(|am| am == &wgt::AddressMode::ClampToBorder) + { + self.require_features(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER)?; } let actual_clamp = if let Some(clamp) = desc.anisotropy_clamp { @@ -1198,9 +1195,11 @@ impl Device { entry_map: binding_model::BindEntryMap, ) -> Result, binding_model::CreateBindGroupLayoutError> { let mut desc_count = descriptor::DescriptorTotalCount::default(); - for binding in entry_map.values() { + for entry in entry_map.values() { use wgt::BindingType as Bt; - let (counter, array_feature) = match binding.ty { + + let mut required_features = wgt::Features::empty(); + let (counter, array_feature, is_writable_storage) = match entry.ty { Bt::Buffer { ty: wgt::BufferBindingType::Uniform, has_dynamic_offset: false, @@ -1208,6 +1207,7 @@ impl Device { } => ( &mut desc_count.uniform_buffer, Some(wgt::Features::BUFFER_BINDING_ARRAY), + false, ), Bt::Buffer { ty: wgt::BufferBindingType::Uniform, @@ -1216,44 +1216,65 @@ impl Device { } => ( &mut desc_count.uniform_buffer_dynamic, Some(wgt::Features::BUFFER_BINDING_ARRAY), + false, ), Bt::Buffer { - ty: wgt::BufferBindingType::Storage { .. }, - has_dynamic_offset: false, + ty: wgt::BufferBindingType::Storage { read_only }, + has_dynamic_offset, min_binding_size: _, } => ( - &mut desc_count.storage_buffer, + if has_dynamic_offset { + &mut desc_count.storage_buffer_dynamic + } else { + &mut desc_count.storage_buffer + }, Some(wgt::Features::BUFFER_BINDING_ARRAY), + !read_only, ), - Bt::Buffer { - ty: wgt::BufferBindingType::Storage { .. }, - has_dynamic_offset: true, - min_binding_size: _, - } => ( - &mut desc_count.storage_buffer_dynamic, - Some(wgt::Features::BUFFER_BINDING_ARRAY), - ), - Bt::Sampler { .. } => (&mut desc_count.sampler, None), + Bt::Sampler { .. } => (&mut desc_count.sampler, None, false), Bt::Texture { .. } => ( &mut desc_count.sampled_image, Some(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), + false, + ), + Bt::StorageTexture { access, .. } => ( + &mut desc_count.storage_image, + None, + match access { + wgt::StorageTextureAccess::ReadOnly => false, + wgt::StorageTextureAccess::WriteOnly => true, + wgt::StorageTextureAccess::ReadWrite => { + required_features |= + wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES; + true + } + }, ), - Bt::StorageTexture { .. } => (&mut desc_count.storage_image, None), }; - *counter += match binding.count { + + *counter += match entry.count { // Validate the count parameter Some(count) => { - let feature = array_feature - .ok_or(binding_model::CreateBindGroupLayoutError::ArrayUnsupported)?; - if !self.features.contains(feature) { - return Err(binding_model::CreateBindGroupLayoutError::MissingFeature( - feature, - )); - } + required_features |= array_feature + .ok_or(binding_model::BindGroupLayoutEntryError::ArrayUnsupported) + .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry { + binding: entry.binding, + error, + })?; count.get() } None => 1, }; + if is_writable_storage && entry.visibility.contains(wgt::ShaderStage::VERTEX) { + required_features |= wgt::Features::VERTEX_WRITABLE_STORAGE; + } + + self.require_features(required_features) + .map_err(binding_model::BindGroupLayoutEntryError::MissingFeatures) + .map_err(|error| binding_model::CreateBindGroupLayoutError::Entry { + binding: entry.binding, + error, + })?; } let raw_bindings = entry_map @@ -1478,11 +1499,6 @@ impl Device { SmallVec::from([buffer_desc]) } Br::BufferArray(ref bindings_array) => { - let required_feats = wgt::Features::BUFFER_BINDING_ARRAY; - if !self.features.contains(required_feats) { - return Err(Error::MissingFeatures(required_feats)); - } - if let Some(count) = decl.count { let count = count.get() as usize; let num_bindings = bindings_array.len(); @@ -1631,13 +1647,9 @@ impl Device { if !view.format_features.flags.contains( wgt::TextureFormatFeatureFlags::STORAGE_READ_WRITE, ) { - return Err(if self.features.contains( - wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES, - ) { - Error::StorageReadWriteNotSupported(view.format) - } else { - Error::MissingFeatures(wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES) - }); + return Err(Error::StorageReadWriteNotSupported( + view.format, + )); } resource::TextureUse::STORAGE_STORE @@ -1686,11 +1698,6 @@ impl Device { } } Br::TextureViewArray(ref bindings_array) => { - let required_feats = wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY; - if !self.features.contains(required_feats) { - return Err(Error::MissingFeatures(required_feats)); - } - if let Some(count) = decl.count { let count = count.get() as usize; let num_bindings = bindings_array.len(); @@ -1814,11 +1821,10 @@ impl Device { }); } - if !desc.push_constant_ranges.is_empty() - && !self.features.contains(wgt::Features::PUSH_CONSTANTS) - { - return Err(Error::MissingFeature(wgt::Features::PUSH_CONSTANTS)); + if !desc.push_constant_ranges.is_empty() { + self.require_features(wgt::Features::PUSH_CONSTANTS)?; } + let mut used_stages = wgt::ShaderStage::empty(); for (index, pc) in desc.push_constant_ranges.iter().enumerate() { if pc.stages.intersects(used_stages) { @@ -2169,14 +2175,7 @@ impl Device { | wgt::VertexFormat::Float64x3 | wgt::VertexFormat::Float64x4 = attribute.format { - if !self - .features - .contains(wgt::Features::VERTEX_ATTRIBUTE_64BIT) - { - return Err(pipeline::CreateRenderPipelineError::MissingFeature( - wgt::Features::VERTEX_ATTRIBUTE_64BIT, - )); - } + self.require_features(wgt::Features::VERTEX_ATTRIBUTE_64BIT)?; } attributes.alloc().init(hal::pso::AttributeDesc { @@ -2221,27 +2220,15 @@ impl Device { ); } - if desc.primitive.clamp_depth && !self.features.contains(wgt::Features::DEPTH_CLAMPING) { - return Err(pipeline::CreateRenderPipelineError::MissingFeature( - wgt::Features::DEPTH_CLAMPING, - )); + if desc.primitive.clamp_depth { + self.require_features(wgt::Features::DEPTH_CLAMPING)?; } - if desc.primitive.polygon_mode != wgt::PolygonMode::Fill - && !self.features.contains(wgt::Features::NON_FILL_POLYGON_MODE) - { - return Err(pipeline::CreateRenderPipelineError::MissingFeature( - wgt::Features::NON_FILL_POLYGON_MODE, - )); + if desc.primitive.polygon_mode != wgt::PolygonMode::Fill { + self.require_features(wgt::Features::NON_FILL_POLYGON_MODE)?; } - if desc.primitive.conservative - && !self - .features - .contains(wgt::Features::CONSERVATIVE_RASTERIZATION) - { - return Err(pipeline::CreateRenderPipelineError::MissingFeature( - wgt::Features::CONSERVATIVE_RASTERIZATION, - )); + if desc.primitive.conservative { + self.require_features(wgt::Features::CONSERVATIVE_RASTERIZATION)?; } if desc.primitive.conservative && desc.primitive.polygon_mode != wgt::PolygonMode::Fill { @@ -2596,6 +2583,47 @@ impl Device { Ok(()) } } + + fn create_query_set( + &self, + self_id: id::DeviceId, + desc: &wgt::QuerySetDescriptor, + ) -> Result, resource::CreateQuerySetError> { + use resource::CreateQuerySetError as Error; + + match desc.ty { + wgt::QueryType::Timestamp => { + self.require_features(wgt::Features::TIMESTAMP_QUERY)?; + } + wgt::QueryType::PipelineStatistics(..) => { + self.require_features(wgt::Features::PIPELINE_STATISTICS_QUERY)?; + } + } + + if desc.count == 0 { + return Err(Error::ZeroCount); + } + + if desc.count >= wgt::QUERY_SET_MAX_QUERIES { + return Err(Error::TooManyQueries { + count: desc.count, + maximum: wgt::QUERY_SET_MAX_QUERIES, + }); + } + + let (hal_type, elements) = conv::map_query_type(&desc.ty); + + Ok(resource::QuerySet { + raw: unsafe { self.raw.create_query_pool(hal_type, desc.count).unwrap() }, + device_id: Stored { + value: id::Valid(self_id), + ref_count: self.life_guard.add_ref(), + }, + life_guard: LifeGuard::new(""), + desc: desc.clone(), + elements, + }) + } } impl Device { @@ -2703,6 +2731,10 @@ impl DeviceError { } } +#[derive(Clone, Debug, Error)] +#[error("Features {0:?} are required but not enabled on the device")] +pub struct MissingFeatures(pub wgt::Features); + #[derive(Clone, Debug)] #[cfg_attr(feature = "trace", derive(serde::Serialize))] #[cfg_attr(feature = "replay", derive(serde::Deserialize))] @@ -4017,50 +4049,9 @@ impl Global { }); } - match desc.ty { - wgt::QueryType::Timestamp => { - if !device.features.contains(wgt::Features::TIMESTAMP_QUERY) { - break resource::CreateQuerySetError::MissingFeature( - wgt::Features::TIMESTAMP_QUERY, - ); - } - } - wgt::QueryType::PipelineStatistics(..) => { - if !device - .features - .contains(wgt::Features::PIPELINE_STATISTICS_QUERY) - { - break resource::CreateQuerySetError::MissingFeature( - wgt::Features::PIPELINE_STATISTICS_QUERY, - ); - } - } - } - - if desc.count == 0 { - break resource::CreateQuerySetError::ZeroCount; - } - - if desc.count >= wgt::QUERY_SET_MAX_QUERIES { - break resource::CreateQuerySetError::TooManyQueries { - count: desc.count, - maximum: wgt::QUERY_SET_MAX_QUERIES, - }; - } - - let query_set = { - let (hal_type, elements) = conv::map_query_type(&desc.ty); - - resource::QuerySet { - raw: unsafe { device.raw.create_query_pool(hal_type, desc.count).unwrap() }, - device_id: Stored { - value: id::Valid(device_id), - ref_count: device.life_guard.add_ref(), - }, - life_guard: LifeGuard::new(""), - desc: desc.clone(), - elements, - } + let query_set = match device.create_query_set(device_id, desc) { + Ok(query_set) => query_set, + Err(err) => break err, }; let ref_count = query_set.life_guard.add_ref(); diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 1f79be13b3..0f2c2b318b 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -116,6 +116,87 @@ impl crate::hub::Resource for Surface { } } +const FEATURE_MAP: &[(wgt::Features, hal::Features)] = &[ + (wgt::Features::DEPTH_CLAMPING, hal::Features::DEPTH_CLAMP), + ( + wgt::Features::TEXTURE_COMPRESSION_BC, + hal::Features::FORMAT_BC, + ), + ( + wgt::Features::TEXTURE_COMPRESSION_ETC2, + hal::Features::FORMAT_ETC2, + ), + ( + wgt::Features::TEXTURE_COMPRESSION_ASTC_LDR, + hal::Features::FORMAT_ASTC_LDR, + ), + ( + wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY, + hal::Features::TEXTURE_DESCRIPTOR_ARRAY, + ), + ( + wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, + hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING, + ), + ( + wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING, + ), + ( + wgt::Features::UNSIZED_BINDING_ARRAY, + hal::Features::UNSIZED_DESCRIPTOR_ARRAY, + ), + ( + wgt::Features::MULTI_DRAW_INDIRECT, + hal::Features::MULTI_DRAW_INDIRECT, + ), + ( + wgt::Features::MULTI_DRAW_INDIRECT_COUNT, + hal::Features::DRAW_INDIRECT_COUNT, + ), + ( + wgt::Features::NON_FILL_POLYGON_MODE, + hal::Features::NON_FILL_POLYGON_MODE, + ), + ( + wgt::Features::PIPELINE_STATISTICS_QUERY, + hal::Features::PIPELINE_STATISTICS_QUERY, + ), + (wgt::Features::SHADER_FLOAT64, hal::Features::SHADER_FLOAT64), + ( + wgt::Features::CONSERVATIVE_RASTERIZATION, + hal::Features::CONSERVATIVE_RASTERIZATION, + ), + ( + wgt::Features::BUFFER_BINDING_ARRAY, + hal::Features::BUFFER_DESCRIPTOR_ARRAY, + ), + ( + wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, + hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, + ), + ( + wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING, + ), + ( + wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, + hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, + ), + ( + wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, + hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING, + ), + ( + wgt::Features::VERTEX_WRITABLE_STORAGE, + hal::Features::VERTEX_STORES_AND_ATOMICS, + ), + ( + wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, + hal::Features::SAMPLER_BORDER_COLOR, + ), +]; + #[derive(Debug)] pub struct Adapter { pub(crate) raw: hal::adapter::Adapter, @@ -137,89 +218,13 @@ impl Adapter { | wgt::Features::MAPPABLE_PRIMARY_BUFFERS | wgt::Features::PUSH_CONSTANTS | wgt::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES; - features.set( - 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::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), - ); - features.set( - wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, - adapter_features.contains(hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING), - ); - features.set( - wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, - adapter_features.contains(hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING), - ); - features.set( - wgt::Features::UNSIZED_BINDING_ARRAY, - adapter_features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY), - ); - features.set( - wgt::Features::MULTI_DRAW_INDIRECT, - adapter_features.contains(hal::Features::MULTI_DRAW_INDIRECT), - ); - features.set( - wgt::Features::MULTI_DRAW_INDIRECT_COUNT, - adapter_features.contains(hal::Features::DRAW_INDIRECT_COUNT), - ); - features.set( - wgt::Features::NON_FILL_POLYGON_MODE, - adapter_features.contains(hal::Features::NON_FILL_POLYGON_MODE), - ); + for &(hi, lo) in FEATURE_MAP.iter() { + features.set(hi, adapter_features.contains(lo)); + } features.set( wgt::Features::TIMESTAMP_QUERY, properties.limits.timestamp_compute_and_graphics, ); - features.set( - wgt::Features::PIPELINE_STATISTICS_QUERY, - adapter_features.contains(hal::Features::PIPELINE_STATISTICS_QUERY), - ); - features.set( - wgt::Features::SHADER_FLOAT64, - adapter_features.contains(hal::Features::SHADER_FLOAT64), - ); - features.set( - wgt::Features::CONSERVATIVE_RASTERIZATION, - adapter_features.contains(hal::Features::CONSERVATIVE_RASTERIZATION), - ); - features.set( - wgt::Features::BUFFER_BINDING_ARRAY, - adapter_features.contains(hal::Features::BUFFER_DESCRIPTOR_ARRAY), - ); - features.set( - wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, - adapter_features.contains(hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING), - ); - features.set( - wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - adapter_features.contains(hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING), - ); - features.set( - wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, - adapter_features.contains(hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING), - ); - features.set( - wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING, - adapter_features.contains(hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING), - ); - #[cfg(not(target_os = "ios"))] - //TODO: https://github.com/gfx-rs/gfx/issues/3346 - features.set(wgt::Features::ADDRESS_MODE_CLAMP_TO_BORDER, true); let private_features = PrivateFeatures { anisotropic_filtering: adapter_features.contains(hal::Features::SAMPLER_ANISOTROPY), @@ -452,12 +457,12 @@ impl Adapter { // Check features that are always needed let wishful_features = hal::Features::ROBUST_BUFFER_ACCESS - | hal::Features::VERTEX_STORES_AND_ATOMICS | hal::Features::FRAGMENT_STORES_AND_ATOMICS | hal::Features::NDC_Y_UP | hal::Features::INDEPENDENT_BLENDING | hal::Features::SAMPLER_ANISOTROPY - | hal::Features::IMAGE_CUBE_ARRAY; + | hal::Features::IMAGE_CUBE_ARRAY + | hal::Features::SAMPLE_RATE_SHADING; let mut enabled_features = available_features & wishful_features; if enabled_features != wishful_features { log::warn!( @@ -466,96 +471,10 @@ impl Adapter { ); } - // Features - enabled_features.set( - hal::Features::DEPTH_CLAMP, - desc.features.contains(wgt::Features::DEPTH_CLAMPING), - ); - 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 - .contains(wgt::Features::SAMPLED_TEXTURE_BINDING_ARRAY), - ); - enabled_features.set( - hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING, - desc.features - .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING), - ); - enabled_features.set( - hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING, - desc.features - .contains(wgt::Features::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING), - ); - enabled_features.set( - hal::Features::UNSIZED_DESCRIPTOR_ARRAY, - desc.features.contains(wgt::Features::UNSIZED_BINDING_ARRAY), - ); - enabled_features.set( - hal::Features::MULTI_DRAW_INDIRECT, - desc.features.contains(wgt::Features::MULTI_DRAW_INDIRECT), - ); - enabled_features.set( - hal::Features::DRAW_INDIRECT_COUNT, - desc.features - .contains(wgt::Features::MULTI_DRAW_INDIRECT_COUNT), - ); - enabled_features.set( - hal::Features::NON_FILL_POLYGON_MODE, - desc.features.contains(wgt::Features::NON_FILL_POLYGON_MODE), - ); - enabled_features.set( - hal::Features::PIPELINE_STATISTICS_QUERY, - desc.features - .contains(wgt::Features::PIPELINE_STATISTICS_QUERY), - ); - enabled_features.set( - hal::Features::SHADER_FLOAT64, - desc.features.contains(wgt::Features::SHADER_FLOAT64), - ); - enabled_features.set( - hal::Features::CONSERVATIVE_RASTERIZATION, - desc.features - .contains(wgt::Features::CONSERVATIVE_RASTERIZATION), - ); - enabled_features.set( - hal::Features::BUFFER_DESCRIPTOR_ARRAY, - desc.features.contains(wgt::Features::BUFFER_BINDING_ARRAY), - ); - enabled_features.set( - hal::Features::SHADER_UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING, - desc.features - .contains(wgt::Features::UNIFORM_BUFFER_ARRAY_DYNAMIC_INDEXING), - ); - enabled_features.set( - hal::Features::UNIFORM_BUFFER_DESCRIPTOR_INDEXING, - desc.features - .contains(wgt::Features::UNIFORM_BUFFER_ARRAY_NON_UNIFORM_INDEXING), - ); - enabled_features.set( - hal::Features::SHADER_STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING, - desc.features - .contains(wgt::Features::STORAGE_BUFFER_ARRAY_DYNAMIC_INDEXING), - ); - enabled_features.set( - hal::Features::STORAGE_BUFFER_DESCRIPTOR_INDEXING, - desc.features - .contains(wgt::Features::STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING), - ); + // Enable low-level features + for &(hi, lo) in FEATURE_MAP.iter() { + enabled_features.set(lo, desc.features.contains(hi)); + } let family = self .raw diff --git a/wgpu-core/src/pipeline.rs b/wgpu-core/src/pipeline.rs index a65b593af3..a770ba2e57 100644 --- a/wgpu-core/src/pipeline.rs +++ b/wgpu-core/src/pipeline.rs @@ -4,7 +4,7 @@ use crate::{ binding_model::{CreateBindGroupLayoutError, CreatePipelineLayoutError}, - device::{DeviceError, RenderPassContext}, + device::{DeviceError, MissingFeatures, RenderPassContext}, hub::Resource, id::{DeviceId, PipelineLayoutId, ShaderModuleId}, validation, Label, LifeGuard, Stored, DOWNLEVEL_ERROR_WARNING_MESSAGE, @@ -62,8 +62,8 @@ pub enum CreateShaderModuleError { Device(#[from] DeviceError), #[error(transparent)] Validation(#[from] naga::valid::ValidationError), - #[error("missing required device features {0:?}")] - MissingFeature(wgt::Features), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), } /// Describes a programmable pipeline stage. @@ -246,8 +246,8 @@ pub enum CreateRenderPipelineError { }, #[error("Conservative Rasterization is only supported for wgt::PolygonMode::Fill")] ConservativeRasterizationNonFillPolygonMode, - #[error("missing required device features {0:?}")] - MissingFeature(wgt::Features), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), #[error("error matching {stage:?} shader requirements against the pipeline")] Stage { stage: wgt::ShaderStage, diff --git a/wgpu-core/src/resource.rs b/wgpu-core/src/resource.rs index 398166e90c..802033ba2d 100644 --- a/wgpu-core/src/resource.rs +++ b/wgpu-core/src/resource.rs @@ -3,7 +3,7 @@ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ use crate::{ - device::{alloc::MemoryBlock, DeviceError, HostMap}, + device::{alloc::MemoryBlock, DeviceError, HostMap, MissingFeatures}, hub::Resource, id::{DeviceId, SwapChainId, TextureId}, memory_init_tracker::MemoryInitTracker, @@ -266,8 +266,8 @@ pub enum CreateTextureError { InvalidMipLevelCount(u32), #[error("The texture usages {0:?} are not allowed on a texture of type {1:?}")] InvalidUsages(wgt::TextureUsage, wgt::TextureFormat), - #[error("Feature {0:?} must be enabled to create a texture of type {1:?}")] - MissingFeature(wgt::Features, wgt::TextureFormat), + #[error("Texture format {0:?} can't be used")] + MissingFeatures(wgt::TextureFormat, #[source] MissingFeatures), } impl Resource for Texture { @@ -458,9 +458,9 @@ pub enum CreateSamplerError { InvalidClamp(u8), #[error("cannot create any more samplers")] TooManyObjects, - /// AddressMode::ClampToBorder requires feature ADDRESS_MODE_CLAMP_TO_BORDER - #[error("Feature {0:?} must be enabled")] - MissingFeature(wgt::Features), + /// AddressMode::ClampToBorder requires feature ADDRESS_MODE_CLAMP_TO_BORDER. + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), } impl Resource for Sampler { @@ -484,8 +484,8 @@ pub enum CreateQuerySetError { ZeroCount, #[error("{count} is too many queries for a single QuerySet. QuerySets cannot be made more than {maximum} queries.")] TooManyQueries { count: u32, maximum: u32 }, - #[error("Feature {0:?} must be enabled")] - MissingFeature(wgt::Features), + #[error(transparent)] + MissingFeatures(#[from] MissingFeatures), } #[derive(Debug)] diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 0520349033..338d23d8ed 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -526,6 +526,15 @@ bitflags::bitflags! { /// /// This is a native only feature. const STORAGE_BUFFER_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0010_0000_0000; + /// Enables bindings of writable storage buffers and textures visible to vertex shaders. + /// + /// Note: some (tiled-based) platforms do not support vertex shaders with any side-effects. + /// + /// Supported Platforms: + /// - All + /// + /// This is a native-only feature. + const VERTEX_WRITABLE_STORAGE = 0x0000_0020_0000_0000; /// Features which are part of the upstream WebGPU standard. const ALL_WEBGPU = 0x0000_0000_0000_FFFF; /// Features that are only available when targeting native (not web).