-
Notifications
You must be signed in to change notification settings - Fork 373
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 based picking with points #1721
Merged
Merged
Changes from all commits
Commits
Show all changes
35 commits
Select commit
Hold shift + click to select a range
e03f027
move outline processor to draw phases, started implementation of picking
Wumpf c52f20d
Add picking layer draw phase
Wumpf 4e99981
debug overlay works now
Wumpf 6b939be
rendering to picking layer works (without projection adjustment)
Wumpf 7f037aa
picking layer pass has its own frame uniform buffer and corrects proj…
Wumpf fe1c13f
fix warnings, add picking data vec to picking sample
Wumpf ae2efe0
doc string corrections, todo notes
Wumpf bb78b44
picking rect in picking sample follows mouse now
Wumpf 203d87c
webgl fix
Wumpf 87ea6fa
newline lint
Wumpf 68b786d
Warning fix
Wumpf 1004fc0
doc string fixes
Wumpf f39a8c2
debug overlay now supports both showing uint & float textures
Wumpf 956f4d9
picking layer is now rgba_u32
Wumpf 6743985
picking layer object ids for point clouds
Wumpf f20f1c3
picking instance id for point clouds
Wumpf 7091673
demonstrate point cloud picking in picking example, more utilities
Wumpf 0cae682
warning fixes, doc addition
Wumpf 3051ce8
typo fix
Wumpf 9efc76c
fix web by being more lenient on alignment
Wumpf 27e4ea0
use hashmap for picking data in picking sample
Wumpf 5a2a1bc
make picking demo code less insane by having two distinct point sets
Wumpf 256c2a3
fix info log string
Wumpf 546c8bd
improve and document debug overlay shader
Wumpf 6f93fff
doc strings for picking layer
Wumpf 6105541
spelling
Wumpf e82766e
Merge remote-tracking branch 'origin/main' into andreas/re_renderer/g…
Wumpf 39263c9
use new remove_padding in viewport.rs
Wumpf 6f98efb
TextureRowDataInfo has now a new method
Wumpf d237a26
`remove_padding` uses a Cow now
Wumpf c0601f9
more docstrings!
Wumpf e09dea2
introduce IntRect in renderer
Wumpf 969eadb
provide proper uint fallback texture for debug overlay
Wumpf 58b3c21
picking accuracy improvements
Wumpf 9ff255d
fixup enum names
Wumpf File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,198 @@ | ||
use ahash::HashMap; | ||
use itertools::Itertools as _; | ||
use rand::Rng; | ||
use re_renderer::{ | ||
view_builder::{Projection, TargetConfiguration, ViewBuilder}, | ||
Color32, GpuReadbackBufferIdentifier, IntRect, PickingLayerId, PickingLayerInstanceId, | ||
PointCloudBuilder, RenderContext, ScheduledPickingRect, Size, | ||
}; | ||
|
||
mod framework; | ||
|
||
struct PointSet { | ||
positions: Vec<glam::Vec3>, | ||
radii: Vec<Size>, | ||
colors: Vec<Color32>, | ||
picking_ids: Vec<PickingLayerInstanceId>, | ||
} | ||
|
||
struct Picking { | ||
point_sets: Vec<PointSet>, | ||
scheduled_picking_rects: HashMap<GpuReadbackBufferIdentifier, ScheduledPickingRect>, | ||
picking_position: glam::UVec2, | ||
} | ||
|
||
fn random_color(rnd: &mut impl rand::Rng) -> Color32 { | ||
ecolor::Hsva { | ||
h: rnd.gen::<f32>(), | ||
s: rnd.gen::<f32>() * 0.5 + 0.5, | ||
v: rnd.gen::<f32>() * 0.5 + 0.5, | ||
a: 1.0, | ||
} | ||
.into() | ||
} | ||
|
||
impl Picking { | ||
#[allow(clippy::unused_self)] | ||
fn handle_incoming_picking_data(&mut self, re_ctx: &mut RenderContext, _time: f32) { | ||
re_ctx | ||
.gpu_readback_belt | ||
.lock() | ||
.receive_data(|data, identifier| { | ||
if let Some(picking_rect_info) = self.scheduled_picking_rects.remove(&identifier) { | ||
// TODO(andreas): Move this into a utility function? | ||
let picking_data_without_padding = | ||
picking_rect_info.row_info.remove_padding(data); | ||
let picking_data: &[PickingLayerId] = | ||
bytemuck::cast_slice(&picking_data_without_padding); | ||
|
||
// Grab the middle pixel. usually we'd want to do something clever that snaps the the closest object of interest. | ||
let picked_pixel = picking_data[(picking_rect_info.rect.extent.x / 2 | ||
+ (picking_rect_info.rect.extent.y / 2) * picking_rect_info.rect.extent.x) | ||
as usize]; | ||
|
||
if picked_pixel.object.0 != 0 { | ||
let point_set = &mut self.point_sets[picked_pixel.object.0 as usize - 1]; | ||
point_set.radii[picked_pixel.instance.0 as usize] = Size::new_scene(0.1); | ||
point_set.colors[picked_pixel.instance.0 as usize] = Color32::DEBUG_COLOR; | ||
} | ||
} else { | ||
re_log::error!("Received picking data for unknown identifier"); | ||
} | ||
}); | ||
} | ||
} | ||
|
||
impl framework::Example for Picking { | ||
fn title() -> &'static str { | ||
"Picking" | ||
} | ||
|
||
fn on_cursor_moved(&mut self, position_in_pixel: glam::UVec2) { | ||
self.picking_position = position_in_pixel; | ||
} | ||
|
||
fn new(_re_ctx: &mut re_renderer::RenderContext) -> Self { | ||
let mut rnd = <rand::rngs::StdRng as rand::SeedableRng>::seed_from_u64(42); | ||
let random_point_range = -5.0_f32..5.0_f32; | ||
let point_count = 10000; | ||
|
||
// Split point cloud into several batches to test picking of multiple objects. | ||
let point_sets = (0..2) | ||
.map(|_| PointSet { | ||
positions: (0..point_count) | ||
.map(|_| { | ||
glam::vec3( | ||
rnd.gen_range(random_point_range.clone()), | ||
rnd.gen_range(random_point_range.clone()), | ||
rnd.gen_range(random_point_range.clone()), | ||
) | ||
}) | ||
.collect_vec(), | ||
radii: std::iter::repeat(Size::new_scene(0.08)) | ||
.take(point_count) | ||
.collect_vec(), | ||
colors: (0..point_count) | ||
.map(|_| random_color(&mut rnd)) | ||
.collect_vec(), | ||
picking_ids: (0..point_count as u64) | ||
.map(PickingLayerInstanceId) | ||
.collect_vec(), | ||
}) | ||
.collect_vec(); | ||
|
||
Picking { | ||
point_sets, | ||
scheduled_picking_rects: HashMap::default(), | ||
picking_position: glam::UVec2::ZERO, | ||
} | ||
} | ||
|
||
fn draw( | ||
&mut self, | ||
re_ctx: &mut re_renderer::RenderContext, | ||
resolution: [u32; 2], | ||
time: &framework::Time, | ||
pixels_from_point: f32, | ||
) -> Vec<framework::ViewDrawResult> { | ||
self.handle_incoming_picking_data(re_ctx, time.seconds_since_startup()); | ||
|
||
let mut view_builder = ViewBuilder::default(); | ||
|
||
// TODO(#1426): unify camera logic between examples. | ||
let camera_position = glam::vec3(1.0, 3.5, 7.0); | ||
|
||
view_builder | ||
.setup_view( | ||
re_ctx, | ||
TargetConfiguration { | ||
name: "OutlinesDemo".into(), | ||
resolution_in_pixel: resolution, | ||
view_from_world: macaw::IsoTransform::look_at_rh( | ||
camera_position, | ||
glam::Vec3::ZERO, | ||
glam::Vec3::Y, | ||
) | ||
.unwrap(), | ||
projection_from_view: Projection::Perspective { | ||
vertical_fov: 70.0 * std::f32::consts::TAU / 360.0, | ||
near_plane_distance: 0.01, | ||
}, | ||
pixels_from_point, | ||
outline_config: None, | ||
..Default::default() | ||
}, | ||
) | ||
.unwrap(); | ||
|
||
// Use an uneven number of pixels for the picking rect so that there is a clearly defined middle-pixel. | ||
// (for this sample a size of 1 would be sufficient, but for a real application you'd want to use a larger size to allow snapping) | ||
let picking_rect_size = 31; | ||
|
||
let picking_rect = view_builder | ||
.schedule_picking_readback( | ||
re_ctx, | ||
IntRect::from_middle_and_extent( | ||
self.picking_position.as_ivec2(), | ||
glam::uvec2(picking_rect_size, picking_rect_size), | ||
), | ||
false, | ||
) | ||
.unwrap(); | ||
self.scheduled_picking_rects | ||
.insert(picking_rect.identifier, picking_rect); | ||
|
||
let mut builder = PointCloudBuilder::<()>::new(re_ctx); | ||
|
||
for (i, point_set) in self.point_sets.iter().enumerate() { | ||
builder | ||
.batch(format!("Random Points {i}")) | ||
.picking_object_id(re_renderer::PickingLayerObjectId(i as u64 + 1)) // offset by one since 0=default=no hit | ||
.add_points( | ||
point_set.positions.len(), | ||
point_set.positions.iter().cloned(), | ||
) | ||
.radii(point_set.radii.iter().cloned()) | ||
.colors(point_set.colors.iter().cloned()) | ||
.picking_instance_ids(point_set.picking_ids.iter().cloned()); | ||
} | ||
view_builder.queue_draw(&builder.to_draw_data(re_ctx).unwrap()); | ||
view_builder.queue_draw(&re_renderer::renderer::GenericSkyboxDrawData::new(re_ctx)); | ||
|
||
let command_buffer = view_builder | ||
.draw(re_ctx, ecolor::Rgba::TRANSPARENT) | ||
.unwrap(); | ||
|
||
vec![framework::ViewDrawResult { | ||
view_builder, | ||
command_buffer, | ||
target_location: glam::Vec2::ZERO, | ||
}] | ||
} | ||
|
||
fn on_keyboard_input(&mut self, _input: winit::event::KeyboardInput) {} | ||
} | ||
|
||
fn main() { | ||
framework::start::<Picking>(); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,68 @@ | ||
// Debug overlay shader | ||
// | ||
// Works together with `debug_overlay.rs` to display a texture on top of the screen. | ||
// It is meant to be used as last part of the compositor phase in order to present the debug output unfiltered. | ||
// It's sole purpose is for developing new rendering features and it should not be used in production! | ||
// | ||
// The fragment shader is a blueprint for handling different texture outputs. | ||
// *Do* edit it on the fly for debugging purposes! | ||
|
||
#import <./types.wgsl> | ||
#import <./global_bindings.wgsl> | ||
|
||
struct UniformBuffer { | ||
Wumpf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
screen_resolution: Vec2, | ||
position_in_pixel: Vec2, | ||
extent_in_pixel: Vec2, | ||
mode: u32, | ||
_padding: u32, | ||
}; | ||
@group(1) @binding(0) | ||
var<uniform> uniforms: UniformBuffer; | ||
|
||
@group(1) @binding(1) | ||
var debug_texture_float: texture_2d<f32>; | ||
@group(1) @binding(2) | ||
var debug_texture_uint: texture_2d<u32>; | ||
|
||
// Mode options, see `DebugOverlayMode` in `debug_overlay.rs` | ||
const ShowFloatTexture: u32 = 0u; | ||
Wumpf marked this conversation as resolved.
Show resolved
Hide resolved
|
||
const ShowUintTexture: u32 = 1u; | ||
|
||
struct VertexOutput { | ||
@builtin(position) position: Vec4, | ||
@location(0) texcoord: Vec2, | ||
}; | ||
|
||
@vertex | ||
fn main_vs(@builtin(vertex_index) vertex_index: u32) -> VertexOutput { | ||
let texcoord = Vec2(f32(vertex_index / 2u), f32(vertex_index % 2u)); | ||
|
||
// This calculation could be simplified by pre-computing things on the CPU. | ||
// But this is not the point here - we want to debug this and other things rapidly by editing the shader. | ||
let screen_fraction = texcoord * (uniforms.extent_in_pixel / uniforms.screen_resolution) + | ||
uniforms.position_in_pixel / uniforms.screen_resolution; | ||
let screen_ndc = Vec2(screen_fraction.x * 2.0 - 1.0, 1.0 - screen_fraction.y * 2.0); | ||
|
||
var out: VertexOutput; | ||
out.position = Vec4(screen_ndc, 0.0, 1.0); | ||
out.texcoord = texcoord; | ||
return out; | ||
} | ||
|
||
@fragment | ||
fn main_fs(in: VertexOutput) -> @location(0) Vec4 { | ||
if uniforms.mode == ShowFloatTexture { | ||
return Vec4(textureSample(debug_texture_float, nearest_sampler, in.texcoord).rgb, 1.0); | ||
} else if uniforms.mode == ShowUintTexture { | ||
let coords = IVec2(in.texcoord * Vec2(textureDimensions(debug_texture_uint).xy)); | ||
let raw_values = textureLoad(debug_texture_uint, coords, 0); | ||
|
||
let num_color_levels = 20u; | ||
let mapped_values = Vec4(raw_values % num_color_levels) / f32(num_color_levels - 1u); | ||
|
||
return Vec4(mapped_values.rgb, 1.0); | ||
} else { | ||
return Vec4(1.0, 0.0, 1.0, 1.0); | ||
} | ||
} |
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel already that we should have a helper layer between the user and
gpu_readback_belt
. If every users need to store the original rectangles so that they can remove the padding, then there should be a helper layer that does all that for usThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Yeah that's what I meant with the comment :)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
although, not having the user store the originals is kinda hard I think. I didn't want to do dynamic dispatching on the level of the readbackbelt, i.e. not categorizing all the possible kinds of readbacks on the re_renderer side
there's some other work I have planned to deal with the problem of conjoined buffers. I'll have a look into higher level layers then.