Skip to content

Initial DLSS implementation #19864

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 30 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
30 commits
Select commit Hold shift + click to select a range
c75b2b4
Initial DLSS implementation
JMS55 Jun 29, 2025
af1b47c
Update example description
JMS55 Jun 29, 2025
eb8c77f
Rename variable
JMS55 Jun 29, 2025
951c3f9
Merge commit '1579256709dd64dcc4910b0bad5e81622d9ede0c' into dlss3
JMS55 Jul 6, 2025
742ebfa
Fix merge
JMS55 Jul 6, 2025
463c66d
Switch to git dep
JMS55 Jul 6, 2025
950f3e8
Merge branch 'main' into dlss3
JMS55 Jul 6, 2025
bf56980
Add release notes
JMS55 Jul 6, 2025
6b960b0
Modify release notes
JMS55 Jul 6, 2025
cf1a1f7
Add note about DlssSupported
JMS55 Jul 6, 2025
1b71463
Merge commit '5e3927ba489f597dd189f63286dc7985840db1b5' into dlss3
JMS55 Jul 8, 2025
10c96e8
Fix resolution override to work even if the camera lacks a viewport
JMS55 Jul 8, 2025
04f488b
Update release notes
JMS55 Jul 8, 2025
e2a9761
Docs
JMS55 Jul 8, 2025
fda09d1
Fix typo
JMS55 Jul 8, 2025
ab113c8
Fix
JMS55 Jul 8, 2025
5ab0093
Add missing import
JMS55 Jul 8, 2025
baa2668
Switch link to https://github.com/bevyengine/dlss_wgpu
JMS55 Jul 9, 2025
e2d8a9e
Fix another link
JMS55 Jul 9, 2025
8084745
Merge branch 'main' into dlss3
JMS55 Jul 9, 2025
e202226
Update release-content/release-notes/dlss.md
JMS55 Jul 9, 2025
1701bb1
Add blurb
JMS55 Jul 9, 2025
6f9114b
Merge branch 'main' into dlss3
JMS55 Jul 9, 2025
484310d
Move DLSS setup to ManageViews, so that prepare systems can later che…
JMS55 Jul 11, 2025
27f40ef
Merge commit '20dfae9a2d07038bda2921f82af50ded6151c3de' into dlss3
JMS55 Jul 11, 2025
d962a37
Release notes
JMS55 Jul 11, 2025
baa46ae
Have Solari account for MainPassResolutionOverride
JMS55 Jul 11, 2025
a6751bd
Misc
JMS55 Jul 15, 2025
b89387d
Merge commit '2bddbdfd7c920d1ea61245dcdb7ff1c155e6b03b' into dlss3
JMS55 Jul 15, 2025
b0303fe
Fix bug
JMS55 Jul 15, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"]

Expand Down
2 changes: 2 additions & 0 deletions crates/bevy_anti_aliasing/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ trace = []
webgl = []
webgpu = []
smaa_luts = ["bevy_render/ktx2", "bevy_image/ktx2", "bevy_image/zstd"]
dlss = ["dep:dlss_wgpu"]

[dependencies]
# bevy
Expand All @@ -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
Expand Down
29 changes: 29 additions & 0 deletions crates/bevy_anti_aliasing/src/dlss/extract.rs
Original file line number Diff line number Diff line change
@@ -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<MainWorld>) {
let mut cameras_3d = main_world
.query_filtered::<(RenderEntity, &Camera, &Projection, Option<&mut Dlss>), With<Hdr>>();

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)>();
}
}
}
105 changes: 105 additions & 0 deletions crates/bevy_anti_aliasing/src/dlss/mod.rs
Original file line number Diff line number Diff line change
@@ -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::<Dlss>();
}

fn finish(&self, app: &mut App) {
if app.world().get_resource::<DlssSupported>().is_none() {
info!("DLSS is not supported on this system");
return;
}

let dlss_project_id = app.world().resource::<DlssProjectId>().0;

let render_app = app.get_sub_app_mut(RenderApp).unwrap();
let render_device = render_app.world().resource::<RenderDevice>().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::<DlssSupported>();
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::<ViewNodeRunner<node::DlssNode>>(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<Mutex<dlss_wgpu::DlssSdk>>);
85 changes: 85 additions & 0 deletions crates/bevy_anti_aliasing/src/dlss/node.rs
Original file line number Diff line number Diff line change
@@ -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<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let adapter = world.resource::<RenderAdapter>();
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(())
}
}
111 changes: 111 additions & 0 deletions crates/bevy_anti_aliasing/src/dlss/prepare.rs
Original file line number Diff line number Diff line change
@@ -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<Camera3d>,
With<TemporalJitter>,
With<DepthPrepass>,
With<MotionVectorPrepass>,
),
>,
dlss_sdk: Res<DlssSdk>,
render_device: Res<RenderDevice>,
render_queue: Res<RenderQueue>,
frame_count: Res<FrameCount>,
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<DlssContext>,
pub perf_quality_mode: DlssPerfQualityMode,
pub feature_flags: DlssFeatureFlags,
}

pub fn configure_dlss_view_targets(
mut view_targets: Query<(&mut Camera3d, &mut CameraMainTextureUsages), With<Dlss>>,
) {
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();
}
}
11 changes: 10 additions & 1 deletion crates/bevy_anti_aliasing/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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,
));
}
}
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ pub mod graph {
Wireframe,
LateDownsampleDepth,
Taa,
Dlss,
MotionBlur,
Bloom,
AutoExposure,
Expand Down
Loading
Loading