From 4db9a4522ee213ec8dbc8a659a41a4bb61c75156 Mon Sep 17 00:00:00 2001 From: Connor Fitzgerald Date: Wed, 31 Mar 2021 00:59:38 -0400 Subject: [PATCH] Add downlevel infrastructure --- wgpu-core/src/device/mod.rs | 28 +++++++++++++----- wgpu-core/src/instance.rs | 34 +++++++++++++++++++++ wgpu-types/src/lib.rs | 59 +++++++++++++++++++++++++++++++++++++ 3 files changed, 114 insertions(+), 7 deletions(-) diff --git a/wgpu-core/src/device/mod.rs b/wgpu-core/src/device/mod.rs index db32adbbdc..e3d71b0fce 100644 --- a/wgpu-core/src/device/mod.rs +++ b/wgpu-core/src/device/mod.rs @@ -284,6 +284,7 @@ pub struct Device { pub(crate) private_features: PrivateFeatures, pub(crate) limits: wgt::Limits, pub(crate) features: wgt::Features, + pub(crate) downlevel: wgt::DownlevelProperties, spv_options: naga::back::spv::Options, //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`. @@ -307,6 +308,7 @@ impl Device { mem_props: hal::adapter::MemoryProperties, hal_limits: hal::Limits, private_features: PrivateFeatures, + downlevel: wgt::DownlevelProperties, desc: &DeviceDescriptor, trace_path: Option<&std::path::Path>, ) -> Result { @@ -376,6 +378,7 @@ impl Device { private_features, limits: desc.limits.clone(), features: desc.features, + downlevel, spv_options, pending_writes: queue::PendingWrites::new(), }) @@ -1017,7 +1020,8 @@ impl Device { let (naga_result, interface) = match module { // If succeeded, then validate it and attempt to give it to gfx-hal directly. Some(module) if desc.flags.contains(wgt::ShaderFlags::VALIDATION) || spv.is_none() => { - let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all()).validate(&module)?; + let info = naga::valid::Validator::new(naga::valid::ValidationFlags::all()) + .validate(&module)?; if !self.features.contains(wgt::Features::PUSH_CONSTANTS) && module .global_variables @@ -1060,12 +1064,8 @@ impl Device { None => { // Produce a SPIR-V from the Naga module let shader = maybe_shader.unwrap(); - naga::back::spv::write_vec( - &shader.module, - &shader.info, - &self.spv_options, - ) - .map(Cow::Owned) + naga::back::spv::write_vec(&shader.module, &shader.info, &self.spv_options) + .map(Cow::Owned) } }; match spv { @@ -2656,6 +2656,20 @@ impl Global { Ok(device.limits.clone()) } + pub fn device_downlevel_properties( + &self, + device_id: id::DeviceId, + ) -> Result { + profiling::scope!("Device::downlevel_properties"); + + let hub = B::hub(self); + let mut token = Token::root(); + let (device_guard, _) = hub.devices.read(&mut token); + let device = device_guard.get(device_id).map_err(|_| InvalidDevice)?; + + Ok(device.downlevel) + } + pub fn device_create_buffer( &self, device_id: id::DeviceId, diff --git a/wgpu-core/src/instance.rs b/wgpu-core/src/instance.rs index 05d0d58e69..2f2f59a5c6 100644 --- a/wgpu-core/src/instance.rs +++ b/wgpu-core/src/instance.rs @@ -122,6 +122,7 @@ pub struct Adapter { features: wgt::Features, pub(crate) private_features: PrivateFeatures, limits: wgt::Limits, + downlevel: wgt::DownlevelProperties, life_guard: LifeGuard, } @@ -274,11 +275,28 @@ impl Adapter { .max(MIN_PUSH_CONSTANT_SIZE), // As an extension, the default is always 0, so define a separate minimum. }; + let downlevel = wgt::DownlevelProperties { + compute_shaders: properties.downlevel.compute_shaders, + shader_model: match properties.downlevel.shader_model { + hal::DownlevelShaderModel::ShaderModel2 => wgt::ShaderModel::Sm2, + hal::DownlevelShaderModel::ShaderModel4 => wgt::ShaderModel::Sm4, + hal::DownlevelShaderModel::ShaderModel5 => wgt::ShaderModel::Sm5, + }, + storage_images: properties.downlevel.storage_images, + read_only_depth_stencil: properties.downlevel.read_only_depth_stencil, + device_local_image_copies: properties.downlevel.device_local_image_copies, + non_power_of_two_mipmapped_textures: properties + .downlevel + .non_power_of_two_mipmapped_textures, + anisotropic_filtering: private_features.anisotropic_filtering, + }; + Self { raw, features, private_features, limits, + downlevel, life_guard: LifeGuard::new(""), } } @@ -523,6 +541,7 @@ impl Adapter { mem_props, limits, self.private_features, + self.downlevel, desc, trace_path, ) @@ -926,6 +945,21 @@ impl Global { .map_err(|_| InvalidAdapter) } + pub fn adapter_downlevel_properties( + &self, + adapter_id: AdapterId, + ) -> Result { + profiling::scope!("Adapter::downlevel_properties"); + + let hub = B::hub(self); + let mut token = Token::root(); + let (adapter_guard, _) = hub.adapters.read(&mut token); + adapter_guard + .get(adapter_id) + .map(|adapter| adapter.downlevel) + .map_err(|_| InvalidAdapter) + } + pub fn adapter_drop(&self, adapter_id: AdapterId) { profiling::scope!("Adapter::drop"); diff --git a/wgpu-types/src/lib.rs b/wgpu-types/src/lib.rs index cb167be69b..aef2c5a35b 100644 --- a/wgpu-types/src/lib.rs +++ b/wgpu-types/src/lib.rs @@ -538,6 +538,65 @@ impl Default for Limits { } } +/// Lists various ways the underlying platform does not conform to the WebGPU standard. +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub struct DownlevelProperties { + /// The device supports compiling and using compute shaders. + pub compute_shaders: bool, + /// Which collections of features shaders support. Defined in terms of D3D's shader models. + pub shader_model: ShaderModel, + /// Supports creating storage images. + pub storage_images: bool, + /// Supports reading from a depth/stencil buffer while using as a read-only depth/stencil attachment. + pub read_only_depth_stencil: bool, + /// Supports: + /// - copy_image_to_image + /// - copy_buffer_to_image and copy_image_to_buffer with a buffer without a MAP_* usage + pub device_local_image_copies: bool, + /// Supports textures with mipmaps which have a non power of two size. + pub non_power_of_two_mipmapped_textures: bool, + /// Supports samplers with anisotropic filtering + pub anisotropic_filtering: bool, +} + +impl Default for DownlevelProperties { + // Note, this defaults to all on, as that is the default assumption in wgpu. + // gfx-hal's equivalent structure defaults to all off. + fn default() -> Self { + Self { + compute_shaders: true, + shader_model: ShaderModel::Sm5, + storage_images: true, + read_only_depth_stencil: true, + device_local_image_copies: true, + non_power_of_two_mipmapped_textures: true, + anisotropic_filtering: true, + } + } +} + +impl DownlevelProperties { + /// Returns true if the underlying platform offers complete support of the baseline WebGPU standard. + /// + /// If this returns false, some parts of the API will result in validation errors where they would not normally. + /// These parts can be determined by the values in this structure. + pub fn is_webgpu_compliant(self) -> bool { + self == Self::default() + } +} + +/// Collections of shader features a device supports if they support less than WebGPU normally allows. +// TODO: Fill out the differences between shader models more completely +#[derive(Copy, Clone, Debug, PartialEq, Eq, PartialOrd, Ord, Hash)] +pub enum ShaderModel { + /// Extremely limited shaders, including a total instruction limit. + Sm2, + /// Missing minor features and storage images. + Sm4, + /// WebGPU supports shader module 5. + Sm5, +} + /// Supported physical device types. #[repr(u8)] #[derive(Clone, Debug, PartialEq)]