diff --git a/Cargo.toml b/Cargo.toml index bde47050a9..d262889ab4 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -463,6 +463,9 @@ tonemapping_luts = ["bevy_internal/tonemapping_luts", "ktx2", "bevy_image/zstd"] # Include SMAA Look Up Tables KTX2 Files smaa_luts = ["bevy_internal/smaa_luts"] +# NVIDIA Deep Learning Super Sampling +dlss = ["bevy_internal/dlss"] + # Enable AccessKit on Unix backends (currently only works with experimental screen readers and forks.) accesskit_unix = ["bevy_internal/accesskit_unix"] diff --git a/crates/bevy_anti_aliasing/Cargo.toml b/crates/bevy_anti_aliasing/Cargo.toml index 8c32d70fff..f3000b5c98 100644 --- a/crates/bevy_anti_aliasing/Cargo.toml +++ b/crates/bevy_anti_aliasing/Cargo.toml @@ -13,6 +13,7 @@ trace = [] webgl = [] webgpu = [] smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"] +dlss = ["dep:dlss_wgpu"] [dependencies] # bevy @@ -30,6 +31,7 @@ bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" } # other tracing = { version = "0.1", default-features = false, features = ["std"] } +dlss_wgpu = { path = "../../../dlss_wgpu", optional = true } [lints] workspace = true diff --git a/crates/bevy_anti_aliasing/src/dlss/extract.rs b/crates/bevy_anti_aliasing/src/dlss/extract.rs new file mode 100644 index 0000000000..418c2fbf89 --- /dev/null +++ b/crates/bevy_anti_aliasing/src/dlss/extract.rs @@ -0,0 +1,29 @@ +use super::{prepare::ViewDlssContext, Dlss}; +use bevy_ecs::{ + query::With, + system::{Commands, ResMut}, +}; +use bevy_render::{ + camera::{Camera, MainPassResolutionOverride, Projection}, + sync_world::RenderEntity, + view::Hdr, + MainWorld, +}; + +pub fn extract_dlss(mut commands: Commands, mut main_world: ResMut) { + let mut cameras_3d = main_world + .query_filtered::<(RenderEntity, &Camera, &Projection, Option<&mut Dlss>), With>(); + + for (entity, camera, camera_projection, mut dlss) in cameras_3d.iter_mut(&mut main_world) { + let has_perspective_projection = matches!(camera_projection, Projection::Perspective(_)); + let mut entity_commands = commands + .get_entity(entity) + .expect("Camera entity wasn't synced."); + if dlss.is_some() && camera.is_active && has_perspective_projection { + entity_commands.insert(dlss.as_deref().unwrap().clone()); + dlss.as_mut().unwrap().reset = false; + } else { + entity_commands.remove::<(Dlss, ViewDlssContext, MainPassResolutionOverride)>(); + } + } +} diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs new file mode 100644 index 0000000000..186ae26f04 --- /dev/null +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -0,0 +1,105 @@ +mod extract; +mod node; +mod prepare; + +use bevy_app::{App, Plugin}; +use bevy_core_pipeline::{ + core_3d::graph::{Core3d, Node3d}, + prepass::{DepthPrepass, MotionVectorPrepass}, +}; +use bevy_ecs::{ + component::Component, prelude::ReflectComponent, resource::Resource, + schedule::IntoScheduleConfigs, +}; +use bevy_reflect::{prelude::ReflectDefault, reflect_remote, Reflect}; +use bevy_render::{ + camera::{MipBias, TemporalJitter}, + render_graph::{RenderGraphApp, ViewNodeRunner}, + renderer::RenderDevice, + view::{prepare_view_targets, prepare_view_uniforms, Hdr}, + ExtractSchedule, Render, RenderApp, RenderSystems, +}; +use std::sync::{Arc, Mutex}; +use tracing::info; + +pub use bevy_render::{DlssProjectId, DlssSupported}; +pub use dlss_wgpu::DlssPerfQualityMode; + +pub struct DlssPlugin; + +impl Plugin for DlssPlugin { + fn build(&self, app: &mut App) { + app.register_type::(); + } + + fn finish(&self, app: &mut App) { + if app.world().get_resource::().is_none() { + info!("DLSS is not supported on this system"); + return; + } + + let dlss_project_id = app.world().resource::().0; + + let render_app = app.get_sub_app_mut(RenderApp).unwrap(); + let render_device = render_app.world().resource::().clone(); + + let dlss_sdk = + dlss_wgpu::DlssSdk::new(dlss_project_id, render_device.wgpu_device().clone()); + if dlss_sdk.is_err() { + app.world_mut().remove_resource::(); + info!("DLSS is not supported on this system"); + return; + } + + render_app + .insert_resource(DlssSdk(dlss_sdk.unwrap())) + .add_systems(ExtractSchedule, extract::extract_dlss) + .add_systems( + Render, + prepare::configure_dlss_view_targets + .in_set(RenderSystems::ManageViews) + .before(prepare_view_targets), + ) + .add_systems( + Render, + prepare::prepare_dlss + .in_set(RenderSystems::PrepareResources) + .before(prepare_view_uniforms), + ) + .add_render_graph_node::>(Core3d, Node3d::Dlss) + .add_render_graph_edges( + Core3d, + ( + Node3d::EndMainPass, + Node3d::MotionBlur, // Running before DLSS reduces edge artifacts and noise + Node3d::Dlss, + Node3d::Bloom, + Node3d::Tonemapping, + ), + ); + } +} + +#[derive(Component, Reflect, Clone, Default)] +#[reflect(Component, Default)] +#[require(TemporalJitter, MipBias, DepthPrepass, MotionVectorPrepass, Hdr)] +pub struct Dlss { + #[reflect(remote = DlssPerfQualityModeRemoteReflect)] + pub perf_quality_mode: DlssPerfQualityMode, + pub reset: bool, +} + +#[reflect_remote(DlssPerfQualityMode)] +#[derive(Default)] +enum DlssPerfQualityModeRemoteReflect { + #[default] + Auto, + Dlaa, + Quality, + Balanced, + Performance, + UltraPerformance, +} + +#[derive(Resource)] +struct DlssSdk(Arc>); diff --git a/crates/bevy_anti_aliasing/src/dlss/node.rs b/crates/bevy_anti_aliasing/src/dlss/node.rs new file mode 100644 index 0000000000..f7c4e9dd9b --- /dev/null +++ b/crates/bevy_anti_aliasing/src/dlss/node.rs @@ -0,0 +1,85 @@ +use super::{prepare::ViewDlssContext, Dlss}; +use bevy_core_pipeline::prepass::ViewPrepassTextures; +use bevy_ecs::{query::QueryItem, world::World}; +use bevy_render::{ + camera::{MainPassResolutionOverride, TemporalJitter}, + render_graph::{NodeRunError, RenderGraphContext, ViewNode}, + renderer::{RenderAdapter, RenderContext}, + view::ViewTarget, +}; +use dlss_wgpu::{DlssExposure, DlssRenderParameters, DlssTexture}; + +#[derive(Default)] +pub struct DlssNode; + +impl ViewNode for DlssNode { + type ViewQuery = ( + &'static Dlss, + &'static ViewDlssContext, + &'static MainPassResolutionOverride, + &'static TemporalJitter, + &'static ViewTarget, + &'static ViewPrepassTextures, + ); + + fn run( + &self, + _graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + ( + dlss, + dlss_context, + viewport_override, + temporal_jitter, + view_target, + prepass_textures, + ): QueryItem, + world: &World, + ) -> Result<(), NodeRunError> { + let adapter = world.resource::(); + let (Some(prepass_motion_vectors_texture), Some(prepass_depth_texture)) = + (&prepass_textures.motion_vectors, &prepass_textures.depth) + else { + return Ok(()); + }; + + let view_target = view_target.post_process_write(); + + let render_resolution = viewport_override.0; + let render_parameters = DlssRenderParameters { + color: DlssTexture { + texture: &view_target.source_texture, + view: &view_target.source, + }, + depth: DlssTexture { + texture: &prepass_depth_texture.texture.texture, + view: &prepass_depth_texture.texture.default_view, + }, + motion_vectors: DlssTexture { + texture: &prepass_motion_vectors_texture.texture.texture, + view: &prepass_motion_vectors_texture.texture.default_view, + }, + exposure: DlssExposure::Automatic, // TODO + bias: None, // TODO + dlss_output: DlssTexture { + texture: &view_target.destination_texture, + view: &view_target.destination, + }, + reset: dlss.reset, + jitter_offset: -temporal_jitter.offset, + partial_texture_size: Some(render_resolution), + motion_vector_scale: Some(-render_resolution.as_vec2()), + }; + + let command_encoder = render_context.command_encoder(); + let mut dlss_context = dlss_context.context.lock().unwrap(); + + command_encoder.push_debug_group("dlss"); + dlss_context + .render(render_parameters, command_encoder, &adapter) + .expect("Failed to render DLSS"); + command_encoder.pop_debug_group(); + + Ok(()) + } +} diff --git a/crates/bevy_anti_aliasing/src/dlss/prepare.rs b/crates/bevy_anti_aliasing/src/dlss/prepare.rs new file mode 100644 index 0000000000..77a3eb5f32 --- /dev/null +++ b/crates/bevy_anti_aliasing/src/dlss/prepare.rs @@ -0,0 +1,111 @@ +use super::{Dlss, DlssSdk}; +use bevy_core_pipeline::{ + core_3d::Camera3d, + prepass::{DepthPrepass, MotionVectorPrepass}, +}; +use bevy_diagnostic::FrameCount; +use bevy_ecs::{ + component::Component, + entity::Entity, + query::With, + system::{Commands, Query, Res}, +}; +use bevy_math::Vec4Swizzles; +use bevy_render::{ + camera::{CameraMainTextureUsages, MainPassResolutionOverride, MipBias, TemporalJitter}, + render_resource::TextureUsages, + renderer::{RenderDevice, RenderQueue}, + view::ExtractedView, +}; +use dlss_wgpu::{DlssContext, DlssFeatureFlags, DlssPerfQualityMode}; +use std::sync::{Arc, Mutex}; + +pub fn prepare_dlss( + mut query: Query< + ( + Entity, + &ExtractedView, + &Dlss, + &mut TemporalJitter, + &mut MipBias, + Option<&mut ViewDlssContext>, + ), + ( + With, + With, + With, + With, + ), + >, + dlss_sdk: Res, + render_device: Res, + render_queue: Res, + frame_count: Res, + mut commands: Commands, +) { + for (entity, view, dlss, mut temporal_jitter, mut mip_bias, mut dlss_context) in &mut query { + let upscaled_resolution = view.viewport.zw(); + + let dlss_feature_flags = DlssFeatureFlags::LowResolutionMotionVectors + | DlssFeatureFlags::InvertedDepth + | DlssFeatureFlags::HighDynamicRange + | DlssFeatureFlags::AutoExposure; // TODO + + match dlss_context.as_deref_mut() { + Some(dlss_context) + if upscaled_resolution + == dlss_context.context.lock().unwrap().upscaled_resolution() + && dlss.perf_quality_mode == dlss_context.perf_quality_mode + && dlss_feature_flags == dlss_context.feature_flags => + { + let dlss_context = dlss_context.context.lock().unwrap(); + temporal_jitter.offset = + dlss_context.suggested_jitter(frame_count.0, dlss_context.render_resolution()); + } + _ => { + let dlss_context = DlssContext::new( + upscaled_resolution, + dlss.perf_quality_mode, + dlss_feature_flags, + Arc::clone(&dlss_sdk.0), + render_device.wgpu_device(), + &render_queue, + ) + .expect("Failed to create DlssContext"); + + let render_resolution = dlss_context.render_resolution(); + temporal_jitter.offset = + dlss_context.suggested_jitter(frame_count.0, render_resolution); + mip_bias.0 = dlss_context.suggested_mip_bias(render_resolution); + + commands.entity(entity).insert(( + ViewDlssContext { + context: Mutex::new(dlss_context), + perf_quality_mode: dlss.perf_quality_mode, + feature_flags: dlss_feature_flags, + }, + MainPassResolutionOverride(render_resolution), + )); + } + } + } +} + +#[derive(Component)] +pub struct ViewDlssContext { + pub context: Mutex, + pub perf_quality_mode: DlssPerfQualityMode, + pub feature_flags: DlssFeatureFlags, +} + +pub fn configure_dlss_view_targets( + mut view_targets: Query<(&mut Camera3d, &mut CameraMainTextureUsages), With>, +) { + for (mut camera_3d, mut camera_main_texture_usages) in view_targets.iter_mut() { + camera_main_texture_usages.0 |= TextureUsages::STORAGE_BINDING; + + let mut depth_texture_usages = TextureUsages::from(camera_3d.depth_texture_usages); + depth_texture_usages |= TextureUsages::TEXTURE_BINDING; + camera_3d.depth_texture_usages = depth_texture_usages.into(); + } +} diff --git a/crates/bevy_anti_aliasing/src/lib.rs b/crates/bevy_anti_aliasing/src/lib.rs index 12b7982cb5..6fb40d5bc3 100644 --- a/crates/bevy_anti_aliasing/src/lib.rs +++ b/crates/bevy_anti_aliasing/src/lib.rs @@ -13,6 +13,8 @@ use smaa::SmaaPlugin; use taa::TemporalAntiAliasPlugin; pub mod contrast_adaptive_sharpening; +#[cfg(feature = "dlss")] +pub mod dlss; pub mod fxaa; pub mod smaa; pub mod taa; @@ -21,6 +23,13 @@ pub mod taa; pub struct AntiAliasingPlugin; impl Plugin for AntiAliasingPlugin { fn build(&self, app: &mut bevy_app::App) { - app.add_plugins((FxaaPlugin, SmaaPlugin, TemporalAntiAliasPlugin, CasPlugin)); + app.add_plugins(( + FxaaPlugin, + SmaaPlugin, + TemporalAntiAliasPlugin, + CasPlugin, + #[cfg(feature = "dlss")] + dlss::DlssPlugin, + )); } } diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index 6fcccd3a72..6dc995813e 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -31,6 +31,7 @@ pub mod graph { Wireframe, LateDownsampleDepth, Taa, + Dlss, MotionBlur, Bloom, AutoExposure, diff --git a/crates/bevy_internal/Cargo.toml b/crates/bevy_internal/Cargo.toml index f418bc0edc..7f3f73c2d6 100644 --- a/crates/bevy_internal/Cargo.toml +++ b/crates/bevy_internal/Cargo.toml @@ -73,6 +73,9 @@ tonemapping_luts = ["bevy_core_pipeline/tonemapping_luts"] # Include SMAA LUT KTX2 Files smaa_luts = ["bevy_anti_aliasing/smaa_luts"] +# NVIDIA Deep Learning Super Sampling +dlss = ["bevy_anti_aliasing/dlss", "bevy_render/dlss"] + # Audio format support (vorbis is enabled by default) flac = ["bevy_audio/flac"] mp3 = ["bevy_audio/mp3"] diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index a657da4f0e..98afa223b2 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -51,6 +51,7 @@ webgpu = ["wgpu/webgpu"] detailed_trace = [] ## Adds serialization support through `serde`. serialize = ["bevy_mesh/serialize"] +dlss = ["dep:dlss_wgpu"] [dependencies] # bevy @@ -100,6 +101,7 @@ wgpu = { version = "25", default-features = false, features = [ "fragile-send-sync-non-atomic-wasm", ] } naga = { version = "25", features = ["wgsl-in"] } +dlss_wgpu = { path = "../../../dlss_wgpu", optional = true } serde = { version = "1", features = ["derive"] } bytemuck = { version = "1.5", features = ["derive", "must_cast"] } downcast-rs = { version = "2", default-features = false, features = ["std"] } diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 8bf7f41195..e662a1c3c6 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -104,7 +104,7 @@ use crate::{ mesh::{MeshPlugin, MorphPlugin, RenderMesh}, render_asset::prepare_assets, render_resource::{PipelineCache, Shader, ShaderLoader}, - renderer::{render_system, RenderInstance}, + renderer::render_system, settings::RenderCreation, storage::StoragePlugin, view::{ViewPlugin, WindowRenderPlugin}, @@ -113,11 +113,9 @@ use alloc::sync::Arc; use bevy_app::{App, AppLabel, Plugin, SubApp}; use bevy_asset::{AssetApp, AssetServer}; use bevy_ecs::{prelude::*, schedule::ScheduleLabel}; -use bevy_utils::WgpuWrapper; use bitflags::bitflags; use core::ops::{Deref, DerefMut}; use std::sync::Mutex; -use tracing::debug; /// Inline shader as an `embedded_asset` and load it permanently. /// @@ -346,6 +344,14 @@ impl Plugin for RenderPlugin { .ok() .cloned(); let settings = render_creation.clone(); + + #[cfg(feature = "dlss")] + let dlss_project_id = app + .world() + .get_resource::() + .expect("The `dlss` feature is enabled, but DlssProjectId was not added to the App before DefaultPlugins.") + .0; + let async_renderer = async move { let instance = wgpu::Instance::new(&wgpu::InstanceDescriptor { backends, @@ -385,24 +391,16 @@ impl Plugin for RenderPlugin { ..Default::default() }; - let (device, queue, adapter_info, render_adapter) = - renderer::initialize_renderer( - &instance, - &settings, - &request_adapter_options, - ) - .await; - debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); - debug!("Configured wgpu adapter Features: {:#?}", device.features()); - let mut future_render_resources_inner = - future_render_resources_wrapper.lock().unwrap(); - *future_render_resources_inner = Some(RenderResources( - device, - queue, - adapter_info, - render_adapter, - RenderInstance(Arc::new(WgpuWrapper::new(instance))), - )); + let render_resources = renderer::initialize_renderer( + instance, + &settings, + &request_adapter_options, + #[cfg(feature = "dlss")] + dlss_project_id, + ) + .await; + + *future_render_resources_wrapper.lock().unwrap() = Some(render_resources); }; // In wasm, spawn a task and detach it for execution #[cfg(target_arch = "wasm32")] @@ -475,6 +473,16 @@ impl Plugin for RenderPlugin { if let Some(future_render_resources) = app.world_mut().remove_resource::() { + #[cfg(feature = "dlss")] + let RenderResources( + device, + queue, + adapter_info, + render_adapter, + instance, + dlss_available, + ) = future_render_resources.0.lock().unwrap().take().unwrap(); + #[cfg(not(feature = "dlss"))] let RenderResources(device, queue, adapter_info, render_adapter, instance) = future_render_resources.0.lock().unwrap().take().unwrap(); @@ -488,6 +496,11 @@ impl Plugin for RenderPlugin { .insert_resource(render_adapter.clone()) .insert_resource(compressed_image_format_support); + #[cfg(feature = "dlss")] + if let Some(dlss_available) = dlss_available { + app.insert_resource(dlss_available); + } + let render_app = app.sub_app_mut(RenderApp); render_app @@ -632,3 +645,11 @@ pub fn get_mali_driver_version(adapter: &RenderAdapter) -> Option { None } + +#[cfg(feature = "dlss")] +#[derive(Resource)] +pub struct DlssProjectId(pub bevy_asset::uuid::Uuid); + +#[cfg(feature = "dlss")] +#[derive(Resource, Clone, Copy)] +pub struct DlssSupported; diff --git a/crates/bevy_render/src/renderer/mod.rs b/crates/bevy_render/src/renderer/mod.rs index 5df4967757..c424272148 100644 --- a/crates/bevy_render/src/renderer/mod.rs +++ b/crates/bevy_render/src/renderer/mod.rs @@ -7,14 +7,14 @@ use bevy_tasks::ComputeTaskPool; use bevy_utils::WgpuWrapper; pub use graph_runner::*; pub use render_device::*; -use tracing::{error, info, info_span, warn}; +use tracing::{debug, error, info, info_span, warn}; use crate::{ diagnostic::{internal::DiagnosticsRecorder, RecordDiagnostics}, render_graph::RenderGraph, render_phase::TrackedRenderPass, render_resource::RenderPassDescriptor, - settings::{WgpuSettings, WgpuSettingsPriority}, + settings::{RenderResources, WgpuSettings, WgpuSettingsPriority}, view::{ExtractedWindows, ViewTarget}, }; use alloc::sync::Arc; @@ -148,10 +148,11 @@ const GPU_NOT_FOUND_ERROR_MESSAGE: &str = if cfg!(target_os = "linux") { /// Initializes the renderer by retrieving and preparing the GPU instance, device and queue /// for the specified backend. pub async fn initialize_renderer( - instance: &Instance, + instance: Instance, options: &WgpuSettings, request_adapter_options: &RequestAdapterOptions<'_, '_>, -) -> (RenderDevice, RenderQueue, RenderAdapterInfo, RenderAdapter) { + #[cfg(feature = "dlss")] dlss_project_id: bevy_asset::uuid::Uuid, +) -> RenderResources { let adapter = instance .request_adapter(request_adapter_options) .await @@ -309,24 +310,47 @@ pub async fn initialize_renderer( }; } - let (device, queue) = adapter - .request_device(&wgpu::DeviceDescriptor { - label: options.device_label.as_ref().map(AsRef::as_ref), - required_features: features, - required_limits: limits, - memory_hints: options.memory_hints.clone(), - // See https://github.com/gfx-rs/wgpu/issues/5974 - trace: Trace::Off, - }) - .await - .unwrap(); - let queue = Arc::new(WgpuWrapper::new(queue)); - let adapter = Arc::new(WgpuWrapper::new(adapter)); - ( + let device_descriptor = wgpu::DeviceDescriptor { + label: options.device_label.as_ref().map(AsRef::as_ref), + required_features: features, + required_limits: limits, + memory_hints: options.memory_hints.clone(), + // See https://github.com/gfx-rs/wgpu/issues/5974 + trace: Trace::Off, + }; + + #[cfg(not(feature = "dlss"))] + let (device, queue) = adapter.request_device(&device_descriptor).await.unwrap(); + + #[cfg(feature = "dlss")] + let mut dlss_supported = false; + #[cfg(feature = "dlss")] + let (device, queue) = { + match dlss_wgpu::request_device(dlss_project_id, &adapter, &device_descriptor) { + Ok(x) => { + dlss_supported = true; + x + } + // Fallback to standard device request + Err(_) => adapter.request_device(&device_descriptor).await.unwrap(), + } + }; + + debug!("Configured wgpu adapter Limits: {:#?}", device.limits()); + debug!("Configured wgpu adapter Features: {:#?}", device.features()); + + RenderResources( RenderDevice::from(device), - RenderQueue(queue), + RenderQueue(Arc::new(WgpuWrapper::new(queue))), RenderAdapterInfo(WgpuWrapper::new(adapter_info)), - RenderAdapter(adapter), + RenderAdapter(Arc::new(WgpuWrapper::new(adapter))), + RenderInstance(Arc::new(WgpuWrapper::new(instance))), + #[cfg(feature = "dlss")] + if dlss_supported { + Some(crate::DlssSupported) + } else { + None + }, ) } diff --git a/crates/bevy_render/src/settings.rs b/crates/bevy_render/src/settings.rs index 715bbb35f8..e33e87db63 100644 --- a/crates/bevy_render/src/settings.rs +++ b/crates/bevy_render/src/settings.rs @@ -142,11 +142,12 @@ impl Default for WgpuSettings { #[derive(Clone)] pub struct RenderResources( - pub RenderDevice, - pub RenderQueue, - pub RenderAdapterInfo, - pub RenderAdapter, - pub RenderInstance, + pub(crate) RenderDevice, + pub(crate) RenderQueue, + pub(crate) RenderAdapterInfo, + pub(crate) RenderAdapter, + pub(crate) RenderInstance, + #[cfg(feature = "dlss")] pub(crate) Option, ); /// An enum describing how the renderer will initialize resources. This is used when creating the [`RenderPlugin`](crate::RenderPlugin). @@ -170,7 +171,16 @@ impl RenderCreation { adapter: RenderAdapter, instance: RenderInstance, ) -> Self { - RenderResources(device, queue, adapter_info, adapter, instance).into() + RenderResources( + device, + queue, + adapter_info, + adapter, + instance, + #[cfg(feature = "dlss")] + None, + ) + .into() } } diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index a348b361c7..e08631d390 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -8,7 +8,8 @@ pub use window::*; use crate::{ camera::{ CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, ExtractedCamera, - ManualTextureViews, MipBias, NormalizedRenderTarget, TemporalJitter, + MainPassResolutionOverride, ManualTextureViews, MipBias, NormalizedRenderTarget, + TemporalJitter, }, experimental::occlusion_culling::OcclusionCulling, extract_component::ExtractComponentPlugin, @@ -912,6 +913,7 @@ pub fn prepare_view_uniforms( Option<&Frustum>, Option<&TemporalJitter>, Option<&MipBias>, + Option<&MainPassResolutionOverride>, )>, frame_count: Res, ) { @@ -924,13 +926,23 @@ pub fn prepare_view_uniforms( else { return; }; - for (entity, extracted_camera, extracted_view, frustum, temporal_jitter, mip_bias) in &views { + for ( + entity, + extracted_camera, + extracted_view, + frustum, + temporal_jitter, + mip_bias, + viewport_override, + ) in &views + { let viewport = extracted_view.viewport.as_vec4(); let unjittered_projection = extracted_view.clip_from_view; let mut clip_from_view = unjittered_projection; if let Some(temporal_jitter) = temporal_jitter { - temporal_jitter.jitter_projection(&mut clip_from_view, viewport.zw()); + let jitter_view_size = viewport_override.map_or(viewport.zw(), |v| v.0.as_vec2()); + temporal_jitter.jitter_projection(&mut clip_from_view, jitter_view_size); } let view_from_clip = clip_from_view.inverse(); diff --git a/docs/cargo_features.md b/docs/cargo_features.md index 0b12f1ffec..9c770d98b2 100644 --- a/docs/cargo_features.md +++ b/docs/cargo_features.md @@ -79,6 +79,7 @@ The default feature set enables most of the expected features of a game engine, |debug_glam_assert|Enable assertions in debug builds to check the validity of parameters passed to glam| |default_no_std|Recommended defaults for no_std applications| |detailed_trace|Enable detailed trace event logging. These trace events are expensive even when off, thus they require compile time opt-in| +|dlss|NVIDIA Deep Learning Super Sampling| |dynamic_linking|Force dynamic linking, which improves iterative compile times| |embedded_watcher|Enables watching in memory asset providers for Bevy Asset hot-reloading| |experimental_bevy_feathers|Feathers widget collection.| diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index fd93625c0e..991c61414e 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -21,25 +21,58 @@ use bevy::{ }, }; +#[cfg(feature = "dlss")] +use bevy::anti_aliasing::dlss::{Dlss, DlssPerfQualityMode, DlssProjectId, DlssSupported}; + fn main() { - App::new() - .add_plugins(DefaultPlugins) + let mut app = App::new(); + + #[cfg(feature = "dlss")] + app.insert_resource(DlssProjectId(bevy_asset::uuid::uuid!( + "5417916c-0291-4e3f-8f65-326c1858ab96" + ))); + + app.add_plugins(DefaultPlugins) .add_systems(Startup, setup) - .add_systems(Update, (modify_aa, modify_sharpening, update_ui)) - .run(); + .add_systems(Update, (modify_aa, modify_sharpening, update_ui)); + + app.run(); } type TaaComponents = ( TemporalAntiAliasing, TemporalJitter, MipBias, + MipBias, DepthPrepass, MotionVectorPrepass, ); +#[cfg(feature = "dlss")] +type DlssComponents = ( + Dlss, + TemporalJitter, + MipBias, + DepthPrepass, + MotionVectorPrepass, +); +#[cfg(not(feature = "dlss"))] +type DlssComponents = (); + fn modify_aa( keys: Res>, - camera: Single< + #[cfg(feature = "dlss")] camera: Single< + ( + Entity, + Option<&mut Fxaa>, + Option<&mut Smaa>, + Option<&TemporalAntiAliasing>, + &mut Msaa, + Option<&mut Dlss>, + ), + With, + >, + #[cfg(not(feature = "dlss"))] camera: Single< ( Entity, Option<&mut Fxaa>, @@ -49,8 +82,12 @@ fn modify_aa( ), With, >, + #[cfg(feature = "dlss")] dlss_supported: Option>, mut commands: Commands, ) { + #[cfg(feature = "dlss")] + let (camera_entity, fxaa, smaa, taa, mut msaa, dlss) = camera.into_inner(); + #[cfg(not(feature = "dlss"))] let (camera_entity, fxaa, smaa, taa, mut msaa) = camera.into_inner(); let mut camera = commands.entity(camera_entity); @@ -60,7 +97,8 @@ fn modify_aa( camera .remove::() .remove::() - .remove::(); + .remove::() + .remove::(); } // MSAA @@ -68,7 +106,8 @@ fn modify_aa( camera .remove::() .remove::() - .remove::(); + .remove::() + .remove::(); *msaa = Msaa::Sample4; } @@ -92,6 +131,7 @@ fn modify_aa( camera .remove::() .remove::() + .remove::() .insert(Fxaa::default()); } @@ -125,6 +165,7 @@ fn modify_aa( camera .remove::() .remove::() + .remove::() .insert(Smaa::default()); } @@ -150,8 +191,43 @@ fn modify_aa( camera .remove::() .remove::() + .remove::() .insert(TemporalAntiAliasing::default()); } + + // DLSS + #[cfg(feature = "dlss")] + if keys.just_pressed(KeyCode::Digit6) && dlss.is_none() && dlss_supported.is_some() { + *msaa = Msaa::Off; + camera + .remove::() + .remove::() + .remove::() + .insert(Dlss::default()); + } + + // DLSS Settings + #[cfg(feature = "dlss")] + if let Some(mut dlss) = dlss { + if keys.just_pressed(KeyCode::KeyZ) { + dlss.perf_quality_mode = DlssPerfQualityMode::Auto; + } + if keys.just_pressed(KeyCode::KeyX) { + dlss.perf_quality_mode = DlssPerfQualityMode::UltraPerformance; + } + if keys.just_pressed(KeyCode::KeyC) { + dlss.perf_quality_mode = DlssPerfQualityMode::Performance; + } + if keys.just_pressed(KeyCode::KeyV) { + dlss.perf_quality_mode = DlssPerfQualityMode::Balanced; + } + if keys.just_pressed(KeyCode::KeyB) { + dlss.perf_quality_mode = DlssPerfQualityMode::Quality; + } + if keys.just_pressed(KeyCode::KeyN) { + dlss.perf_quality_mode = DlssPerfQualityMode::Dlaa; + } + } } fn modify_sharpening( @@ -179,7 +255,18 @@ fn modify_sharpening( } fn update_ui( - camera: Single< + #[cfg(feature = "dlss")] camera: Single< + ( + Option<&Fxaa>, + Option<&Smaa>, + Option<&TemporalAntiAliasing>, + &ContrastAdaptiveSharpening, + &Msaa, + Option<&Dlss>, + ), + With, + >, + #[cfg(not(feature = "dlss"))] camera: Single< ( Option<&Fxaa>, Option<&Smaa>, @@ -190,22 +277,35 @@ fn update_ui( With, >, mut ui: Single<&mut Text>, + #[cfg(feature = "dlss")] dlss_supported: Option>, ) { + #[cfg(feature = "dlss")] + let (fxaa, smaa, taa, cas, msaa, dlss) = *camera; + #[cfg(not(feature = "dlss"))] let (fxaa, smaa, taa, cas, msaa) = *camera; let ui = &mut ui.0; *ui = "Antialias Method\n".to_string(); + #[cfg(feature = "dlss")] + let dlss_none = dlss.is_none(); + #[cfg(not(feature = "dlss"))] + let dlss_none = true; + draw_selectable_menu_item( ui, "No AA", '1', - *msaa == Msaa::Off && fxaa.is_none() && taa.is_none() && smaa.is_none(), + *msaa == Msaa::Off && fxaa.is_none() && taa.is_none() && smaa.is_none() && dlss_none, ); draw_selectable_menu_item(ui, "MSAA", '2', *msaa != Msaa::Off); draw_selectable_menu_item(ui, "FXAA", '3', fxaa.is_some()); draw_selectable_menu_item(ui, "SMAA", '4', smaa.is_some()); draw_selectable_menu_item(ui, "TAA", '5', taa.is_some()); + #[cfg(feature = "dlss")] + if dlss_supported.is_some() { + draw_selectable_menu_item(ui, "DLSS", '6', dlss.is_some()); + } if *msaa != Msaa::Off { ui.push_str("\n----------\n\nSample Count\n"); @@ -241,6 +341,28 @@ fn update_ui( draw_selectable_menu_item(ui, "Ultra", 'R', smaa.preset == SmaaPreset::Ultra); } + #[cfg(feature = "dlss")] + if let Some(dlss) = dlss { + let pqm = dlss.perf_quality_mode; + ui.push_str("\n----------\n\nQuality\n"); + draw_selectable_menu_item(ui, "Auto", 'Z', pqm == DlssPerfQualityMode::Auto); + draw_selectable_menu_item( + ui, + "UltraPerformance", + 'X', + pqm == DlssPerfQualityMode::UltraPerformance, + ); + draw_selectable_menu_item( + ui, + "Performance", + 'C', + pqm == DlssPerfQualityMode::Performance, + ); + draw_selectable_menu_item(ui, "Balanced", 'V', pqm == DlssPerfQualityMode::Balanced); + draw_selectable_menu_item(ui, "Quality", 'B', pqm == DlssPerfQualityMode::Quality); + draw_selectable_menu_item(ui, "DLAA", 'N', pqm == DlssPerfQualityMode::Dlaa); + } + ui.push_str("\n----------\n\n"); draw_selectable_menu_item(ui, "Sharpening", '0', cas.enabled); @@ -260,7 +382,7 @@ fn setup( ) { // Plane commands.spawn(( - Mesh3d(meshes.add(Plane3d::default().mesh().size(50.0, 50.0))), + Mesh3d(meshes.add(Plane3d::default().mesh().size(20.0, 20.0))), MeshMaterial3d(materials.add(Color::srgb(0.1, 0.2, 0.1))), ));