mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
Require token for experimental features (#8163)
This commit is contained in:
parent
e58223bedf
commit
8d1f4bb5f2
15
CHANGELOG.md
15
CHANGELOG.md
@ -73,6 +73,21 @@ Difference for SPIR-V passthrough:
|
||||
```
|
||||
This allows using precompiled shaders without manually checking which backend's code to pass, for example if you have shaders precompiled for both DXIL and SPIR-V.
|
||||
|
||||
#### `EXPERIMENTAL_*` features now require unsafe code to enable
|
||||
|
||||
We want to be able to expose potentially experimental features to our users before we have ensured that they are fully sound to use.
|
||||
As such, we now require any feature that is prefixed with `EXPERIMENTAL` to have a special unsafe token enabled in the device descriptor
|
||||
acknowledging that the features may still have bugs in them and to report any they find.
|
||||
|
||||
```rust
|
||||
adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,
|
||||
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() }
|
||||
..
|
||||
})
|
||||
```
|
||||
|
||||
By @cwfitzgerald in [#8163](https://github.com/gfx-rs/wgpu/pull/8163).
|
||||
|
||||
#### Multi-draw indirect is now unconditionally supported when indirect draws are supported
|
||||
|
||||
|
||||
@ -47,6 +47,7 @@ impl DeviceState {
|
||||
required_features: adapter.features(),
|
||||
required_limits: adapter.limits(),
|
||||
memory_hints: wgpu::MemoryHints::Performance,
|
||||
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
|
||||
label: Some("Compute/RenderPass Device"),
|
||||
trace: wgpu::Trace::Off,
|
||||
}))
|
||||
|
||||
@ -146,6 +146,7 @@ impl GPUAdapter {
|
||||
descriptor.required_features,
|
||||
),
|
||||
required_limits,
|
||||
experimental_features: wgpu_types::ExperimentalFeatures::disabled(),
|
||||
memory_hints: Default::default(),
|
||||
trace,
|
||||
};
|
||||
|
||||
@ -287,6 +287,7 @@ impl ExampleContext {
|
||||
required_features: (E::optional_features() & adapter.features())
|
||||
| E::required_features(),
|
||||
required_limits: needed_limits,
|
||||
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: match std::env::var_os("WGPU_TRACE") {
|
||||
Some(path) => wgpu::Trace::Directory(path.into()),
|
||||
|
||||
@ -16,6 +16,7 @@ async fn run() {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::Performance,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -31,6 +31,7 @@ async fn run(event_loop: EventLoop<()>, window: Window) {
|
||||
// Make sure we use the texture resolution limits from the adapter, so we can support images the size of the swapchain.
|
||||
required_limits: wgpu::Limits::downlevel_webgl2_defaults()
|
||||
.using_resolution(adapter.limits()),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -74,6 +74,7 @@ async fn run(event_loop: EventLoop<()>, viewports: Vec<(Arc<Window>, wgpu::Color
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -31,6 +31,7 @@ async fn run() {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -20,6 +20,7 @@ async fn run(_path: Option<String>) {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -164,6 +164,7 @@ impl WgpuContext {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::Performance,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -34,6 +34,7 @@ async fn run(_path: Option<String>) {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -210,6 +210,7 @@ async fn run() {
|
||||
label: None,
|
||||
required_features: features,
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -114,6 +114,7 @@ impl WgpuContext {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -70,6 +70,7 @@ fn main() {
|
||||
label: None,
|
||||
required_features: wgpu::Features::empty(),
|
||||
required_limits: wgpu::Limits::downlevel_defaults(),
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
}))
|
||||
|
||||
@ -95,6 +95,7 @@ impl Test<'_> {
|
||||
label: None,
|
||||
required_features: self.features,
|
||||
required_limits: wgt::Limits::default(),
|
||||
experimental_features: unsafe { wgt::ExperimentalFeatures::enabled() },
|
||||
memory_hints: wgt::MemoryHints::default(),
|
||||
trace: wgt::Trace::Off,
|
||||
},
|
||||
|
||||
@ -163,6 +163,7 @@ pub async fn initialize_device(
|
||||
label: None,
|
||||
required_features: features,
|
||||
required_limits: limits,
|
||||
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
|
||||
memory_hints: wgpu::MemoryHints::MemoryUsage,
|
||||
trace: wgpu::Trace::Off,
|
||||
})
|
||||
|
||||
@ -156,7 +156,7 @@ async fn request_device_error_message() {
|
||||
let expected = "TypeError";
|
||||
} else {
|
||||
// This message appears whenever wgpu-core is used as the implementation.
|
||||
let expected = "Unsupported features were requested: Features {";
|
||||
let expected = "Unsupported features were requested:";
|
||||
}
|
||||
}
|
||||
assert!(device_error.contains(expected), "{device_error}");
|
||||
|
||||
70
tests/tests/wgpu-validation/api/experimental.rs
Normal file
70
tests/tests/wgpu-validation/api/experimental.rs
Normal file
@ -0,0 +1,70 @@
|
||||
fn noop_adapter() -> wgpu::Adapter {
|
||||
let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor {
|
||||
backends: wgpu::Backends::NOOP,
|
||||
backend_options: wgpu::BackendOptions {
|
||||
noop: wgpu::NoopBackendOptions { enable: true },
|
||||
..Default::default()
|
||||
},
|
||||
..Default::default()
|
||||
});
|
||||
|
||||
pollster::block_on(instance.request_adapter(&wgpu::RequestAdapterOptions::default()))
|
||||
.expect("noop backend adapter absent when it should be")
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_no_experimental_features() {
|
||||
let adapter = noop_adapter();
|
||||
|
||||
let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
// Not experimental
|
||||
required_features: wgpu::Features::FLOAT32_FILTERABLE,
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
assert!(dq.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_experimental_features() {
|
||||
let adapter = noop_adapter();
|
||||
|
||||
let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
// Experimental
|
||||
required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,
|
||||
experimental_features: unsafe { wgpu::ExperimentalFeatures::enabled() },
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
assert!(dq.is_ok());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_experimental_features_when_not_enabled() {
|
||||
let adapter = noop_adapter();
|
||||
|
||||
let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
// Experimental
|
||||
required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER,
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
assert!(dq.is_err());
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn request_multiple_experimental_features_when_not_enabled() {
|
||||
let adapter = noop_adapter();
|
||||
|
||||
let dq = pollster::block_on(adapter.request_device(&wgpu::DeviceDescriptor {
|
||||
// Experimental
|
||||
required_features: wgpu::Features::EXPERIMENTAL_MESH_SHADER
|
||||
| wgpu::Features::EXPERIMENTAL_PASSTHROUGH_SHADERS,
|
||||
experimental_features: wgpu::ExperimentalFeatures::disabled(),
|
||||
..Default::default()
|
||||
}));
|
||||
|
||||
assert!(dq.is_err());
|
||||
}
|
||||
@ -2,6 +2,7 @@ mod binding_arrays;
|
||||
mod buffer;
|
||||
mod buffer_slice;
|
||||
mod device;
|
||||
mod experimental;
|
||||
mod external_texture;
|
||||
mod instance;
|
||||
mod texture;
|
||||
|
||||
@ -797,6 +797,18 @@ impl Adapter {
|
||||
));
|
||||
}
|
||||
|
||||
// Check if experimental features are permitted to be enabled.
|
||||
if desc
|
||||
.required_features
|
||||
.intersects(wgt::Features::all_experimental_mask())
|
||||
&& !desc.experimental_features.is_enabled()
|
||||
{
|
||||
return Err(RequestDeviceError::ExperimentalFeaturesNotEnabled(
|
||||
desc.required_features
|
||||
.intersection(wgt::Features::all_experimental_mask()),
|
||||
));
|
||||
}
|
||||
|
||||
let caps = &self.raw.capabilities;
|
||||
if Backends::PRIMARY.contains(Backends::from(self.backend()))
|
||||
&& !caps.downlevel.is_webgpu_compliant()
|
||||
@ -857,8 +869,12 @@ pub enum RequestDeviceError {
|
||||
LimitsExceeded(#[from] FailedLimit),
|
||||
#[error("Failed to initialize Timestamp Normalizer")]
|
||||
TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError),
|
||||
#[error("Unsupported features were requested: {0:?}")]
|
||||
#[error("Unsupported features were requested: {0}")]
|
||||
UnsupportedFeature(wgt::Features),
|
||||
#[error(
|
||||
"Some experimental features, {0}, were requested, but experimental features are not enabled"
|
||||
)]
|
||||
ExperimentalFeaturesNotEnabled(wgt::Features),
|
||||
}
|
||||
|
||||
#[derive(Clone, Debug, Error)]
|
||||
|
||||
@ -1498,6 +1498,19 @@ impl Features {
|
||||
]))
|
||||
}
|
||||
|
||||
/// Mask of all features which are experimental.
|
||||
#[must_use]
|
||||
pub const fn all_experimental_mask() -> Self {
|
||||
Self::from_bits_truncate(FeatureBits([
|
||||
FeaturesWGPU::EXPERIMENTAL_MESH_SHADER.bits()
|
||||
| FeaturesWGPU::EXPERIMENTAL_MESH_SHADER_MULTIVIEW.bits()
|
||||
| FeaturesWGPU::EXPERIMENTAL_RAY_QUERY.bits()
|
||||
| FeaturesWGPU::EXPERIMENTAL_RAY_HIT_VERTEX_RETURN.bits()
|
||||
| FeaturesWGPU::EXPERIMENTAL_PASSTHROUGH_SHADERS.bits(),
|
||||
FeaturesWebGPU::empty().bits(),
|
||||
]))
|
||||
}
|
||||
|
||||
/// Vertex formats allowed for creating and building BLASes
|
||||
#[must_use]
|
||||
pub fn allowed_vertex_formats_for_blas(&self) -> Vec<VertexFormat> {
|
||||
@ -1624,4 +1637,13 @@ mod tests {
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn experimental_features_part_of_experimental_mask() {
|
||||
for (name, feature) in Features::all().iter_names() {
|
||||
let prefixed_with_experimental = name.starts_with("EXPERIMENTAL_");
|
||||
let in_experimental_mask = Features::all_experimental_mask().contains(feature);
|
||||
assert_eq!(in_experimental_mask, prefixed_with_experimental);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -41,11 +41,13 @@ pub mod error;
|
||||
mod features;
|
||||
pub mod instance;
|
||||
pub mod math;
|
||||
mod tokens;
|
||||
mod transfers;
|
||||
|
||||
pub use counters::*;
|
||||
pub use features::*;
|
||||
pub use instance::*;
|
||||
pub use tokens::*;
|
||||
pub use transfers::*;
|
||||
|
||||
/// Integral type used for [`Buffer`] offsets and sizes.
|
||||
@ -1468,6 +1470,9 @@ pub struct DeviceDescriptor<L> {
|
||||
/// Exactly the specified limits, and no better or worse,
|
||||
/// will be allowed in validation of API calls on the resulting device.
|
||||
pub required_limits: Limits,
|
||||
/// Specifies whether `self.required_features` is allowed to contain experimental features.
|
||||
#[cfg_attr(feature = "serde", serde(skip))]
|
||||
pub experimental_features: ExperimentalFeatures,
|
||||
/// Hints for memory allocation strategies.
|
||||
pub memory_hints: MemoryHints,
|
||||
/// Whether API tracing for debugging is enabled,
|
||||
@ -1483,6 +1488,7 @@ impl<L> DeviceDescriptor<L> {
|
||||
label: fun(&self.label),
|
||||
required_features: self.required_features,
|
||||
required_limits: self.required_limits.clone(),
|
||||
experimental_features: self.experimental_features,
|
||||
memory_hints: self.memory_hints.clone(),
|
||||
trace: self.trace.clone(),
|
||||
}
|
||||
|
||||
43
wgpu-types/src/tokens.rs
Normal file
43
wgpu-types/src/tokens.rs
Normal file
@ -0,0 +1,43 @@
|
||||
/// Token of the user agreeing to access experimental features.
|
||||
#[derive(Debug, Default, Copy, Clone)]
|
||||
pub struct ExperimentalFeatures {
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
impl ExperimentalFeatures {
|
||||
/// Uses of [`Features`] prefixed with "EXPERIMENTAL" are disallowed.
|
||||
///
|
||||
/// [`Features`]: ../wgpu/struct.Features.html
|
||||
pub const fn disabled() -> Self {
|
||||
Self { enabled: false }
|
||||
}
|
||||
|
||||
/// Uses of [`Features`] prefixed with "EXPERIMENTAL" may result
|
||||
/// in undefined behavior when used incorrectly. The exact bounds
|
||||
/// of these issues varies by the feature. These instances are
|
||||
/// inherently bugs in our implementation that we will eventually fix.
|
||||
///
|
||||
/// By giving access to still work-in-progress APIs, users can get
|
||||
/// access to newer technology sooner, and we can work with users
|
||||
/// to fix bugs quicker.
|
||||
///
|
||||
/// Look inside our repo at the [`api-specs`] for more information
|
||||
/// on various experimental apis.
|
||||
///
|
||||
/// # Safety
|
||||
///
|
||||
/// - You acknowledge that there may be UB-containing bugs in these
|
||||
/// apis and those may be hit by calling otherwise safe code.
|
||||
/// - You agree to report any such bugs to us, if you find them.
|
||||
///
|
||||
/// [`Features`]: ../wgpu/struct.Features.html
|
||||
/// [`api-specs`]: https://github.com/gfx-rs/wgpu/tree/trunk/docs/api-specs
|
||||
pub const unsafe fn enabled() -> Self {
|
||||
Self { enabled: true }
|
||||
}
|
||||
|
||||
/// Returns true if the user has agreed to access experimental features.
|
||||
pub const fn is_enabled(&self) -> bool {
|
||||
self.enabled
|
||||
}
|
||||
}
|
||||
@ -91,7 +91,7 @@ pub use wgt::{
|
||||
CommandBufferDescriptor, CompareFunction, CompositeAlphaMode, CopyExternalImageDestInfo,
|
||||
CoreCounters, DepthBiasState, DepthStencilState, DeviceLostReason, DeviceType,
|
||||
DownlevelCapabilities, DownlevelFlags, DownlevelLimits, Dx12BackendOptions, Dx12Compiler,
|
||||
DxcShaderModel, DynamicOffset, Extent3d, ExternalTextureFormat,
|
||||
DxcShaderModel, DynamicOffset, ExperimentalFeatures, Extent3d, ExternalTextureFormat,
|
||||
ExternalTextureTransferFunction, Face, Features, FeaturesWGPU, FeaturesWebGPU, FilterMode,
|
||||
FrontFace, GlBackendOptions, GlFenceBehavior, Gles3MinorVersion, HalCounters,
|
||||
ImageSubresourceRange, IndexFormat, InstanceDescriptor, InstanceFlags, InternalCounters,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user