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}