mirror of
https://github.com/gfx-rs/wgpu.git
synced 2025-12-08 21:26:17 +00:00
[deno] Fix some problems in the handling of device limits (#8085)
* Correct error type when a requested limit is too high * Clamp requested limits to the WebGPU defaults Fixes #8084
This commit is contained in:
parent
fe86710af7
commit
e7a99be058
@ -98,6 +98,7 @@ This allows using precompiled shaders without manually checking which backend's
|
|||||||
- The function you pass to `Device::on_uncaptured_error()` must now implement `Sync` in addition to `Send`, and be wrapped in `Arc` instead of `Box`.
|
- The function you pass to `Device::on_uncaptured_error()` must now implement `Sync` in addition to `Send`, and be wrapped in `Arc` instead of `Box`.
|
||||||
In exchange for this, it is no longer possible for calling `wgpu` functions while in that callback to cause a deadlock (not that we encourage you to actually do that).
|
In exchange for this, it is no longer possible for calling `wgpu` functions while in that callback to cause a deadlock (not that we encourage you to actually do that).
|
||||||
By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011).
|
By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011).
|
||||||
|
- The limits requested for a device must now satisfy `min_subgroup_size <= max_subgroup_size`. By @andyleiserson in [#8085](https://github.com/gfx-rs/wgpu/pull/8085).
|
||||||
|
|
||||||
#### Naga
|
#### Naga
|
||||||
|
|
||||||
|
|||||||
@ -19,6 +19,7 @@ webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_w
|
|||||||
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:*
|
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:*
|
||||||
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,multiple_color_attachments:*
|
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,multiple_color_attachments:*
|
||||||
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:*
|
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:*
|
||||||
|
fails-if(dx12) webgpu:api,validation,capability_checks,limits,maxBindGroups:setBindGroup,*
|
||||||
webgpu:api,validation,createBindGroup:buffer,effective_buffer_binding_size:*
|
webgpu:api,validation,createBindGroup:buffer,effective_buffer_binding_size:*
|
||||||
webgpu:api,validation,encoding,beginComputePass:*
|
webgpu:api,validation,encoding,beginComputePass:*
|
||||||
webgpu:api,validation,encoding,beginRenderPass:*
|
webgpu:api,validation,encoding,beginRenderPass:*
|
||||||
|
|||||||
@ -125,8 +125,16 @@ impl GPUAdapter {
|
|||||||
return Err(CreateDeviceError::RequiredFeaturesNotASubset);
|
return Err(CreateDeviceError::RequiredFeaturesNotASubset);
|
||||||
}
|
}
|
||||||
|
|
||||||
let required_limits =
|
// When support for compatibility mode is added, this will need to look
|
||||||
serde_json::from_value(serde_json::to_value(descriptor.required_limits)?)?;
|
// at whether the adapter is "compatibility-defaulting" or
|
||||||
|
// "core-defaulting", and choose the appropriate set of defaults.
|
||||||
|
//
|
||||||
|
// Support for compatibility mode is tracked in
|
||||||
|
// https://github.com/gfx-rs/wgpu/issues/8124.
|
||||||
|
let required_limits = serde_json::from_value::<wgpu_types::Limits>(serde_json::to_value(
|
||||||
|
descriptor.required_limits,
|
||||||
|
)?)?
|
||||||
|
.or_better_values_from(&wgpu_types::Limits::default());
|
||||||
|
|
||||||
let trace = std::env::var_os("DENO_WEBGPU_TRACE")
|
let trace = std::env::var_os("DENO_WEBGPU_TRACE")
|
||||||
.map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path)))
|
.map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path)))
|
||||||
@ -196,7 +204,7 @@ pub enum CreateDeviceError {
|
|||||||
#[class(inherit)]
|
#[class(inherit)]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Serde(#[from] serde_json::Error),
|
Serde(#[from] serde_json::Error),
|
||||||
#[class(type)]
|
#[class("DOMExceptionOperationError")]
|
||||||
#[error(transparent)]
|
#[error(transparent)]
|
||||||
Device(#[from] wgpu_core::instance::RequestDeviceError),
|
Device(#[from] wgpu_core::instance::RequestDeviceError),
|
||||||
}
|
}
|
||||||
|
|||||||
@ -16,6 +16,7 @@ extern crate alloc;
|
|||||||
|
|
||||||
use alloc::borrow::Cow;
|
use alloc::borrow::Cow;
|
||||||
use alloc::{string::String, vec, vec::Vec};
|
use alloc::{string::String, vec, vec::Vec};
|
||||||
|
use core::cmp::Ordering;
|
||||||
use core::{
|
use core::{
|
||||||
fmt,
|
fmt,
|
||||||
hash::{Hash, Hasher},
|
hash::{Hash, Hasher},
|
||||||
@ -459,6 +460,71 @@ impl fmt::Display for RequestAdapterError {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// Invoke a macro for each of the limits.
|
||||||
|
///
|
||||||
|
/// The supplied macro should take two arguments. The first is a limit name, as
|
||||||
|
/// an identifier, typically used to access a member of `struct Limits`. The
|
||||||
|
/// second is `Ordering::Less` if valid values are less than the limit (the
|
||||||
|
/// common case), or `Ordering::Greater` if valid values are more than the limit
|
||||||
|
/// (for limits like alignments, which are minima instead of maxima).
|
||||||
|
macro_rules! with_limits {
|
||||||
|
($macro_name:ident) => {
|
||||||
|
$macro_name!(max_texture_dimension_1d, Ordering::Less);
|
||||||
|
$macro_name!(max_texture_dimension_1d, Ordering::Less);
|
||||||
|
$macro_name!(max_texture_dimension_2d, Ordering::Less);
|
||||||
|
$macro_name!(max_texture_dimension_3d, Ordering::Less);
|
||||||
|
$macro_name!(max_texture_array_layers, Ordering::Less);
|
||||||
|
$macro_name!(max_bind_groups, Ordering::Less);
|
||||||
|
$macro_name!(max_bindings_per_bind_group, Ordering::Less);
|
||||||
|
$macro_name!(
|
||||||
|
max_dynamic_uniform_buffers_per_pipeline_layout,
|
||||||
|
Ordering::Less
|
||||||
|
);
|
||||||
|
$macro_name!(
|
||||||
|
max_dynamic_storage_buffers_per_pipeline_layout,
|
||||||
|
Ordering::Less
|
||||||
|
);
|
||||||
|
$macro_name!(max_sampled_textures_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_samplers_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_storage_buffers_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_storage_textures_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_uniform_buffers_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_binding_array_elements_per_shader_stage, Ordering::Less);
|
||||||
|
$macro_name!(max_uniform_buffer_binding_size, Ordering::Less);
|
||||||
|
$macro_name!(max_storage_buffer_binding_size, Ordering::Less);
|
||||||
|
$macro_name!(max_vertex_buffers, Ordering::Less);
|
||||||
|
$macro_name!(max_buffer_size, Ordering::Less);
|
||||||
|
$macro_name!(max_vertex_attributes, Ordering::Less);
|
||||||
|
$macro_name!(max_vertex_buffer_array_stride, Ordering::Less);
|
||||||
|
$macro_name!(min_uniform_buffer_offset_alignment, Ordering::Greater);
|
||||||
|
$macro_name!(min_storage_buffer_offset_alignment, Ordering::Greater);
|
||||||
|
$macro_name!(max_inter_stage_shader_components, Ordering::Less);
|
||||||
|
$macro_name!(max_color_attachments, Ordering::Less);
|
||||||
|
$macro_name!(max_color_attachment_bytes_per_sample, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_workgroup_storage_size, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_invocations_per_workgroup, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_workgroup_size_x, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_workgroup_size_y, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_workgroup_size_z, Ordering::Less);
|
||||||
|
$macro_name!(max_compute_workgroups_per_dimension, Ordering::Less);
|
||||||
|
|
||||||
|
$macro_name!(min_subgroup_size, Ordering::Greater);
|
||||||
|
$macro_name!(max_subgroup_size, Ordering::Less);
|
||||||
|
|
||||||
|
$macro_name!(max_push_constant_size, Ordering::Less);
|
||||||
|
$macro_name!(max_non_sampler_bindings, Ordering::Less);
|
||||||
|
|
||||||
|
$macro_name!(max_task_workgroup_total_count, Ordering::Less);
|
||||||
|
$macro_name!(max_task_workgroups_per_dimension, Ordering::Less);
|
||||||
|
$macro_name!(max_mesh_multiview_count, Ordering::Less);
|
||||||
|
$macro_name!(max_mesh_output_layers, Ordering::Less);
|
||||||
|
|
||||||
|
$macro_name!(max_blas_primitive_count, Ordering::Less);
|
||||||
|
$macro_name!(max_blas_geometry_count, Ordering::Less);
|
||||||
|
$macro_name!(max_tlas_instance_count, Ordering::Less);
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Represents the sets of limits an adapter/device supports.
|
/// Represents the sets of limits an adapter/device supports.
|
||||||
///
|
///
|
||||||
/// We provide three different defaults.
|
/// We provide three different defaults.
|
||||||
@ -1015,68 +1081,61 @@ impl Limits {
|
|||||||
fatal: bool,
|
fatal: bool,
|
||||||
mut fail_fn: impl FnMut(&'static str, u64, u64),
|
mut fail_fn: impl FnMut(&'static str, u64, u64),
|
||||||
) {
|
) {
|
||||||
use core::cmp::Ordering;
|
macro_rules! check_with_fail_fn {
|
||||||
|
($name:ident, $ordering:expr) => {
|
||||||
macro_rules! compare {
|
let invalid_ord = $ordering.reverse();
|
||||||
($name:ident, $ordering:ident) => {
|
// In the case of `min_subgroup_size`, requesting a value of
|
||||||
match self.$name.cmp(&allowed.$name) {
|
// zero means "I'm not going to use subgroups", so we have to
|
||||||
Ordering::$ordering | Ordering::Equal => (),
|
// special case that. If any of our minimum limits could
|
||||||
_ => {
|
// meaningfully go all the way to zero, that would conflict with
|
||||||
fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64);
|
// this.
|
||||||
if fatal {
|
if self.$name != 0 && self.$name.cmp(&allowed.$name) == invalid_ord {
|
||||||
return;
|
fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64);
|
||||||
}
|
if fatal {
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
compare!(max_texture_dimension_1d, Less);
|
if self.min_subgroup_size > self.max_subgroup_size {
|
||||||
compare!(max_texture_dimension_2d, Less);
|
fail_fn(
|
||||||
compare!(max_texture_dimension_3d, Less);
|
"max_subgroup_size",
|
||||||
compare!(max_texture_array_layers, Less);
|
self.min_subgroup_size as u64,
|
||||||
compare!(max_bind_groups, Less);
|
allowed.min_subgroup_size as u64,
|
||||||
compare!(max_bindings_per_bind_group, 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_binding_array_elements_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_buffer_size, Less);
|
|
||||||
compare!(max_vertex_attributes, Less);
|
|
||||||
compare!(max_vertex_buffer_array_stride, Less);
|
|
||||||
compare!(min_uniform_buffer_offset_alignment, Greater);
|
|
||||||
compare!(min_storage_buffer_offset_alignment, Greater);
|
|
||||||
compare!(max_inter_stage_shader_components, Less);
|
|
||||||
compare!(max_color_attachments, Less);
|
|
||||||
compare!(max_color_attachment_bytes_per_sample, Less);
|
|
||||||
compare!(max_compute_workgroup_storage_size, Less);
|
|
||||||
compare!(max_compute_invocations_per_workgroup, Less);
|
|
||||||
compare!(max_compute_workgroup_size_x, Less);
|
|
||||||
compare!(max_compute_workgroup_size_y, Less);
|
|
||||||
compare!(max_compute_workgroup_size_z, Less);
|
|
||||||
compare!(max_compute_workgroups_per_dimension, Less);
|
|
||||||
if self.min_subgroup_size > 0 && self.max_subgroup_size > 0 {
|
|
||||||
compare!(min_subgroup_size, Greater);
|
|
||||||
compare!(max_subgroup_size, Less);
|
|
||||||
}
|
}
|
||||||
compare!(max_push_constant_size, Less);
|
with_limits!(check_with_fail_fn);
|
||||||
compare!(max_non_sampler_bindings, Less);
|
}
|
||||||
|
|
||||||
compare!(max_task_workgroup_total_count, Less);
|
/// For each limit in `other` that is better than the value in `self`,
|
||||||
compare!(max_task_workgroups_per_dimension, Less);
|
/// replace the value in `self` with the value from `other`.
|
||||||
compare!(max_mesh_multiview_count, Less);
|
///
|
||||||
compare!(max_mesh_output_layers, Less);
|
/// A request for a limit value less than the WebGPU-specified default must
|
||||||
|
/// be ignored. This function is used to clamp such requests to the default
|
||||||
|
/// value.
|
||||||
|
///
|
||||||
|
/// This function is not for clamping requests for values beyond the
|
||||||
|
/// supported limits. For that purpose the desired function would be
|
||||||
|
/// `or_worse_values_from` (which doesn't exist, but could be added if
|
||||||
|
/// needed).
|
||||||
|
#[must_use]
|
||||||
|
pub fn or_better_values_from(mut self, other: &Self) -> Self {
|
||||||
|
macro_rules! or_better_value_from {
|
||||||
|
($name:ident, $ordering:expr) => {
|
||||||
|
match $ordering {
|
||||||
|
// Limits that are maximum values (most of them)
|
||||||
|
Ordering::Less => self.$name = self.$name.max(other.$name),
|
||||||
|
// Limits that are minimum values
|
||||||
|
Ordering::Greater => self.$name = self.$name.min(other.$name),
|
||||||
|
Ordering::Equal => unreachable!(),
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
compare!(max_blas_primitive_count, Less);
|
with_limits!(or_better_value_from);
|
||||||
compare!(max_blas_geometry_count, Less);
|
|
||||||
compare!(max_tlas_instance_count, Less);
|
self
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user