Skip to content

Commit 803e8cd

Browse files
committed
bevy_render: Support overriding wgpu features and limits (#3912)
# Objective - Support overriding wgpu features and limits that were calculated from default values or queried from the adapter/backend. - Fixes #3686 ## Solution - Add `disabled_features: Option<wgpu::Features>` to `WgpuOptions` - Add `constrained_limits: Option<wgpu::Limits>` to `WgpuOptions` - After maybe obtaining updated features and limits from the adapter/backend in the case of `WgpuOptionsPriority::Functionality`, enable the `WgpuOptions` `features`, disable the `disabled_features`, and constrain the `limits` by `constrained_limits`. - Note that constraining the limits means for `wgpu::Limits` members named `max_.*` we take the minimum of that which was configured/queried for the backend/adapter and the specified constrained limit value. This means the configured/queried value is used if the constrained limit is larger as that is as much as the device/API supports, or the constrained limit value is used if it is smaller as we are imposing an artificial constraint. For members named `min_.*` we take the maximum instead. For example, a minimum stride might be 256 but we set constrained limit value of 1024, then 1024 is the more conservative value. If the constrained limit value were 16, then 256 would be the more conservative.
1 parent e749ee7 commit 803e8cd

File tree

2 files changed

+116
-4
lines changed

2 files changed

+116
-4
lines changed

crates/bevy_render/src/options.rs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,8 +15,16 @@ pub struct WgpuOptions {
1515
pub backends: Option<Backends>,
1616
pub power_preference: PowerPreference,
1717
pub priority: WgpuOptionsPriority,
18+
/// The enabled features. Setting features will require them to be enabled when initializing
19+
/// the renderer.
1820
pub features: WgpuFeatures,
21+
/// The features to ensure are disabled regardless of what the adapter/backend supports
22+
pub disabled_features: Option<WgpuFeatures>,
23+
/// The imposed limits. Updated based on adapter/backend limits when initializing the renderer
24+
/// if using WgpuOptionsPriority::Functionality
1925
pub limits: WgpuLimits,
26+
/// The constraints on limits allowed regardless of what the adapter/backend supports
27+
pub constrained_limits: Option<WgpuLimits>,
2028
}
2129

2230
impl Default for WgpuOptions {
@@ -50,7 +58,9 @@ impl Default for WgpuOptions {
5058
power_preference: PowerPreference::HighPerformance,
5159
priority,
5260
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
61+
disabled_features: None,
5362
limits,
63+
constrained_limits: None,
5464
}
5565
}
5666
}

crates/bevy_render/src/renderer/mod.rs

