1592: Detailed limits check and error r=cwfitzgerald a=kvark

**Connections**
Related to #1590

**Description**
The old comparison "A < B" was simply wrong, because it would do every field in order and could bail out mid-way without reaching for all limits.

**Testing**
Untested


Co-authored-by: Dzmitry Malyshau <kvarkus@gmail.com>
This commit is contained in:
bors[bot]
2021-07-03 01:12:43 +00:00
committed by GitHub
3 changed files with 53 additions and 16 deletions

View File

@@ -8,7 +8,7 @@ use crate::{
track::{BufferState, TextureSelector, TextureState, TrackerSet, UsageConflict},
validation::{self, check_buffer_usage, check_texture_usage},
FastHashMap, Label, LabelHelpers as _, LifeGuard, MultiRefCount, Stored, SubmissionIndex,
DOWNLEVEL_ERROR_WARNING_MESSAGE,
DOWNLEVEL_ERROR_MESSAGE,
};
use arrayvec::ArrayVec;
@@ -2426,7 +2426,7 @@ pub struct MissingFeatures(pub wgt::Features);
#[derive(Clone, Debug, Error)]
#[error(
"Downlevel flags {0:?} are required but not supported on the device.\n{}",
DOWNLEVEL_ERROR_WARNING_MESSAGE
DOWNLEVEL_ERROR_MESSAGE
)]
pub struct MissingDownlevelFlags(pub wgt::DownlevelFlags);

View File

@@ -14,6 +14,52 @@ pub type RequestAdapterOptions = wgt::RequestAdapterOptions<SurfaceId>;
type HalInstance<A> = <A as hal::Api>::Instance;
type HalSurface<A> = <A as hal::Api>::Surface;
#[derive(Clone, Debug, Error)]
#[error("Limit '{name}' value {requested} is better than allowed {allowed}")]
pub struct FailedLimit {
name: &'static str,
requested: u32,
allowed: u32,
}
fn check_limits(requested: &wgt::Limits, allowed: &wgt::Limits) -> Vec<FailedLimit> {
use std::cmp::Ordering;
let mut failed = Vec::new();
macro_rules! compare {
($name:ident, $ordering:ident) => {
match requested.$name.cmp(&allowed.$name) {
Ordering::$ordering | Ordering::Equal => (),
_ => failed.push(FailedLimit {
name: stringify!($name),
requested: requested.$name,
allowed: allowed.$name,
}),
}
};
}
compare!(max_texture_dimension_1d, Less);
compare!(max_texture_dimension_2d, Less);
compare!(max_texture_dimension_3d, Less);
compare!(max_texture_array_layers, Less);
compare!(max_bind_groups, Less);
compare!(max_dynamic_uniform_buffers_per_pipeline_layout, Less);
compare!(max_dynamic_storage_buffers_per_pipeline_layout, Less);
compare!(max_sampled_textures_per_shader_stage, Less);
compare!(max_samplers_per_shader_stage, Less);
compare!(max_storage_buffers_per_shader_stage, Less);
compare!(max_storage_textures_per_shader_stage, Less);
compare!(max_uniform_buffers_per_shader_stage, Less);
compare!(max_uniform_buffer_binding_size, Less);
compare!(max_storage_buffer_binding_size, Less);
compare!(max_vertex_buffers, Less);
compare!(max_vertex_attributes, Less);
compare!(max_vertex_buffer_array_stride, Less);
compare!(max_push_constant_size, Less);
failed
}
pub struct Instance {
#[allow(dead_code)]
name: String,
@@ -247,8 +293,8 @@ impl<A: HalApi> Adapter<A> {
BIND_BUFFER_ALIGNMENT % caps.alignments.uniform_buffer_offset,
"Adapter uniform buffer offset alignment not compatible with WGPU"
);
if caps.limits < desc.limits {
return Err(RequestDeviceError::LimitsExceeded);
if let Some(failed) = check_limits(&desc.limits, &caps.limits).pop() {
return Err(RequestDeviceError::LimitsExceeded(failed));
}
Device::new(
@@ -295,8 +341,8 @@ pub enum RequestDeviceError {
DeviceLost,
#[error("device initialization failed due to implementation specific errors")]
Internal,
#[error("some of the requested device limits are not supported")]
LimitsExceeded,
#[error(transparent)]
LimitsExceeded(#[from] FailedLimit),
#[error("device has no queue supporting graphics")]
NoGraphicsQueue,
#[error("not enough memory left")]

View File

@@ -81,15 +81,6 @@ impl RefCount {
unsafe { self.0.as_ref() }.load(Ordering::Acquire)
}
/// This works like `std::mem::drop`, except that it returns a boolean which is true if and only
/// if we deallocated the underlying memory, i.e. if this was the last clone of this `RefCount`
/// to be dropped. This is useful for loom testing because it allows us to verify that we
/// deallocated the underlying memory exactly once.
#[cfg(test)]
fn rich_drop_outer(self) -> bool {
unsafe { std::mem::ManuallyDrop::new(self).rich_drop_inner() }
}
/// This function exists to allow `Self::rich_drop_outer` and `Drop::drop` to share the same
/// logic. To use this safely from outside of `Drop::drop`, the calling function must move
/// `Self` into a `ManuallyDrop`.
@@ -191,7 +182,7 @@ support enough features to be a fully compliant implementation of WebGPU. A subs
If you are running this program on native and not in a browser and wish to limit the features you use to the supported subset, \
call Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \
platform supports.";
const DOWNLEVEL_ERROR_WARNING_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \
const DOWNLEVEL_ERROR_MESSAGE: &str = "This is not an invalid use of WebGPU: the underlying API or device does not \
support enough features to be a fully compliant implementation. A subset of the features can still be used. \
If you are running this program on native and not in a browser and wish to work around this issue, call \
Adapter::downlevel_properties or Device::downlevel_properties to get a listing of the features the current \