Skip to content

Commit

Permalink
Implement outlines for line renderer & use them for select & hover of…
Browse files Browse the repository at this point in the history
… "line-like" primitives in Viewer (#1553)

* line batches support for outline masks
* Allow outlines for individual line strip ranges
* tweak selection outline style a bit
* change outline depth test for overwritability by same depth value
* rename outline_mask to outline_mask_ids
  • Loading branch information
Wumpf authored Mar 10, 2023
1 parent 4dbc2a1 commit 3813dd1
Show file tree
Hide file tree
Showing 28 changed files with 453 additions and 213 deletions.
1 change: 1 addition & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,7 @@ egui_dock = "0.4"
egui_extras = "0.21.0"
egui-wgpu = "0.21.0"
emath = "0.21.0"
enumset = "1.0.12"
epaint = "0.21.0"
glam = "0.22"
gltf = "1.1"
Expand Down
1 change: 1 addition & 0 deletions crates/re_renderer/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,7 @@ bytemuck = { version = "1.12", features = ["derive"] }
clean-path = "0.2"
document-features = "0.2"
ecolor = { workspace = true, features = ["bytemuck"] }
enumset.workspace = true
glam = { workspace = true, features = ["bytemuck"] }
half = { workspace = true, features = ["bytemuck"] }
itertools = "0.10"
Expand Down
10 changes: 5 additions & 5 deletions crates/re_renderer/examples/outlines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ struct Outlines {
}

struct MeshProperties {
outline_mask: OutlineMaskPreference,
outline_mask_ids: OutlineMaskPreference,
position: glam::Vec3,
rotation: glam::Quat,
}
Expand Down Expand Up @@ -89,17 +89,17 @@ impl framework::Example for Outlines {

let mesh_properties = [
MeshProperties {
outline_mask: outline_mask_large_mesh,
outline_mask_ids: outline_mask_large_mesh,
position: glam::Vec3::ZERO,
rotation: glam::Quat::IDENTITY,
},
MeshProperties {
outline_mask: OutlineMaskPreference::some(1, 0),
outline_mask_ids: OutlineMaskPreference::some(1, 0),
position: glam::vec3(2.0, 0.0, -3.0),
rotation: glam::Quat::from_rotation_y(seconds_since_startup),
},
MeshProperties {
outline_mask: OutlineMaskPreference::some(0, 1),
outline_mask_ids: OutlineMaskPreference::some(0, 1),
position: glam::vec3(-2.0, 1.0, 3.0),
rotation: glam::Quat::from_rotation_x(seconds_since_startup),
},
Expand All @@ -117,7 +117,7 @@ impl framework::Example for Outlines {
props.rotation,
props.position,
) * instance.world_from_mesh,
outline_mask: props.outline_mask,
outline_mask_ids: props.outline_mask_ids,
..Default::default()
})
})
Expand Down
6 changes: 3 additions & 3 deletions crates/re_renderer/shader/instanced_mesh.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ struct VertexOut {
@location(2) additive_tint_rgb: Vec3,

@location(3) @interpolate(flat)
outline_mask: UVec2,
outline_mask_ids: UVec2,
};

@vertex
Expand All @@ -41,7 +41,7 @@ fn vs_main(in_vertex: VertexIn, in_instance: InstanceIn) -> VertexOut {
out.texcoord = in_vertex.texcoord;
out.normal_world_space = world_normal;
out.additive_tint_rgb = linear_from_srgb(in_instance.additive_tint_srgb.rgb);
out.outline_mask = in_instance.outline_mask;
out.outline_mask_ids = in_instance.outline_mask_ids;

return out;
}
Expand All @@ -63,5 +63,5 @@ fn fs_main_shaded(in: VertexOut) -> @location(0) Vec4 {

@fragment
fn fs_main_outline_mask(in: VertexOut) -> @location(0) UVec2 {
return in.outline_mask;
return in.outline_mask_ids;
}
29 changes: 23 additions & 6 deletions crates/re_renderer/shader/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ var position_data_texture: texture_2d<u32>;

struct BatchUniformBuffer {
world_from_obj: Mat4,
outline_mask_ids: UVec2,
};
@group(2) @binding(0)
var<uniform> batch: BatchUniformBuffer;
Expand Down Expand Up @@ -222,9 +223,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
return out;
}

