bevy_render: Use RenderDevice to get limits/features and expose AdapterInfo (#3931)
# Objective - `WgpuOptions` is mutated to be updated with the actual device limits and features, but this information is readily available to both the main and render worlds through the `RenderDevice` which has .limits() and .features() methods - Information about the adapter in terms of its name, the backend in use, etc were not being exposed but have clear use cases for being used to take decisions about what rendering code to use. For example, if something works well on AMD GPUs but poorly on Intel GPUs. Or perhaps something works well in Vulkan but poorly in DX12. ## Solution - Stop mutating `WgpuOptions `and don't insert the updated values into the main and render worlds - Return `AdapterInfo` from `initialize_renderer` and insert it into the main and render worlds - Use `RenderDevice` limits in the lighting code that was using `WgpuOptions.limits`. - Renamed `WgpuOptions` to `WgpuSettings`
This commit is contained in:
parent
d3e526bfc0
commit
936468aa1e
@ -113,6 +113,10 @@ pub mod pbr {
|
||||
#[cfg(feature = "bevy_render")]
|
||||
pub mod render {
|
||||
//! Cameras, meshes, textures, shaders, and pipelines.
|
||||
//! Use [`RenderDevice::features`](bevy_render::renderer::RenderDevice::features),
|
||||
//! [`RenderDevice::limits`](bevy_render::renderer::RenderDevice::limits), and the
|
||||
//! [`WgpuAdapterInfo`](bevy_render::render_resource::WgpuAdapterInfo) resource to
|
||||
//! get runtime information about the actual adapter, backend, features, and limits.
|
||||
pub use bevy_render::*;
|
||||
}
|
||||
|
||||
|
@ -15,7 +15,6 @@ use bevy_render::{
|
||||
camera::{Camera, CameraProjection},
|
||||
color::Color,
|
||||
mesh::Mesh,
|
||||
options::WgpuOptions,
|
||||
render_asset::RenderAssets,
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
|
||||
render_phase::{
|
||||
@ -607,7 +606,6 @@ 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();
|
||||
|
||||
@ -697,9 +695,9 @@ pub fn prepare_lights(
|
||||
TextureDescriptor {
|
||||
size: Extent3d {
|
||||
width: (directional_light_shadow_map.size as u32)
|
||||
.min(wgpu_options.limits.max_texture_dimension_2d),
|
||||
.min(render_device.limits().max_texture_dimension_2d),
|
||||
height: (directional_light_shadow_map.size as u32)
|
||||
.min(wgpu_options.limits.max_texture_dimension_2d),
|
||||
.min(render_device.limits().max_texture_dimension_2d),
|
||||
depth_or_array_layers: DIRECTIONAL_SHADOW_LAYERS,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
|
@ -1,7 +1,6 @@
|
||||
pub mod camera;
|
||||
pub mod color;
|
||||
pub mod mesh;
|
||||
pub mod options;
|
||||
pub mod primitives;
|
||||
pub mod render_asset;
|
||||
pub mod render_component;
|
||||
@ -9,6 +8,7 @@ pub mod render_graph;
|
||||
pub mod render_phase;
|
||||
pub mod render_resource;
|
||||
pub mod renderer;
|
||||
pub mod settings;
|
||||
pub mod texture;
|
||||
pub mod view;
|
||||
|
||||
@ -108,9 +108,9 @@ 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 mut options = app
|
||||
let options = app
|
||||
.world
|
||||
.get_resource::<options::WgpuOptions>()
|
||||
.get_resource::<settings::WgpuSettings>()
|
||||
.cloned()
|
||||
.unwrap_or_default();
|
||||
|
||||
@ -134,16 +134,14 @@ impl Plugin for RenderPlugin {
|
||||
compatible_surface: surface.as_ref(),
|
||||
..Default::default()
|
||||
};
|
||||
let (device, queue) = futures_lite::future::block_on(renderer::initialize_renderer(
|
||||
&instance,
|
||||
&mut options,
|
||||
&request_adapter_options,
|
||||
));
|
||||
debug!("Configured wgpu adapter Limits: {:#?}", &options.limits);
|
||||
debug!("Configured wgpu adapter Features: {:#?}", &options.features);
|
||||
let (device, queue, adapter_info) = futures_lite::future::block_on(
|
||||
renderer::initialize_renderer(&instance, &options, &request_adapter_options),
|
||||
);
|
||||
debug!("Configured wgpu adapter Limits: {:#?}", device.limits());
|
||||
debug!("Configured wgpu adapter Features: {:#?}", device.features());
|
||||
app.insert_resource(device.clone())
|
||||
.insert_resource(queue.clone())
|
||||
.insert_resource(options.clone())
|
||||
.insert_resource(adapter_info.clone())
|
||||
.init_resource::<ScratchRenderWorld>()
|
||||
.register_type::<Frustum>()
|
||||
.register_type::<CubemapFrusta>();
|
||||
@ -171,7 +169,7 @@ impl Plugin for RenderPlugin {
|
||||
.insert_resource(instance)
|
||||
.insert_resource(device)
|
||||
.insert_resource(queue)
|
||||
.insert_resource(options)
|
||||
.insert_resource(adapter_info)
|
||||
.insert_resource(render_pipeline_cache)
|
||||
.insert_resource(asset_server)
|
||||
.init_resource::<RenderGraph>();
|
||||
|
@ -22,23 +22,24 @@ pub use uniform_vec::*;
|
||||
|
||||
// TODO: decide where re-exports should go
|
||||
pub use wgpu::{
|
||||
util::BufferInitDescriptor, AddressMode, BindGroupDescriptor, BindGroupEntry,
|
||||
BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType, BlendComponent,
|
||||
BlendFactor, BlendOperation, BlendState, BufferAddress, BufferBinding, BufferBindingType,
|
||||
BufferDescriptor, BufferSize, BufferUsages, ColorTargetState, ColorWrites, CommandEncoder,
|
||||
CommandEncoderDescriptor, CompareFunction, ComputePassDescriptor, ComputePipelineDescriptor,
|
||||
DepthBiasState, DepthStencilState, Extent3d, Face, Features as WgpuFeatures, FilterMode,
|
||||
FragmentState as RawFragmentState, FrontFace, ImageCopyBuffer, ImageCopyBufferBase,
|
||||
ImageCopyTexture, ImageCopyTextureBase, ImageDataLayout, ImageSubresourceRange, IndexFormat,
|
||||
Limits as WgpuLimits, LoadOp, MapMode, MultisampleState, Operations, Origin3d, PipelineLayout,
|
||||
PipelineLayoutDescriptor, PolygonMode, PrimitiveState, PrimitiveTopology,
|
||||
RenderPassColorAttachment, RenderPassDepthStencilAttachment, RenderPassDescriptor,
|
||||
RenderPipelineDescriptor as RawRenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor,
|
||||
ShaderModule, ShaderModuleDescriptor, ShaderSource, ShaderStages, StencilFaceState,
|
||||
StencilOperation, StencilState, StorageTextureAccess, TextureAspect, TextureDescriptor,
|
||||
TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureViewDescriptor,
|
||||
TextureViewDimension, VertexAttribute, VertexBufferLayout as RawVertexBufferLayout,
|
||||
VertexFormat, VertexState as RawVertexState, VertexStepMode,
|
||||
util::BufferInitDescriptor, AdapterInfo as WgpuAdapterInfo, AddressMode, BindGroupDescriptor,
|
||||
BindGroupEntry, BindGroupLayoutDescriptor, BindGroupLayoutEntry, BindingResource, BindingType,
|
||||
BlendComponent, BlendFactor, BlendOperation, BlendState, BufferAddress, BufferBinding,
|
||||
BufferBindingType, BufferDescriptor, BufferSize, BufferUsages, ColorTargetState, ColorWrites,
|
||||
CommandEncoder, CommandEncoderDescriptor, CompareFunction, ComputePassDescriptor,
|
||||
ComputePipelineDescriptor, DepthBiasState, DepthStencilState, Extent3d, Face,
|
||||
Features as WgpuFeatures, FilterMode, FragmentState as RawFragmentState, FrontFace,
|
||||
ImageCopyBuffer, ImageCopyBufferBase, ImageCopyTexture, ImageCopyTextureBase, ImageDataLayout,
|
||||
ImageSubresourceRange, IndexFormat, Limits as WgpuLimits, LoadOp, MapMode, MultisampleState,
|
||||
Operations, Origin3d, PipelineLayout, PipelineLayoutDescriptor, PolygonMode, PrimitiveState,
|
||||
PrimitiveTopology, RenderPassColorAttachment, RenderPassDepthStencilAttachment,
|
||||
RenderPassDescriptor, RenderPipelineDescriptor as RawRenderPipelineDescriptor,
|
||||
SamplerBindingType, SamplerDescriptor, ShaderModule, ShaderModuleDescriptor, ShaderSource,
|
||||
ShaderStages, StencilFaceState, StencilOperation, StencilState, StorageTextureAccess,
|
||||
TextureAspect, TextureDescriptor, TextureDimension, TextureFormat, TextureSampleType,
|
||||
TextureUsages, TextureViewDescriptor, TextureViewDimension, VertexAttribute,
|
||||
VertexBufferLayout as RawVertexBufferLayout, VertexFormat, VertexState as RawVertexState,
|
||||
VertexStepMode,
|
||||
};
|
||||
|
||||
pub use bevy_crevice::*;
|
||||
|
@ -6,13 +6,13 @@ pub use graph_runner::*;
|
||||
pub use render_device::*;
|
||||
|
||||
use crate::{
|
||||
options::{WgpuOptions, WgpuOptionsPriority},
|
||||
render_graph::RenderGraph,
|
||||
settings::{WgpuSettings, WgpuSettingsPriority},
|
||||
view::{ExtractedWindows, ViewTarget},
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use wgpu::{CommandEncoder, Instance, Queue, RequestAdapterOptions};
|
||||
use wgpu::{AdapterInfo, 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) {
|
||||
@ -65,9 +65,9 @@ pub type RenderInstance = Instance;
|
||||
/// for the specified backend.
|
||||
pub async fn initialize_renderer(
|
||||
instance: &Instance,
|
||||
options: &mut WgpuOptions,
|
||||
options: &WgpuSettings,
|
||||
request_adapter_options: &RequestAdapterOptions<'_>,
|
||||
) -> (RenderDevice, RenderQueue) {
|
||||
) -> (RenderDevice, RenderQueue, AdapterInfo) {
|
||||
let adapter = instance
|
||||
.request_adapter(request_adapter_options)
|
||||
.await
|
||||
@ -89,7 +89,7 @@ pub async fn initialize_renderer(
|
||||
// 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) {
|
||||
if matches!(options.priority, WgpuSettingsPriority::Functionality) {
|
||||
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
|
||||
@ -106,7 +106,7 @@ pub async fn initialize_renderer(
|
||||
features -= disabled_features;
|
||||
}
|
||||
// NOTE: |= is used here to ensure that any explicitly-enabled features are respected.
|
||||
options.features |= features;
|
||||
features |= options.features;
|
||||
|
||||
// Enforce the limit constraints
|
||||
if let Some(constrained_limits) = options.constrained_limits.as_ref() {
|
||||
@ -115,7 +115,7 @@ pub async fn initialize_renderer(
|
||||
// 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 {
|
||||
limits = wgpu::Limits {
|
||||
max_texture_dimension_1d: limits
|
||||
.max_texture_dimension_1d
|
||||
.min(constrained_limits.max_texture_dimension_1d),
|
||||
@ -198,16 +198,14 @@ pub async fn initialize_renderer(
|
||||
.max_compute_workgroups_per_dimension
|
||||
.min(constrained_limits.max_compute_workgroups_per_dimension),
|
||||
};
|
||||
} else {
|
||||
options.limits = limits;
|
||||
}
|
||||
|
||||
let (device, queue) = adapter
|
||||
.request_device(
|
||||
&wgpu::DeviceDescriptor {
|
||||
label: options.device_label.as_ref().map(|a| a.as_ref()),
|
||||
features: options.features,
|
||||
limits: options.limits.clone(),
|
||||
features,
|
||||
limits,
|
||||
},
|
||||
trace_path,
|
||||
)
|
||||
@ -215,7 +213,7 @@ pub async fn initialize_renderer(
|
||||
.unwrap();
|
||||
let device = Arc::new(device);
|
||||
let queue = Arc::new(queue);
|
||||
(RenderDevice::from(device), queue)
|
||||
(RenderDevice::from(device), queue, adapter_info)
|
||||
}
|
||||
|
||||
/// The context with all information required to interact with the GPU.
|
||||
|
@ -2,32 +2,38 @@ use std::borrow::Cow;
|
||||
|
||||
pub use wgpu::{Backends, Features as WgpuFeatures, Limits as WgpuLimits, PowerPreference};
|
||||
|
||||
/// Configures the priority used when automatically configuring the features/limits of `wgpu`.
|
||||
#[derive(Clone)]
|
||||
pub enum WgpuOptionsPriority {
|
||||
pub enum WgpuSettingsPriority {
|
||||
/// WebGPU default features and limits
|
||||
Compatibility,
|
||||
/// The maximum supported features and limits of the adapter and backend
|
||||
Functionality,
|
||||
/// WebGPU default limits plus additional constraints in order to be compatible with WebGL2
|
||||
WebGL2,
|
||||
}
|
||||
|
||||
/// Provides configuration for renderer initialization. Use [`RenderDevice::features`](crate::renderer::RenderDevice::features),
|
||||
/// [`RenderDevice::limits`](crate::renderer::RenderDevice::limits), and the [`WgpuAdapterInfo`](crate::render_resource::WgpuAdapterInfo)
|
||||
/// resource to get runtime information about the actual adapter, backend, features, and limits.
|
||||
#[derive(Clone)]
|
||||
pub struct WgpuOptions {
|
||||
pub struct WgpuSettings {
|
||||
pub device_label: Option<Cow<'static, str>>,
|
||||
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 priority: WgpuSettingsPriority,
|
||||
/// The features to ensure are enabled regardless of what the adapter/backend supports.
|
||||
/// Setting these explicitly may cause renderer initialization to fail.
|
||||
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
|
||||
/// The imposed limits.
|
||||
pub limits: WgpuLimits,
|
||||
/// The constraints on limits allowed regardless of what the adapter/backend supports
|
||||
pub constrained_limits: Option<WgpuLimits>,
|
||||
}
|
||||
|
||||
impl Default for WgpuOptions {
|
||||
impl Default for WgpuSettings {
|
||||
fn default() -> Self {
|
||||
let default_backends = if cfg!(feature = "webgl") {
|
||||
Backends::GL
|
||||
@ -37,9 +43,10 @@ impl Default for WgpuOptions {
|
||||
|
||||
let backends = Some(wgpu::util::backend_bits_from_env().unwrap_or(default_backends));
|
||||
|
||||
let priority = options_priority_from_env().unwrap_or(WgpuOptionsPriority::Functionality);
|
||||
let priority = settings_priority_from_env().unwrap_or(WgpuSettingsPriority::Functionality);
|
||||
|
||||
let limits = if cfg!(feature = "webgl") || matches!(priority, WgpuOptionsPriority::WebGL2) {
|
||||
let limits = if cfg!(feature = "webgl") || matches!(priority, WgpuSettingsPriority::WebGL2)
|
||||
{
|
||||
wgpu::Limits::downlevel_webgl2_defaults()
|
||||
} else {
|
||||
#[allow(unused_mut)]
|
||||
@ -65,17 +72,17 @@ impl Default for WgpuOptions {
|
||||
}
|
||||
}
|
||||
|
||||
/// Get a features/limits priority from the environment variable `WGPU_OPTIONS_PRIO`
|
||||
pub fn options_priority_from_env() -> Option<WgpuOptionsPriority> {
|
||||
/// Get a features/limits priority from the environment variable `WGPU_SETTINGS_PRIO`
|
||||
pub fn settings_priority_from_env() -> Option<WgpuSettingsPriority> {
|
||||
Some(
|
||||
match std::env::var("WGPU_OPTIONS_PRIO")
|
||||
match std::env::var("WGPU_SETTINGS_PRIO")
|
||||
.as_deref()
|
||||
.map(str::to_lowercase)
|
||||
.as_deref()
|
||||
{
|
||||
Ok("compatibility") => WgpuOptionsPriority::Compatibility,
|
||||
Ok("functionality") => WgpuOptionsPriority::Functionality,
|
||||
Ok("webgl2") => WgpuOptionsPriority::WebGL2,
|
||||
Ok("compatibility") => WgpuSettingsPriority::Compatibility,
|
||||
Ok("functionality") => WgpuSettingsPriority::Functionality,
|
||||
Ok("webgl2") => WgpuSettingsPriority::WebGL2,
|
||||
_ => return None,
|
||||
},
|
||||
)
|
@ -1,13 +1,13 @@
|
||||
use bevy::{
|
||||
pbr::wireframe::{Wireframe, WireframeConfig, WireframePlugin},
|
||||
prelude::*,
|
||||
render::{options::WgpuOptions, render_resource::WgpuFeatures},
|
||||
render::{render_resource::WgpuFeatures, settings::WgpuSettings},
|
||||
};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(Msaa { samples: 4 })
|
||||
.insert_resource(WgpuOptions {
|
||||
.insert_resource(WgpuSettings {
|
||||
features: WgpuFeatures::POLYGON_MODE_LINE,
|
||||
..Default::default()
|
||||
})
|
||||
|
@ -1,8 +1,8 @@
|
||||
use bevy::{prelude::*, render::options::WgpuOptions};
|
||||
use bevy::{prelude::*, render::settings::WgpuSettings};
|
||||
|
||||
fn main() {
|
||||
App::new()
|
||||
.insert_resource(WgpuOptions {
|
||||
.insert_resource(WgpuSettings {
|
||||
backends: None,
|
||||
..Default::default()
|
||||
})
|
||||
|
Loading…
Reference in New Issue
Block a user