wgpu_types/
adapter.rs

1use alloc::string::String;
2use core::{fmt, mem};
3
4use crate::{link_to_wgpu_docs, Backend, Backends};
5
6#[cfg(any(feature = "serde", test))]
7use serde::{Deserialize, Serialize};
8
9#[cfg(doc)]
10use crate::{Features, TextureUsages};
11
12/// Options for requesting adapter.
13///
14/// Corresponds to [WebGPU `GPURequestAdapterOptions`](
15/// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions).
16#[repr(C)]
17#[derive(Clone, Debug, PartialEq, Eq, Hash)]
18#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
19pub struct RequestAdapterOptions<S> {
20    /// Power preference for the adapter.
21    pub power_preference: PowerPreference,
22    /// Indicates that only a fallback adapter can be returned. This is generally a "software"
23    /// implementation on the system.
24    pub force_fallback_adapter: bool,
25    /// Surface that is required to be presentable with the requested adapter. This does not
26    /// create the surface, only guarantees that the adapter can present to said surface.
27    /// For WebGL, this is strictly required, as an adapter can not be created without a surface.
28    pub compatible_surface: Option<S>,
29}
30
31impl<S> Default for RequestAdapterOptions<S> {
32    fn default() -> Self {
33        Self {
34            power_preference: PowerPreference::default(),
35            force_fallback_adapter: false,
36            compatible_surface: None,
37        }
38    }
39}
40
41/// Power Preference when choosing a physical adapter.
42///
43/// Corresponds to [WebGPU `GPUPowerPreference`](
44/// https://gpuweb.github.io/gpuweb/#enumdef-gpupowerpreference).
45#[repr(C)]
46#[derive(Copy, Clone, Debug, PartialEq, Eq, Hash, Default)]
47#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
48#[cfg_attr(feature = "serde", serde(rename_all = "kebab-case"))]
49pub enum PowerPreference {
50    #[default]
51    /// Power usage is not considered when choosing an adapter.
52    None = 0,
53    /// Adapter that uses the least possible power. This is often an integrated GPU.
54    LowPower = 1,
55    /// Adapter that has the highest performance. This is often a discrete GPU.
56    HighPerformance = 2,
57}
58
59impl PowerPreference {
60    /// Get a power preference from the environment variable `WGPU_POWER_PREF`.
61    pub fn from_env() -> Option<Self> {
62        let env = crate::env::var("WGPU_POWER_PREF")?;
63        match env.to_lowercase().as_str() {
64            "low" => Some(Self::LowPower),
65            "high" => Some(Self::HighPerformance),
66            "none" => Some(Self::None),
67            _ => None,
68        }
69    }
70}
71
72/// Supported physical device types.
73#[repr(u8)]
74#[derive(Clone, Copy, Debug, Eq, PartialEq, Hash)]
75#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
76pub enum DeviceType {
77    /// Other or Unknown.
78    Other,
79    /// Integrated GPU with shared CPU/GPU memory.
80    IntegratedGpu,
81    /// Discrete GPU with separate CPU/GPU memory.
82    DiscreteGpu,
83    /// Virtual / Hosted.
84    VirtualGpu,
85    /// Cpu / Software Rendering.
86    Cpu,
87}
88
89//TODO: convert `vendor` and `device` to `u32`
90
91/// Information about an adapter.
92#[derive(Clone, Debug, Eq, PartialEq, Hash)]
93#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
94pub struct AdapterInfo {
95    /// Adapter name
96    pub name: String,
97    /// [`Backend`]-specific vendor ID of the adapter
98    ///
99    /// This generally is a 16-bit PCI vendor ID in the least significant bytes of this field.
100    /// However, more significant bytes may be non-zero if the backend uses a different
101    /// representation.
102    ///
103    /// * For [`Backend::Vulkan`], the [`VkPhysicalDeviceProperties::vendorID`] is used, which is
104    ///   a superset of PCI IDs.
105    ///
106    /// [`VkPhysicalDeviceProperties::vendorID`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html
107    pub vendor: u32,
108    /// [`Backend`]-specific device ID of the adapter
109    ///
110    ///
111    /// This generally is a 16-bit PCI device ID in the least significant bytes of this field.
112    /// However, more significant bytes may be non-zero if the backend uses a different
113    /// representation.
114    ///
115    /// * For [`Backend::Vulkan`], the [`VkPhysicalDeviceProperties::deviceID`] is used, which is
116    ///   a superset of PCI IDs.
117    ///
118    /// [`VkPhysicalDeviceProperties::deviceID`]: https://registry.khronos.org/vulkan/specs/1.3-extensions/man/html/VkPhysicalDeviceProperties.html
119    pub device: u32,
120    /// Type of device
121    pub device_type: DeviceType,
122    /// [`Backend`]-specific PCI bus ID of the adapter.
123    ///
124    /// * For [`Backend::Vulkan`], [`VkPhysicalDevicePCIBusInfoPropertiesEXT`] is used,
125    ///   if available, in the form `bus:device.function`, e.g. `0000:01:00.0`.
126    ///
127    /// [`VkPhysicalDevicePCIBusInfoPropertiesEXT`]: https://registry.khronos.org/vulkan/specs/latest/man/html/VkPhysicalDevicePCIBusInfoPropertiesEXT.html
128    pub device_pci_bus_id: String,
129    /// Driver name
130    pub driver: String,
131    /// Driver info
132    pub driver_info: String,
133    /// Backend used for device
134    pub backend: Backend,
135    /// Minimum possible size of a subgroup on this adapter. Will
136    /// never be lower than [`crate::MINIMUM_SUBGROUP_MIN_SIZE`].
137    ///
138    /// This will vary from device to device. Typical values are listed below.
139    ///
140    /// - NVIDIA: 32
141    /// - AMD GCN/Vega: 64
142    /// - AMD RDNA+: 32
143    /// - Intel: 8 or 16
144    /// - Qualcomm: 64
145    /// - WARP: 4
146    /// - lavapipe: 8
147    pub subgroup_min_size: u32,
148    /// Maximum possible size of a subgroup on this adapter. Will
149    /// never be higher than [`crate::MAXIMUM_SUBGROUP_MAX_SIZE`].
150    ///
151    /// This will vary from device to device. Typical values are listed below:
152    ///
153    /// - NVIDIA: 32
154    /// - AMD GCN/Vega: 64
155    /// - AMD RDNA+: 64
156    /// - Intel: 16 or 32
157    /// - Qualcomm: 128
158    /// - WARP: 4 or 128
159    /// - lavapipe: 8
160    pub subgroup_max_size: u32,
161    /// If true, adding [`TextureUsages::TRANSIENT`] to a texture will decrease memory usage.
162    pub transient_saves_memory: bool,
163}
164
165/// Error when [`Instance::request_adapter()`] fails.
166///
167/// This type is not part of the WebGPU standard, where `requestAdapter()` would simply return null.
168///
169#[doc = link_to_wgpu_docs!(["`Instance::request_adapter()`"]: "struct.Instance.html#method.request_adapter")]
170#[derive(Clone, Debug, PartialEq)]
171#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
172#[non_exhaustive]
173pub enum RequestAdapterError {
174    /// No adapter available via the instance’s backends matched the request’s adapter criteria.
175    NotFound {
176        // These fields must be set by wgpu-core and wgpu, but are not intended to be stable API,
177        // only data for the production of the error message.
178        #[doc(hidden)]
179        active_backends: Backends,
180        #[doc(hidden)]
181        requested_backends: Backends,
182        #[doc(hidden)]
183        supported_backends: Backends,
184        #[doc(hidden)]
185        no_fallback_backends: Backends,
186        #[doc(hidden)]
187        no_adapter_backends: Backends,
188        #[doc(hidden)]
189        incompatible_surface_backends: Backends,
190    },
191
192    /// Attempted to obtain adapter specified by environment variable, but the environment variable
193    /// was not set.
194    EnvNotSet,
195}
196
197impl core::error::Error for RequestAdapterError {}
198impl fmt::Display for RequestAdapterError {
199    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
200        match self {
201            RequestAdapterError::NotFound {
202                active_backends,
203                requested_backends,
204                supported_backends,
205                no_fallback_backends,
206                no_adapter_backends,
207                incompatible_surface_backends,
208            } => {
209                write!(f, "No suitable graphics adapter found; ")?;
210                let mut first = true;
211                for backend in Backend::ALL {
212                    let bit = Backends::from(backend);
213                    let comma = if mem::take(&mut first) { "" } else { ", " };
214                    let explanation = if !requested_backends.contains(bit) {
215                        // We prefer reporting this, because it makes the error most stable with
216                        // respect to what is directly controllable by the caller, as opposed to
217                        // compilation options or the run-time environment.
218                        "not requested"
219                    } else if !supported_backends.contains(bit) {
220                        "support not compiled in"
221                    } else if no_adapter_backends.contains(bit) {
222                        "found no adapters"
223                    } else if incompatible_surface_backends.contains(bit) {
224                        "not compatible with provided surface"
225                    } else if no_fallback_backends.contains(bit) {
226                        "had no fallback adapters"
227                    } else if !active_backends.contains(bit) {
228                        // Backend requested but not active in this instance
229                        if backend == Backend::Noop {
230                            "not explicitly enabled"
231                        } else {
232                            "drivers/libraries could not be loaded"
233                        }
234                    } else {
235                        // This path should be unreachable, but don't crash.
236                        "[unknown reason]"
237                    };
238                    write!(f, "{comma}{backend} {explanation}")?;
239                }
240            }
241            RequestAdapterError::EnvNotSet => f.write_str("WGPU_ADAPTER_NAME not set")?,
242        }
243        Ok(())
244    }
245}