From 5915dc4761a51c2ea1e69bd37ab14a6065ac9267 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 28 May 2024 15:17:33 +0200 Subject: [PATCH 1/6] Fix image preview error display --- crates/re_data_ui/src/image.rs | 55 +++++++++++++++++++++------------- 1 file changed, 35 insertions(+), 20 deletions(-) diff --git a/crates/re_data_ui/src/image.rs b/crates/re_data_ui/src/image.rs index f5e810ad5391..d0759f26b1a1 100644 --- a/crates/re_data_ui/src/image.rs +++ b/crates/re_data_ui/src/image.rs @@ -146,26 +146,29 @@ pub fn tensor_ui( |ui| { ui.set_min_size(preview_size); - show_image_preview( + match show_image_preview( ctx.render_ctx, ctx.re_ui, ui, texture.clone(), &debug_name, preview_size, - ) - .on_hover_ui(|ui| { - // Show larger image on hover. - let preview_size = Vec2::splat(400.0); - show_image_preview( - ctx.render_ctx, - ctx.re_ui, - ui, - texture.clone(), - &debug_name, - preview_size, - ); - }); + ) { + Ok(response) => response.on_hover_ui(|ui| { + // Show larger image on hover. + let preview_size = Vec2::splat(400.0); + show_image_preview( + ctx.render_ctx, + ctx.re_ui, + ui, + texture.clone(), + &debug_name, + preview_size, + ) + .ok(); + }), + Err((response, err)) => response.on_hover_text(err.to_string()), + } }, ); } @@ -215,14 +218,17 @@ pub fn tensor_ui( .available_size() .min(texture_size(texture)) .min(egui::vec2(150.0, 300.0)); - let response = show_image_preview( + let response = match show_image_preview( ctx.render_ctx, ctx.re_ui, ui, texture.clone(), &debug_name, preview_size, - ); + ) { + Ok(response) => response, + Err((response, err)) => response.on_hover_text(err.to_string()), + }; if let Some(pointer_pos) = ui.ctx().pointer_latest_pos() { let image_rect = response.rect; @@ -279,6 +285,8 @@ fn texture_size(colormapped_texture: &ColormappedTexture) -> Vec2 { /// /// Extremely small images will be stretched on their thin axis to make them visible. /// This does not preserve aspect ratio, but we only stretch it to a very thin size, so it is fine. +/// +/// Returns error if the image could not be rendered. fn show_image_preview( render_ctx: &re_renderer::RenderContext, re_ui: &ReUi, @@ -286,7 +294,7 @@ fn show_image_preview( colormapped_texture: ColormappedTexture, debug_name: &str, desired_size: egui::Vec2, -) -> egui::Response { +) -> Result { const MIN_SIZE: f32 = 2.0; let texture_size = texture_size(&colormapped_texture); @@ -309,10 +317,17 @@ fn show_image_preview( egui::TextureOptions::LINEAR, debug_name, ) { - let label_response = ui.label(re_ui.error_text(err.to_string())); - response.union(label_response) + let color = ui.visuals().error_fg_color; + painter.text( + response.rect.left_top(), + egui::Align2::LEFT_TOP, + "🚫", + egui::FontId::default(), + color, + ); + Err((response, err)) } else { - response + Ok(response) } } From 45c9d62f6d2437f58eb88ade51392722f02a7f08 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 28 May 2024 15:26:54 +0200 Subject: [PATCH 2/6] improve color mapping error string --- crates/re_renderer/src/renderer/rectangles.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/crates/re_renderer/src/renderer/rectangles.rs b/crates/re_renderer/src/renderer/rectangles.rs index f2cddebe0e8d..057614b97e92 100644 --- a/crates/re_renderer/src/renderer/rectangles.rs +++ b/crates/re_renderer/src/renderer/rectangles.rs @@ -210,7 +210,7 @@ pub enum RectangleError { #[error("Texture format not supported: {0:?} - use float or integer textures instead.")] TextureFormatNotSupported(wgpu::TextureFormat), - #[error("Color mapping is being applied to a four-component RGBA texture")] + #[error("Color mapping cannot be applied to a four-component RGBA image, but only to a single-component image.")] ColormappingRgbaTexture, #[error("Only 1 and 4 component textures are supported, got {0} components")] From 183ecf4337b07f5eb141c4de6f9cb17aeb67fa11 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 28 May 2024 15:28:14 +0200 Subject: [PATCH 3/6] warning fixes --- crates/re_data_ui/src/image.rs | 5 ----- 1 file changed, 5 deletions(-) diff --git a/crates/re_data_ui/src/image.rs b/crates/re_data_ui/src/image.rs index d0759f26b1a1..16a0458c4965 100644 --- a/crates/re_data_ui/src/image.rs +++ b/crates/re_data_ui/src/image.rs @@ -6,7 +6,6 @@ use re_renderer::renderer::ColormappedTexture; use re_types::components::{ClassId, DepthMeter}; use re_types::datatypes::{TensorBuffer, TensorData, TensorDimension}; use re_types::tensor_data::{DecodedTensor, TensorDataMeaning, TensorElement}; -use re_ui::ReUi; use re_viewer_context::{ gpu_bridge, Annotations, TensorDecodeCache, TensorStats, TensorStatsCache, UiLayout, ViewerContext, @@ -148,7 +147,6 @@ pub fn tensor_ui( match show_image_preview( ctx.render_ctx, - ctx.re_ui, ui, texture.clone(), &debug_name, @@ -159,7 +157,6 @@ pub fn tensor_ui( let preview_size = Vec2::splat(400.0); show_image_preview( ctx.render_ctx, - ctx.re_ui, ui, texture.clone(), &debug_name, @@ -220,7 +217,6 @@ pub fn tensor_ui( .min(egui::vec2(150.0, 300.0)); let response = match show_image_preview( ctx.render_ctx, - ctx.re_ui, ui, texture.clone(), &debug_name, @@ -289,7 +285,6 @@ fn texture_size(colormapped_texture: &ColormappedTexture) -> Vec2 { /// Returns error if the image could not be rendered. fn show_image_preview( render_ctx: &re_renderer::RenderContext, - re_ui: &ReUi, ui: &mut egui::Ui, colormapped_texture: ColormappedTexture, debug_name: &str, From ca2338b8786a524aa2dabc5e85d3af472b783264 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 28 May 2024 16:08:04 +0200 Subject: [PATCH 4/6] Fix error when logging segmentation image & annotation context on the same entity at once --- .../src/visualizers/images.rs | 4 +- .../re_viewer_context/src/gpu_bridge/mod.rs | 5 +- .../src/gpu_bridge/tensor_to_gpu.rs | 107 ++++++++++-------- 3 files changed, 66 insertions(+), 50 deletions(-) diff --git a/crates/re_space_view_spatial/src/visualizers/images.rs b/crates/re_space_view_spatial/src/visualizers/images.rs index 41f0f9c2c658..1747a4452bca 100644 --- a/crates/re_space_view_spatial/src/visualizers/images.rs +++ b/crates/re_space_view_spatial/src/visualizers/images.rs @@ -560,12 +560,14 @@ impl ImageVisualizer { let tensor_stats = ctx .cache .entry(|c: &mut TensorStatsCache| c.entry(tensor_data_row_id, tensor)); - let depth_texture = re_viewer_context::gpu_bridge::depth_tensor_to_gpu( + let depth_texture = re_viewer_context::gpu_bridge::tensor_to_gpu( ctx.render_ctx, &debug_name, tensor_data_row_id, tensor, + TensorDataMeaning::Depth, &tensor_stats, + &ent_context.annotations, )?; let depth_from_world_scale = *properties.depth_from_world_scale; diff --git a/crates/re_viewer_context/src/gpu_bridge/mod.rs b/crates/re_viewer_context/src/gpu_bridge/mod.rs index fd1072bcc545..5d1ab633f603 100644 --- a/crates/re_viewer_context/src/gpu_bridge/mod.rs +++ b/crates/re_viewer_context/src/gpu_bridge/mod.rs @@ -6,10 +6,7 @@ mod tensor_to_gpu; pub use colormap::colormap_dropdown_button_ui; pub use re_renderer_callback::new_renderer_callback; -pub use tensor_to_gpu::{ - class_id_tensor_to_gpu, color_tensor_to_gpu, depth_tensor_to_gpu, tensor_to_gpu, - texture_height_width_channels, -}; +pub use tensor_to_gpu::{tensor_to_gpu, texture_height_width_channels}; use crate::TensorStats; diff --git a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs index 0b50d04d6760..1abf4e41607d 100644 --- a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs +++ b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs @@ -27,6 +27,27 @@ use super::{get_or_create_texture, try_get_or_create_texture}; // ---------------------------------------------------------------------------- +enum TextureKeyUsage { + AnnotationContextColormap, + TensorData(TensorDataMeaning), +} + +/// Returns a texture key for a given row id & usage. +/// +/// Several textures may be created from the same row. +/// This makes sure that they all get different keys! +fn generate_texture_key(row_id: RowId, usage: TextureKeyUsage) -> u64 { + hash(row_id) + ^ hash(match usage { + TextureKeyUsage::TensorData(meaning) => match meaning { + TensorDataMeaning::Unknown => 0x12345678, + TensorDataMeaning::ClassId => 0x23456789, + TensorDataMeaning::Depth => 0x34567890, + }, + TextureKeyUsage::AnnotationContextColormap => 0x45678901, + }) +} + /// Set up tensor for rendering on the GPU. /// /// This will only upload the tensor if it isn't on the GPU already. @@ -49,43 +70,37 @@ pub fn tensor_to_gpu( tensor.shape() )); + let texture_key = + generate_texture_key(tensor_data_row_id, TextureKeyUsage::TensorData(meaning)); + match meaning { - TensorDataMeaning::Unknown => color_tensor_to_gpu( - render_ctx, - debug_name, - tensor_data_row_id, - tensor, - tensor_stats, - ), + TensorDataMeaning::Unknown => { + color_tensor_to_gpu(render_ctx, debug_name, texture_key, tensor, tensor_stats) + } TensorDataMeaning::ClassId => class_id_tensor_to_gpu( render_ctx, debug_name, - tensor_data_row_id, + texture_key, tensor, tensor_stats, annotations, ), - TensorDataMeaning::Depth => depth_tensor_to_gpu( - render_ctx, - debug_name, - tensor_data_row_id, - tensor, - tensor_stats, - ), + TensorDataMeaning::Depth => { + depth_tensor_to_gpu(render_ctx, debug_name, texture_key, tensor, tensor_stats) + } } } // ---------------------------------------------------------------------------- // Color textures: -pub fn color_tensor_to_gpu( +fn color_tensor_to_gpu( render_ctx: &RenderContext, debug_name: &str, - tensor_data_row_id: RowId, + texture_key: u64, tensor: &DecodedTensor, tensor_stats: &TensorStats, ) -> anyhow::Result { - let texture_key = hash(tensor_data_row_id); let [height, width, depth] = texture_height_width_channels(tensor)?; let texture_handle = try_get_or_create_texture(render_ctx, texture_key, || { @@ -197,16 +212,20 @@ pub fn color_tensor_to_gpu( // ---------------------------------------------------------------------------- // Textures with class_id annotations: -pub fn class_id_tensor_to_gpu( +fn class_id_tensor_to_gpu( render_ctx: &RenderContext, debug_name: &str, - tensor_data_row_id: RowId, + texture_key: u64, tensor: &DecodedTensor, tensor_stats: &TensorStats, annotations: &Annotations, ) -> anyhow::Result { re_tracing::profile_function!(); - let texture_key = hash(tensor_data_row_id); + + let colormap_key = generate_texture_key( + annotations.row_id(), + TextureKeyUsage::AnnotationContextColormap, + ); let [_height, _width, depth] = texture_height_width_channels(tensor)?; anyhow::ensure!( @@ -229,27 +248,26 @@ pub fn class_id_tensor_to_gpu( let colormap_width = 256; let colormap_height = (num_colors + colormap_width - 1) / colormap_width; - let colormap_texture_handle = - get_or_create_texture(render_ctx, hash(annotations.row_id()), || { - let data: Vec = (0..(colormap_width * colormap_height)) - .flat_map(|id| { - let color = annotations - .resolved_class_description(Some(ClassId::from(id as u16))) - .annotation_info() - .color(None, DefaultColor::TransparentBlack); - color.to_array() // premultiplied! - }) - .collect(); - - Texture2DCreationDesc { - label: "class_id_colormap".into(), - data: data.into(), - format: TextureFormat::Rgba8UnormSrgb, - width: colormap_width as u32, - height: colormap_height as u32, - } - }) - .context("Failed to create class_id_colormap.")?; + let colormap_texture_handle = get_or_create_texture(render_ctx, colormap_key, || { + let data: Vec = (0..(colormap_width * colormap_height)) + .flat_map(|id| { + let color = annotations + .resolved_class_description(Some(ClassId::from(id as u16))) + .annotation_info() + .color(None, DefaultColor::TransparentBlack); + color.to_array() // premultiplied! + }) + .collect(); + + Texture2DCreationDesc { + label: "class_id_colormap".into(), + data: data.into(), + format: TextureFormat::Rgba8UnormSrgb, + width: colormap_width as u32, + height: colormap_height as u32, + } + }) + .context("Failed to create class_id_colormap.")?; let main_texture_handle = try_get_or_create_texture(render_ctx, texture_key, || { general_texture_creation_desc_from_tensor(debug_name, tensor) @@ -270,15 +288,14 @@ pub fn class_id_tensor_to_gpu( // ---------------------------------------------------------------------------- // Depth textures: -pub fn depth_tensor_to_gpu( +fn depth_tensor_to_gpu( render_ctx: &RenderContext, debug_name: &str, - tensor_data_row_id: RowId, + texture_key: u64, tensor: &DecodedTensor, tensor_stats: &TensorStats, ) -> anyhow::Result { re_tracing::profile_function!(); - let texture_key = hash(tensor_data_row_id); let [_height, _width, depth] = texture_height_width_channels(tensor)?; anyhow::ensure!( From da107036ebbe7c5284f092b109a14843620d1e8e Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Tue, 28 May 2024 17:05:04 +0200 Subject: [PATCH 5/6] do a proper hash combine by hashing a tuple --- crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs index 1abf4e41607d..91b9b91981f9 100644 --- a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs +++ b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs @@ -27,6 +27,7 @@ use super::{get_or_create_texture, try_get_or_create_texture}; // ---------------------------------------------------------------------------- +#[derive(Copy, Clone)] enum TextureKeyUsage { AnnotationContextColormap, TensorData(TensorDataMeaning), @@ -37,15 +38,17 @@ enum TextureKeyUsage { /// Several textures may be created from the same row. /// This makes sure that they all get different keys! fn generate_texture_key(row_id: RowId, usage: TextureKeyUsage) -> u64 { - hash(row_id) - ^ hash(match usage { + hash(( + row_id, + match usage { TextureKeyUsage::TensorData(meaning) => match meaning { TensorDataMeaning::Unknown => 0x12345678, TensorDataMeaning::ClassId => 0x23456789, TensorDataMeaning::Depth => 0x34567890, }, TextureKeyUsage::AnnotationContextColormap => 0x45678901, - }) + }, + )) } /// Set up tensor for rendering on the GPU. From 462a7ab6e9ce2e3c763796a24fb27d5fca10e8b2 Mon Sep 17 00:00:00 2001 From: Andreas Reich Date: Wed, 29 May 2024 10:02:17 +0200 Subject: [PATCH 6/6] simply hash `TextureKeyUsage` --- crates/re_types/src/tensor_data.rs | 2 +- .../src/gpu_bridge/tensor_to_gpu.rs | 14 ++------------ 2 files changed, 3 insertions(+), 13 deletions(-) diff --git a/crates/re_types/src/tensor_data.rs b/crates/re_types/src/tensor_data.rs index c80aaf4a9f55..9830b8a8f18b 100644 --- a/crates/re_types/src/tensor_data.rs +++ b/crates/re_types/src/tensor_data.rs @@ -418,7 +418,7 @@ impl DecodedTensor { // Backwards comparabillity shim // TODO(jleibs): fully express this in terms of indicator components -#[derive(Clone, Copy, Debug, PartialEq, Eq)] +#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash)] pub enum TensorDataMeaning { /// Default behavior: guess based on shape Unknown, diff --git a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs index 91b9b91981f9..4e383e853f91 100644 --- a/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs +++ b/crates/re_viewer_context/src/gpu_bridge/tensor_to_gpu.rs @@ -27,7 +27,7 @@ use super::{get_or_create_texture, try_get_or_create_texture}; // ---------------------------------------------------------------------------- -#[derive(Copy, Clone)] +#[derive(Copy, Clone, Hash)] enum TextureKeyUsage { AnnotationContextColormap, TensorData(TensorDataMeaning), @@ -38,17 +38,7 @@ enum TextureKeyUsage { /// Several textures may be created from the same row. /// This makes sure that they all get different keys! fn generate_texture_key(row_id: RowId, usage: TextureKeyUsage) -> u64 { - hash(( - row_id, - match usage { - TextureKeyUsage::TensorData(meaning) => match meaning { - TensorDataMeaning::Unknown => 0x12345678, - TensorDataMeaning::ClassId => 0x23456789, - TensorDataMeaning::Depth => 0x34567890, - }, - TextureKeyUsage::AnnotationContextColormap => 0x45678901, - }, - )) + hash((row_id, usage)) } /// Set up tensor for rendering on the GPU.