Skip to content

Commit

Permalink
Size boosted outlines for points & lines, color & size tweaking (#1667)
Browse files Browse the repository at this point in the history
* boost size of points in outline mask

* thinner outlines in viewer

* wip line size boost

* fix point_size_to_world called incorrectly

* outline boost for lines working now

* final touches to colors, widths etc.

* missing doc strings

* added todo note on missing outline boost

* Fix outline gaps in line strips

* comment fix and world_size_from_point_size rename
  • Loading branch information
Wumpf committed Mar 23, 2023
1 parent c42e1f1 commit 0dc563e
Show file tree
Hide file tree
Showing 15 changed files with 224 additions and 42 deletions.
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/2d.rs
Original file line number Diff line number Diff line change
Expand Up @@ -67,7 +67,7 @@ impl framework::Example for Render2D {
splits[0].resolution_in_pixel[1] as f32,
);

let mut line_strip_builder = LineStripSeriesBuilder::<()>::default();
let mut line_strip_builder = LineStripSeriesBuilder::<()>::new(re_ctx);

// Blue rect outline around the bottom right quarter.
{
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/depth_cloud.rs
Original file line number Diff line number Diff line change
Expand Up @@ -305,7 +305,7 @@ impl framework::Example for RenderDepthClouds {
let world_from_model = rotation * translation_center * scale;

let frame_draw_data = {
let mut builder = LineStripSeriesBuilder::<()>::default();
let mut builder = LineStripSeriesBuilder::<()>::new(re_ctx);
{
let mut line_batch = builder.batch("frame").world_from_obj(world_from_model);
line_batch.add_box_outline(glam::Affine3A::from_scale_rotation_translation(
Expand Down
2 changes: 1 addition & 1 deletion crates/re_renderer/examples/multiview.rs
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@ fn build_lines(re_ctx: &mut RenderContext, seconds_since_startup: f32) -> LineDr
// Calculate some points that look nice for an animated line.
let lorenz_points = lorenz_points(seconds_since_startup);

let mut builder = LineStripSeriesBuilder::<()>::default();
let mut builder = LineStripSeriesBuilder::<()>::new(re_ctx);
{
let mut batch = builder.batch("lines without transform");

Expand Down
3 changes: 2 additions & 1 deletion crates/re_renderer/shader/depth_cloud.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,8 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
let point_data = compute_point_data(quad_idx);

// Span quad
let quad = sphere_quad_span(vertex_idx, point_data.pos_in_world, point_data.unresolved_radius);
// TODO(andreas): Implement outline-mask size boost for depth cloud as well.
let quad = sphere_quad_span(vertex_idx, point_data.pos_in_world, point_data.unresolved_radius, 0.0);

var out: VertexOut;
out.pos_in_clip = frame.projection_from_world * Vec4(quad.pos_in_world, 1.0);
Expand Down
25 changes: 20 additions & 5 deletions crates/re_renderer/shader/lines.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ var line_strip_texture: texture_2d<f32>;
@group(1) @binding(1)
var position_data_texture: texture_2d<u32>;

struct DrawDataUniformBuffer {
size_boost_in_points: f32,
};
@group(1) @binding(2)
var<uniform> draw_data: DrawDataUniformBuffer;

struct BatchUniformBuffer {
world_from_obj: Mat4,
outline_mask_ids: UVec2,
Expand Down Expand Up @@ -159,11 +165,6 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
// Data valid for the entire strip that this vertex belongs to.
let strip_data = read_strip_data(pos_data_current.strip_index);

// Resolve radius.
// (slight inaccuracy: End caps are going to adjust their center_position)
let camera_ray = camera_ray_to_world_pos(center_position);
let strip_radius = unresolved_size_to_world(strip_data.unresolved_radius, length(camera_ray.origin - center_position), frame.auto_size_lines);

// Active flags are all flags that we react to at the current vertex.
// I.e. cap flags are only active in the respective cap triangle.
var currently_active_flags = strip_data.flags & (~(CAP_START_TRIANGLE | CAP_END_TRIANGLE | CAP_START_ROUND | CAP_END_ROUND));
Expand All @@ -189,6 +190,20 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
quad_dir = normalize(pos_data_quad_end.pos - pos_data_quad_begin.pos);
}

// Resolve radius.
// (slight inaccuracy: End caps are going to adjust their center_position)
let camera_ray = camera_ray_to_world_pos(center_position);
let camera_distance = distance(camera_ray.origin, center_position);
var strip_radius = unresolved_size_to_world(strip_data.unresolved_radius, camera_distance, frame.auto_size_lines);
if draw_data.size_boost_in_points > 0.0 {
let size_boost = world_size_from_point_size(draw_data.size_boost_in_points, camera_distance);
strip_radius += size_boost;
// Push out positions as well along the quad dir.
// This is especially important if there's no miters on a line-strip (TODO(#829)),
// as this would enhance gaps between lines otherwise.
center_position += quad_dir * (size_boost * select(-1.0, 1.0, is_at_quad_end));
}

var active_radius = strip_radius;
// If this is a triangle cap, we blow up our ("virtual") quad by twice the size.
if has_any_flag(currently_active_flags, CAP_START_TRIANGLE | CAP_END_TRIANGLE) {
Expand Down
8 changes: 7 additions & 1 deletion crates/re_renderer/shader/point_cloud.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,12 @@ var position_data_texture: texture_2d<f32>;
@group(1) @binding(1)
var color_texture: texture_2d<f32>;

struct DrawDataUniformBuffer {
size_boost_in_points: f32,
};
@group(1) @binding(2)
var<uniform> draw_data: DrawDataUniformBuffer;

struct BatchUniformBuffer {
world_from_obj: Mat4,
flags: u32,
Expand Down Expand Up @@ -63,7 +69,7 @@ fn vs_main(@builtin(vertex_index) vertex_idx: u32) -> VertexOut {
let point_data = read_data(quad_idx);

// Span quad
let quad = sphere_quad_span(vertex_idx, point_data.pos, point_data.unresolved_radius);
let quad = sphere_quad_span(vertex_idx, point_data.pos, point_data.unresolved_radius, draw_data.size_boost_in_points);

// Output, transform to projection space and done.
var out: VertexOut;
Expand Down
7 changes: 5 additions & 2 deletions crates/re_renderer/shader/utils/size.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
#import <camera.wgsl>


fn world_size_from_point_size(size_in_points: f32, camera_distance: f32) -> f32 {
let pixel_size = frame.pixels_from_point * size_in_points;
return approx_pixel_world_size_at(camera_distance) * pixel_size;
}

fn unresolved_size_to_world(_unresolved_size: f32, camera_distance: f32, auto_size: f32) -> f32 {
// Resolve auto size.
Expand All @@ -23,6 +27,5 @@ fn unresolved_size_to_world(_unresolved_size: f32, camera_distance: f32, auto_si
}

// Negative size indicates size in points.
let pixel_size = frame.pixels_from_point * (-unresolved_size);
return approx_pixel_world_size_at(camera_distance) * pixel_size;
return world_size_from_point_size(-unresolved_size, camera_distance);
}
5 changes: 3 additions & 2 deletions crates/re_renderer/shader/utils/sphere_quad.wgsl
Original file line number Diff line number Diff line change
Expand Up @@ -68,11 +68,12 @@ struct SphereQuadData {
/// Span a quad onto which perspective correct spheres can be drawn.
///
/// Spanning is done in perspective or orthographically depending of the state of the global cam.
fn sphere_quad_span(vertex_idx: u32, point_pos: Vec3, point_unresolved_radius: f32) -> SphereQuadData {
fn sphere_quad_span(vertex_idx: u32, point_pos: Vec3, point_unresolved_radius: f32, size_boost_in_points: f32) -> SphereQuadData {
// Resolve radius to a world size. We need the camera distance for this, which is useful later on.
let to_camera = frame.camera_position - point_pos;
let camera_distance = length(to_camera);
let radius = unresolved_size_to_world(point_unresolved_radius, camera_distance, frame.auto_size_points);
let radius = unresolved_size_to_world(point_unresolved_radius, camera_distance, frame.auto_size_points) +
world_size_from_point_size(size_boost_in_points, camera_distance);

// Basic properties of the vertex we're at.
let local_idx = vertex_idx % 6u;
Expand Down
36 changes: 33 additions & 3 deletions crates/re_renderer/src/line_strip_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ use crate::{
LineBatchInfo, LineDrawData, LineStripFlags, LineStripInfo, LineVertex,
OutlineMaskPreference,
},
Color32, DebugLabel, Size,
Color32, DebugLabel, RenderContext, Size,
};

/// Builder for a vector of line strips, making it easy to create [`crate::renderer::LineDrawData`].
Expand All @@ -14,7 +14,6 @@ use crate::{
/// of writing to a GPU readable memory location.
/// This will require some ahead of time size limit, but should be feasible.
/// But before that we first need to sort out cpu->gpu transfers better by providing staging buffers.
#[derive(Default)]
pub struct LineStripSeriesBuilder<PerStripUserData> {
pub vertices: Vec<LineVertex>,

Expand All @@ -23,12 +22,36 @@ pub struct LineStripSeriesBuilder<PerStripUserData> {
pub strip_user_data: Vec<PerStripUserData>,

pub batches: Vec<LineBatchInfo>,

pub(crate) size_boost_in_points_for_outlines: f32,
}

impl<PerStripUserData> LineStripSeriesBuilder<PerStripUserData>
where
PerStripUserData: Default + Copy,
{
// TODO(andreas): ctx not yet needed since we don't write to GPU yet, but soon needed.
pub fn new(_ctx: &RenderContext) -> Self {
const RESERVE_SIZE: usize = 512;

Self {
vertices: Vec::with_capacity(RESERVE_SIZE * 2),
strips: Vec::with_capacity(RESERVE_SIZE),
strip_user_data: Vec::with_capacity(RESERVE_SIZE),
batches: Vec::with_capacity(16),
size_boost_in_points_for_outlines: 0.0,
}
}

/// Boosts the size of the points by the given amount of ui-points for the purpose of drawing outlines.
pub fn size_boost_in_points_for_outlines(
mut self,
size_boost_in_points_for_outlines: f32,
) -> Self {
self.size_boost_in_points_for_outlines = size_boost_in_points_for_outlines;
self
}

/// Start of a new batch.
pub fn batch(
&mut self,
Expand Down Expand Up @@ -65,7 +88,14 @@ where

/// Finalizes the builder and returns a line draw data with all the lines added so far.
pub fn to_draw_data(&self, ctx: &mut crate::context::RenderContext) -> LineDrawData {
LineDrawData::new(ctx, &self.vertices, &self.strips, &self.batches).unwrap()
LineDrawData::new(
ctx,
&self.vertices,
&self.strips,
&self.batches,
self.size_boost_in_points_for_outlines,
)
.unwrap()
}

/// Iterates over all line strips batches together with their strips and their respective vertices.
Expand Down
14 changes: 13 additions & 1 deletion crates/re_renderer/src/point_cloud_builder.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,13 +16,15 @@ pub struct PointCloudBuilder<PerPointUserData> {
pub user_data: Vec<PerPointUserData>,

pub(crate) batches: Vec<PointCloudBatchInfo>,

pub(crate) size_boost_in_points_for_outlines: f32,
}

impl<PerPointUserData> PointCloudBuilder<PerPointUserData>
where
PerPointUserData: Default + Copy,
{
pub fn new(ctx: &mut RenderContext) -> Self {
pub fn new(ctx: &RenderContext) -> Self {
const RESERVE_SIZE: usize = 512;

// TODO(andreas): Be more resourceful about the size allocated here. Typically we know in advance!
Expand All @@ -37,9 +39,19 @@ where
color_buffer,
user_data: Vec::with_capacity(RESERVE_SIZE),
batches: Vec::with_capacity(16),
size_boost_in_points_for_outlines: 0.0,
}
}

/// Boosts the size of the points by the given amount of ui-points for the purpose of drawing outlines.
pub fn size_boost_in_points_for_outlines(
mut self,
size_boost_in_points_for_outlines: f32,
) -> Self {
self.size_boost_in_points_for_outlines = size_boost_in_points_for_outlines;
self
}

/// Start of a new batch.
#[inline]
pub fn batch(
Expand Down
71 changes: 64 additions & 7 deletions crates/re_renderer/src/renderer/lines.rs
Original file line number Diff line number Diff line change
Expand Up @@ -160,6 +160,14 @@ pub mod gpu_data {
}
static_assertions::assert_eq_size!(LineStripInfo, [u32; 2]);

/// Uniform buffer that changes once per draw data rendering.
#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
pub struct DrawDataUniformBuffer {
pub size_boost_in_points: wgpu_buffer_types::F32RowPadded,
pub end_padding: [wgpu_buffer_types::PaddingRow; 16 - 1],
}

/// Uniform buffer that changes for every batch of line strips.
#[repr(C, align(256))]
#[derive(Clone, Copy, bytemuck::Pod, bytemuck::Zeroable)]
Expand All @@ -184,6 +192,7 @@ struct LineStripBatch {
#[derive(Clone)]
pub struct LineDrawData {
bind_group_all_lines: Option<GpuBindGroup>,
bind_group_all_lines_outline_mask: Option<GpuBindGroup>,
batches: Vec<LineStripBatch>,
}

Expand Down Expand Up @@ -312,9 +321,11 @@ impl LineDrawData {
/// If no batches are passed, all lines are assumed to be in a single batch with identity transform.
pub fn new(
ctx: &mut RenderContext,
// TODO(andreas): Take LineBuilder directly
vertices: &[gpu_data::LineVertex],
strips: &[LineStripInfo],
batches: &[LineBatchInfo],
size_boost_in_points_for_outlines: f32,
) -> Result<Self, LineDrawDataError> {
let mut renderers = ctx.renderers.write();
let line_renderer = renderers.get_or_create::<_, LineRenderer>(
Expand All @@ -327,6 +338,7 @@ impl LineDrawData {
if strips.is_empty() {
return Ok(LineDrawData {
bind_group_all_lines: None,
bind_group_all_lines_outline_mask: None,
batches: Vec::new(),
});
}
Expand Down Expand Up @@ -495,14 +507,43 @@ impl LineDrawData {
},
);

let draw_data_uniform_buffer_bindings = create_and_fill_uniform_buffer_batch(
ctx,
"LineDrawData::DrawDataUniformBuffer".into(),
[
gpu_data::DrawDataUniformBuffer {
size_boost_in_points: 0.0.into(),
end_padding: Default::default(),
},
gpu_data::DrawDataUniformBuffer {
size_boost_in_points: size_boost_in_points_for_outlines.into(),
end_padding: Default::default(),
},
]
.into_iter(),
);
let bind_group_all_lines = ctx.gpu_resources.bind_groups.alloc(
&ctx.device,
&ctx.gpu_resources,
&BindGroupDesc {
label: "line draw data".into(),
label: "LineDrawData::bind_group_all_lines".into(),
entries: smallvec![
BindGroupEntry::DefaultTextureView(position_data_texture.handle),
BindGroupEntry::DefaultTextureView(line_strip_texture.handle),
draw_data_uniform_buffer_bindings[0].clone(),
],
layout: line_renderer.bind_group_layout_all_lines,
},
);
let bind_group_all_lines_outline_mask = ctx.gpu_resources.bind_groups.alloc(
&ctx.device,
&ctx.gpu_resources,
&BindGroupDesc {
label: "LineDrawData::bind_group_all_lines_outline_mask".into(),
entries: smallvec![
BindGroupEntry::DefaultTextureView(position_data_texture.handle),
BindGroupEntry::DefaultTextureView(line_strip_texture.handle),
draw_data_uniform_buffer_bindings[1].clone(),
],
layout: line_renderer.bind_group_layout_all_lines,
},
Expand Down Expand Up @@ -597,6 +638,7 @@ impl LineDrawData {

Ok(LineDrawData {
bind_group_all_lines: Some(bind_group_all_lines),
bind_group_all_lines_outline_mask: Some(bind_group_all_lines_outline_mask),
batches: batches_internal,
})
}
Expand Down Expand Up @@ -679,6 +721,18 @@ impl Renderer for LineRenderer {
},
count: None,
},
wgpu::BindGroupLayoutEntry {
binding: 2,
visibility: wgpu::ShaderStages::VERTEX,
ty: wgpu::BindingType::Buffer {
ty: wgpu::BufferBindingType::Uniform,
has_dynamic_offset: false,
min_binding_size: NonZeroU64::new(std::mem::size_of::<
gpu_data::DrawDataUniformBuffer,
>() as _),
},
count: None,
},
],
},
);
Expand Down Expand Up @@ -789,15 +843,18 @@ impl Renderer for LineRenderer {
pass: &mut wgpu::RenderPass<'a>,
draw_data: &'a Self::RendererDrawData,
) -> anyhow::Result<()> {
let Some(bind_group_all_lines) = &draw_data.bind_group_all_lines else {
let (pipeline_handle, bind_group_all_lines) = match phase {
DrawPhase::OutlineMask => (
self.render_pipeline_outline_mask,
&draw_data.bind_group_all_lines_outline_mask,
),
DrawPhase::Opaque => (self.render_pipeline_color, &draw_data.bind_group_all_lines),
_ => unreachable!("We were called on a phase we weren't subscribed to: {phase:?}"),
};
let Some(bind_group_all_lines) = bind_group_all_lines else {
return Ok(()); // No lines submitted.
};

let pipeline_handle = match phase {
DrawPhase::OutlineMask => self.render_pipeline_outline_mask,
DrawPhase::Opaque => self.render_pipeline_color,
_ => unreachable!("We were called on a phase we weren't subscribed to: {phase:?}"),
};
let pipeline = pools.render_pipelines.get_resource(pipeline_handle)?;

pass.set_pipeline(pipeline);
Expand Down
Loading

1 comment on commit 0dc563e

@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: 0dc563e Previous: 0f2569d Ratio
datastore/insert/batch/rects/insert 570344 ns/iter (± 2327) 554901 ns/iter (± 4763) 1.03
datastore/latest_at/batch/rects/query 1850 ns/iter (± 17) 1832 ns/iter (± 13) 1.01
datastore/latest_at/missing_components/primary 287 ns/iter (± 2) 288 ns/iter (± 2) 1.00
datastore/latest_at/missing_components/secondaries 438 ns/iter (± 0) 436 ns/iter (± 4) 1.00
datastore/range/batch/rects/query 151405 ns/iter (± 232) 148470 ns/iter (± 1851) 1.02
mono_points_arrow/generate_message_bundles 47198094 ns/iter (± 655215) 46902333 ns/iter (± 1574851) 1.01
mono_points_arrow/generate_messages 126590192 ns/iter (± 1365170) 136243044 ns/iter (± 1733045) 0.93
mono_points_arrow/encode_log_msg 159180448 ns/iter (± 1608062) 163260809 ns/iter (± 1274405) 0.98
mono_points_arrow/encode_total 332427923 ns/iter (± 2668758) 350047046 ns/iter (± 3121600) 0.95
mono_points_arrow/decode_log_msg 176649652 ns/iter (± 767175) 183563257 ns/iter (± 1719775) 0.96
mono_points_arrow/decode_message_bundles 65496423 ns/iter (± 785967) 70807602 ns/iter (± 1262660) 0.92
mono_points_arrow/decode_total 238121545 ns/iter (± 1473416) 253857416 ns/iter (± 2680007) 0.94
batch_points_arrow/generate_message_bundles 340476 ns/iter (± 587) 335181 ns/iter (± 4048) 1.02
batch_points_arrow/generate_messages 6388 ns/iter (± 11) 6233 ns/iter (± 75) 1.02
batch_points_arrow/encode_log_msg 370504 ns/iter (± 1187) 363065 ns/iter (± 3186) 1.02
batch_points_arrow/encode_total 732623 ns/iter (± 2634) 739927 ns/iter (± 6685) 0.99
batch_points_arrow/decode_log_msg 349084 ns/iter (± 882) 341720 ns/iter (± 2605) 1.02
batch_points_arrow/decode_message_bundles 2063 ns/iter (± 13) 2001 ns/iter (± 24) 1.03
batch_points_arrow/decode_total 355696 ns/iter (± 1121) 348933 ns/iter (± 2676) 1.02
arrow_mono_points/insert 6157379273 ns/iter (± 20146819) 6910039741 ns/iter (± 23446336) 0.89
arrow_mono_points/query 1793696 ns/iter (± 12953) 1747989 ns/iter (± 19555) 1.03
arrow_batch_points/insert 2704452 ns/iter (± 14131) 2566653 ns/iter (± 19991) 1.05
arrow_batch_points/query 16188 ns/iter (± 74) 16219 ns/iter (± 127) 1.00
arrow_batch_vecs/insert 42570 ns/iter (± 143) 41553 ns/iter (± 388) 1.02
arrow_batch_vecs/query 389302 ns/iter (± 1127) 384700 ns/iter (± 4077) 1.01
tuid/Tuid::random 34 ns/iter (± 0) 34 ns/iter (± 1) 1

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

Please sign in to comment.