717: Implement Capability/Extension Split r=kvark a=cwfitzgerald

## Connections

Follow up to a discussion we had on #wgpu:matrix.org. 

## Description

Splits capabilities and extensions so that extensions are things you ask for and possibly change behavior and capabilities are passively enabled when their extension (if any) are enabled.

Co-authored-by: Connor Fitzgerald <connorwadefitzgerald@gmail.com>
This commit is contained in:
bors[bot]
2020-06-15 01:46:14 +00:00
committed by GitHub
4 changed files with 194 additions and 100 deletions

View File

@@ -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

View File

@@ -190,6 +190,7 @@ pub struct Device<B: hal::Backend> {
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<B>,
@@ -206,6 +207,7 @@ impl<B: GfxBackend> Device<B> {
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<B: GfxBackend> Device<B> {
private_features,
limits: desc.limits.clone(),
extensions: desc.extensions.clone(),
capabilities: capabilities.clone(),
pending_writes: queue::PendingWrites::new(),
}
}
@@ -637,7 +640,7 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
let (device_guard, _) = hub.devices.read(&mut token);
let device = &device_guard[device_id];
device.extensions.clone()
device.extensions
}
pub fn device_limits<B: GfxBackend>(&self, device_id: id::DeviceId) -> wgt::Limits {
@@ -649,6 +652,15 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
device.limits.clone()
}
pub fn device_capabilities<B: GfxBackend>(&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<B: GfxBackend>(
&self,
device_id: id::DeviceId,
@@ -1193,11 +1205,11 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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<G: GlobalIdentityHandlerFactory> Global<G> {
}
}
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!(

View File

@@ -120,6 +120,7 @@ pub struct Adapter<B: hal::Backend> {
pub(crate) raw: hal::adapter::Adapter<B>,
extensions: wgt::Extensions,
limits: wgt::Limits,
capabilities: wgt::Capabilities,
unsafe_extensions: wgt::UnsafeExtensions,
life_guard: LifeGuard,
}
@@ -134,20 +135,11 @@ impl<B: hal::Backend> Adapter<B> {
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<B: hal::Backend> Adapter<B> {
_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<G: GlobalIdentityHandlerFactory> Global<G> {
adapter.limits.clone()
}
pub fn adapter_capabilities<B: GfxBackend>(&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<B: GfxBackend>(&self, adapter_id: AdapterId) {
let hub = B::hub(self);
let mut token = Token::root();
@@ -624,20 +645,6 @@ impl<G: GlobalIdentityHandlerFactory> Global<G> {
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<G: GlobalIdentityHandlerFactory> Global<G> {
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<G: GlobalIdentityHandlerFactory> Global<G> {
{
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<G: GlobalIdentityHandlerFactory> Global<G> {
limits,
private_features,
desc,
enabled_capabilities,
trace_path,
)
};

View File

@@ -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))]