|
| 1 | +use std::{future::Future, sync::Arc, thread}; |
| 2 | + |
| 3 | +use crate::context::{DeviceRequest, DynContext, ObjectId}; |
| 4 | +use crate::*; |
| 5 | + |
| 6 | +/// Handle to a physical graphics and/or compute device. |
| 7 | +/// |
| 8 | +/// Adapters can be used to open a connection to the corresponding [`Device`] |
| 9 | +/// on the host system by using [`Adapter::request_device`]. |
| 10 | +/// |
| 11 | +/// Does not have to be kept alive. |
| 12 | +/// |
| 13 | +/// Corresponds to [WebGPU `GPUAdapter`](https://gpuweb.github.io/gpuweb/#gpu-adapter). |
| 14 | +#[derive(Debug)] |
| 15 | +pub struct Adapter { |
| 16 | + pub(crate) context: Arc<C>, |
| 17 | + pub(crate) id: ObjectId, |
| 18 | + pub(crate) data: Box<Data>, |
| 19 | +} |
| 20 | +#[cfg(send_sync)] |
| 21 | +static_assertions::assert_impl_all!(Adapter: Send, Sync); |
| 22 | + |
| 23 | +impl Drop for Adapter { |
| 24 | + fn drop(&mut self) { |
| 25 | + if !thread::panicking() { |
| 26 | + self.context.adapter_drop(&self.id, self.data.as_ref()) |
| 27 | + } |
| 28 | + } |
| 29 | +} |
| 30 | + |
| 31 | +pub use wgt::RequestAdapterOptions as RequestAdapterOptionsBase; |
| 32 | +/// Additional information required when requesting an adapter. |
| 33 | +/// |
| 34 | +/// For use with [`Instance::request_adapter`]. |
| 35 | +/// |
| 36 | +/// Corresponds to [WebGPU `GPURequestAdapterOptions`]( |
| 37 | +/// https://gpuweb.github.io/gpuweb/#dictdef-gpurequestadapteroptions). |
| 38 | +pub type RequestAdapterOptions<'a, 'b> = RequestAdapterOptionsBase<&'a Surface<'b>>; |
| 39 | +#[cfg(send_sync)] |
| 40 | +static_assertions::assert_impl_all!(RequestAdapterOptions<'_, '_>: Send, Sync); |
| 41 | + |
| 42 | +impl Adapter { |
| 43 | + /// Returns a globally-unique identifier for this `Adapter`. |
| 44 | + /// |
| 45 | + /// Calling this method multiple times on the same object will always return the same value. |
| 46 | + /// The returned value is guaranteed to be different for all resources created from the same `Instance`. |
| 47 | + pub fn global_id(&self) -> Id<Self> { |
| 48 | + Id::new(self.id) |
| 49 | + } |
| 50 | + |
| 51 | + /// Requests a connection to a physical device, creating a logical device. |
| 52 | + /// |
| 53 | + /// Returns the [`Device`] together with a [`Queue`] that executes command buffers. |
| 54 | + /// |
| 55 | + /// [Per the WebGPU specification], an [`Adapter`] may only be used once to create a device. |
| 56 | + /// If another device is wanted, call [`Instance::request_adapter()`] again to get a fresh |
| 57 | + /// [`Adapter`]. |
| 58 | + /// However, `wgpu` does not currently enforce this restriction. |
| 59 | + /// |
| 60 | + /// # Arguments |
| 61 | + /// |
| 62 | + /// - `desc` - Description of the features and limits requested from the given device. |
| 63 | + /// - `trace_path` - Can be used for API call tracing, if that feature is |
| 64 | + /// enabled in `wgpu-core`. |
| 65 | + /// |
| 66 | + /// # Panics |
| 67 | + /// |
| 68 | + /// - `request_device()` was already called on this `Adapter`. |
| 69 | + /// - Features specified by `desc` are not supported by this adapter. |
| 70 | + /// - Unsafe features were requested but not enabled when requesting the adapter. |
| 71 | + /// - Limits requested exceed the values provided by the adapter. |
| 72 | + /// - Adapter does not support all features wgpu requires to safely operate. |
| 73 | + /// |
| 74 | + /// [Per the WebGPU specification]: https://www.w3.org/TR/webgpu/#dom-gpuadapter-requestdevice |
| 75 | + pub fn request_device( |
| 76 | + &self, |
| 77 | + desc: &DeviceDescriptor<'_>, |
| 78 | + trace_path: Option<&std::path::Path>, |
| 79 | + ) -> impl Future<Output = Result<(Device, Queue), RequestDeviceError>> + WasmNotSend { |
| 80 | + let context = Arc::clone(&self.context); |
| 81 | + let device = DynContext::adapter_request_device( |
| 82 | + &*self.context, |
| 83 | + &self.id, |
| 84 | + self.data.as_ref(), |
| 85 | + desc, |
| 86 | + trace_path, |
| 87 | + ); |
| 88 | + async move { |
| 89 | + device.await.map( |
| 90 | + |DeviceRequest { |
| 91 | + device_id, |
| 92 | + device_data, |
| 93 | + queue_id, |
| 94 | + queue_data, |
| 95 | + }| { |
| 96 | + ( |
| 97 | + Device { |
| 98 | + context: Arc::clone(&context), |
| 99 | + id: device_id, |
| 100 | + data: device_data, |
| 101 | + }, |
| 102 | + Queue { |
| 103 | + context, |
| 104 | + id: queue_id, |
| 105 | + data: queue_data, |
| 106 | + }, |
| 107 | + ) |
| 108 | + }, |
| 109 | + ) |
| 110 | + } |
| 111 | + } |
| 112 | + |
| 113 | + /// Create a wgpu [`Device`] and [`Queue`] from a wgpu-hal `OpenDevice` |
| 114 | + /// |
| 115 | + /// # Safety |
| 116 | + /// |
| 117 | + /// - `hal_device` must be created from this adapter internal handle. |
| 118 | + /// - `desc.features` must be a subset of `hal_device` features. |
| 119 | + #[cfg(wgpu_core)] |
| 120 | + pub unsafe fn create_device_from_hal<A: wgc::hal_api::HalApi>( |
| 121 | + &self, |
| 122 | + hal_device: hal::OpenDevice<A>, |
| 123 | + desc: &DeviceDescriptor<'_>, |
| 124 | + trace_path: Option<&std::path::Path>, |
| 125 | + ) -> Result<(Device, Queue), RequestDeviceError> { |
| 126 | + let context = Arc::clone(&self.context); |
| 127 | + unsafe { |
| 128 | + self.context |
| 129 | + .as_any() |
| 130 | + .downcast_ref::<crate::backend::ContextWgpuCore>() |
| 131 | + // Part of the safety requirements is that the device was generated from the same adapter. |
| 132 | + // Therefore, unwrap is fine here since only WgpuCoreContext based adapters have the ability to create hal devices. |
| 133 | + .unwrap() |
| 134 | + .create_device_from_hal(&self.id.into(), hal_device, desc, trace_path) |
| 135 | + } |
| 136 | + .map(|(device, queue)| { |
| 137 | + ( |
| 138 | + Device { |
| 139 | + context: Arc::clone(&context), |
| 140 | + id: device.id().into(), |
| 141 | + data: Box::new(device), |
| 142 | + }, |
| 143 | + Queue { |
| 144 | + context, |
| 145 | + id: queue.id().into(), |
| 146 | + data: Box::new(queue), |
| 147 | + }, |
| 148 | + ) |
| 149 | + }) |
| 150 | + } |
| 151 | + |
| 152 | + /// Apply a callback to this `Adapter`'s underlying backend adapter. |
| 153 | + /// |
| 154 | + /// If this `Adapter` is implemented by the backend API given by `A` (Vulkan, |
| 155 | + /// Dx12, etc.), then apply `hal_adapter_callback` to `Some(&adapter)`, where |
| 156 | + /// `adapter` is the underlying backend adapter type, [`A::Adapter`]. |
| 157 | + /// |
| 158 | + /// If this `Adapter` uses a different backend, apply `hal_adapter_callback` |
| 159 | + /// to `None`. |
| 160 | + /// |
| 161 | + /// The adapter is locked for reading while `hal_adapter_callback` runs. If |
| 162 | + /// the callback attempts to perform any `wgpu` operations that require |
| 163 | + /// write access to the adapter, deadlock will occur. The locks are |
| 164 | + /// automatically released when the callback returns. |
| 165 | + /// |
| 166 | + /// # Safety |
| 167 | + /// |
| 168 | + /// - The raw handle passed to the callback must not be manually destroyed. |
| 169 | + /// |
| 170 | + /// [`A::Adapter`]: hal::Api::Adapter |
| 171 | + #[cfg(wgpu_core)] |
| 172 | + pub unsafe fn as_hal<A: wgc::hal_api::HalApi, F: FnOnce(Option<&A::Adapter>) -> R, R>( |
| 173 | + &self, |
| 174 | + hal_adapter_callback: F, |
| 175 | + ) -> R { |
| 176 | + if let Some(ctx) = self |
| 177 | + .context |
| 178 | + .as_any() |
| 179 | + .downcast_ref::<crate::backend::ContextWgpuCore>() |
| 180 | + { |
| 181 | + unsafe { ctx.adapter_as_hal::<A, F, R>(self.id.into(), hal_adapter_callback) } |
| 182 | + } else { |
| 183 | + hal_adapter_callback(None) |
| 184 | + } |
| 185 | + } |
| 186 | + |
| 187 | + /// Returns whether this adapter may present to the passed surface. |
| 188 | + pub fn is_surface_supported(&self, surface: &Surface<'_>) -> bool { |
| 189 | + DynContext::adapter_is_surface_supported( |
| 190 | + &*self.context, |
| 191 | + &self.id, |
| 192 | + self.data.as_ref(), |
| 193 | + &surface.id, |
| 194 | + surface.surface_data.as_ref(), |
| 195 | + ) |
| 196 | + } |
| 197 | + |
| 198 | + /// The features which can be used to create devices on this adapter. |
| 199 | + pub fn features(&self) -> Features { |
| 200 | + DynContext::adapter_features(&*self.context, &self.id, self.data.as_ref()) |
| 201 | + } |
| 202 | + |
| 203 | + /// The best limits which can be used to create devices on this adapter. |
| 204 | + pub fn limits(&self) -> Limits { |
| 205 | + DynContext::adapter_limits(&*self.context, &self.id, self.data.as_ref()) |
| 206 | + } |
| 207 | + |
| 208 | + /// Get info about the adapter itself. |
| 209 | + pub fn get_info(&self) -> AdapterInfo { |
| 210 | + DynContext::adapter_get_info(&*self.context, &self.id, self.data.as_ref()) |
| 211 | + } |
| 212 | + |
| 213 | + /// Get info about the adapter itself. |
| 214 | + pub fn get_downlevel_capabilities(&self) -> DownlevelCapabilities { |
| 215 | + DynContext::adapter_downlevel_capabilities(&*self.context, &self.id, self.data.as_ref()) |
| 216 | + } |
| 217 | + |
| 218 | + /// Returns the features supported for a given texture format by this adapter. |
| 219 | + /// |
| 220 | + /// Note that the WebGPU spec further restricts the available usages/features. |
| 221 | + /// To disable these restrictions on a device, request the [`Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES`] feature. |
| 222 | + pub fn get_texture_format_features(&self, format: TextureFormat) -> TextureFormatFeatures { |
| 223 | + DynContext::adapter_get_texture_format_features( |
| 224 | + &*self.context, |
| 225 | + &self.id, |
| 226 | + self.data.as_ref(), |
| 227 | + format, |
| 228 | + ) |
| 229 | + } |
| 230 | + |
| 231 | + /// Generates a timestamp using the clock used by the presentation engine. |
| 232 | + /// |
| 233 | + /// When comparing completely opaque timestamp systems, we need a way of generating timestamps that signal |
| 234 | + /// the exact same time. You can do this by calling your own timestamp function immediately after a call to |
| 235 | + /// this function. This should result in timestamps that are 0.5 to 5 microseconds apart. There are locks |
| 236 | + /// that must be taken during the call, so don't call your function before. |
| 237 | + /// |
| 238 | + /// ```no_run |
| 239 | + /// # let adapter: wgpu::Adapter = panic!(); |
| 240 | + /// # let some_code = || wgpu::PresentationTimestamp::INVALID_TIMESTAMP; |
| 241 | + /// use std::time::{Duration, Instant}; |
| 242 | + /// let presentation = adapter.get_presentation_timestamp(); |
| 243 | + /// let instant = Instant::now(); |
| 244 | + /// |
| 245 | + /// // We can now turn a new presentation timestamp into an Instant. |
| 246 | + /// let some_pres_timestamp = some_code(); |
| 247 | + /// let duration = Duration::from_nanos((some_pres_timestamp.0 - presentation.0) as u64); |
| 248 | + /// let new_instant: Instant = instant + duration; |
| 249 | + /// ``` |
| 250 | + // |
| 251 | + /// [Instant]: std::time::Instant |
| 252 | + pub fn get_presentation_timestamp(&self) -> PresentationTimestamp { |
| 253 | + DynContext::adapter_get_presentation_timestamp(&*self.context, &self.id, self.data.as_ref()) |
| 254 | + } |
| 255 | +} |
0 commit comments