Skip to content

Commit beffbc3

Browse files
authored
Show previews of colormaps when selecting them (rerun-io#1846)
* Make infallible version of get_or_create_texture * Show colormap previews in UI * Spelling
1 parent fe7ac0e commit beffbc3

File tree

4 files changed

+119
-23
lines changed

4 files changed

+119
-23
lines changed

crates/re_viewer/src/gpu_bridge/mod.rs

+26-4
Original file line numberDiff line numberDiff line change
@@ -52,7 +52,7 @@ pub fn viewport_resolution_in_pixels(clip_rect: egui::Rect, pixels_from_point: f
5252
[resolution.x as u32, resolution.y as u32]
5353
}
5454

55-
pub fn get_or_create_texture<'a, Err>(
55+
pub fn try_get_or_create_texture<'a, Err>(
5656
render_ctx: &mut RenderContext,
5757
texture_key: u64,
5858
try_create_texture_desc: impl FnOnce() -> Result<Texture2DCreationDesc<'a>, Err>,
@@ -64,6 +64,23 @@ pub fn get_or_create_texture<'a, Err>(
6464
)
6565
}
6666

67+
pub fn get_or_create_texture<'a>(
68+
render_ctx: &mut RenderContext,
69+
texture_key: u64,
70+
create_texture_desc: impl FnOnce() -> Texture2DCreationDesc<'a>,
71+
) -> GpuTexture2DHandle {
72+
enum Never {}
73+
let result: Result<GpuTexture2DHandle, Never> = render_ctx
74+
.texture_manager_2d
75+
.get_or_create_with(texture_key, &mut render_ctx.gpu_resources.textures, || {
76+
Ok(create_texture_desc())
77+
});
78+
match result {
79+
Ok(handle) => handle,
80+
Err(never) => match never {},
81+
}
82+
}
83+
6784
/// Render a `re_render` view using the given clip rectangle.
6885
pub fn renderer_paint_callback(
6986
render_ctx: &mut re_renderer::RenderContext,
@@ -132,6 +149,11 @@ pub fn render_image(
132149

133150
use re_renderer::renderer::{TextureFilterMag, TextureFilterMin};
134151

152+
let clip_rect = painter.clip_rect().intersect(image_rect_on_screen);
153+
if !clip_rect.is_positive() {
154+
return Ok(());
155+
}
156+
135157
// Where in "world space" to paint the image.
136158
let space_rect = egui::Rect::from_min_size(egui::Pos2::ZERO, image_rect_on_screen.size());
137159

@@ -163,10 +185,10 @@ pub fn render_image(
163185
let space_from_pixel = space_from_points * points_from_pixels;
164186

165187
let resolution_in_pixel =
166-
crate::gpu_bridge::viewport_resolution_in_pixels(painter.clip_rect(), pixels_from_points);
188+
crate::gpu_bridge::viewport_resolution_in_pixels(clip_rect, pixels_from_points);
167189
anyhow::ensure!(resolution_in_pixel[0] > 0 && resolution_in_pixel[1] > 0);
168190

169-
let camera_position_space = space_from_ui.transform_pos(painter.clip_rect().min);
191+
let camera_position_space = space_from_ui.transform_pos(clip_rect.min);
170192

171193
let top_left_position = glam::vec2(camera_position_space.x, camera_position_space.y);
172194
let target_config = re_renderer::view_builder::TargetConfiguration {
@@ -196,7 +218,7 @@ pub fn render_image(
196218
render_ctx,
197219
command_buffer,
198220
view_builder,
199-
painter.clip_rect(),
221+
clip_rect,
200222
painter.ctx().pixels_per_point(),
201223
));
202224

crates/re_viewer/src/gpu_bridge/tensor_to_gpu.rs

+10-13
Original file line numberDiff line numberDiff line change
@@ -13,9 +13,9 @@ use re_renderer::{
1313
RenderContext,
1414
};
1515

16-
use crate::misc::caches::TensorStats;
16+
use crate::{gpu_bridge::get_or_create_texture, misc::caches::TensorStats};
1717

18-
use super::get_or_create_texture;
18+
use super::try_get_or_create_texture;
1919

2020
// ----------------------------------------------------------------------------
2121

@@ -63,7 +63,7 @@ fn color_tensor_to_gpu(
6363
tensor: &Tensor,
6464
tensor_stats: &TensorStats,
6565
) -> anyhow::Result<ColormappedTexture> {
66-
let texture_handle = get_or_create_texture(render_ctx, hash(tensor.id()), || {
66+
let texture_handle = try_get_or_create_texture(render_ctx, hash(tensor.id()), || {
6767
let [height, width, depth] = height_width_depth(tensor)?;
6868
let (data, format) = match (depth, &tensor.data) {
6969
// Use R8Unorm and R8Snorm to get filtering on the GPU:
@@ -161,10 +161,8 @@ fn class_id_tensor_to_gpu(
161161
let colormap_width = 256;
162162
let colormap_height = (max as usize + colormap_width - 1) / colormap_width;
163163

164-
let colormap_texture_handle = get_or_create_texture(
165-
render_ctx,
166-
hash(annotations.row_id),
167-
|| -> anyhow::Result<_> {
164+
let colormap_texture_handle =
165+
get_or_create_texture(render_ctx, hash(annotations.row_id), || {
168166
let data: Vec<u8> = (0..(colormap_width * colormap_height))
169167
.flat_map(|id| {
170168
let color = annotations
@@ -175,17 +173,16 @@ fn class_id_tensor_to_gpu(
175173
})
176174
.collect();
177175

178-
Ok(Texture2DCreationDesc {
176+
Texture2DCreationDesc {
179177
label: "class_id_colormap".into(),
180178
data: data.into(),
181179
format: TextureFormat::Rgba8UnormSrgb,
182180
width: colormap_width as u32,
183181
height: colormap_height as u32,
184-
})
185-
},
186-
)?;
182+
}
183+
});
187184

188-
let main_texture_handle = get_or_create_texture(render_ctx, hash(tensor.id()), || {
185+
let main_texture_handle = try_get_or_create_texture(render_ctx, hash(tensor.id()), || {
189186
general_texture_creation_desc_from_tensor(debug_name, tensor)
190187
})?;
191188

@@ -214,7 +211,7 @@ fn depth_tensor_to_gpu(
214211
);
215212
let (min, max) = depth_tensor_range(tensor, tensor_stats)?;
216213

217-
let texture = get_or_create_texture(render_ctx, hash(tensor.id()), || {
214+
let texture = try_get_or_create_texture(render_ctx, hash(tensor.id()), || {
218215
general_texture_creation_desc_from_tensor(debug_name, tensor)
219216
})?;
220217

crates/re_viewer/src/ui/view_tensor/tensor_slice_to_gpu.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ fn upload_texture_slice_to_gpu(
5656
) -> Result<re_renderer::resource_managers::GpuTexture2DHandle, TensorUploadError> {
5757
let id = egui::util::hash((tensor.id(), slice_selection));
5858

59-
crate::gpu_bridge::get_or_create_texture(render_ctx, id, || {
59+
crate::gpu_bridge::try_get_or_create_texture(render_ctx, id, || {
6060
texture_desc_from_tensor(tensor, slice_selection)
6161
})
6262
}

crates/re_viewer/src/ui/view_tensor/ui.rs

+82-5
Original file line numberDiff line numberDiff line change
@@ -78,7 +78,7 @@ impl ViewTensorState {
7878
Some(ctx.cache.tensor_stats(tensor)),
7979
);
8080
self.texture_settings.ui(ctx.re_ui, ui);
81-
self.color_mapping.ui(ctx.re_ui, ui);
81+
self.color_mapping.ui(ctx.render_ctx, ctx.re_ui, ui);
8282
});
8383

8484
ui.separator();
@@ -245,17 +245,29 @@ impl Default for ColorMapping {
245245
}
246246

247247
impl ColorMapping {
248-
fn ui(&mut self, re_ui: &re_ui::ReUi, ui: &mut egui::Ui) {
248+
fn ui(
249+
&mut self,
250+
render_ctx: &mut re_renderer::RenderContext,
251+
re_ui: &re_ui::ReUi,
252+
ui: &mut egui::Ui,
253+
) {
249254
let ColorMapping { map, gamma } = self;
250255

251256
re_ui.grid_left_hand_label(ui, "Color map");
252257
egui::ComboBox::from_id_source("color map select")
253258
.selected_text(map.to_string())
254259
.show_ui(ui, |ui| {
255260
ui.style_mut().wrap = Some(false);
256-
for option in Colormap::ALL {
257-
ui.selectable_value(map, option, option.to_string());
258-
}
261+
262+
egui::Grid::new("colormap_selector")
263+
.num_columns(2)
264+
.show(ui, |ui| {
265+
for option in Colormap::ALL {
266+
ui.selectable_value(map, option, option.to_string());
267+
colormap_preview_ui(render_ctx, ui, option);
268+
ui.end_row();
269+
}
270+
});
259271
});
260272
ui.end_row();
261273

@@ -267,6 +279,71 @@ impl ColorMapping {
267279
}
268280
}
269281

282+
/// Show the given colormap as a horizontal bar.
283+
fn colormap_preview_ui(
284+
render_ctx: &mut re_renderer::RenderContext,
285+
ui: &mut egui::Ui,
286+
colormap: Colormap,
287+
) -> egui::Response {
288+
crate::profile_function!();
289+
290+
let desired_size = egui::vec2(128.0, 16.0);
291+
let (rect, response) = ui.allocate_exact_size(desired_size, egui::Sense::hover());
292+
293+
if ui.is_rect_visible(rect) {
294+
if let Err(err) = paint_colormap_gradient(render_ctx, colormap, ui, rect) {
295+
re_log::error_once!("Failed to paint colormap preview: {err}");
296+
}
297+
}
298+
299+
response
300+
}
301+
302+
fn paint_colormap_gradient(
303+
render_ctx: &mut re_renderer::RenderContext,
304+
colormap: Colormap,
305+
ui: &mut egui::Ui,
306+
rect: egui::Rect,
307+
) -> anyhow::Result<()> {
308+
let horizontal_gradient_id = egui::util::hash("horizontal_gradient");
309+
let horizontal_gradient =
310+
crate::gpu_bridge::get_or_create_texture(render_ctx, horizontal_gradient_id, || {
311+
let width = 256;
312+
let height = 1;
313+
let data: Vec<u8> = (0..width)
314+
.flat_map(|x| {
315+
let t = x as f32 / (width as f32 - 1.0);
316+
half::f16::from_f32(t).to_le_bytes()
317+
})
318+
.collect();
319+
320+
re_renderer::resource_managers::Texture2DCreationDesc {
321+
label: "horizontal_gradient".into(),
322+
data: data.into(),
323+
format: wgpu::TextureFormat::R16Float,
324+
width,
325+
height,
326+
}
327+
});
328+
329+
let colormapped_texture = re_renderer::renderer::ColormappedTexture {
330+
texture: horizontal_gradient,
331+
range: [0.0, 1.0],
332+
gamma: 1.0,
333+
color_mapper: Some(re_renderer::renderer::ColorMapper::Function(colormap)),
334+
};
335+
336+
let debug_name = format!("colormap_{colormap}");
337+
crate::gpu_bridge::render_image(
338+
render_ctx,
339+
ui.painter(),
340+
rect,
341+
colormapped_texture,
342+
egui::TextureOptions::LINEAR,
343+
&debug_name,
344+
)
345+
}
346+
270347
// ----------------------------------------------------------------------------
271348

272349
/// Should we scale the rendered texture, and if so, how?

0 commit comments

Comments
 (0)