@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {

fn compute_coverage(in: VertexOut) -> f32{
var coverage = 1.0;
if has_any_flag(in.currently_active_flags, CAP_START_ROUND | CAP_END_ROUND) {
let distance_to_skeleton = length(in.position_world - in.closest_strip_position);
Expand All @@ -234,11 +233,17 @@ fn fs_main(in: VertexOut) -> @location(0) Vec4 {
// If we do only outwards, rectangle outlines won't line up nicely
let half_pixel_world_size = pixel_world_size * 0.5;
let signed_distance_to_border = distance_to_skeleton - in.active_radius;
if signed_distance_to_border > half_pixel_world_size {
discard;
}
coverage = 1.0 - saturate((signed_distance_to_border + half_pixel_world_size) / pixel_world_size);
}
return coverage;
}

@fragment
fn fs_main(in: VertexOut) -> @location(0) Vec4 {
var coverage = compute_coverage(in);
if coverage < 0.00001 {
discard;
}

// TODO(andreas): lighting setup
var shading = 1.0;
Expand All @@ -250,3 +255,15 @@ fn fs_main(in: VertexOut) -> @location(0) Vec4 {

return Vec4(in.color.rgb * shading, coverage);
}

@fragment
fn fs_main_outline_mask(in: VertexOut) -> @location(0) UVec2 {
// Output is an integer target, can't use coverage therefore.
// But we still want to discard fragments where coverage is low.
// Since the outline extends a bit, a very low cut off tends to look better.
var coverage = compute_coverage(in);
if coverage < 1.0 {
discard;
}
return batch.outline_mask_ids;
}
2 changes: 1 addition & 1 deletion crates/re_renderer/shader/mesh_vertex.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,5 @@ struct InstanceIn {
@location(7) world_from_mesh_normal_row_1: Vec3,
@location(8) world_from_mesh_normal_row_2: Vec3,
@location(9) additive_tint_srgb: Vec4,
@location(10) outline_mask: UVec2,
@location(10) outline_mask_ids: UVec2,
};
4 changes: 4 additions & 0 deletions crates/re_renderer/src/allocator/uniform_buffer_fill.rs
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,10 @@ pub fn create_and_fill_uniform_buffer_batch<T: bytemuck::Pod>(
#[allow(clippy::let_unit_value)]
let _ = UniformBufferAlignmentCheck::<T>::CHECK;

if content.len() == 0 {
return vec![];
}

let num_buffers = content.len() as u64;
let element_size = std::mem::size_of::<T>() as u64;

Expand Down
87 changes: 68 additions & 19 deletions crates/re_renderer/src/line_strip_builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,10 @@
use std::ops::Range;

use crate::{
renderer::{LineBatchInfo, LineDrawData, LineStripFlags, LineStripInfo, LineVertex},
renderer::{
LineBatchInfo, LineDrawData, LineStripFlags, LineStripInfo, LineVertex,
OutlineMaskPreference,
},
Color32, DebugLabel, Size,
};

Expand Down Expand Up @@ -33,6 +38,8 @@ where
label: label.into(),
world_from_obj: glam::Mat4::IDENTITY,
line_vertex_count: 0,
overall_outline_mask_ids: OutlineMaskPreference::NONE,
additional_outline_mask_ids_vertex_ranges: Vec::new(),
});

LineBatchBuilder(self)
Expand Down Expand Up @@ -135,23 +142,35 @@ where
self
}

/// Sets an outline mask for every element in the batch.
#[inline]
pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
self.batch_mut().overall_outline_mask_ids = outline_mask_ids;
self
}

/// Adds a 3D series of line connected points.
pub fn add_strip(
&mut self,
points: impl Iterator<Item = glam::Vec3>,
) -> LineStripBuilder<'_, PerStripUserData> {
let old_len = self.0.strips.len();
let strip_index = old_len as _;
let old_strip_count = self.0.strips.len();
let old_vertex_count = self.0.vertices.len();
let strip_index = old_strip_count as _;