Lines changed: 106 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -86,18 +86,120 @@ pub async fn initialize_renderer(
8686
#[cfg(not(feature = "wgpu_trace"))]
8787
let trace_path = None;
8888

89+
// Maybe get features and limits based on what is supported by the adapter/backend
90+
let mut features = wgpu::Features::empty();
91+
let mut limits = options.limits.clone();
8992
if matches!(options.priority, WgpuOptionsPriority::Functionality) {
90-
let mut features =
91-
adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
93+
features = adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
9294
if adapter_info.device_type == wgpu::DeviceType::DiscreteGpu {
9395
// `MAPPABLE_PRIMARY_BUFFERS` can have a significant, negative performance impact for
9496
// discrete GPUs due to having to transfer data across the PCI-E bus and so it
9597
// should not be automatically enabled in this case. It is however beneficial for
9698
// integrated GPUs.
9799
features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS;
98100
}
99-
options.features = features;
100-
options.limits = adapter.limits();
101+
limits = adapter.limits();
102+
}
103+
104+
// Enforce the disabled features
105+
if let Some(disabled_features) = options.disabled_features {
106+
features -= disabled_features;
107+
}
108+
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
109+
options.features |= features;
110+
111+
// Enforce the limit constraints
112+
if let Some(constrained_limits) = options.constrained_limits.as_ref() {
113+
// NOTE: Respect the configured limits as an 'upper bound'. This means for 'max' limits, we
114+
// take the minimum of the calculated limits according to the adapter/backend and the
115+
// specified max_limits. For 'min' limits, take the maximum instead. This is intended to
116+
// err on the side of being conservative. We can't claim 'higher' limits that are supported
117+
// but we can constrain to 'lower' limits.
118+
options.limits = wgpu::Limits {
119+
max_texture_dimension_1d: limits
120+
.max_texture_dimension_1d
121+
.min(constrained_limits.max_texture_dimension_1d),
122+
max_texture_dimension_2d: limits
123+
.max_texture_dimension_2d
124+
.min(constrained_limits.max_texture_dimension_2d),
125+
max_texture_dimension_3d: limits
126+
.max_texture_dimension_3d
127+
.min(constrained_limits.max_texture_dimension_3d),
128+
max_texture_array_layers: limits
129+
.max_texture_array_layers
130+
.min(constrained_limits.max_texture_array_layers),
131+
max_bind_groups: limits
132+
.max_bind_groups
133+
.min(constrained_limits.max_bind_groups),
134+
max_dynamic_uniform_buffers_per_pipeline_layout: limits
135+
.max_dynamic_uniform_buffers_per_pipeline_layout
136+
.min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
137+
max_dynamic_storage_buffers_per_pipeline_layout: limits
138+
.max_dynamic_storage_buffers_per_pipeline_layout
139+
.min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
140+
max_sampled_textures_per_shader_stage: limits
141+
.max_sampled_textures_per_shader_stage
142+
.min(constrained_limits.max_sampled_textures_per_shader_stage),
143+
max_samplers_per_shader_stage: limits
144+
.max_samplers_per_shader_stage
145+
.min(constrained_limits.max_samplers_per_shader_stage),
146+
max_storage_buffers_per_shader_stage: limits
147+
.max_storage_buffers_per_shader_stage
148+
.min(constrained_limits.max_storage_buffers_per_shader_stage),
149+
max_storage_textures_per_shader_stage: limits
150+
.max_storage_textures_per_shader_stage
151+
.min(constrained_limits.max_storage_textures_per_shader_stage),
152+
max_uniform_buffers_per_shader_stage: limits
153+
.max_uniform_buffers_per_shader_stage
154+
.min(constrained_limits.max_uniform_buffers_per_shader_stage),
155+
max_uniform_buffer_binding_size: limits
156+
.max_uniform_buffer_binding_size
157+
.min(constrained_limits.max_uniform_buffer_binding_size),
158+
max_storage_buffer_binding_size: limits
159+
.max_storage_buffer_binding_size
160+
.min(constrained_limits.max_storage_buffer_binding_size),
161+
max_vertex_buffers: limits
162+
.max_vertex_buffers
163+
.min(constrained_limits.max_vertex_buffers),
164+
max_vertex_attributes: limits
165+
.max_vertex_attributes
166+
.min(constrained_limits.max_vertex_attributes),
167+
max_vertex_buffer_array_stride: limits
168+
.max_vertex_buffer_array_stride
169+
.min(constrained_limits.max_vertex_buffer_array_stride),
170+
max_push_constant_size: limits
171+
.max_push_constant_size
172+
.min(constrained_limits.max_push_constant_size),
173+
min_uniform_buffer_offset_alignment: limits
174+
.min_uniform_buffer_offset_alignment
175+
.max(constrained_limits.min_uniform_buffer_offset_alignment),
176+
min_storage_buffer_offset_alignment: limits
177+
.min_storage_buffer_offset_alignment
178+
.max(constrained_limits.min_storage_buffer_offset_alignment),
179+
max_inter_stage_shader_components: limits
180+
.max_inter_stage_shader_components
181+
.min(constrained_limits.max_inter_stage_shader_components),
182+
max_compute_workgroup_storage_size: limits
183+
.max_compute_workgroup_storage_size
184+
.min(constrained_limits.max_compute_workgroup_storage_size),
185+
max_compute_invocations_per_workgroup: limits
186+
.max_compute_invocations_per_workgroup
187+
.min(constrained_limits.max_compute_invocations_per_workgroup),
188+
max_compute_workgroup_size_x: limits
189+
.max_compute_workgroup_size_x
190+
.min(constrained_limits.max_compute_workgroup_size_x),
191+
max_compute_workgroup_size_y: limits
192+
.max_compute_workgroup_size_y
193+
.min(constrained_limits.max_compute_workgroup_size_y),
194+
max_compute_workgroup_size_z: limits
195+
.max_compute_workgroup_size_z
196+
.min(constrained_limits.max_compute_workgroup_size_z),
197+
max_compute_workgroups_per_dimension: limits
198+
.max_compute_workgroups_per_dimension
199+
.min(constrained_limits.max_compute_workgroups_per_dimension),
200+
};
201+
} else {
202+
options.limits = limits;
101203
}
102204

103205
let (device, queue) = adapter

0 commit comments

Comments
 (0)