From c75b2b4f264dae7305b84bea175ba2b463c89883 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 29 Jun 2025 14:40:56 -0400 Subject: [PATCH 01/23] Initial DLSS implementation --- Cargo.toml | 3 + crates/bevy_anti_aliasing/Cargo.toml | 2 + crates/bevy_anti_aliasing/src/dlss/extract.rs | 29 ++++ crates/bevy_anti_aliasing/src/dlss/mod.rs | 105 +++++++++++++ crates/bevy_anti_aliasing/src/dlss/node.rs | 85 +++++++++++ crates/bevy_anti_aliasing/src/dlss/prepare.rs | 111 ++++++++++++++ crates/bevy_anti_aliasing/src/lib.rs | 11 +- crates/bevy_core_pipeline/src/core_3d/mod.rs | 1 + crates/bevy_internal/Cargo.toml | 3 + crates/bevy_render/Cargo.toml | 2 + crates/bevy_render/src/lib.rs | 63 +++++--- crates/bevy_render/src/renderer/mod.rs | 64 +++++--- crates/bevy_render/src/settings.rs | 22 ++- crates/bevy_render/src/view/mod.rs | 18 ++- docs/cargo_features.md | 1 + examples/3d/anti_aliasing.rs | 142 ++++++++++++++++-- 16 files changed, 601 insertions(+), 61 deletions(-) create mode 100644 crates/bevy_anti_aliasing/src/dlss/extract.rs create mode 100644 crates/bevy_anti_aliasing/src/dlss/mod.rs create mode 100644 crates/bevy_anti_aliasing/src/dlss/node.rs create mode 100644 crates/bevy_anti_aliasing/src/dlss/prepare.rs diff --git a/Cargo.toml b/Cargo.toml index bde47050a98e9..d262889ab49b4 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 8c32d70fff0bb..f3000b5c98450 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 0000000000000..418c2fbf89458 --- /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 0000000000000..186ae26f04e98 --- /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 0000000000000..f7c4e9dd9be58 --- /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 0000000000000..77a3eb5f324a4 --- /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 12b7982cb57a1..6fb40d5bc3172 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 6fcccd3a72d88..6dc995813ea5c 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 f418bc0edccd6..7f3f73c2d6f72 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 a657da4f0e641..98afa223b2f08 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 8bf7f41195c53..e662a1c3c6e3a 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 5df4967757749..c424272148226 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 715bbb35f845b..e33e87db63bc5 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 a348b361c75ea..e08631d390faa 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 0b12f1ffecb54..9c770d98b2070 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 fd93625c0e634..991c61414e782 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -21,36 +21,73 @@ 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>, + Option<&mut Smaa>, + Option<&TemporalAntiAliasing>, + &mut Msaa, + ), + 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))), )); From af1b47c9c5fbb028d510ac21803429320587b266 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 29 Jun 2025 15:00:41 -0400 Subject: [PATCH 02/23] Update example description --- Cargo.toml | 2 +- examples/3d/anti_aliasing.rs | 2 +- examples/README.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index d262889ab49b4..0ef3e945d1c30 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1003,7 +1003,7 @@ doc-scrape-examples = true [package.metadata.example.anti_aliasing] name = "Anti-aliasing" -description = "Compares different anti-aliasing methods" +description = "Compares different anti-aliasing techniques supported by Bevy" category = "3D Rendering" wasm = false diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index 991c61414e782..3a97d44b6d2ed 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -1,4 +1,4 @@ -//! This example compares MSAA (Multi-Sample Anti-aliasing), FXAA (Fast Approximate Anti-aliasing), and TAA (Temporal Anti-aliasing). +//! Compares different anti-aliasing techniques supported by Bevy. use std::{f32::consts::PI, fmt::Write}; diff --git a/examples/README.md b/examples/README.md index a8b9bfb3b406b..fd27053ac6d16 100644 --- a/examples/README.md +++ b/examples/README.md @@ -142,7 +142,7 @@ Example | Description [3D Viewport To World](../examples/3d/3d_viewport_to_world.rs) | Demonstrates how to use the `Camera::viewport_to_world` method [Animated Material](../examples/3d/animated_material.rs) | Shows how to animate material properties [Anisotropy](../examples/3d/anisotropy.rs) | Displays an example model with anisotropy -[Anti-aliasing](../examples/3d/anti_aliasing.rs) | Compares different anti-aliasing methods +[Anti-aliasing](../examples/3d/anti_aliasing.rs) | Compares different anti-aliasing techniques supported by Bevy [Atmosphere](../examples/3d/atmosphere.rs) | A scene showcasing pbr atmospheric scattering [Atmospheric Fog](../examples/3d/atmospheric_fog.rs) | A scene showcasing the atmospheric fog effect [Auto Exposure](../examples/3d/auto_exposure.rs) | A scene showcasing auto exposure From eb8c77fceeb69a11fbd1978abcee0a0f59a69a9a Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 29 Jun 2025 15:30:18 -0400 Subject: [PATCH 03/23] Rename variable --- crates/bevy_anti_aliasing/src/dlss/node.rs | 4 ++-- crates/bevy_render/src/view/mod.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/crates/bevy_anti_aliasing/src/dlss/node.rs b/crates/bevy_anti_aliasing/src/dlss/node.rs index f7c4e9dd9be58..4ad6d4f10059f 100644 --- a/crates/bevy_anti_aliasing/src/dlss/node.rs +++ b/crates/bevy_anti_aliasing/src/dlss/node.rs @@ -29,7 +29,7 @@ impl ViewNode for DlssNode { ( dlss, dlss_context, - viewport_override, + resolution_override, temporal_jitter, view_target, prepass_textures, @@ -45,7 +45,7 @@ impl ViewNode for DlssNode { let view_target = view_target.post_process_write(); - let render_resolution = viewport_override.0; + let render_resolution = resolution_override.0; let render_parameters = DlssRenderParameters { color: DlssTexture { texture: &view_target.source_texture, diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index e08631d390faa..d54e694faf4d0 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -933,7 +933,7 @@ pub fn prepare_view_uniforms( frustum, temporal_jitter, mip_bias, - viewport_override, + resolution_override, ) in &views { let viewport = extracted_view.viewport.as_vec4(); @@ -941,7 +941,7 @@ pub fn prepare_view_uniforms( let mut clip_from_view = unjittered_projection; if let Some(temporal_jitter) = temporal_jitter { - let jitter_view_size = viewport_override.map_or(viewport.zw(), |v| v.0.as_vec2()); + let jitter_view_size = resolution_override.map_or(viewport.zw(), |v| v.0.as_vec2()); temporal_jitter.jitter_projection(&mut clip_from_view, jitter_view_size); } From 742ebfa8acaf9bb0bc785848110dc8edd18c36fb Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:15:29 -0400 Subject: [PATCH 04/23] Fix merge --- crates/bevy_anti_aliasing/src/dlss/mod.rs | 2 +- crates/bevy_render/src/view/mod.rs | 1 + examples/3d/anti_aliasing.rs | 1 - 3 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs index 186ae26f04e98..dae40e1082d32 100644 --- a/crates/bevy_anti_aliasing/src/dlss/mod.rs +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -14,7 +14,7 @@ use bevy_ecs::{ use bevy_reflect::{prelude::ReflectDefault, reflect_remote, Reflect}; use bevy_render::{ camera::{MipBias, TemporalJitter}, - render_graph::{RenderGraphApp, ViewNodeRunner}, + render_graph::{RenderGraphExt, ViewNodeRunner}, renderer::RenderDevice, view::{prepare_view_targets, prepare_view_uniforms, Hdr}, ExtractSchedule, Render, RenderApp, RenderSystems, diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index 97a1d1aedc397..839231e083bac 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -3,6 +3,7 @@ pub mod window; use bevy_camera::{ primitives::Frustum, CameraMainTextureUsages, ClearColor, ClearColorConfig, Exposure, + MainPassResolutionOverride, }; use bevy_diagnostic::FrameCount; pub use visibility::*; diff --git a/examples/3d/anti_aliasing.rs b/examples/3d/anti_aliasing.rs index 3a97d44b6d2ed..44632fd07ac1c 100644 --- a/examples/3d/anti_aliasing.rs +++ b/examples/3d/anti_aliasing.rs @@ -43,7 +43,6 @@ type TaaComponents = ( TemporalAntiAliasing, TemporalJitter, MipBias, - MipBias, DepthPrepass, MotionVectorPrepass, ); From 463c66d362f1f449194bc9e77a88a7240db1e6e7 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:20:27 -0400 Subject: [PATCH 05/23] Switch to git dep --- crates/bevy_anti_aliasing/Cargo.toml | 2 +- crates/bevy_render/Cargo.toml | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_anti_aliasing/Cargo.toml b/crates/bevy_anti_aliasing/Cargo.toml index f3000b5c98450..a8b51c8568fa9 100644 --- a/crates/bevy_anti_aliasing/Cargo.toml +++ b/crates/bevy_anti_aliasing/Cargo.toml @@ -31,7 +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 } +dlss_wgpu = { git = "https://github.com/JMS55/dlss_wgpu", optional = true } [lints] workspace = true diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index c68d11ab7200f..595abe76d018a 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -102,7 +102,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 } +dlss_wgpu = { git = "https://github.com/JMS55/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"] } From bf56980080fcd6ed7465bcb3925116805ba254f6 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 6 Jul 2025 17:53:36 -0400 Subject: [PATCH 06/23] Add release notes --- release-content/release-notes/dlss.md | 28 +++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) create mode 100644 release-content/release-notes/dlss.md diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md new file mode 100644 index 0000000000000..3c3c03e5a198d --- /dev/null +++ b/release-content/release-notes/dlss.md @@ -0,0 +1,28 @@ +--- +title: Deep Learning Super Sampling (DLSS) +authors: ["@JMS55"] +pull_requests: [19817, 19864] +--- + +For users with NVIDIA RTX GPUs, Bevy now offers yet another form of anti-aliasing: DLSS. + +Try it out by running Bevy's anti_aliasing example: `cargo run --example anti_aliasing --features dlss --release` (after performing setup from https://github.com/JMS55/dlss_wgpu). + +Compared to Bevy's built-in TAA, DLSS: +* Is much higher quality +* Supports upscaling in addition to anti-aliasing, leading to much cheaper render times, particularly when used with GPU-heavy features like Bevy Solari +* Requires a NVIDIA RTX GPU +* Requires running via the Vulkan backend on Windows/Linux (no macOS, web, or mobile support) + +To use DLSS in your app: +* See https://github.com/JMS55/dlss_wgpu for licensing requirements and setup instructions +* Enable Bevy's `dlss` feature +* Insert the `DlssProjectId` resource before `DefaultPlugins` when setting up your app +* Add the `Dlss` component to your camera entity, optionally setting a specific `DlssPerfQualityMode` (defaults to `Auto`) +* Optionally add sharpening via `ContrastAdaptiveSharpening` + +Other temporal upscalers like AMD's FidelityFX™ Super Resolution (FSR), Intel's Xe Super Sampling XeSS (XeSS), and Apple's MTLFXTemporalScaler are not integrated in this release. However they all use similiar APIs, and would not be a challenge to integrate in future releases. + +Support for other swapchain-related features like frame interpolation/extrapolation or latency redunction are not currently planned, but support for DLSS Ray Reconstruction for use in Bevy Solari _is_ planned for a future release. + +Special thanks to @cwfitzgerald for helping with the wgpu backend interop APIs. From 6b960b0d7f1dacacdb97e7905bbd1a556796bd64 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:03:36 -0400 Subject: [PATCH 07/23] Modify release notes --- release-content/release-notes/dlss.md | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 3c3c03e5a198d..2dfaab43b24b2 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -20,9 +20,12 @@ To use DLSS in your app: * Insert the `DlssProjectId` resource before `DefaultPlugins` when setting up your app * Add the `Dlss` component to your camera entity, optionally setting a specific `DlssPerfQualityMode` (defaults to `Auto`) * Optionally add sharpening via `ContrastAdaptiveSharpening` +* Custom rendering code, including third party crates, should account for the optional `MainPassResolutionOverride` to work with DLSS + +Note that DLSS integration is expected to have some bugs in this release related to certain rendering effects not respecting upscaling settings, and possible issues with transparencies or camera exposure. Please report any bugs encountered. Other temporal upscalers like AMD's FidelityFX™ Super Resolution (FSR), Intel's Xe Super Sampling XeSS (XeSS), and Apple's MTLFXTemporalScaler are not integrated in this release. However they all use similiar APIs, and would not be a challenge to integrate in future releases. -Support for other swapchain-related features like frame interpolation/extrapolation or latency redunction are not currently planned, but support for DLSS Ray Reconstruction for use in Bevy Solari _is_ planned for a future release. +Support for other swapchain-related features like frame interpolation/extrapolation, latency reduction, or dynamic resolution scaling are not currently planned, but support for DLSS Ray Reconstruction for use in Bevy Solari _is_ planned for a future release. Special thanks to @cwfitzgerald for helping with the wgpu backend interop APIs. From cf1a1f7b7f374cc659198b0262cd09de4aa2b02e Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Sun, 6 Jul 2025 18:07:16 -0400 Subject: [PATCH 08/23] Add note about DlssSupported --- release-content/release-notes/dlss.md | 1 + 1 file changed, 1 insertion(+) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 2dfaab43b24b2..9b8fef3849fcf 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -18,6 +18,7 @@ To use DLSS in your app: * See https://github.com/JMS55/dlss_wgpu for licensing requirements and setup instructions * Enable Bevy's `dlss` feature * Insert the `DlssProjectId` resource before `DefaultPlugins` when setting up your app +* Check for the presence of `Option>` at runtime to see if DLSS is supported on the current machine * Add the `Dlss` component to your camera entity, optionally setting a specific `DlssPerfQualityMode` (defaults to `Auto`) * Optionally add sharpening via `ContrastAdaptiveSharpening` * Custom rendering code, including third party crates, should account for the optional `MainPassResolutionOverride` to work with DLSS From 10c96e8301ce70e1433fe371e864efb124b88fdf Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 7 Jul 2025 22:27:11 -0400 Subject: [PATCH 09/23] Fix resolution override to work even if the camera lacks a viewport --- crates/bevy_camera/src/camera.rs | 18 ++++++++++++------ .../src/core_3d/main_opaque_pass_3d_node.rs | 7 +++++-- .../core_3d/main_transmissive_pass_3d_node.rs | 8 ++++++-- .../core_3d/main_transparent_pass_3d_node.rs | 7 +++++-- crates/bevy_core_pipeline/src/deferred/node.rs | 7 +++++-- .../bevy_core_pipeline/src/oit/resolve/node.rs | 7 +++++-- crates/bevy_core_pipeline/src/prepass/node.rs | 7 +++++-- examples/shader/custom_render_phase.rs | 7 +++++-- 8 files changed, 48 insertions(+), 20 deletions(-) diff --git a/crates/bevy_camera/src/camera.rs b/crates/bevy_camera/src/camera.rs index a70cbeb39e3b3..aaa85de27edb5 100644 --- a/crates/bevy_camera/src/camera.rs +++ b/crates/bevy_camera/src/camera.rs @@ -80,14 +80,20 @@ impl Viewport { } } - pub fn with_override( - &self, + pub fn from_viewport_and_override( + viewport: Option<&Self>, main_pass_resolution_override: Option<&MainPassResolutionOverride>, - ) -> Self { - let mut viewport = self.clone(); + ) -> Option { + let mut viewport = viewport.cloned(); + if let Some(override_size) = main_pass_resolution_override { - viewport.physical_size = **override_size; + if viewport.is_none() { + viewport = Some(Viewport::default()); + } + + viewport.as_mut().unwrap().physical_size = **override_size; } + viewport } } @@ -101,7 +107,7 @@ impl Viewport { /// * Insert this component on a 3d camera entity in the render world. /// * The resolution override must be smaller than the camera's viewport size. /// * The resolution override is specified in physical pixels. -#[derive(Component, Reflect, Deref)] +#[derive(Component, Reflect, Deref, Debug)] #[reflect(Component)] pub struct MainPassResolutionOverride(pub UVec2); diff --git a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs index c5ee7a798db2e..0ee9144a954d1 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs @@ -2,6 +2,7 @@ use crate::{ core_3d::Opaque3d, skybox::{SkyboxBindGroup, SkyboxPipelineId}, }; +use bevy_camera::Viewport; use bevy_ecs::{prelude::World, query::QueryItem}; use bevy_render::{ camera::{ExtractedCamera, MainPassResolutionOverride}, @@ -91,8 +92,10 @@ impl ViewNode for MainOpaquePass3dNode { let mut render_pass = TrackedRenderPass::new(&render_device, render_pass); let pass_span = diagnostics.pass_span(&mut render_pass, "main_opaque_pass_3d"); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } // Opaque draws diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs index 393167227f8db..ec108b8753924 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transmissive_pass_3d_node.rs @@ -1,5 +1,6 @@ use super::{Camera3d, ViewTransmissionTexture}; use crate::core_3d::Transmissive3d; +use bevy_camera::Viewport; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_image::ToExtents; use bevy_render::{ @@ -110,8 +111,11 @@ impl ViewNode for MainTransmissivePass3dNode { let mut render_pass = render_context.begin_tracked_render_pass(render_pass_descriptor); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = Viewport::from_viewport_and_override( + camera.viewport.as_ref(), + resolution_override, + ) { + render_pass.set_camera_viewport(&viewport); } if let Err(err) = transmissive_phase.render(&mut render_pass, world, view_entity) { diff --git a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs index 0c70ec23a0863..bbe14578b0903 100644 --- a/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs +++ b/crates/bevy_core_pipeline/src/core_3d/main_transparent_pass_3d_node.rs @@ -1,4 +1,5 @@ use crate::core_3d::Transparent3d; +use bevy_camera::Viewport; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::{ExtractedCamera, MainPassResolutionOverride}, @@ -69,8 +70,10 @@ impl ViewNode for MainTransparentPass3dNode { let pass_span = diagnostics.pass_span(&mut render_pass, "main_transparent_pass_3d"); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } if let Err(err) = transparent_phase.render(&mut render_pass, world, view_entity) { diff --git a/crates/bevy_core_pipeline/src/deferred/node.rs b/crates/bevy_core_pipeline/src/deferred/node.rs index ab87fccee6e89..dc65fa811df6a 100644 --- a/crates/bevy_core_pipeline/src/deferred/node.rs +++ b/crates/bevy_core_pipeline/src/deferred/node.rs @@ -1,3 +1,4 @@ +use bevy_camera::Viewport; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::camera::MainPassResolutionOverride; use bevy_render::experimental::occlusion_culling::OcclusionCulling; @@ -221,8 +222,10 @@ fn run_deferred_prepass<'w>( occlusion_query_set: None, }); let mut render_pass = TrackedRenderPass::new(&render_device, render_pass); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } // Opaque draws diff --git a/crates/bevy_core_pipeline/src/oit/resolve/node.rs b/crates/bevy_core_pipeline/src/oit/resolve/node.rs index 77352e5ecb42b..83b8c604f442a 100644 --- a/crates/bevy_core_pipeline/src/oit/resolve/node.rs +++ b/crates/bevy_core_pipeline/src/oit/resolve/node.rs @@ -1,3 +1,4 @@ +use bevy_camera::Viewport; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::{ExtractedCamera, MainPassResolutionOverride}, @@ -63,8 +64,10 @@ impl ViewNode for OitResolveNode { occlusion_query_set: None, }); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } render_pass.set_render_pipeline(pipeline); diff --git a/crates/bevy_core_pipeline/src/prepass/node.rs b/crates/bevy_core_pipeline/src/prepass/node.rs index c4cc7b1d55da5..1b54ee8c5d393 100644 --- a/crates/bevy_core_pipeline/src/prepass/node.rs +++ b/crates/bevy_core_pipeline/src/prepass/node.rs @@ -1,3 +1,4 @@ +use bevy_camera::Viewport; use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_render::{ camera::{ExtractedCamera, MainPassResolutionOverride}, @@ -186,8 +187,10 @@ fn run_prepass<'w>( let mut render_pass = TrackedRenderPass::new(&render_device, render_pass); let pass_span = diagnostics.pass_span(&mut render_pass, label); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } // Opaque draws diff --git a/examples/shader/custom_render_phase.rs b/examples/shader/custom_render_phase.rs index 89f5ce6d6703a..3dffdf2ba5c1f 100644 --- a/examples/shader/custom_render_phase.rs +++ b/examples/shader/custom_render_phase.rs @@ -12,6 +12,7 @@ use std::ops::Range; +use bevy::camera::Viewport; use bevy::pbr::SetMeshViewEmptyBindGroup; use bevy::{ core_pipeline::core_3d::graph::{Core3d, Node3d}, @@ -618,8 +619,10 @@ impl ViewNode for CustomDrawNode { occlusion_query_set: None, }); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } // Render the phase From 04f488bc472aac7f770634013efa110fe076b99a Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Mon, 7 Jul 2025 22:28:51 -0400 Subject: [PATCH 10/23] Update release notes --- release-content/release-notes/dlss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 9b8fef3849fcf..b31007299764e 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -21,7 +21,7 @@ To use DLSS in your app: * Check for the presence of `Option>` at runtime to see if DLSS is supported on the current machine * Add the `Dlss` component to your camera entity, optionally setting a specific `DlssPerfQualityMode` (defaults to `Auto`) * Optionally add sharpening via `ContrastAdaptiveSharpening` -* Custom rendering code, including third party crates, should account for the optional `MainPassResolutionOverride` to work with DLSS +* Custom rendering code, including third party crates, should account for the optional `MainPassResolutionOverride` to work with DLSS (see the `custom_render_phase` example) Note that DLSS integration is expected to have some bugs in this release related to certain rendering effects not respecting upscaling settings, and possible issues with transparencies or camera exposure. Please report any bugs encountered. From e2a9761c7bd387eced398f20242976bfa5decfb4 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:53:50 -0400 Subject: [PATCH 11/23] Docs --- crates/bevy_anti_aliasing/src/dlss/mod.rs | 13 +++++++++++++ crates/bevy_render/src/lib.rs | 5 +++++ 2 files changed, 18 insertions(+) diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs index dae40e1082d32..89c56bf771271 100644 --- a/crates/bevy_anti_aliasing/src/dlss/mod.rs +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -1,3 +1,15 @@ +//! NVIDIA Deep Learning Super Sampling. +//! +//! See https://github.com/JMS55/dlss_wgpu for licensing requirements and setup instructions. +//! +//! # Usage +//! 1. Enable Bevy's `dlss` feature +//! 2. During app setup, insert the `DlssProjectId` resource before `DefaultPlugins` +//! 3. Check for the presence of `Option>` at runtime to see if DLSS is supported on the current machine +//! 4. Add the `Dlss` component to your camera entity, optionally setting a specific `DlssPerfQualityMode` (defaults to `Auto`) +//! 5. Optionally add sharpening via `ContrastAdaptiveSharpening` +//! 6. Custom rendering code, including third party crates, should account for the optional `MainPassResolutionOverride` to work with DLSS (see the `custom_render_phase` example) + mod extract; mod node; mod prepare; @@ -80,6 +92,7 @@ impl Plugin for DlssPlugin { } } +/// Camera component to enable DLSS. #[derive(Component, Reflect, Clone, Default)] #[reflect(Component, Default)] #[require(TemporalJitter, MipBias, DepthPrepass, MotionVectorPrepass, Hdr)] diff --git a/crates/bevy_render/src/lib.rs b/crates/bevy_render/src/lib.rs index 29bb44a298238..9d6c07432edf9 100644 --- a/crates/bevy_render/src/lib.rs +++ b/crates/bevy_render/src/lib.rs @@ -673,10 +673,15 @@ pub fn get_mali_driver_version(adapter: &RenderAdapter) -> Option { None } +/// Application-specific ID for DLSS. +/// +/// See the DLSS programming guide for more info. #[cfg(feature = "dlss")] #[derive(Resource)] pub struct DlssProjectId(pub bevy_asset::uuid::Uuid); +/// When DLSS is supported by the current system, this resource will exist in the main world. +/// Otherwise this resource will be absent. #[cfg(feature = "dlss")] #[derive(Resource, Clone, Copy)] pub struct DlssSupported; From fda09d19c7b1f437d35568c66b6ed4ee4f81842c Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 11:56:50 -0400 Subject: [PATCH 12/23] Fix typo --- release-content/release-notes/dlss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index b31007299764e..1c6a8b486268c 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -25,7 +25,7 @@ To use DLSS in your app: Note that DLSS integration is expected to have some bugs in this release related to certain rendering effects not respecting upscaling settings, and possible issues with transparencies or camera exposure. Please report any bugs encountered. -Other temporal upscalers like AMD's FidelityFX™ Super Resolution (FSR), Intel's Xe Super Sampling XeSS (XeSS), and Apple's MTLFXTemporalScaler are not integrated in this release. However they all use similiar APIs, and would not be a challenge to integrate in future releases. +Other temporal upscalers like AMD's FidelityFX™ Super Resolution (FSR), Intel's Xe Super Sampling XeSS (XeSS), and Apple's MTLFXTemporalScaler are not integrated in this release. However they all use similar APIs, and would not be a challenge to integrate in future releases. Support for other swapchain-related features like frame interpolation/extrapolation, latency reduction, or dynamic resolution scaling are not currently planned, but support for DLSS Ray Reconstruction for use in Bevy Solari _is_ planned for a future release. From ab113c8837036a0a02cc092ba55411fc1880bc78 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:40:01 -0400 Subject: [PATCH 13/23] Fix --- .../src/meshlet/material_shade_nodes.rs | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs index 39dcb0c1690fa..66d3d89ad07d6 100644 --- a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs +++ b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs @@ -102,8 +102,10 @@ impl ViewNode for MeshletMainOpaquePass3dNode { timestamp_writes: None, occlusion_query_set: None, }); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } render_pass.set_bind_group( @@ -223,8 +225,10 @@ impl ViewNode for MeshletPrepassNode { timestamp_writes: None, occlusion_query_set: None, }); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } if view_has_motion_vector_prepass { @@ -354,8 +358,10 @@ impl ViewNode for MeshletDeferredGBufferPrepassNode { timestamp_writes: None, occlusion_query_set: None, }); - if let Some(viewport) = camera.viewport.as_ref() { - render_pass.set_camera_viewport(&viewport.with_override(resolution_override)); + if let Some(viewport) = + Viewport::from_viewport_and_override(camera.viewport.as_ref(), resolution_override) + { + render_pass.set_camera_viewport(&viewport); } if view_has_motion_vector_prepass { From 5ab00934d2def1cf874341f4b9cff497cfc46f5e Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 15:41:31 -0400 Subject: [PATCH 14/23] Add missing import --- crates/bevy_pbr/src/meshlet/material_shade_nodes.rs | 1 + 1 file changed, 1 insertion(+) diff --git a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs index 66d3d89ad07d6..b0e34d5e1a79a 100644 --- a/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs +++ b/crates/bevy_pbr/src/meshlet/material_shade_nodes.rs @@ -10,6 +10,7 @@ use crate::{ MeshViewBindGroup, PrepassViewBindGroup, ViewEnvironmentMapUniformOffset, ViewFogUniformOffset, ViewLightProbesUniformOffset, ViewLightsUniformOffset, ViewScreenSpaceReflectionsUniformOffset, }; +use bevy_camera::Viewport; use bevy_core_pipeline::prepass::{ MotionVectorPrepass, PreviousViewUniformOffset, ViewPrepassTextures, }; From baa26682e9dc16798f7f379db9b77cf17908a953 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:18:41 -0400 Subject: [PATCH 15/23] Switch link to https://github.com/bevyengine/dlss_wgpu --- crates/bevy_anti_aliasing/Cargo.toml | 2 +- crates/bevy_render/Cargo.toml | 2 +- release-content/release-notes/dlss.md | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_anti_aliasing/Cargo.toml b/crates/bevy_anti_aliasing/Cargo.toml index a8b51c8568fa9..29cab2621c5f7 100644 --- a/crates/bevy_anti_aliasing/Cargo.toml +++ b/crates/bevy_anti_aliasing/Cargo.toml @@ -31,7 +31,7 @@ bevy_diagnostic = { path = "../bevy_diagnostic", version = "0.17.0-dev" } # other tracing = { version = "0.1", default-features = false, features = ["std"] } -dlss_wgpu = { git = "https://github.com/JMS55/dlss_wgpu", optional = true } +dlss_wgpu = { git = "https://github.com/bevyengine/dlss_wgpu", optional = true } [lints] workspace = true diff --git a/crates/bevy_render/Cargo.toml b/crates/bevy_render/Cargo.toml index 31ac5113a424f..f1d2e100ace7b 100644 --- a/crates/bevy_render/Cargo.toml +++ b/crates/bevy_render/Cargo.toml @@ -103,7 +103,7 @@ wgpu = { version = "25", default-features = false, features = [ "fragile-send-sync-non-atomic-wasm", ] } naga = { version = "25", features = ["wgsl-in"] } -dlss_wgpu = { git = "https://github.com/JMS55/dlss_wgpu", optional = true } +dlss_wgpu = { git = "https://github.com/bevyengine/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/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 1c6a8b486268c..4a9828b91302d 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -15,7 +15,7 @@ Compared to Bevy's built-in TAA, DLSS: * Requires running via the Vulkan backend on Windows/Linux (no macOS, web, or mobile support) To use DLSS in your app: -* See https://github.com/JMS55/dlss_wgpu for licensing requirements and setup instructions +* See https://github.com/bevyengine/dlss_wgpu for licensing requirements and setup instructions * Enable Bevy's `dlss` feature * Insert the `DlssProjectId` resource before `DefaultPlugins` when setting up your app * Check for the presence of `Option>` at runtime to see if DLSS is supported on the current machine From e2d8a9e08b7f754c04c7ba497368551c993f537b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 8 Jul 2025 21:55:50 -0400 Subject: [PATCH 16/23] Fix another link --- crates/bevy_anti_aliasing/src/dlss/mod.rs | 2 +- release-content/release-notes/dlss.md | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs index 89c56bf771271..4e621f7859131 100644 --- a/crates/bevy_anti_aliasing/src/dlss/mod.rs +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -1,6 +1,6 @@ //! NVIDIA Deep Learning Super Sampling. //! -//! See https://github.com/JMS55/dlss_wgpu for licensing requirements and setup instructions. +//! See https://github.com/bevyengine/dlss_wgpu for licensing requirements and setup instructions. //! //! # Usage //! 1. Enable Bevy's `dlss` feature diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 4a9828b91302d..3ce9cfab1f18b 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -6,7 +6,7 @@ pull_requests: [19817, 19864] For users with NVIDIA RTX GPUs, Bevy now offers yet another form of anti-aliasing: DLSS. -Try it out by running Bevy's anti_aliasing example: `cargo run --example anti_aliasing --features dlss --release` (after performing setup from https://github.com/JMS55/dlss_wgpu). +Try it out by running Bevy's anti_aliasing example: `cargo run --example anti_aliasing --features dlss --release` (after performing setup from https://github.com/bevyengine/dlss_wgpu). Compared to Bevy's built-in TAA, DLSS: * Is much higher quality From e20222695202bb658a38e12902714430723d2c3b Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Wed, 9 Jul 2025 11:42:19 -0700 Subject: [PATCH 17/23] Update release-content/release-notes/dlss.md Co-authored-by: Alice Cecile --- release-content/release-notes/dlss.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index 3ce9cfab1f18b..f38ae64a03565 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -29,4 +29,4 @@ Other temporal upscalers like AMD's FidelityFX™ Super Resolution (FSR), Intel' Support for other swapchain-related features like frame interpolation/extrapolation, latency reduction, or dynamic resolution scaling are not currently planned, but support for DLSS Ray Reconstruction for use in Bevy Solari _is_ planned for a future release. -Special thanks to @cwfitzgerald for helping with the wgpu backend interop APIs. +Special thanks to @cwfitzgerald for helping with the [`wgpu`](https://github.com/gfx-rs/wgpu) backend interop APIs. From 1701bb1e29ba5fba40294d14a96bbcd86e92e94c Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Wed, 9 Jul 2025 14:47:42 -0400 Subject: [PATCH 18/23] Add blurb --- crates/bevy_anti_aliasing/src/dlss/mod.rs | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs index 4e621f7859131..b58a3b22b08ff 100644 --- a/crates/bevy_anti_aliasing/src/dlss/mod.rs +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -1,4 +1,8 @@ -//! NVIDIA Deep Learning Super Sampling. +//! NVIDIA Deep Learning Super Sampling (DLSS). +//! +//! DLSS uses machine learning models to upscale and anti-alias images. +//! +//! Requires a NVIDIA RTX GPU, and the Windows/Linux Vulkan rendering backend. Does not work on other platforms. //! //! See https://github.com/bevyengine/dlss_wgpu for licensing requirements and setup instructions. //! From 484310d95b559d63819dcdb3317e4672eef088a8 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:30:15 -0400 Subject: [PATCH 19/23] Move DLSS setup to ManageViews, so that prepare systems can later check MainPassResolutionOverride --- crates/bevy_anti_aliasing/src/dlss/mod.rs | 8 +--- crates/bevy_anti_aliasing/src/dlss/prepare.rs | 46 +++++++++++-------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/crates/bevy_anti_aliasing/src/dlss/mod.rs b/crates/bevy_anti_aliasing/src/dlss/mod.rs index b58a3b22b08ff..1a1ec64e06e74 100644 --- a/crates/bevy_anti_aliasing/src/dlss/mod.rs +++ b/crates/bevy_anti_aliasing/src/dlss/mod.rs @@ -72,16 +72,10 @@ impl Plugin for DlssPlugin { .add_systems(ExtractSchedule, extract::extract_dlss) .add_systems( Render, - prepare::configure_dlss_view_targets + prepare::prepare_dlss .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, diff --git a/crates/bevy_anti_aliasing/src/dlss/prepare.rs b/crates/bevy_anti_aliasing/src/dlss/prepare.rs index 77a3eb5f324a4..ad790fd31a62b 100644 --- a/crates/bevy_anti_aliasing/src/dlss/prepare.rs +++ b/crates/bevy_anti_aliasing/src/dlss/prepare.rs @@ -20,12 +20,21 @@ use bevy_render::{ use dlss_wgpu::{DlssContext, DlssFeatureFlags, DlssPerfQualityMode}; use std::sync::{Arc, Mutex}; +#[derive(Component)] +pub struct ViewDlssContext { + pub context: Mutex, + pub perf_quality_mode: DlssPerfQualityMode, + pub feature_flags: DlssFeatureFlags, +} + pub fn prepare_dlss( mut query: Query< ( Entity, &ExtractedView, &Dlss, + &mut Camera3d, + &mut CameraMainTextureUsages, &mut TemporalJitter, &mut MipBias, Option<&mut ViewDlssContext>, @@ -43,7 +52,23 @@ pub fn prepare_dlss( frame_count: Res, mut commands: Commands, ) { - for (entity, view, dlss, mut temporal_jitter, mut mip_bias, mut dlss_context) in &mut query { + for ( + entity, + view, + dlss, + mut camera_3d, + mut camera_main_texture_usages, + mut temporal_jitter, + mut mip_bias, + mut dlss_context, + ) in &mut query + { + 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(); + let upscaled_resolution = view.viewport.zw(); let dlss_feature_flags = DlssFeatureFlags::LowResolutionMotionVectors @@ -90,22 +115,3 @@ pub fn prepare_dlss( } } } - -#[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(); - } -} From d962a370a60a7cefce2ef7fbf782897aab3919a6 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Fri, 11 Jul 2025 11:53:44 -0400 Subject: [PATCH 20/23] Release notes --- release-content/release-notes/dlss.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/release-content/release-notes/dlss.md b/release-content/release-notes/dlss.md index f38ae64a03565..951e42a9492fd 100644 --- a/release-content/release-notes/dlss.md +++ b/release-content/release-notes/dlss.md @@ -8,6 +8,8 @@ For users with NVIDIA RTX GPUs, Bevy now offers yet another form of anti-aliasin Try it out by running Bevy's anti_aliasing example: `cargo run --example anti_aliasing --features dlss --release` (after performing setup from https://github.com/bevyengine/dlss_wgpu). +Additionally, we've open sourced https://github.com/bevyengine/dlss_wgpu as a standalone crate to help other wgpu-based renderers integrate DLSS. + Compared to Bevy's built-in TAA, DLSS: * Is much higher quality * Supports upscaling in addition to anti-aliasing, leading to much cheaper render times, particularly when used with GPU-heavy features like Bevy Solari From baa46aeaa2108f73d459b060b7065c469798b2b4 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Fri, 11 Jul 2025 12:00:30 -0400 Subject: [PATCH 21/23] Have Solari account for MainPassResolutionOverride --- crates/bevy_solari/src/realtime/node.rs | 7 +++++-- crates/bevy_solari/src/realtime/prepare.rs | 16 ++++++++++++---- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/crates/bevy_solari/src/realtime/node.rs b/crates/bevy_solari/src/realtime/node.rs index 2fcc29b415b1e..2090ef13aaaac 100644 --- a/crates/bevy_solari/src/realtime/node.rs +++ b/crates/bevy_solari/src/realtime/node.rs @@ -125,6 +125,9 @@ impl ViewNode for SolariLightingNode { }); let pass_span = diagnostics.pass_span(&mut pass, "solari_lighting"); + let dx = solari_lighting_resources.view_size.x.div_ceil(8); + let dy = solari_lighting_resources.view_size.y.div_ceil(8); + pass.set_bind_group(0, scene_bindings, &[]); pass.set_bind_group( 1, @@ -140,10 +143,10 @@ impl ViewNode for SolariLightingNode { 0, bytemuck::cast_slice(&[frame_index, solari_lighting.reset as u32]), ); - pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1); + pass.dispatch_workgroups(dx, dy, 1); pass.set_pipeline(spatial_and_shade_pipeline); - pass.dispatch_workgroups(viewport.x.div_ceil(8), viewport.y.div_ceil(8), 1); + pass.dispatch_workgroups(dx, dy, 1); pass_span.end(&mut pass); drop(pass); diff --git a/crates/bevy_solari/src/realtime/prepare.rs b/crates/bevy_solari/src/realtime/prepare.rs index 992a75c451134..6379ad696f95f 100644 --- a/crates/bevy_solari/src/realtime/prepare.rs +++ b/crates/bevy_solari/src/realtime/prepare.rs @@ -9,7 +9,7 @@ use bevy_ecs::{ use bevy_image::ToExtents; use bevy_math::UVec2; use bevy_render::{ - camera::ExtractedCamera, + camera::{ExtractedCamera, MainPassResolutionOverride}, render_resource::{ Buffer, BufferDescriptor, BufferUsages, Texture, TextureDescriptor, TextureDimension, TextureUsages, TextureView, TextureViewDescriptor, @@ -32,16 +32,24 @@ pub struct SolariLightingResources { pub fn prepare_solari_lighting_resources( query: Query< - (Entity, &ExtractedCamera, Option<&SolariLightingResources>), + ( + Entity, + &ExtractedCamera, + Option<&SolariLightingResources>, + Option<&MainPassResolutionOverride>, + ), With, >, render_device: Res, mut commands: Commands, ) { - for (entity, camera, solari_lighting_resources) in &query { - let Some(view_size) = camera.physical_viewport_size else { + for (entity, camera, solari_lighting_resources, resolution_override) in &query { + let Some(mut view_size) = camera.physical_viewport_size else { continue; }; + if let Some(MainPassResolutionOverride(resolution_override)) = resolution_override { + view_size = *resolution_override; + } if solari_lighting_resources.map(|r| r.view_size) == Some(view_size) { continue; From a6751bdb41f36e9a914ea06efa92d25c188b7110 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 15 Jul 2025 11:25:20 -0400 Subject: [PATCH 22/23] Misc --- crates/bevy_core_pipeline/src/core_3d/mod.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/bevy_core_pipeline/src/core_3d/mod.rs b/crates/bevy_core_pipeline/src/core_3d/mod.rs index fc4790beafb6b..b412cb4c55d03 100644 --- a/crates/bevy_core_pipeline/src/core_3d/mod.rs +++ b/crates/bevy_core_pipeline/src/core_3d/mod.rs @@ -29,9 +29,9 @@ pub mod graph { EndMainPass, Wireframe, LateDownsampleDepth, + MotionBlur, Taa, Dlss, - MotionBlur, Bloom, AutoExposure, DepthOfField, From b0303fe3594ca65a09dbd2d20d365a83d20cf504 Mon Sep 17 00:00:00 2001 From: JMS55 <47158642+JMS55@users.noreply.github.com> Date: Tue, 15 Jul 2025 15:57:42 -0400 Subject: [PATCH 23/23] Fix bug --- crates/bevy_solari/src/realtime/node.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_solari/src/realtime/node.rs b/crates/bevy_solari/src/realtime/node.rs index 73251f6d5aaeb..20bf7bb9a3e43 100644 --- a/crates/bevy_solari/src/realtime/node.rs +++ b/crates/bevy_solari/src/realtime/node.rs @@ -162,13 +162,13 @@ impl ViewNode for SolariLightingNode { pass.dispatch_workgroups(dx, dy, 1); pass.set_pipeline(di_spatial_and_shade_pipeline); - pass.dispatch_workgroups(dy, dy, 1); + pass.dispatch_workgroups(dx, dy, 1); pass.set_pipeline(gi_initial_and_temporal_pipeline); - pass.dispatch_workgroups(dy, dy, 1); + pass.dispatch_workgroups(dx, dy, 1); pass.set_pipeline(gi_spatial_and_shade_pipeline); - pass.dispatch_workgroups(dy, dy, 1); + pass.dispatch_workgroups(dx, dy, 1); pass_span.end(&mut pass); drop(pass);