Skip to content

Commit

Permalink
wip
Browse files Browse the repository at this point in the history
  • Loading branch information
IceSentry committed Jun 13, 2023
1 parent 019432a commit 0fb1a46
Show file tree
Hide file tree
Showing 16 changed files with 822 additions and 27 deletions.
12 changes: 12 additions & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,8 @@ bevy_gltf = ["bevy_internal/bevy_gltf", "bevy_asset", "bevy_scene", "bevy_pbr"]
# Adds PBR rendering
bevy_pbr = ["bevy_internal/bevy_pbr", "bevy_asset", "bevy_render", "bevy_core_pipeline"]

gpu_picking = []

# Provides rendering functionality
bevy_render = ["bevy_internal/bevy_render"]

Expand Down Expand Up @@ -1294,6 +1296,16 @@ description = "Shows how to rumble a gamepad using force feedback"
category = "Input"
wasm = false

[[example]]
name = "gpu_picking"
path = "examples/input/gpu_picking.rs"

[package.metadata.example.gpu_picking]
name = "GPU picking"
description = "Mouse picking using the gpu"
category = "Input"
wasm = true

[[example]]
name = "keyboard_input"
path = "examples/input/keyboard_input.rs"
Expand Down
29 changes: 29 additions & 0 deletions assets/shaders/gpu_picking_material.wgsl
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
// This shader shows how to enable the gpu picking feature for a material

// You'll need the mesh binding because that's where the entity index is
#import bevy_pbr::mesh_bindings

@group(1) @binding(0)
var<uniform> color: vec4<f32>;

// Gpu picking uses multiple fragment output
struct FragmentOutput {
@location(0) color: vec4<f32>,
// You can detect the feature with this flag
#ifdef GPU_PICKING
@location(1) entity: vec2<u32>,
#endif
};

