1#[cfg(not(feature = "smaa_luts"))]
33use crate::tonemapping::lut_placeholder;
34use crate::{
35 core_2d::graph::{Core2d, Node2d},
36 core_3d::graph::{Core3d, Node3d},
37};
38use bevy_app::{App, Plugin};
39#[cfg(feature = "smaa_luts")]
40use bevy_asset::load_internal_binary_asset;
41use bevy_asset::{load_internal_asset, Handle};
42use bevy_derive::{Deref, DerefMut};
43use bevy_ecs::{
44 component::Component,
45 entity::Entity,
46 query::{QueryItem, With},
47 reflect::ReflectComponent,
48 schedule::IntoSystemConfigs as _,
49 system::{lifetimeless::Read, Commands, Query, Res, ResMut, Resource},
50 world::{FromWorld, World},
51};
52use bevy_image::{BevyDefault, Image};
53use bevy_math::{vec4, Vec4};
54use bevy_reflect::{std_traits::ReflectDefault, Reflect};
55use bevy_render::{
56 camera::ExtractedCamera,
57 extract_component::{ExtractComponent, ExtractComponentPlugin},
58 render_asset::RenderAssets,
59 render_graph::{
60 NodeRunError, RenderGraphApp as _, RenderGraphContext, ViewNode, ViewNodeRunner,
61 },
62 render_resource::{
63 binding_types::{sampler, texture_2d, uniform_buffer},
64 AddressMode, BindGroup, BindGroupEntries, BindGroupLayout, BindGroupLayoutEntries,
65 CachedRenderPipelineId, ColorTargetState, ColorWrites, CompareFunction, DepthStencilState,
66 DynamicUniformBuffer, Extent3d, FilterMode, FragmentState, LoadOp, MultisampleState,
67 Operations, PipelineCache, PrimitiveState, RenderPassColorAttachment,
68 RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline,
69 RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, Shader, ShaderDefVal,
70 ShaderStages, ShaderType, SpecializedRenderPipeline, SpecializedRenderPipelines,
71 StencilFaceState, StencilOperation, StencilState, StoreOp, TextureDescriptor,
72 TextureDimension, TextureFormat, TextureSampleType, TextureUsages, TextureView,
73 VertexState,
74 },
75 renderer::{RenderContext, RenderDevice, RenderQueue},
76 texture::{CachedTexture, GpuImage, TextureCache},
77 view::{ExtractedView, ViewTarget},
78 Render, RenderApp, RenderSet,
79};
80use bevy_utils::prelude::default;
81
82const SMAA_SHADER_HANDLE: Handle<Shader> = Handle::weak_from_u128(12247928498010601081);
84const SMAA_AREA_LUT_TEXTURE_HANDLE: Handle<Image> = Handle::weak_from_u128(15283551734567401670);
86const SMAA_SEARCH_LUT_TEXTURE_HANDLE: Handle<Image> = Handle::weak_from_u128(3187314362190283210);
88
89pub struct SmaaPlugin;
91
92#[derive(Clone, Copy, Default, Component, Reflect, ExtractComponent)]
95#[reflect(Component, Default)]
96#[doc(alias = "SubpixelMorphologicalAntiAliasing")]
97pub struct Smaa {
98 pub preset: SmaaPreset,
102}
103
104#[deprecated(since = "0.15.0", note = "Renamed to `Smaa`")]
105pub type SmaaSettings = Smaa;
106
107#[derive(Clone, Copy, Reflect, Default, PartialEq, Eq, Hash)]
113#[reflect(Default)]
114pub enum SmaaPreset {
115 Low,
117
118 Medium,
120
121 #[default]
125 High,
126
127 Ultra,
129}
130
131#[derive(Resource)]
135pub struct SmaaPipelines {
136 edge_detection: SmaaEdgeDetectionPipeline,
138 blending_weight_calculation: SmaaBlendingWeightCalculationPipeline,
140 neighborhood_blending: SmaaNeighborhoodBlendingPipeline,
142}
143
144struct SmaaEdgeDetectionPipeline {
146 postprocess_bind_group_layout: BindGroupLayout,
148 edge_detection_bind_group_layout: BindGroupLayout,
150}
151
152struct SmaaBlendingWeightCalculationPipeline {
154 postprocess_bind_group_layout: BindGroupLayout,
156 blending_weight_calculation_bind_group_layout: BindGroupLayout,
158}
159
160struct SmaaNeighborhoodBlendingPipeline {
162 postprocess_bind_group_layout: BindGroupLayout,
164 neighborhood_blending_bind_group_layout: BindGroupLayout,
166}
167
168#[derive(Clone, PartialEq, Eq, Hash)]
170pub struct SmaaNeighborhoodBlendingPipelineKey {
171 texture_format: TextureFormat,
173 preset: SmaaPreset,
175}
176
177#[derive(Component)]
182pub struct ViewSmaaPipelines {
183 edge_detection_pipeline_id: CachedRenderPipelineId,
185 blending_weight_calculation_pipeline_id: CachedRenderPipelineId,
187 neighborhood_blending_pipeline_id: CachedRenderPipelineId,
189}
190
191#[derive(Default)]
194pub struct SmaaNode;
195
196#[derive(Clone, Copy, ShaderType)]
203pub struct SmaaInfoUniform {
204 pub rt_metrics: Vec4,
214}
215
216#[derive(Clone, Copy, Deref, DerefMut, Component)]
219pub struct SmaaInfoUniformOffset(pub u32);
220
221#[derive(Resource, Default, Deref, DerefMut)]
225pub struct SmaaInfoUniformBuffer(pub DynamicUniformBuffer<SmaaInfoUniform>);
226
227#[derive(Component)]
232pub struct SmaaTextures {
233 pub edge_detection_color_texture: CachedTexture,
239
240 pub edge_detection_stencil_texture: CachedTexture,
246
247 pub blend_texture: CachedTexture,
253}
254
255#[derive(Component)]
260pub struct SmaaBindGroups {
261 pub edge_detection_bind_group: BindGroup,
263 pub blending_weight_calculation_bind_group: BindGroup,
265 pub neighborhood_blending_bind_group: BindGroup,
267}
268
269#[derive(Resource, Default)]
274pub struct SmaaSpecializedRenderPipelines {
275 edge_detection: SpecializedRenderPipelines<SmaaEdgeDetectionPipeline>,
277
278 blending_weight_calculation: SpecializedRenderPipelines<SmaaBlendingWeightCalculationPipeline>,
281
282 neighborhood_blending: SpecializedRenderPipelines<SmaaNeighborhoodBlendingPipeline>,
285}
286
287impl Plugin for SmaaPlugin {
288 fn build(&self, app: &mut App) {
289 load_internal_asset!(app, SMAA_SHADER_HANDLE, "smaa.wgsl", Shader::from_wgsl);
291
292 #[cfg(feature = "smaa_luts")]
295 load_internal_binary_asset!(
296 app,
297 SMAA_AREA_LUT_TEXTURE_HANDLE,
298 "SMAAAreaLUT.ktx2",
299 |bytes, _: String| Image::from_buffer(
300 #[cfg(all(debug_assertions, feature = "dds"))]
301 "SMAAAreaLUT".to_owned(),
302 bytes,
303 bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
304 bevy_image::CompressedImageFormats::NONE,
305 false,
306 bevy_image::ImageSampler::Default,
307 bevy_asset::RenderAssetUsages::RENDER_WORLD,
308 )
309 .expect("Failed to load SMAA area LUT")
310 );
311
312 #[cfg(feature = "smaa_luts")]
313 load_internal_binary_asset!(
314 app,
315 SMAA_SEARCH_LUT_TEXTURE_HANDLE,
316 "SMAASearchLUT.ktx2",
317 |bytes, _: String| Image::from_buffer(
318 #[cfg(all(debug_assertions, feature = "dds"))]
319 "SMAASearchLUT".to_owned(),
320 bytes,
321 bevy_image::ImageType::Format(bevy_image::ImageFormat::Ktx2),
322 bevy_image::CompressedImageFormats::NONE,
323 false,
324 bevy_image::ImageSampler::Default,
325 bevy_asset::RenderAssetUsages::RENDER_WORLD,
326 )
327 .expect("Failed to load SMAA search LUT")
328 );
329
330 #[cfg(not(feature = "smaa_luts"))]
331 app.world_mut()
332 .resource_mut::<bevy_asset::Assets<Image>>()
333 .insert(SMAA_AREA_LUT_TEXTURE_HANDLE.id(), lut_placeholder());
334
335 #[cfg(not(feature = "smaa_luts"))]
336 app.world_mut()
337 .resource_mut::<bevy_asset::Assets<Image>>()
338 .insert(SMAA_SEARCH_LUT_TEXTURE_HANDLE.id(), lut_placeholder());
339
340 app.add_plugins(ExtractComponentPlugin::<Smaa>::default())
341 .register_type::<Smaa>();
342
343 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
344 return;
345 };
346
347 render_app
348 .init_resource::<SmaaSpecializedRenderPipelines>()
349 .init_resource::<SmaaInfoUniformBuffer>()
350 .add_systems(
351 Render,
352 (
353 prepare_smaa_pipelines.in_set(RenderSet::Prepare),
354 prepare_smaa_uniforms.in_set(RenderSet::PrepareResources),
355 prepare_smaa_textures.in_set(RenderSet::PrepareResources),
356 prepare_smaa_bind_groups.in_set(RenderSet::PrepareBindGroups),
357 ),
358 )
359 .add_render_graph_node::<ViewNodeRunner<SmaaNode>>(Core3d, Node3d::Smaa)
360 .add_render_graph_edges(
361 Core3d,
362 (
363 Node3d::Tonemapping,
364 Node3d::Smaa,
365 Node3d::EndMainPassPostProcessing,
366 ),
367 )
368 .add_render_graph_node::<ViewNodeRunner<SmaaNode>>(Core2d, Node2d::Smaa)
369 .add_render_graph_edges(
370 Core2d,
371 (
372 Node2d::Tonemapping,
373 Node2d::Smaa,
374 Node2d::EndMainPassPostProcessing,
375 ),
376 );
377 }
378
379 fn finish(&self, app: &mut App) {
380 if let Some(render_app) = app.get_sub_app_mut(RenderApp) {
381 render_app.init_resource::<SmaaPipelines>();
382 }
383 }
384}
385
386impl FromWorld for SmaaPipelines {
387 fn from_world(world: &mut World) -> Self {
388 let render_device = world.resource::<RenderDevice>();
389
390 let postprocess_bind_group_layout = render_device.create_bind_group_layout(
392 "SMAA postprocess bind group layout",
393 &BindGroupLayoutEntries::sequential(
394 ShaderStages::FRAGMENT,
395 (
396 texture_2d(TextureSampleType::Float { filterable: true }),
397 uniform_buffer::<SmaaInfoUniform>(true)
398 .visibility(ShaderStages::VERTEX_FRAGMENT),
399 ),
400 ),
401 );
402
403 let edge_detection_bind_group_layout = render_device.create_bind_group_layout(
405 "SMAA edge detection bind group layout",
406 &BindGroupLayoutEntries::sequential(
407 ShaderStages::FRAGMENT,
408 (sampler(SamplerBindingType::Filtering),),
409 ),
410 );
411
412 let blending_weight_calculation_bind_group_layout = render_device.create_bind_group_layout(
414 "SMAA blending weight calculation bind group layout",
415 &BindGroupLayoutEntries::sequential(
416 ShaderStages::FRAGMENT,
417 (
418 texture_2d(TextureSampleType::Float { filterable: true }), sampler(SamplerBindingType::Filtering), texture_2d(TextureSampleType::Float { filterable: true }), texture_2d(TextureSampleType::Float { filterable: true }), ),
423 ),
424 );
425
426 let neighborhood_blending_bind_group_layout = render_device.create_bind_group_layout(
428 "SMAA neighborhood blending bind group layout",
429 &BindGroupLayoutEntries::sequential(
430 ShaderStages::FRAGMENT,
431 (
432 texture_2d(TextureSampleType::Float { filterable: true }),
433 sampler(SamplerBindingType::Filtering),
434 ),
435 ),
436 );
437
438 SmaaPipelines {
439 edge_detection: SmaaEdgeDetectionPipeline {
440 postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
441 edge_detection_bind_group_layout,
442 },
443 blending_weight_calculation: SmaaBlendingWeightCalculationPipeline {
444 postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
445 blending_weight_calculation_bind_group_layout,
446 },
447 neighborhood_blending: SmaaNeighborhoodBlendingPipeline {
448 postprocess_bind_group_layout,
449 neighborhood_blending_bind_group_layout,
450 },
451 }
452 }
453}
454
455impl SpecializedRenderPipeline for SmaaEdgeDetectionPipeline {
457 type Key = SmaaPreset;
458
459 fn specialize(&self, preset: Self::Key) -> RenderPipelineDescriptor {
460 let shader_defs = vec!["SMAA_EDGE_DETECTION".into(), preset.shader_def()];
461
462 let stencil_face_state = StencilFaceState {
466 compare: CompareFunction::Always,
467 fail_op: StencilOperation::Replace,
468 depth_fail_op: StencilOperation::Replace,
469 pass_op: StencilOperation::Replace,
470 };
471
472 RenderPipelineDescriptor {
473 label: Some("SMAA edge detection".into()),
474 layout: vec![
475 self.postprocess_bind_group_layout.clone(),
476 self.edge_detection_bind_group_layout.clone(),
477 ],
478 vertex: VertexState {
479 shader: SMAA_SHADER_HANDLE,
480 shader_defs: shader_defs.clone(),
481 entry_point: "edge_detection_vertex_main".into(),
482 buffers: vec![],
483 },
484 fragment: Some(FragmentState {
485 shader: SMAA_SHADER_HANDLE,
486 shader_defs,
487 entry_point: "luma_edge_detection_fragment_main".into(),
488 targets: vec![Some(ColorTargetState {
489 format: TextureFormat::Rg8Unorm,
490 blend: None,
491 write_mask: ColorWrites::ALL,
492 })],
493 }),
494 push_constant_ranges: vec![],
495 primitive: PrimitiveState::default(),
496 depth_stencil: Some(DepthStencilState {
497 format: TextureFormat::Stencil8,
498 depth_write_enabled: false,
499 depth_compare: CompareFunction::Always,
500 stencil: StencilState {
501 front: stencil_face_state,
502 back: stencil_face_state,
503 read_mask: 1,
504 write_mask: 1,
505 },
506 bias: default(),
507 }),
508 multisample: MultisampleState::default(),
509 zero_initialize_workgroup_memory: false,
510 }
511 }
512}
513
514impl SpecializedRenderPipeline for SmaaBlendingWeightCalculationPipeline {
516 type Key = SmaaPreset;
517
518 fn specialize(&self, preset: Self::Key) -> RenderPipelineDescriptor {
519 let shader_defs = vec![
520 "SMAA_BLENDING_WEIGHT_CALCULATION".into(),
521 preset.shader_def(),
522 ];
523
524 let stencil_face_state = StencilFaceState {
526 compare: CompareFunction::Equal,
527 fail_op: StencilOperation::Keep,
528 depth_fail_op: StencilOperation::Keep,
529 pass_op: StencilOperation::Keep,
530 };
531
532 RenderPipelineDescriptor {
533 label: Some("SMAA blending weight calculation".into()),
534 layout: vec![
535 self.postprocess_bind_group_layout.clone(),
536 self.blending_weight_calculation_bind_group_layout.clone(),
537 ],
538 vertex: VertexState {
539 shader: SMAA_SHADER_HANDLE,
540 shader_defs: shader_defs.clone(),
541 entry_point: "blending_weight_calculation_vertex_main".into(),
542 buffers: vec![],
543 },
544 fragment: Some(FragmentState {
545 shader: SMAA_SHADER_HANDLE,
546 shader_defs,
547 entry_point: "blending_weight_calculation_fragment_main".into(),
548 targets: vec![Some(ColorTargetState {
549 format: TextureFormat::Rgba8Unorm,
550 blend: None,
551 write_mask: ColorWrites::ALL,
552 })],
553 }),
554 push_constant_ranges: vec![],
555 primitive: PrimitiveState::default(),
556 depth_stencil: Some(DepthStencilState {
557 format: TextureFormat::Stencil8,
558 depth_write_enabled: false,
559 depth_compare: CompareFunction::Always,
560 stencil: StencilState {
561 front: stencil_face_state,
562 back: stencil_face_state,
563 read_mask: 1,
564 write_mask: 1,
565 },
566 bias: default(),
567 }),
568 multisample: MultisampleState::default(),
569 zero_initialize_workgroup_memory: false,
570 }
571 }
572}
573
574impl SpecializedRenderPipeline for SmaaNeighborhoodBlendingPipeline {
575 type Key = SmaaNeighborhoodBlendingPipelineKey;
576
577 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
578 let shader_defs = vec!["SMAA_NEIGHBORHOOD_BLENDING".into(), key.preset.shader_def()];
579
580 RenderPipelineDescriptor {
581 label: Some("SMAA neighborhood blending".into()),
582 layout: vec![
583 self.postprocess_bind_group_layout.clone(),
584 self.neighborhood_blending_bind_group_layout.clone(),
585 ],
586 vertex: VertexState {
587 shader: SMAA_SHADER_HANDLE,
588 shader_defs: shader_defs.clone(),
589 entry_point: "neighborhood_blending_vertex_main".into(),
590 buffers: vec![],
591 },
592 fragment: Some(FragmentState {
593 shader: SMAA_SHADER_HANDLE,
594 shader_defs,
595 entry_point: "neighborhood_blending_fragment_main".into(),
596 targets: vec![Some(ColorTargetState {
597 format: key.texture_format,
598 blend: None,
599 write_mask: ColorWrites::ALL,
600 })],
601 }),
602 push_constant_ranges: vec![],
603 primitive: PrimitiveState::default(),
604 depth_stencil: None,
605 multisample: MultisampleState::default(),
606 zero_initialize_workgroup_memory: false,
607 }
608 }
609}
610
611fn prepare_smaa_pipelines(
614 mut commands: Commands,
615 pipeline_cache: Res<PipelineCache>,
616 mut specialized_render_pipelines: ResMut<SmaaSpecializedRenderPipelines>,
617 smaa_pipelines: Res<SmaaPipelines>,
618 view_targets: Query<(Entity, &ExtractedView, &Smaa)>,
619) {
620 for (entity, view, smaa) in &view_targets {
621 let edge_detection_pipeline_id = specialized_render_pipelines.edge_detection.specialize(
622 &pipeline_cache,
623 &smaa_pipelines.edge_detection,
624 smaa.preset,
625 );
626
627 let blending_weight_calculation_pipeline_id = specialized_render_pipelines
628 .blending_weight_calculation
629 .specialize(
630 &pipeline_cache,
631 &smaa_pipelines.blending_weight_calculation,
632 smaa.preset,
633 );
634
635 let neighborhood_blending_pipeline_id = specialized_render_pipelines
636 .neighborhood_blending
637 .specialize(
638 &pipeline_cache,
639 &smaa_pipelines.neighborhood_blending,
640 SmaaNeighborhoodBlendingPipelineKey {
641 texture_format: if view.hdr {
642 ViewTarget::TEXTURE_FORMAT_HDR
643 } else {
644 TextureFormat::bevy_default()
645 },
646 preset: smaa.preset,
647 },
648 );
649
650 commands.entity(entity).insert(ViewSmaaPipelines {
651 edge_detection_pipeline_id,
652 blending_weight_calculation_pipeline_id,
653 neighborhood_blending_pipeline_id,
654 });
655 }
656}
657
658fn prepare_smaa_uniforms(
661 mut commands: Commands,
662 render_device: Res<RenderDevice>,
663 render_queue: Res<RenderQueue>,
664 view_targets: Query<(Entity, &ExtractedView), With<Smaa>>,
665 mut smaa_info_buffer: ResMut<SmaaInfoUniformBuffer>,
666) {
667 smaa_info_buffer.clear();
668 for (entity, view) in &view_targets {
669 let offset = smaa_info_buffer.push(&SmaaInfoUniform {
670 rt_metrics: vec4(
671 1.0 / view.viewport.z as f32,
672 1.0 / view.viewport.w as f32,
673 view.viewport.z as f32,
674 view.viewport.w as f32,
675 ),
676 });
677 commands
678 .entity(entity)
679 .insert(SmaaInfoUniformOffset(offset));
680 }
681
682 smaa_info_buffer.write_buffer(&render_device, &render_queue);
683}
684
685fn prepare_smaa_textures(
692 mut commands: Commands,
693 render_device: Res<RenderDevice>,
694 mut texture_cache: ResMut<TextureCache>,
695 view_targets: Query<(Entity, &ExtractedCamera), (With<ExtractedView>, With<Smaa>)>,
696) {
697 for (entity, camera) in &view_targets {
698 let Some(texture_size) = camera.physical_target_size else {
699 continue;
700 };
701
702 let texture_size = Extent3d {
703 width: texture_size.x,
704 height: texture_size.y,
705 depth_or_array_layers: 1,
706 };
707
708 let edge_detection_color_texture = texture_cache.get(
710 &render_device,
711 TextureDescriptor {
712 label: Some("SMAA edge detection color texture"),
713 size: texture_size,
714 mip_level_count: 1,
715 sample_count: 1,
716 dimension: TextureDimension::D2,
717 format: TextureFormat::Rg8Unorm,
718 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::RENDER_ATTACHMENT,
719 view_formats: &[],
720 },
721 );
722
723 let edge_detection_stencil_texture = texture_cache.get(
725 &render_device,
726 TextureDescriptor {
727 label: Some("SMAA edge detection stencil texture"),
728 size: texture_size,
729 mip_level_count: 1,
730 sample_count: 1,
731 dimension: TextureDimension::D2,
732 format: TextureFormat::Stencil8,
733 usage: TextureUsages::RENDER_ATTACHMENT,
734 view_formats: &[],
735 },
736 );
737
738 let blend_texture = texture_cache.get(
741 &render_device,
742 TextureDescriptor {
743 label: Some("SMAA blend texture"),
744 size: texture_size,
745 mip_level_count: 1,
746 sample_count: 1,
747 dimension: TextureDimension::D2,
748 format: TextureFormat::Rgba8Unorm,
749 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::RENDER_ATTACHMENT,
750 view_formats: &[],
751 },
752 );
753
754 commands.entity(entity).insert(SmaaTextures {
755 edge_detection_color_texture,
756 edge_detection_stencil_texture,
757 blend_texture,
758 });
759 }
760}
761
762fn prepare_smaa_bind_groups(
765 mut commands: Commands,
766 render_device: Res<RenderDevice>,
767 smaa_pipelines: Res<SmaaPipelines>,
768 images: Res<RenderAssets<GpuImage>>,
769 view_targets: Query<(Entity, &SmaaTextures), (With<ExtractedView>, With<Smaa>)>,
770) {
771 let (Some(search_texture), Some(area_texture)) = (
773 images.get(&SMAA_SEARCH_LUT_TEXTURE_HANDLE),
774 images.get(&SMAA_AREA_LUT_TEXTURE_HANDLE),
775 ) else {
776 return;
777 };
778
779 for (entity, smaa_textures) in &view_targets {
780 let sampler = render_device.create_sampler(&SamplerDescriptor {
783 label: Some("SMAA sampler"),
784 address_mode_u: AddressMode::ClampToEdge,
785 address_mode_v: AddressMode::ClampToEdge,
786 address_mode_w: AddressMode::ClampToEdge,
787 mag_filter: FilterMode::Linear,
788 min_filter: FilterMode::Linear,
789 ..default()
790 });
791
792 commands.entity(entity).insert(SmaaBindGroups {
793 edge_detection_bind_group: render_device.create_bind_group(
794 Some("SMAA edge detection bind group"),
795 &smaa_pipelines
796 .edge_detection
797 .edge_detection_bind_group_layout,
798 &BindGroupEntries::sequential((&sampler,)),
799 ),
800 blending_weight_calculation_bind_group: render_device.create_bind_group(
801 Some("SMAA blending weight calculation bind group"),
802 &smaa_pipelines
803 .blending_weight_calculation
804 .blending_weight_calculation_bind_group_layout,
805 &BindGroupEntries::sequential((
806 &smaa_textures.edge_detection_color_texture.default_view,
807 &sampler,
808 &search_texture.texture_view,
809 &area_texture.texture_view,
810 )),
811 ),
812 neighborhood_blending_bind_group: render_device.create_bind_group(
813 Some("SMAA neighborhood blending bind group"),
814 &smaa_pipelines
815 .neighborhood_blending
816 .neighborhood_blending_bind_group_layout,
817 &BindGroupEntries::sequential((
818 &smaa_textures.blend_texture.default_view,
819 &sampler,
820 )),
821 ),
822 });
823 }
824}
825
826impl ViewNode for SmaaNode {
827 type ViewQuery = (
828 Read<ViewTarget>,
829 Read<ViewSmaaPipelines>,
830 Read<SmaaInfoUniformOffset>,
831 Read<SmaaTextures>,
832 Read<SmaaBindGroups>,
833 );
834
835 fn run<'w>(
836 &self,
837 _: &mut RenderGraphContext,
838 render_context: &mut RenderContext<'w>,
839 (
840 view_target,
841 view_pipelines,
842 view_smaa_uniform_offset,
843 smaa_textures,
844 view_smaa_bind_groups,
845 ): QueryItem<'w, Self::ViewQuery>,
846 world: &'w World,
847 ) -> Result<(), NodeRunError> {
848 let pipeline_cache = world.resource::<PipelineCache>();
849 let smaa_pipelines = world.resource::<SmaaPipelines>();
850 let smaa_info_uniform_buffer = world.resource::<SmaaInfoUniformBuffer>();
851
852 let (
854 Some(edge_detection_pipeline),
855 Some(blending_weight_calculation_pipeline),
856 Some(neighborhood_blending_pipeline),
857 ) = (
858 pipeline_cache.get_render_pipeline(view_pipelines.edge_detection_pipeline_id),
859 pipeline_cache
860 .get_render_pipeline(view_pipelines.blending_weight_calculation_pipeline_id),
861 pipeline_cache.get_render_pipeline(view_pipelines.neighborhood_blending_pipeline_id),
862 )
863 else {
864 return Ok(());
865 };
866
867 let postprocess = view_target.post_process_write();
869 let (source, destination) = (postprocess.source, postprocess.destination);
870
871 perform_edge_detection(
873 render_context,
874 smaa_pipelines,
875 smaa_textures,
876 view_smaa_bind_groups,
877 smaa_info_uniform_buffer,
878 view_smaa_uniform_offset,
879 edge_detection_pipeline,
880 source,
881 );
882
883 perform_blending_weight_calculation(
885 render_context,
886 smaa_pipelines,
887 smaa_textures,
888 view_smaa_bind_groups,
889 smaa_info_uniform_buffer,
890 view_smaa_uniform_offset,
891 blending_weight_calculation_pipeline,
892 source,
893 );
894
895 perform_neighborhood_blending(
897 render_context,
898 smaa_pipelines,
899 view_smaa_bind_groups,
900 smaa_info_uniform_buffer,
901 view_smaa_uniform_offset,
902 neighborhood_blending_pipeline,
903 source,
904 destination,
905 );
906
907 Ok(())
908 }
909}
910
911#[allow(clippy::too_many_arguments)]
918fn perform_edge_detection(
919 render_context: &mut RenderContext,
920 smaa_pipelines: &SmaaPipelines,
921 smaa_textures: &SmaaTextures,
922 view_smaa_bind_groups: &SmaaBindGroups,
923 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
924 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
925 edge_detection_pipeline: &RenderPipeline,
926 source: &TextureView,
927) {
928 let postprocess_bind_group = render_context.render_device().create_bind_group(
930 None,
931 &smaa_pipelines.edge_detection.postprocess_bind_group_layout,
932 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
933 );
934
935 let pass_descriptor = RenderPassDescriptor {
937 label: Some("SMAA edge detection pass"),
938 color_attachments: &[Some(RenderPassColorAttachment {
939 view: &smaa_textures.edge_detection_color_texture.default_view,
940 resolve_target: None,
941 ops: default(),
942 })],
943 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
944 view: &smaa_textures.edge_detection_stencil_texture.default_view,
945 depth_ops: None,
946 stencil_ops: Some(Operations {
947 load: LoadOp::Clear(0),
948 store: StoreOp::Store,
949 }),
950 }),
951 timestamp_writes: None,
952 occlusion_query_set: None,
953 };
954
955 let mut render_pass = render_context
957 .command_encoder()
958 .begin_render_pass(&pass_descriptor);
959 render_pass.set_pipeline(edge_detection_pipeline);
960 render_pass.set_bind_group(0, &postprocess_bind_group, &[**view_smaa_uniform_offset]);
961 render_pass.set_bind_group(1, &view_smaa_bind_groups.edge_detection_bind_group, &[]);
962 render_pass.set_stencil_reference(1);
963 render_pass.draw(0..3, 0..1);
964}
965
966#[allow(clippy::too_many_arguments)]
972fn perform_blending_weight_calculation(
973 render_context: &mut RenderContext,
974 smaa_pipelines: &SmaaPipelines,
975 smaa_textures: &SmaaTextures,
976 view_smaa_bind_groups: &SmaaBindGroups,
977 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
978 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
979 blending_weight_calculation_pipeline: &RenderPipeline,
980 source: &TextureView,
981) {
982 let postprocess_bind_group = render_context.render_device().create_bind_group(
984 None,
985 &smaa_pipelines
986 .blending_weight_calculation
987 .postprocess_bind_group_layout,
988 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
989 );
990
991 let pass_descriptor = RenderPassDescriptor {
993 label: Some("SMAA blending weight calculation pass"),
994 color_attachments: &[Some(RenderPassColorAttachment {
995 view: &smaa_textures.blend_texture.default_view,
996 resolve_target: None,
997 ops: default(),
998 })],
999 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
1000 view: &smaa_textures.edge_detection_stencil_texture.default_view,
1001 depth_ops: None,
1002 stencil_ops: Some(Operations {
1003 load: LoadOp::Load,
1004 store: StoreOp::Discard,
1005 }),
1006 }),
1007 timestamp_writes: None,
1008 occlusion_query_set: None,
1009 };
1010
1011 let mut render_pass = render_context
1013 .command_encoder()
1014 .begin_render_pass(&pass_descriptor);
1015 render_pass.set_pipeline(blending_weight_calculation_pipeline);
1016 render_pass.set_bind_group(0, &postprocess_bind_group, &[**view_smaa_uniform_offset]);
1017 render_pass.set_bind_group(
1018 1,
1019 &view_smaa_bind_groups.blending_weight_calculation_bind_group,
1020 &[],
1021 );
1022 render_pass.set_stencil_reference(1);
1023 render_pass.draw(0..3, 0..1);
1024}
1025
1026#[allow(clippy::too_many_arguments)]
1031fn perform_neighborhood_blending(
1032 render_context: &mut RenderContext,
1033 smaa_pipelines: &SmaaPipelines,
1034 view_smaa_bind_groups: &SmaaBindGroups,
1035 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
1036 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
1037 neighborhood_blending_pipeline: &RenderPipeline,
1038 source: &TextureView,
1039 destination: &TextureView,
1040) {
1041 let postprocess_bind_group = render_context.render_device().create_bind_group(
1042 None,
1043 &smaa_pipelines
1044 .neighborhood_blending
1045 .postprocess_bind_group_layout,
1046 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
1047 );
1048
1049 let pass_descriptor = RenderPassDescriptor {
1050 label: Some("SMAA neighborhood blending pass"),
1051 color_attachments: &[Some(RenderPassColorAttachment {
1052 view: destination,
1053 resolve_target: None,
1054 ops: default(),
1055 })],
1056 depth_stencil_attachment: None,
1057 timestamp_writes: None,
1058 occlusion_query_set: None,
1059 };
1060
1061 let mut neighborhood_blending_render_pass = render_context
1062 .command_encoder()
1063 .begin_render_pass(&pass_descriptor);
1064 neighborhood_blending_render_pass.set_pipeline(neighborhood_blending_pipeline);
1065 neighborhood_blending_render_pass.set_bind_group(
1066 0,
1067 &postprocess_bind_group,
1068 &[**view_smaa_uniform_offset],
1069 );
1070 neighborhood_blending_render_pass.set_bind_group(
1071 1,
1072 &view_smaa_bind_groups.neighborhood_blending_bind_group,
1073 &[],
1074 );
1075 neighborhood_blending_render_pass.draw(0..3, 0..1);
1076}
1077
1078impl SmaaPreset {
1079 fn shader_def(&self) -> ShaderDefVal {
1082 match *self {
1083 SmaaPreset::Low => "SMAA_PRESET_LOW".into(),
1084 SmaaPreset::Medium => "SMAA_PRESET_MEDIUM".into(),
1085 SmaaPreset::High => "SMAA_PRESET_HIGH".into(),
1086 SmaaPreset::Ultra => "SMAA_PRESET_ULTRA".into(),
1087 }
1088 }
1089}