1use bevy_app::{App, Plugin};
33use bevy_asset::{embedded_asset, load_embedded_asset, AssetServer, Handle};
34#[cfg(not(feature = "smaa_luts"))]
35use bevy_core_pipeline::tonemapping::lut_placeholder;
36use bevy_core_pipeline::{
37 core_2d::graph::{Core2d, Node2d},
38 core_3d::graph::{Core3d, Node3d},
39};
40use bevy_derive::{Deref, DerefMut};
41use bevy_ecs::{
42 component::Component,
43 entity::Entity,
44 query::{QueryItem, With},
45 reflect::ReflectComponent,
46 resource::Resource,
47 schedule::IntoScheduleConfigs as _,
48 system::{lifetimeless::Read, Commands, Query, Res, ResMut},
49 world::World,
50};
51use bevy_image::{BevyDefault, Image, ToExtents};
52use bevy_math::{vec4, Vec4};
53use bevy_reflect::{std_traits::ReflectDefault, Reflect};
54use bevy_render::{
55 camera::ExtractedCamera,
56 diagnostic::RecordDiagnostics,
57 extract_component::{ExtractComponent, ExtractComponentPlugin},
58 render_asset::RenderAssets,
59 render_graph::{
60 NodeRunError, RenderGraphContext, RenderGraphExt as _, ViewNode, ViewNodeRunner,
61 },
62 render_resource::{
63 binding_types::{sampler, texture_2d, uniform_buffer},
64 AddressMode, BindGroup, BindGroupEntries, BindGroupLayoutDescriptor,
65 BindGroupLayoutEntries, CachedRenderPipelineId, ColorTargetState, ColorWrites,
66 CompareFunction, DepthStencilState, DynamicUniformBuffer, FilterMode, FragmentState,
67 LoadOp, Operations, PipelineCache, RenderPassColorAttachment,
68 RenderPassDepthStencilAttachment, RenderPassDescriptor, RenderPipeline,
69 RenderPipelineDescriptor, SamplerBindingType, SamplerDescriptor, ShaderStages, ShaderType,
70 SpecializedRenderPipeline, SpecializedRenderPipelines, StencilFaceState, StencilOperation,
71 StencilState, StoreOp, TextureDescriptor, TextureDimension, TextureFormat,
72 TextureSampleType, TextureUsages, TextureView, VertexState,
73 },
74 renderer::{RenderContext, RenderDevice, RenderQueue},
75 texture::{CachedTexture, GpuImage, TextureCache},
76 view::{ExtractedView, ViewTarget},
77 Render, RenderApp, RenderStartup, RenderSystems,
78};
79use bevy_shader::{Shader, ShaderDefVal};
80use bevy_utils::prelude::default;
81
82#[derive(Default)]
84pub struct SmaaPlugin;
85
86#[derive(Clone, Copy, Default, Component, Reflect, ExtractComponent)]
89#[reflect(Component, Default, Clone)]
90#[doc(alias = "SubpixelMorphologicalAntiAliasing")]
91pub struct Smaa {
92 pub preset: SmaaPreset,
96}
97
98#[derive(Clone, Copy, Reflect, Default, PartialEq, Eq, Hash)]
104#[reflect(Default, Clone, PartialEq, Hash)]
105pub enum SmaaPreset {
106 Low,
108
109 Medium,
111
112 #[default]
116 High,
117
118 Ultra,
120}
121
122#[derive(Resource)]
123struct SmaaLuts {
124 area_lut: Handle<Image>,
126 search_lut: Handle<Image>,
128}
129
130#[derive(Resource)]
134pub struct SmaaPipelines {
135 edge_detection: SmaaEdgeDetectionPipeline,
137 blending_weight_calculation: SmaaBlendingWeightCalculationPipeline,
139 neighborhood_blending: SmaaNeighborhoodBlendingPipeline,
141}
142
143struct SmaaEdgeDetectionPipeline {
145 postprocess_bind_group_layout: BindGroupLayoutDescriptor,
147 edge_detection_bind_group_layout: BindGroupLayoutDescriptor,
149 shader: Handle<Shader>,
151}
152
153struct SmaaBlendingWeightCalculationPipeline {
155 postprocess_bind_group_layout: BindGroupLayoutDescriptor,
157 blending_weight_calculation_bind_group_layout: BindGroupLayoutDescriptor,
159 shader: Handle<Shader>,
161}
162
163struct SmaaNeighborhoodBlendingPipeline {
165 postprocess_bind_group_layout: BindGroupLayoutDescriptor,
167 neighborhood_blending_bind_group_layout: BindGroupLayoutDescriptor,
169 shader: Handle<Shader>,
171}
172
173#[derive(Clone, PartialEq, Eq, Hash)]
175pub struct SmaaNeighborhoodBlendingPipelineKey {
176 texture_format: TextureFormat,
178 preset: SmaaPreset,
180}
181
182#[derive(Component)]
187pub struct ViewSmaaPipelines {
188 edge_detection_pipeline_id: CachedRenderPipelineId,
190 blending_weight_calculation_pipeline_id: CachedRenderPipelineId,
192 neighborhood_blending_pipeline_id: CachedRenderPipelineId,
194}
195
196#[derive(Default)]
199pub struct SmaaNode;
200
201#[derive(Clone, Copy, ShaderType)]
208pub struct SmaaInfoUniform {
209 pub rt_metrics: Vec4,
219}
220
221#[derive(Clone, Copy, Deref, DerefMut, Component)]
224pub struct SmaaInfoUniformOffset(pub u32);
225
226#[derive(Resource, Default, Deref, DerefMut)]
230pub struct SmaaInfoUniformBuffer(pub DynamicUniformBuffer<SmaaInfoUniform>);
231
232#[derive(Component)]
237pub struct SmaaTextures {
238 pub edge_detection_color_texture: CachedTexture,
244
245 pub edge_detection_stencil_texture: CachedTexture,
251
252 pub blend_texture: CachedTexture,
258}
259
260#[derive(Component)]
265pub struct SmaaBindGroups {
266 pub edge_detection_bind_group: BindGroup,
268 pub blending_weight_calculation_bind_group: BindGroup,
270 pub neighborhood_blending_bind_group: BindGroup,
272}
273
274#[derive(Resource, Default)]
279pub struct SmaaSpecializedRenderPipelines {
280 edge_detection: SpecializedRenderPipelines<SmaaEdgeDetectionPipeline>,
282
283 blending_weight_calculation: SpecializedRenderPipelines<SmaaBlendingWeightCalculationPipeline>,
286
287 neighborhood_blending: SpecializedRenderPipelines<SmaaNeighborhoodBlendingPipeline>,
290}
291
292impl Plugin for SmaaPlugin {
293 fn build(&self, app: &mut App) {
294 embedded_asset!(app, "smaa.wgsl");
296
297 #[cfg(feature = "smaa_luts")]
298 let smaa_luts = {
299 use bevy_asset::RenderAssetUsages;
300 use bevy_image::ImageLoaderSettings;
301
302 embedded_asset!(app, "SMAAAreaLUT.ktx2");
304 embedded_asset!(app, "SMAASearchLUT.ktx2");
305
306 SmaaLuts {
307 area_lut: load_embedded_asset!(
308 app,
309 "SMAAAreaLUT.ktx2",
310 |settings: &mut ImageLoaderSettings| {
311 settings.is_srgb = false;
312 settings.asset_usage = RenderAssetUsages::RENDER_WORLD;
313 }
314 ),
315 search_lut: load_embedded_asset!(
316 app,
317 "SMAASearchLUT.ktx2",
318 |settings: &mut ImageLoaderSettings| {
319 settings.is_srgb = false;
320 settings.asset_usage = RenderAssetUsages::RENDER_WORLD;
321 }
322 ),
323 }
324 };
325 #[cfg(not(feature = "smaa_luts"))]
326 let smaa_luts = {
327 let mut images = app.world_mut().resource_mut::<bevy_asset::Assets<Image>>();
328 let handle = images.add(lut_placeholder());
329 SmaaLuts {
330 area_lut: handle.clone(),
331 search_lut: handle.clone(),
332 }
333 };
334
335 app.add_plugins(ExtractComponentPlugin::<Smaa>::default());
336
337 let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
338 return;
339 };
340
341 render_app
342 .insert_resource(smaa_luts)
343 .init_resource::<SmaaSpecializedRenderPipelines>()
344 .init_resource::<SmaaInfoUniformBuffer>()
345 .add_systems(RenderStartup, init_smaa_pipelines)
346 .add_systems(
347 Render,
348 (
349 prepare_smaa_pipelines.in_set(RenderSystems::Prepare),
350 prepare_smaa_uniforms.in_set(RenderSystems::PrepareResources),
351 prepare_smaa_textures.in_set(RenderSystems::PrepareResources),
352 prepare_smaa_bind_groups.in_set(RenderSystems::PrepareBindGroups),
353 ),
354 )
355 .add_render_graph_node::<ViewNodeRunner<SmaaNode>>(Core3d, Node3d::Smaa)
356 .add_render_graph_edges(
357 Core3d,
358 (
359 Node3d::Tonemapping,
360 Node3d::Smaa,
361 Node3d::EndMainPassPostProcessing,
362 ),
363 )
364 .add_render_graph_node::<ViewNodeRunner<SmaaNode>>(Core2d, Node2d::Smaa)
365 .add_render_graph_edges(
366 Core2d,
367 (
368 Node2d::Tonemapping,
369 Node2d::Smaa,
370 Node2d::EndMainPassPostProcessing,
371 ),
372 );
373 }
374}
375
376pub fn init_smaa_pipelines(mut commands: Commands, asset_server: Res<AssetServer>) {
377 let postprocess_bind_group_layout = BindGroupLayoutDescriptor::new(
379 "SMAA postprocess bind group layout",
380 &BindGroupLayoutEntries::sequential(
381 ShaderStages::FRAGMENT,
382 (
383 texture_2d(TextureSampleType::Float { filterable: true }),
384 uniform_buffer::<SmaaInfoUniform>(true).visibility(ShaderStages::VERTEX_FRAGMENT),
385 ),
386 ),
387 );
388
389 let edge_detection_bind_group_layout = BindGroupLayoutDescriptor::new(
391 "SMAA edge detection bind group layout",
392 &BindGroupLayoutEntries::sequential(
393 ShaderStages::FRAGMENT,
394 (sampler(SamplerBindingType::Filtering),),
395 ),
396 );
397
398 let blending_weight_calculation_bind_group_layout = BindGroupLayoutDescriptor::new(
400 "SMAA blending weight calculation bind group layout",
401 &BindGroupLayoutEntries::sequential(
402 ShaderStages::FRAGMENT,
403 (
404 texture_2d(TextureSampleType::Float { filterable: true }), sampler(SamplerBindingType::Filtering), texture_2d(TextureSampleType::Float { filterable: true }), texture_2d(TextureSampleType::Float { filterable: true }), ),
409 ),
410 );
411
412 let neighborhood_blending_bind_group_layout = BindGroupLayoutDescriptor::new(
414 "SMAA neighborhood blending bind group layout",
415 &BindGroupLayoutEntries::sequential(
416 ShaderStages::FRAGMENT,
417 (
418 texture_2d(TextureSampleType::Float { filterable: true }),
419 sampler(SamplerBindingType::Filtering),
420 ),
421 ),
422 );
423
424 let shader = load_embedded_asset!(asset_server.as_ref(), "smaa.wgsl");
425
426 commands.insert_resource(SmaaPipelines {
427 edge_detection: SmaaEdgeDetectionPipeline {
428 postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
429 edge_detection_bind_group_layout,
430 shader: shader.clone(),
431 },
432 blending_weight_calculation: SmaaBlendingWeightCalculationPipeline {
433 postprocess_bind_group_layout: postprocess_bind_group_layout.clone(),
434 blending_weight_calculation_bind_group_layout,
435 shader: shader.clone(),
436 },
437 neighborhood_blending: SmaaNeighborhoodBlendingPipeline {
438 postprocess_bind_group_layout,
439 neighborhood_blending_bind_group_layout,
440 shader,
441 },
442 });
443}
444
445impl SpecializedRenderPipeline for SmaaEdgeDetectionPipeline {
447 type Key = SmaaPreset;
448
449 fn specialize(&self, preset: Self::Key) -> RenderPipelineDescriptor {
450 let shader_defs = vec!["SMAA_EDGE_DETECTION".into(), preset.shader_def()];
451
452 let stencil_face_state = StencilFaceState {
456 compare: CompareFunction::Always,
457 fail_op: StencilOperation::Replace,
458 depth_fail_op: StencilOperation::Replace,
459 pass_op: StencilOperation::Replace,
460 };
461
462 RenderPipelineDescriptor {
463 label: Some("SMAA edge detection".into()),
464 layout: vec![
465 self.postprocess_bind_group_layout.clone(),
466 self.edge_detection_bind_group_layout.clone(),
467 ],
468 vertex: VertexState {
469 shader: self.shader.clone(),
470 shader_defs: shader_defs.clone(),
471 entry_point: Some("edge_detection_vertex_main".into()),
472 buffers: vec![],
473 },
474 fragment: Some(FragmentState {
475 shader: self.shader.clone(),
476 shader_defs,
477 entry_point: Some("luma_edge_detection_fragment_main".into()),
478 targets: vec![Some(ColorTargetState {
479 format: TextureFormat::Rg8Unorm,
480 blend: None,
481 write_mask: ColorWrites::ALL,
482 })],
483 }),
484 depth_stencil: Some(DepthStencilState {
485 format: TextureFormat::Stencil8,
486 depth_write_enabled: false,
487 depth_compare: CompareFunction::Always,
488 stencil: StencilState {
489 front: stencil_face_state,
490 back: stencil_face_state,
491 read_mask: 1,
492 write_mask: 1,
493 },
494 bias: default(),
495 }),
496 ..default()
497 }
498 }
499}
500
501impl SpecializedRenderPipeline for SmaaBlendingWeightCalculationPipeline {
503 type Key = SmaaPreset;
504
505 fn specialize(&self, preset: Self::Key) -> RenderPipelineDescriptor {
506 let shader_defs = vec![
507 "SMAA_BLENDING_WEIGHT_CALCULATION".into(),
508 preset.shader_def(),
509 ];
510
511 let stencil_face_state = StencilFaceState {
513 compare: CompareFunction::Equal,
514 fail_op: StencilOperation::Keep,
515 depth_fail_op: StencilOperation::Keep,
516 pass_op: StencilOperation::Keep,
517 };
518
519 RenderPipelineDescriptor {
520 label: Some("SMAA blending weight calculation".into()),
521 layout: vec![
522 self.postprocess_bind_group_layout.clone(),
523 self.blending_weight_calculation_bind_group_layout.clone(),
524 ],
525 vertex: VertexState {
526 shader: self.shader.clone(),
527 shader_defs: shader_defs.clone(),
528 entry_point: Some("blending_weight_calculation_vertex_main".into()),
529 buffers: vec![],
530 },
531 fragment: Some(FragmentState {
532 shader: self.shader.clone(),
533 shader_defs,
534 entry_point: Some("blending_weight_calculation_fragment_main".into()),
535 targets: vec![Some(ColorTargetState {
536 format: TextureFormat::Rgba8Unorm,
537 blend: None,
538 write_mask: ColorWrites::ALL,
539 })],
540 }),
541 depth_stencil: Some(DepthStencilState {
542 format: TextureFormat::Stencil8,
543 depth_write_enabled: false,
544 depth_compare: CompareFunction::Always,
545 stencil: StencilState {
546 front: stencil_face_state,
547 back: stencil_face_state,
548 read_mask: 1,
549 write_mask: 1,
550 },
551 bias: default(),
552 }),
553 ..default()
554 }
555 }
556}
557
558impl SpecializedRenderPipeline for SmaaNeighborhoodBlendingPipeline {
559 type Key = SmaaNeighborhoodBlendingPipelineKey;
560
561 fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
562 let shader_defs = vec!["SMAA_NEIGHBORHOOD_BLENDING".into(), key.preset.shader_def()];
563
564 RenderPipelineDescriptor {
565 label: Some("SMAA neighborhood blending".into()),
566 layout: vec![
567 self.postprocess_bind_group_layout.clone(),
568 self.neighborhood_blending_bind_group_layout.clone(),
569 ],
570 vertex: VertexState {
571 shader: self.shader.clone(),
572 shader_defs: shader_defs.clone(),
573 entry_point: Some("neighborhood_blending_vertex_main".into()),
574 buffers: vec![],
575 },
576 fragment: Some(FragmentState {
577 shader: self.shader.clone(),
578 shader_defs,
579 entry_point: Some("neighborhood_blending_fragment_main".into()),
580 targets: vec![Some(ColorTargetState {
581 format: key.texture_format,
582 blend: None,
583 write_mask: ColorWrites::ALL,
584 })],
585 }),
586 ..default()
587 }
588 }
589}
590
591fn prepare_smaa_pipelines(
594 mut commands: Commands,
595 pipeline_cache: Res<PipelineCache>,
596 mut specialized_render_pipelines: ResMut<SmaaSpecializedRenderPipelines>,
597 smaa_pipelines: Res<SmaaPipelines>,
598 view_targets: Query<(Entity, &ExtractedView, &Smaa)>,
599) {
600 for (entity, view, smaa) in &view_targets {
601 let edge_detection_pipeline_id = specialized_render_pipelines.edge_detection.specialize(
602 &pipeline_cache,
603 &smaa_pipelines.edge_detection,
604 smaa.preset,
605 );
606
607 let blending_weight_calculation_pipeline_id = specialized_render_pipelines
608 .blending_weight_calculation
609 .specialize(
610 &pipeline_cache,
611 &smaa_pipelines.blending_weight_calculation,
612 smaa.preset,
613 );
614
615 let neighborhood_blending_pipeline_id = specialized_render_pipelines
616 .neighborhood_blending
617 .specialize(
618 &pipeline_cache,
619 &smaa_pipelines.neighborhood_blending,
620 SmaaNeighborhoodBlendingPipelineKey {
621 texture_format: if view.hdr {
622 ViewTarget::TEXTURE_FORMAT_HDR
623 } else {
624 TextureFormat::bevy_default()
625 },
626 preset: smaa.preset,
627 },
628 );
629
630 commands.entity(entity).insert(ViewSmaaPipelines {
631 edge_detection_pipeline_id,
632 blending_weight_calculation_pipeline_id,
633 neighborhood_blending_pipeline_id,
634 });
635 }
636}
637
638fn prepare_smaa_uniforms(
641 mut commands: Commands,
642 render_device: Res<RenderDevice>,
643 render_queue: Res<RenderQueue>,
644 view_targets: Query<(Entity, &ExtractedView), With<Smaa>>,
645 mut smaa_info_buffer: ResMut<SmaaInfoUniformBuffer>,
646) {
647 smaa_info_buffer.clear();
648 for (entity, view) in &view_targets {
649 let offset = smaa_info_buffer.push(&SmaaInfoUniform {
650 rt_metrics: vec4(
651 1.0 / view.viewport.z as f32,
652 1.0 / view.viewport.w as f32,
653 view.viewport.z as f32,
654 view.viewport.w as f32,
655 ),
656 });
657 commands
658 .entity(entity)
659 .insert(SmaaInfoUniformOffset(offset));
660 }
661
662 smaa_info_buffer.write_buffer(&render_device, &render_queue);
663}
664
665fn prepare_smaa_textures(
672 mut commands: Commands,
673 render_device: Res<RenderDevice>,
674 mut texture_cache: ResMut<TextureCache>,
675 view_targets: Query<(Entity, &ExtractedCamera), (With<ExtractedView>, With<Smaa>)>,
676) {
677 for (entity, camera) in &view_targets {
678 let Some(texture_size) = camera.physical_target_size else {
679 continue;
680 };
681
682 let edge_detection_color_texture = texture_cache.get(
684 &render_device,
685 TextureDescriptor {
686 label: Some("SMAA edge detection color texture"),
687 size: texture_size.to_extents(),
688 mip_level_count: 1,
689 sample_count: 1,
690 dimension: TextureDimension::D2,
691 format: TextureFormat::Rg8Unorm,
692 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::RENDER_ATTACHMENT,
693 view_formats: &[],
694 },
695 );
696
697 let edge_detection_stencil_texture = texture_cache.get(
699 &render_device,
700 TextureDescriptor {
701 label: Some("SMAA edge detection stencil texture"),
702 size: texture_size.to_extents(),
703 mip_level_count: 1,
704 sample_count: 1,
705 dimension: TextureDimension::D2,
706 format: TextureFormat::Stencil8,
707 usage: TextureUsages::RENDER_ATTACHMENT,
708 view_formats: &[],
709 },
710 );
711
712 let blend_texture = texture_cache.get(
715 &render_device,
716 TextureDescriptor {
717 label: Some("SMAA blend texture"),
718 size: texture_size.to_extents(),
719 mip_level_count: 1,
720 sample_count: 1,
721 dimension: TextureDimension::D2,
722 format: TextureFormat::Rgba8Unorm,
723 usage: TextureUsages::TEXTURE_BINDING | TextureUsages::RENDER_ATTACHMENT,
724 view_formats: &[],
725 },
726 );
727
728 commands.entity(entity).insert(SmaaTextures {
729 edge_detection_color_texture,
730 edge_detection_stencil_texture,
731 blend_texture,
732 });
733 }
734}
735
736fn prepare_smaa_bind_groups(
739 mut commands: Commands,
740 render_device: Res<RenderDevice>,
741 smaa_pipelines: Res<SmaaPipelines>,
742 smaa_luts: Res<SmaaLuts>,
743 images: Res<RenderAssets<GpuImage>>,
744 pipeline_cache: Res<PipelineCache>,
745 view_targets: Query<(Entity, &SmaaTextures), (With<ExtractedView>, With<Smaa>)>,
746) {
747 let (Some(search_texture), Some(area_texture)) = (
749 images.get(&smaa_luts.search_lut),
750 images.get(&smaa_luts.area_lut),
751 ) else {
752 return;
753 };
754
755 for (entity, smaa_textures) in &view_targets {
756 let sampler = render_device.create_sampler(&SamplerDescriptor {
759 label: Some("SMAA sampler"),
760 address_mode_u: AddressMode::ClampToEdge,
761 address_mode_v: AddressMode::ClampToEdge,
762 address_mode_w: AddressMode::ClampToEdge,
763 mag_filter: FilterMode::Linear,
764 min_filter: FilterMode::Linear,
765 ..default()
766 });
767
768 commands.entity(entity).insert(SmaaBindGroups {
769 edge_detection_bind_group: render_device.create_bind_group(
770 Some("SMAA edge detection bind group"),
771 &pipeline_cache.get_bind_group_layout(
772 &smaa_pipelines
773 .edge_detection
774 .edge_detection_bind_group_layout,
775 ),
776 &BindGroupEntries::sequential((&sampler,)),
777 ),
778 blending_weight_calculation_bind_group: render_device.create_bind_group(
779 Some("SMAA blending weight calculation bind group"),
780 &pipeline_cache.get_bind_group_layout(
781 &smaa_pipelines
782 .blending_weight_calculation
783 .blending_weight_calculation_bind_group_layout,
784 ),
785 &BindGroupEntries::sequential((
786 &smaa_textures.edge_detection_color_texture.default_view,
787 &sampler,
788 &search_texture.texture_view,
789 &area_texture.texture_view,
790 )),
791 ),
792 neighborhood_blending_bind_group: render_device.create_bind_group(
793 Some("SMAA neighborhood blending bind group"),
794 &pipeline_cache.get_bind_group_layout(
795 &smaa_pipelines
796 .neighborhood_blending
797 .neighborhood_blending_bind_group_layout,
798 ),
799 &BindGroupEntries::sequential((
800 &smaa_textures.blend_texture.default_view,
801 &sampler,
802 )),
803 ),
804 });
805 }
806}
807
808impl ViewNode for SmaaNode {
809 type ViewQuery = (
810 Read<ViewTarget>,
811 Read<ViewSmaaPipelines>,
812 Read<SmaaInfoUniformOffset>,
813 Read<SmaaTextures>,
814 Read<SmaaBindGroups>,
815 );
816
817 fn run<'w>(
818 &self,
819 _: &mut RenderGraphContext,
820 render_context: &mut RenderContext<'w>,
821 (
822 view_target,
823 view_pipelines,
824 view_smaa_uniform_offset,
825 smaa_textures,
826 view_smaa_bind_groups,
827 ): QueryItem<'w, '_, Self::ViewQuery>,
828 world: &'w World,
829 ) -> Result<(), NodeRunError> {
830 let pipeline_cache = world.resource::<PipelineCache>();
831 let smaa_pipelines = world.resource::<SmaaPipelines>();
832 let smaa_info_uniform_buffer = world.resource::<SmaaInfoUniformBuffer>();
833
834 let (
836 Some(edge_detection_pipeline),
837 Some(blending_weight_calculation_pipeline),
838 Some(neighborhood_blending_pipeline),
839 ) = (
840 pipeline_cache.get_render_pipeline(view_pipelines.edge_detection_pipeline_id),
841 pipeline_cache
842 .get_render_pipeline(view_pipelines.blending_weight_calculation_pipeline_id),
843 pipeline_cache.get_render_pipeline(view_pipelines.neighborhood_blending_pipeline_id),
844 )
845 else {
846 return Ok(());
847 };
848
849 let diagnostics = render_context.diagnostic_recorder();
850 render_context.command_encoder().push_debug_group("smaa");
851 let time_span = diagnostics.time_span(render_context.command_encoder(), "smaa");
852
853 let postprocess = view_target.post_process_write();
855 let (source, destination) = (postprocess.source, postprocess.destination);
856
857 perform_edge_detection(
859 render_context,
860 pipeline_cache,
861 smaa_pipelines,
862 smaa_textures,
863 view_smaa_bind_groups,
864 smaa_info_uniform_buffer,
865 view_smaa_uniform_offset,
866 edge_detection_pipeline,
867 source,
868 );
869
870 perform_blending_weight_calculation(
872 render_context,
873 pipeline_cache,
874 smaa_pipelines,
875 smaa_textures,
876 view_smaa_bind_groups,
877 smaa_info_uniform_buffer,
878 view_smaa_uniform_offset,
879 blending_weight_calculation_pipeline,
880 source,
881 );
882
883 perform_neighborhood_blending(
885 render_context,
886 pipeline_cache,
887 smaa_pipelines,
888 view_smaa_bind_groups,
889 smaa_info_uniform_buffer,
890 view_smaa_uniform_offset,
891 neighborhood_blending_pipeline,
892 source,
893 destination,
894 );
895
896 time_span.end(render_context.command_encoder());
897 render_context.command_encoder().pop_debug_group();
898
899 Ok(())
900 }
901}
902
903fn perform_edge_detection(
910 render_context: &mut RenderContext,
911 pipeline_cache: &PipelineCache,
912 smaa_pipelines: &SmaaPipelines,
913 smaa_textures: &SmaaTextures,
914 view_smaa_bind_groups: &SmaaBindGroups,
915 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
916 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
917 edge_detection_pipeline: &RenderPipeline,
918 source: &TextureView,
919) {
920 let postprocess_bind_group = render_context.render_device().create_bind_group(
922 None,
923 &pipeline_cache
924 .get_bind_group_layout(&smaa_pipelines.edge_detection.postprocess_bind_group_layout),
925 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
926 );
927
928 let pass_descriptor = RenderPassDescriptor {
930 label: Some("SMAA edge detection pass"),
931 color_attachments: &[Some(RenderPassColorAttachment {
932 view: &smaa_textures.edge_detection_color_texture.default_view,
933 depth_slice: None,
934 resolve_target: None,
935 ops: default(),
936 })],
937 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
938 view: &smaa_textures.edge_detection_stencil_texture.default_view,
939 depth_ops: None,
940 stencil_ops: Some(Operations {
941 load: LoadOp::Clear(0),
942 store: StoreOp::Store,
943 }),
944 }),
945 timestamp_writes: None,
946 occlusion_query_set: None,
947 };
948
949 let mut render_pass = render_context
951 .command_encoder()
952 .begin_render_pass(&pass_descriptor);
953 render_pass.set_pipeline(edge_detection_pipeline);
954 render_pass.set_bind_group(0, &postprocess_bind_group, &[**view_smaa_uniform_offset]);
955 render_pass.set_bind_group(1, &view_smaa_bind_groups.edge_detection_bind_group, &[]);
956 render_pass.set_stencil_reference(1);
957 render_pass.draw(0..3, 0..1);
958}
959
960fn perform_blending_weight_calculation(
966 render_context: &mut RenderContext,
967 pipeline_cache: &PipelineCache,
968 smaa_pipelines: &SmaaPipelines,
969 smaa_textures: &SmaaTextures,
970 view_smaa_bind_groups: &SmaaBindGroups,
971 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
972 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
973 blending_weight_calculation_pipeline: &RenderPipeline,
974 source: &TextureView,
975) {
976 let postprocess_bind_group = render_context.render_device().create_bind_group(
978 None,
979 &pipeline_cache.get_bind_group_layout(
980 &smaa_pipelines
981 .blending_weight_calculation
982 .postprocess_bind_group_layout,
983 ),
984 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
985 );
986
987 let pass_descriptor = RenderPassDescriptor {
989 label: Some("SMAA blending weight calculation pass"),
990 color_attachments: &[Some(RenderPassColorAttachment {
991 view: &smaa_textures.blend_texture.default_view,
992 depth_slice: None,
993 resolve_target: None,
994 ops: default(),
995 })],
996 depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
997 view: &smaa_textures.edge_detection_stencil_texture.default_view,
998 depth_ops: None,
999 stencil_ops: Some(Operations {
1000 load: LoadOp::Load,
1001 store: StoreOp::Discard,
1002 }),
1003 }),
1004 timestamp_writes: None,
1005 occlusion_query_set: None,
1006 };
1007
1008 let mut render_pass = render_context
1010 .command_encoder()
1011 .begin_render_pass(&pass_descriptor);
1012 render_pass.set_pipeline(blending_weight_calculation_pipeline);
1013 render_pass.set_bind_group(0, &postprocess_bind_group, &[**view_smaa_uniform_offset]);
1014 render_pass.set_bind_group(
1015 1,
1016 &view_smaa_bind_groups.blending_weight_calculation_bind_group,
1017 &[],
1018 );
1019 render_pass.set_stencil_reference(1);
1020 render_pass.draw(0..3, 0..1);
1021}
1022
1023fn perform_neighborhood_blending(
1028 render_context: &mut RenderContext,
1029 pipeline_cache: &PipelineCache,
1030 smaa_pipelines: &SmaaPipelines,
1031 view_smaa_bind_groups: &SmaaBindGroups,
1032 smaa_info_uniform_buffer: &SmaaInfoUniformBuffer,
1033 view_smaa_uniform_offset: &SmaaInfoUniformOffset,
1034 neighborhood_blending_pipeline: &RenderPipeline,
1035 source: &TextureView,
1036 destination: &TextureView,
1037) {
1038 let postprocess_bind_group = render_context.render_device().create_bind_group(
1039 None,
1040 &pipeline_cache.get_bind_group_layout(
1041 &smaa_pipelines
1042 .neighborhood_blending
1043 .postprocess_bind_group_layout,
1044 ),
1045 &BindGroupEntries::sequential((source, &**smaa_info_uniform_buffer)),
1046 );
1047
1048 let pass_descriptor = RenderPassDescriptor {
1049 label: Some("SMAA neighborhood blending pass"),
1050 color_attachments: &[Some(RenderPassColorAttachment {
1051 view: destination,
1052 depth_slice: None,
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}