Skip to content

Commit 03bca7d

Browse files
committed
[deno] Clamp requested limits to the WebGPU defaults
Fixes #8084
1 parent b500ca9 commit 03bca7d

File tree

4 files changed

+120
-57
lines changed

4 files changed

+120
-57
lines changed

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -75,6 +75,7 @@ By @Vecvec in [#7913](https://github.com/gfx-rs/wgpu/pull/7913).
7575
- 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`.
7676
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).
7777
By @kpreid in [#8011](https://github.com/gfx-rs/wgpu/pull/8011).
78+
- `wgpu` now requires that the requested device limits satisfy `min_subgroup_size <= max_subgroup_size`. By @andyleiserson in [#8085](https://github.com/gfx-rs/wgpu/pull/8085).
7879

7980
#### Naga
8081

cts_runner/test.lst

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_w
1919
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,color_attachment_only:*
2020
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,multiple_color_attachments:*
2121
webgpu:api,operation,render_pass,storeOp:render_pass_store_op,depth_stencil_attachment_only:*
22-
webgpu:api,validation,capability_checks,limits,maxBindGroups:setBindGroup,at_over:limitTest="overMaximum";*
22+
webgpu:api,validation,capability_checks,limits,maxBindGroups:setBindGroup,*
2323
webgpu:api,validation,createBindGroup:buffer,effective_buffer_binding_size:*
2424
webgpu:api,validation,encoding,beginComputePass:*
2525
webgpu:api,validation,encoding,beginRenderPass:*

deno_webgpu/adapter.rs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -125,8 +125,13 @@ impl GPUAdapter {
125125
return Err(CreateDeviceError::RequiredFeaturesNotASubset);
126126
}
127127

128-
let required_limits =
129-
serde_json::from_value(serde_json::to_value(descriptor.required_limits)?)?;
128+
// When support for compatibility mode is added, this will need to look
129+
// at whether the adapter is "compatibility-defaulting" or "core-defaulting",
130+
// and choose the appropriate set of defaults.
131+
let required_limits = serde_json::from_value::<wgpu_types::Limits>(serde_json::to_value(
132+
descriptor.required_limits,
133+
)?)?
134+
.or_better_values_from(&wgpu_types::Limits::default());
130135

131136
let trace = std::env::var_os("DENO_WEBGPU_TRACE")
132137
.map(|path| wgpu_types::Trace::Directory(std::path::PathBuf::from(path)))

wgpu-types/src/lib.rs

Lines changed: 111 additions & 54 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@ extern crate alloc;
1616

1717
use alloc::borrow::Cow;
1818
use alloc::{string::String, vec, vec::Vec};
19+
use core::cmp::Ordering;
1920
use core::{
2021
fmt,
2122
hash::{Hash, Hasher},
@@ -459,6 +460,71 @@ impl fmt::Display for RequestAdapterError {
459460
}
460461
}
461462

463+
/// Invoke a macro for each of the limits.
464+
///
465+
/// The supplied macro should take two arguments. The first is a limit name, as
466+
/// an identifier, typically used to access a member of `struct Limits`. The
467+
/// second is `Ordering::Less` if valid values are less than the limit (the
468+
/// common case), or `Ordering::Greater` if valid values are more than the limit
469+
/// (for limits like alignments, which are minima instead of maxima).
470+
macro_rules! with_limits {
471+
($macro_name:ident) => {
472+
$macro_name!(max_texture_dimension_1d, Ordering::Less);
473+
$macro_name!(max_texture_dimension_1d, Ordering::Less);
474+
$macro_name!(max_texture_dimension_2d, Ordering::Less);
475+
$macro_name!(max_texture_dimension_3d, Ordering::Less);
476+
$macro_name!(max_texture_array_layers, Ordering::Less);
477+
$macro_name!(max_bind_groups, Ordering::Less);
478+
$macro_name!(max_bindings_per_bind_group, Ordering::Less);
479+
$macro_name!(
480+
max_dynamic_uniform_buffers_per_pipeline_layout,
481+
Ordering::Less
482+
);
483+
$macro_name!(
484+
max_dynamic_storage_buffers_per_pipeline_layout,
485+
Ordering::Less
486+
);
487+
$macro_name!(max_sampled_textures_per_shader_stage, Ordering::Less);
488+
$macro_name!(max_samplers_per_shader_stage, Ordering::Less);
489+
$macro_name!(max_storage_buffers_per_shader_stage, Ordering::Less);
490+
$macro_name!(max_storage_textures_per_shader_stage, Ordering::Less);
491+
$macro_name!(max_uniform_buffers_per_shader_stage, Ordering::Less);
492+
$macro_name!(max_binding_array_elements_per_shader_stage, Ordering::Less);
493+
$macro_name!(max_uniform_buffer_binding_size, Ordering::Less);
494+
$macro_name!(max_storage_buffer_binding_size, Ordering::Less);
495+
$macro_name!(max_vertex_buffers, Ordering::Less);
496+
$macro_name!(max_buffer_size, Ordering::Less);
497+
$macro_name!(max_vertex_attributes, Ordering::Less);
498+
$macro_name!(max_vertex_buffer_array_stride, Ordering::Less);
499+
$macro_name!(min_uniform_buffer_offset_alignment, Ordering::Greater);
500+
$macro_name!(min_storage_buffer_offset_alignment, Ordering::Greater);
501+
$macro_name!(max_inter_stage_shader_components, Ordering::Less);
502+
$macro_name!(max_color_attachments, Ordering::Less);
503+
$macro_name!(max_color_attachment_bytes_per_sample, Ordering::Less);
504+
$macro_name!(max_compute_workgroup_storage_size, Ordering::Less);
505+
$macro_name!(max_compute_invocations_per_workgroup, Ordering::Less);
506+
$macro_name!(max_compute_workgroup_size_x, Ordering::Less);
507+
$macro_name!(max_compute_workgroup_size_y, Ordering::Less);
508+
$macro_name!(max_compute_workgroup_size_z, Ordering::Less);
509+
$macro_name!(max_compute_workgroups_per_dimension, Ordering::Less);
510+
511+
$macro_name!(min_subgroup_size, Ordering::Greater);
512+
$macro_name!(max_subgroup_size, Ordering::Less);
513+
514+
$macro_name!(max_push_constant_size, Ordering::Less);
515+
$macro_name!(max_non_sampler_bindings, Ordering::Less);
516+
517+
$macro_name!(max_task_workgroup_total_count, Ordering::Less);
518+
$macro_name!(max_task_workgroups_per_dimension, Ordering::Less);
519+
$macro_name!(max_mesh_multiview_count, Ordering::Less);
520+
$macro_name!(max_mesh_output_layers, Ordering::Less);
521+
522+
$macro_name!(max_blas_primitive_count, Ordering::Less);
523+
$macro_name!(max_blas_geometry_count, Ordering::Less);
524+
$macro_name!(max_tlas_instance_count, Ordering::Less);
525+
};
526+
}
527+
462528
/// Represents the sets of limits an adapter/device supports.
463529
///
464530
/// We provide three different defaults.
@@ -1015,68 +1081,59 @@ impl Limits {
10151081
fatal: bool,
10161082
mut fail_fn: impl FnMut(&'static str, u64, u64),
10171083
) {
1018-
use core::cmp::Ordering;
1019-
1020-
macro_rules! compare {
1021-
($name:ident, $ordering:ident) => {
1022-
match self.$name.cmp(&allowed.$name) {
1023-
Ordering::$ordering | Ordering::Equal => (),
1024-
_ => {
1025-
fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64);
1026-
if fatal {
1027-
return;
1028-
}
1084+
macro_rules! check_with_fail_fn {
1085+
($name:ident, $ordering:expr) => {
1086+
let invalid_ord = $ordering.reverse();
1087+
// In the case of `min_subgroup_size`, requesting a value of
1088+
// zero means "I'm not going to use subgroups", so we have to
1089+
// special case that. If any of our minimum limits could
1090+
// meaningfully go all the way to zero, that would conflict with
1091+
// this.
1092+
if self.$name != 0 && self.$name.cmp(&allowed.$name) == invalid_ord {
1093+
fail_fn(stringify!($name), self.$name as u64, allowed.$name as u64);
1094+
if fatal {
1095+
return;
10291096
}
10301097
}
10311098
};
10321099
}
10331100

1034-
compare!(max_texture_dimension_1d, Less);
1035-
compare!(max_texture_dimension_2d, Less);
1036-
compare!(max_texture_dimension_3d, Less);
1037-
compare!(max_texture_array_layers, Less);
1038-
compare!(max_bind_groups, Less);
1039-
compare!(max_bindings_per_bind_group, Less);
1040-
compare!(max_dynamic_uniform_buffers_per_pipeline_layout, Less);
1041-
compare!(max_dynamic_storage_buffers_per_pipeline_layout, Less);
1042-
compare!(max_sampled_textures_per_shader_stage, Less);
1043-
compare!(max_samplers_per_shader_stage, Less);
1044-
compare!(max_storage_buffers_per_shader_stage, Less);
1045-
compare!(max_storage_textures_per_shader_stage, Less);
1046-
compare!(max_uniform_buffers_per_shader_stage, Less);
1047-
compare!(max_binding_array_elements_per_shader_stage, Less);
1048-
compare!(max_uniform_buffer_binding_size, Less);
1049-
compare!(max_storage_buffer_binding_size, Less);
1050-
compare!(max_vertex_buffers, Less);
1051-
compare!(max_buffer_size, Less);
1052-
compare!(max_vertex_attributes, Less);
1053-
compare!(max_vertex_buffer_array_stride, Less);
1054-
compare!(min_uniform_buffer_offset_alignment, Greater);
1055-
compare!(min_storage_buffer_offset_alignment, Greater);
1056-
compare!(max_inter_stage_shader_components, Less);
1057-
compare!(max_color_attachments, Less);
1058-
compare!(max_color_attachment_bytes_per_sample, Less);
1059-
compare!(max_compute_workgroup_storage_size, Less);
1060-
compare!(max_compute_invocations_per_workgroup, Less);
1061-
compare!(max_compute_workgroup_size_x, Less);
1062-
compare!(max_compute_workgroup_size_y, Less);
1063-
compare!(max_compute_workgroup_size_z, Less);
1064-
compare!(max_compute_workgroups_per_dimension, Less);
1065-
if self.min_subgroup_size > 0 && self.max_subgroup_size > 0 {
1066-
compare!(min_subgroup_size, Greater);
1067-
compare!(max_subgroup_size, Less);
1101+
if self.min_subgroup_size > self.max_subgroup_size {
1102+
fail_fn(
1103+
"max_subgroup_size",
1104+
self.min_subgroup_size as u64,
1105+
allowed.min_subgroup_size as u64,
1106+
);
1107+
}
1108+
with_limits!(check_with_fail_fn);
1109+
}
1110+
1111+
/// For each limit in `other` that is better than the value in `self`,
1112+
/// replace the value in `self` with the value from `other`.
1113+
///
1114+
/// A request for a limit value less than the WebGPU-specified default must
1115+
/// be ignored. This function is used to clamp such requests to the default
1116+
/// value.
1117+
///
1118+
/// This is not what you want to clamp a request that otherwise might be
1119+
/// asking for something beyond the supported limits.
1120+
#[must_use]
1121+
pub fn or_better_values_from(mut self, other: &Self) -> Self {
1122+
macro_rules! or_better_value_from {
1123+
($name:ident, $ordering:expr) => {
1124+
match $ordering {
1125+
// Limits that are maximum values (most of them)
1126+
Ordering::Less => self.$name = self.$name.max(other.$name),
1127+
// Limits that are minimum values
1128+
Ordering::Greater => self.$name = self.$name.min(other.$name),
1129+
Ordering::Equal => unreachable!(),
1130+
}
1131+
};
10681132
}
1069-
compare!(max_push_constant_size, Less);
1070-
compare!(max_non_sampler_bindings, Less);
10711133

1072-
compare!(max_task_workgroup_total_count, Less);
1073-
compare!(max_task_workgroups_per_dimension, Less);
1074-
compare!(max_mesh_multiview_count, Less);
1075-
compare!(max_mesh_output_layers, Less);
1134+
with_limits!(or_better_value_from);
10761135

1077-
compare!(max_blas_primitive_count, Less);
1078-
compare!(max_blas_geometry_count, Less);
1079-
compare!(max_tlas_instance_count, Less);
1136+
self
10801137
}
10811138
}
10821139

0 commit comments

Comments
 (0)