mirror of
https://github.com/bevyengine/bevy
synced 2024-11-22 04:33:37 +00:00
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.
This commit is contained in:
parent
3ae96dd307
commit
ef5c8e83ad
2 changed files with 116 additions and 4 deletions
|
@ -15,8 +15,16 @@ pub struct WgpuOptions {
|
|||
pub backends: Option<Backends>,
|
||||
pub power_preference: PowerPreference,
|
||||
pub priority: WgpuOptionsPriority,
|
||||
/// The enabled features. Setting features will require them to be enabled when initializing
|
||||
/// the renderer.
|
||||
pub features: WgpuFeatures,
|
||||
/// The features to ensure are disabled regardless of what the adapter/backend supports
|
||||
pub disabled_features: Option<WgpuFeatures>,
|
||||
/// The imposed limits. Updated based on adapter/backend limits when initializing the renderer
|
||||
/// if using WgpuOptionsPriority::Functionality
|
||||
pub limits: WgpuLimits,
|
||||
/// The constraints on limits allowed regardless of what the adapter/backend supports
|
||||
pub constrained_limits: Option<WgpuLimits>,
|
||||
}
|
||||
|
||||
impl Default for WgpuOptions {
|
||||
|
@ -50,7 +58,9 @@ impl Default for WgpuOptions {
|
|||
power_preference: PowerPreference::HighPerformance,
|
||||
priority,
|
||||
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
disabled_features: None,
|
||||
limits,
|
||||
constrained_limits: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -86,9 +86,11 @@ pub async fn initialize_renderer(
|
|||
#[cfg(not(feature = "wgpu_trace"))]
|
||||
let trace_path = None;
|
||||
|
||||
// Maybe get features and limits based on what is supported by the adapter/backend
|
||||
let mut features = wgpu::Features::empty();
|
||||
let mut limits = options.limits.clone();
|
||||
if matches!(options.priority, WgpuOptionsPriority::Functionality) {
|
||||
let mut features =
|
||||
adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
|
||||
features = adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
|
||||
if adapter_info.device_type == wgpu::DeviceType::DiscreteGpu {
|
||||
// `MAPPABLE_PRIMARY_BUFFERS` can have a significant, negative performance impact for
|
||||
// discrete GPUs due to having to transfer data across the PCI-E bus and so it
|
||||
|
@ -96,8 +98,108 @@ pub async fn initialize_renderer(
|
|||
// integrated GPUs.
|
||||
features -= wgpu::Features::MAPPABLE_PRIMARY_BUFFERS;
|
||||
}
|
||||
options.features = features;
|
||||
options.limits = adapter.limits();
|
||||
limits = adapter.limits();
|
||||
}
|
||||
|
||||
// Enforce the disabled features
|
||||
if let Some(disabled_features) = options.disabled_features {
|
||||
features -= disabled_features;
|
||||
}
|
||||
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
|
||||
options.features |= features;
|
||||
|
||||
// Enforce the limit constraints
|
||||
if let Some(constrained_limits) = options.constrained_limits.as_ref() {
|
||||
// NOTE: Respect the configured limits as an 'upper bound'. This means for 'max' limits, we
|
||||
// take the minimum of the calculated limits according to the adapter/backend and the
|
||||
// specified max_limits. For 'min' limits, take the maximum instead. This is intended to
|
||||
// err on the side of being conservative. We can't claim 'higher' limits that are supported
|
||||
// but we can constrain to 'lower' limits.
|
||||
options.limits = wgpu::Limits {
|
||||
max_texture_dimension_1d: limits
|
||||
.max_texture_dimension_1d
|
||||
.min(constrained_limits.max_texture_dimension_1d),
|
||||
max_texture_dimension_2d: limits
|
||||
.max_texture_dimension_2d
|
||||
.min(constrained_limits.max_texture_dimension_2d),
|
||||
max_texture_dimension_3d: limits
|
||||
.max_texture_dimension_3d
|
||||
.min(constrained_limits.max_texture_dimension_3d),
|
||||
max_texture_array_layers: limits
|
||||
.max_texture_array_layers
|
||||
.min(constrained_limits.max_texture_array_layers),
|
||||
max_bind_groups: limits
|
||||
.max_bind_groups
|
||||
.min(constrained_limits.max_bind_groups),
|
||||
max_dynamic_uniform_buffers_per_pipeline_layout: limits
|
||||
.max_dynamic_uniform_buffers_per_pipeline_layout
|
||||
.min(constrained_limits.max_dynamic_uniform_buffers_per_pipeline_layout),
|
||||
max_dynamic_storage_buffers_per_pipeline_layout: limits
|
||||
.max_dynamic_storage_buffers_per_pipeline_layout
|
||||
.min(constrained_limits.max_dynamic_storage_buffers_per_pipeline_layout),
|
||||
max_sampled_textures_per_shader_stage: limits
|
||||
.max_sampled_textures_per_shader_stage
|
||||
.min(constrained_limits.max_sampled_textures_per_shader_stage),
|
||||
max_samplers_per_shader_stage: limits
|
||||
.max_samplers_per_shader_stage
|
||||
.min(constrained_limits.max_samplers_per_shader_stage),
|
||||
max_storage_buffers_per_shader_stage: limits
|
||||
.max_storage_buffers_per_shader_stage
|
||||
.min(constrained_limits.max_storage_buffers_per_shader_stage),
|
||||
max_storage_textures_per_shader_stage: limits
|
||||
.max_storage_textures_per_shader_stage
|
||||
.min(constrained_limits.max_storage_textures_per_shader_stage),
|
||||
max_uniform_buffers_per_shader_stage: limits
|
||||
.max_uniform_buffers_per_shader_stage
|
||||
.min(constrained_limits.max_uniform_buffers_per_shader_stage),
|
||||
max_uniform_buffer_binding_size: limits
|
||||
.max_uniform_buffer_binding_size
|
||||
.min(constrained_limits.max_uniform_buffer_binding_size),
|
||||
max_storage_buffer_binding_size: limits
|
||||
.max_storage_buffer_binding_size
|
||||
.min(constrained_limits.max_storage_buffer_binding_size),
|
||||
max_vertex_buffers: limits
|
||||
.max_vertex_buffers
|
||||
.min(constrained_limits.max_vertex_buffers),
|
||||
max_vertex_attributes: limits
|
||||
.max_vertex_attributes
|
||||
.min(constrained_limits.max_vertex_attributes),
|
||||
max_vertex_buffer_array_stride: limits
|
||||
.max_vertex_buffer_array_stride
|
||||
.min(constrained_limits.max_vertex_buffer_array_stride),
|
||||
max_push_constant_size: limits
|
||||
.max_push_constant_size
|
||||
.min(constrained_limits.max_push_constant_size),
|
||||
min_uniform_buffer_offset_alignment: limits
|
||||
.min_uniform_buffer_offset_alignment
|
||||
.max(constrained_limits.min_uniform_buffer_offset_alignment),
|
||||
min_storage_buffer_offset_alignment: limits
|
||||
.min_storage_buffer_offset_alignment
|
||||
.max(constrained_limits.min_storage_buffer_offset_alignment),
|
||||
max_inter_stage_shader_components: limits
|
||||
.max_inter_stage_shader_components
|
||||
.min(constrained_limits.max_inter_stage_shader_components),
|
||||
max_compute_workgroup_storage_size: limits
|
||||
.max_compute_workgroup_storage_size
|
||||
.min(constrained_limits.max_compute_workgroup_storage_size),
|
||||
max_compute_invocations_per_workgroup: limits
|
||||
.max_compute_invocations_per_workgroup
|
||||
.min(constrained_limits.max_compute_invocations_per_workgroup),
|
||||
max_compute_workgroup_size_x: limits
|
||||
.max_compute_workgroup_size_x
|
||||
.min(constrained_limits.max_compute_workgroup_size_x),
|
||||
max_compute_workgroup_size_y: limits
|
||||
.max_compute_workgroup_size_y
|
||||
.min(constrained_limits.max_compute_workgroup_size_y),
|
||||
max_compute_workgroup_size_z: limits
|
||||
.max_compute_workgroup_size_z
|
||||
.min(constrained_limits.max_compute_workgroup_size_z),
|
||||
max_compute_workgroups_per_dimension: limits
|
||||
.max_compute_workgroups_per_dimension
|
||||
.min(constrained_limits.max_compute_workgroups_per_dimension),
|
||||
};
|
||||
} else {
|
||||
options.limits = limits;
|
||||
}
|
||||
|
||||
let (device, queue) = adapter
|
||||
|
|
Loading…
Reference in a new issue