render: add MSAA support
This commit is contained in:
parent
a2c1a90695
commit
ca87359c6e
@ -63,6 +63,10 @@ path = "examples/2d/texture_atlas.rs"
|
||||
name = "load_model"
|
||||
path = "examples/3d/load_model.rs"
|
||||
|
||||
[[example]]
|
||||
name = "msaa"
|
||||
path = "examples/3d/msaa.rs"
|
||||
|
||||
[[example]]
|
||||
name = "parenting"
|
||||
path = "examples/3d/parenting.rs"
|
||||
|
||||
@ -15,6 +15,7 @@ pub use once_cell;
|
||||
|
||||
pub mod prelude {
|
||||
pub use crate::{
|
||||
base::Msaa,
|
||||
color::Color,
|
||||
draw::Draw,
|
||||
entity::*,
|
||||
@ -26,6 +27,7 @@ pub mod prelude {
|
||||
}
|
||||
|
||||
use crate::prelude::*;
|
||||
use base::Msaa;
|
||||
use bevy_app::prelude::*;
|
||||
use bevy_asset::AddAsset;
|
||||
use bevy_ecs::{IntoQuerySystem, IntoThreadLocalSystem};
|
||||
@ -131,10 +133,15 @@ impl AppPlugin for RenderPlugin {
|
||||
shader::clear_shader_defs_system.system(),
|
||||
);
|
||||
|
||||
if app.resources().get::<Msaa>().is_none() {
|
||||
app.init_resource::<Msaa>();
|
||||
}
|
||||
|
||||
if let Some(ref config) = self.base_render_graph_config {
|
||||
let resources = app.resources();
|
||||
let mut render_graph = resources.get_mut::<RenderGraph>().unwrap();
|
||||
render_graph.add_base_graph(config);
|
||||
let msaa = resources.get::<Msaa>().unwrap();
|
||||
render_graph.add_base_graph(config, &msaa);
|
||||
let mut active_cameras = resources.get_mut::<ActiveCameras>().unwrap();
|
||||
if config.add_3d_camera {
|
||||
active_cameras.add(base::camera::CAMERA3D);
|
||||
|
||||
@ -7,11 +7,23 @@ use bevy_asset::{Assets, Handle};
|
||||
use once_cell::sync::Lazy;
|
||||
use std::collections::{HashMap, HashSet};
|
||||
|
||||
#[derive(Clone, Eq, PartialEq, Debug, Default)]
|
||||
#[derive(Clone, Eq, PartialEq, Debug)]
|
||||
pub struct PipelineSpecialization {
|
||||
pub shader_specialization: ShaderSpecialization,
|
||||
pub primitive_topology: PrimitiveTopology,
|
||||
pub dynamic_bindings: Vec<DynamicBinding>,
|
||||
pub sample_count: u32,
|
||||
}
|
||||
|
||||
impl Default for PipelineSpecialization {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
sample_count: 1,
|
||||
shader_specialization: Default::default(),
|
||||
primitive_topology: Default::default(),
|
||||
dynamic_bindings: Default::default(),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
impl PipelineSpecialization {
|
||||
@ -145,6 +157,8 @@ impl PipelineCompiler {
|
||||
Some(vertex_buffer_descriptors),
|
||||
&pipeline_specialization.dynamic_bindings,
|
||||
);
|
||||
|
||||
specialized_descriptor.sample_count = pipeline_specialization.sample_count;
|
||||
specialized_descriptor.primitive_topology = pipeline_specialization.primitive_topology;
|
||||
|
||||
let specialized_pipeline_handle = pipelines.add(specialized_descriptor);
|
||||
|
||||
@ -1,10 +1,10 @@
|
||||
use super::{PipelineDescriptor, PipelineSpecialization};
|
||||
use crate::{
|
||||
draw::{Draw, DrawContext, DrawError, Drawable},
|
||||
renderer::RenderResourceBindings,
|
||||
renderer::RenderResourceBindings, prelude::Msaa,
|
||||
};
|
||||
use bevy_asset::Handle;
|
||||
use bevy_ecs::{Query, ResMut};
|
||||
use bevy_ecs::{Query, ResMut, Res};
|
||||
use bevy_property::Properties;
|
||||
#[derive(Properties, Default, Clone)]
|
||||
pub struct RenderPipeline {
|
||||
@ -104,9 +104,14 @@ impl<'a> Drawable for DrawableRenderPipelines<'a> {
|
||||
pub fn draw_render_pipelines_system(
|
||||
mut draw_context: DrawContext,
|
||||
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
||||
msaa: Res<Msaa>,
|
||||
mut query: Query<(&mut Draw, &mut RenderPipelines)>,
|
||||
) {
|
||||
for (mut draw, mut render_pipelines) in &mut query.iter() {
|
||||
for pipeline in render_pipelines.pipelines.iter_mut() {
|
||||
pipeline.specialization.sample_count = msaa.samples;
|
||||
}
|
||||
|
||||
let mut drawable = DrawableRenderPipelines {
|
||||
render_pipelines: &mut render_pipelines,
|
||||
render_resource_bindings: &mut render_resource_bindings,
|
||||
|
||||
@ -13,6 +13,18 @@ use crate::{
|
||||
};
|
||||
use bevy_window::WindowId;
|
||||
|
||||
pub struct Msaa {
|
||||
pub samples: u32,
|
||||
}
|
||||
|
||||
impl Default for Msaa {
|
||||
fn default() -> Self {
|
||||
Self {
|
||||
samples: 4,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
pub struct BaseRenderGraphConfig {
|
||||
pub add_2d_camera: bool,
|
||||
pub add_3d_camera: bool,
|
||||
@ -28,6 +40,7 @@ pub mod node {
|
||||
pub const CAMERA2D: &str = "camera2d";
|
||||
pub const TEXTURE_COPY: &str = "texture_copy";
|
||||
pub const MAIN_DEPTH_TEXTURE: &str = "main_pass_depth_texture";
|
||||
pub const MAIN_SAMPLED_COLOR_ATTACHMENT: &str = "main_pass_sampled_color_attachment";
|
||||
pub const MAIN_PASS: &str = "main_pass";
|
||||
pub const SHARED_BUFFERS: &str = "shared_buffers";
|
||||
}
|
||||
@ -53,11 +66,11 @@ impl Default for BaseRenderGraphConfig {
|
||||
/// By itself this graph doesn't do much, but it allows Render plugins to interop with each other by having a common
|
||||
/// set of nodes. It can be customized using `BaseRenderGraphConfig`.
|
||||
pub trait BaseRenderGraphBuilder {
|
||||
fn add_base_graph(&mut self, config: &BaseRenderGraphConfig) -> &mut Self;
|
||||
fn add_base_graph(&mut self, config: &BaseRenderGraphConfig, msaa: &Msaa) -> &mut Self;
|
||||
}
|
||||
|
||||
impl BaseRenderGraphBuilder for RenderGraph {
|
||||
fn add_base_graph(&mut self, config: &BaseRenderGraphConfig) -> &mut Self {
|
||||
fn add_base_graph(&mut self, config: &BaseRenderGraphConfig, msaa: &Msaa) -> &mut Self {
|
||||
self.add_node(node::TEXTURE_COPY, TextureCopyNode::default());
|
||||
if config.add_3d_camera {
|
||||
self.add_system_node(node::CAMERA3D, CameraNode::new(camera::CAMERA3D));
|
||||
@ -80,7 +93,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||
height: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Depth32Float, // PERF: vulkan docs recommend using 24 bit depth for better performance
|
||||
usage: TextureUsage::OUTPUT_ATTACHMENT,
|
||||
@ -90,15 +103,29 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||
}
|
||||
|
||||
if config.add_main_pass {
|
||||
let mut main_pass_node = PassNode::<&MainPass>::new(PassDescriptor {
|
||||
color_attachments: vec![RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color".to_string()),
|
||||
let color_attachment = if msaa.samples > 1 {
|
||||
RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color_attachment".to_string()),
|
||||
resolve_target: Some(TextureAttachment::Input(
|
||||
"color_resolve_target".to_string(),
|
||||
)),
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(Color::rgb(0.1, 0.1, 0.1)),
|
||||
store: true,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color_attachment".to_string()),
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(Color::rgb(0.1, 0.1, 0.1)),
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
}
|
||||
};
|
||||
let mut main_pass_node = PassNode::<&MainPass>::new(PassDescriptor {
|
||||
color_attachments: vec![color_attachment],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("depth".to_string()),
|
||||
depth_ops: Some(Operations {
|
||||
@ -107,7 +134,7 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
sample_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
});
|
||||
|
||||
main_pass_node.use_default_clear_color(0);
|
||||
@ -146,7 +173,40 @@ impl BaseRenderGraphBuilder for RenderGraph {
|
||||
node::PRIMARY_SWAP_CHAIN,
|
||||
WindowSwapChainNode::OUT_TEXTURE,
|
||||
node::MAIN_PASS,
|
||||
"color",
|
||||
if msaa.samples > 1 {
|
||||
"color_resolve_target"
|
||||
} else {
|
||||
"color_attachment"
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
if msaa.samples > 1 {
|
||||
self.add_node(
|
||||
node::MAIN_SAMPLED_COLOR_ATTACHMENT,
|
||||
WindowTextureNode::new(
|
||||
WindowId::primary(),
|
||||
TextureDescriptor {
|
||||
size: Extent3d {
|
||||
depth: 1,
|
||||
width: 1,
|
||||
height: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::Bgra8UnormSrgb,
|
||||
usage: TextureUsage::OUTPUT_ATTACHMENT,
|
||||
},
|
||||
),
|
||||
);
|
||||
|
||||
self.add_slot_edge(
|
||||
node::MAIN_SAMPLED_COLOR_ATTACHMENT,
|
||||
WindowSwapChainNode::OUT_TEXTURE,
|
||||
node::MAIN_PASS,
|
||||
"color_attachment",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
@ -24,6 +24,7 @@ pub struct PassNode<Q: HecsQuery> {
|
||||
inputs: Vec<ResourceSlotInfo>,
|
||||
cameras: Vec<CameraInfo>,
|
||||
color_attachment_input_indices: Vec<Option<usize>>,
|
||||
color_resolve_target_indices: Vec<Option<usize>>,
|
||||
depth_stencil_attachment_input_index: Option<usize>,
|
||||
default_clear_color_inputs: Vec<usize>,
|
||||
camera_bind_group_descriptor: BindGroupDescriptor,
|
||||
@ -34,26 +35,37 @@ impl<Q: HecsQuery> PassNode<Q> {
|
||||
pub fn new(descriptor: PassDescriptor) -> Self {
|
||||
let mut inputs = Vec::new();
|
||||
let mut color_attachment_input_indices = Vec::new();
|
||||
let mut color_resolve_target_indices = Vec::new();
|
||||
for color_attachment in descriptor.color_attachments.iter() {
|
||||
if let TextureAttachment::Input(ref name) = color_attachment.attachment {
|
||||
color_attachment_input_indices.push(Some(inputs.len()));
|
||||
inputs.push(ResourceSlotInfo::new(
|
||||
name.to_string(),
|
||||
RenderResourceType::Texture,
|
||||
));
|
||||
color_attachment_input_indices.push(Some(inputs.len() - 1));
|
||||
} else {
|
||||
color_attachment_input_indices.push(None);
|
||||
}
|
||||
|
||||
if let Some(TextureAttachment::Input(ref name)) = color_attachment.resolve_target {
|
||||
color_resolve_target_indices.push(Some(inputs.len()));
|
||||
inputs.push(ResourceSlotInfo::new(
|
||||
name.to_string(),
|
||||
RenderResourceType::Texture,
|
||||
));
|
||||
} else {
|
||||
color_resolve_target_indices.push(None);
|
||||
}
|
||||
}
|
||||
|
||||
let mut depth_stencil_attachment_input_index = None;
|
||||
if let Some(ref depth_stencil_attachment) = descriptor.depth_stencil_attachment {
|
||||
if let TextureAttachment::Input(ref name) = depth_stencil_attachment.attachment {
|
||||
depth_stencil_attachment_input_index = Some(inputs.len());
|
||||
inputs.push(ResourceSlotInfo::new(
|
||||
name.to_string(),
|
||||
RenderResourceType::Texture,
|
||||
));
|
||||
depth_stencil_attachment_input_index = Some(inputs.len() - 1);
|
||||
}
|
||||
}
|
||||
|
||||
@ -74,6 +86,7 @@ impl<Q: HecsQuery> PassNode<Q> {
|
||||
inputs,
|
||||
cameras: Vec::new(),
|
||||
color_attachment_input_indices,
|
||||
color_resolve_target_indices,
|
||||
depth_stencil_attachment_input_index,
|
||||
default_clear_color_inputs: Vec::new(),
|
||||
camera_bind_group_descriptor,
|
||||
@ -120,6 +133,10 @@ impl<Q: HecsQuery + Send + Sync + 'static> Node for PassNode<Q> {
|
||||
color_attachment.attachment =
|
||||
TextureAttachment::Id(input.get(input_index).unwrap().get_texture().unwrap());
|
||||
}
|
||||
if let Some(input_index) = self.color_resolve_target_indices[i] {
|
||||
color_attachment.resolve_target =
|
||||
Some(TextureAttachment::Id(input.get(input_index).unwrap().get_texture().unwrap()));
|
||||
}
|
||||
}
|
||||
|
||||
if let Some(input_index) = self.depth_stencil_attachment_input_index {
|
||||
|
||||
@ -10,7 +10,7 @@ use bevy_render::{
|
||||
renderer::{
|
||||
AssetRenderResourceBindings, BindGroup, BufferUsage, RenderResourceBindings,
|
||||
RenderResourceId,
|
||||
},
|
||||
}, prelude::Msaa,
|
||||
};
|
||||
use bevy_sprite::{TextureAtlas, TextureAtlasSprite};
|
||||
|
||||
@ -38,6 +38,7 @@ pub struct DrawableText<'a> {
|
||||
pub container_size: Vec2,
|
||||
pub style: &'a TextStyle,
|
||||
pub text: &'a str,
|
||||
pub msaa: &'a Msaa,
|
||||
}
|
||||
|
||||
impl<'a> Drawable for DrawableText<'a> {
|
||||
@ -45,8 +46,10 @@ impl<'a> Drawable for DrawableText<'a> {
|
||||
context.set_pipeline(
|
||||
draw,
|
||||
bevy_sprite::SPRITE_SHEET_PIPELINE_HANDLE,
|
||||
// TODO: remove this shader def specialization when its easier to manually bind global render resources to specific bind groups
|
||||
&PipelineSpecialization::default(),
|
||||
&PipelineSpecialization {
|
||||
sample_count: self.msaa.samples,
|
||||
..Default::default()
|
||||
},
|
||||
)?;
|
||||
|
||||
let render_resource_context = &**context.render_resource_context;
|
||||
|
||||
@ -8,6 +8,7 @@ use bevy_render::{
|
||||
RenderPassDepthStencilAttachmentDescriptor, TextureAttachment,
|
||||
},
|
||||
pipeline::*,
|
||||
prelude::Msaa,
|
||||
render_graph::{
|
||||
base, CameraNode, PassNode, RenderGraph, RenderResourcesNode, WindowSwapChainNode,
|
||||
WindowTextureNode,
|
||||
@ -82,17 +83,31 @@ impl UiRenderGraphBuilder for RenderGraph {
|
||||
fn add_ui_graph(&mut self, resources: &Resources) -> &mut Self {
|
||||
let mut pipelines = resources.get_mut::<Assets<PipelineDescriptor>>().unwrap();
|
||||
let mut shaders = resources.get_mut::<Assets<Shader>>().unwrap();
|
||||
let msaa = resources.get::<Msaa>().unwrap();
|
||||
pipelines.set(UI_PIPELINE_HANDLE, build_ui_pipeline(&mut shaders));
|
||||
|
||||
let mut ui_pass_node = PassNode::<&Node>::new(PassDescriptor {
|
||||
color_attachments: vec![RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color".to_string()),
|
||||
let color_attachment = if msaa.samples > 1 {
|
||||
RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color_attachment".to_string()),
|
||||
resolve_target: Some(TextureAttachment::Input("color_resolve_target".to_string())),
|
||||
ops: Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
}
|
||||
} else {
|
||||
RenderPassColorAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("color_attachment".to_string()),
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Load,
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
}
|
||||
};
|
||||
|
||||
let mut ui_pass_node = PassNode::<&Node>::new(PassDescriptor {
|
||||
color_attachments: vec![color_attachment],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachmentDescriptor {
|
||||
attachment: TextureAttachment::Input("depth".to_string()),
|
||||
depth_ops: Some(Operations {
|
||||
@ -101,7 +116,7 @@ impl UiRenderGraphBuilder for RenderGraph {
|
||||
}),
|
||||
stencil_ops: None,
|
||||
}),
|
||||
sample_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
});
|
||||
ui_pass_node.add_camera(camera::UI_CAMERA);
|
||||
self.add_node(node::UI_PASS, ui_pass_node);
|
||||
@ -110,7 +125,11 @@ impl UiRenderGraphBuilder for RenderGraph {
|
||||
base::node::PRIMARY_SWAP_CHAIN,
|
||||
WindowSwapChainNode::OUT_TEXTURE,
|
||||
node::UI_PASS,
|
||||
"color",
|
||||
if msaa.samples > 1 {
|
||||
"color_resolve_target"
|
||||
} else {
|
||||
"color_attachment"
|
||||
},
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
@ -122,6 +141,17 @@ impl UiRenderGraphBuilder for RenderGraph {
|
||||
)
|
||||
.unwrap();
|
||||
|
||||
if msaa.samples > 1 {
|
||||
self.add_slot_edge(
|
||||
base::node::MAIN_SAMPLED_COLOR_ATTACHMENT,
|
||||
WindowSwapChainNode::OUT_TEXTURE,
|
||||
node::UI_PASS,
|
||||
"color_attachment",
|
||||
)
|
||||
.unwrap();
|
||||
}
|
||||
|
||||
|
||||
// ensure ui pass runs after main pass
|
||||
self.add_node_edge(base::node::MAIN_PASS, node::UI_PASS)
|
||||
.unwrap();
|
||||
|
||||
@ -5,7 +5,7 @@ use bevy_math::Size;
|
||||
use bevy_render::{
|
||||
draw::{Draw, DrawContext, Drawable},
|
||||
renderer::{AssetRenderResourceBindings, RenderResourceBindings},
|
||||
texture::Texture,
|
||||
texture::Texture, prelude::Msaa,
|
||||
};
|
||||
use bevy_sprite::TextureAtlas;
|
||||
use bevy_text::{DrawableText, Font, FontAtlasSet, TextStyle};
|
||||
@ -51,6 +51,7 @@ pub fn text_system(
|
||||
pub fn draw_text_system(
|
||||
mut draw_context: DrawContext,
|
||||
fonts: Res<Assets<Font>>,
|
||||
msaa: Res<Msaa>,
|
||||
font_atlas_sets: Res<Assets<FontAtlasSet>>,
|
||||
texture_atlases: Res<Assets<TextureAtlas>>,
|
||||
mut render_resource_bindings: ResMut<RenderResourceBindings>,
|
||||
@ -69,6 +70,7 @@ pub fn draw_text_system(
|
||||
render_resource_bindings: &mut render_resource_bindings,
|
||||
asset_render_resource_bindings: &mut asset_render_resource_bindings,
|
||||
position,
|
||||
msaa: &msaa,
|
||||
style: &text.style,
|
||||
text: &text.value,
|
||||
container_size: node.size,
|
||||
|
||||
45
examples/3d/msaa.rs
Normal file
45
examples/3d/msaa.rs
Normal file
@ -0,0 +1,45 @@
|
||||
use bevy::prelude::*;
|
||||
|
||||
/// This example shows how to configure Multi-Sample Anti-Aliasing. Setting the sample count higher will result in smoother edges,
|
||||
/// but it will also increase the cost to render those edges. The range should generally be somewhere between 1 (no multi sampling,
|
||||
/// but cheap) to 8 (crisp but expensive)
|
||||
fn main() {
|
||||
App::build()
|
||||
.add_resource(Msaa { samples: 4 })
|
||||
.add_default_plugins()
|
||||
.add_startup_system(setup.system())
|
||||
.run();
|
||||
}
|
||||
|
||||
/// set up a simple 3D scene
|
||||
fn setup(
|
||||
mut commands: Commands,
|
||||
mut meshes: ResMut<Assets<Mesh>>,
|
||||
mut materials: ResMut<Assets<StandardMaterial>>,
|
||||
) {
|
||||
// add entities to the world
|
||||
commands
|
||||
// cube
|
||||
.spawn(PbrComponents {
|
||||
mesh: meshes.add(Mesh::from(shape::Cube { size: 1.0 })),
|
||||
material: materials.add(StandardMaterial {
|
||||
albedo: Color::rgb(0.5, 0.4, 0.3),
|
||||
..Default::default()
|
||||
}),
|
||||
..Default::default()
|
||||
})
|
||||
// light
|
||||
.spawn(LightComponents {
|
||||
translation: Translation::new(4.0, 8.0, 4.0),
|
||||
..Default::default()
|
||||
})
|
||||
// camera
|
||||
.spawn(Camera3dComponents {
|
||||
transform: Transform::new_sync_disabled(Mat4::face_toward(
|
||||
Vec3::new(-3.0, 3.0, 5.0),
|
||||
Vec3::new(0.0, 0.0, 0.0),
|
||||
Vec3::new(0.0, 1.0, 0.0),
|
||||
)),
|
||||
..Default::default()
|
||||
});
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user