Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

GPU picking #6991

Closed
wants to merge 22 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -670,6 +670,17 @@ description = "An application that runs with default plugins and displays an emp
category = "Application"
wasm = false

[[example]]
name = "picking"
path = "examples/app/picking.rs"

[package.metadata.example.picking]
name = "Picking"
description = "An application that show how to pick/select objects and UI elements"
category = "Application"
# TODO, are we wasm compatible?
wasm = false

[[example]]
name = "without_winit"
path = "examples/app/without_winit.rs"
Expand Down
1 change: 1 addition & 0 deletions crates/bevy_core_pipeline/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,3 +31,4 @@ bevy_utils = { path = "../bevy_utils", version = "0.9.0" }
serde = { version = "1", features = ["derive"] }
bitflags = "1.2"
radsort = "0.1"
bytemuck = { version = "1.5", features = ["derive"] }
33 changes: 22 additions & 11 deletions crates/bevy_core_pipeline/src/core_2d/main_pass_2d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
picking::PickingTextures,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDescriptor},
Expand All @@ -21,6 +22,7 @@ pub struct MainPass2dNode {
&'static RenderPhase<Transparent2d>,
&'static ViewTarget,
&'static Camera2d,
Option<&'static PickingTextures>,
),
With<ExtractedView>,
>,
Expand Down Expand Up @@ -52,7 +54,7 @@ impl Node for MainPass2dNode {
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let (camera, transparent_phase, target, camera_2d) =
let (camera, transparent_phase, target, camera_2d, picking_textures) =
if let Ok(result) = self.query.get_manual(world, view_entity) {
result
} else {
Expand All @@ -63,18 +65,27 @@ impl Node for MainPass2dNode {
#[cfg(feature = "trace")]
let _main_pass_2d = info_span!("main_pass_2d").entered();

let mut color_attachments = vec![Some(target.get_color_attachment(Operations {
load: match camera_2d.clear_color {
ClearColorConfig::Default => {
LoadOp::Clear(world.resource::<ClearColor>().0.into())
}
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
},
store: true,
}))];

if let Some(picking_textures) = picking_textures {
color_attachments.push(Some(picking_textures.get_color_attachment(Operations {
load: LoadOp::Clear(PickingTextures::clear_color()),
store: true,
})));
}

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_pass_2d"),
color_attachments: &[Some(target.get_color_attachment(Operations {
load: match camera_2d.clear_color {
ClearColorConfig::Default => {
LoadOp::Clear(world.resource::<ClearColor>().0.into())
}
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
},
store: true,
}))],
color_attachments: &color_attachments,
depth_stencil_attachment: None,
});

Expand Down
13 changes: 12 additions & 1 deletion crates/bevy_core_pipeline/src/core_2d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod graph {
pub const BLOOM: &str = "bloom";
pub const TONEMAPPING: &str = "tonemapping";
pub const FXAA: &str = "fxaa";
pub const PICKING: &str = "picking";
pub const UPSCALING: &str = "upscaling";
pub const END_MAIN_PASS_POST_PROCESSING: &str = "end_main_pass_post_processing";
}
Expand All @@ -35,7 +36,7 @@ use bevy_render::{
use bevy_utils::FloatOrd;
use std::ops::Range;

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
use crate::{picking::node::PickingNode, tonemapping::TonemappingNode, upscaling::UpscalingNode};

pub struct Core2dPlugin;

Expand All @@ -61,13 +62,16 @@ impl Plugin for Core2dPlugin {
let pass_node_2d = MainPass2dNode::new(&mut render_app.world);
let tonemapping = TonemappingNode::new(&mut render_app.world);
let upscaling = UpscalingNode::new(&mut render_app.world);
let picking = PickingNode::new(&mut render_app.world);

let mut graph = render_app.world.resource_mut::<RenderGraph>();

let mut draw_2d_graph = RenderGraph::default();
draw_2d_graph.add_node(graph::node::MAIN_PASS, pass_node_2d);
draw_2d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
draw_2d_graph.add_node(graph::node::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
draw_2d_graph.add_node(graph::node::UPSCALING, upscaling);
draw_2d_graph.add_node(graph::node::PICKING, picking);
let input_node_id = draw_2d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
Expand All @@ -90,6 +94,12 @@ impl Plugin for Core2dPlugin {
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
);
draw_2d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::PICKING,
PickingNode::IN_VIEW,
);
draw_2d_graph.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING);
draw_2d_graph.add_node_edge(
graph::node::TONEMAPPING,
Expand All @@ -99,6 +109,7 @@ impl Plugin for Core2dPlugin {
graph::node::END_MAIN_PASS_POST_PROCESSING,
graph::node::UPSCALING,
);
draw_2d_graph.add_node_edge(graph::node::UPSCALING, graph::node::PICKING);
graph.add_sub_graph(graph::NAME, draw_2d_graph);
}
}
Expand Down
86 changes: 61 additions & 25 deletions crates/bevy_core_pipeline/src/core_3d/main_pass_3d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ use crate::{
use bevy_ecs::prelude::*;
use bevy_render::{
camera::ExtractedCamera,
picking::PickingTextures,
render_graph::{Node, NodeRunError, RenderGraphContext, SlotInfo, SlotType},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
Expand All @@ -24,6 +25,7 @@ pub struct MainPass3dNode {
&'static Camera3d,
&'static ViewTarget,
&'static ViewDepthTexture,
Option<&'static PickingTextures>,
),
With<ExtractedView>,
>,
Expand Down Expand Up @@ -55,13 +57,21 @@ impl Node for MainPass3dNode {
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.get_input_entity(Self::IN_VIEW)?;
let (camera, opaque_phase, alpha_mask_phase, transparent_phase, camera_3d, target, depth) =
match self.query.get_manual(world, view_entity) {
Ok(query) => query,
Err(_) => {
return Ok(());
} // No window
};
let (
camera,
opaque_phase,
alpha_mask_phase,
transparent_phase,
camera_3d,
target,
depth,
picking_textures,
) = match self.query.get_manual(world, view_entity) {
Ok(query) => query,
Err(_) => {
return Ok(());
} // No window
};

// Always run opaque pass to ensure screen is cleared
{
Expand All @@ -70,20 +80,34 @@ impl Node for MainPass3dNode {
#[cfg(feature = "trace")]
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
// NOTE: The opaque pass loads the color
// buffer as well as writing to it.
color_attachments: &[Some(target.get_color_attachment(Operations {
let mut color_attachments = vec![Some(target.get_color_attachment(Operations {
load: match camera_3d.clear_color {
ClearColorConfig::Default => {
LoadOp::Clear(world.resource::<ClearColor>().0.into())
}
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
},
store: true,
}))];

if let Some(picking_textures) = picking_textures {
color_attachments.push(Some(picking_textures.get_color_attachment(Operations {
// If the wish is to not clear the screen, don't clear the picking buffer either.
// This may happen in situations such as a split screen game.
load: match camera_3d.clear_color {
ClearColorConfig::Default => {
LoadOp::Clear(world.resource::<ClearColor>().0.into())
}
ClearColorConfig::Custom(color) => LoadOp::Clear(color.into()),
ClearColorConfig::None => LoadOp::Load,
_ => LoadOp::Clear(PickingTextures::clear_color()),
},
store: true,
}))],
})));
}

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_opaque_pass_3d"),
// NOTE: The opaque pass loads the color
// buffer as well as writing to it.
color_attachments: &color_attachments,
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: The opaque main pass loads the depth buffer and possibly overwrites it
Expand All @@ -109,13 +133,19 @@ impl Node for MainPass3dNode {
#[cfg(feature = "trace")]
let _main_alpha_mask_pass_3d_span = info_span!("main_alpha_mask_pass_3d").entered();

let operations = Operations {
load: LoadOp::Load,
store: true,
};
let mut color_attachments = vec![Some(target.get_color_attachment(operations))];
if let Some(picking_textures) = picking_textures {
color_attachments.push(Some(picking_textures.get_color_attachment(operations)));
}

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_alpha_mask_pass_3d"),
// NOTE: The alpha_mask pass loads the color buffer as well as overwriting it where appropriate.
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: true,
}))],
color_attachments: &color_attachments,
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: The alpha mask pass loads the depth buffer and possibly overwrites it
Expand All @@ -140,13 +170,19 @@ impl Node for MainPass3dNode {
#[cfg(feature = "trace")]
let _main_transparent_pass_3d_span = info_span!("main_transparent_pass_3d").entered();

let operations = Operations {
load: LoadOp::Load,
store: true,
};
let mut color_attachments = vec![Some(target.get_color_attachment(operations))];
if let Some(picking_textures) = picking_textures {
color_attachments.push(Some(picking_textures.get_color_attachment(operations)));
}

let mut render_pass = render_context.begin_tracked_render_pass(RenderPassDescriptor {
label: Some("main_transparent_pass_3d"),
// NOTE: The transparent pass loads the color buffer as well as overwriting it where appropriate.
color_attachments: &[Some(target.get_color_attachment(Operations {
load: LoadOp::Load,
store: true,
}))],
color_attachments: &color_attachments,
depth_stencil_attachment: Some(RenderPassDepthStencilAttachment {
view: &depth.view,
// NOTE: For the transparent pass we load the depth buffer. There should be no
Expand Down
15 changes: 13 additions & 2 deletions crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ pub mod graph {
pub const BLOOM: &str = "bloom";
pub const TONEMAPPING: &str = "tonemapping";
pub const FXAA: &str = "fxaa";
pub const PICKING: &str = "picking";
pub const UPSCALING: &str = "upscaling";
pub const END_MAIN_PASS_POST_PROCESSING: &str = "end_main_pass_post_processing";
}
Expand Down Expand Up @@ -43,7 +44,7 @@ use bevy_render::{
};
use bevy_utils::{FloatOrd, HashMap};

use crate::{tonemapping::TonemappingNode, upscaling::UpscalingNode};
use crate::{picking::node::PickingNode, tonemapping::TonemappingNode, upscaling::UpscalingNode};

pub struct Core3dPlugin;

Expand Down Expand Up @@ -71,13 +72,16 @@ impl Plugin for Core3dPlugin {
let pass_node_3d = MainPass3dNode::new(&mut render_app.world);
let tonemapping = TonemappingNode::new(&mut render_app.world);
let upscaling = UpscalingNode::new(&mut render_app.world);
let picking = PickingNode::new(&mut render_app.world);

let mut graph = render_app.world.resource_mut::<RenderGraph>();

let mut draw_3d_graph = RenderGraph::default();
draw_3d_graph.add_node(graph::node::MAIN_PASS, pass_node_3d);
draw_3d_graph.add_node(graph::node::TONEMAPPING, tonemapping);
draw_3d_graph.add_node(graph::node::END_MAIN_PASS_POST_PROCESSING, EmptyNode);
draw_3d_graph.add_node(graph::node::UPSCALING, upscaling);
draw_3d_graph.add_node(graph::node::PICKING, picking);
let input_node_id = draw_3d_graph.set_input(vec![SlotInfo::new(
graph::input::VIEW_ENTITY,
SlotType::Entity,
Expand All @@ -100,6 +104,12 @@ impl Plugin for Core3dPlugin {
graph::node::UPSCALING,
UpscalingNode::IN_VIEW,
);
draw_3d_graph.add_slot_edge(
input_node_id,
graph::input::VIEW_ENTITY,
graph::node::PICKING,
PickingNode::IN_VIEW,
);
draw_3d_graph.add_node_edge(graph::node::MAIN_PASS, graph::node::TONEMAPPING);
draw_3d_graph.add_node_edge(
graph::node::TONEMAPPING,
Expand All @@ -109,6 +119,7 @@ impl Plugin for Core3dPlugin {
graph::node::END_MAIN_PASS_POST_PROCESSING,
graph::node::UPSCALING,
);
draw_3d_graph.add_node_edge(graph::node::UPSCALING, graph::node::PICKING);
graph.add_sub_graph(graph::NAME, draw_3d_graph);
}
}
Expand Down Expand Up @@ -281,7 +292,7 @@ pub fn prepare_core_3d_depth_textures(
dimension: TextureDimension::D2,
format: TextureFormat::Depth32Float, /* PERF: vulkan docs recommend using 24
* bit depth for better performance */
usage: TextureUsages::RENDER_ATTACHMENT,
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
},
)
})
Expand Down
12 changes: 11 additions & 1 deletion crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ pub mod core_2d;
pub mod core_3d;
pub mod fullscreen_vertex_shader;
pub mod fxaa;
pub mod picking;
pub mod tonemapping;
pub mod upscaling;

Expand All @@ -23,6 +24,7 @@ use crate::{
core_3d::Core3dPlugin,
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
fxaa::FxaaPlugin,
picking::{PickingPlugin, PICKING_SHADER_HANDLE},
tonemapping::TonemappingPlugin,
upscaling::UpscalingPlugin,
};
Expand All @@ -42,6 +44,13 @@ impl Plugin for CorePipelinePlugin {
Shader::from_wgsl
);

load_internal_asset!(
app,
PICKING_SHADER_HANDLE,
"picking/picking.wgsl",
Shader::from_wgsl
);

app.register_type::<ClearColor>()
.register_type::<ClearColorConfig>()
.init_resource::<ClearColor>()
Expand All @@ -51,6 +60,7 @@ impl Plugin for CorePipelinePlugin {
.add_plugin(TonemappingPlugin)
.add_plugin(UpscalingPlugin)
.add_plugin(BloomPlugin)
.add_plugin(FxaaPlugin);
.add_plugin(FxaaPlugin)
.add_plugin(PickingPlugin);
}
}
Loading