Initial DLSS implementation

This commit is contained in:
JMS55 2025-06-29 14:40:56 -04:00
parent e9daac4f11
commit c75b2b4f26
16 changed files with 601 additions and 61 deletions

View File

@ -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"]

View File

@ -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

View File

@ -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)>();
}
}
}

View File

@ -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>>);

View File

@ -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(())
}
}

View File

@ -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();
}
}

View File

@ -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,
));
}
}

View File

@ -31,6 +31,7 @@ pub mod graph {
Wireframe,
LateDownsampleDepth,
Taa,
Dlss,
MotionBlur,
Bloom,
AutoExposure,

View File

@ -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"]

View File

@ -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"] }

View File

@ -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::<DlssProjectId>()
.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,
let render_resources = renderer::initialize_renderer(
instance,
&settings,
&request_adapter_options,
#[cfg(feature = "dlss")]
dlss_project_id,
)
.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))),
));
*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::<FutureRenderResources>()
{
#[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<u32> {
None
}
#[cfg(feature = "dlss")]
#[derive(Resource)]
pub struct DlssProjectId(pub bevy_asset::uuid::Uuid);
#[cfg(feature = "dlss")]
#[derive(Resource, Clone, Copy)]
pub struct DlssSupported;

View File

@ -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 {
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,
})
.await
.unwrap();
let queue = Arc::new(WgpuWrapper::new(queue));
let adapter = Arc::new(WgpuWrapper::new(adapter));
(
};
#[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
},
)
}

View File

@ -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<crate::DlssSupported>,
);
/// 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()
}
}

View File

@ -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<FrameCount>,
) {
@ -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();

View File

@ -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.|

View File

@ -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<ButtonInput<KeyCode>>,
camera: Single<
#[cfg(feature = "dlss")] camera: Single<
(
Entity,
Option<&mut Fxaa>,
Option<&mut Smaa>,
Option<&TemporalAntiAliasing>,
&mut Msaa,
Option<&mut Dlss>,
),
With<Camera>,
>,
#[cfg(not(feature = "dlss"))] camera: Single<
(
Entity,
Option<&mut Fxaa>,
@ -49,8 +82,12 @@ fn modify_aa(
),
With<Camera>,
>,
#[cfg(feature = "dlss")] dlss_supported: Option<Res<DlssSupported>>,
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::<Fxaa>()
.remove::<Smaa>()
.remove::<TaaComponents>();
.remove::<TaaComponents>()
.remove::<DlssComponents>();
}
// MSAA
@ -68,7 +106,8 @@ fn modify_aa(
camera
.remove::<Fxaa>()
.remove::<Smaa>()
.remove::<TaaComponents>();
.remove::<TaaComponents>()
.remove::<DlssComponents>();
*msaa = Msaa::Sample4;
}
@ -92,6 +131,7 @@ fn modify_aa(
camera
.remove::<Smaa>()
.remove::<TaaComponents>()
.remove::<DlssComponents>()
.insert(Fxaa::default());
}
@ -125,6 +165,7 @@ fn modify_aa(
camera
.remove::<Fxaa>()
.remove::<TaaComponents>()
.remove::<DlssComponents>()
.insert(Smaa::default());
}
@ -150,8 +191,43 @@ fn modify_aa(
camera
.remove::<Fxaa>()
.remove::<Smaa>()
.remove::<DlssComponents>()
.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::<Fxaa>()
.remove::<Smaa>()
.remove::<TaaComponents>()
.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<Camera>,
>,
#[cfg(not(feature = "dlss"))] camera: Single<
(
Option<&Fxaa>,
Option<&Smaa>,
@ -190,22 +277,35 @@ fn update_ui(
With<Camera>,
>,
mut ui: Single<&mut Text>,
#[cfg(feature = "dlss")] dlss_supported: Option<Res<DlssSupported>>,
) {
#[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))),
));