self.add_vertices(points, strip_index);
let new_vertex_count = self.0.vertices.len();

debug_assert_eq!(self.0.strips.len(), self.0.strip_user_data.len());
self.0.strips.push(LineStripInfo::default());
self.0.strip_user_data.push(PerStripUserData::default());
let new_strip_count = self.0.strips.len();

LineStripBuilder {
strips: &mut self.0.strips[old_len..],
user_data: &mut self.0.strip_user_data[old_len..],
builder: self.0,
outline_mask_ids: OutlineMaskPreference::NONE,
vertex_range: old_vertex_count..new_vertex_count,
strip_range: old_strip_count..new_strip_count,
}
}

Expand All @@ -170,18 +189,19 @@ where
&mut self,
segments: impl Iterator<Item = (glam::Vec3, glam::Vec3)>,
) -> LineStripBuilder<'_, PerStripUserData> {
let mut num_strips = self.0.strips.len() as u32;
let old_strip_count = self.0.strips.len();
let old_vertex_count = self.0.vertices.len();
let mut strip_index = old_strip_count as u32;

// It's tempting to assign the same strip to all vertices, after all they share
// color/radius/tag properties.
// However, if we don't assign different strip indices, we don't know when a strip (==segment) starts and ends.
for (a, b) in segments {
self.add_vertices([a, b].into_iter(), num_strips);
num_strips += 1;
self.add_vertices([a, b].into_iter(), strip_index);
strip_index += 1;
}

let old_len = self.0.strips.len();
let num_strips_added = num_strips as usize - old_len;
let new_vertex_count = self.0.vertices.len();
let num_strips_added = strip_index as usize - old_strip_count;

debug_assert_eq!(self.0.strips.len(), self.0.strip_user_data.len());
self.0
Expand All @@ -190,10 +210,13 @@ where
self.0
.strip_user_data
.extend(std::iter::repeat(PerStripUserData::default()).take(num_strips_added));
let new_strip_count = self.0.strips.len();

LineStripBuilder {
strips: &mut self.0.strips[old_len..],
user_data: &mut self.0.strip_user_data[old_len..],
builder: self.0,
outline_mask_ids: OutlineMaskPreference::NONE,
vertex_range: old_vertex_count..new_vertex_count,
strip_range: old_strip_count..new_strip_count,
}
}

Expand Down Expand Up @@ -345,8 +368,10 @@ where
}

pub struct LineStripBuilder<'a, PerStripUserData> {
strips: &'a mut [LineStripInfo],
user_data: &'a mut [PerStripUserData],
builder: &'a mut LineStripSeriesBuilder<PerStripUserData>,
outline_mask_ids: OutlineMaskPreference,
vertex_range: Range<usize>,
strip_range: Range<usize>,
}

