mirror of
https://github.com/bevyengine/bevy
synced 2024-11-24 21:53:07 +00:00
Add MSAA to new renderer (#3042)
Adds support for MSAA to the new renderer. This is done using the new [pipeline specialization](#3031) support to specialize on sample count. This is an alternative implementation to #2541 that cuts out the need for complicated render graph edge management by moving the relevant target information into View entities. This reuses @superdump's clever MSAA bitflag range code from #2541. Note that wgpu currently only supports 1 or 4 samples due to those being the values supported by WebGPU. However they do plan on exposing ways to [enable/query for natively supported sample counts](https://github.com/gfx-rs/wgpu/issues/1832). When this happens we should integrate
This commit is contained in:
parent
7d932ac1d8
commit
c5af1335eb
8 changed files with 213 additions and 123 deletions
|
@ -24,7 +24,7 @@ use bevy_render2::{
|
|||
render_resource::*,
|
||||
renderer::RenderDevice,
|
||||
texture::{Image, TextureCache},
|
||||
view::ExtractedView,
|
||||
view::{ExtractedView, Msaa, ViewDepthTexture},
|
||||
RenderApp, RenderStage, RenderWorld,
|
||||
};
|
||||
|
||||
|
@ -53,7 +53,6 @@ pub mod draw_2d_graph {
|
|||
pub const NAME: &str = "draw_2d";
|
||||
pub mod input {
|
||||
pub const VIEW_ENTITY: &str = "view_entity";
|
||||
pub const RENDER_TARGET: &str = "render_target";
|
||||
}
|
||||
pub mod node {
|
||||
pub const MAIN_PASS: &str = "main_pass";
|
||||
|
@ -64,8 +63,6 @@ pub mod draw_3d_graph {
|
|||
pub const NAME: &str = "draw_3d";
|
||||
pub mod input {
|
||||
pub const VIEW_ENTITY: &str = "view_entity";
|
||||
pub const RENDER_TARGET: &str = "render_target";
|
||||
pub const DEPTH: &str = "depth";
|
||||
}
|
||||
pub mod node {
|
||||
pub const MAIN_PASS: &str = "main_pass";
|
||||
|
@ -95,10 +92,10 @@ impl Plugin for CorePipelinePlugin {
|
|||
|
||||
let mut draw_2d_graph = RenderGraph::default();
|
||||
draw_2d_graph.add_node(draw_2d_graph::node::MAIN_PASS, pass_node_2d);
|
||||
let input_node_id = draw_2d_graph.set_input(vec![
|
||||
SlotInfo::new(draw_2d_graph::input::VIEW_ENTITY, SlotType::Entity),
|
||||
SlotInfo::new(draw_2d_graph::input::RENDER_TARGET, SlotType::TextureView),
|
||||
]);
|
||||
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
|
||||
draw_2d_graph::input::VIEW_ENTITY,
|
||||
SlotType::Entity,
|
||||
)]);
|
||||
draw_2d_graph
|
||||
.add_slot_edge(
|
||||
input_node_id,
|
||||
|
@ -107,23 +104,14 @@ impl Plugin for CorePipelinePlugin {
|
|||
MainPass2dNode::IN_VIEW,
|
||||
)
|
||||
.unwrap();
|
||||
draw_2d_graph
|
||||
.add_slot_edge(
|
||||
input_node_id,
|
||||
draw_2d_graph::input::RENDER_TARGET,
|
||||
draw_2d_graph::node::MAIN_PASS,
|
||||
MainPass2dNode::IN_COLOR_ATTACHMENT,
|
||||
)
|
||||
.unwrap();
|
||||
graph.add_sub_graph(draw_2d_graph::NAME, draw_2d_graph);
|
||||
|
||||
let mut draw_3d_graph = RenderGraph::default();
|
||||
draw_3d_graph.add_node(draw_3d_graph::node::MAIN_PASS, pass_node_3d);
|
||||
let input_node_id = draw_3d_graph.set_input(vec![
|
||||
SlotInfo::new(draw_3d_graph::input::VIEW_ENTITY, SlotType::Entity),
|
||||
SlotInfo::new(draw_3d_graph::input::RENDER_TARGET, SlotType::TextureView),
|
||||
SlotInfo::new(draw_3d_graph::input::DEPTH, SlotType::TextureView),
|
||||
]);
|
||||
let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
|
||||
draw_3d_graph::input::VIEW_ENTITY,
|
||||
SlotType::Entity,
|
||||
)]);
|
||||
draw_3d_graph
|
||||
.add_slot_edge(
|
||||
input_node_id,
|
||||
|
@ -132,22 +120,6 @@ impl Plugin for CorePipelinePlugin {
|
|||
MainPass3dNode::IN_VIEW,
|
||||
)
|
||||
.unwrap();
|
||||
draw_3d_graph
|
||||
.add_slot_edge(
|
||||
input_node_id,
|
||||
draw_3d_graph::input::RENDER_TARGET,
|
||||
draw_3d_graph::node::MAIN_PASS,
|
||||
MainPass3dNode::IN_COLOR_ATTACHMENT,
|
||||
)
|
||||
.unwrap();
|
||||
draw_3d_graph
|
||||
.add_slot_edge(
|
||||
input_node_id,
|
||||
draw_3d_graph::input::DEPTH,
|
||||
draw_3d_graph::node::MAIN_PASS,
|
||||
MainPass3dNode::IN_DEPTH,
|
||||
)
|
||||
.unwrap();
|
||||
graph.add_sub_graph(draw_3d_graph::NAME, draw_3d_graph);
|
||||
|
||||
graph.add_node(node::MAIN_PASS_DEPENDENCIES, EmptyNode);
|
||||
|
@ -235,11 +207,6 @@ impl RenderCommand<Transparent2d> for SetItemPipeline {
|
|||
}
|
||||
}
|
||||
|
||||
pub struct ViewDepthTexture {
|
||||
pub texture: Texture,
|
||||
pub view: TextureView,
|
||||
}
|
||||
|
||||
pub fn extract_clear_color(clear_color: Res<ClearColor>, mut render_world: ResMut<RenderWorld>) {
|
||||
// If the clear color has changed
|
||||
if clear_color.is_changed() {
|
||||
|
@ -271,10 +238,11 @@ pub fn extract_core_pipeline_camera_phases(
|
|||
pub fn prepare_core_views_system(
|
||||
mut commands: Commands,
|
||||
mut texture_cache: ResMut<TextureCache>,
|
||||
msaa: Res<Msaa>,
|
||||
render_device: Res<RenderDevice>,
|
||||
views: Query<(Entity, &ExtractedView), With<RenderPhase<Transparent3d>>>,
|
||||
views_3d: Query<(Entity, &ExtractedView), With<RenderPhase<Transparent3d>>>,
|
||||
) {
|
||||
for (entity, view) in views.iter() {
|
||||
for (entity, view) in views_3d.iter() {
|
||||
let cached_texture = texture_cache.get(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
|
@ -285,7 +253,7 @@ pub fn prepare_core_views_system(
|
|||
height: view.height as u32,
|
||||
},
|
||||
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 */
|
||||
|
|
|
@ -5,15 +5,15 @@ use bevy_render2::{
|
|||
render_phase::{DrawFunctions, RenderPhase, TrackedRenderPass},
|
||||
render_resource::{LoadOp, Operations, RenderPassColorAttachment, RenderPassDescriptor},
|
||||
renderer::RenderContext,
|
||||
view::ExtractedView,
|
||||
view::{ExtractedView, ViewTarget},
|
||||
};
|
||||
|
||||
pub struct MainPass2dNode {
|
||||
query: QueryState<&'static RenderPhase<Transparent2d>, With<ExtractedView>>,
|
||||
query:
|
||||
QueryState<(&'static RenderPhase<Transparent2d>, &'static ViewTarget), With<ExtractedView>>,
|
||||
}
|
||||
|
||||
impl MainPass2dNode {
|
||||
pub const IN_COLOR_ATTACHMENT: &'static str = "color_attachment";
|
||||
pub const IN_VIEW: &'static str = "view";
|
||||
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
|
@ -25,10 +25,7 @@ impl MainPass2dNode {
|
|||
|
||||
impl Node for MainPass2dNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![
|
||||
SlotInfo::new(MainPass2dNode::IN_COLOR_ATTACHMENT, SlotType::TextureView),
|
||||
SlotInfo::new(MainPass2dNode::IN_VIEW, SlotType::Entity),
|
||||
]
|
||||
vec![SlotInfo::new(MainPass2dNode::IN_VIEW, SlotType::Entity)]
|
||||
}
|
||||
|
||||
fn update(&mut self, world: &mut World) {
|
||||
|
@ -41,12 +38,16 @@ impl Node for MainPass2dNode {
|
|||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?;
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let (transparent_phase, target) = self
|
||||
.query
|
||||
.get_manual(world, view_entity)
|
||||
.expect("view entity should exist");
|
||||
let clear_color = world.get_resource::<ClearColor>().unwrap();
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("main_pass_2d"),
|
||||
color_attachments: &[RenderPassColorAttachment {
|
||||
view: color_attachment_texture,
|
||||
view: &target.view,
|
||||
resolve_target: None,
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(clear_color.0.into()),
|
||||
|
@ -56,16 +57,10 @@ impl Node for MainPass2dNode {
|
|||
depth_stencil_attachment: None,
|
||||
};
|
||||
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let draw_functions = world
|
||||
.get_resource::<DrawFunctions<Transparent2d>>()
|
||||
.unwrap();
|
||||
|
||||
let transparent_phase = self
|
||||
.query
|
||||
.get_manual(world, view_entity)
|
||||
.expect("view entity should exist");
|
||||
|
||||
let render_pass = render_context
|
||||
.command_encoder
|
||||
.begin_render_pass(&pass_descriptor);
|
||||
|
|
|
@ -8,16 +8,21 @@ use bevy_render2::{
|
|||
RenderPassDescriptor,
|
||||
},
|
||||
renderer::RenderContext,
|
||||
view::ExtractedView,
|
||||
view::{ExtractedView, ViewDepthTexture, ViewTarget},
|
||||
};
|
||||
|
||||
pub struct MainPass3dNode {
|
||||
query: QueryState<&'static RenderPhase<Transparent3d>, With<ExtractedView>>,
|
||||
query: QueryState<
|
||||
(
|
||||
&'static RenderPhase<Transparent3d>,
|
||||
&'static ViewTarget,
|
||||
&'static ViewDepthTexture,
|
||||
),
|
||||
With<ExtractedView>,
|
||||
>,
|
||||
}
|
||||
|
||||
impl MainPass3dNode {
|
||||
pub const IN_COLOR_ATTACHMENT: &'static str = "color_attachment";
|
||||
pub const IN_DEPTH: &'static str = "depth";
|
||||
pub const IN_VIEW: &'static str = "view";
|
||||
|
||||
pub fn new(world: &mut World) -> Self {
|
||||
|
@ -29,11 +34,7 @@ impl MainPass3dNode {
|
|||
|
||||
impl Node for MainPass3dNode {
|
||||
fn input(&self) -> Vec<SlotInfo> {
|
||||
vec![
|
||||
SlotInfo::new(MainPass3dNode::IN_COLOR_ATTACHMENT, SlotType::TextureView),
|
||||
SlotInfo::new(MainPass3dNode::IN_DEPTH, SlotType::TextureView),
|
||||
SlotInfo::new(MainPass3dNode::IN_VIEW, SlotType::Entity),
|
||||
]
|
||||
vec![SlotInfo::new(MainPass3dNode::IN_VIEW, SlotType::Entity)]
|
||||
}
|
||||
|
||||
fn update(&mut self, world: &mut World) {
|
||||
|
@ -46,21 +47,32 @@ impl Node for MainPass3dNode {
|
|||
render_context: &mut RenderContext,
|
||||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let color_attachment_texture = graph.get_input_texture(Self::IN_COLOR_ATTACHMENT)?;
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let (transparent_phase, target, depth) = self
|
||||
.query
|
||||
.get_manual(world, view_entity)
|
||||
.expect("view entity should exist");
|
||||
let clear_color = world.get_resource::<ClearColor>().unwrap();
|
||||
let depth_texture = graph.get_input_texture(Self::IN_DEPTH)?;
|
||||
let pass_descriptor = RenderPassDescriptor {
|
||||
label: Some("main_pass_3d"),
|
||||
color_attachments: &[RenderPassColorAttachment {
|
||||
view: color_attachment_texture,
|
||||
resolve_target: None,
|
||||
view: if let Some(sampled_target) = &target.sampled_target {
|
||||
sampled_target
|
||||
} else {
|
||||
&target.view
|
||||
},
|
||||
resolve_target: if target.sampled_target.is_some() {
|
||||
Some(&target.view)
|
||||
} else {
|
||||
None
|
||||
},
|
||||
ops: Operations {
|
||||
load: LoadOp::Clear(clear_color.0.into()),
|
||||
store: true,
|
||||
},
|
||||
}],
|
||||
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
|
||||
view: depth_texture,
|
||||
view: &depth.view,
|
||||
depth_ops: Some(Operations {
|
||||
load: LoadOp::Clear(0.0),
|
||||
store: true,
|
||||
|
@ -69,14 +81,9 @@ impl Node for MainPass3dNode {
|
|||
}),
|
||||
};
|
||||
|
||||
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
|
||||
let draw_functions = world
|
||||
.get_resource::<DrawFunctions<Transparent3d>>()
|
||||
.unwrap();
|
||||
let transparent_phase = self
|
||||
.query
|
||||
.get_manual(world, view_entity)
|
||||
.expect("view entity should exist");
|
||||
|
||||
let render_pass = render_context
|
||||
.command_encoder
|
||||
|
|
|
@ -1,10 +1,8 @@
|
|||
use crate::ViewDepthTexture;
|
||||
use bevy_ecs::world::World;
|
||||
use bevy_render2::{
|
||||
camera::{CameraPlugin, ExtractedCamera, ExtractedCameraNames},
|
||||
camera::{CameraPlugin, ExtractedCameraNames},
|
||||
render_graph::{Node, NodeRunError, RenderGraphContext, SlotValue},
|
||||
renderer::RenderContext,
|
||||
view::ExtractedWindows,
|
||||
};
|
||||
|
||||
pub struct MainPassDriverNode;
|
||||
|
@ -17,41 +15,17 @@ impl Node for MainPassDriverNode {
|
|||
world: &World,
|
||||
) -> Result<(), NodeRunError> {
|
||||
let extracted_cameras = world.get_resource::<ExtractedCameraNames>().unwrap();
|
||||
let extracted_windows = world.get_resource::<ExtractedWindows>().unwrap();
|
||||
|
||||
if let Some(camera_2d) = extracted_cameras.entities.get(CameraPlugin::CAMERA_2D) {
|
||||
let extracted_camera = world.entity(*camera_2d).get::<ExtractedCamera>().unwrap();
|
||||
let extracted_window = extracted_windows.get(&extracted_camera.window_id).unwrap();
|
||||
let swap_chain_texture = extracted_window
|
||||
.swap_chain_texture
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone();
|
||||
graph.run_sub_graph(
|
||||
crate::draw_2d_graph::NAME,
|
||||
vec![
|
||||
SlotValue::Entity(*camera_2d),
|
||||
SlotValue::TextureView(swap_chain_texture),
|
||||
],
|
||||
vec![SlotValue::Entity(*camera_2d)],
|
||||
)?;
|
||||
}
|
||||
|
||||
if let Some(camera_3d) = extracted_cameras.entities.get(CameraPlugin::CAMERA_3D) {
|
||||
let extracted_camera = world.entity(*camera_3d).get::<ExtractedCamera>().unwrap();
|
||||
let depth_texture = world.entity(*camera_3d).get::<ViewDepthTexture>().unwrap();
|
||||
let extracted_window = extracted_windows.get(&extracted_camera.window_id).unwrap();
|
||||
let swap_chain_texture = extracted_window
|
||||
.swap_chain_texture
|
||||
.as_ref()
|
||||
.unwrap()
|
||||
.clone();
|
||||
graph.run_sub_graph(
|
||||
crate::draw_3d_graph::NAME,
|
||||
vec![
|
||||
SlotValue::Entity(*camera_3d),
|
||||
SlotValue::TextureView(swap_chain_texture),
|
||||
SlotValue::TextureView(depth_texture.view.clone()),
|
||||
],
|
||||
vec![SlotValue::Entity(*camera_3d)],
|
||||
)?;
|
||||
}
|
||||
|
||||
|
|
|
@ -21,7 +21,7 @@ use bevy_render2::{
|
|||
render_resource::*,
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, GpuImage, Image, TextureFormatPixelInfo},
|
||||
view::{ExtractedView, ViewUniformOffset, ViewUniforms},
|
||||
view::{ExtractedView, Msaa, ViewUniformOffset, ViewUniforms},
|
||||
};
|
||||
use bevy_transform::components::GlobalTransform;
|
||||
use crevice::std140::AsStd140;
|
||||
|
@ -369,16 +369,34 @@ impl FromWorld for PbrPipeline {
|
|||
}
|
||||
}
|
||||
|
||||
// TODO: add actual specialization key: MSAA, normal maps, shadeless, etc
|
||||
bitflags::bitflags! {
|
||||
#[repr(transparent)]
|
||||
pub struct PbrPipelineKey: u32 { }
|
||||
// NOTE: Apparently quadro drivers support up to 64x MSAA.
|
||||
/// MSAA uses the highest 6 bits for the MSAA sample count - 1 to support up to 64x MSAA.
|
||||
pub struct PbrPipelineKey: u32 {
|
||||
const NONE = 0;
|
||||
const MSAA_RESERVED_BITS = PbrPipelineKey::MSAA_MASK_BITS << PbrPipelineKey::MSAA_SHIFT_BITS;
|
||||
}
|
||||
}
|
||||
|
||||
impl PbrPipelineKey {
|
||||
const MSAA_MASK_BITS: u32 = 0b111111;
|
||||
const MSAA_SHIFT_BITS: u32 = 32 - 6;
|
||||
|
||||
pub fn from_msaa_samples(msaa_samples: u32) -> Self {
|
||||
let msaa_bits = ((msaa_samples - 1) & Self::MSAA_MASK_BITS) << Self::MSAA_SHIFT_BITS;
|
||||
PbrPipelineKey::from_bits(msaa_bits).unwrap()
|
||||
}
|
||||
|
||||
pub fn msaa_samples(&self) -> u32 {
|
||||
((self.bits >> Self::MSAA_SHIFT_BITS) & Self::MSAA_MASK_BITS) + 1
|
||||
}
|
||||
}
|
||||
|
||||
impl SpecializedPipeline for PbrPipeline {
|
||||
type Key = PbrPipelineKey;
|
||||
|
||||
fn specialize(&self, _key: Self::Key) -> RenderPipelineDescriptor {
|
||||
fn specialize(&self, key: Self::Key) -> RenderPipelineDescriptor {
|
||||
RenderPipelineDescriptor {
|
||||
vertex: VertexState {
|
||||
shader: PBR_SHADER_HANDLE.typed::<Shader>(),
|
||||
|
@ -461,7 +479,7 @@ impl SpecializedPipeline for PbrPipeline {
|
|||
},
|
||||
}),
|
||||
multisample: MultisampleState {
|
||||
count: 1,
|
||||
count: key.msaa_samples(),
|
||||
mask: !0,
|
||||
alpha_to_coverage_enabled: false,
|
||||
},
|
||||
|
@ -508,6 +526,7 @@ pub fn queue_meshes(
|
|||
mut pipelines: ResMut<SpecializedPipelines<PbrPipeline>>,
|
||||
mut pipeline_cache: ResMut<RenderPipelineCache>,
|
||||
light_meta: Res<LightMeta>,
|
||||
msaa: Res<Msaa>,
|
||||
view_uniforms: Res<ViewUniforms>,
|
||||
render_materials: Res<RenderAssets<StandardMaterial>>,
|
||||
standard_material_meshes: Query<
|
||||
|
@ -525,6 +544,7 @@ pub fn queue_meshes(
|
|||
view_uniforms.uniforms.binding(),
|
||||
light_meta.view_gpu_lights.binding(),
|
||||
) {
|
||||
let msaa_key = PbrPipelineKey::from_msaa_samples(msaa.samples);
|
||||
for (entity, view, view_lights, mut transparent_phase) in views.iter_mut() {
|
||||
let view_bind_group = render_device.create_bind_group(&BindGroupDescriptor {
|
||||
entries: &[
|
||||
|
@ -580,8 +600,8 @@ pub fn queue_meshes(
|
|||
continue;
|
||||
}
|
||||
|
||||
let key = PbrPipelineKey::empty();
|
||||
let pipeline_id = pipelines.specialize(&mut pipeline_cache, &pbr_pipeline, key);
|
||||
let pipeline_id =
|
||||
pipelines.specialize(&mut pipeline_cache, &pbr_pipeline, msaa_key);
|
||||
// NOTE: row 2 of the view matrix dotted with column 3 of the model matrix
|
||||
// gives the z component of translation of the mesh in view space
|
||||
let mesh_z = view_row_2.dot(mesh_uniform.transform.col(3));
|
||||
|
@ -692,3 +712,14 @@ impl RenderCommand<Transparent3d> for DrawMesh {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
#[cfg(test)]
|
||||
mod tests {
|
||||
use super::PbrPipelineKey;
|
||||
#[test]
|
||||
fn pbr_key_msaa_samples() {
|
||||
for i in 1..=64 {
|
||||
assert_eq!(PbrPipelineKey::from_msaa_samples(i).msaa_samples(), i);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,10 @@ use bevy_utils::tracing::{info, info_span};
|
|||
pub use graph_runner::*;
|
||||
pub use render_device::*;
|
||||
|
||||
use crate::{render_graph::RenderGraph, view::ExtractedWindows};
|
||||
use crate::{
|
||||
render_graph::RenderGraph,
|
||||
view::{ExtractedWindows, ViewTarget},
|
||||
};
|
||||
use bevy_ecs::prelude::*;
|
||||
use std::sync::Arc;
|
||||
use wgpu::{CommandEncoder, DeviceDescriptor, Instance, Queue, RequestAdapterOptions};
|
||||
|
@ -27,6 +30,17 @@ pub fn render_system(world: &mut World) {
|
|||
{
|
||||
let span = info_span!("present_frames");
|
||||
let _guard = span.enter();
|
||||
|
||||
// Remove ViewTarget components to ensure swap chain TextureViews are dropped.
|
||||
// If all TextureViews aren't dropped before present, acquiring the next swap chain texture will fail.
|
||||
let view_entities = world
|
||||
.query_filtered::<Entity, With<ViewTarget>>()
|
||||
.iter(world)
|
||||
.collect::<Vec<_>>();
|
||||
for view_entity in view_entities {
|
||||
world.entity_mut(view_entity).remove::<ViewTarget>();
|
||||
}
|
||||
|
||||
let mut windows = world.get_resource_mut::<ExtractedWindows>().unwrap();
|
||||
for window in windows.values_mut() {
|
||||
if let Some(texture_view) = window.swap_chain_texture.take() {
|
||||
|
|
|
@ -1,10 +1,13 @@
|
|||
pub mod window;
|
||||
|
||||
use wgpu::{Extent3d, TextureDescriptor, TextureDimension, TextureFormat, TextureUsages};
|
||||
pub use window::*;
|
||||
|
||||
use crate::{
|
||||
render_resource::DynamicUniformVec,
|
||||
camera::{ExtractedCamera, ExtractedCameraNames},
|
||||
render_resource::{DynamicUniformVec, Texture, TextureView},
|
||||
renderer::{RenderDevice, RenderQueue},
|
||||
texture::{BevyDefault, TextureCache},
|
||||
RenderApp, RenderStage,
|
||||
};
|
||||
use bevy_app::{App, Plugin};
|
||||
|
@ -17,12 +20,38 @@ pub struct ViewPlugin;
|
|||
|
||||
impl Plugin for ViewPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.init_resource::<Msaa>();
|
||||
app.sub_app(RenderApp)
|
||||
.init_resource::<ViewUniforms>()
|
||||
.add_system_to_stage(RenderStage::Prepare, prepare_views);
|
||||
.add_system_to_stage(RenderStage::Extract, extract_msaa)
|
||||
.add_system_to_stage(RenderStage::Prepare, prepare_view_uniforms)
|
||||
.add_system_to_stage(
|
||||
RenderStage::Prepare,
|
||||
prepare_view_targets.after(WindowSystem::Prepare),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Clone)]
|
||||
pub struct Msaa {
|
||||
/// The number of samples to run for Multi-Sample Anti-Aliasing. Higher numbers result in
|
||||
/// smoother edges. Note that WGPU currently only supports 1 or 4 samples.
|
||||
/// Ultimately we plan on supporting whatever is natively supported on a given device.
|
||||
/// Check out this issue for more info: https://github.com/gfx-rs/wgpu/issues/1832
|
||||
pub samples: u32,
|
||||
}
|
||||
|
||||
impl Default for Msaa {
|
||||
fn default() -> Self {
|
||||
Self { samples: 4 }
|
||||
}
|
||||
}
|
||||
|
||||
pub fn extract_msaa(mut commands: Commands, msaa: Res<Msaa>) {
|
||||
// NOTE: windows.is_changed() handles cases where a window was resized
|
||||
commands.insert_resource(msaa.clone());
|
||||
}
|
||||
|
||||
pub struct ExtractedView {
|
||||
pub projection: Mat4,
|
||||
pub transform: GlobalTransform,
|
||||
|
@ -46,17 +75,27 @@ pub struct ViewUniformOffset {
|
|||
pub offset: u32,
|
||||
}
|
||||
|
||||
fn prepare_views(
|
||||
pub struct ViewTarget {
|
||||
pub view: TextureView,
|
||||
pub sampled_target: Option<TextureView>,
|
||||
}
|
||||
|
||||
pub struct ViewDepthTexture {
|
||||
pub texture: Texture,
|
||||
pub view: TextureView,
|
||||
}
|
||||
|
||||
fn prepare_view_uniforms(
|
||||
mut commands: Commands,
|
||||
render_device: Res<RenderDevice>,
|
||||
render_queue: Res<RenderQueue>,
|
||||
mut view_uniforms: ResMut<ViewUniforms>,
|
||||
mut extracted_views: Query<(Entity, &ExtractedView)>,
|
||||
mut views: Query<(Entity, &ExtractedView)>,
|
||||
) {
|
||||
view_uniforms
|
||||
.uniforms
|
||||
.reserve_and_clear(extracted_views.iter_mut().len(), &render_device);
|
||||
for (entity, camera) in extracted_views.iter() {
|
||||
.reserve_and_clear(views.iter_mut().len(), &render_device);
|
||||
for (entity, camera) in views.iter() {
|
||||
let projection = camera.projection;
|
||||
let view_uniforms = ViewUniformOffset {
|
||||
offset: view_uniforms.uniforms.push(ViewUniform {
|
||||
|
@ -71,3 +110,57 @@ fn prepare_views(
|
|||
|
||||
view_uniforms.uniforms.write_buffer(&render_queue);
|
||||
}
|
||||
|
||||
fn prepare_view_targets(
|
||||
mut commands: Commands,
|
||||
camera_names: Res<ExtractedCameraNames>,
|
||||
windows: Res<ExtractedWindows>,
|
||||
msaa: Res<Msaa>,
|
||||
render_device: Res<RenderDevice>,
|
||||
mut texture_cache: ResMut<TextureCache>,
|
||||
cameras: Query<&ExtractedCamera>,
|
||||
) {
|
||||
for entity in camera_names.entities.values().copied() {
|
||||
let camera = if let Ok(camera) = cameras.get(entity) {
|
||||
camera
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let window = if let Some(window) = windows.get(&camera.window_id) {
|
||||
window
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let swap_chain_texture = if let Some(texture) = &window.swap_chain_texture {
|
||||
texture
|
||||
} else {
|
||||
continue;
|
||||
};
|
||||
let sampled_target = if msaa.samples > 1 {
|
||||
let sampled_texture = texture_cache.get(
|
||||
&render_device,
|
||||
TextureDescriptor {
|
||||
label: Some("sampled_color_attachment_texture"),
|
||||
size: Extent3d {
|
||||
width: window.physical_width,
|
||||
height: window.physical_height,
|
||||
depth_or_array_layers: 1,
|
||||
},
|
||||
mip_level_count: 1,
|
||||
sample_count: msaa.samples,
|
||||
dimension: TextureDimension::D2,
|
||||
format: TextureFormat::bevy_default(),
|
||||
usage: TextureUsages::RENDER_ATTACHMENT,
|
||||
},
|
||||
);
|
||||
Some(sampled_texture.default_view.clone())
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
commands.entity(entity).insert(ViewTarget {
|
||||
view: swap_chain_texture.clone(),
|
||||
sampled_target,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
|
|
@ -17,6 +17,11 @@ pub struct NonSendMarker;
|
|||
|
||||
pub struct WindowRenderPlugin;
|
||||
|
||||
#[derive(SystemLabel, Debug, Clone, PartialEq, Eq, Hash)]
|
||||
pub enum WindowSystem {
|
||||
Prepare,
|
||||
}
|
||||
|
||||
impl Plugin for WindowRenderPlugin {
|
||||
fn build(&self, app: &mut App) {
|
||||
app.sub_app(RenderApp)
|
||||
|
@ -24,7 +29,10 @@ impl Plugin for WindowRenderPlugin {
|
|||
.init_resource::<WindowSurfaces>()
|
||||
.init_resource::<NonSendMarker>()
|
||||
.add_system_to_stage(RenderStage::Extract, extract_windows)
|
||||
.add_system_to_stage(RenderStage::Prepare, prepare_windows);
|
||||
.add_system_to_stage(
|
||||
RenderStage::Prepare,
|
||||
prepare_windows.label(WindowSystem::Prepare),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue