
# Objective - Related to #19024. ## Solution - Remove the `FULLSCREEN_SHADER_HANDLE` `weak_handle` with a resource holding the shader handle. - This also changes us from using `load_internal_asset` to `embedded_asset`/`load_embedded_asset`. - All uses have been migrated to clone the `FullscreenShader` resource and use its `to_vertex_state` method. ## Testing - `anti_aliasing` example still works. - `bloom_3d` example still works. --------- Co-authored-by: charlotte 🌸 <charlotte.c.mcelwain@gmail.com>
239 lines
7.3 KiB
Rust
239 lines
7.3 KiB
Rust
use bevy_app::prelude::*;
|
|
use bevy_asset::{embedded_asset, load_embedded_asset, Handle};
|
|
use bevy_core_pipeline::{
|
|
core_2d::graph::{Core2d, Node2d},
|
|
core_3d::graph::{Core3d, Node3d},
|
|
FullscreenShader,
|
|
};
|
|
use bevy_ecs::prelude::*;
|
|
use bevy_image::BevyDefault as _;
|
|
use bevy_reflect::{std_traits::ReflectDefault, Reflect};
|
|
use bevy_render::{
|
|
extract_component::{ExtractComponent, ExtractComponentPlugin},
|
|
prelude::Camera,
|
|
render_graph::{RenderGraphApp, ViewNodeRunner},
|
|
render_resource::{
|
|
binding_types::{sampler, texture_2d},
|
|
*,
|
|
},
|
|
renderer::RenderDevice,
|
|
view::{ExtractedView, ViewTarget},
|
|
Render, RenderApp, RenderSystems,
|
|
};
|
|
use bevy_utils::default;
|
|
|
|
mod node;
|
|
|
|
pub use node::FxaaNode;
|
|
|
|
#[derive(Debug, Reflect, Eq, PartialEq, Hash, Clone, Copy)]
|
|
#[reflect(PartialEq, Hash, Clone)]
|
|
pub enum Sensitivity {
|
|
Low,
|
|
Medium,
|
|
High,
|
|
Ultra,
|
|
Extreme,
|
|
}
|
|
|
|
impl Sensitivity {
|
|
pub fn get_str(&self) -> &str {
|
|
match self {
|
|
Sensitivity::Low => "LOW",
|
|
Sensitivity::Medium => "MEDIUM",
|
|
Sensitivity::High => "HIGH",
|
|
Sensitivity::Ultra => "ULTRA",
|
|
Sensitivity::Extreme => "EXTREME",
|
|
}
|
|
}
|
|
}
|
|
|
|
/// A component for enabling Fast Approximate Anti-Aliasing (FXAA)
|
|
/// for a [`bevy_render::camera::Camera`].
|
|
#[derive(Reflect, Component, Clone, ExtractComponent)]
|
|
#[reflect(Component, Default, Clone)]
|
|
#[extract_component_filter(With<Camera>)]
|
|
#[doc(alias = "FastApproximateAntiAliasing")]
|
|
pub struct Fxaa {
|
|
/// Enable render passes for FXAA.
|
|
pub enabled: bool,
|
|
|
|
/// Use lower sensitivity for a sharper, faster, result.
|
|
/// Use higher sensitivity for a slower, smoother, result.
|
|
/// [`Ultra`](`Sensitivity::Ultra`) and [`Extreme`](`Sensitivity::Extreme`)
|
|
/// settings can result in significant smearing and loss of detail.
|
|
///
|
|
/// The minimum amount of local contrast required to apply algorithm.
|
|
pub edge_threshold: Sensitivity,
|
|
|
|
/// Trims the algorithm from processing darks.
|
|
pub edge_threshold_min: Sensitivity,
|
|
}
|
|
|
|
impl Default for Fxaa {
|
|
fn default() -> Self {
|
|
Fxaa {
|
|
enabled: true,
|
|
edge_threshold: Sensitivity::High,
|
|
edge_threshold_min: Sensitivity::High,
|
|
}
|
|
}
|
|
}
|
|
|
|
/// Adds support for Fast Approximate Anti-Aliasing (FXAA)
|
|
pub struct FxaaPlugin;
|
|
impl Plugin for FxaaPlugin {
|
|
fn build(&self, app: &mut App) {
|
|
embedded_asset!(app, "fxaa.wgsl");
|
|
|
|
app.register_type::<Fxaa>();
|
|
app.add_plugins(ExtractComponentPlugin::<Fxaa>::default());
|
|
|
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
|
return;
|
|
};
|
|
render_app
|
|
.init_resource::<SpecializedRenderPipelines<FxaaPipeline>>()
|
|
.add_systems(
|
|
Render,
|
|
prepare_fxaa_pipelines.in_set(RenderSystems::Prepare),
|
|
)
|
|
.add_render_graph_node::<ViewNodeRunner<FxaaNode>>(Core3d, Node3d::Fxaa)
|
|
.add_render_graph_edges(
|
|
Core3d,
|
|
(
|
|
Node3d::Tonemapping,
|
|
Node3d::Fxaa,
|
|
Node3d::EndMainPassPostProcessing,
|
|
),
|
|
)
|
|
.add_render_graph_node::<ViewNodeRunner<FxaaNode>>(Core2d, Node2d::Fxaa)
|
|
.add_render_graph_edges(
|
|
Core2d,
|
|
(
|
|
Node2d::Tonemapping,
|
|
Node2d::Fxaa,
|
|
Node2d::EndMainPassPostProcessing,
|
|
),
|
|
);
|
|
}
|
|
|
|
fn finish(&self, app: &mut App) {
|
|
let Some(render_app) = app.get_sub_app_mut(RenderApp) else {
|
|
return;
|
|
};
|
|
render_app.init_resource::<FxaaPipeline>();
|
|
}
|
|
}
|
|
|
|
#[derive(Resource)]
|
|
pub struct FxaaPipeline {
|
|
texture_bind_group: BindGroupLayout,
|
|
sampler: Sampler,
|
|
fullscreen_shader: FullscreenShader,
|
|
fragment_shader: Handle<Shader>,
|
|
}
|
|
|
|
impl FromWorld for FxaaPipeline {
|
|
fn from_world(render_world: &mut World) -> Self {
|
|
let render_device = render_world.resource::<RenderDevice>();
|
|
let texture_bind_group = render_device.create_bind_group_layout(
|
|
"fxaa_texture_bind_group_layout",
|
|
&BindGroupLayoutEntries::sequential(
|
|
ShaderStages::FRAGMENT,
|
|
(
|
|
texture_2d(TextureSampleType::Float { filterable: true }),
|
|
sampler(SamplerBindingType::Filtering),
|
|
),
|
|
),
|
|
);
|
|
|
|
let sampler = render_device.create_sampler(&SamplerDescriptor {
|
|
mipmap_filter: FilterMode::Linear,
|
|
mag_filter: FilterMode::Linear,
|
|
min_filter: FilterMode::Linear,
|
|
..default()
|
|
});
|
|
|
|
FxaaPipeline {
|
|
texture_bind_group,
|
|
sampler,
|
|
fullscreen_shader: render_world.resource::<FullscreenShader>().clone(),
|
|
fragment_shader: load_embedded_asset!(render_world, "fxaa.wgsl"),
|
|
}
|
|
}
|
|
}
|
|
|
|
#[derive(Component)]
|
|
pub struct CameraFxaaPipeline {
|
|
pub pipeline_id: CachedRenderPipelineId,
|
|
}
|
|
|
|
#[derive(PartialEq, Eq, Hash, Clone, Copy)]
|
|
pub struct FxaaPipelineKey {
|
|
edge_threshold: Sensitivity,
|
|
edge_threshold_min: Sensitivity,
|
|
texture_format: TextureFormat,
|
|
}
|
|
|
|
impl SpecializedRenderPipeline for FxaaPipeline {
|
|
type Key = FxaaPipelineKey;
|
|
|
|
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
|
RenderPipelineDescriptor {
|
|
label: Some("fxaa".into()),
|
|
layout: vec![self.texture_bind_group.clone()],
|
|
vertex: self.fullscreen_shader.to_vertex_state(),
|
|
fragment: Some(FragmentState {
|
|
shader: self.fragment_shader.clone(),
|
|
shader_defs: vec![
|
|
format!("EDGE_THRESH_{}", key.edge_threshold.get_str()).into(),
|
|
format!("EDGE_THRESH_MIN_{}", key.edge_threshold_min.get_str()).into(),
|
|
],
|
|
entry_point: "fragment".into(),
|
|
targets: vec![Some(ColorTargetState {
|
|
format: key.texture_format,
|
|
blend: None,
|
|
write_mask: ColorWrites::ALL,
|
|
})],
|
|
}),
|
|
primitive: PrimitiveState::default(),
|
|
depth_stencil: None,
|
|
multisample: MultisampleState::default(),
|
|
push_constant_ranges: Vec::new(),
|
|
zero_initialize_workgroup_memory: false,
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn prepare_fxaa_pipelines(
|
|
mut commands: Commands,
|
|
pipeline_cache: Res<PipelineCache>,
|
|
mut pipelines: ResMut<SpecializedRenderPipelines<FxaaPipeline>>,
|
|
fxaa_pipeline: Res<FxaaPipeline>,
|
|
views: Query<(Entity, &ExtractedView, &Fxaa)>,
|
|
) {
|
|
for (entity, view, fxaa) in &views {
|
|
if !fxaa.enabled {
|
|
continue;
|
|
}
|
|
let pipeline_id = pipelines.specialize(
|
|
&pipeline_cache,
|
|
&fxaa_pipeline,
|
|
FxaaPipelineKey {
|
|
edge_threshold: fxaa.edge_threshold,
|
|
edge_threshold_min: fxaa.edge_threshold_min,
|
|
texture_format: if view.hdr {
|
|
ViewTarget::TEXTURE_FORMAT_HDR
|
|
} else {
|
|
TextureFormat::bevy_default()
|
|
},
|
|
},
|
|
);
|
|
|
|
commands
|
|
.entity(entity)
|
|
.insert(CameraFxaaPipeline { pipeline_id });
|
|
}
|
|
}
|