#[test] fn downlevel_default_limits_less_than_default_limits() { let res = check_limits(&wgt::Limits::downlevel_defaults(), &wgt::Limits::default());
assert!(
res.is_empty(), "Downlevel limits are greater than default limits",
)
}
/// List of instances per `wgpu-hal` backend. /// /// The ordering in this list implies prioritization and needs to be preserved.
instance_per_backend: Vec<(Backend, Box<dyn hal::DynInstance>)>,
/// The backends that were requested by the user.
requested_backends: Backends,
/// The backends that we could have attempted to obtain from `wgpu-hal` — /// those for which support is compiled in, currently. /// /// The union of this and `requested_backends` is the set of backends that would be used, /// independent of whether accessing the drivers/hardware for them succeeds. /// To obtain the set of backends actually in use by this instance, check /// `instance_per_backend` instead.
supported_backends: Backends,
pub flags: InstanceFlags,
/// Non-lifetimed [`raw_window_handle::DisplayHandle`], for keepalive and validation purposes in /// [`Self::create_surface()`]. /// /// When used with `winit`, callers are expected to pass its `OwnedDisplayHandle` (created from /// the `EventLoop`) here.
display: Option<Box<dyn wgt::WgpuHasDisplayHandle>>,
}
impl Instance { pubfn new(
name: &str, mut instance_desc: wgt::InstanceDescriptor,
telemetry: Option<hal::Telemetry>,
) -> Self { letmut this = Self {
_name: name.to_owned(),
instance_per_backend: Vec::new(),
requested_backends: instance_desc.backends,
supported_backends: Backends::empty(),
flags: instance_desc.flags, // HACK: We must take ownership of the field here, without being able to pass it into // try_add_hal(). Remove it from the mutable descriptor instead, while try_add_hal() // borrows the handle from `this.display` instead.
display: instance_desc.display.take(),
};
/// Helper for `Instance::new()`; attempts to add a single `wgpu-hal` backend to this instance. fn try_add_hal<A: hal::Api>(
&mutself,
_: A,
instance_desc: &wgt::InstanceDescriptor,
telemetry: Option<hal::Telemetry>,
) { // Whether or not the backend was requested, and whether or not it succeeds, // note that we *could* try it. self.supported_backends |= A::VARIANT.into();
if !instance_desc.backends.contains(A::VARIANT.into()) {
log::trace!("Instance::new: backend {:?} not requested", A::VARIANT); return;
}
// If this was Some, it was moved into self
assert!(instance_desc.display.is_none());
let hal_desc = hal::InstanceDescriptor {
name: "wgpu",
flags: self.flags,
memory_budget_thresholds: instance_desc.memory_budget_thresholds,
backend_options: instance_desc.backend_options.clone(),
telemetry, // Pass a borrow, the core instance here keeps the owned handle alive already // WARNING: Using self here, not instance_desc!
display: self.display.as_ref().map(|hdh| {
hdh.display_handle()
.expect("Implementation did not provide a DisplayHandle")
}),
};
/// # Safety /// /// - The raw instance handle returned must not be manually destroyed. pubunsafefn as_hal<A: hal::Api>(&self) -> Option<&A::Instance> { self.raw(A::VARIANT).map(|instance| {
instance
.as_any()
.downcast_ref() // This should be impossible. It would mean that backend instance and enum type are mismatching.
.expect("Stored instance is not of the correct type")
})
}
/// Creates a new surface targeting the given display/window handles. /// /// Internally attempts to create hal surfaces for all enabled backends. /// /// Fails only if creation for surfaces for all enabled backends fails in which case /// the error for each enabled backend is listed. /// Vice versa, if creation for any backend succeeds, success is returned. /// Surface creation errors are logged to the debug log in any case. /// /// # Safety /// /// - `display_handle` must be a valid object to create a surface upon, /// falls back to the instance display handle otherwise. /// - `window_handle` must remain valid as long as the returned /// [`SurfaceId`] is being used. pubunsafefn create_surface(
&self,
display_handle: Option<raw_window_handle::RawDisplayHandle>,
window_handle: raw_window_handle::RawWindowHandle,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::create_surface");
let instance_display_handle = self.display.as_ref().map(|d| {
d.display_handle()
.expect("Implementation did not provide a DisplayHandle")
.as_raw()
}); let display_handle = match (instance_display_handle, display_handle) {
(Some(a), Some(b)) => { if a != b { return Err(CreateSurfaceError::MismatchingDisplayHandle);
}
a
}
(Some(hnd), None) => hnd,
(None, Some(hnd)) => hnd,
(None, None) => return Err(CreateSurfaceError::MissingDisplayHandle),
};
for (backend, instance) in &self.instance_per_backend { matchunsafe {
instance
.as_ref()
.create_surface(display_handle, window_handle)
} {
Ok(raw) => {
surface_per_backend.insert(*backend, raw);
}
Err(err) => {
log::debug!( "Instance::create_surface: failed to create surface for {backend:?}: {err:?}"
);
errors.insert(*backend, err);
}
}
}
if surface_per_backend.is_empty() {
Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
errors,
))
} else { let surface = Surface {
presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
surface_per_backend,
};
Ok(surface)
}
}
/// Creates a new surface from the given drm configuration. /// /// # Safety /// /// - All parameters must point to valid DRM values. /// /// # Platform Support /// /// This function requires the `"drm"` feature. It is only available on /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works /// with the Vulkan backend. #[cfg(drm)] #[cfg_attr(not(vulkan), expect(unused_variables, unused_mut))] pubunsafefn create_surface_from_drm(
&self,
fd: i32,
plane: u32,
connector_id: u32,
width: u32,
height: u32,
refresh_rate: u32,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::create_surface_from_drm");
#[cfg(vulkan)]
{ let instance = unsafe { self.as_hal::<hal::api::Vulkan>() }
.ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Vulkan))?;
// Safety must be upheld by the caller matchunsafe {
instance.create_surface_from_drm(
fd,
plane,
connector_id,
width,
height,
refresh_rate,
)
} {
Ok(surface) => {
surface_per_backend.insert(Backend::Vulkan, Box::new(surface));
}
Err(err) => {
errors.insert(Backend::Vulkan, err);
}
}
}
if surface_per_backend.is_empty() {
Err(CreateSurfaceError::FailedToCreateSurfaceForAnyBackend(
errors,
))
} else { let surface = Surface {
presentation: Mutex::new(rank::SURFACE_PRESENTATION, None),
surface_per_backend,
};
Ok(surface)
}
}
/// # Safety /// /// `layer` must be a valid pointer. #[cfg(metal)] pubunsafefn create_surface_metal(
&self,
layer: *mut core::ffi::c_void,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::create_surface_metal");
let instance = unsafe { self.as_hal::<hal::api::Metal>() }
.ok_or(CreateSurfaceError::BackendNotEnabled(Backend::Metal))?;
let layer = layer.cast(); // SAFETY: We do this cast and deref. (rather than using `metal` to get the // object we want) to avoid direct coupling on the `metal` crate. // // To wit, this pointer… // // - …is properly aligned. // - …is dereferenceable to a `MetalLayerRef` as an invariant of the `metal` // field. // - …points to an _initialized_ `MetalLayerRef`. // - …is only ever aliased via an immutable reference that lives within this // lexical scope. let layer = unsafe { &*layer }; let raw_surface: Box<dyn hal::DynSurface> = Box::new(instance.create_surface_from_layer(layer));
#[cfg(dx12)] /// # Safety /// /// The visual must be valid and able to be used to make a swapchain with. pubunsafefn create_surface_from_visual(
&self,
visual: *mut core::ffi::c_void,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_visual"); self.create_surface_dx12(|inst| unsafe { inst.create_surface_from_visual(visual) })
}
#[cfg(dx12)] /// # Safety /// /// The surface_handle must be valid and able to be used to make a swapchain with. pubunsafefn create_surface_from_surface_handle(
&self,
surface_handle: *mut core::ffi::c_void,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_surface_handle"); self.create_surface_dx12(|inst| unsafe {
inst.create_surface_from_surface_handle(surface_handle)
})
}
#[cfg(dx12)] /// # Safety /// /// The swap_chain_panel must be valid and able to be used to make a swapchain with. pubunsafefn create_surface_from_swap_chain_panel(
&self,
swap_chain_panel: *mut core::ffi::c_void,
) -> Result<Surface, CreateSurfaceError> {
profiling::scope!("Instance::instance_create_surface_from_swap_chain_panel"); self.create_surface_dx12(|inst| unsafe {
inst.create_surface_from_swap_chain_panel(swap_chain_panel)
})
}
letmut adapters = Vec::new(); for (_backend, instance) inself
.instance_per_backend
.iter()
.filter(|(backend, _)| backends.contains(Backends::from(*backend)))
{ // NOTE: We might be using `profiling` without any features. The empty backend of this // macro emits no code, so unused code linting changes depending on the backend.
profiling::scope!("enumerating", &*alloc::format!("{_backend:?}"));
let hal_adapters = unsafe { instance.enumerate_adapters(None) };
for &(backend, ref instance) inself
.instance_per_backend
.iter()
.filter(|&&(backend, _)| backends.contains(Backends::from(backend)))
{ let compatible_hal_surface = desc
.compatible_surface
.and_then(|surface| surface.raw(backend));
letmut backend_adapters = unsafe { instance.enumerate_adapters(compatible_hal_surface) }; if backend_adapters.is_empty() {
log::debug!("enabled backend `{backend:?}` has no adapters");
no_adapter_backends |= Backends::from(backend); // by continuing, we avoid setting the further error bits below continue;
}
if desc.force_fallback_adapter {
log::debug!("Filtering `{backend:?}` for `force_fallback_adapter`");
backend_adapters.retain(|exposed| { let keep = exposed.info.device_type == wgt::DeviceType::Cpu; if !keep {
log::debug!("* Eliminating adapter `{}`", exposed.info.name);
}
keep
}); if backend_adapters.is_empty() {
log::debug!("* Backend `{backend:?}` has no fallback adapters");
no_fallback_backends |= Backends::from(backend); continue;
}
}
fn get_order(device_type: wgt::DeviceType, prefer_integrated_gpu: bool) -> u8 { // Since devices of type "Other" might really be "Unknown" and come // from APIs like OpenGL that don't specify device type, Prefer more // Specific types over Other. // // This means that backends which do provide accurate device types // will be preferred if their device type indicates an actual // hardware GPU (integrated or discrete). match device_type {
wgt::DeviceType::DiscreteGpu if prefer_integrated_gpu => 2,
wgt::DeviceType::IntegratedGpu if prefer_integrated_gpu => 1,
wgt::DeviceType::DiscreteGpu => 1,
wgt::DeviceType::IntegratedGpu => 2,
wgt::DeviceType::Other => 3,
wgt::DeviceType::VirtualGpu => 4,
wgt::DeviceType::Cpu => 5,
}
}
// `request_adapter` can be a bit of a black box. // Shine some light on its decision in debug log. if adapters.is_empty() {
log::debug!("Request adapter didn't find compatible adapters.");
} else {
log::debug!( "Found {} compatible adapters. Sorted by preference:",
adapters.len()
); for adapter in &adapters {
log::debug!("* {:?}", adapter.info);
}
}
/// This is similar to wgpu-hal's `adjust_raw_limits` but tailored to /// wgpu-core's constraints. fn adjust_limits_for_indirect_validation(&self, limits: &style='color:red'>mut wgt::Limits) { // Indirect draw validation can't support u64 offsets, // lower max buffer and binding size to fit in an u32. ifself.flags.contains(InstanceFlags::VALIDATION_INDIRECT_CALL) {
limits.max_buffer_size = limits.max_buffer_size.min(u32::MAX as u64);
limits.max_uniform_buffer_binding_size =
limits.max_uniform_buffer_binding_size.min(u32::MAX as u64);
limits.max_storage_buffer_binding_size =
limits.max_storage_buffer_binding_size.min(u32::MAX as u64);
}
}
/// Returns the backend this adapter is using. pubfn backend(&self) -> Backend { self.raw.backend()
}
pubfn is_surface_supported(&self, surface: &Surface) -> bool { // If get_capabilities returns Err, then the API does not advertise support for the surface. // // This could occur if the user is running their app on Wayland but Vulkan does not support // VK_KHR_wayland_surface.
surface.get_capabilities(self).is_ok()
}
pubfn create_device_and_queue( self: &Arc<Self>,
desc: &DeviceDescriptor,
instance_flags: InstanceFlags,
) -> Result<(Arc<Device>, Arc<Queue>), RequestDeviceError> { // Verify all features were exposed by the adapter if !self.raw.features.contains(desc.required_features) { return Err(RequestDeviceError::UnsupportedFeature(
desc.required_features - self.raw.features,
));
}
// 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()
{ let missing_flags = wgt::DownlevelFlags::compliant() - caps.downlevel.flags;
log::warn!("Missing downlevel flags: {missing_flags:?}\n{DOWNLEVEL_WARNING_MESSAGE}");
log::warn!("{:#?}", caps.downlevel);
}
// Verify feature preconditions if desc
.required_features
.contains(wgt::Features::MAPPABLE_PRIMARY_BUFFERS)
&& self.raw.info.device_type == wgt::DeviceType::DiscreteGpu
{
log::warn!( "Feature MAPPABLE_PRIMARY_BUFFERS enabled on a discrete gpu. \
This is a massive performance footgun and likely not what you wanted"
);
}
#[derive(Clone, Debug, Error)] #[non_exhaustive] pubenum GetSurfaceSupportError { #[error("Surface is not supported for the specified backend {0}")]
NotSupportedByBackend(Backend), #[error("Failed to retrieve surface capabilities for the specified adapter.")]
FailedToRetrieveSurfaceCapabilitiesForAdapter,
}
#[derive(Clone, Debug, Error)] /// Error when requesting a device from the adapter #[non_exhaustive] pubenum RequestDeviceError { #[error(transparent)]
Device(#[from] DeviceError), #[error(transparent)]
LimitsExceeded(#[from] FailedLimit), #[error("Failed to initialize Timestamp Normalizer")]
TimestampNormalizerInitFailed(#[from] TimestampNormalizerInitError), #[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)] #[non_exhaustive] pubenum CreateSurfaceError { #[error("The backend {0} was not enabled on the instance.")]
BackendNotEnabled(Backend), #[error("Failed to create surface for any enabled backend: {0:?}")]
FailedToCreateSurfaceForAnyBackend(HashMap<Backend, hal::InstanceError>), #[error("The display handle used to create this Instance does not match the one used to create a surface on it")]
MismatchingDisplayHandle, #[error( "No `DisplayHandle` is available to create this surface with. When creating a surface with `create_surface()` \
you must specify a display handle in `InstanceDescriptor::display`. \
Rarely, if you need to create surfaces from different `DisplayHandle`s (ex. different Wayland or X11 connections), \
you must use `create_surface_unsafe()`."
)]
MissingDisplayHandle,
}
impl Global { /// Creates a new surface targeting the given display/window handles. /// /// Internally attempts to create hal surfaces for all enabled backends. /// /// Fails only if creation for surfaces for all enabled backends fails in which case /// the error for each enabled backend is listed. /// Vice versa, if creation for any backend succeeds, success is returned. /// Surface creation errors are logged to the debug log in any case. /// /// id_in: /// - If `Some`, the id to assign to the surface. A new one will be generated otherwise. /// /// # Safety /// /// - `display_handle` must be a valid object to create a surface upon, /// falls back to the instance display handle otherwise. /// - `window_handle` must remain valid as long as the returned /// [`SurfaceId`] is being used. pubunsafefn instance_create_surface(
&self,
display_handle: Option<raw_window_handle::RawDisplayHandle>,
window_handle: raw_window_handle::RawWindowHandle,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance.create_surface(display_handle, window_handle) }?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
/// Creates a new surface from the given drm configuration. /// /// # Safety /// /// - All parameters must point to valid DRM values. /// /// # Platform Support /// /// This function requires the `"drm"` feature, and is only available on /// non-apple Unix-like platforms (Linux, FreeBSD) and currently only works /// with the Vulkan backend. #[cfg(drm)] pubunsafefn instance_create_surface_from_drm(
&self,
fd: i32,
plane: u32,
connector_id: u32,
width: u32,
height: u32,
refresh_rate: u32,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance.create_surface_from_drm(
fd,
plane,
connector_id,
width,
height,
refresh_rate,
)
}?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
/// # Safety /// /// `layer` must be a valid pointer. #[cfg(metal)] pubunsafefn instance_create_surface_metal(
&self,
layer: *mut core::ffi::c_void,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance.create_surface_metal(layer) }?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
#[cfg(dx12)] /// # Safety /// /// The visual must be valid and able to be used to make a swapchain with. pubunsafefn instance_create_surface_from_visual(
&self,
visual: *mut core::ffi::c_void,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance.create_surface_from_visual(visual) }?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
#[cfg(dx12)] /// # Safety /// /// The surface_handle must be valid and able to be used to make a swapchain with. pubunsafefn instance_create_surface_from_surface_handle(
&self,
surface_handle: *mut core::ffi::c_void,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance
.create_surface_from_surface_handle(surface_handle)
}?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
#[cfg(dx12)] /// # Safety /// /// The swap_chain_panel must be valid and able to be used to make a swapchain with. pubunsafefn instance_create_surface_from_swap_chain_panel(
&self,
swap_chain_panel: *mut core::ffi::c_void,
id_in: Option<SurfaceId>,
) -> Result<SurfaceId, CreateSurfaceError> { let surface = unsafe { self.instance
.create_surface_from_swap_chain_panel(swap_chain_panel)
}?; let id = self.surfaces.prepare(id_in).assign(Arc::new(surface));
Ok(id)
}
pubfn request_adapter(
&self,
desc: &RequestAdapterOptions,
backends: Backends,
id_in: Option<AdapterId>,
) -> Result<AdapterId, wgt::RequestAdapterError> { let compatible_surface = desc.compatible_surface.map(|id| self.surfaces.get(id)); let desc = wgt::RequestAdapterOptions {
power_preference: desc.power_preference,
force_fallback_adapter: desc.force_fallback_adapter,
compatible_surface: compatible_surface.as_deref(),
apply_limit_buckets: desc.apply_limit_buckets,
}; let adapter = self.instance.request_adapter(&desc, backends)?; let id = self.hub.adapters.prepare(id_in).assign(Arc::new(adapter));
Ok(id)
}
/// Create an adapter from a HAL adapter. /// /// The HAL adapter may be obtained e.g. by calling `enumerate_adapters` on /// the HAL directly. /// /// If [limit bucketing][lt] is desired, [`crate::limits::apply_limit_buckets`] /// should be called with the HAL adapter before calling this function. /// /// # Safety /// /// `hal_adapter` must be created from this global internal instance handle. /// /// [lt]: crate::limits#Limit-bucketing pubunsafefn create_adapter_from_hal(
&self,
hal_adapter: hal::DynExposedAdapter,
input: Option<AdapterId>,
) -> AdapterId {
profiling::scope!("Instance::create_adapter_from_hal");
let fid = self.hub.adapters.prepare(input); let id = fid.assign(Arc::new(Adapter::new(hal_adapter)));
let device_fid = self.hub.devices.prepare(device_id_in); let queue_fid = self.hub.queues.prepare(queue_id_in);
let adapter = self.hub.adapters.get(adapter_id); let (device, queue) = adapter.create_device_and_queue(desc, self.instance.flags)?;
let device_id = device_fid.assign(device);
resource_log!("Created Device {:?}", device_id);
let queue_id = queue_fid.assign(queue);
resource_log!("Created Queue {:?}", queue_id);
Ok((device_id, queue_id))
}
/// # Safety /// /// - `hal_device` must be created from `adapter_id` or its internal handle. /// - `desc` must be a subset of `hal_device` features and limits. pubunsafefn create_device_from_hal(
&self,
adapter_id: AdapterId,
hal_device: hal::DynOpenDevice,
desc: &DeviceDescriptor,
device_id_in: Option<DeviceId>,
queue_id_in: Option<QueueId>,
) -> Result<(DeviceId, QueueId), RequestDeviceError> {
profiling::scope!("Global::create_device_from_hal");
let devices_fid = self.hub.devices.prepare(device_id_in); let queues_fid = self.hub.queues.prepare(queue_id_in);
let adapter = self.hub.adapters.get(adapter_id); let (device, queue) =
adapter.create_device_and_queue_from_hal(hal_device, desc, self.instance.flags)?;
let device_id = devices_fid.assign(device);
resource_log!("Created Device {:?}", device_id);
let queue_id = queues_fid.assign(queue);
resource_log!("Created Queue {:?}", queue_id);
Ok((device_id, queue_id))
}
}
/// This function checks that the adapter obeys WebGPU's adapter capability /// guarantees. Most of the limits are adjusted in wgpu-hal's /// `adjust_raw_limits` fn. So we only check the remaining properties here. /// See <https://gpuweb.github.io/gpuweb/#adapter-capability-guarantees>. fn adapter_allowed(
flags: InstanceFlags,
info: &impl fmt::Debug,
limits: &wgt::Limits,
downlevel: &wgt::DownlevelCapabilities,
) -> bool { // Check "All alignment-class limits must be powers of 2." // // Even if the application has not requested strict WebGPU compliance, // non-power-of-two alignment limits are nonsensical, so don't attempt // to use such a device. let min_uniform_buffer_offset_alignment = limits.min_uniform_buffer_offset_alignment; if !min_uniform_buffer_offset_alignment.is_power_of_two() {
log::error!( "Adapter {:?} min_uniform_buffer_offset_alignment limit is not a power of 2: {:?}",
info,
min_uniform_buffer_offset_alignment
); returnfalse;
} let min_storage_buffer_offset_alignment = limits.min_storage_buffer_offset_alignment; if !min_storage_buffer_offset_alignment.is_power_of_two() {
log::error!( "Adapter {:?} min_storage_buffer_offset_alignment limit is not a power of 2: {:?}",
info,
min_storage_buffer_offset_alignment
); returnfalse;
}
// Following checks are only enabled if `STRICT_WEBGPU_COMPLIANCE` is set. if !flags.contains(InstanceFlags::STRICT_WEBGPU_COMPLIANCE) { returntrue;
}
// Check "All supported limits must be either the default value or better." let failed_limits = check_limits(&wgt::Limits::defaults(), limits); if !failed_limits.is_empty() {
log::debug!( "Adapter {:?} is not WebGPU compliant due to limits: {:?}",
info,
failed_limits
); returnfalse;
}
if !downlevel.is_webgpu_compliant() { let missing_flags = wgt::DownlevelFlags::compliant() - downlevel.flags;
log::debug!( "Adapter {:?} is not WebGPU compliant due to missing downlevel flags: {:?}",
info,
missing_flags
); returnfalse;
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.