Configurable wgpu features/limits priority (#3452)
# Objective - Allow the user to specify the priority when configuring wgpu features/limits and by default use the maximum capabilities of the chosen adapter. ## Solution - Add a `WgpuOptionsPriority` enum with `Compatibility`, `Functionality` and `WebGL2` options. - Add a `priority: WgpuOptionsPriority` member to `WgpuOptions`. - When initialising the renderer, if `WgpuOptions::priority == WgpuOptionsPriority::Functionality`, query the adapter for the available features and limits, use them when creating a device, and update `WgpuOptions` with those values. If `Compatibility` use the behaviour as before this PR. If `WebGL2` then use the WebGL2 downlevel limits as used when when building for wasm, for convenience of testing WebGL2 limits without having to build for wasm. - Add an environment variable `WGPU_OPTIONS_PRIO` that takes `compatibility`, `functionality`, `webgl2`. - Default to `WgpuOptionsPriority::Functionality`. - Insert updated `WgpuOptions` into render app world as well. This is useful for applying the limits when rendering, such as limiting the directional light shadow map texture to 2048x2048 when using WebGL2 downlevel limits but not on wasm. - Reduced `draw_state` logs from `debug` to `trace` and added `debug` level logs for the wgpu features and limits. Use `RUST_LOG=bevy_render=debug` to see the output.
This commit is contained in:
parent
37f9e418d2
commit
b9c623e4f3
@ -15,6 +15,7 @@ use bevy_render::{
|
||||
camera::{Camera, CameraProjection},
|
||||
color::Color,
|
||||
mesh::Mesh,
|
||||
options::WgpuOptions,
|
||||
render_asset::RenderAssets,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||
render_phase::{
|
||||
@ -576,6 +577,7 @@ pub fn prepare_lights(
|
||||
directional_light_shadow_map: Res<ExtractedDirectionalLightShadowMap>,
|
||||
point_lights: Query<(Entity, &ExtractedPointLight)>,
|
||||
directional_lights: Query<(Entity, &ExtractedDirectionalLight)>,
|
||||
wgpu_options: Res<WgpuOptions>,
|
||||
) {
|
||||
light_meta.view_gpu_lights.clear();
|
||||
|
||||
@ -664,8 +666,10 @@ pub fn prepare_lights(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
size: Extent3d {
|
||||
width: directional_light_shadow_map.size as u32,
|
||||
height: directional_light_shadow_map.size as u32,
|
||||
width: (directional_light_shadow_map.size as u32)
|
||||
.min(wgpu_options.limits.max_texture_dimension_2d),
|
||||
height: (directional_light_shadow_map.size as u32)
|
||||
.min(wgpu_options.limits.max_texture_dimension_2d),
|
||||
depth_or_array_layers: DIRECTIONAL_SHADOW_LAYERS,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
|
||||
@ -27,6 +27,7 @@ pub mod prelude {
|
||||
};
|
||||
}
|
||||
|
||||
use bevy_utils::tracing::debug;
|
||||
pub use once_cell;
|
||||
|
||||
use crate::{
|
||||
@ -107,7 +108,7 @@ struct ScratchRenderWorld(World);
|
||||
impl Plugin for RenderPlugin {
|
||||
/// Initializes the renderer, sets up the [`RenderStage`](RenderStage) and creates the rendering sub-app.
|
||||
fn build(&self, app: &mut App) {
|
||||
let options = app
|
||||
let mut options = app
|
||||
.world
|
||||
.get_resource::<options::WgpuOptions>()
|
||||
.cloned()
|
||||
@ -122,21 +123,21 @@ impl Plugin for RenderPlugin {
|
||||
});
|
||||
raw_handle
|
||||
};
|
||||
let request_adapter_options = wgpu::RequestAdapterOptions {
|
||||
power_preference: options.power_preference,
|
||||
compatible_surface: surface.as_ref(),
|
||||
..Default::default()
|
||||
};
|
||||
let (device, queue) = futures_lite::future::block_on(renderer::initialize_renderer(
|
||||
&instance,
|
||||
&wgpu::RequestAdapterOptions {
|
||||
power_preference: options.power_preference,
|
||||
compatible_surface: surface.as_ref(),
|
||||
..Default::default()
|
||||
},
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: options.device_label.as_ref().map(|a| a.as_ref()),
|
||||
features: options.features,
|
||||
limits: options.limits,
|
||||
},
|
||||
&mut options,
|
||||
&request_adapter_options,
|
||||
));
|
||||
debug!("Configured wgpu adapter Limits: {:#?}", &options.limits);
|
||||
debug!("Configured wgpu adapter Features: {:#?}", &options.features);
|
||||
app.insert_resource(device.clone())
|
||||
.insert_resource(queue.clone())
|
||||
.insert_resource(options.clone())
|
||||
.add_asset::<Shader>()
|
||||
.init_asset_loader::<ShaderLoader>()
|
||||
.init_resource::<ScratchRenderWorld>()
|
||||
@ -167,6 +168,7 @@ impl Plugin for RenderPlugin {
|
||||
.insert_resource(instance)
|
||||
.insert_resource(device)
|
||||
.insert_resource(queue)
|
||||
.insert_resource(options)
|
||||
.insert_resource(render_pipeline_cache)
|
||||
.insert_resource(asset_server)
|
||||
.init_resource::<RenderGraph>();
|
||||
|
||||
@ -2,11 +2,19 @@ use std::borrow::Cow;
|
||||
|
||||
pub use wgpu::{Backends, Features as WgpuFeatures, Limits as WgpuLimits, PowerPreference};
|
||||
|
||||
#[derive(Clone)]
|
||||
pub enum WgpuOptionsPriority {
|
||||
Compatibility,
|
||||
Functionality,
|
||||
WebGL2,
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuOptions {
|
||||
pub device_label: Option<Cow<'static, str>>,
|
||||
pub backends: Backends,
|
||||
pub power_preference: PowerPreference,
|
||||
pub priority: WgpuOptionsPriority,
|
||||
pub features: WgpuFeatures,
|
||||
pub limits: WgpuLimits,
|
||||
}
|
||||
@ -21,7 +29,9 @@ impl Default for WgpuOptions {
|
||||
|
||||
let backends = wgpu::util::backend_bits_from_env().unwrap_or(default_backends);
|
||||
|
||||
let limits = if cfg!(feature = "webgl") {
|
||||
let priority = options_priority_from_env().unwrap_or(WgpuOptionsPriority::Functionality);
|
||||
|
||||
let limits = if cfg!(feature = "webgl") || matches!(priority, WgpuOptionsPriority::WebGL2) {
|
||||
wgpu::Limits::downlevel_webgl2_defaults()
|
||||
} else {
|
||||
#[allow(unused_mut)]
|
||||
@ -38,8 +48,25 @@ impl Default for WgpuOptions {
|
||||
device_label: Default::default(),
|
||||
backends,
|
||||
power_preference: PowerPreference::HighPerformance,
|
||||
priority,
|
||||
features: wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES,
|
||||
limits,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a features/limits priority from the environment variable WGPU_OPTIONS_PRIO
|
||||
pub fn options_priority_from_env() -> Option<WgpuOptionsPriority> {
|
||||
Some(
|
||||
match std::env::var("WGPU_OPTIONS_PRIO")
|
||||
.as_deref()
|
||||
.map(str::to_lowercase)
|
||||
.as_deref()
|
||||
{
|
||||
Ok("compatibility") => WgpuOptionsPriority::Compatibility,
|
||||
Ok("functionality") => WgpuOptionsPriority::Functionality,
|
||||
Ok("webgl2") => WgpuOptionsPriority::WebGL2,
|
||||
_ => return None,
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
@ -5,7 +5,7 @@ use crate::{
|
||||
ShaderStages,
|
||||
},
|
||||
};
|
||||
use bevy_utils::tracing::debug;
|
||||
use bevy_utils::tracing::trace;
|
||||
use std::ops::Range;
|
||||
use wgpu::{IndexFormat, RenderPass};
|
||||
|
||||
@ -108,7 +108,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
///
|
||||
/// Subsequent draw calls will exhibit the behavior defined by the `pipeline`.
|
||||
pub fn set_render_pipeline(&mut self, pipeline: &'a RenderPipeline) {
|
||||
debug!("set pipeline: {:?}", pipeline);
|
||||
trace!("set pipeline: {:?}", pipeline);
|
||||
if self.state.is_pipeline_set(pipeline.id()) {
|
||||
return;
|
||||
}
|
||||
@ -128,15 +128,19 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
.state
|
||||
.is_bind_group_set(index as usize, bind_group.id(), dynamic_uniform_indices)
|
||||
{
|
||||
debug!(
|
||||
trace!(
|
||||
"set bind_group {} (already set): {:?} ({:?})",
|
||||
index, bind_group, dynamic_uniform_indices
|
||||
index,
|
||||
bind_group,
|
||||
dynamic_uniform_indices
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
debug!(
|
||||
trace!(
|
||||
"set bind_group {}: {:?} ({:?})",
|
||||
index, bind_group, dynamic_uniform_indices
|
||||
index,
|
||||
bind_group,
|
||||
dynamic_uniform_indices
|
||||
);
|
||||
}
|
||||
self.pass
|
||||
@ -158,7 +162,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
.state
|
||||
.is_vertex_buffer_set(slot_index, buffer_slice.id(), offset)
|
||||
{
|
||||
debug!(
|
||||
trace!(
|
||||
"set vertex buffer {} (already set): {:?} ({})",
|
||||
slot_index,
|
||||
buffer_slice.id(),
|
||||
@ -166,7 +170,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
debug!(
|
||||
trace!(
|
||||
"set vertex buffer {}: {:?} ({})",
|
||||
slot_index,
|
||||
buffer_slice.id(),
|
||||
@ -193,14 +197,14 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
.state
|
||||
.is_index_buffer_set(buffer_slice.id(), offset, index_format)
|
||||
{
|
||||
debug!(
|
||||
trace!(
|
||||
"set index buffer (already set): {:?} ({})",
|
||||
buffer_slice.id(),
|
||||
offset
|
||||
);
|
||||
return;
|
||||
} else {
|
||||
debug!("set index buffer: {:?} ({})", buffer_slice.id(), offset);
|
||||
trace!("set index buffer: {:?} ({})", buffer_slice.id(), offset);
|
||||
}
|
||||
self.pass.set_index_buffer(*buffer_slice, index_format);
|
||||
self.state
|
||||
@ -211,7 +215,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
///
|
||||
/// The active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].
|
||||
pub fn draw(&mut self, vertices: Range<u32>, instances: Range<u32>) {
|
||||
debug!("draw: {:?} {:?}", vertices, instances);
|
||||
trace!("draw: {:?} {:?}", vertices, instances);
|
||||
self.pass.draw(vertices, instances);
|
||||
}
|
||||
|
||||
@ -220,15 +224,17 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
/// The active index buffer can be set with [`TrackedRenderPass::set_index_buffer`], while the
|
||||
/// active vertex buffer(s) can be set with [`TrackedRenderPass::set_vertex_buffer`].
|
||||
pub fn draw_indexed(&mut self, indices: Range<u32>, base_vertex: i32, instances: Range<u32>) {
|
||||
debug!(
|
||||
trace!(
|
||||
"draw indexed: {:?} {} {:?}",
|
||||
indices, base_vertex, instances
|
||||
indices,
|
||||
base_vertex,
|
||||
instances
|
||||
);
|
||||
self.pass.draw_indexed(indices, base_vertex, instances);
|
||||
}
|
||||
|
||||
pub fn set_stencil_reference(&mut self, reference: u32) {
|
||||
debug!("set stencil reference: {}", reference);
|
||||
trace!("set stencil reference: {}", reference);
|
||||
|
||||
self.pass.set_stencil_reference(reference);
|
||||
}
|
||||
@ -237,7 +243,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
///
|
||||
/// Subsequent draw calls will discard any fragments that fall outside this region.
|
||||
pub fn set_scissor_rect(&mut self, x: u32, y: u32, width: u32, height: u32) {
|
||||
debug!("set_scissor_rect: {} {} {} {}", x, y, width, height);
|
||||
trace!("set_scissor_rect: {} {} {} {}", x, y, width, height);
|
||||
self.pass.set_scissor_rect(x, y, width, height);
|
||||
}
|
||||
|
||||
@ -245,7 +251,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
///
|
||||
/// Features::PUSH_CONSTANTS must be enabled on the device in order to call these functions.
|
||||
pub fn set_push_constants(&mut self, stages: ShaderStages, offset: u32, data: &[u8]) {
|
||||
debug!(
|
||||
trace!(
|
||||
"set push constants: {:?} offset: {} data.len: {}",
|
||||
stages,
|
||||
offset,
|
||||
@ -266,9 +272,14 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
min_depth: f32,
|
||||
max_depth: f32,
|
||||
) {
|
||||
debug!(
|
||||
trace!(
|
||||
"set viewport: {} {} {} {} {} {}",
|
||||
x, y, width, height, min_depth, max_depth
|
||||
x,
|
||||
y,
|
||||
width,
|
||||
height,
|
||||
min_depth,
|
||||
max_depth
|
||||
);
|
||||
self.pass
|
||||
.set_viewport(x, y, width, height, min_depth, max_depth)
|
||||
@ -278,7 +289,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
///
|
||||
/// This is a GPU debugging feature. This has no effect on the rendering itself.
|
||||
pub fn insert_debug_marker(&mut self, label: &str) {
|
||||
debug!("insert debug marker: {}", label);
|
||||
trace!("insert debug marker: {}", label);
|
||||
self.pass.insert_debug_marker(label)
|
||||
}
|
||||
|
||||
@ -303,7 +314,7 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
/// [`push_debug_group`]: TrackedRenderPass::push_debug_group
|
||||
/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group
|
||||
pub fn push_debug_group(&mut self, label: &str) {
|
||||
debug!("push_debug_group marker: {}", label);
|
||||
trace!("push_debug_group marker: {}", label);
|
||||
self.pass.push_debug_group(label)
|
||||
}
|
||||
|
||||
@ -320,12 +331,12 @@ impl<'a> TrackedRenderPass<'a> {
|
||||
/// [`push_debug_group`]: TrackedRenderPass::push_debug_group
|
||||
/// [`pop_debug_group`]: TrackedRenderPass::pop_debug_group
|
||||
pub fn pop_debug_group(&mut self) {
|
||||
debug!("pop_debug_group");
|
||||
trace!("pop_debug_group");
|
||||
self.pass.pop_debug_group()
|
||||
}
|
||||
|
||||
pub fn set_blend_constant(&mut self, color: Color) {
|
||||
debug!("set blend constant: {:?}", color);
|
||||
trace!("set blend constant: {:?}", color);
|
||||
self.pass.set_blend_constant(wgpu::Color::from(color))
|
||||
}
|
||||
}
|
||||
|
||||
@ -6,12 +6,13 @@ pub use graph_runner::*;
|
||||
pub use render_device::*;
|
||||
|
||||
use crate::{
|
||||
options::{WgpuOptions, WgpuOptionsPriority},
|
||||
render_graph::RenderGraph,
|
||||
view::{ExtractedWindows, ViewTarget},
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use wgpu::{CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions};
|
||||
use wgpu::{CommandEncoder, Instance, Queue, RequestAdapterOptions};
|
||||
|
||||
/// Updates the [`RenderGraph`] with all of its nodes and then runs it to render the entire frame.
|
||||
pub fn render_system(world: &mut World) {
|
||||
@ -64,8 +65,8 @@ pub type RenderInstance = Instance;
|
||||
/// for the specified backend.
|
||||
pub async fn initialize_renderer(
|
||||
instance: &Instance,
|
||||
options: &mut WgpuOptions,
|
||||
request_adapter_options: &RequestAdapterOptions<'_>,
|
||||
device_descriptor: &DeviceDescriptor<'_>,
|
||||
) -> (RenderDevice, RenderQueue) {
|
||||
let adapter = instance
|
||||
.request_adapter(request_adapter_options)
|
||||
@ -84,8 +85,21 @@ pub async fn initialize_renderer(
|
||||
#[cfg(not(feature = "wgpu_trace"))]
|
||||
let trace_path = None;
|
||||
|
||||
if matches!(options.priority, WgpuOptionsPriority::Functionality) {
|
||||
options.features =
|
||||
adapter.features() | wgpu::Features::TEXTURE_ADAPTER_SPECIFIC_FORMAT_FEATURES;
|
||||
options.limits = adapter.limits();
|
||||
}
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(device_descriptor, trace_path)
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: options.device_label.as_ref().map(|a| a.as_ref()),
|
||||
features: options.features,
|
||||
limits: options.limits.clone(),
|
||||
},
|
||||
trace_path,
|
||||
)
|
||||
.await
|
||||
.unwrap();
|
||||
let device = Arc::new(device);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user