@fragment
fn fragment(
#import bevy_pbr::mesh_vertex_output
) -> FragmentOutput {
var out: FragmentOutput;
out.color = color;
// make sure to output the entity index for gpu picking to work correctly
#ifdef GPU_PICKING
out.entity = mesh.entity;
#endif
return out;
}
38 changes: 28 additions & 10 deletions crates/bevy_core_pipeline/src/core_3d/main_opaque_pass_3d_node.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use crate::{
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
picking::{EntityTextures, ExtractedGpuPickingCamera},
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{
Expand Down Expand Up @@ -34,8 +35,10 @@ impl ViewNode for MainOpaquePass3dNode {
Option<&'static DepthPrepass>,
Option<&'static NormalPrepass>,
Option<&'static MotionVectorPrepass>,
Option<&'static ExtractedGpuPickingCamera>,
Option<&'static SkyboxPipelineId>,
Option<&'static SkyboxBindGroup>,
Option<&'static EntityTextures>,
&'static ViewUniformOffset,
);

Expand All @@ -53,8 +56,10 @@ impl ViewNode for MainOpaquePass3dNode {
depth_prepass,
normal_prepass,
motion_vector_prepass,
gpu_picking_camera,
skybox_pipeline,
skybox_bind_group,
entity_index_textures,
view_uniform_offset,
): QueryItem<Self::ViewQuery>,
world: &World,
Expand All @@ -64,21 +69,34 @@ impl ViewNode for MainOpaquePass3dNode {
#[cfg(feature = "trace")]
let _main_opaque_pass_3d_span = info_span!("main_opaque_pass_3d").entered();

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 gpu_picking_camera.is_some() {
if let Some(picking_textures) = entity_index_textures {
color_attachments.push(Some(picking_textures.get_color_attachment(Operations {
load: match camera_3d.clear_color {
ClearColorConfig::None => LoadOp::Load,
// TODO clear this earlier?
_ => LoadOp::Clear(EntityTextures::no_entity_color()),
},
store: true,
})));
}
}

// Setup render pass
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 {
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,
}))],
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 Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use crate::core_3d::Transparent3d;
use bevy_ecs::{prelude::*, query::QueryItem};
use bevy_render::{
camera::ExtractedCamera,
picking::{EntityTextures, ExtractedGpuPickingCamera},
render_graph::{NodeRunError, RenderGraphContext, ViewNode},
render_phase::RenderPhase,
render_resource::{LoadOp, Operations, RenderPassDepthStencilAttachment, RenderPassDescriptor},
Expand All @@ -21,12 +22,14 @@ impl ViewNode for MainTransparentPass3dNode {
&'static RenderPhase<Transparent3d>,
&'static ViewTarget,
&'static ViewDepthTexture,
Option<&'static ExtractedGpuPickingCamera>,
Option<&'static EntityTextures>,
);
fn run(
&self,
graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(camera, transparent_phase, target, depth): QueryItem<Self::ViewQuery>,
(camera, transparent_phase, target, depth, gpu_picking_camera, entity_index_textures): QueryItem<Self::ViewQuery>,
world: &World,
) -> Result<(), NodeRunError> {
let view_entity = graph.view_entity();
Expand All @@ -37,13 +40,27 @@ impl ViewNode for MainTransparentPass3dNode {
#[cfg(feature = "trace")]
let _main_transparent_pass_3d_span = info_span!("main_transparent_pass_3d").entered();

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

if gpu_picking_camera.is_some() {
if let Some(entity_index_textures) = entity_index_textures {
color_attachments.push(Some(entity_index_textures.get_color_attachment(
Operations {
// The texture is already cleared in the opaque pass
load: LoadOp::Load,
store: true,
},
)));
}
}

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
65 changes: 64 additions & 1 deletion crates/bevy_core_pipeline/src/core_3d/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ pub mod graph {
pub const MAIN_OPAQUE_PASS: &str = "main_opaque_pass";
pub const MAIN_TRANSPARENT_PASS: &str = "main_transparent_pass";
pub const END_MAIN_PASS: &str = "end_main_pass";
pub const ENTITY_INDEX_BUFFER_COPY: &str = "entity_index_buffer_copy";
pub const BLOOM: &str = "bloom";
pub const TONEMAPPING: &str = "tonemapping";
pub const FXAA: &str = "fxaa";
Expand All @@ -35,6 +36,7 @@ use bevy_ecs::prelude::*;
use bevy_render::{
camera::{Camera, ExtractedCamera},
extract_component::ExtractComponentPlugin,
picking::{EntityTextures, ExtractedGpuPickingCamera, ENTITY_TEXTURE_FORMAT},
prelude::Msaa,
render_graph::{EmptyNode, RenderGraphApp, ViewNodeRunner},
render_phase::{
Expand Down Expand Up @@ -91,7 +93,8 @@ impl Plugin for Core3dPlugin {
prepare_core_3d_depth_textures
.in_set(RenderSet::Prepare)
.after(bevy_render::view::prepare_windows),
prepare_prepass_textures
// #[cfg(feature = "gpu_picking")]
prepare_entity_textures
.in_set(RenderSet::Prepare)
.after(bevy_render::view::prepare_windows),
sort_phase_system::<Opaque3d>.in_set(RenderSet::PhaseSort),
Expand Down Expand Up @@ -480,3 +483,63 @@ pub fn prepare_prepass_textures(
});
}
}

/// Create the required buffers based on the camera size
pub fn prepare_entity_textures(
mut commands: Commands,
mut texture_cache: ResMut<TextureCache>,
msaa: Res<Msaa>,
render_device: Res<RenderDevice>,
views_3d: Query<
(Entity, &ExtractedCamera, Option<&ExtractedGpuPickingCamera>),
(With<RenderPhase<Opaque3d>>, With<RenderPhase<AlphaMask3d>>),
>,
) {
for (entity, camera, gpu_picking_camera) in &views_3d {
if gpu_picking_camera.is_none() {
continue;
}

let Some(physical_target_size) = camera.physical_target_size else {
continue;
};

let size = Extent3d {
depth_or_array_layers: 1,
width: physical_target_size.x,
height: physical_target_size.y,
};

let descriptor = TextureDescriptor {
label: None,
size,
mip_level_count: 1,
sample_count: 1,
dimension: TextureDimension::D2,
format: ENTITY_TEXTURE_FORMAT,
usage: TextureUsages::RENDER_ATTACHMENT | TextureUsages::COPY_SRC,
view_formats: &[],
};

let entity_textures = EntityTextures {
main: texture_cache.get(
&render_device,
TextureDescriptor {
label: Some("main_entity_texture"),
..descriptor
},
),
sampled: (msaa.samples() > 1).then(|| {
texture_cache.get(
&render_device,
TextureDescriptor {
label: Some("main_entity_texture_sampled"),
sample_count: msaa.samples(),
..descriptor
},
)
}),
};
commands.entity(entity).insert(entity_textures);
}
}
51 changes: 51 additions & 0 deletions crates/bevy_core_pipeline/src/entity_index_buffer_copy/mod.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
use bevy_app::Plugin;
use bevy_ecs::{query::QueryItem, world::World};
use bevy_render::{
picking::{EntityTextures, ExtractedGpuPickingCamera},
render_graph::{RenderGraphApp, RenderGraphContext, ViewNode, ViewNodeRunner},
renderer::RenderContext,
RenderApp,
};

use crate::core_3d::CORE_3D;

#[derive(Default)]
pub struct EntityIndexBufferCopyNode;
impl ViewNode for EntityIndexBufferCopyNode {
type ViewQuery = (&'static EntityTextures, &'static ExtractedGpuPickingCamera);

fn run(
&self,
_graph: &mut RenderGraphContext,
render_context: &mut RenderContext,
(entity_index_textures, gpu_picking_camera): QueryItem<Self::ViewQuery>,
_world: &World,
) -> Result<(), bevy_render::render_graph::NodeRunError> {
let Some(buffers) = gpu_picking_camera.buffers.as_ref() else {
return Ok(());
};

buffers.copy_texture_to_buffer(
render_context.command_encoder(),
&entity_index_textures.main.texture,
);

Ok(())
}
}

pub struct EntityIndexBufferCopyPlugin;
impl Plugin for EntityIndexBufferCopyPlugin {
fn build(&self, app: &mut bevy_app::App) {
let Ok(render_app) = app.get_sub_app_mut(RenderApp) else { return; };

// 3D
use crate::core_3d::graph::node::*;
render_app
.add_render_graph_node::<ViewNodeRunner<EntityIndexBufferCopyNode>>(
CORE_3D,
ENTITY_INDEX_BUFFER_COPY,
)
.add_render_graph_edge(CORE_3D, UPSCALING, ENTITY_INDEX_BUFFER_COPY);
}
}
5 changes: 4 additions & 1 deletion crates/bevy_core_pipeline/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ pub mod clear_color;
pub mod contrast_adaptive_sharpening;
pub mod core_2d;
pub mod core_3d;
pub mod entity_index_buffer_copy;
pub mod fullscreen_vertex_shader;
pub mod fxaa;
pub mod msaa_writeback;
Expand Down Expand Up @@ -40,6 +41,7 @@ use crate::{
contrast_adaptive_sharpening::CASPlugin,
core_2d::Core2dPlugin,
core_3d::Core3dPlugin,
entity_index_buffer_copy::EntityIndexBufferCopyPlugin,
fullscreen_vertex_shader::FULLSCREEN_SHADER_HANDLE,
fxaa::FxaaPlugin,
msaa_writeback::MsaaWritebackPlugin,
Expand Down Expand Up @@ -77,6 +79,7 @@ impl Plugin for CorePipelinePlugin {
.add_plugin(UpscalingPlugin)
.add_plugin(BloomPlugin)
.add_plugin(FxaaPlugin)
.add_plugin(CASPlugin);
.add_plugin(CASPlugin)
.add_plugin(EntityIndexBufferCopyPlugin);
}
}
1 change: 1 addition & 0 deletions crates/bevy_pbr/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ keywords = ["bevy"]

[features]
webgl = []
gpu_picking = []

[dependencies]
# bevy
Expand Down
Loading

0 comments on commit 0fb1a46

Please sign in to comment.