From 5a7fb4c1e57408c308ba058c90e28c8598c86bbb Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Sat, 13 Jun 2020 00:09:54 -0400 Subject: [PATCH] Implement Capability/Extension Split --- wgpu-core/src/binding_model.rs | 1 + wgpu-core/src/device/mod.rs | 27 +++++-- wgpu-core/src/instance.rs | 134 +++++++++++++++++++++------------ wgpu-types/src/lib.rs | 132 +++++++++++++++++++++----------- 4 files changed, 194 insertions(+), 100 deletions(-) diff --git a/wgpu-core/src/binding_model.rs b/wgpu-core/src/binding_model.rs index 6a7f5c6a04..3620a17150 100644 --- a/wgpu-core/src/binding_model.rs +++ b/wgpu-core/src/binding_model.rs @@ -21,6 +21,7 @@ use std::borrow::Borrow; pub enum BindGroupLayoutError { ConflictBinding(u32), MissingExtension(wgt::Extensions), + MissingCapability(wgt::Capabilities), /// Arrays of bindings can't be 0 elements long ZeroCount, /// Arrays of bindings unsupported for this type of binding diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index 4e14561717..fc5e732e3d 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -190,6 +190,7 @@ pub struct Device { pub(crate) private_features: PrivateFeatures, limits: wgt::Limits, extensions: wgt::Extensions, + capabilities: wgt::Capabilities, //TODO: move this behind another mutex. This would allow several methods to switch // to borrow Device immutably, such as `write_buffer`, `write_texture`, and `buffer_unmap`. pending_writes: queue::PendingWrites, @@ -206,6 +207,7 @@ impl Device { hal_limits: hal::Limits, private_features: PrivateFeatures, desc: &wgt::DeviceDescriptor, + capabilities: wgt::Capabilities, trace_path: Option<&std::path::Path>, ) -> Self { // don't start submission index at zero @@ -264,6 +266,7 @@ impl Device { private_features, limits: desc.limits.clone(), extensions: desc.extensions.clone(), + capabilities: capabilities.clone(), pending_writes: queue::PendingWrites::new(), } } @@ -637,7 +640,7 @@ impl Global { let (device_guard, _) = hub.devices.read(&mut token); let device = &device_guard[device_id]; - device.extensions.clone() + device.extensions } pub fn device_limits(&self, device_id: id::DeviceId) -> wgt::Limits { @@ -649,6 +652,15 @@ impl Global { device.limits.clone() } + pub fn device_capabilities(&self, device_id: id::DeviceId) -> wgt::Capabilities { + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, _) = hub.devices.read(&mut token); + let device = &device_guard[device_id]; + + device.capabilities + } + pub fn device_create_buffer( &self, device_id: id::DeviceId, @@ -1193,11 +1205,11 @@ impl Global { match binding.ty { wgt::BindingType::SampledTexture { .. } => { if !device - .extensions - .contains(wgt::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY) + .capabilities + .contains(wgt::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY) { - return Err(binding_model::BindGroupLayoutError::MissingExtension( - wgt::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY, + return Err(binding_model::BindGroupLayoutError::MissingCapability( + wgt::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY, )); } } @@ -1573,7 +1585,10 @@ impl Global { } } binding_model::BindingResource::TextureViewArray(ref bindings_array) => { - assert!(device.extensions.contains(wgt::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY), "Extension SAMPLED_TEXTURE_BINDING_ARRAY must be enabled to use TextureViewArrays in a bind group"); + assert!( + device.capabilities.contains(wgt::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY), + "Capability SAMPLED_TEXTURE_BINDING_ARRAY must be supported to use TextureViewArrays in a bind group" + ); if let Some(count) = decl.count { assert_eq!( diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index dd4044941c..5891823146 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -120,6 +120,7 @@ pub struct Adapter { pub(crate) raw: hal::adapter::Adapter, extensions: wgt::Extensions, limits: wgt::Limits, + capabilities: wgt::Capabilities, unsafe_extensions: wgt::UnsafeExtensions, life_guard: LifeGuard, } @@ -134,20 +135,11 @@ impl Adapter { adapter_features.contains(hal::Features::SAMPLER_ANISOTROPY), ); extensions.set( - wgt::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY, - adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY), - ); - extensions.set( - wgt::Extensions::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, - adapter_features.contains(hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING), - ); - extensions.set( - wgt::Extensions::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, - adapter_features.contains(hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING), - ); - extensions.set( - wgt::Extensions::UNSIZED_BINDING_ARRAY, - adapter_features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY), + wgt::Extensions::BINDING_INDEXING, + adapter_features.intersects( + hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING + | hal::Features::UNSIZED_DESCRIPTOR_ARRAY, + ), ); if unsafe_extensions.allowed() { // Unsafe extensions go here @@ -161,10 +153,30 @@ impl Adapter { _non_exhaustive: unsafe { wgt::NonExhaustive::new() }, }; + let mut capabilities = wgt::Capabilities::empty(); + + capabilities.set( + wgt::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY, + adapter_features.contains(hal::Features::TEXTURE_DESCRIPTOR_ARRAY), + ); + capabilities.set( + wgt::Capabilities::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING, + adapter_features.contains(hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING), + ); + capabilities.set( + wgt::Capabilities::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING, + adapter_features.contains(hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING), + ); + capabilities.set( + wgt::Capabilities::UNSIZED_BINDING_ARRAY, + adapter_features.contains(hal::Features::UNSIZED_DESCRIPTOR_ARRAY), + ); + Adapter { raw, extensions, limits, + capabilities, unsafe_extensions, life_guard: LifeGuard::new(), } @@ -590,6 +602,15 @@ impl Global { adapter.limits.clone() } + pub fn adapter_capabilities(&self, adapter_id: AdapterId) -> wgt::Capabilities { + let hub = B::hub(self); + let mut token = Token::root(); + let (adapter_guard, _) = hub.adapters.read(&mut token); + let adapter = &adapter_guard[adapter_id]; + + adapter.capabilities + } + pub fn adapter_destroy(&self, adapter_id: AdapterId) { let hub = B::hub(self); let mut token = Token::root(); @@ -624,20 +645,6 @@ impl Global { let adapter = &adapter_guard[adapter_id]; let phd = &adapter.raw.physical_device; - let available_features = adapter.raw.physical_device.features(); - - // Check features that are always needed - let wishful_features = hal::Features::VERTEX_STORES_AND_ATOMICS - | hal::Features::FRAGMENT_STORES_AND_ATOMICS - | hal::Features::NDC_Y_UP; - let mut enabled_features = available_features & wishful_features; - if enabled_features != wishful_features { - log::warn!( - "Missing features: {:?}", - wishful_features - enabled_features - ); - } - // Verify all extensions were exposed by the adapter if !adapter.unsafe_extensions.allowed() { assert!( @@ -652,12 +659,7 @@ impl Global { desc.extensions - adapter.extensions ); - // Check features needed by extensions - enabled_features.set( - hal::Features::SAMPLER_ANISOTROPY, - desc.extensions - .contains(wgt::Extensions::ANISOTROPIC_FILTERING), - ); + // Verify extension preconditions if desc .extensions .contains(wgt::Extensions::MAPPABLE_PRIMARY_BUFFERS) @@ -665,27 +667,62 @@ impl Global { { log::warn!("Extension MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. This is a massive performance footgun and likely not what you wanted"); } + + let available_features = adapter.raw.physical_device.features(); + + // Check features that are always needed + let wishful_features = hal::Features::VERTEX_STORES_AND_ATOMICS + | hal::Features::FRAGMENT_STORES_AND_ATOMICS + | hal::Features::NDC_Y_UP; + let mut enabled_features = available_features & wishful_features; + if enabled_features != wishful_features { + log::warn!( + "Missing features: {:?}", + wishful_features - enabled_features + ); + } + + // Extensions + enabled_features.set( + hal::Features::SAMPLER_ANISOTROPY, + desc.extensions + .contains(wgt::Extensions::ANISOTROPIC_FILTERING), + ); + + let mut enabled_capabilities = adapter.capabilities & wgt::Capabilities::ALL_BUILT_IN; + + // Capabilities without extension gates enabled_features.set( hal::Features::TEXTURE_DESCRIPTOR_ARRAY, - desc.extensions - .contains(wgt::Extensions::SAMPLED_TEXTURE_BINDING_ARRAY), + adapter + .capabilities + .contains(wgt::Capabilities::SAMPLED_TEXTURE_BINDING_ARRAY), ); enabled_features.set( hal::Features::SHADER_SAMPLED_IMAGE_ARRAY_DYNAMIC_INDEXING, - desc.extensions - .contains(wgt::Extensions::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING), - ); - enabled_features.set( - hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING, - desc.extensions - .contains(wgt::Extensions::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING), - ); - enabled_features.set( - hal::Features::UNSIZED_DESCRIPTOR_ARRAY, - desc.extensions - .contains(wgt::Extensions::UNSIZED_BINDING_ARRAY), + adapter + .capabilities + .contains(wgt::Capabilities::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING), ); + // Capabilities behind BINDING_INDEXING + if desc.extensions.contains(wgt::Extensions::BINDING_INDEXING) { + enabled_capabilities + .insert(adapter.capabilities & wgt::Capabilities::ALL_BINDING_INDEXING); + enabled_features.set( + hal::Features::SAMPLED_TEXTURE_DESCRIPTOR_INDEXING, + adapter + .capabilities + .contains(wgt::Capabilities::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING), + ); + enabled_features.set( + hal::Features::UNSIZED_DESCRIPTOR_ARRAY, + adapter + .capabilities + .contains(wgt::Capabilities::UNSIZED_BINDING_ARRAY), + ); + } + let family = adapter .raw .queue_families @@ -737,6 +774,7 @@ impl Global { limits, private_features, desc, + enabled_capabilities, trace_path, ) }; diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index 2c7f710ac1..f8958ce8d7 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -142,6 +142,9 @@ bitflags::bitflags! { /// - Metal /// - Vulkan /// + /// Provided Capabilities: + /// - None + /// /// This is a native only extension. Support is planned to be added to webgpu, /// but it is not yet implemented. /// @@ -157,37 +160,12 @@ bitflags::bitflags! { /// Supported platforms: /// - All /// + /// Provided Capabilities: + /// - None + /// /// This is a native only extension. const MAPPABLE_PRIMARY_BUFFERS = 0x0000_0000_0002_0000; - /// Allows the user to create uniform arrays of sampled textures in shaders: - /// - /// eg. `uniform texture2D textures[10]`. - /// - /// This extension only allows them to exist and to be indexed by compile time constant - /// values. - /// - /// Supported platforms: - /// - DX12 - /// - Metal (with MSL 2.0+ on macOS 10.13+) - /// - Vulkan - /// - /// This is a native only extension. - const SAMPLED_TEXTURE_BINDING_ARRAY = 0x0000_0000_0004_0000; - /// Allows shaders to index sampled texture arrays with dynamically uniform values: - /// - /// eg. `texture_array[uniform_value]` - /// - /// This extension means the hardware will also support SAMPLED_TEXTURE_BINDING_ARRAY, - /// but it still must be requested to use it. - /// - /// Supported platforms: - /// - DX12 - /// - Metal (with MSL 2.0+ on macOS 10.13+) - /// - Vulkan (via feature shaderSampledImageArrayDynamicIndexing) - /// - /// This is a native only extension. - const SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING = 0x0000_0000_0008_0000; - /// Allows shaders to index sampled texture arrays with dynamically non-uniform values: + /// Allows shaders to index arrays of bindings with dynamically non-uniform values: /// /// eg. `texture_array[vertex_data]` /// @@ -197,30 +175,17 @@ bitflags::bitflags! { /// /// HLSL does not need any extension. /// - /// This extension means the hardware will also support SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING - /// and SAMPLED_TEXTURE_BINDING_ARRAY, but they still must be requested to use them. - /// /// Supported platforms: /// - DX12 /// - Metal (with MSL 2.0+ on macOS 10.13+) /// - Vulkan 1.2+ (or via VK_EXT_descriptor_indexing) /// - /// This is a native only extension. - const SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0000_0010_0000; - /// Allows the user to create unsized uniform arrays of bindings: - /// - /// eg. `uniform texture2D textures[]`. - /// - /// This extension only allows them to exist and to be indexed by compile time constant - /// values. However if this extension exists, SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING is - /// _very_ likely to exist. - /// - /// Supported platforms: - /// - DX12 - /// - Vulkan 1.2+ (or via VK_EXT_descriptor_indexing) + /// Provided Capabilities: + /// - SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING + /// - UNSIZED_BINDING_ARRAY /// /// This is a native only extension. - const UNSIZED_BINDING_ARRAY = 0x0000_0000_0020_0000; + const BINDING_INDEXING = 0x0000_0000_0004_0000; /// Extensions which are part of the upstream webgpu standard const ALL_WEBGPU = 0x0000_0000_0000_FFFF; /// Extensions that require activating the unsafe extension flag @@ -268,6 +233,81 @@ impl Default for Limits { } } +bitflags::bitflags! { + #[repr(transparent)] + #[derive(Default)] + #[cfg_attr(feature = "trace", derive(Serialize))] + #[cfg_attr(feature = "replay", derive(Deserialize))] + pub struct Capabilities: u64 { + /// Allows the user to create uniform arrays of sampled textures in shaders: + /// + /// eg. `uniform texture2D textures[10]`. + /// + /// This capability allows them to exist and to be indexed by compile time constant + /// values. + /// + /// Supported platforms: + /// - DX12 + /// - Metal (with MSL 2.0+ on macOS 10.13+) + /// - Vulkan + const SAMPLED_TEXTURE_BINDING_ARRAY = 0x0000_0000_0000_0001; + /// Allows shaders to index sampled texture arrays with dynamically uniform values: + /// + /// eg. `texture_array[uniform_value]` + /// + /// This capability means the hardware will also support SAMPLED_TEXTURE_BINDING_ARRAY. + /// + /// Supported platforms: + /// - DX12 + /// - Metal (with MSL 2.0+ on macOS 10.13+) + /// - Vulkan's shaderSampledImageArrayDynamicIndexing feature + const SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING = 0x0000_0000_0000_0002; + /// Allows shaders to index sampled texture arrays with dynamically non-uniform values: + /// + /// eg. `texture_array[vertex_data]` + /// + /// In order to use this capability, the corresponding GLSL extension must be enabled like so: + /// + /// `#extension GL_EXT_nonuniform_qualifier : require` + /// + /// HLSL does not need any extension. + /// + /// This capability means the hardware will also support SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING + /// and SAMPLED_TEXTURE_BINDING_ARRAY. + /// + /// Supported platforms: + /// - DX12 + /// - Metal (with MSL 2.0+ on macOS 10.13+) + /// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s shaderSampledImageArrayNonUniformIndexing feature) + /// + /// Provided by the BINDING_INDEXING extension. This extension must be enabled for this capability + /// to be anything but false. + const SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING = 0x0000_0000_0000_0004; + /// Allows the user to create unsized uniform arrays of bindings: + /// + /// eg. `uniform texture2D textures[]`. + /// + /// If this capability is supported, SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING is very likely + /// to also be supported + /// + /// Supported platforms: + /// - DX12 + /// - Vulkan 1.2+ (or VK_EXT_descriptor_indexing)'s runtimeDescriptorArray feature + /// + /// Provided by the BINDING_INDEXING extension. This extension must be enabled for this capability + /// to be anything but false. + const UNSIZED_BINDING_ARRAY = 0x0000_0000_0000_0008; + /// All capabilities that are not provided by an extension + const ALL_BUILT_IN = + Self::SAMPLED_TEXTURE_BINDING_ARRAY.bits | + Self::SAMPLED_TEXTURE_ARRAY_DYNAMIC_INDEXING.bits; + /// All capabilities that are provided by the BINDING_INDEXING extension + const ALL_BINDING_INDEXING = + Self::SAMPLED_TEXTURE_ARRAY_NON_UNIFORM_INDEXING.bits | + Self::UNSIZED_BINDING_ARRAY.bits; + } +} + #[repr(C)] #[derive(Clone, Debug, Default)] #[cfg_attr(feature = "trace", derive(Serialize))]