From 9bbde400ab8a87bbb2f576a5fc4daab47870198e Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Thu, 27 Oct 2022 20:14:18 -0700 Subject: [PATCH 01/18] add fxaa --- Cargo.toml | 10 + crates/bevy_core_pipeline/src/fxaa/blit.wgsl | 11 + crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 285 +++++++++++++++ crates/bevy_core_pipeline/src/fxaa/mod.rs | 334 ++++++++++++++++++ crates/bevy_core_pipeline/src/fxaa/node.rs | 177 ++++++++++ .../bevy_core_pipeline/src/fxaa/to_ldr.wgsl | 21 ++ crates/bevy_core_pipeline/src/lib.rs | 1 + examples/3d/fxaa.rs | 172 +++++++++ examples/wasm/assets | 0 9 files changed, 1011 insertions(+) create mode 100644 crates/bevy_core_pipeline/src/fxaa/blit.wgsl create mode 100644 crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl create mode 100644 crates/bevy_core_pipeline/src/fxaa/mod.rs create mode 100644 crates/bevy_core_pipeline/src/fxaa/node.rs create mode 100644 crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl create mode 100644 examples/3d/fxaa.rs mode change 120000 => 100644 examples/wasm/assets diff --git a/Cargo.toml b/Cargo.toml index f4c6f3f9f9037..7f2d1fd25317b 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -323,6 +323,16 @@ description = "Loads and renders a glTF file as a scene" category = "3D Rendering" wasm = true +[[example]] +name = "fxaa" +path = "examples/3d/fxaa.rs" + +[package.metadata.example.fxaa] +name = "FXAA" +description = "Compares MSAA (Multi-Sample Anti-Aliasing) and FXAA (Fast Approximate Anti-Aliasing)" +category = "3D Rendering" +wasm = true + [[example]] name = "msaa" path = "examples/3d/msaa.rs" diff --git a/crates/bevy_core_pipeline/src/fxaa/blit.wgsl b/crates/bevy_core_pipeline/src/fxaa/blit.wgsl new file mode 100644 index 0000000000000..feb46c5405b35 --- /dev/null +++ b/crates/bevy_core_pipeline/src/fxaa/blit.wgsl @@ -0,0 +1,11 @@ +#import bevy_core_pipeline::fullscreen_vertex_shader + +@group(0) @binding(0) +var texture: texture_2d; +@group(0) @binding(1) +var texture_sampler: sampler; + +@fragment +fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { + return textureSample(texture, texture_sampler, in.uv); +} diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl new file mode 100644 index 0000000000000..b0fb714b55c75 --- /dev/null +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -0,0 +1,285 @@ +// NVIDIA FXAA 3.11 +// Original source code by TIMOTHY LOTTES +// https://gist.github.com/kosua20/0c506b81b3812ac900048059d2383126 +// +// Cleaned version - https://github.com/kosua20/Rendu/blob/master/resources/common/shaders/screens/fxaa.frag +// +// Tweaks by mrDIMAS - https://github.com/FyroxEngine/Fyrox/blob/master/src/renderer/shaders/fxaa_fs.glsl + +#import bevy_core_pipeline::fullscreen_vertex_shader + +@group(0) @binding(0) +var screenTexture: texture_2d; +@group(0) @binding(1) +var samp: sampler; + +// Trims the algorithm from processing darks. +#ifdef EDGE_THRESH_MIN_LOW + let EDGE_THRESHOLD_MIN: f32 = 0.0833; +#endif + +#ifdef EDGE_THRESH_MIN_MEDIUM + let EDGE_THRESHOLD_MIN: f32 = 0.0625; +#endif + +#ifdef EDGE_THRESH_MIN_HIGH + let EDGE_THRESHOLD_MIN: f32 = 0.0312; +#endif + +#ifdef EDGE_THRESH_MIN_ULTRA + let EDGE_THRESHOLD_MIN: f32 = 0.0156; +#endif + +// The minimum amount of local contrast required to apply algorithm. +#ifdef EDGE_THRESH_LOW + let EDGE_THRESHOLD_MAX: f32 = 0.250; +#endif + +#ifdef EDGE_THRESH_MEDIUM + let EDGE_THRESHOLD_MAX: f32 = 0.166; +#endif + +#ifdef EDGE_THRESH_HIGH + let EDGE_THRESHOLD_MAX: f32 = 0.125; +#endif + +#ifdef EDGE_THRESH_ULTRA + let EDGE_THRESHOLD_MAX: f32 = 0.063; +#endif + +let ITERATIONS: i32 = 12; //default is 12 +let SUBPIXEL_QUALITY: f32 = 0.75; +// #define QUALITY(q) ((q) < 5 ? 1.0 : ((q) > 5 ? ((q) < 10 ? 2.0 : ((q) < 11 ? 4.0 : 8.0)) : 1.5)) +fn QUALITY(q: i32) -> f32 { + switch (q) { + //case 0, 1, 2, 3, 4: { return 1.0; } + default: { return 1.0; } + case 5: { return 1.5; } + case 6, 7, 8, 9: { return 2.0; } + case 10: { return 4.0; } + case 11: { return 8.0; } + } +} + +fn rgb2luma(rgb: vec3) -> f32 { + return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114))); +} + +// https://gpuopen.com/learn/optimized-reversible-tonemapper-for-resolve/ +fn tonemap(c: vec3) -> vec3 { + return c * (1.0 / (max(c.r, max(c.g, c.b)) + 1.0)); +} + +fn tonemap_invert(c: vec3) -> vec3 { + return c * (1.0 / (1.0 - max(c.r, max(c.g, c.b)))); +} + +// Performs FXAA post-process anti-aliasing as described in the Nvidia FXAA white paper and the associated shader code. +@fragment +fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { + let resolution = vec2(textureDimensions(screenTexture)); + let fragCoord = in.position.xy; + let inverseScreenSize = 1.0 / resolution.xy; + let texCoord = in.position.xy * inverseScreenSize; + + let centerSample = textureSampleLevel(screenTexture, samp, texCoord, 0.0); + let colorCenter = centerSample.rgb; + + // Luma at the current fragment + let lumaCenter = rgb2luma(colorCenter); + + // Luma at the four direct neighbours of the current fragment. + let lumaDown = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(0, -1)).rgb); + let lumaUp = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(0, 1)).rgb); + let lumaLeft = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(-1, 0)).rgb); + let lumaRight = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(1, 0)).rgb); + + // Find the maximum and minimum luma around the current fragment. + let lumaMin = min(lumaCenter, min(min(lumaDown, lumaUp), min(lumaLeft, lumaRight))); + let lumaMax = max(lumaCenter, max(max(lumaDown, lumaUp), max(lumaLeft, lumaRight))); + + // Compute the delta. + let lumaRange = lumaMax - lumaMin; + + // If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA. + if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax*EDGE_THRESHOLD_MAX)) { + var col = centerSample; + #ifdef TONEMAP + col = vec4(tonemap_invert(col.rgb), col.a); + #endif + return col; + } + + // Query the 4 remaining corners lumas. + let lumaDownLeft = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(-1, -1)).rgb); + let lumaUpRight = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(1, 1)).rgb); + let lumaUpLeft = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(-1, 1)).rgb); + let lumaDownRight = rgb2luma(textureSampleLevel(screenTexture, samp, texCoord, 0.0, vec2(1, -1)).rgb); + + // Combine the four edges lumas (using intermediary variables for future computations with the same values). + let lumaDownUp = lumaDown + lumaUp; + let lumaLeftRight = lumaLeft + lumaRight; + + // Same for corners + let lumaLeftCorners = lumaDownLeft + lumaUpLeft; + let lumaDownCorners = lumaDownLeft + lumaDownRight; + let lumaRightCorners = lumaDownRight + lumaUpRight; + let lumaUpCorners = lumaUpRight + lumaUpLeft; + + // Compute an estimation of the gradient along the horizontal and vertical axis. + let edgeHorizontal = abs(-2.0 * lumaLeft + lumaLeftCorners) + + abs(-2.0 * lumaCenter + lumaDownUp) * 2.0 + + abs(-2.0 * lumaRight + lumaRightCorners); + + let edgeVertical = abs(-2.0 * lumaUp + lumaUpCorners) + + abs(-2.0 * lumaCenter + lumaLeftRight) * 2.0 + + abs(-2.0 * lumaDown + lumaDownCorners); + + // Is the local edge horizontal or vertical ? + let isHorizontal = (edgeHorizontal >= edgeVertical); + + // Choose the step size (one pixel) accordingly. + var stepLength = select(inverseScreenSize.x, inverseScreenSize.y, isHorizontal); + + // Select the two neighboring texels lumas in the opposite direction to the local edge. + var luma1 = select(lumaLeft, lumaDown, isHorizontal); + var luma2 = select(lumaRight, lumaUp, isHorizontal); + + // Compute gradients in this direction. + let gradient1 = luma1 - lumaCenter; + let gradient2 = luma2 - lumaCenter; + + // Which direction is the steepest ? + let is1Steepest = abs(gradient1) >= abs(gradient2); + + // Gradient in the corresponding direction, normalized. + let gradientScaled = 0.25 * max(abs(gradient1), abs(gradient2)); + + // Average luma in the correct direction. + var lumaLocalAverage = 0.0; + if (is1Steepest) { + // Switch the direction + stepLength = -stepLength; + lumaLocalAverage = 0.5 * (luma1 + lumaCenter); + } else { + lumaLocalAverage = 0.5 * (luma2 + lumaCenter); + } + + // Shift UV in the correct direction by half a pixel. + // Compute offset (for each iteration step) in the right direction. + var currentUv = texCoord; + var offset = vec2(0.0, 0.0); + if (isHorizontal) { + currentUv.y = currentUv.y + stepLength * 0.5; + offset.x = inverseScreenSize.x; + } else { + currentUv.x = currentUv.x + stepLength * 0.5; + offset.y = inverseScreenSize.y; + } + + // Compute UVs to explore on each side of the edge, orthogonally. The QUALITY allows us to step faster. + var uv1 = currentUv - offset; // * QUALITY(0); // (quality 0 is 1.0) + var uv2 = currentUv + offset; // * QUALITY(0); // (quality 0 is 1.0) + + // Read the lumas at both current extremities of the exploration segment, and compute the delta wrt to the local average luma. + var lumaEnd1 = rgb2luma(textureSampleLevel(screenTexture, samp, uv1, 0.0).rgb); + var lumaEnd2 = rgb2luma(textureSampleLevel(screenTexture, samp, uv2, 0.0).rgb); + lumaEnd1 = lumaEnd1 - lumaLocalAverage; + lumaEnd2 = lumaEnd2 - lumaLocalAverage; + + // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge. + var reached1 = abs(lumaEnd1) >= gradientScaled; + var reached2 = abs(lumaEnd2) >= gradientScaled; + var reachedBoth = reached1 && reached2; + + // If the side is not reached, we continue to explore in this direction. + uv1 = select(uv1 - offset, uv1, reached1); // * QUALITY(1); // (quality 1 is 1.0) + uv2 = select(uv2 - offset, uv2, reached2); // * QUALITY(1); // (quality 1 is 1.0) + + // If both sides have not been reached, continue to explore. + if (!reachedBoth) { + for (var i: i32 = 2; i < ITERATIONS; i = i + 1) { + // If needed, read luma in 1st direction, compute delta. + if (!reached1) { + lumaEnd1 = rgb2luma(textureSampleLevel(screenTexture, samp, uv1, 0.0).rgb); + lumaEnd1 = lumaEnd1 - lumaLocalAverage; + } + // If needed, read luma in opposite direction, compute delta. + if (!reached2) { + lumaEnd2 = rgb2luma(textureSampleLevel(screenTexture, samp, uv2, 0.0).rgb); + lumaEnd2 = lumaEnd2 - lumaLocalAverage; + } + // If the luma deltas at the current extremities is larger than the local gradient, we have reached the side of the edge. + reached1 = abs(lumaEnd1) >= gradientScaled; + reached2 = abs(lumaEnd2) >= gradientScaled; + reachedBoth = reached1 && reached2; + + // If the side is not reached, we continue to explore in this direction, with a variable quality. + if (!reached1) { + uv1 = uv1 - offset * QUALITY(i); + } + if (!reached2) { + uv2 = uv2 + offset * QUALITY(i); + } + + // If both sides have been reached, stop the exploration. + if (reachedBoth) { + break; + } + } + } + + // Compute the distances to each side edge of the edge (!). + var distance1 = select(texCoord.y - uv1.y, texCoord.x - uv1.x, isHorizontal); + var distance2 = select(uv2.y - texCoord.y, uv2.x - texCoord.x, isHorizontal); + + // In which direction is the side of the edge closer ? + let isDirection1 = distance1 < distance2; + let distanceFinal = min(distance1, distance2); + + // Thickness of the edge. + let edgeThickness = (distance1 + distance2); + + // Is the luma at center smaller than the local average ? + let isLumaCenterSmaller = lumaCenter < lumaLocalAverage; + + // If the luma at center is smaller than at its neighbour, the delta luma at each end should be positive (same variation). + let correctVariation1 = (lumaEnd1 < 0.0) != isLumaCenterSmaller; + let correctVariation2 = (lumaEnd2 < 0.0) != isLumaCenterSmaller; + + // Only keep the result in the direction of the closer side of the edge. + var correctVariation = select(correctVariation2, correctVariation1, isDirection1); + + // UV offset: read in the direction of the closest side of the edge. + let pixelOffset = - distanceFinal / edgeThickness + 0.5; + + // If the luma variation is incorrect, do not offset. + var finalOffset = select(0.0, pixelOffset, correctVariation); + + // Sub-pixel shifting + // Full weighted average of the luma over the 3x3 neighborhood. + let lumaAverage = (1.0 / 12.0) * (2.0 * (lumaDownUp + lumaLeftRight) + lumaLeftCorners + lumaRightCorners); + // Ratio of the delta between the global average and the center luma, over the luma range in the 3x3 neighborhood. + let subPixelOffset1 = clamp(abs(lumaAverage - lumaCenter) / lumaRange, 0.0, 1.0); + let subPixelOffset2 = (-2.0 * subPixelOffset1 + 3.0) * subPixelOffset1 * subPixelOffset1; + // Compute a sub-pixel offset based on this delta. + let subPixelOffsetFinal = subPixelOffset2 * subPixelOffset2 * SUBPIXEL_QUALITY; + + // Pick the biggest of the two offsets. + finalOffset = max(finalOffset, subPixelOffsetFinal); + + // Compute the final UV coordinates. + var finalUv = texCoord; + if (isHorizontal) { + finalUv.y = finalUv.y + finalOffset * stepLength; + } else { + finalUv.x = finalUv.x + finalOffset * stepLength; + } + + // Read the color at the new UV coordinates, and use it. + var finalColor = textureSampleLevel(screenTexture, samp, finalUv, 0.0).rgb; + #ifdef TONEMAP + finalColor = tonemap_invert(finalColor); + #endif + return vec4(finalColor, centerSample.a); +} diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs new file mode 100644 index 0000000000000..f26a37c50fa60 --- /dev/null +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -0,0 +1,334 @@ +use std::borrow::Cow; + +use bevy_derive::Deref; +use bevy_ecs::query::QueryItem; +use bevy_render::camera::ExtractedCamera; +use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy_render::prelude::Camera; +use bevy_render::render_graph::RenderGraph; +use bevy_render::texture::{BevyDefault, CachedTexture, TextureCache}; +use bevy_utils::HashMap; + +use bevy_app::prelude::*; +use bevy_asset::{load_internal_asset, HandleUntyped}; +use bevy_ecs::prelude::*; +use bevy_render::renderer::RenderDevice; +use bevy_render::view::{ExtractedView, Msaa, ViewTarget}; +use bevy_render::{render_resource::*, RenderApp, RenderStage}; + +use bevy_reflect::TypeUuid; + +mod node; + +use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state; +use crate::fxaa::node::FXAANode; +use crate::{core_2d, core_3d}; +#[derive(Clone)] +pub enum Quality { + Low, + Medium, + High, + Ultra, +} + +impl Quality { + pub fn get_str(&self) -> &str { + match self { + Quality::Low => "LOW", + Quality::Medium => "MEDIUM", + Quality::High => "HIGH", + Quality::Ultra => "ULTRA", + } + } +} + +#[derive(Component, Clone)] +pub struct FXAA { + /// Enable render passes for FXAA. + pub enabled: bool, + + /// The minimum amount of local contrast required to apply algorithm. + /// Use lower settings for a sharper, faster, result. + /// Use higher settings for a slower, smoother, result. + pub edge_threshold: Quality, + + /// Trims the algorithm from processing darks. + /// Use lower settings for a sharper, faster, result. + /// Use higher settings for a slower, smoother, result. + pub edge_threshold_min: Quality, +} + +impl Default for FXAA { + fn default() -> Self { + FXAA { + enabled: true, + edge_threshold: Quality::High, + edge_threshold_min: Quality::High, + } + } +} + +impl FXAA { + pub fn get_settings(&self) -> Vec { + vec![ + format!("EDGE_THRESH_{}", self.edge_threshold.get_str()), + format!("EDGE_THRESH_MIN_{}", self.edge_threshold_min.get_str()), + ] + } +} + +impl ExtractComponent for FXAA { + type Query = &'static Self; + type Filter = With; + + fn extract_component(item: QueryItem) -> Self { + item.clone() + } +} + +const LDR_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7112161265414793412); + +const FXAA_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4182761465141723543); + +const BLIT_SHADER_HANDLE: HandleUntyped = + HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 8382381532578979433); + +pub const FXAA_NODE_3D: &str = "fxaa_node_3d"; +pub const FXAA_NODE_2D: &str = "fxaa_node_2d"; + +pub struct FXAAPlugin; +impl Plugin for FXAAPlugin { + fn build(&self, app: &mut App) { + app.insert_resource(Msaa { samples: 1 }); // Disable MSAA be default + + load_internal_asset!(app, LDR_SHADER_HANDLE, "to_ldr.wgsl", Shader::from_wgsl); + load_internal_asset!(app, FXAA_SHADER_HANDLE, "fxaa.wgsl", Shader::from_wgsl); + load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl); + + app.add_plugin(ExtractComponentPlugin::::default()); + + let render_app = match app.get_sub_app_mut(RenderApp) { + Ok(render_app) => render_app, + Err(_) => return, + }; + render_app + .init_resource::() + .add_system_to_stage(RenderStage::Prepare, prepare_fxaa_texture); + + { + let fxaa_node = FXAANode::new(&mut render_app.world); + let mut binding = render_app.world.resource_mut::(); + let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap(); + + graph.add_node(FXAA_NODE_3D, fxaa_node); + + graph + .add_slot_edge( + graph.input_node().unwrap().id, + core_3d::graph::input::VIEW_ENTITY, + FXAA_NODE_3D, + FXAANode::IN_VIEW, + ) + .unwrap(); + + graph + .add_node_edge(core_3d::graph::node::MAIN_PASS, FXAA_NODE_3D) + .unwrap(); + + graph + .add_node_edge(FXAA_NODE_3D, core_3d::graph::node::TONEMAPPING) + .unwrap(); + } + { + let fxaa_node = FXAANode::new(&mut render_app.world); + let mut binding = render_app.world.resource_mut::(); + let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap(); + + graph.add_node(FXAA_NODE_2D, fxaa_node); + + graph + .add_slot_edge( + graph.input_node().unwrap().id, + core_2d::graph::input::VIEW_ENTITY, + FXAA_NODE_2D, + FXAANode::IN_VIEW, + ) + .unwrap(); + + graph + .add_node_edge(core_2d::graph::node::MAIN_PASS, FXAA_NODE_2D) + .unwrap(); + + graph + .add_node_edge(FXAA_NODE_2D, core_2d::graph::node::TONEMAPPING) + .unwrap(); + } + } +} + +#[derive(Resource, Deref)] +pub struct FXAAPipelineBindGroup(BindGroupLayout); + +impl FromWorld for FXAAPipelineBindGroup { + fn from_world(render_world: &mut World) -> Self { + let fxaa_texture_bind_group = render_world + .resource::() + .create_bind_group_layout(&BindGroupLayoutDescriptor { + label: Some("fxaa_texture_bind_group_layout"), + entries: &[ + BindGroupLayoutEntry { + binding: 0, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Texture { + sample_type: TextureSampleType::Float { filterable: true }, + view_dimension: TextureViewDimension::D2, + multisampled: false, + }, + count: None, + }, + BindGroupLayoutEntry { + binding: 1, + visibility: ShaderStages::FRAGMENT, + ty: BindingType::Sampler(SamplerBindingType::Filtering), + count: None, + }, + ], + }); + + FXAAPipelineBindGroup(fxaa_texture_bind_group) + } +} + +fn fullscreen_vertex_pipeline_descriptor( + label: &'static str, + bind_group_layout: &BindGroupLayout, + shader: HandleUntyped, + shader_defs: Vec, + entry_point: &'static str, + format: TextureFormat, +) -> RenderPipelineDescriptor { + RenderPipelineDescriptor { + label: Some(label.into()), + layout: Some(vec![bind_group_layout.clone()]), + vertex: fullscreen_shader_vertex_state(), + fragment: Some(FragmentState { + shader: shader.typed(), + shader_defs, + entry_point: Cow::Borrowed(entry_point), + targets: vec![Some(ColorTargetState { + format, + blend: None, + write_mask: ColorWrites::ALL, + })], + }), + primitive: PrimitiveState::default(), + depth_stencil: None, + multisample: MultisampleState::default(), + } +} + +#[derive(Component)] +pub struct FXAATexture { + pub output: CachedTexture, +} + +#[derive(Component)] +pub struct FXAAPipelines { + pub fxaa_ldr_pipeline_id: CachedRenderPipelineId, + pub fxaa_hdr_pipeline_id: CachedRenderPipelineId, + pub to_ldr_pipeline_id: CachedRenderPipelineId, + pub blit_pipeline_id: CachedRenderPipelineId, +} + +pub fn prepare_fxaa_texture( + mut commands: Commands, + mut texture_cache: ResMut, + mut pipeline_cache: ResMut, + bind_group: Res, + render_device: Res, + views: Query<(Entity, &ExtractedCamera, &ExtractedView, &FXAA)>, +) { + let mut output_textures = HashMap::default(); + + for (entity, camera, view, fxaa) in &views { + if let Some(physical_target_size) = camera.physical_target_size { + let mut texture_descriptor = TextureDescriptor { + label: None, + size: Extent3d { + depth_or_array_layers: 1, + width: physical_target_size.x, + height: physical_target_size.y, + }, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: if view.hdr { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }, + usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, + }; + + texture_descriptor.label = Some("fxaa_view_target_texture"); + + let output = output_textures + .entry(camera.target.clone()) + .or_insert_with(|| texture_cache.get(&render_device, texture_descriptor)) + .clone(); + + let shader_defs = fxaa.get_settings(); + let fxaa_ldr_descriptor = fullscreen_vertex_pipeline_descriptor( + "fxaa ldr pipeline", + &bind_group, + FXAA_SHADER_HANDLE, + shader_defs, + "fs_main", + TextureFormat::bevy_default(), + ); + + let mut shader_defs = fxaa.get_settings(); + shader_defs.push(String::from("TONEMAP")); + let fxaa_hdr_descriptor = fullscreen_vertex_pipeline_descriptor( + "fxaa hdr pipeline", + &bind_group, + FXAA_SHADER_HANDLE, + shader_defs, + "fs_main", + ViewTarget::TEXTURE_FORMAT_HDR, + ); + + let to_ldr_descriptor = fullscreen_vertex_pipeline_descriptor( + "to ldr pipeline", + &bind_group, + LDR_SHADER_HANDLE, + vec![], + "fs_main", + ViewTarget::TEXTURE_FORMAT_HDR, + ); + + let blit_descriptor = fullscreen_vertex_pipeline_descriptor( + "blit pipeline", + &bind_group, + BLIT_SHADER_HANDLE, + vec![], + "fs_main", + TextureFormat::bevy_default(), + ); + + let pipelines = FXAAPipelines { + fxaa_ldr_pipeline_id: pipeline_cache.queue_render_pipeline(fxaa_ldr_descriptor), + fxaa_hdr_pipeline_id: pipeline_cache.queue_render_pipeline(fxaa_hdr_descriptor), + to_ldr_pipeline_id: pipeline_cache.queue_render_pipeline(to_ldr_descriptor), + blit_pipeline_id: pipeline_cache.queue_render_pipeline(blit_descriptor), + }; + + commands + .entity(entity) + .insert(FXAATexture { output }) + .insert(pipelines); + } + } +} diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs new file mode 100644 index 0000000000000..297c6c6484ed6 --- /dev/null +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -0,0 +1,177 @@ +use std::sync::Mutex; + +use bevy_ecs::prelude::*; +use bevy_ecs::query::QueryState; +use bevy_render::{ + render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, + render_resource::{ + BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, + CachedRenderPipelineId, FilterMode, Operations, PipelineCache, RenderPassColorAttachment, + RenderPassDescriptor, SamplerDescriptor, TextureView, TextureViewId, + }, + renderer::RenderContext, + view::{ExtractedView, ViewTarget}, +}; +use bevy_utils::default; + +use super::{FXAAPipelineBindGroup, FXAAPipelines, FXAATexture, FXAA}; + +pub struct FXAANode { + query: QueryState< + ( + &'static ExtractedView, + &'static ViewTarget, + &'static FXAATexture, + &'static FXAAPipelines, + &'static FXAA, + ), + With, + >, + cached_texture_bind_group: Mutex>, +} + +impl FXAANode { + pub const IN_VIEW: &'static str = "view"; + + pub fn new(world: &mut World) -> Self { + Self { + query: QueryState::new(world), + cached_texture_bind_group: Mutex::new(None), + } + } +} + +impl Node for FXAANode { + fn input(&self) -> Vec { + vec![SlotInfo::new(FXAANode::IN_VIEW, SlotType::Entity)] + } + + fn update(&mut self, world: &mut World) { + self.query.update_archetypes(world); + } + + fn run( + &self, + graph: &mut RenderGraphContext, + render_context: &mut RenderContext, + world: &World, + ) -> Result<(), NodeRunError> { + let view_entity = graph.get_input_entity(Self::IN_VIEW)?; + let pipeline_cache = world.resource::(); + let fxaa_bind_group = world.resource::(); + + let (view, target, fxaa_texture, pipelines, fxaa) = + match self.query.get_manual(world, view_entity) { + Ok(result) => result, + Err(_) => return Ok(()), + }; + + if !fxaa.enabled { + return Ok(()); + }; + + let main_texture = target.main_texture.texture(); + let fxaa_texture = &fxaa_texture.output.default_view; + + let pipeline_id = if view.hdr { + pipelines.to_ldr_pipeline_id + } else { + pipelines.blit_pipeline_id + }; + self.render_pass( + render_context, + pipeline_cache, + pipeline_id, + main_texture, + fxaa_texture, + &fxaa_bind_group, + ); + + let pipeline_id = if view.hdr { + pipelines.fxaa_hdr_pipeline_id + } else { + pipelines.fxaa_ldr_pipeline_id + }; + self.render_pass( + render_context, + pipeline_cache, + pipeline_id, + fxaa_texture, + main_texture, + &fxaa_bind_group, + ); + + Ok(()) + } +} + +impl FXAANode { + fn render_pass( + &self, + render_context: &mut RenderContext, + pipeline_cache: &PipelineCache, + pipeline_id: CachedRenderPipelineId, + in_texture: &TextureView, + out_texture: &TextureView, + bind_group_layout: &BindGroupLayout, + ) { + let pipeline = match pipeline_cache.get_render_pipeline(pipeline_id) { + Some(pipeline) => pipeline, + None => return, + }; + + let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); + let bind_group = match &mut *cached_bind_group { + Some((id, bind_group)) if in_texture.id() == *id => bind_group, + cached_bind_group => { + let sampler = render_context + .render_device + .create_sampler(&SamplerDescriptor { + mipmap_filter: FilterMode::Linear, + mag_filter: FilterMode::Linear, + min_filter: FilterMode::Linear, + ..default() + }); + + let bind_group = + render_context + .render_device + .create_bind_group(&BindGroupDescriptor { + label: None, + layout: bind_group_layout, + entries: &[ + BindGroupEntry { + binding: 0, + resource: BindingResource::TextureView(in_texture), + }, + BindGroupEntry { + binding: 1, + resource: BindingResource::Sampler(&sampler), + }, + ], + }); + + let (_, bind_group) = cached_bind_group.insert((in_texture.id(), bind_group)); + bind_group + } + }; + + let pass_descriptor = RenderPassDescriptor { + label: Some("fxaa_pass"), + color_attachments: &[Some(RenderPassColorAttachment { + view: out_texture, + resolve_target: None, + ops: Operations::default(), + })], + depth_stencil_attachment: None, + }; + + let mut render_pass = render_context + .command_encoder + .begin_render_pass(&pass_descriptor); + + render_pass.set_pipeline(pipeline); + render_pass.set_bind_group(0, bind_group, &[]); + render_pass.draw(0..3, 0..1); + } +} diff --git a/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl b/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl new file mode 100644 index 0000000000000..52307bb9bdb51 --- /dev/null +++ b/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl @@ -0,0 +1,21 @@ +#import bevy_core_pipeline::fullscreen_vertex_shader + +@group(0) @binding(0) +var texture: texture_2d; +@group(0) @binding(1) +var texture_sampler: sampler; + +// https://gpuopen.com/learn/optimized-reversible-tonemapper-for-resolve/ +fn tonemap(c: vec3) -> vec3 { + return c * (1.0 / (max(c.r, max(c.g, c.b)) + 1.0)); +} + +fn tonemap_invert(c: vec3) -> vec3 { + return c * (1.0 / (1.0 - max(c.r, max(c.g, c.b)))); +} + +@fragment +fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { + let col = textureSample(texture, texture_sampler, in.uv); + return vec4(tonemap(col.rgb), col.a); +} diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index b46dba079b0cb..7fc04227dbf13 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -2,6 +2,7 @@ pub mod clear_color; pub mod core_2d; pub mod core_3d; pub mod fullscreen_vertex_shader; +pub mod fxaa; pub mod tonemapping; pub mod upscaling; diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs new file mode 100644 index 0000000000000..fbbec65f35328 --- /dev/null +++ b/examples/3d/fxaa.rs @@ -0,0 +1,172 @@ +//! This examples compares MSAA (Multi-Sample Anti-Aliasing) and FXAA (Fast Approximate Anti-Aliasing). + +use std::f32::consts::PI; + +use bevy::{ + core_pipeline::fxaa::{FXAAPlugin, Quality, FXAA}, + prelude::*, + render::{ + render_resource::{Extent3d, SamplerDescriptor, TextureDimension, TextureFormat}, + texture::ImageSampler, + }, +}; + +fn main() { + let mut app = App::new(); + app.add_plugins(DefaultPlugins) + .add_plugin(FXAAPlugin) // Disables MSAA by default. + .add_startup_system(setup) + .add_system(toggle_fxaa); + + app.run(); +} + +/// set up a simple 3D scene +fn setup( + mut commands: Commands, + mut meshes: ResMut>, + mut materials: ResMut>, + mut images: ResMut>, + asset_server: Res, +) { + // plane + commands.spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), + material: materials.add(Color::rgb(0.3, 0.5, 0.3).into()), + ..default() + }); + + // cubes + for i in 0..5 { + commands.spawn(PbrBundle { + mesh: meshes.add(Mesh::from(shape::Cube { size: 0.25 })), + material: materials.add(StandardMaterial { + base_color_texture: Some(images.add(uv_debug_texture())), + ..default() + }), + transform: Transform::from_xyz(i as f32 * 0.25 - 1.0, 0.125, -i as f32 * 0.5), + ..default() + }); + } + + // Flight Helmet + commands.spawn(SceneBundle { + scene: asset_server.load("models/FlightHelmet/FlightHelmet.gltf#Scene0"), + ..default() + }); + + // light + const HALF_SIZE: f32 = 2.0; + commands.spawn(DirectionalLightBundle { + directional_light: DirectionalLight { + shadow_projection: OrthographicProjection { + left: -HALF_SIZE, + right: HALF_SIZE, + bottom: -HALF_SIZE, + top: HALF_SIZE, + near: -10.0 * HALF_SIZE, + far: 10.0 * HALF_SIZE, + ..default() + }, + shadows_enabled: true, + ..default() + }, + transform: Transform::from_rotation(Quat::from_euler( + EulerRot::ZYX, + 0.0, + PI * -0.15, + PI * -0.15, + )), + ..default() + }); + + // camera + commands + .spawn(Camera3dBundle { + camera: Camera { + hdr: false, // Works with and without hdr + ..default() + }, + transform: Transform::from_xyz(0.7, 0.7, 1.0) + .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), + ..default() + }) + .insert(FXAA::default()); + + println!("Toggle with:\n1 - NO AA\n2 - MSAA 4\n3 - FXAA (default)"); + println!("Threshold:\n7 - LOW\n8 - MEDIUM\n9 - HIGH (default)\n0 - ULTRA"); +} + +fn toggle_fxaa(keys: Res>, mut query: Query<&mut FXAA>, mut msaa: ResMut) { + let set_no_aa = keys.just_pressed(KeyCode::Key1); + let set_msaa = keys.just_pressed(KeyCode::Key2); + let set_fxaa = keys.just_pressed(KeyCode::Key3); + let fxaa_low = keys.just_pressed(KeyCode::Key7); + let fxaa_med = keys.just_pressed(KeyCode::Key8); + let fxaa_high = keys.just_pressed(KeyCode::Key9); + let fxaa_ultra = keys.just_pressed(KeyCode::Key0); + let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra; + for mut fxaa in &mut query { + if set_msaa { + fxaa.enabled = false; + msaa.samples = 4; + info!("MSAA 4x"); + } + if set_no_aa { + fxaa.enabled = false; + msaa.samples = 1; + info!("NO AA"); + } + if set_no_aa | set_fxaa { + msaa.samples = 1; + } + if fxaa_low { + fxaa.edge_threshold = Quality::Low; + fxaa.edge_threshold_min = Quality::Low; + } else if fxaa_med { + fxaa.edge_threshold = Quality::Medium; + fxaa.edge_threshold_min = Quality::Medium; + } else if fxaa_high { + fxaa.edge_threshold = Quality::High; + fxaa.edge_threshold_min = Quality::High; + } else if fxaa_ultra { + fxaa.edge_threshold = Quality::Ultra; + fxaa.edge_threshold_min = Quality::Ultra; + } + if set_fxaa { + fxaa.enabled = true; + msaa.samples = 1; + info!("FXAA {}", fxaa.edge_threshold.get_str()); + } + } +} + +/// Creates a colorful test pattern +fn uv_debug_texture() -> Image { + const TEXTURE_SIZE: usize = 8; + + let mut palette: [u8; 32] = [ + 255, 102, 159, 255, 255, 159, 102, 255, 236, 255, 102, 255, 121, 255, 102, 255, 102, 255, + 198, 255, 102, 198, 255, 255, 121, 102, 255, 255, 236, 102, 255, 255, + ]; + + let mut texture_data = [0; TEXTURE_SIZE * TEXTURE_SIZE * 4]; + for y in 0..TEXTURE_SIZE { + let offset = TEXTURE_SIZE * y * 4; + texture_data[offset..(offset + TEXTURE_SIZE * 4)].copy_from_slice(&palette); + palette.rotate_right(4); + } + + let mut img = Image::new_fill( + Extent3d { + width: TEXTURE_SIZE as u32, + height: TEXTURE_SIZE as u32, + depth_or_array_layers: 1, + }, + TextureDimension::D2, + &texture_data, + TextureFormat::Rgba8UnormSrgb, + ); + img.sampler_descriptor = ImageSampler::Descriptor(SamplerDescriptor::default()); + img +} diff --git a/examples/wasm/assets b/examples/wasm/assets deleted file mode 120000 index 41aef43f18246..0000000000000 --- a/examples/wasm/assets +++ /dev/null @@ -1 +0,0 @@ -../../assets \ No newline at end of file diff --git a/examples/wasm/assets b/examples/wasm/assets new file mode 100644 index 0000000000000..41aef43f18246 --- /dev/null +++ b/examples/wasm/assets @@ -0,0 +1 @@ +../../assets \ No newline at end of file From 97251f404dbae9c39cf71865f584ac98521022e7 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Thu, 27 Oct 2022 20:35:14 -0700 Subject: [PATCH 02/18] clippy --- crates/bevy_core_pipeline/src/fxaa/node.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index 297c6c6484ed6..4113c10a6261a 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -84,7 +84,7 @@ impl Node for FXAANode { pipeline_id, main_texture, fxaa_texture, - &fxaa_bind_group, + fxaa_bind_group, ); let pipeline_id = if view.hdr { @@ -98,7 +98,7 @@ impl Node for FXAANode { pipeline_id, fxaa_texture, main_texture, - &fxaa_bind_group, + fxaa_bind_group, ); Ok(()) From 2a2e3023e64e4908e9bcb28075dfb12188a07326 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 29 Oct 2022 19:27:33 -0700 Subject: [PATCH 03/18] Rework ViewTarget to better support post processing --- .../src/tonemapping/blit.wgsl | 11 - .../bevy_core_pipeline/src/tonemapping/mod.rs | 33 +-- .../src/tonemapping/node.rs | 36 ++- .../src/tonemapping/tonemapping.wgsl | 2 +- .../bevy_core_pipeline/src/upscaling/mod.rs | 14 +- .../bevy_core_pipeline/src/upscaling/node.rs | 11 +- crates/bevy_render/src/view/mod.rs | 241 +++++++++--------- 7 files changed, 157 insertions(+), 191 deletions(-) delete mode 100644 crates/bevy_core_pipeline/src/tonemapping/blit.wgsl diff --git a/crates/bevy_core_pipeline/src/tonemapping/blit.wgsl b/crates/bevy_core_pipeline/src/tonemapping/blit.wgsl deleted file mode 100644 index feb46c5405b35..0000000000000 --- a/crates/bevy_core_pipeline/src/tonemapping/blit.wgsl +++ /dev/null @@ -1,11 +0,0 @@ -#import bevy_core_pipeline::fullscreen_vertex_shader - -@group(0) @binding(0) -var texture: texture_2d; -@group(0) @binding(1) -var texture_sampler: sampler; - -@fragment -fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { - return textureSample(texture, texture_sampler, in.uv); -} diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index cb0f74ba12207..26deeb2aec35d 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -3,6 +3,7 @@ mod node; use bevy_ecs::query::QueryItem; use bevy_render::camera::Camera; use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; +use bevy_render::view::ViewTarget; pub use node::TonemappingNode; use bevy_app::prelude::*; @@ -22,9 +23,6 @@ const TONEMAPPING_SHADER_HANDLE: HandleUntyped = const TONEMAPPING_SHARED_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2499430578245347910); -const BLIT_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 2982361071241723543); - pub struct TonemappingPlugin; impl Plugin for TonemappingPlugin { @@ -41,7 +39,6 @@ impl Plugin for TonemappingPlugin { "tonemapping_shared.wgsl", Shader::from_wgsl ); - load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl); app.add_plugin(ExtractComponentPlugin::::default()); @@ -53,9 +50,8 @@ impl Plugin for TonemappingPlugin { #[derive(Resource)] pub struct TonemappingPipeline { - hdr_texture_bind_group: BindGroupLayout, + texture_bind_group: BindGroupLayout, tonemapping_pipeline_id: CachedRenderPipelineId, - blit_pipeline_id: CachedRenderPipelineId, } impl FromWorld for TonemappingPipeline { @@ -91,9 +87,9 @@ impl FromWorld for TonemappingPipeline { fragment: Some(FragmentState { shader: TONEMAPPING_SHADER_HANDLE.typed(), shader_defs: vec![], - entry_point: "fs_main".into(), + entry_point: "fragment".into(), targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), + format: ViewTarget::TEXTURE_FORMAT_HDR, blend: None, write_mask: ColorWrites::ALL, })], @@ -103,29 +99,10 @@ impl FromWorld for TonemappingPipeline { multisample: MultisampleState::default(), }; - let blit_descriptor = RenderPipelineDescriptor { - label: Some("blit pipeline".into()), - layout: Some(vec![tonemap_texture_bind_group.clone()]), - vertex: fullscreen_shader_vertex_state(), - fragment: Some(FragmentState { - shader: BLIT_SHADER_HANDLE.typed(), - shader_defs: vec![], - entry_point: "fs_main".into(), - targets: vec![Some(ColorTargetState { - format: TextureFormat::bevy_default(), - blend: None, - write_mask: ColorWrites::ALL, - })], - }), - primitive: PrimitiveState::default(), - depth_stencil: None, - multisample: MultisampleState::default(), - }; let mut cache = render_world.resource_mut::(); TonemappingPipeline { - hdr_texture_bind_group: tonemap_texture_bind_group, + texture_bind_group: tonemap_texture_bind_group, tonemapping_pipeline_id: cache.queue_render_pipeline(tonemap_descriptor), - blit_pipeline_id: cache.queue_render_pipeline(blit_descriptor), } } } diff --git a/crates/bevy_core_pipeline/src/tonemapping/node.rs b/crates/bevy_core_pipeline/src/tonemapping/node.rs index ddae11c8cbb54..3a41a22025f12 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/node.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/node.rs @@ -11,7 +11,7 @@ use bevy_render::{ TextureViewId, }, renderer::RenderContext, - view::{ExtractedView, ViewMainTexture, ViewTarget}, + view::{ExtractedView, ViewTarget}, }; pub struct TonemappingNode { @@ -54,31 +54,25 @@ impl Node for TonemappingNode { Err(_) => return Ok(()), }; - let ldr_texture = match &target.main_texture { - ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture, - ViewMainTexture::Sdr { .. } => { - // non-hdr does tone mapping in the main pass node - return Ok(()); - } - }; - let tonemapping_enabled = tonemapping.map_or(false, |t| t.is_enabled); - let pipeline_id = if tonemapping_enabled { - tonemapping_pipeline.tonemapping_pipeline_id - } else { - tonemapping_pipeline.blit_pipeline_id - }; + if !tonemapping_enabled || !target.is_hdr() { + return Ok(()); + } - let pipeline = match pipeline_cache.get_render_pipeline(pipeline_id) { + let pipeline = match pipeline_cache + .get_render_pipeline(tonemapping_pipeline.tonemapping_pipeline_id) + { Some(pipeline) => pipeline, None => return Ok(()), }; - let main_texture = target.main_texture.texture(); + let post_process = target.post_process_write(); + let source = post_process.source; + let destination = post_process.destination; let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); let bind_group = match &mut *cached_bind_group { - Some((id, bind_group)) if main_texture.id() == *id => bind_group, + Some((id, bind_group)) if source.id() == *id => bind_group, cached_bind_group => { let sampler = render_context .render_device @@ -89,11 +83,11 @@ impl Node for TonemappingNode { .render_device .create_bind_group(&BindGroupDescriptor { label: None, - layout: &tonemapping_pipeline.hdr_texture_bind_group, + layout: &tonemapping_pipeline.texture_bind_group, entries: &[ BindGroupEntry { binding: 0, - resource: BindingResource::TextureView(main_texture), + resource: BindingResource::TextureView(source), }, BindGroupEntry { binding: 1, @@ -102,7 +96,7 @@ impl Node for TonemappingNode { ], }); - let (_, bind_group) = cached_bind_group.insert((main_texture.id(), bind_group)); + let (_, bind_group) = cached_bind_group.insert((source.id(), bind_group)); bind_group } }; @@ -110,7 +104,7 @@ impl Node for TonemappingNode { let pass_descriptor = RenderPassDescriptor { label: Some("tonemapping_pass"), color_attachments: &[Some(RenderPassColorAttachment { - view: ldr_texture, + view: destination, resolve_target: None, ops: Operations { load: LoadOp::Clear(Default::default()), // TODO shouldn't need to be cleared diff --git a/crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl b/crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl index 08a07aa56fe02..e18ae8a026f40 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl +++ b/crates/bevy_core_pipeline/src/tonemapping/tonemapping.wgsl @@ -7,7 +7,7 @@ var hdr_texture: texture_2d; var hdr_sampler: sampler; @fragment -fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { +fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let hdr_color = textureSample(hdr_texture, hdr_sampler, in.uv); return vec4(reinhard_luminance(hdr_color.rgb), hdr_color.a); diff --git a/crates/bevy_core_pipeline/src/upscaling/mod.rs b/crates/bevy_core_pipeline/src/upscaling/mod.rs index 59db4984c5d53..565ebad7c15f0 100644 --- a/crates/bevy_core_pipeline/src/upscaling/mod.rs +++ b/crates/bevy_core_pipeline/src/upscaling/mod.rs @@ -38,16 +38,16 @@ impl Plugin for UpscalingPlugin { #[derive(Resource)] pub struct UpscalingPipeline { - ldr_texture_bind_group: BindGroupLayout, + texture_bind_group: BindGroupLayout, } impl FromWorld for UpscalingPipeline { fn from_world(render_world: &mut World) -> Self { let render_device = render_world.resource::(); - let ldr_texture_bind_group = + let texture_bind_group = render_device.create_bind_group_layout(&BindGroupLayoutDescriptor { - label: Some("upscaling_ldr_texture_bind_group_layout"), + label: Some("upscaling_texture_bind_group_layout"), entries: &[ BindGroupLayoutEntry { binding: 0, @@ -68,9 +68,7 @@ impl FromWorld for UpscalingPipeline { ], }); - UpscalingPipeline { - ldr_texture_bind_group, - } + UpscalingPipeline { texture_bind_group } } } @@ -92,7 +90,7 @@ impl SpecializedRenderPipeline for UpscalingPipeline { fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { RenderPipelineDescriptor { label: Some("upscaling pipeline".into()), - layout: Some(vec![self.ldr_texture_bind_group.clone()]), + layout: Some(vec![self.texture_bind_group.clone()]), vertex: fullscreen_shader_vertex_state(), fragment: Some(FragmentState { shader: UPSCALING_SHADER_HANDLE.typed(), @@ -126,7 +124,7 @@ fn queue_upscaling_bind_groups( for (entity, view_target) in view_targets.iter() { let key = UpscalingPipelineKey { upscaling_mode: UpscalingMode::Filtering, - texture_format: view_target.out_texture_format, + texture_format: view_target.out_texture_format(), }; let pipeline = pipelines.specialize(&mut pipeline_cache, &upscaling_pipeline, key); diff --git a/crates/bevy_core_pipeline/src/upscaling/node.rs b/crates/bevy_core_pipeline/src/upscaling/node.rs index 8ab6f327ea129..fd785a46477fa 100644 --- a/crates/bevy_core_pipeline/src/upscaling/node.rs +++ b/crates/bevy_core_pipeline/src/upscaling/node.rs @@ -56,10 +56,7 @@ impl Node for UpscalingNode { Err(_) => return Ok(()), }; - let upscaled_texture = match &target.main_texture { - bevy_render::view::ViewMainTexture::Hdr { ldr_texture, .. } => ldr_texture, - bevy_render::view::ViewMainTexture::Sdr { texture, .. } => texture, - }; + let upscaled_texture = target.main_texture(); let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); let bind_group = match &mut *cached_bind_group { @@ -74,7 +71,7 @@ impl Node for UpscalingNode { .render_device .create_bind_group(&BindGroupDescriptor { label: None, - layout: &upscaling_pipeline.ldr_texture_bind_group, + layout: &upscaling_pipeline.texture_bind_group, entries: &[ BindGroupEntry { binding: 0, @@ -100,10 +97,10 @@ impl Node for UpscalingNode { let pass_descriptor = RenderPassDescriptor { label: Some("upscaling_pass"), color_attachments: &[Some(RenderPassColorAttachment { - view: &target.out_texture, + view: target.out_texture(), resolve_target: None, ops: Operations { - load: LoadOp::Clear(Default::default()), // TODO dont_care + load: LoadOp::Clear(Default::default()), store: true, }, })], diff --git a/crates/bevy_render/src/view/mod.rs b/crates/bevy_render/src/view/mod.rs index fbe42f38d0c36..ff2794ca4020c 100644 --- a/crates/bevy_render/src/view/mod.rs +++ b/crates/bevy_render/src/view/mod.rs @@ -1,12 +1,7 @@ pub mod visibility; pub mod window; -use bevy_utils::HashMap; pub use visibility::*; -use wgpu::{ - Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension, - TextureFormat, TextureUsages, -}; pub use window::*; use crate::{ @@ -25,6 +20,12 @@ use bevy_ecs::prelude::*; use bevy_math::{Mat4, UVec4, Vec3, Vec4}; use bevy_reflect::Reflect; use bevy_transform::components::GlobalTransform; +use bevy_utils::HashMap; +use std::sync::atomic::{AtomicUsize, Ordering}; +use wgpu::{ + Color, Extent3d, Operations, RenderPassColorAttachment, TextureDescriptor, TextureDimension, + TextureFormat, TextureUsages, +}; pub struct ViewPlugin; @@ -116,62 +117,32 @@ pub struct ViewUniformOffset { pub offset: u32, } -#[derive(Clone)] -pub enum ViewMainTexture { - Hdr { - hdr_texture: TextureView, - sampled_hdr_texture: Option, - - ldr_texture: TextureView, - }, - Sdr { - texture: TextureView, - sampled_texture: Option, - }, -} - -impl ViewMainTexture { - pub fn texture(&self) -> &TextureView { - match self { - ViewMainTexture::Hdr { hdr_texture, .. } => hdr_texture, - ViewMainTexture::Sdr { texture, .. } => texture, - } - } -} - #[derive(Component)] pub struct ViewTarget { - pub main_texture: ViewMainTexture, - pub out_texture: TextureView, - pub out_texture_format: TextureFormat, + main_textures: MainTargetTextures, + main_texture_format: TextureFormat, + /// 0 represents `texture_a`, 1 represents `texture_b` + main_texture: AtomicUsize, + out_texture: TextureView, + out_texture_format: TextureFormat, +} + +pub struct PostProcessWrite<'a> { + pub source: &'a TextureView, + pub destination: &'a TextureView, } impl ViewTarget { pub const TEXTURE_FORMAT_HDR: TextureFormat = TextureFormat::Rgba16Float; pub fn get_color_attachment(&self, ops: Operations) -> RenderPassColorAttachment { - let (target, sampled) = match &self.main_texture { - ViewMainTexture::Hdr { - hdr_texture, - sampled_hdr_texture, - .. - } => (hdr_texture, sampled_hdr_texture), - ViewMainTexture::Sdr { - texture, - sampled_texture, - } => (texture, sampled_texture), - }; - match sampled { - Some(sampled_target) => RenderPassColorAttachment { - view: sampled_target, - resolve_target: Some(target), - ops, - }, - None => RenderPassColorAttachment { - view: target, - resolve_target: None, + match &self.main_textures.sampled { + Some(sampled_texture) => RenderPassColorAttachment { + view: sampled_texture, + resolve_target: Some(self.main_texture()), ops, }, + None => self.get_unsampled_color_attachment(ops), } } @@ -180,14 +151,55 @@ impl ViewTarget { ops: Operations, ) -> RenderPassColorAttachment { RenderPassColorAttachment { - view: match &self.main_texture { - ViewMainTexture::Hdr { hdr_texture, .. } => hdr_texture, - ViewMainTexture::Sdr { texture, .. } => texture, - }, + view: self.main_texture(), resolve_target: None, ops, } } + + pub fn main_texture(&self) -> &TextureView { + if self.main_texture.load(Ordering::SeqCst) == 0 { + &self.main_textures.a + } else { + &self.main_textures.b + } + } + + #[inline] + pub fn main_texture_format(&self) -> TextureFormat { + self.main_texture_format + } + + #[inline] + pub fn is_hdr(&self) -> bool { + self.main_texture_format == ViewTarget::TEXTURE_FORMAT_HDR + } + + #[inline] + pub fn out_texture(&self) -> &TextureView { + &self.out_texture + } + + #[inline] + pub fn out_texture_format(&self) -> TextureFormat { + self.out_texture_format + } + + pub fn post_process_write(&self) -> PostProcessWrite { + let old_is_a_main_texture = self.main_texture.fetch_xor(1, Ordering::SeqCst); + // if the old main texture is a, then the post processing must write from a to b + if old_is_a_main_texture == 0 { + PostProcessWrite { + source: &self.main_textures.a, + destination: &self.main_textures.b, + } + } else { + PostProcessWrite { + source: &self.main_textures.b, + destination: &self.main_textures.a, + } + } + } } #[derive(Component)] @@ -230,6 +242,13 @@ fn prepare_view_uniforms( .write_buffer(&render_device, &render_queue); } +#[derive(Clone)] +struct MainTargetTextures { + a: TextureView, + b: TextureView, + sampled: Option, +} + #[allow(clippy::too_many_arguments)] fn prepare_view_targets( mut commands: Commands, @@ -243,7 +262,7 @@ fn prepare_view_targets( let mut textures = HashMap::default(); for (entity, camera, view) in cameras.iter() { if let Some(target_size) = camera.physical_target_size { - if let (Some(texture_view), Some(texture_format)) = ( + if let (Some(out_texture_view), Some(out_texture_format)) = ( camera.target.get_texture_view(&windows, &images), camera.target.get_texture_format(&windows, &images), ) { @@ -253,77 +272,69 @@ fn prepare_view_targets( depth_or_array_layers: 1, }; - let main_texture = textures + let main_texture_format = if view.hdr { + ViewTarget::TEXTURE_FORMAT_HDR + } else { + TextureFormat::bevy_default() + }; + + let main_textures = textures .entry((camera.target.clone(), view.hdr)) .or_insert_with(|| { - let main_texture_format = if view.hdr { - ViewTarget::TEXTURE_FORMAT_HDR - } else { - TextureFormat::bevy_default() + let descriptor = TextureDescriptor { + label: None, + size, + mip_level_count: 1, + sample_count: 1, + dimension: TextureDimension::D2, + format: main_texture_format, + usage: TextureUsages::RENDER_ATTACHMENT + | TextureUsages::TEXTURE_BINDING, }; - - let main_texture = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("main_texture"), - size, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: main_texture_format, - usage: TextureUsages::RENDER_ATTACHMENT - | TextureUsages::TEXTURE_BINDING, - }, - ); - - let sampled_main_texture = (msaa.samples > 1).then(|| { - texture_cache + MainTargetTextures { + a: texture_cache + .get( + &render_device, + TextureDescriptor { + label: Some("main_texture_a"), + ..descriptor + }, + ) + .default_view, + b: texture_cache .get( &render_device, TextureDescriptor { - label: Some("main_texture_sampled"), - size, - mip_level_count: 1, - sample_count: msaa.samples, - dimension: TextureDimension::D2, - format: main_texture_format, - usage: TextureUsages::RENDER_ATTACHMENT, + label: Some("main_texture_b"), + ..descriptor }, ) - .default_view - }); - if view.hdr { - let ldr_texture = texture_cache.get( - &render_device, - TextureDescriptor { - label: Some("ldr_texture"), - size, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: TextureFormat::bevy_default(), - usage: TextureUsages::RENDER_ATTACHMENT - | TextureUsages::TEXTURE_BINDING, - }, - ); - - ViewMainTexture::Hdr { - hdr_texture: main_texture.default_view, - sampled_hdr_texture: sampled_main_texture, - ldr_texture: ldr_texture.default_view, - } - } else { - ViewMainTexture::Sdr { - texture: main_texture.default_view, - sampled_texture: sampled_main_texture, - } + .default_view, + sampled: (msaa.samples > 1).then(|| { + texture_cache + .get( + &render_device, + TextureDescriptor { + label: Some("main_texture_sampled"), + size, + mip_level_count: 1, + sample_count: msaa.samples, + dimension: TextureDimension::D2, + format: main_texture_format, + usage: TextureUsages::RENDER_ATTACHMENT, + }, + ) + .default_view + }), } }); commands.entity(entity).insert(ViewTarget { - main_texture: main_texture.clone(), - out_texture: texture_view.clone(), - out_texture_format: texture_format, + main_textures: main_textures.clone(), + main_texture_format, + main_texture: AtomicUsize::new(0), + out_texture: out_texture_view.clone(), + out_texture_format, }); } } From b2a7d0409e1b78709204b5ea16d4988bb0a8b27f Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Sat, 29 Oct 2022 19:27:42 -0700 Subject: [PATCH 04/18] Port FXAA --- crates/bevy_core_pipeline/src/fxaa/blit.wgsl | 11 - crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 2 +- crates/bevy_core_pipeline/src/fxaa/mod.rs | 276 +++++++----------- crates/bevy_core_pipeline/src/fxaa/node.rs | 101 ++----- .../bevy_core_pipeline/src/fxaa/to_ldr.wgsl | 21 -- examples/3d/fxaa.rs | 19 +- 6 files changed, 140 insertions(+), 290 deletions(-) delete mode 100644 crates/bevy_core_pipeline/src/fxaa/blit.wgsl delete mode 100644 crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl diff --git a/crates/bevy_core_pipeline/src/fxaa/blit.wgsl b/crates/bevy_core_pipeline/src/fxaa/blit.wgsl deleted file mode 100644 index feb46c5405b35..0000000000000 --- a/crates/bevy_core_pipeline/src/fxaa/blit.wgsl +++ /dev/null @@ -1,11 +0,0 @@ -#import bevy_core_pipeline::fullscreen_vertex_shader - -@group(0) @binding(0) -var texture: texture_2d; -@group(0) @binding(1) -var texture_sampler: sampler; - -@fragment -fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { - return textureSample(texture, texture_sampler, in.uv); -} diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl index b0fb714b55c75..8e05a7c6a7bc3 100644 --- a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -76,7 +76,7 @@ fn tonemap_invert(c: vec3) -> vec3 { // Performs FXAA post-process anti-aliasing as described in the Nvidia FXAA white paper and the associated shader code. @fragment -fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { +fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let resolution = vec2(textureDimensions(screenTexture)); let fragCoord = in.position.xy; let inverseScreenSize = 1.0 / resolution.xy; diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index f26a37c50fa60..ff1cbf613df89 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -1,29 +1,26 @@ -use std::borrow::Cow; - -use bevy_derive::Deref; -use bevy_ecs::query::QueryItem; -use bevy_render::camera::ExtractedCamera; -use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; -use bevy_render::prelude::Camera; -use bevy_render::render_graph::RenderGraph; -use bevy_render::texture::{BevyDefault, CachedTexture, TextureCache}; -use bevy_utils::HashMap; +mod node; +use crate::{ + core_2d, core_3d, fullscreen_vertex_shader::fullscreen_shader_vertex_state, + fxaa::node::FxaaNode, +}; use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, HandleUntyped}; -use bevy_ecs::prelude::*; -use bevy_render::renderer::RenderDevice; -use bevy_render::view::{ExtractedView, Msaa, ViewTarget}; -use bevy_render::{render_resource::*, RenderApp, RenderStage}; - +use bevy_derive::Deref; +use bevy_ecs::{prelude::*, query::QueryItem}; use bevy_reflect::TypeUuid; - -mod node; - -use crate::fullscreen_vertex_shader::fullscreen_shader_vertex_state; -use crate::fxaa::node::FXAANode; -use crate::{core_2d, core_3d}; -#[derive(Clone)] +use bevy_render::{ + extract_component::{ExtractComponent, ExtractComponentPlugin}, + prelude::Camera, + render_graph::RenderGraph, + render_resource::*, + renderer::RenderDevice, + texture::{BevyDefault, CachedTexture}, + view::{ExtractedView, ViewTarget}, + RenderApp, RenderStage, +}; + +#[derive(Eq, PartialEq, Hash, Clone, Copy)] pub enum Quality { Low, Medium, @@ -43,7 +40,7 @@ impl Quality { } #[derive(Component, Clone)] -pub struct FXAA { +pub struct Fxaa { /// Enable render passes for FXAA. pub enabled: bool, @@ -58,9 +55,9 @@ pub struct FXAA { pub edge_threshold_min: Quality, } -impl Default for FXAA { +impl Default for Fxaa { fn default() -> Self { - FXAA { + Fxaa { enabled: true, edge_threshold: Quality::High, edge_threshold_min: Quality::High, @@ -68,7 +65,7 @@ impl Default for FXAA { } } -impl FXAA { +impl Fxaa { pub fn get_settings(&self) -> Vec { vec![ format!("EDGE_THRESH_{}", self.edge_threshold.get_str()), @@ -77,7 +74,7 @@ impl FXAA { } } -impl ExtractComponent for FXAA { +impl ExtractComponent for Fxaa { type Query = &'static Self; type Filter = With; @@ -86,39 +83,30 @@ impl ExtractComponent for FXAA { } } -const LDR_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 7112161265414793412); - const FXAA_SHADER_HANDLE: HandleUntyped = HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 4182761465141723543); -const BLIT_SHADER_HANDLE: HandleUntyped = - HandleUntyped::weak_from_u64(Shader::TYPE_UUID, 8382381532578979433); - pub const FXAA_NODE_3D: &str = "fxaa_node_3d"; pub const FXAA_NODE_2D: &str = "fxaa_node_2d"; -pub struct FXAAPlugin; -impl Plugin for FXAAPlugin { +pub struct FxaaPlugin; +impl Plugin for FxaaPlugin { fn build(&self, app: &mut App) { - app.insert_resource(Msaa { samples: 1 }); // Disable MSAA be default - - load_internal_asset!(app, LDR_SHADER_HANDLE, "to_ldr.wgsl", Shader::from_wgsl); load_internal_asset!(app, FXAA_SHADER_HANDLE, "fxaa.wgsl", Shader::from_wgsl); - load_internal_asset!(app, BLIT_SHADER_HANDLE, "blit.wgsl", Shader::from_wgsl); - app.add_plugin(ExtractComponentPlugin::::default()); + app.add_plugin(ExtractComponentPlugin::::default()); let render_app = match app.get_sub_app_mut(RenderApp) { Ok(render_app) => render_app, Err(_) => return, }; render_app - .init_resource::() - .add_system_to_stage(RenderStage::Prepare, prepare_fxaa_texture); + .init_resource::() + .init_resource::>() + .add_system_to_stage(RenderStage::Prepare, prepare_fxaa_pipelines); { - let fxaa_node = FXAANode::new(&mut render_app.world); + let fxaa_node = FxaaNode::new(&mut render_app.world); let mut binding = render_app.world.resource_mut::(); let graph = binding.get_sub_graph_mut(core_3d::graph::NAME).unwrap(); @@ -129,20 +117,16 @@ impl Plugin for FXAAPlugin { graph.input_node().unwrap().id, core_3d::graph::input::VIEW_ENTITY, FXAA_NODE_3D, - FXAANode::IN_VIEW, + FxaaNode::IN_VIEW, ) .unwrap(); graph - .add_node_edge(core_3d::graph::node::MAIN_PASS, FXAA_NODE_3D) - .unwrap(); - - graph - .add_node_edge(FXAA_NODE_3D, core_3d::graph::node::TONEMAPPING) + .add_node_edge(core_3d::graph::node::TONEMAPPING, FXAA_NODE_3D) .unwrap(); } { - let fxaa_node = FXAANode::new(&mut render_app.world); + let fxaa_node = FxaaNode::new(&mut render_app.world); let mut binding = render_app.world.resource_mut::(); let graph = binding.get_sub_graph_mut(core_2d::graph::NAME).unwrap(); @@ -153,27 +137,25 @@ impl Plugin for FXAAPlugin { graph.input_node().unwrap().id, core_2d::graph::input::VIEW_ENTITY, FXAA_NODE_2D, - FXAANode::IN_VIEW, + FxaaNode::IN_VIEW, ) .unwrap(); graph - .add_node_edge(core_2d::graph::node::MAIN_PASS, FXAA_NODE_2D) - .unwrap(); - - graph - .add_node_edge(FXAA_NODE_2D, core_2d::graph::node::TONEMAPPING) + .add_node_edge(core_2d::graph::node::TONEMAPPING, FXAA_NODE_2D) .unwrap(); } } } #[derive(Resource, Deref)] -pub struct FXAAPipelineBindGroup(BindGroupLayout); +pub struct FxaaPipeline { + texture_bind_group: BindGroupLayout, +} -impl FromWorld for FXAAPipelineBindGroup { +impl FromWorld for FxaaPipeline { fn from_world(render_world: &mut World) -> Self { - let fxaa_texture_bind_group = render_world + let texture_bind_group = render_world .resource::() .create_bind_group_layout(&BindGroupLayoutDescriptor { label: Some("fxaa_texture_bind_group_layout"), @@ -197,138 +179,82 @@ impl FromWorld for FXAAPipelineBindGroup { ], }); - FXAAPipelineBindGroup(fxaa_texture_bind_group) - } -} - -fn fullscreen_vertex_pipeline_descriptor( - label: &'static str, - bind_group_layout: &BindGroupLayout, - shader: HandleUntyped, - shader_defs: Vec, - entry_point: &'static str, - format: TextureFormat, -) -> RenderPipelineDescriptor { - RenderPipelineDescriptor { - label: Some(label.into()), - layout: Some(vec![bind_group_layout.clone()]), - vertex: fullscreen_shader_vertex_state(), - fragment: Some(FragmentState { - shader: shader.typed(), - shader_defs, - entry_point: Cow::Borrowed(entry_point), - targets: vec![Some(ColorTargetState { - format, - blend: None, - write_mask: ColorWrites::ALL, - })], - }), - primitive: PrimitiveState::default(), - depth_stencil: None, - multisample: MultisampleState::default(), + FxaaPipeline { texture_bind_group } } } #[derive(Component)] -pub struct FXAATexture { +pub struct FxaaTexture { pub output: CachedTexture, } #[derive(Component)] -pub struct FXAAPipelines { - pub fxaa_ldr_pipeline_id: CachedRenderPipelineId, - pub fxaa_hdr_pipeline_id: CachedRenderPipelineId, - pub to_ldr_pipeline_id: CachedRenderPipelineId, - pub blit_pipeline_id: CachedRenderPipelineId, +pub struct CameraFxaaPipeline { + pub pipeline_id: CachedRenderPipelineId, +} + +#[derive(PartialEq, Eq, Hash, Clone, Copy)] +pub struct FxaaPipelineKey { + edge_threshold: Quality, + edge_threshold_min: Quality, + texture_format: TextureFormat, +} + +impl SpecializedRenderPipeline for FxaaPipeline { + type Key = FxaaPipelineKey; + + fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor { + RenderPipelineDescriptor { + label: Some("fxaa".into()), + layout: Some(vec![self.texture_bind_group.clone()]), + vertex: fullscreen_shader_vertex_state(), + fragment: Some(FragmentState { + shader: FXAA_SHADER_HANDLE.typed(), + shader_defs: vec![ + format!("EDGE_THRESH_{}", key.edge_threshold.get_str()), + format!("EDGE_THRESH_MIN_{}", key.edge_threshold_min.get_str()), + ], + 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(), + } + } } -pub fn prepare_fxaa_texture( +pub fn prepare_fxaa_pipelines( mut commands: Commands, - mut texture_cache: ResMut, mut pipeline_cache: ResMut, - bind_group: Res, - render_device: Res, - views: Query<(Entity, &ExtractedCamera, &ExtractedView, &FXAA)>, + mut pipelines: ResMut>, + fxaa_pipeline: Res, + views: Query<(Entity, &ExtractedView, &Fxaa)>, ) { - let mut output_textures = HashMap::default(); - - for (entity, camera, view, fxaa) in &views { - if let Some(physical_target_size) = camera.physical_target_size { - let mut texture_descriptor = TextureDescriptor { - label: None, - size: Extent3d { - depth_or_array_layers: 1, - width: physical_target_size.x, - height: physical_target_size.y, - }, - mip_level_count: 1, - sample_count: 1, - dimension: TextureDimension::D2, - format: if view.hdr { + for (entity, view, fxaa) in &views { + if !fxaa.enabled { + continue; + } + let pipeline_id = pipelines.specialize( + &mut 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() }, - usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::TEXTURE_BINDING, - }; - - texture_descriptor.label = Some("fxaa_view_target_texture"); - - let output = output_textures - .entry(camera.target.clone()) - .or_insert_with(|| texture_cache.get(&render_device, texture_descriptor)) - .clone(); - - let shader_defs = fxaa.get_settings(); - let fxaa_ldr_descriptor = fullscreen_vertex_pipeline_descriptor( - "fxaa ldr pipeline", - &bind_group, - FXAA_SHADER_HANDLE, - shader_defs, - "fs_main", - TextureFormat::bevy_default(), - ); - - let mut shader_defs = fxaa.get_settings(); - shader_defs.push(String::from("TONEMAP")); - let fxaa_hdr_descriptor = fullscreen_vertex_pipeline_descriptor( - "fxaa hdr pipeline", - &bind_group, - FXAA_SHADER_HANDLE, - shader_defs, - "fs_main", - ViewTarget::TEXTURE_FORMAT_HDR, - ); - - let to_ldr_descriptor = fullscreen_vertex_pipeline_descriptor( - "to ldr pipeline", - &bind_group, - LDR_SHADER_HANDLE, - vec![], - "fs_main", - ViewTarget::TEXTURE_FORMAT_HDR, - ); - - let blit_descriptor = fullscreen_vertex_pipeline_descriptor( - "blit pipeline", - &bind_group, - BLIT_SHADER_HANDLE, - vec![], - "fs_main", - TextureFormat::bevy_default(), - ); - - let pipelines = FXAAPipelines { - fxaa_ldr_pipeline_id: pipeline_cache.queue_render_pipeline(fxaa_ldr_descriptor), - fxaa_hdr_pipeline_id: pipeline_cache.queue_render_pipeline(fxaa_hdr_descriptor), - to_ldr_pipeline_id: pipeline_cache.queue_render_pipeline(to_ldr_descriptor), - blit_pipeline_id: pipeline_cache.queue_render_pipeline(blit_descriptor), - }; - - commands - .entity(entity) - .insert(FXAATexture { output }) - .insert(pipelines); - } + }, + ); + + commands + .entity(entity) + .insert(CameraFxaaPipeline { pipeline_id }); } } diff --git a/crates/bevy_core_pipeline/src/fxaa/node.rs b/crates/bevy_core_pipeline/src/fxaa/node.rs index 4113c10a6261a..6e12151c2fe63 100644 --- a/crates/bevy_core_pipeline/src/fxaa/node.rs +++ b/crates/bevy_core_pipeline/src/fxaa/node.rs @@ -1,36 +1,33 @@ use std::sync::Mutex; +use crate::fxaa::{CameraFxaaPipeline, Fxaa, FxaaPipeline}; use bevy_ecs::prelude::*; use bevy_ecs::query::QueryState; use bevy_render::{ render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType}, render_resource::{ - BindGroup, BindGroupDescriptor, BindGroupEntry, BindGroupLayout, BindingResource, - CachedRenderPipelineId, FilterMode, Operations, PipelineCache, RenderPassColorAttachment, - RenderPassDescriptor, SamplerDescriptor, TextureView, TextureViewId, + BindGroup, BindGroupDescriptor, BindGroupEntry, BindingResource, FilterMode, Operations, + PipelineCache, RenderPassColorAttachment, RenderPassDescriptor, SamplerDescriptor, + TextureViewId, }, renderer::RenderContext, view::{ExtractedView, ViewTarget}, }; use bevy_utils::default; -use super::{FXAAPipelineBindGroup, FXAAPipelines, FXAATexture, FXAA}; - -pub struct FXAANode { +pub struct FxaaNode { query: QueryState< ( - &'static ExtractedView, &'static ViewTarget, - &'static FXAATexture, - &'static FXAAPipelines, - &'static FXAA, + &'static CameraFxaaPipeline, + &'static Fxaa, ), With, >, cached_texture_bind_group: Mutex>, } -impl FXAANode { +impl FxaaNode { pub const IN_VIEW: &'static str = "view"; pub fn new(world: &mut World) -> Self { @@ -41,9 +38,9 @@ impl FXAANode { } } -impl Node for FXAANode { +impl Node for FxaaNode { fn input(&self) -> Vec { - vec![SlotInfo::new(FXAANode::IN_VIEW, SlotType::Entity)] + vec![SlotInfo::new(FxaaNode::IN_VIEW, SlotType::Entity)] } fn update(&mut self, world: &mut World) { @@ -58,71 +55,27 @@ impl Node for FXAANode { ) -> Result<(), NodeRunError> { let view_entity = graph.get_input_entity(Self::IN_VIEW)?; let pipeline_cache = world.resource::(); - let fxaa_bind_group = world.resource::(); + let fxaa_pipeline = world.resource::(); - let (view, target, fxaa_texture, pipelines, fxaa) = - match self.query.get_manual(world, view_entity) { - Ok(result) => result, - Err(_) => return Ok(()), - }; + let (target, pipeline, fxaa) = match self.query.get_manual(world, view_entity) { + Ok(result) => result, + Err(_) => return Ok(()), + }; if !fxaa.enabled { return Ok(()); }; - let main_texture = target.main_texture.texture(); - let fxaa_texture = &fxaa_texture.output.default_view; - - let pipeline_id = if view.hdr { - pipelines.to_ldr_pipeline_id - } else { - pipelines.blit_pipeline_id - }; - self.render_pass( - render_context, - pipeline_cache, - pipeline_id, - main_texture, - fxaa_texture, - fxaa_bind_group, - ); - - let pipeline_id = if view.hdr { - pipelines.fxaa_hdr_pipeline_id - } else { - pipelines.fxaa_ldr_pipeline_id - }; - self.render_pass( - render_context, - pipeline_cache, - pipeline_id, - fxaa_texture, - main_texture, - fxaa_bind_group, - ); - - Ok(()) - } -} - -impl FXAANode { - fn render_pass( - &self, - render_context: &mut RenderContext, - pipeline_cache: &PipelineCache, - pipeline_id: CachedRenderPipelineId, - in_texture: &TextureView, - out_texture: &TextureView, - bind_group_layout: &BindGroupLayout, - ) { - let pipeline = match pipeline_cache.get_render_pipeline(pipeline_id) { - Some(pipeline) => pipeline, - None => return, - }; + let pipeline = pipeline_cache + .get_render_pipeline(pipeline.pipeline_id) + .unwrap(); + let post_process = target.post_process_write(); + let source = post_process.source; + let destination = post_process.destination; let mut cached_bind_group = self.cached_texture_bind_group.lock().unwrap(); let bind_group = match &mut *cached_bind_group { - Some((id, bind_group)) if in_texture.id() == *id => bind_group, + Some((id, bind_group)) if source.id() == *id => bind_group, cached_bind_group => { let sampler = render_context .render_device @@ -138,11 +91,11 @@ impl FXAANode { .render_device .create_bind_group(&BindGroupDescriptor { label: None, - layout: bind_group_layout, + layout: &fxaa_pipeline.texture_bind_group, entries: &[ BindGroupEntry { binding: 0, - resource: BindingResource::TextureView(in_texture), + resource: BindingResource::TextureView(source), }, BindGroupEntry { binding: 1, @@ -151,7 +104,7 @@ impl FXAANode { ], }); - let (_, bind_group) = cached_bind_group.insert((in_texture.id(), bind_group)); + let (_, bind_group) = cached_bind_group.insert((source.id(), bind_group)); bind_group } }; @@ -159,7 +112,7 @@ impl FXAANode { let pass_descriptor = RenderPassDescriptor { label: Some("fxaa_pass"), color_attachments: &[Some(RenderPassColorAttachment { - view: out_texture, + view: destination, resolve_target: None, ops: Operations::default(), })], @@ -173,5 +126,7 @@ impl FXAANode { render_pass.set_pipeline(pipeline); render_pass.set_bind_group(0, bind_group, &[]); render_pass.draw(0..3, 0..1); + + Ok(()) } } diff --git a/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl b/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl deleted file mode 100644 index 52307bb9bdb51..0000000000000 --- a/crates/bevy_core_pipeline/src/fxaa/to_ldr.wgsl +++ /dev/null @@ -1,21 +0,0 @@ -#import bevy_core_pipeline::fullscreen_vertex_shader - -@group(0) @binding(0) -var texture: texture_2d; -@group(0) @binding(1) -var texture_sampler: sampler; - -// https://gpuopen.com/learn/optimized-reversible-tonemapper-for-resolve/ -fn tonemap(c: vec3) -> vec3 { - return c * (1.0 / (max(c.r, max(c.g, c.b)) + 1.0)); -} - -fn tonemap_invert(c: vec3) -> vec3 { - return c * (1.0 / (1.0 - max(c.r, max(c.g, c.b)))); -} - -@fragment -fn fs_main(in: FullscreenVertexOutput) -> @location(0) vec4 { - let col = textureSample(texture, texture_sampler, in.uv); - return vec4(tonemap(col.rgb), col.a); -} diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index fbbec65f35328..9e874ce09a2c1 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -3,7 +3,7 @@ use std::f32::consts::PI; use bevy::{ - core_pipeline::fxaa::{FXAAPlugin, Quality, FXAA}, + core_pipeline::fxaa::{Fxaa, FxaaPlugin, Quality}, prelude::*, render::{ render_resource::{Extent3d, SamplerDescriptor, TextureDimension, TextureFormat}, @@ -12,13 +12,14 @@ use bevy::{ }; fn main() { - let mut app = App::new(); - app.add_plugins(DefaultPlugins) - .add_plugin(FXAAPlugin) // Disables MSAA by default. + App::new() + // Disable MSAA be default + .insert_resource(Msaa { samples: 1 }) + .add_plugins(DefaultPlugins) + .add_plugin(FxaaPlugin) .add_startup_system(setup) - .add_system(toggle_fxaa); - - app.run(); + .add_system(toggle_fxaa) + .run(); } /// set up a simple 3D scene @@ -91,13 +92,13 @@ fn setup( .looking_at(Vec3::new(0.0, 0.3, 0.0), Vec3::Y), ..default() }) - .insert(FXAA::default()); + .insert(Fxaa::default()); println!("Toggle with:\n1 - NO AA\n2 - MSAA 4\n3 - FXAA (default)"); println!("Threshold:\n7 - LOW\n8 - MEDIUM\n9 - HIGH (default)\n0 - ULTRA"); } -fn toggle_fxaa(keys: Res>, mut query: Query<&mut FXAA>, mut msaa: ResMut) { +fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: ResMut) { let set_no_aa = keys.just_pressed(KeyCode::Key1); let set_msaa = keys.just_pressed(KeyCode::Key2); let set_fxaa = keys.just_pressed(KeyCode::Key3); From 632c250844e4c529374635755b82010e4672255a Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 13:47:48 -0700 Subject: [PATCH 05/18] remove duplicate imports --- crates/bevy_core_pipeline/src/tonemapping/mod.rs | 4 ---- 1 file changed, 4 deletions(-) diff --git a/crates/bevy_core_pipeline/src/tonemapping/mod.rs b/crates/bevy_core_pipeline/src/tonemapping/mod.rs index 04082f4fe3f1a..3702e28b030bc 100644 --- a/crates/bevy_core_pipeline/src/tonemapping/mod.rs +++ b/crates/bevy_core_pipeline/src/tonemapping/mod.rs @@ -7,15 +7,11 @@ use bevy_app::prelude::*; use bevy_asset::{load_internal_asset, HandleUntyped}; use bevy_ecs::prelude::*; use bevy_ecs::query::QueryItem; -use bevy_ecs::query::QueryItem; use bevy_reflect::TypeUuid; use bevy_render::camera::Camera; -use bevy_render::camera::Camera; -use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; use bevy_render::extract_component::{ExtractComponent, ExtractComponentPlugin}; use bevy_render::renderer::RenderDevice; use bevy_render::view::ViewTarget; -use bevy_render::view::ViewTarget; use bevy_render::{render_resource::*, RenderApp}; const TONEMAPPING_SHADER_HANDLE: HandleUntyped = From c5eb0a8320694955337d25d121799883dc7fb85b Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 13:53:48 -0700 Subject: [PATCH 06/18] remove tonemap fn from wgsl --- crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 15 --------------- 1 file changed, 15 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl index 8e05a7c6a7bc3..56234752b6624 100644 --- a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -65,15 +65,6 @@ fn rgb2luma(rgb: vec3) -> f32 { return sqrt(dot(rgb, vec3(0.299, 0.587, 0.114))); } -// https://gpuopen.com/learn/optimized-reversible-tonemapper-for-resolve/ -fn tonemap(c: vec3) -> vec3 { - return c * (1.0 / (max(c.r, max(c.g, c.b)) + 1.0)); -} - -fn tonemap_invert(c: vec3) -> vec3 { - return c * (1.0 / (1.0 - max(c.r, max(c.g, c.b)))); -} - // Performs FXAA post-process anti-aliasing as described in the Nvidia FXAA white paper and the associated shader code. @fragment fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { @@ -104,9 +95,6 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { // If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA. if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax*EDGE_THRESHOLD_MAX)) { var col = centerSample; - #ifdef TONEMAP - col = vec4(tonemap_invert(col.rgb), col.a); - #endif return col; } @@ -278,8 +266,5 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { // Read the color at the new UV coordinates, and use it. var finalColor = textureSampleLevel(screenTexture, samp, finalUv, 0.0).rgb; - #ifdef TONEMAP - finalColor = tonemap_invert(finalColor); - #endif return vec4(finalColor, centerSample.a); } From 75f2bfad93656bb1c971d7bcbb3184857e6fb9cd Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 13:53:59 -0700 Subject: [PATCH 07/18] remove FxaaTexture --- crates/bevy_core_pipeline/src/fxaa/mod.rs | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index ff1cbf613df89..c2e2ba105b6fb 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -15,7 +15,7 @@ use bevy_render::{ render_graph::RenderGraph, render_resource::*, renderer::RenderDevice, - texture::{BevyDefault, CachedTexture}, + texture::BevyDefault, view::{ExtractedView, ViewTarget}, RenderApp, RenderStage, }; @@ -183,11 +183,6 @@ impl FromWorld for FxaaPipeline { } } -#[derive(Component)] -pub struct FxaaTexture { - pub output: CachedTexture, -} - #[derive(Component)] pub struct CameraFxaaPipeline { pub pipeline_id: CachedRenderPipelineId, From 5ff721b8d56fa5954044e111bbb4f42bb5a228c6 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 14:00:43 -0700 Subject: [PATCH 08/18] Delete assets symlink --- examples/wasm/assets | 1 - 1 file changed, 1 deletion(-) delete mode 100644 examples/wasm/assets diff --git a/examples/wasm/assets b/examples/wasm/assets deleted file mode 100644 index 41aef43f18246..0000000000000 --- a/examples/wasm/assets +++ /dev/null @@ -1 +0,0 @@ -../../assets \ No newline at end of file From 1404067af3bcd9bf789e685f205af28a3324d375 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 14:01:32 -0700 Subject: [PATCH 09/18] re-add assets symlink --- examples/wasm/assets | 1 + 1 file changed, 1 insertion(+) create mode 120000 examples/wasm/assets diff --git a/examples/wasm/assets b/examples/wasm/assets new file mode 120000 index 0000000000000..41aef43f18246 --- /dev/null +++ b/examples/wasm/assets @@ -0,0 +1 @@ +../../assets \ No newline at end of file From 2b8ee416f3764671118ef38751a0d79398a6499e Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Mon, 31 Oct 2022 14:05:48 -0700 Subject: [PATCH 10/18] Add FxaaPlugin automatically --- crates/bevy_core_pipeline/src/fxaa/mod.rs | 1 + crates/bevy_core_pipeline/src/lib.rs | 4 +++- examples/3d/fxaa.rs | 3 +-- 3 files changed, 5 insertions(+), 3 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index c2e2ba105b6fb..a6adc1cc733a9 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -89,6 +89,7 @@ const FXAA_SHADER_HANDLE: HandleUntyped = pub const FXAA_NODE_3D: &str = "fxaa_node_3d"; pub const FXAA_NODE_2D: &str = "fxaa_node_2d"; +/// Adds support for Fast Approximate Anti-Aliasing (FXAA) pub struct FxaaPlugin; impl Plugin for FxaaPlugin { fn build(&self, app: &mut App) { diff --git a/crates/bevy_core_pipeline/src/lib.rs b/crates/bevy_core_pipeline/src/lib.rs index 7fc04227dbf13..85f2c213ae344 100644 --- a/crates/bevy_core_pipeline/src/lib.rs +++ b/crates/bevy_core_pipeline/src/lib.rs @@ -20,6 +20,7 @@ use crate::{ core_2d::Core2dPlugin, core_3d::Core3dPlugin, fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE, + fxaa::FxaaPlugin, tonemapping::TonemappingPlugin, upscaling::UpscalingPlugin, }; @@ -46,6 +47,7 @@ impl Plugin for CorePipelinePlugin { .add_plugin(TonemappingPlugin) .add_plugin(UpscalingPlugin) .add_plugin(Core2dPlugin) - .add_plugin(Core3dPlugin); + .add_plugin(Core3dPlugin) + .add_plugin(FxaaPlugin); } } diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 9e874ce09a2c1..1f8bccf7b5d1f 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -3,7 +3,7 @@ use std::f32::consts::PI; use bevy::{ - core_pipeline::fxaa::{Fxaa, FxaaPlugin, Quality}, + core_pipeline::fxaa::{Fxaa, Quality}, prelude::*, render::{ render_resource::{Extent3d, SamplerDescriptor, TextureDimension, TextureFormat}, @@ -16,7 +16,6 @@ fn main() { // Disable MSAA be default .insert_resource(Msaa { samples: 1 }) .add_plugins(DefaultPlugins) - .add_plugin(FxaaPlugin) .add_startup_system(setup) .add_system(toggle_fxaa) .run(); From 81de58180d9ad51d5fe5503fc750fb1522d1a4b6 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Mon, 31 Oct 2022 18:53:13 -0700 Subject: [PATCH 11/18] better options layout --- examples/3d/fxaa.rs | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 1f8bccf7b5d1f..7291de4fb1504 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -29,6 +29,17 @@ fn setup( mut images: ResMut>, asset_server: Res, ) { + println!("Toggle with:"); + println!("1 - NO AA"); + println!("2 - MSAA 4"); + println!("3 - FXAA (default)"); + + println!("Threshold:"); + println!("7 - LOW"); + println!("8 - MEDIUM"); + println!("9 - HIGH (default)"); + println!("0 - ULTRA"); + // plane commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Plane { size: 5.0 })), @@ -92,9 +103,6 @@ fn setup( ..default() }) .insert(Fxaa::default()); - - println!("Toggle with:\n1 - NO AA\n2 - MSAA 4\n3 - FXAA (default)"); - println!("Threshold:\n7 - LOW\n8 - MEDIUM\n9 - HIGH (default)\n0 - ULTRA"); } fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: ResMut) { From 4fd111ed744bbd65a9e475a58cb4af322f8951a3 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Mon, 31 Oct 2022 19:02:37 -0700 Subject: [PATCH 12/18] remove unused get_settings Fxaa impl --- crates/bevy_core_pipeline/src/fxaa/mod.rs | 9 --------- 1 file changed, 9 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index a6adc1cc733a9..bf4cbdb610260 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -65,15 +65,6 @@ impl Default for Fxaa { } } -impl Fxaa { - pub fn get_settings(&self) -> Vec { - vec![ - format!("EDGE_THRESH_{}", self.edge_threshold.get_str()), - format!("EDGE_THRESH_MIN_{}", self.edge_threshold_min.get_str()), - ] - } -} - impl ExtractComponent for Fxaa { type Query = &'static Self; type Filter = With; From 2bd5fd8a6b971a81a39e247e082dcbe027fb26b5 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Mon, 31 Oct 2022 19:09:24 -0700 Subject: [PATCH 13/18] clean up shader a bit --- crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl index 56234752b6624..a105e46f49306 100644 --- a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -93,9 +93,8 @@ fn fragment(in: FullscreenVertexOutput) -> @location(0) vec4 { let lumaRange = lumaMax - lumaMin; // If the luma variation is lower that a threshold (or if we are in a really dark area), we are not on an edge, don't perform any AA. - if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax*EDGE_THRESHOLD_MAX)) { - var col = centerSample; - return col; + if (lumaRange < max(EDGE_THRESHOLD_MIN, lumaMax * EDGE_THRESHOLD_MAX)) { + return centerSample; } // Query the 4 remaining corners lumas. From 5e0477259d446d98744e8b6960b47a4124d845ee Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 1 Nov 2022 16:06:41 -0700 Subject: [PATCH 14/18] Move tonemapping outside of runtime conditions in pbr shader --- crates/bevy_pbr/src/render/pbr.wgsl | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/crates/bevy_pbr/src/render/pbr.wgsl b/crates/bevy_pbr/src/render/pbr.wgsl index b11700342f79f..1b943c666f330 100644 --- a/crates/bevy_pbr/src/render/pbr.wgsl +++ b/crates/bevy_pbr/src/render/pbr.wgsl @@ -88,12 +88,12 @@ fn fragment(in: FragmentInput) -> @location(0) vec4 { ); pbr_input.V = calculate_view(in.world_position, pbr_input.is_orthographic); output_color = pbr(pbr_input); -#ifdef TONEMAP_IN_SHADER - output_color = tone_mapping(output_color); -#endif } else { output_color = alpha_discard(material, output_color); } +#ifdef TONEMAP_IN_SHADER + output_color = tone_mapping(output_color); +#endif return output_color; } From f5965dbce875dfcace51abdc0ee963677060c163 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Tue, 1 Nov 2022 17:01:27 -0700 Subject: [PATCH 15/18] add turbo --- crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 8 ++++ crates/bevy_core_pipeline/src/fxaa/mod.rs | 34 ++++++++-------- examples/3d/fxaa.rs | 41 +++++++++++--------- 3 files changed, 49 insertions(+), 34 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl index a105e46f49306..191c4e47cc351 100644 --- a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -30,6 +30,10 @@ var samp: sampler; let EDGE_THRESHOLD_MIN: f32 = 0.0156; #endif +#ifdef EDGE_THRESH_MIN_TURBO + let EDGE_THRESHOLD_MIN: f32 = 0.0078; +#endif + // The minimum amount of local contrast required to apply algorithm. #ifdef EDGE_THRESH_LOW let EDGE_THRESHOLD_MAX: f32 = 0.250; @@ -47,6 +51,10 @@ var samp: sampler; let EDGE_THRESHOLD_MAX: f32 = 0.063; #endif +#ifdef EDGE_THRESH_TURBO + let EDGE_THRESHOLD_MAX: f32 = 0.031; +#endif + let ITERATIONS: i32 = 12; //default is 12 let SUBPIXEL_QUALITY: f32 = 0.75; // #define QUALITY(q) ((q) < 5 ? 1.0 : ((q) > 5 ? ((q) < 10 ? 2.0 : ((q) < 11 ? 4.0 : 8.0)) : 1.5)) diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index bf4cbdb610260..1d644627216f7 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -21,20 +21,22 @@ use bevy_render::{ }; #[derive(Eq, PartialEq, Hash, Clone, Copy)] -pub enum Quality { +pub enum Sensitivity { Low, Medium, High, Ultra, + Turbo, } -impl Quality { +impl Sensitivity { pub fn get_str(&self) -> &str { match self { - Quality::Low => "LOW", - Quality::Medium => "MEDIUM", - Quality::High => "HIGH", - Quality::Ultra => "ULTRA", + Sensitivity::Low => "LOW", + Sensitivity::Medium => "MEDIUM", + Sensitivity::High => "HIGH", + Sensitivity::Ultra => "ULTRA", + Sensitivity::Turbo => "TURBO", } } } @@ -44,23 +46,23 @@ 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 and Turbo settings can result in significant smearing and loss of detail. + /// The minimum amount of local contrast required to apply algorithm. - /// Use lower settings for a sharper, faster, result. - /// Use higher settings for a slower, smoother, result. - pub edge_threshold: Quality, + pub edge_threshold: Sensitivity, /// Trims the algorithm from processing darks. - /// Use lower settings for a sharper, faster, result. - /// Use higher settings for a slower, smoother, result. - pub edge_threshold_min: Quality, + pub edge_threshold_min: Sensitivity, } impl Default for Fxaa { fn default() -> Self { Fxaa { enabled: true, - edge_threshold: Quality::High, - edge_threshold_min: Quality::High, + edge_threshold: Sensitivity::High, + edge_threshold_min: Sensitivity::High, } } } @@ -182,8 +184,8 @@ pub struct CameraFxaaPipeline { #[derive(PartialEq, Eq, Hash, Clone, Copy)] pub struct FxaaPipelineKey { - edge_threshold: Quality, - edge_threshold_min: Quality, + edge_threshold: Sensitivity, + edge_threshold_min: Sensitivity, texture_format: TextureFormat, } diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 7291de4fb1504..5b9bcf7410711 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -3,7 +3,7 @@ use std::f32::consts::PI; use bevy::{ - core_pipeline::fxaa::{Fxaa, Quality}, + core_pipeline::fxaa::{Fxaa, Sensitivity}, prelude::*, render::{ render_resource::{Extent3d, SamplerDescriptor, TextureDimension, TextureFormat}, @@ -35,10 +35,11 @@ fn setup( println!("3 - FXAA (default)"); println!("Threshold:"); - println!("7 - LOW"); - println!("8 - MEDIUM"); - println!("9 - HIGH (default)"); - println!("0 - ULTRA"); + println!("6 - LOW"); + println!("7 - MEDIUM"); + println!("8 - HIGH (default)"); + println!("9 - ULTRA"); + println!("0 - TURBO"); // plane commands.spawn(PbrBundle { @@ -109,11 +110,12 @@ fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: let set_no_aa = keys.just_pressed(KeyCode::Key1); let set_msaa = keys.just_pressed(KeyCode::Key2); let set_fxaa = keys.just_pressed(KeyCode::Key3); - let fxaa_low = keys.just_pressed(KeyCode::Key7); - let fxaa_med = keys.just_pressed(KeyCode::Key8); - let fxaa_high = keys.just_pressed(KeyCode::Key9); - let fxaa_ultra = keys.just_pressed(KeyCode::Key0); - let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra; + let fxaa_low = keys.just_pressed(KeyCode::Key6); + let fxaa_med = keys.just_pressed(KeyCode::Key7); + let fxaa_high = keys.just_pressed(KeyCode::Key8); + let fxaa_ultra = keys.just_pressed(KeyCode::Key9); + let fxaa_turbo = keys.just_pressed(KeyCode::Key0); + let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra | fxaa_turbo; for mut fxaa in &mut query { if set_msaa { fxaa.enabled = false; @@ -129,17 +131,20 @@ fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: msaa.samples = 1; } if fxaa_low { - fxaa.edge_threshold = Quality::Low; - fxaa.edge_threshold_min = Quality::Low; + fxaa.edge_threshold = Sensitivity::Low; + fxaa.edge_threshold_min = Sensitivity::Low; } else if fxaa_med { - fxaa.edge_threshold = Quality::Medium; - fxaa.edge_threshold_min = Quality::Medium; + fxaa.edge_threshold = Sensitivity::Medium; + fxaa.edge_threshold_min = Sensitivity::Medium; } else if fxaa_high { - fxaa.edge_threshold = Quality::High; - fxaa.edge_threshold_min = Quality::High; + fxaa.edge_threshold = Sensitivity::High; + fxaa.edge_threshold_min = Sensitivity::High; } else if fxaa_ultra { - fxaa.edge_threshold = Quality::Ultra; - fxaa.edge_threshold_min = Quality::Ultra; + fxaa.edge_threshold = Sensitivity::Ultra; + fxaa.edge_threshold_min = Sensitivity::Ultra; + } else if fxaa_turbo { + fxaa.edge_threshold = Sensitivity::Turbo; + fxaa.edge_threshold_min = Sensitivity::Turbo; } if set_fxaa { fxaa.enabled = true; From 438dd08dde61c3625e3cbb874cd5001b0bc10159 Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Tue, 1 Nov 2022 17:03:33 -0700 Subject: [PATCH 16/18] reuse cube material --- examples/3d/fxaa.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 5b9bcf7410711..46d9426513a54 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -48,14 +48,16 @@ fn setup( ..default() }); + let cube_material = materials.add(StandardMaterial { + base_color_texture: Some(images.add(uv_debug_texture())), + ..default() + }); + // cubes for i in 0..5 { commands.spawn(PbrBundle { mesh: meshes.add(Mesh::from(shape::Cube { size: 0.25 })), - material: materials.add(StandardMaterial { - base_color_texture: Some(images.add(uv_debug_texture())), - ..default() - }), + material: cube_material.clone(), transform: Transform::from_xyz(i as f32 * 0.25 - 1.0, 0.125, -i as f32 * 0.5), ..default() }); From 702a7bdd84433866701b08017e458f050ca3bd5d Mon Sep 17 00:00:00 2001 From: DGriffin91 Date: Tue, 1 Nov 2022 20:47:05 -0700 Subject: [PATCH 17/18] turbo -> extreme --- crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl | 4 ++-- crates/bevy_core_pipeline/src/fxaa/mod.rs | 4 ++-- examples/3d/fxaa.rs | 12 ++++++------ 3 files changed, 10 insertions(+), 10 deletions(-) diff --git a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl index 191c4e47cc351..1c24af6d80c32 100644 --- a/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl +++ b/crates/bevy_core_pipeline/src/fxaa/fxaa.wgsl @@ -30,7 +30,7 @@ var samp: sampler; let EDGE_THRESHOLD_MIN: f32 = 0.0156; #endif -#ifdef EDGE_THRESH_MIN_TURBO +#ifdef EDGE_THRESH_MIN_EXTREME let EDGE_THRESHOLD_MIN: f32 = 0.0078; #endif @@ -51,7 +51,7 @@ var samp: sampler; let EDGE_THRESHOLD_MAX: f32 = 0.063; #endif -#ifdef EDGE_THRESH_TURBO +#ifdef EDGE_THRESH_EXTREME let EDGE_THRESHOLD_MAX: f32 = 0.031; #endif diff --git a/crates/bevy_core_pipeline/src/fxaa/mod.rs b/crates/bevy_core_pipeline/src/fxaa/mod.rs index 1d644627216f7..61f9aa17c770d 100644 --- a/crates/bevy_core_pipeline/src/fxaa/mod.rs +++ b/crates/bevy_core_pipeline/src/fxaa/mod.rs @@ -26,7 +26,7 @@ pub enum Sensitivity { Medium, High, Ultra, - Turbo, + Extreme, } impl Sensitivity { @@ -36,7 +36,7 @@ impl Sensitivity { Sensitivity::Medium => "MEDIUM", Sensitivity::High => "HIGH", Sensitivity::Ultra => "ULTRA", - Sensitivity::Turbo => "TURBO", + Sensitivity::Extreme => "EXTREME", } } } diff --git a/examples/3d/fxaa.rs b/examples/3d/fxaa.rs index 46d9426513a54..9a51145badb35 100644 --- a/examples/3d/fxaa.rs +++ b/examples/3d/fxaa.rs @@ -39,7 +39,7 @@ fn setup( println!("7 - MEDIUM"); println!("8 - HIGH (default)"); println!("9 - ULTRA"); - println!("0 - TURBO"); + println!("0 - EXTREME"); // plane commands.spawn(PbrBundle { @@ -116,8 +116,8 @@ fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: let fxaa_med = keys.just_pressed(KeyCode::Key7); let fxaa_high = keys.just_pressed(KeyCode::Key8); let fxaa_ultra = keys.just_pressed(KeyCode::Key9); - let fxaa_turbo = keys.just_pressed(KeyCode::Key0); - let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra | fxaa_turbo; + let fxaa_extreme = keys.just_pressed(KeyCode::Key0); + let set_fxaa = set_fxaa | fxaa_low | fxaa_med | fxaa_high | fxaa_ultra | fxaa_extreme; for mut fxaa in &mut query { if set_msaa { fxaa.enabled = false; @@ -144,9 +144,9 @@ fn toggle_fxaa(keys: Res>, mut query: Query<&mut Fxaa>, mut msaa: } else if fxaa_ultra { fxaa.edge_threshold = Sensitivity::Ultra; fxaa.edge_threshold_min = Sensitivity::Ultra; - } else if fxaa_turbo { - fxaa.edge_threshold = Sensitivity::Turbo; - fxaa.edge_threshold_min = Sensitivity::Turbo; + } else if fxaa_extreme { + fxaa.edge_threshold = Sensitivity::Extreme; + fxaa.edge_threshold_min = Sensitivity::Extreme; } if set_fxaa { fxaa.enabled = true; From 2c3f0992ccd95e42f1b40e56a39c87b15755dda9 Mon Sep 17 00:00:00 2001 From: Carter Anderson Date: Tue, 1 Nov 2022 23:50:59 -0700 Subject: [PATCH 18/18] Update readme --- examples/README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/examples/README.md b/examples/README.md index e5e1139785669..c2d00514e0c40 100644 --- a/examples/README.md +++ b/examples/README.md @@ -106,6 +106,7 @@ Example | Description --- | --- [3D Scene](../examples/3d/3d_scene.rs) | Simple 3D scene with basic shapes and lighting [3D Shapes](../examples/3d/3d_shapes.rs) | A scene showcasing the built-in 3D shapes +[FXAA](../examples/3d/fxaa.rs) | Compares MSAA (Multi-Sample Anti-Aliasing) and FXAA (Fast Approximate Anti-Aliasing) [Lighting](../examples/3d/lighting.rs) | Illustrates various lighting options in a simple scene [Lines](../examples/3d/lines.rs) | Create a custom material to draw 3d lines [Load glTF](../examples/3d/load_gltf.rs) | Loads and renders a glTF file as a scene