impl<'a, PerStripUserData> LineStripBuilder<'a, PerStripUserData>
Expand All @@ -355,36 +380,60 @@ where
{
#[inline]
pub fn radius(self, radius: Size) -> Self {
for strip in self.strips.iter_mut() {
for strip in self.builder.strips[self.strip_range.clone()].iter_mut() {
strip.radius = radius;
}
self
}

#[inline]
pub fn color(self, color: Color32) -> Self {
for strip in self.strips.iter_mut() {
for strip in self.builder.strips[self.strip_range.clone()].iter_mut() {
strip.color = color;
}
self
}

#[inline]
pub fn flags(self, flags: LineStripFlags) -> Self {
for strip in self.strips.iter_mut() {
for strip in self.builder.strips[self.strip_range.clone()].iter_mut() {
strip.flags = flags;
}
self
}

/// Sets an individual outline mask ids.
/// Note that this has a relatively high performance impact.
#[inline]
pub fn outline_mask_ids(mut self, outline_mask_ids: OutlineMaskPreference) -> Self {
self.outline_mask_ids = outline_mask_ids;
self
}

/// Adds user data for every strip this builder adds.
///
/// User data is currently not available on the GPU.
#[inline]
pub fn user_data(self, user_data: PerStripUserData) -> Self {
for d in self.user_data.iter_mut() {
for d in self.builder.strip_user_data[self.strip_range.clone()].iter_mut() {
*d = user_data.clone();
}
self
}
}

impl<'a, PerStripUserData> Drop for LineStripBuilder<'a, PerStripUserData> {
fn drop(&mut self) {
if self.outline_mask_ids.is_some() {
self.builder
.batches
.last_mut()
.unwrap()
.additional_outline_mask_ids_vertex_ranges
.push((
self.vertex_range.start as u32..self.vertex_range.end as u32,
self.outline_mask_ids,
));
}
}
}
Loading

1 comment on commit 3813dd1

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Rust Benchmark

Benchmark suite Current: 3813dd1 Previous: 4dbc2a1 Ratio
datastore/insert/batch/rects/insert 551073 ns/iter (± 3244) 573243 ns/iter (± 4992) 0.96
datastore/latest_at/batch/rects/query 1842 ns/iter (± 18) 1841 ns/iter (± 4) 1.00
datastore/latest_at/missing_components/primary 292 ns/iter (± 1) 289 ns/iter (± 5) 1.01
datastore/latest_at/missing_components/secondaries 444 ns/iter (± 4) 433 ns/iter (± 0) 1.03
datastore/range/batch/rects/query 148252 ns/iter (± 2018) 149846 ns/iter (± 265) 0.99
mono_points_arrow/generate_message_bundles 46262473 ns/iter (± 1320946) 49726116 ns/iter (± 931950) 0.93
mono_points_arrow/generate_messages 136482761 ns/iter (± 1522532) 130928868 ns/iter (± 1289090) 1.04
mono_points_arrow/encode_log_msg 164230560 ns/iter (± 1004696) 157823906 ns/iter (± 964768) 1.04
mono_points_arrow/encode_total 350475852 ns/iter (± 3089733) 340345147 ns/iter (± 6536630) 1.03
mono_points_arrow/decode_log_msg 184425548 ns/iter (± 1510726) 182949311 ns/iter (± 983708) 1.01
mono_points_arrow/decode_message_bundles 73548126 ns/iter (± 1342999) 68698118 ns/iter (± 1038974) 1.07
mono_points_arrow/decode_total 256776275 ns/iter (± 1923041) 249971039 ns/iter (± 1454610) 1.03
batch_points_arrow/generate_message_bundles 327515 ns/iter (± 2845) 341096 ns/iter (± 563) 0.96
batch_points_arrow/generate_messages 6179 ns/iter (± 79) 6299 ns/iter (± 15) 0.98
batch_points_arrow/encode_log_msg 367982 ns/iter (± 2502) 370650 ns/iter (± 1125) 0.99
batch_points_arrow/encode_total 717546 ns/iter (± 6153) 726231 ns/iter (± 2200) 0.99
batch_points_arrow/decode_log_msg 348140 ns/iter (± 1616) 348908 ns/iter (± 1342) 1.00
batch_points_arrow/decode_message_bundles 2051 ns/iter (± 26) 2083 ns/iter (± 6) 0.98
batch_points_arrow/decode_total 352845 ns/iter (± 2129) 355501 ns/iter (± 1212) 0.99
arrow_mono_points/insert 7014660696 ns/iter (± 24039123) 6543177068 ns/iter (± 39845980) 1.07
arrow_mono_points/query 1750550 ns/iter (± 14688) 1786556 ns/iter (± 17868) 0.98
arrow_batch_points/insert 2637840 ns/iter (± 18830) 2658024 ns/iter (± 19047) 0.99
arrow_batch_points/query 16782 ns/iter (± 117) 16855 ns/iter (± 40) 1.00
arrow_batch_vecs/insert 41488 ns/iter (± 278) 42329 ns/iter (± 107) 0.98
arrow_batch_vecs/query 500231 ns/iter (± 5039) 506446 ns/iter (± 1133) 0.99
tuid/Tuid::random 34 ns/iter (± 0) 34 ns/iter (± 0) 1

This comment was automatically generated by workflow using github-action-benchmark.

Please sign in to comment.